Skip to content

Commit

Permalink
feat(planning): In PSP symmetry-braking, ensure actions are not inser…
Browse files Browse the repository at this point in the history
…ted only for detrimental supports.
  • Loading branch information
arbimo committed Apr 6, 2024
1 parent 7509e13 commit a9e3ec5
Show file tree
Hide file tree
Showing 11 changed files with 674 additions and 160 deletions.
2 changes: 1 addition & 1 deletion planning/planners/src/bin/lcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use aries::core::state::Domains;
use aries::utils::input::Input;
use aries_planners::solver::{format_plan, solve, SolverResult};
use aries_planners::solver::{Metric, Strat};
use aries_planning::chronicles::analysis::hierarchical_is_non_recursive;
use aries_planning::chronicles::analysis::hierarchy::hierarchical_is_non_recursive;
use aries_planning::chronicles::FiniteProblem;
use aries_planning::parsing::pddl::{find_domain_of, parse_pddl_domain, parse_pddl_problem, PddlFeature};
use aries_planning::parsing::pddl_to_chronicles;
Expand Down
4 changes: 2 additions & 2 deletions planning/planners/src/encode.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
//! Functions whose purpose is to encode a planning problem (represented with chronicles)
//! into a combinatorial problem from Aries core.
mod fluent_hierarchy;
mod symmetry;

use crate::encode::symmetry::SYMMETRY_BREAKING;
use crate::encoding::*;
use crate::solver::{init_solver, Metric};
use crate::Model;
Expand All @@ -21,7 +23,6 @@ use env_param::EnvParam;
use std::collections::{HashMap, HashSet};
use std::convert::TryFrom;
use std::ptr;
use crate::encode::symmetry::SYMMETRY_BREAKING;

/// Parameter that activates the debug view of the resource constraints.
/// The parameter is loaded from the environment variable `ARIES_RESOURCE_CONSTRAINT_DEBUG`.
Expand Down Expand Up @@ -391,7 +392,6 @@ fn enforce_refinement(t: TaskRef, supporters: Vec<TaskRef>, model: &mut Model) {
}
}


