Skip to content

Commit eef2273

Browse files
authored
Merge pull request #92 from APLA-Toolbox/critical-path
Critical Path Heuristic
2 parents d937990 + 2e83e87 commit eef2273

2 files changed

Lines changed: 114 additions & 6 deletions

File tree

jupyddl/automated_planner.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from .a_star import AStarBestFirstSearch
55
from .greedy_best_first import GreedyBestFirstSearch
66
from .metrics import Metrics
7-
from .heuristics import BasicHeuristic, DeleteRelaxationHeuristic
7+
from .heuristics import BasicHeuristic, DeleteRelaxationHeuristic, CriticalPathHeuristic
88
import coloredlogs
99
import logging
1010
import julia
@@ -27,10 +27,9 @@ def __init__(self, domain_path, problem_path, log_level="DEBUG"):
2727
self.initial_state = self.pddl.initialize(self.problem)
2828
self.goals = self.__flatten_goal()
2929
self.available_heuristics = [
30-
"basic/zero",
31-
"basic/goal_count",
32-
"delete_relaxation/h_add",
33-
"delete_relaxation/h_max",
30+
"basic/zero", "basic/goal_count",
31+
"delete_relaxation/h_add", "delete_relaxation/h_max",
32+
"critical_path/1", "critical_path/2", "critical_path/3",
3433
]
3534

3635
# Logger
@@ -158,6 +157,8 @@ def astar_best_first_search(
158157
heuristic = BasicHeuristic(self, heuristic_key)
159158
elif "delete_relaxation" in heuristic_key:
160159
heuristic = DeleteRelaxationHeuristic(self, heuristic_key)
160+
elif "critical_path" in heuristic_key:
161+
heuristic = CriticalPathHeuristic(self, int(heuristic_key[-1]))
161162
else:
162163
logging.fatal("Not yet implemented")
163164
return [], Metrics()
@@ -178,6 +179,8 @@ def greedy_best_first_search(
178179
heuristic = BasicHeuristic(self, "basic/goal_count")
179180
elif "delete_relaxation" in heuristic_key:
180181
heuristic = DeleteRelaxationHeuristic(self, heuristic_key)
182+
elif "critical_path" in heuristic_key:
183+
heuristic = CriticalPathHeuristic(self, int(heuristic_key[-1]))
181184
else:
182185
logging.fatal("Not yet implemented")
183186
return [], Metrics()

jupyddl/heuristics.py

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ def __goal_count_heuristic(self, state):
2929
count += 1
3030
return count
3131

32-
3332
class DeleteRelaxationHeuristic:
3433
def __init__(self, automated_planner, heuristic_key):
3534
class DRHCache:
@@ -127,3 +126,109 @@ def __facts_eq(self, facts_dict, facts_set):
127126
if not (str(f) in fact_costs_str.keys()):
128127
return False
129128
return True
129+
130+
class CriticalPathHeuristic:
131+
def __init__(self, automated_planner, critical_path_level=1):
132+
class CPCache:
133+
def __init__(self, domain=None, axioms=None, preconds=None, additions=None):
134+
self.domain = domain
135+
self.axioms = axioms
136+
self.preconds = preconds
137+
self.additions = additions
138+
139+
self.automated_planner = automated_planner
140+
self.cache = CPCache()
141+
if critical_path_level > 3:
142+
logging.warning("Critical Path level is only implemented until 3, forcing it to 3.")
143+
self.critical_path_level = 3
144+
if critical_path_level < 1:
145+
logging.warning("Critical Path level has to be at least 1, forcing it to 1.")
146+
self.critical_path_level = 1
147+
else:
148+
self.critical_path_level = critical_path_level
149+
self.has_been_precomputed = False
150+
self.__pre_compute()
151+
# return self.heuristic_keys[self.current_h](state)
152+
153+
def compute(self, state):
154+
if not self.has_been_precomputed:
155+
self.__pre_compute()
156+
domain = self.cache.domain
157+
goals = self.automated_planner.goals
158+
types = state.types
159+
facts = state.facts
160+
fact_costs = self.automated_planner.pddl.init_facts_costs(facts)
161+
while True:
162+
facts, state = self.automated_planner.pddl.get_facts_and_state(
163+
fact_costs, types
164+
)
165+
if self.automated_planner.satisfies(goals, state):
166+
costs = []
167+
fact_costs_str = dict([(str(k), val) for k, val in fact_costs.items()])
168+
if self.critical_path_level == 1:
169+
for g in goals:
170+
if str(g) in fact_costs_str:
171+
costs.append(fact_costs_str[str(g)])
172+
if self.critical_path_level == 2:
173+
pairs_of_goals = [(g1, g2) for g1 in goals for g2 in goals if g1 != g2]
174+
for gs in pairs_of_goals:
175+
if str(gs[0]) in fact_costs_str and str(gs[1]) in fact_costs_str:
176+
costs.append(fact_costs_str[str(gs[0])] + fact_costs_str[str(gs[1])])
177+
if self.critical_path_level == 3:
178+
triplets_of_goals = [(g1, g2, g3) for g1 in goals for g2 in goals for g3 in goals if g1 != g2 and g1 != g3 and g2 != g3]
179+
for gs in triplets_of_goals:
180+
if str(gs[0]) in fact_costs_str and str(gs[1]) in fact_costs_str and str(gs[2]) in fact_costs_str:
181+
costs.append(fact_costs_str[str(gs[0])] + fact_costs_str[str(gs[1])] + fact_costs_str[str(gs[2])])
182+
costs.insert(0, 0)
183+
return max(costs)
184+
185+
for ax in self.cache.axioms:
186+
fact_costs = (
187+
self.automated_planner.pddl.compute_costs_one_step_derivation(
188+
facts, fact_costs, ax, "max"
189+
)
190+
)
191+
192+
actions = self.automated_planner.available_actions(state)
193+
for act in actions:
194+
fact_costs = self.automated_planner.pddl.compute_cost_action_effect(
195+
fact_costs, act, domain, self.cache.additions, "max"
196+
)
197+
198+
if len(fact_costs) == self.automated_planner.pddl.length(
199+
facts
200+
) and self.__facts_eq(fact_costs, facts):
201+
break
202+
203+
return float("inf")
204+
205+
def __pre_compute(self):
206+
if self.has_been_precomputed:
207+
return
208+
domain = self.automated_planner.domain
209+
domain, axioms = self.automated_planner.pddl.compute_hsp_axioms(domain)
210+
# preconditions = dict()
211+
additions = dict()
212+
self.automated_planner.pddl.cache_global_preconditions(domain)
213+
for name, definition in domain.actions.items():
214+
additions[name] = self.automated_planner.pddl.effect_diff(
215+
definition.effect
216+
).add
217+
self.cache.additions = additions
218+
self.cache.preconds = self.automated_planner.pddl.g_preconditions
219+
self.cache.domain = domain
220+
self.cache.axioms = axioms
221+
self.has_been_precomputed = True
222+
223+
def __h_add(self, costs):
224+
return sum(costs)
225+
226+
def __h_max(self, costs):
227+
return max(costs)
228+
229+
def __facts_eq(self, facts_dict, facts_set):
230+
fact_costs_str = dict([(str(k), val) for k, val in facts_dict.items()])
231+
for f in facts_set:
232+
if not (str(f) in fact_costs_str.keys()):
233+
return False
234+
return True

0 commit comments

Comments
 (0)