-
Notifications
You must be signed in to change notification settings - Fork 6
/
remove_negative_preconditions.py
165 lines (132 loc) · 7.26 KB
/
remove_negative_preconditions.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
from examples.create_temporal_domain import create_temporal_domain
from examples.normalise_domain import _simplify_conditions
from pddl.domain import Domain
from pddl.effect import Effect, EffectConditional, EffectConjunction, EffectForall, EffectNegative, EffectSimple, EffectType, TimedEffect
from pddl.goal_descriptor import GoalConjunction, GoalDescriptor, GoalDisjunction, GoalNegative, GoalQuantified, GoalSimple, TimedGoal
def _negate_negative_precondition(condition : GoalNegative, negative_predicate_map : dict) -> GoalDescriptor:
"""
Negate a negative precondition.
The negative precondition is assumed to be a negated simple precondition.
param condition: The condition to negate.
param negative_predicate_map: A map from predicate names to the negated predicate.
"""
if not isinstance(condition.goal, GoalSimple):
raise ValueError("Negative precondition must be simple")
# map negation
pred_name = condition.goal.atomic_formula.name
if pred_name not in negative_predicate_map:
negative_predicate_map[pred_name] = "not_" + pred_name
negative_predicate_map[negative_predicate_map[pred_name]] = pred_name
# create positive precondition
formula = condition.goal.atomic_formula.copy()
formula.name = negative_predicate_map[formula.name]
return GoalSimple(formula)
def _negate_all_negative_preconditions(parent_condition : GoalDescriptor, negative_predicate_map : dict) -> None:
"""
Negate all negative preconditions in the condition.
Precoditions are assumed to be in negated normal form.
param condition: The condition to negate.
param negative_predicate_map: A map from predicate names to the negated predicate.
"""
if isinstance(parent_condition, GoalConjunction) or isinstance(parent_condition, GoalDisjunction):
# recurse on subconditions
for subcondition in parent_condition.goals:
_negate_all_negative_preconditions(subcondition, negative_predicate_map)
# check and possibly negate subconditions
new_conditions = []
for subcondition in parent_condition.goals:
if isinstance(subcondition, GoalNegative):
new_conditions.append(_negate_negative_precondition(subcondition, negative_predicate_map))
else:
new_conditions.append(subcondition)
parent_condition.goals = new_conditions
elif isinstance(parent_condition, GoalQuantified) or isinstance(parent_condition, TimedGoal):
# recurse on subcondition
_negate_all_negative_preconditions(parent_condition.goal, negative_predicate_map)
# check and possibly negate subcondition
if isinstance(parent_condition.goal, GoalNegative):
parent_condition.goal = _negate_negative_precondition(parent_condition.goal, negative_predicate_map)
def _update_effect(effect : Effect, negative_precondition_map) -> Effect:
"""
Update an effect so that each add and delete effect on a key of the negative_precondition_map is negated.
param effect: The effect to update.
param negative_precondition_map: A map from predicate names to the negated predicate.
"""
if isinstance(effect, EffectConjunction):
# recurse on subeffects
for subeffect in effect.effects:
_update_effect(subeffect, negative_precondition_map)
# check and possibly negate subeffects
new_effects = []
for subeffect in effect.effects:
if isinstance(subeffect, EffectSimple):
new_effects.append(_update_effect(subeffect, negative_precondition_map))
effect.effects.extend(new_effects)
elif isinstance(effect, EffectSimple):
if isinstance(effect.atomic_formula, GoalSimple):
if effect.atomic_formula.name in negative_precondition_map:
effect.atomic_formula.name = negative_precondition_map[effect.atomic_formula.name]
return effect
def _update_all_effects(parent_effect : Effect, negative_predicate_map : dict) -> None:
"""
Update effects so that each add and delete effect on a key of the negative_predicate_map is negated.
param parent_effect: The effect to update.
param negative_predicate_map: A map from predicate names to the negated predicate.
"""
if isinstance(parent_effect, EffectConjunction):
# recurse on subeffects
for subeffect in parent_effect.effects:
_update_all_effects(subeffect, negative_predicate_map)
# check and possibly negate subeffects
new_effects = []
for subeffect in parent_effect.effects:
if isinstance(subeffect, EffectNegative) and subeffect.formula.name in negative_predicate_map:
new_effect = EffectSimple(subeffect.formula.copy())
new_effect.formula.name = negative_predicate_map[subeffect.formula.name]
new_effects.append(new_effect)
elif isinstance(subeffect, EffectSimple) and subeffect.formula.name in negative_predicate_map:
new_effect = EffectNegative(subeffect.formula.copy())
new_effect.formula.name = negative_predicate_map[subeffect.formula.name]
new_effects.append(new_effect)
parent_effect.effects.extend(new_effects)
if isinstance(parent_effect, EffectForall) or isinstance(parent_effect, EffectConditional) or isinstance(parent_effect, TimedEffect):
# recurse on subeffect
_update_all_effects(parent_effect.effect, negative_predicate_map)
# check and possibly negate subeffect
if isinstance(parent_effect.effect, EffectSimple) and parent_effect.effect.formula.name in negative_predicate_map:
# make negated effect
if parent_effect.effect.effect_type == EffectType.NEGATIVE:
new_effect = EffectSimple(parent_effect.effect.formula.copy())
else: new_effect = EffectNegative(parent_effect.effect.formula.copy())
new_effect.formula.name = negative_predicate_map[parent_effect.effect.formula.name]
# update parent effect
parent_effect.effect = EffectConjunction([new_effect, parent_effect.effect])
def remove_negative_preconditions(domain: Domain) -> None:
"""
Remove negative preconditions from the domain.
param domain: The domain to remove negative preconditions from.
"""
# put conditions in negated normal form
_simplify_conditions(domain)
# negate all negative preconditions
negative_predicate_map = {}
for _, operator in domain.operators.items():
_negate_all_negative_preconditions(operator.condition, negative_predicate_map)
print(negative_predicate_map)
# add new predicates to the domain
for predicate, other in negative_predicate_map.items():
if other in domain.predicates: continue
formula = domain.predicates[predicate].copy()
formula.name = other
domain.predicates[other] = formula
# adjust all add and delete effects
for _, operator in domain.operators.items():
_update_all_effects(operator.effect, negative_predicate_map)
if __name__ == "__main__":
domain = create_temporal_domain()
print("Domain before:")
print(domain)
print()
remove_negative_preconditions(domain)
print("Domain after:")
print(domain)