/// Encode a metric in the problem and returns an integer that should minimized in order to optimize the metric.
pub fn add_metric(pb: &FiniteProblem, model: &mut Model, metric: Metric) -> IAtom {
match metric {
Expand Down
71 changes: 55 additions & 16 deletions planning/planners/src/encode/symmetry.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
use crate::encoding::{ChronicleId, CondID, Encoding, Tag};
use crate::encode::analysis;
use crate::encode::fluent_hierarchy::hierarchy;
use crate::encoding::{conditions, ChronicleId, CondID, EffID, Encoding, Tag};
use crate::fmt::format_partial_name;
use analysis::CausalSupport;
use aries::core::Lit;
use aries::model::extensions::AssignmentExt;
use aries::model::lang::expr::{and, f_leq, implies, or};
use aries_planning::chronicles::analysis::TemplateCondID;
use aries_planning::chronicles::{ChronicleOrigin, FiniteProblem};
use env_param::EnvParam;
use itertools::Itertools;
Expand All @@ -13,7 +17,8 @@ use crate::Model;
/// Parameter that defines the symmetry breaking strategy to use.
/// The value of this parameter is loaded from the environment variable `ARIES_LCP_SYMMETRY_BREAKING`.
/// Possible values are `none` and `simple` (default).
pub static SYMMETRY_BREAKING: EnvParam<SymmetryBreakingType> = EnvParam::new("ARIES_LCP_SYMMETRY_BREAKING", "simple");
pub static SYMMETRY_BREAKING: EnvParam<SymmetryBreakingType> = EnvParam::new("ARIES_LCP_SYMMETRY_BREAKING", "psp");
pub static USELESS_SUPPORTS: EnvParam<bool> = EnvParam::new("ARIES_USELESS_SUPPORTS", "true");

/// The type of symmetry breaking to apply to problems.
#[derive(Copy, Clone)]
Expand Down Expand Up @@ -70,6 +75,23 @@ pub fn add_symmetry_breaking(pb: &FiniteProblem, model: &mut Model, tpe: Symmetr
}

fn add_plan_space_symmetry_breaking(pb: &FiniteProblem, model: &mut Model, encoding: &Encoding) {
let discard_useless_supports = USELESS_SUPPORTS.get();
let template_id = |instance_id: usize| match pb.chronicles[instance_id].origin {
ChronicleOrigin::FreeAction { template_id, .. } => Some(template_id),
_ => None,
};
let is_primary_support = |c: CondID, eff: EffID| {
let Some(c_template) = template_id(c.instance_id) else {
return true;
};
let Some(e_template) = template_id(eff.instance_id) else {
return true;
};
let causal = CausalSupport::new(e_template, eff.eff_id, c_template, c.cond_id);
// return true if the potential causal link is not flagged as useless
!pb.meta.detrimental_supports.contains(&causal)
};

struct ActionOrigin {
template: usize,
gen: usize,
Expand Down Expand Up @@ -97,8 +119,21 @@ fn add_plan_space_symmetry_breaking(pb: &FiniteProblem, model: &mut Model, encod
lit: Lit,
}
type TemplateID = usize;
let templates = pb
.chronicles
.iter()
.filter_map(|c| match c.origin {
ChronicleOrigin::FreeAction { template_id, .. } => Some(template_id),
_ => None,
})
.sorted()
.dedup()
.collect_vec();
let mut causal_link: HashMap<(ChronicleId, CondID), Lit> = Default::default();
let mut conds_by_templates: HashMap<TemplateID, HashSet<CondID>> = Default::default();
for template in &templates {
conds_by_templates.insert(*template, HashSet::new());
}
for &(k, v) in &encoding.tags {
let Tag::Support(cond, eff) = k else {
panic!("Unsupported tag: {k:?}");
Expand All @@ -111,8 +146,11 @@ fn add_plan_space_symmetry_breaking(pb: &FiniteProblem, model: &mut Model, encod
let ChronicleOrigin::FreeAction { template_id, .. } = ch.origin else {
continue;
};
if discard_useless_supports && !is_primary_support(cond, eff) {
continue; // remove non-primary supports
}
// record that this template may contribute to this condition
conds_by_templates.entry(template_id).or_default().insert(cond);
conds_by_templates.get_mut(&template_id).unwrap().insert(cond);
// non-optional literal that is true iff the causal link is active
let link_active = model.reify(and([v, model.presence_literal(v.variable())]));
// list of outgoing causal links of the supporting action
Expand All @@ -123,14 +161,15 @@ fn add_plan_space_symmetry_breaking(pb: &FiniteProblem, model: &mut Model, encod
conds_by_templates.into_iter().map(|(k, v)| (k, sort(v))).collect();
let supports = |ch: ChronicleId, cond: CondID| causal_link.get(&(ch, cond)).copied().unwrap_or(Lit::FALSE);

for (template_id, conditions) in &conds_by_templates {
for template_id in &templates {
let conditions = &conds_by_templates[template_id];
let instances: Vec<_> = actions
.iter()
.filter_map(|(id, orig)| if orig.template == *template_id { Some(id) } else { None })
.sorted()
.collect();

if let Some(ch) = instances.get(0) {
if let Some(ch) = instances.first() {
let ch = &pb.chronicles[**ch];
let s = format_partial_name(&ch.chronicle.name, model).unwrap();
println!("{template_id} {s} ({})", instances.len());
Expand All @@ -141,11 +180,10 @@ fn add_plan_space_symmetry_breaking(pb: &FiniteProblem, model: &mut Model, encod
}

for (i, instance) in instances.iter().copied().enumerate() {
let mut clause = Vec::with_capacity(64);
if i > 0 {
let prev = instances[i - 1];

let mut clause = Vec::with_capacity(64);

// the chronicle is allowed to support a condition only if the previous chronicle
// supports a condition at an earlier level
for (cond_index, cond) in conditions.iter().enumerate() {
Expand All @@ -157,19 +195,20 @@ fn add_plan_space_symmetry_breaking(pb: &FiniteProblem, model: &mut Model, encod
}
model.enforce(or(clause.as_slice()), []);
}

clause.clear();
// enforce that a chronicle be present only if it supports at least one condition
clause.push(!pb.chronicles[*instance].chronicle.presence);
for cond in conditions {
clause.push(supports(*instance, *cond))
}
model.enforce(or(clause.as_slice()), []);
}
clause.clear();
// enforce that a chronicle be present only if it supports at least one condition
clause.push(!pb.chronicles[*instance].chronicle.presence);
for cond in conditions {
clause.push(supports(*instance, *cond))
}
model.enforce(or(clause.as_slice()), []);
}
}

println!("\n================\n");
// println!("\n================\n");
// hierarchy(pb);
// println!("\n================\n");
// std::process::exit(1)
}

Expand Down
15 changes: 14 additions & 1 deletion planning/planners/src/solver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ pub fn solve(
Printer::print_problem(&base_problem);
}

let metadata = Arc::new(analysis::analyse(&base_problem));

let mut best_cost = INT_CST_MAX + 1;

let start = Instant::now();
Expand All @@ -106,6 +108,7 @@ pub fn solve(
horizon: base_problem.context.horizon(),
makespan_ub: base_problem.context.makespan_ub(),
chronicles: base_problem.chronicles.clone(),
meta: metadata.clone(),
};
let depth_string = if depth == u32::MAX {
"∞".to_string()
Expand Down Expand Up @@ -286,7 +289,17 @@ fn causal_brancher(problem: Arc<FiniteProblem>, encoding: Arc<Encoding>) -> Bran
let causal = ManualCausalSearch::new(problem, encoding);

// conflict directed search on tagged literals only
let conflict = Box::new(ConflictBasedBrancher::new(branching_literals));
let mut conflict = Box::new(ConflictBasedBrancher::new(branching_literals.clone()));
// when possible, set the value of the prefered value of the branching literal
for l in branching_literals {
let var = l.variable();
if l == Lit::gt(var, 0) {
conflict.set_default_value(var, 1);
} else if l == Lit::leq(var, 0) {
conflict.set_default_value(var, 0);
}
}
// std::process::exit(0);

// if all tagged literals are set, fallback to standard activity-based search
let act: Box<ActivityBrancher<VarLabel>> =
Expand Down
Loading

0 comments on commit a9e3ec5

Please sign in to comment.