Skip to content

Commit

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

fn set_cost(&mut self, cost: &Expression) -> Result<(), Error> {
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."),
}
let value = self.reify(cost, None)?;
match value {
Atom::Int(i) => {
self.chronicle.cost = Some(i);
self.chronicle.constraints.push(Constraint::leq(IAtom::ZERO, i));
}
_ => bail!("Unsupported cost expression: {cost:?}"),
_ => bail!("Cost must be an integer value."),
};

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
12 changes: 2 additions & 10 deletions planning/planners/src/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -429,12 +429,6 @@ pub fn add_metric(pb: &FiniteProblem, model: &mut Model, metric: Metric) -> IAto
let mut costs = Vec::with_capacity(8);
for (ch_id, ch) in pb.chronicles.iter().enumerate() {
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 @@ -443,10 +437,8 @@ 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),
};
let bounds = model.domain_of(*cost);
assert!(bounds.0 >= 0, "A chronicle could have a negative cost");
model
.new_optional_ivar(bounds.0, bounds.1, p, Container::Instance(ch_id).var(VarType::Cost))
.or_zero(p)
Expand Down
24 changes: 3 additions & 21 deletions planning/planning/src/chronicles/concrete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::sync::Arc;

use crate::chronicles::constraints::Constraint;
use crate::chronicles::Fluent;
use aries::core::{IntCst, Lit, VarRef};
use aries::core::{Lit, VarRef};
use aries::model::lang::linear::{LinearSum, LinearTerm};
use aries::model::lang::*;

Expand Down Expand Up @@ -280,24 +280,6 @@ 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 @@ -511,7 +493,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<Cost>,
pub cost: Option<IAtom>,
}

struct VarSet(HashSet<VarRef>);
Expand Down Expand Up @@ -643,7 +625,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.as_ref().map(|c| c.substitute(s)),
cost: self.cost.map(|c| s.isub(c)),
}
}
}
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(Cost::Fixed(1)),
ChronicleKind::Action | ChronicleKind::DurativeAction => Some(1.into()),
};

let mut ch = Chronicle {
Expand Down

0 comments on commit 08ff614

Please sign in to comment.