Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Useless Supports #156

Merged
merged 6 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion planning/planners/src/encode/symmetry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::Model;
/// 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", "psp");
pub static USELESS_SUPPORTS: EnvParam<bool> = EnvParam::new("ARIES_USELESS_SUPPORTS", "false");
pub static USELESS_SUPPORTS: EnvParam<bool> = EnvParam::new("ARIES_USELESS_SUPPORTS", "true");
pub static PSP_ABSTRACTION_HIERARCHY: EnvParam<bool> = EnvParam::new("ARIES_PSP_ABSTRACTION_HIERARCHY", "true");

/// The type of symmetry breaking to apply to problems.
Expand Down
137 changes: 73 additions & 64 deletions planning/planning/src/chronicles/analysis/detrimental_supports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,15 @@ pub fn find_useless_supports(pb: &Problem) -> HashSet<CausalSupport> {
}
}

let mut useful_values = HashSet::new(); // TODO: extend with goals

for ch in &pb.templates {
for cond in &ch.chronicle.conditions {
let gval = value_of(&cond.state_var.fluent, &cond.state_var.args, cond.value);
useful_values.insert(gval);
}
}
// Extract all values that are useful.
// A value is useful if it is a goal or a condition of a Chronicle template.
let useful_values = pb
Shi-Raida marked this conversation as resolved.
Show resolved Hide resolved
.chronicles
.iter()
.flat_map(|ch| ch.chronicle.conditions.iter())
.chain(pb.templates.iter().flat_map(|t| t.chronicle.conditions.iter()))
.map(|c| value_of(&c.state_var.fluent, &c.state_var.args, c.value))
.collect::<HashSet<_>>();

println!("Useful values:");
for v in &useful_values {
Expand Down Expand Up @@ -294,7 +295,14 @@ fn gather_detrimental_supports(
useful_values: &HashSet<GAtom>,
detrimentals: &mut HashSet<CausalSupport>,
) {
let mut external_contributors = HashSet::new();
let mut external_contributors = pb
.chronicles
.iter()
.flat_map(|ch| ch.chronicle.conditions.iter())
.filter(|c| c.state_var.fluent.as_ref() == fluent) // Goals on the fluent
.map(|c| value_of(&c.state_var.fluent, &c.state_var.args, c.value))
.collect::<HashSet<_>>();

for ch in &pb.templates {
let mut conds = HashMap::new();
for c in &ch.chronicle.conditions {
Expand All @@ -310,8 +318,6 @@ fn gather_detrimental_supports(
let key = (&e.state_var, e.transition_start);
if !conds.contains_key(&key) {
return; // the fluent is not continuous
} else {
// conds.remove(&key).unwrap();
}
} else {
let EffectOp::Assign(value) = e.operation else { panic!() };
Expand All @@ -337,18 +343,6 @@ fn gather_detrimental_supports(
println!(" - {}", c.format(pb));
}

// gather all values that are affected by non-optional chronicles (ie, non-templates)
let mut initial_values = HashSet::new();
for ch in &pb.chronicles {
for e in &ch.chronicle.effects {
if e.state_var.fluent.as_ref() == fluent {
let EffectOp::Assign(value) = e.operation else { panic!() };
let atom = value_of(&e.state_var.fluent, &e.state_var.args, value);
initial_values.insert(atom.value);
}
}
}

let conditions = find_conditions(fluent, pb);
let transitions = find_transitions(fluent, pb);
let supporters = |val: &GAtom| transitions.iter().filter(move |t| &t.post == val).collect_vec();
Expand Down Expand Up @@ -383,45 +377,60 @@ fn gather_detrimental_supports(
}
}
}
} /* Deactivated as it may be incorrect in some lifted cases where unless checking that the return (supported)
transition ends up at the same value
else {
// detect pattern where the is a single transition value:
// - always is the initial value
// - is not useful in itself
// - is the source/target of all transition to/from useful values
let from_useful = transitions
.iter()
.filter(|t| external_contributors.contains(&t.pre))
.collect_vec();
let post_useful: HashSet<_> = from_useful.iter().map(|t| &t.post).collect();
let to_useful = transitions
.iter()
.filter(|t| external_contributors.contains(&t.post))
.collect_vec();
let pre_useful: HashSet<_> = to_useful.iter().map(|t| &t.pre).collect();
if post_useful.len() == 1 && post_useful == pre_useful {
let transition_value = post_useful.iter().next().copied().unwrap();
// true if there is a unique tranisition value (ie, it does not correspond to a type that could have ultiple values)
let transition_value_is_unique = transition_value.value.is_constant();
// true if the state variables always start from the initial value
let transition_is_initial = initial_values.iter().all(|a| a == &transition_value.value);

if !external_contributors.contains(&transition_value) && transition_value_is_unique && transition_is_initial
{
// we have our single transition value,
// mark as detrimental all transitions to it.
// proof: any transition to it must be preceded by transition from it (with no other side effects)
for t1 in supporters(transition_value) {
for t2 in supported(transition_value) {
if t1.post == t2.pre {
// this is a transition from `transition_value -> useful_value -> transition_value`
detrimentals.insert(CausalSupport::transitive(t1, t2));
}
}
}
}

}
} */
} else {
// detect pattern where there is a single transition value:
// - always is the initial value
// - is not useful in itself
// - is the source/target of all transition to/from useful values

// gather all values that are affected by non-optional chronicles (ie, non-templates)
let mut initial_values = HashSet::new();
for ch in &pb.chronicles {
for e in &ch.chronicle.effects {
if e.state_var.fluent.as_ref() == fluent {
let EffectOp::Assign(value) = e.operation else { panic!() };
let atom = value_of(&e.state_var.fluent, &e.state_var.args, value);
initial_values.insert(atom.value);
}
}
}
let from_useful = transitions
.iter()
.filter(|t| external_contributors.contains(&t.pre))
.collect_vec();
let post_useful: HashSet<_> = from_useful.iter().map(|t| &t.post).collect();
let to_useful = transitions
.iter()
.filter(|t| external_contributors.contains(&t.post))
.collect_vec();
let pre_useful: HashSet<_> = to_useful.iter().map(|t| &t.pre).collect();
if post_useful.len() == 1 && post_useful == pre_useful {
let transition_value = post_useful.iter().next().copied().unwrap();
// true if there is a unique transition value (ie, it does not correspond to a type that could have multiple values)
let transition_value_is_unique = transition_value.value.is_constant();
// true if the state variables always start from the initial value
let transition_is_initial = initial_values.iter().all(|a| a == &transition_value.value);

if !external_contributors.contains(transition_value) && transition_value_is_unique && transition_is_initial
{
// We have our single transition value.
// Mark all transition cycles `useful -> transition_value -> useful` as detrimental.
// Proof: There is not need to go through the transition value to reach
// the useful value again with no other intermediate value.
for t1 in supporters(transition_value) {
for t2 in supported(transition_value) {
if t1.post == t2.pre {
println!(
" => Detrimental: {} -> {} -> {}",
t1.pre.format(pb),
t1.post.format(pb),
t2.post.format(pb)
);
detrimentals.insert(CausalSupport::transitive(t1, t2));
}
}
}
}
}
}
}
Loading