Skip to content

Commit

Permalink
[issue879] remove other source of non-determinism; improve code
Browse files Browse the repository at this point in the history
  • Loading branch information
roeger committed Feb 8, 2024
1 parent f6abb92 commit 9182878
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 32 deletions.
26 changes: 10 additions & 16 deletions src/translate/invariant_finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,8 @@

class BalanceChecker:
def __init__(self, task, reachable_action_params):
self.predicates_to_add_actions = defaultdict(dict)
# Conceptionally, predicates_to_add will store for each predicate the
# set of actions that have an add effect on the predicate. Since sets
# are not stable (introducing non-determinism in the algorithm), we
# store the actions as keys in a dictionary (which is stable).
self.predicates_to_add_actions = defaultdict(list)
self.random = random.Random(314159)
self.action_to_heavy_action = {}
for act in task.actions:
action = self.add_inequality_preconds(act, reachable_action_params)
Expand All @@ -32,7 +29,9 @@ def __init__(self, task, reachable_action_params):
too_heavy_effects.append(eff.copy())
if not eff.literal.negated:
predicate = eff.literal.predicate
self.predicates_to_add_actions[predicate][action] = True
add_actions = self.predicates_to_add_actions[predicate]
if not add_actions or add_actions[-1] is not action:
add_actions.append(action)
if create_heavy_act:
heavy_act = pddl.Action(action.name, action.parameters,
action.num_external_parameters,
Expand All @@ -43,7 +42,7 @@ def __init__(self, task, reachable_action_params):
self.action_to_heavy_action[action] = heavy_act

def get_threats(self, predicate):
return self.predicates_to_add_actions.get(predicate, dict())
return self.predicates_to_add_actions.get(predicate, list())

def get_heavy_action(self, action):
return self.action_to_heavy_action[action]
Expand Down Expand Up @@ -105,11 +104,6 @@ def enqueue_func(invariant):
candidates.append(invariant)
seen_candidates.add(invariant)

# we want to fix the random seed for the invariant synthesis but don't want
# to have this a more global impact. For this reason, we temporary store
# the state of the random generator.
random_state = random.getstate()
random.seed(314159)
start_time = time.process_time()
while candidates:
candidate = candidates.popleft()
Expand All @@ -118,15 +112,14 @@ def enqueue_func(invariant):
return
if candidate.check_balance(balance_checker, enqueue_func):
yield candidate
random.setstate(random_state)

def useful_groups(invariants, initial_facts):
predicate_to_invariants = defaultdict(list)
for invariant in invariants:
for predicate in invariant.predicates:
predicate_to_invariants[predicate].append(invariant)

nonempty_groups = set()
nonempty_groups = dict() # dict instead of set because it is stable
overcrowded_groups = set()
for atom in initial_facts:
if isinstance(atom, pddl.Assign):
Expand All @@ -140,10 +133,11 @@ def useful_groups(invariants, initial_facts):

group_key = (invariant, parameters_tuple)
if group_key not in nonempty_groups:
nonempty_groups.add(group_key)
nonempty_groups[group_key] = True
else:
overcrowded_groups.add(group_key)
useful_groups = nonempty_groups - overcrowded_groups
useful_groups = [group_key for group_key in nonempty_groups.keys()
if group_key not in overcrowded_groups]
for (invariant, parameters) in useful_groups:
yield [part.instantiate(parameters) for part in sorted(invariant.parts)]

Expand Down
29 changes: 13 additions & 16 deletions src/translate/invariants.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from collections import defaultdict
import itertools
from random import randrange

import constraints
import pddl
Expand Down Expand Up @@ -323,27 +322,25 @@ def check_balance(self, balance_checker, enqueue_func):
# We will only use the keys of the dictionary. We do not use a set
# because it's not stable and introduces non-determinism in the
# invariance analysis.
for part in self.parts:
actions_to_check.update(balance_checker.get_threats(part.predicate))

# For a better expected perfomance, we want to randomize the order in
# which actions are checked. Since candidates are often already
# discarded by an early check, we do not want to shuffle the order but
# instead always draw the next action randomly from those we did not
# yet consider.
for part in sorted(self.parts):
for a in balance_checker.get_threats(part.predicate):
actions_to_check[a] = True

actions = list(actions_to_check.keys())
unchecked = len(actions)
while unchecked:
pos = randrange(unchecked)
action = actions[pos]
while actions:
# For a better expected perfomance, we want to randomize the order
# in which actions are checked. Since candidates are often already
# discarded by an early check, we do not want to shuffle the order
# but instead always draw the next action randomly from those we
# did not yet consider.
pos = balance_checker.random.randrange(len(actions))
actions[pos], actions[-1] = actions[-1], actions[pos]
action = actions.pop()
heavy_action = balance_checker.get_heavy_action(action)
if self._operator_too_heavy(heavy_action):
return False
if self._operator_unbalanced(action, enqueue_func):
return False
actions[pos], actions[unchecked - 1] = \
actions[unchecked - 1], actions[pos]
unchecked -= 1
return True

def _operator_too_heavy(self, h_action):
Expand Down

0 comments on commit 9182878

Please sign in to comment.