Skip to content

Commit

Permalink
feat(planning): support parameters as action cost
Browse files Browse the repository at this point in the history
  • Loading branch information
Shi-Raida committed Jul 22, 2024
1 parent acdc651 commit 4f455eb
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 14 deletions.
45 changes: 40 additions & 5 deletions planning/grpc/server/src/chronicles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -895,12 +895,47 @@ impl<'a> ChronicleFactory<'a> {
}

fn set_cost(&mut self, cost: &Expression) -> Result<(), Error> {
ensure!(kind(cost)? == ExpressionKind::Constant);
ensure!(cost.r#type == "up:integer");
let cost = match cost.atom.as_ref().unwrap().content.as_ref().unwrap() {
Content::Int(i) => *i as IntCst,
_ => bail!("Unexpected cost type."),
let tpe = from_upf_type(cost.r#type.as_ref(), &self.context.model.get_symbol_table().types)
.with_context(|| format!("Unknown argument type: {0}", cost.r#type))?;
ensure!(tpe.is_numeric());

let cost = match kind(cost)? {
ExpressionKind::Constant => {
ensure!(cost.r#type == "up:integer");
Cost::Fixed(match cost.atom.as_ref().unwrap().content.as_ref().unwrap() {
Content::Int(i) => *i as IntCst,
_ => bail!("Unexpected cost type."),
})
}
ExpressionKind::Parameter => {
let name = match cost.atom.as_ref().unwrap().content.as_ref().unwrap() {
Content::Symbol(s) => s.clone(),
_ => bail!("Unexpected cost parameter name type."),
};
let var = self
.env
.parameters
.get(&name)
.with_context(|| format!("Unknown parameter: {name}"))?;
match var {
Variable::Int(var) => Cost::Variable(*var),
_ => bail!("Cost parameter must be an integer variable."),
}
}
_ => bail!("Unsupported cost expression: {cost:?}"),
};

match cost {
Cost::Fixed(cost) => ensure!(cost >= 0, "Cost must be a non-negative integer ({cost})"),
Cost::Variable(..) => match tpe {
Type::Int { lb, .. } => ensure!(
lb >= 0,
"Cost variable must be a non-negative integer (lower bound = {lb})"
),
_ => bail!("Cost variable must be an integer variable."),
},
};

self.chronicle.cost = Some(cost);
Ok(())
}
Expand Down
15 changes: 12 additions & 3 deletions planning/planners/src/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,8 +428,13 @@ pub fn add_metric(pb: &FiniteProblem, model: &mut Model, metric: Metric) -> IAto
// retrieve the presence and cost of each chronicle
let mut costs = Vec::with_capacity(8);
for (ch_id, ch) in pb.chronicles.iter().enumerate() {
if let Some(cost) = ch.chronicle.cost {
assert!(cost >= 0, "A chronicle has a negative cost");
if let Some(cost) = &ch.chronicle.cost {
match cost {
Cost::Fixed(c) => assert!(*c >= 0, "A chronicle has a negative cost"),
Cost::Variable(v) => {
assert!(model.domain_of(*v).0 >= 0, "A chronicle could have a negative cost")
}
};
costs.push((ch_id, ch.chronicle.presence, cost));
}
}
Expand All @@ -438,8 +443,12 @@ pub fn add_metric(pb: &FiniteProblem, model: &mut Model, metric: Metric) -> IAto
let action_costs: Vec<LinearTerm> = costs
.iter()
.map(|&(ch_id, p, cost)| {
let bounds = match cost {
Cost::Fixed(c) => (*c, *c),
Cost::Variable(v) => model.domain_of(*v),
};
model
.new_optional_ivar(cost, cost, p, Container::Instance(ch_id).var(VarType::Cost))
.new_optional_ivar(bounds.0, bounds.1, p, Container::Instance(ch_id).var(VarType::Cost))
.or_zero(p)
})
.collect();
Expand Down
22 changes: 20 additions & 2 deletions planning/planning/src/chronicles/concrete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,24 @@ impl Substitute for StateVar {
}
}

/// The cost of a chronicle
#[derive(Clone, Debug)]
pub enum Cost {
/// The cost is a fixed integer value
Fixed(IntCst),
/// The cost is a chronicle's variable
Variable(IVar),
}

impl Substitute for Cost {
fn substitute(&self, s: &impl Substitution) -> Self {
match self {
Cost::Fixed(x) => Cost::Fixed(*x),
Cost::Variable(x) => Cost::Variable(s.sub_ivar(*x)),
}
}
}

/// Represents an effect on a state variable.
/// The effect has a first transition phase `]transition_start, transition_end[` during which the
/// value of the state variable is unknown.
Expand Down Expand Up @@ -493,7 +511,7 @@ pub struct Chronicle {
/// expression on the start/end timepoint of these subtasks.
pub subtasks: Vec<SubTask>,
/// Cost of this chronicle. If left empty, it is interpreted as 0.
pub cost: Option<IntCst>,
pub cost: Option<Cost>,
}

struct VarSet(HashSet<VarRef>);
Expand Down Expand Up @@ -625,7 +643,7 @@ impl Substitute for Chronicle {
effects: self.effects.iter().map(|e| e.substitute(s)).collect(),
constraints: self.constraints.iter().map(|c| c.substitute(s)).collect(),
subtasks: self.subtasks.iter().map(|c| c.substitute(s)).collect(),
cost: self.cost,
cost: self.cost.as_ref().map(|c| c.substitute(s)),
}
}
}
4 changes: 2 additions & 2 deletions planning/planning/src/chronicles/printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ impl<'a> Printer<'a> {
println!()
}

if let Some(cost) = ch.cost {
println!(" cost: {cost}")
if let Some(cost) = &ch.cost {
println!(" cost: {cost:?}")
}

println!()
Expand Down
2 changes: 1 addition & 1 deletion planning/planning/src/parsing/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ fn read_chronicle_template(
// TODO: here the cost is simply 1 for any primitive action
let cost = match pddl.kind() {
ChronicleKind::Problem | ChronicleKind::Method => None,
ChronicleKind::Action | ChronicleKind::DurativeAction => Some(1),
ChronicleKind::Action | ChronicleKind::DurativeAction => Some(Cost::Fixed(1)),
};

let mut ch = Chronicle {
Expand Down
2 changes: 1 addition & 1 deletion planning/unified/plugin/up_aries_tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def test_action_costs():
a = InstantaneousAction("a")
a.add_precondition(Not(pa))
a.add_effect(pa, True)
b = InstantaneousAction("b", k=IntType())
b = InstantaneousAction("b", k=IntType(lower_bound=0))
b.add_precondition(Not(pb))
b.add_effect(pb, True)
costs = {a: 10, b: b.k}
Expand Down

0 comments on commit 4f455eb

Please sign in to comment.