use serde::Serialize;
pub type Name = String;
pub type Variable = String;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
pub enum Requirement {
Strips,
Typing,
NegativePreconditions,
DisjunctivePreconditions,
Equality,
ExistentialPreconditions,
UniversalPreconditions,
QuantifiedPreconditions,
ConditionalEffects,
Fluents,
NumericFluents,
Adl,
DurativeActions,
DurationInequalities,
TimedInitialLiterals,
Preferences,
Constraints,
ActionCosts,
GoalUtilities,
DerivedPredicates,
DomainAxioms,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct TypedGroup<T: Serialize> {
pub items: Vec<T>,
pub type_name: Option<Name>,
}
pub type TypedList<T> = Vec<TypedGroup<T>>;
pub type TypeDeclarations = TypedList<Name>;
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct PredicateDecl {
pub name: Name,
pub parameters: TypedList<Variable>,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct FunctionDecl {
pub name: Name,
pub parameters: TypedList<Variable>,
pub return_type: Option<Name>,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub enum NumericExpr {
Number(f64),
FunctionCall(FunctionTerm),
BinaryOp {
op: BinaryOp,
left: Box<NumericExpr>,
right: Box<NumericExpr>,
},
Negate(Box<NumericExpr>),
TotalTime,
Duration,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub enum BinaryOp {
Add,
Sub,
Mul,
Div,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct FunctionTerm {
pub name: Name,
pub args: Vec<Term>,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub enum Term {
Name(Name),
Variable(Variable),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub enum CompareOp {
Lt,
Lte,
Gt,
Gte,
Eq,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub enum Condition {
And(Vec<Condition>),
Or(Vec<Condition>),
Not(Box<Condition>),
Imply(Box<Condition>, Box<Condition>),
Forall {
variables: TypedList<Variable>,
condition: Box<Condition>,
},
Exists {
variables: TypedList<Variable>,
condition: Box<Condition>,
},
Predicate(AtomicFormula),
Equals(Term, Term),
NumericComparison {
op: CompareOp,
left: NumericExpr,
right: NumericExpr,
},
Preference {
name: Option<Name>,
condition: Box<Condition>,
},
AtStart(Box<Condition>),
AtEnd(Box<Condition>),
OverAll(Box<Condition>),
Always(Box<Condition>),
Sometime(Box<Condition>),
AtMostOnce(Box<Condition>),
Within(f64, Box<Condition>),
SometimeBefore(Box<Condition>, Box<Condition>),
SometimeAfter(Box<Condition>, Box<Condition>),
AlwaysWithin(f64, Box<Condition>, Box<Condition>),
HoldDuring(f64, f64, Box<Condition>),
HoldAfter(f64, Box<Condition>),
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct AtomicFormula {
pub name: Name,
pub args: Vec<Term>,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub enum Effect {
And(Vec<Effect>),
Predicate(AtomicFormula),
NotPredicate(AtomicFormula),
Forall {
variables: TypedList<Variable>,
effect: Box<Effect>,
},
When {
condition: Condition,
effect: Box<Effect>,
},
NumericAssign {
op: AssignOp,
function: FunctionTerm,
expr: NumericExpr,
},
AtStart(Box<Effect>),
AtEnd(Box<Effect>),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub enum AssignOp {
Assign,
ScaleUp,
ScaleDown,
Increase,
Decrease,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub enum DurationConstraint {
And(Vec<DurationConstraint>),
Cmp {
op: CompareOp,
expr: NumericExpr,
},
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct BasicAction {
pub name: Name,
pub parameters: TypedList<Variable>,
pub precondition: Option<Condition>,
pub effect: Option<Effect>,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct DurativeAction {
pub name: Name,
pub parameters: TypedList<Variable>,
pub duration: DurationConstraint,
pub condition: Option<Condition>,
pub effect: Option<Effect>,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct DerivedPredicate {
pub predicate: AtomicFormula,
pub condition: Condition,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub enum InitElement {
Predicate(AtomicFormula),
NotPredicate(AtomicFormula),
NumericAssignment(FunctionTerm, f64),
At(f64, Box<InitElement>),
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub enum Optimization {
Minimize,
Maximize,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct MetricSpec {
pub optimization: Optimization,
pub expr: NumericExpr,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct Domain {
pub name: Name,
pub requirements: Vec<Requirement>,
pub types: TypeDeclarations,
pub constants: TypedList<Name>,
pub predicates: Vec<PredicateDecl>,
pub functions: Vec<FunctionDecl>,
pub actions: Vec<BasicAction>,
pub durative_actions: Vec<DurativeAction>,
pub derived_predicates: Vec<DerivedPredicate>,
}
impl Domain {
pub fn sort_alphabetically(&mut self) {
for group in &mut self.types {
group.items.sort();
}
for group in &mut self.constants {
group.items.sort();
}
self.predicates.sort_by(|a, b| a.name.cmp(&b.name));
self.functions.sort_by(|a, b| a.name.cmp(&b.name));
self.actions.sort_by(|a, b| a.name.cmp(&b.name));
self.durative_actions.sort_by(|a, b| a.name.cmp(&b.name));
self.derived_predicates
.sort_by(|a, b| a.predicate.name.cmp(&b.predicate.name));
}
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct Problem {
pub name: Name,
pub domain_name: Name,
pub requirements: Vec<Requirement>,
pub objects: TypedList<Name>,
pub init: Vec<InitElement>,
pub goal: Condition,
pub metric: Option<MetricSpec>,
pub constraints: Option<Condition>,
}
impl Problem {
pub fn sort_alphabetically(&mut self) {
for group in &mut self.objects {
group.items.sort();
}
self.init.sort_by_key(init_sort_key);
}
}
fn format_atomic_formula(af: &AtomicFormula) -> String {
if af.args.is_empty() {
af.name.clone()
} else {
let args: Vec<&str> = af.args.iter().map(term_name).collect();
format!("{}({})", af.name, args.join(","))
}
}
fn format_function_term(ft: &FunctionTerm) -> String {
if ft.args.is_empty() {
ft.name.clone()
} else {
let args: Vec<&str> = ft.args.iter().map(term_name).collect();
format!("{}({})", ft.name, args.join(","))
}
}
fn init_sort_key(e: &InitElement) -> (u8, String) {
match e {
InitElement::Predicate(af) => (0, format_atomic_formula(af)),
InitElement::NotPredicate(af) => (1, format_atomic_formula(af)),
InitElement::NumericAssignment(ft, _) => (2, format_function_term(ft)),
InitElement::At(t, inner) => {
let (_, s) = init_sort_key(inner);
(3, format!("{t:.6}{s}"))
}
}
}
fn term_name(t: &Term) -> &str {
match t {
Term::Name(n) => n.as_str(),
Term::Variable(v) => v.as_str(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn domain_sort_orders_all_named_declarations() {
let mut domain = Domain {
name: "d".to_string(),
requirements: Vec::new(),
types: vec![TypedGroup {
items: vec!["z".to_string(), "a".to_string()],
type_name: None,
}],
constants: vec![TypedGroup {
items: vec!["c2".to_string(), "c1".to_string()],
type_name: None,
}],
predicates: vec![
PredicateDecl {
name: "zpred".to_string(),
parameters: Vec::new(),
},
PredicateDecl {
name: "apred".to_string(),
parameters: Vec::new(),
},
],
functions: vec![
FunctionDecl {
name: "zfunc".to_string(),
parameters: Vec::new(),
return_type: None,
},
FunctionDecl {
name: "afunc".to_string(),
parameters: Vec::new(),
return_type: None,
},
],
actions: vec![
BasicAction {
name: "zact".to_string(),
parameters: Vec::new(),
precondition: None,
effect: None,
},
BasicAction {
name: "aact".to_string(),
parameters: Vec::new(),
precondition: None,
effect: None,
},
],
durative_actions: vec![
DurativeAction {
name: "zdur".to_string(),
parameters: Vec::new(),
duration: DurationConstraint::Cmp {
op: CompareOp::Eq,
expr: NumericExpr::Number(1.0),
},
condition: None,
effect: None,
},
DurativeAction {
name: "adur".to_string(),
parameters: Vec::new(),
duration: DurationConstraint::Cmp {
op: CompareOp::Eq,
expr: NumericExpr::Number(1.0),
},
condition: None,
effect: None,
},
],
derived_predicates: vec![
DerivedPredicate {
predicate: AtomicFormula {
name: "zderived".to_string(),
args: Vec::new(),
},
condition: Condition::And(Vec::new()),
},
DerivedPredicate {
predicate: AtomicFormula {
name: "aderived".to_string(),
args: Vec::new(),
},
condition: Condition::And(Vec::new()),
},
],
};
domain.sort_alphabetically();
assert_eq!(domain.types[0].items, ["a", "z"]);
assert_eq!(domain.constants[0].items, ["c1", "c2"]);
assert_eq!(domain.predicates[0].name, "apred");
assert_eq!(domain.functions[0].name, "afunc");
assert_eq!(domain.actions[0].name, "aact");
assert_eq!(domain.durative_actions[0].name, "adur");
assert_eq!(domain.derived_predicates[0].predicate.name, "aderived");
}
#[test]
fn problem_sort_handles_zero_arity_predicates_and_variable_terms() {
let mut problem = Problem {
name: "p".to_string(),
domain_name: "d".to_string(),
requirements: Vec::new(),
objects: Vec::new(),
init: vec![
InitElement::NumericAssignment(
FunctionTerm {
name: "cost".to_string(),
args: vec![Term::Variable("?x".to_string())],
},
1.0,
),
InitElement::Predicate(AtomicFormula {
name: "ready".to_string(),
args: Vec::new(),
}),
],
goal: Condition::And(Vec::new()),
metric: None,
constraints: None,
};
problem.sort_alphabetically();
assert!(matches!(problem.init[0], InitElement::Predicate(_)));
assert!(matches!(
problem.init[1],
InitElement::NumericAssignment(_, _)
));
}
}