Skip to content

Commit 1b8fb93

Browse files
committed
chore(ape): clean up solve-finite implementation
1 parent 0ec0f0d commit 1b8fb93

4 files changed

Lines changed: 77 additions & 87 deletions

File tree

planning/engine/src/generate.rs

Lines changed: 74 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,47 +2,41 @@ use std::{collections::BTreeMap, time::Instant};
22

33
use aries::{core::state::Evaluable, prelude::*};
44
use aries_plan_engine::{
5-
encode::{
6-
encoding::Encoding,
7-
tags::{Tag, format_culprit_set},
5+
encode::{encoding::Encoding, tags::Tag},
6+
plans::{
7+
Operation,
8+
lifted_plan::{LiftedPlan, ObjectOrVariable},
89
},
9-
plans::lifted_plan::{self, LiftedPlan},
1010
};
11-
use derive_more::derive::Display;
12-
use planx::{Model, Res, errors::*};
11+
use planx::{Model, Res, Sym};
1312
use timelines::{Sched, explain::ExplainableSolver};
1413

15-
use crate::optimize_plan;
14+
use crate::optimize_plan::{self, Objective};
1615

1716
pub type RelaxableConstraint = Tag;
1817

1918
#[derive(clap::Args, Debug, Clone)]
2019
pub struct Options {
20+
/// Defines the maximum number of instances per action template.
21+
///
22+
/// For instance, if set to 3, the resulting plan may have *at most* three instances
23+
/// of a `pick` action and at most 3 instances of a `drop` action.
2124
#[arg(short, long)]
22-
pub max_depth: usize,
25+
pub max_instances: usize,
2326

24-
// #[arg(short, long, num_args(1..))]
25-
// pub relaxation: Vec<Relaxation>,
26-
#[arg(short, long, default_value("plan-length"))]
27+
/// Defines the objective to be minimized
28+
#[arg(short, long, default_value("original"))]
2729
pub objective: Objective,
28-
}
2930

30-
// #[derive(clap::ValueEnum, Debug, Clone, Copy, Display, PartialEq, PartialOrd, Eq, Ord)]
31-
// pub enum Relaxation {
32-
// ActionPresence,
33-
// StartTime,
34-
// }
35-
36-
#[derive(clap::ValueEnum, Debug, Clone, Copy, Display, PartialEq, Eq, PartialOrd, Ord)]
37-
pub enum Objective {
38-
/// The objective value defined in the domain
39-
Original,
40-
PlanLength,
41-
Makespan,
31+
/// If set, the planner will try tro find the optimal solution
32+
#[arg(long)]
33+
pub optimize: bool,
4234
}
4335

4436
pub fn solve_finite_planning_problem(model: &Model, options: &Options) -> Res<()> {
45-
let plan = &lifted_plan::new_empty_lifted_plan(model, BTreeMap::new(), options.max_depth)?;
37+
// create a dummy plan with the appropriate number of actions
38+
// this is temporary a workaround to reuse the existing `optimize_plan` facilities
39+
let plan = &new_empty_lifted_plan(model, BTreeMap::new(), options.max_instances)?;
4640

4741
let start = Instant::now();
4842
let (mut solver, encoding, _sched) = encode_finite_planning_problem(model, plan, options)?;
@@ -51,37 +45,31 @@ pub fn solve_finite_planning_problem(model: &Model, options: &Options) -> Res<()
5145

5246
let objective = encoding.objective.unwrap(); //TODO: error message
5347

48+
// set the objective to a constant if we are not optimizing
49+
let solver_objective = if options.optimize { objective } else { 0.into() };
50+
5451
let print = |sol: &Solution| {
55-
println!("==== Plan (objective: {}) =====", objective.evaluate(sol).unwrap());
52+
println!("\n==== Plan (objective: {}) =====", objective.evaluate(sol).unwrap());
5653
println!("{}\n", encoding.plan(sol));
5754
};
5855

59-
if let Some(solution) = solver.find_optimal(objective, &print) {
60-
println!("\n> Found optimal solution:");
56+
if let Some(solution) = solver.find_optimal(solver_objective, &print) {
57+
println!("\n> Found {}solution:", if options.optimize { "optimal " } else { "" });
6158
print(&solution);
6259
} else {
6360
println!("No solution !!!!");
64-
for mus in solver.muses() {
65-
let msg = format_culprit_set(Message::error("Invalid in all relaxation"), &mus, model, plan);
66-
println!("\n{msg}\n");
67-
}
6861
}
6962
Ok(())
7063
}
7164

72-
pub fn encode_finite_planning_problem(
65+
fn encode_finite_planning_problem(
7366
model: &Model,
7467
lifted_plan: &LiftedPlan,
7568
options: &Options,
7669
) -> Res<(ExplainableSolver<RelaxableConstraint>, Encoding, Sched)> {
7770
// TODO: make specific function.
7871
// - ability to specify explanations vocabulary via RelaxableConstraint (Tag), including removing (pre)conditions (like in domain repair).
7972

80-
let objective = match options.objective {
81-
Objective::Original => optimize_plan::Objective::Original,
82-
Objective::PlanLength => optimize_plan::Objective::PlanLength,
83-
Objective::Makespan => optimize_plan::Objective::Makespan,
84-
};
8573
optimize_plan::encode_plan_optimization_problem(
8674
model,
8775
lifted_plan,
@@ -90,7 +78,54 @@ pub fn encode_finite_planning_problem(
9078
optimize_plan::Relaxation::ActionPresence,
9179
optimize_plan::Relaxation::StartTime,
9280
],
93-
objective,
81+
objective: options.objective,
9482
},
9583
)
9684
}
85+
86+
fn new_empty_lifted_plan(
87+
model: &Model,
88+
a_instances_per_template: BTreeMap<planx::ActionRef, usize>,
89+
a_instances_default: usize,
90+
) -> Res<LiftedPlan> {
91+
let top_type = model.env.types.top_user_type();
92+
use planx::errors::*;
93+
94+
let num_instances = |a_name| *a_instances_per_template.get(a_name).unwrap_or(&a_instances_default);
95+
96+
// all actions in the plan
97+
let mut operations = Vec::with_capacity(model.actions.iter().map(|a| num_instances(&a.name)).sum());
98+
99+
// all variables appearing in the plan
100+
let mut variables = BTreeMap::new();
101+
102+
for a in model.actions.iter() {
103+
for aid in 0..num_instances(&a.name) {
104+
let mut arguments = Vec::with_capacity(a.parameters.len());
105+
106+
for param in a.parameters.iter() {
107+
let name = Sym::with_source(
108+
format!("{}.{}.{}", a.name.canonical_str(), aid, param.name().canonical_str()),
109+
param.name().span_or_default(),
110+
);
111+
let tpe = if let planx::Type::User(tpe) = param.tpe() {
112+
tpe.to_single_type().unwrap_or_else(|| top_type.clone())
113+
} else {
114+
top_type.clone()
115+
};
116+
117+
variables.insert(name.clone(), tpe);
118+
119+
arguments.push(ObjectOrVariable::Variable { name });
120+
}
121+
operations.push(Operation {
122+
start: 0,
123+
duration: 0,
124+
action_ref: a.name.clone(),
125+
arguments,
126+
span: None,
127+
});
128+
}
129+
}
130+
Ok(LiftedPlan { operations, variables })
131+
}

planning/engine/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ pub struct Problem {
383383
/// Path to the PDDL problem file.
384384
problem: PathBuf,
385385
/// Path to the PDDL domain file.
386-
/// If not specified, we will attempt to automatically infer it based on the plan file.
386+
/// If not specified, we will attempt to automatically infer it based on the problem file.
387387
#[arg(short, long)]
388388
domain: Option<PathBuf>,
389389
}

planning/engine/src/optimize_plan.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ pub enum Relaxation {
4242
pub enum Objective {
4343
/// The objective value defined in the domain
4444
Original,
45+
/// Number of actions in the plan
4546
PlanLength,
47+
/// End time of the latest action
4648
Makespan,
4749
}
4850

planning/engine/src/plans/lifted_plan.rs

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -106,50 +106,3 @@ pub fn parse_lifted_plan(plan: &planx::pddl::Plan, model: &Model) -> Res<LiftedP
106106
}
107107
Ok(LiftedPlan { operations, variables })
108108
}
109-
110-
pub fn new_empty_lifted_plan(
111-
model: &Model,
112-
a_instances_per_template: BTreeMap<planx::ActionRef, usize>,
113-
a_instances_default: usize,
114-
) -> Res<LiftedPlan> {
115-
let top_type = model.env.types.top_user_type();
116-
use planx::errors::*;
117-
118-
let num_instances = |a_name| *a_instances_per_template.get(a_name).unwrap_or(&a_instances_default);
119-
120-
// all actions in the plan
121-
let mut operations = Vec::with_capacity(model.actions.iter().map(|a| num_instances(&a.name)).sum());
122-
123-
// all variables appearing in the plan
124-
let mut variables = BTreeMap::new();
125-
126-
for a in model.actions.iter() {
127-
for aid in 0..num_instances(&a.name) {
128-
let mut arguments = Vec::with_capacity(a.parameters.len());
129-
130-
for param in a.parameters.iter() {
131-
let name = Sym::with_source(
132-
format!("{}.{}.{}", a.name.canonical_str(), aid, param.name().canonical_str()),
133-
param.name().span_or_default(),
134-
);
135-
let tpe = if let planx::Type::User(tpe) = param.tpe() {
136-
tpe.to_single_type().unwrap_or_else(|| top_type.clone())
137-
} else {
138-
top_type.clone()
139-
};
140-
141-
variables.insert(name.clone(), tpe);
142-
143-
arguments.push(ObjectOrVariable::Variable { name });
144-
}
145-
operations.push(Operation {
146-
start: 0,
147-
duration: 0,
148-
action_ref: a.name.clone(),
149-
arguments,
150-
span: None,
151-
});
152-
}
153-
}
154-
Ok(LiftedPlan { operations, variables })
155-
}

0 commit comments

Comments
 (0)