def test_hierarchical_spaces(): dir_path = os.path.dirname(os.path.realpath(__file__)) domain_file = os.path.join(dir_path, 'pddl', 'hierarchical_type_test_domain.pddl') problem_file = os.path.join(dir_path, 'pddl', 'hierarchical_type_test_domain', 'hierarchical_type_test_problem.pddl') domain = PDDLDomainParser(domain_file) problem = PDDLProblemParser(problem_file, domain.domain_name, domain.types, domain.predicates, domain.actions) actions = list(domain.actions) action_predicates = [domain.predicates[a] for a in actions] space = LiteralSpace(set(domain.predicates.values()) - set(action_predicates), type_to_parent_types=domain.type_to_parent_types) all_ground_literals = space.all_ground_literals( State(problem.initial_state, problem.objects, problem.goal)) ispresent = Predicate("ispresent", 1, [Type("entity")]) islight = Predicate("islight", 1, [Type("object")]) isfurry = Predicate("isfurry", 1, [Type("animal")]) ishappy = Predicate("ishappy", 1, [Type("animal")]) attending = Predicate("attending", 2, [Type("animal"), Type("object")]) nomsy = Type("jindo")("nomsy") rover = Type("corgi")("rover") rene = Type("cat")("rene") block1 = Type("block")("block1") block2 = Type("block")("block2") cylinder1 = Type("cylinder")("cylinder1") assert all_ground_literals == { ispresent(nomsy), ispresent(rover), ispresent(rene), ispresent(block1), ispresent(block2), ispresent(cylinder1), islight(block1), islight(block2), islight(cylinder1), isfurry(nomsy), isfurry(rover), isfurry(rene), ishappy(nomsy), ishappy(rover), ishappy(rene), attending(nomsy, block1), attending(nomsy, block2), attending(nomsy, cylinder1), attending(rover, block1), attending(rover, block2), attending(rover, cylinder1), attending(rene, block1), attending(rene, block2), attending(rene, cylinder1), } print("Test passed.")
def _parse_domain_predicates(self): start_ind = re.search(r"\(:predicates", self.domain).start() predicates = self._find_balanced_expression(self.domain, start_ind) predicates = predicates[12:-1].strip() predicates = self._find_all_balanced_expressions(predicates) self.predicates = {} for pred in predicates: pred = pred.strip()[1:-1].split("?") pred_name = pred[0].strip() # arg_types = [self.types[arg.strip().split("-")[1].strip()] # for arg in pred[1:]] arg_types = [] for arg in pred[1:]: if ' - ' in arg: assert arg_types is not None, "Mixing of typed and untyped args not allowed" assert self.uses_typing arg_type = self.types[arg.strip().split("-", 1)[1].strip()] arg_types.append(arg_type) else: assert not self.uses_typing arg_types.append(self.types["default"]) self.predicates[pred_name] = Predicate(pred_name, len(pred[1:]), arg_types) # Handle equality if "=" in self.domain: self.predicates["="] = Predicate("=", 2)
def test_zero_arity_negative_preconditions(): MoveableType = Type('moveable') Holding = Predicate('Holding', 1, var_types=[MoveableType]) HandEmpty = Predicate('HandEmpty', 0, var_types=[]) conds = [Holding("?x1"), Not(HandEmpty())] kb = {Holding("a"), HandEmpty()} assignments = find_satisfying_assignments(kb, conds, allow_redundant_variables=False) assert len(assignments) == 0 print("Pass.")
def test_heuristic(): dir_path = os.path.dirname(os.path.realpath(__file__)) domain_file = os.path.join(dir_path, 'pddl', 'easyblocks.pddl') problem_dir = os.path.join(dir_path, 'pddl', 'easyblocks') block = Type("block") pickup = Predicate("pickup", 1, [block]) stack = Predicate("stack", 2, [block, block]) shaped_env = PDDLEnv( domain_file, problem_dir, shape_reward_mode="lmcut", raise_error_on_invalid_action=True, ) env = PDDLEnv( domain_file, problem_dir, shape_reward_mode=None, raise_error_on_invalid_action=True, ) shaped_env.reset() env.reset() _, rew, _, _ = env.step(pickup("b")) _, shaped_rew, _, _ = shaped_env.step(pickup("b")) assert rew == 0. assert shaped_rew == 1. intermediate_state = env.get_state() def assert_last_step(): _, rew, done, _ = env.step(stack("b", "a")) _, shaped_rew, shaped_done, _ = shaped_env.step(stack("b", "a")) assert done assert shaped_done assert rew == 1. assert shaped_rew == 2. # check if the step to the goal is terminal and with correct rewards assert_last_step() # check if shaped reward is consistent after setting the state shaped_env.set_state(intermediate_state) env.set_state(intermediate_state) assert_last_step() print("Test passed.")
def effect_to_literal(literal): assert isinstance(literal, Literal) assert literal.predicate.name.startswith("Effect") non_effect_pred = Predicate(literal.predicate.name[len("Effect"):], literal.predicate.arity, literal.predicate.var_types, is_negative=literal.predicate.is_negative, is_anti=literal.predicate.is_anti) return non_effect_pred(*literal.variables)
def _create_actions_from_operators(self): actions = set() for name, operator in self.operators.items(): types = [p.var_type for p in operator.params] action = Predicate(name, len(types), types) assert name not in self.predicates, "Cannot have predicate with same name as operator" self.predicates[name] = action actions.add(action) return actions
def _preprocess_negative_literals(cls, kb, conds): # Check for negated quantifiers, which we do not handle if any((isinstance(c, Exists) or isinstance(c, ForAll)) and c.is_negative \ for c in conds): raise NotImplementedError("We do not yet handle negated quantifiers") # Find all predicates with a negated literal in the conds negated_predicates = set() for cond in cls._get_lits_from_conds(conds): if cond.is_negative: negated_predicates.add(cond.predicate) if len(negated_predicates) == 0: return kb, conds # Start the new kb and conds kb = [lit for lit in kb] conds = [c for c in conds] # Sanity check assert all(str(p).startswith("Not") for p in negated_predicates) # Create positive predicates for the negated predicates negated_pred_to_pos_pred = {} for p in negated_predicates: # Prolog hands = specially if p.name == "=": pos_pred = Predicate(f"neg-eq", p.arity, p.var_types) else: pos_pred = Predicate(f"neg-{p.name}", p.arity, p.var_types) negated_pred_to_pos_pred[p] = pos_pred # TODO pass in objects separately objects = { o for lit in kb for o in lit.variables } # Get all instantiations of the new positive predicates for negated_pred, pos_pred in negated_pred_to_pos_pred.items(): original_positive_pred = negated_pred.positive # Get all combinations of objects for objs in get_object_combinations(objects, arity=pos_pred.arity, var_types=pos_pred.var_types, allow_duplicates=True): # Check whether the positive version is in the kb if original_positive_pred(*objs) in kb: continue # Add the new positive literal to the kb kb.append(pos_pred(*objs)) # Update the conds to include the positive pred instead of the negative pred conds = cls._replace_predicate(conds, negated_pred, pos_pred) return kb, conds
def wrap_goal_literal(cls, x): """Helper for converting a state to required input representation """ if isinstance(x, Predicate): return Predicate("WANT" + x.name, x.arity, var_types=x.var_types, is_negative=x.is_negative, is_anti=x.is_anti) new_predicate = cls.wrap_goal_literal(x.predicate) return new_predicate(*x.variables)
def test_negative_preconditions(): MoveableType = Type('moveable') Holding = Predicate('Holding', 1, var_types=[MoveableType]) IsPawn = Predicate('IsPawn', 1, var_types=[MoveableType]) PutOn = Predicate('PutOn', 1, var_types=[MoveableType]) On = Predicate('On', 2, var_types=[MoveableType, MoveableType]) # ?x0 must bind to o0 and ?x1 must bind to o1, so ?x2 must bind to o2 conds = [ PutOn("?x0"), Holding("?x1"), IsPawn("?x2"), Not(On("?x2", "?x0")) ] kb = { PutOn('o0'), IsPawn('o0'), IsPawn('o1'), IsPawn('o2'), Holding('o1'), } assignments = find_satisfying_assignments(kb, conds, allow_redundant_variables=False) assert len(assignments) == 1 # should be the same, even though IsPawn("?x2") is removed conds = [PutOn("?x0"), Holding("?x1"), Not(On("?x2", "?x0"))] kb = { PutOn('o0'), IsPawn('o0'), IsPawn('o1'), IsPawn('o2'), Holding('o1'), } assignments = find_satisfying_assignments(kb, conds, allow_redundant_variables=False) assert len(assignments) == 1 print("Pass.")
def Effect(x): # pylint:disable=invalid-name """An effect predicate or literal. """ if isinstance(x, Predicate): return Predicate("Effect" + x.name, x.arity, var_types=x.var_types, is_negative=x.is_negative, is_anti=x.is_anti) assert isinstance(x, Literal) new_predicate = Effect(x.predicate) return new_predicate(*x.variables)
def reverse_binary_literal(cls, x): """Helper for converting a state to required input representation """ if isinstance(x, Predicate): assert x.arity == 2 return Predicate("REV" + x.name, x.arity, var_types=x.var_types, is_negative=x.is_negative, is_anti=x.is_anti) new_predicate = cls.reverse_binary_literal(x.predicate) variables = [v for v in x.variables] assert len(variables) == 2 return new_predicate(*variables[::-1])
def test_determinize(): dir_path = os.path.dirname(os.path.realpath(__file__)) domain_file = os.path.join(dir_path, 'pddl', 'test_probabilistic_domain.pddl') problem_dir = os.path.join(dir_path, 'pddl', 'test_probabilistic_domain') env = PDDLEnv(domain_file, problem_dir, raise_error_on_invalid_action=True, dynamic_action_space=True) env.domain.determinize() obs, _ = env.reset() action = env.action_space.all_ground_literals(obs).pop() transitions = env.get_all_possible_transitions(action, return_probs=True) transition_list = list(transitions) assert len(transition_list) == 1 assert transition_list[0][1] == 1.0 newstate = transition_list[0][0][0] type1 = Type('type1') type2 = Type('type2') pred1 = Predicate('pred1', 1, [type1]) pred2 = Predicate('pred2', 1, [type2]) pred3 = Predicate('pred3', 3, [type1, type2, type2]) assert newstate.literals == frozenset({ pred1('b2'), pred2('c1'), pred3('a1', 'c1', 'd1'), pred3('a2', 'c2', 'd2'), pred3('b2', 'd1', 'c1') }) print("Test passed.")
def _create_problem_file(self, raw_problem_fname, use_cache=True): if (not use_cache) or (raw_problem_fname not in self._problem_files): problem_fname = os.path.split(raw_problem_fname)[-1] problem_fname = problem_fname.split('.pddl')[0] problem_fname += '_{}_with_diffs_{}.pddl'.format( self.domain_name, random.randint(0, 9999999)) problem_fname = os.path.join('/tmp', problem_fname) # Parse raw problem problem_parser = PDDLProblemParser(raw_problem_fname, self.domain_name.lower(), self._types, self._predicates, self._actions) new_initial_state = set(problem_parser.initial_state) # Add actions action_lits = set( self._action_space.all_ground_literals(State( new_initial_state, problem_parser.objects, problem_parser.goal), valid_only=False)) new_initial_state |= action_lits # Add 'Different' pairs for each pair of objects Different = Predicate('Different', 2) for obj1 in problem_parser.objects: for obj2 in problem_parser.objects: if obj1 == obj2: continue # if obj1.var_type != obj2.var_type: # continue diff_lit = Different(obj1, obj2) new_initial_state.add(diff_lit) # Write out new temporary problem file problem_parser.initial_state = frozenset(new_initial_state) problem_parser.write(problem_fname) # Add to cache self._problem_files[raw_problem_fname] = problem_fname return self._problem_files[raw_problem_fname]
def test_pddlenv(): dir_path = os.path.dirname(os.path.realpath(__file__)) domain_file = os.path.join(dir_path, 'pddl', 'test_domain.pddl') problem_dir = os.path.join(dir_path, 'pddl', 'test_domain') env = PDDLEnv(domain_file, problem_dir, raise_error_on_invalid_action=True, dynamic_action_space=True) env2 = PDDLEnv(domain_file, problem_dir, raise_error_on_invalid_action=True, dynamic_action_space=False) type1 = Type('type1') type2 = Type('type2') pred1 = Predicate('pred1', 1, [type1]) pred2 = Predicate('pred2', 1, [type2]) pred3 = Predicate('pred3', 1, [type1, type2, type2]) operator_name = 'action1' action_pred = Predicate('actionpred', 1, [type1]) obs, _ = env.reset() assert obs == { pred1('b2'), pred2('c1'), pred3('a1', 'c1', 'd1'), pred3('a2', 'c2', 'd2') } operator = env.domain.operators[operator_name] # Invalid action action = action_pred('b1') try: env.step(action) assert False, "Action was supposed to be invalid" except InvalidAction: pass assert action not in env.action_space.all_ground_literals( ), "Dynamic action space not working" env2.reset() assert action in env2.action_space.all_ground_literals( ), "Dynamic action space not working" # Valid args action = action_pred('b2') obs, _, _, _ = env.step(action) assert obs == { pred1('b2'), pred2('c1'), pred2('c2'), pred3('b2', 'd2', 'c2'), pred3('a1', 'c1', 'd1'), pred3('a2', 'c2', 'd2') } print("Test passed.")
def test_pddlenv_hierarchical_types(): dir_path = os.path.dirname(os.path.realpath(__file__)) domain_file = os.path.join(dir_path, 'pddl', 'hierarchical_type_test_domain.pddl') problem_dir = os.path.join(dir_path, 'pddl', 'hierarchical_type_test_domain') env = PDDLEnv(domain_file, problem_dir) obs, _ = env.reset() ispresent = Predicate("ispresent", 1, [Type("entity")]) islight = Predicate("islight", 1, [Type("object")]) isfurry = Predicate("isfurry", 1, [Type("animal")]) ishappy = Predicate("ishappy", 1, [Type("animal")]) pet = Predicate("pet", 1, [Type("animal")]) nomsy = Type("jindo")("nomsy") rover = Type("corgi")("rover") rene = Type("cat")("rene") block1 = Type("block")("block1") block2 = Type("block")("block2") cylinder1 = Type("cylinder")("cylinder1") assert obs.literals == frozenset({ ispresent(nomsy), ispresent(rover), ispresent(rene), ispresent(block1), ispresent(block2), ispresent(cylinder1), islight(block1), islight(cylinder1), isfurry(nomsy), }) obs, _, _, _ = env.step(pet('block1')) assert obs.literals == frozenset({ ispresent(nomsy), ispresent(rover), ispresent(rene), ispresent(block1), ispresent(block2), ispresent(cylinder1), islight(block1), islight(cylinder1), isfurry(nomsy), }) obs, _, _, _ = env.step(pet(nomsy)) assert obs.literals == frozenset({ ispresent(nomsy), ispresent(rover), ispresent(rene), ispresent(block1), ispresent(block2), ispresent(cylinder1), islight(block1), islight(cylinder1), isfurry(nomsy), ishappy(nomsy) }) print("Test passed.")
def test_get_all_possible_transitions_multiple_effects(): dir_path = os.path.dirname(os.path.realpath(__file__)) domain_file = os.path.join(dir_path, 'pddl', 'test_probabilistic_domain_alt_2.pddl') problem_dir = os.path.join(dir_path, 'pddl', 'test_probabilistic_domain_alt_2') env = PDDLEnv(domain_file, problem_dir, raise_error_on_invalid_action=True, dynamic_action_space=True) obs, _ = env.reset() action = env.action_space.all_ground_literals(obs).pop() transitions = env.get_all_possible_transitions(action) transition_list = list(transitions) assert len(transition_list) == 3 states = set({ transition_list[0][0].literals, transition_list[1][0].literals, transition_list[2][0].literals }) type1 = Type('type1') type2 = Type('type2') pred1 = Predicate('pred1', 1, [type1]) pred2 = Predicate('pred2', 1, [type2]) pred3 = Predicate('pred3', 3, [type1, type2, type2]) expected_states = set({ frozenset( {pred1('b2'), pred3('a1', 'c1', 'd1'), pred3('a2', 'c2', 'd2')}), frozenset( {pred2('c1'), pred3('a1', 'c1', 'd1'), pred3('a2', 'c2', 'd2')}), frozenset({ pred1('b2'), pred2('c1'), pred3('a1', 'c1', 'd1'), pred3('a2', 'c2', 'd2'), pred3('b2', 'd1', 'c1') }) }) assert states == expected_states # Now test again with return_probs=True. transitions = env.get_all_possible_transitions(action, return_probs=True) transition_list = list(transitions) assert len(transition_list) == 3 states_and_probs = { transition_list[0][0][0].literals: transition_list[0][1], transition_list[1][0][0].literals: transition_list[1][1], transition_list[2][0][0].literals: transition_list[2][1] } type1 = Type('type1') type2 = Type('type2') pred1 = Predicate('pred1', 1, [type1]) pred2 = Predicate('pred2', 1, [type2]) pred3 = Predicate('pred3', 3, [type1, type2, type2]) expected_states = { frozenset({ pred1('b2'), pred3('a1', 'c1', 'd1'), pred3('a2', 'c2', 'd2') }): 0.5, frozenset({ pred2('c1'), pred3('a1', 'c1', 'd1'), pred3('a2', 'c2', 'd2') }): 0.4, frozenset({ pred1('b2'), pred2('c1'), pred3('a1', 'c1', 'd1'), pred3('a2', 'c2', 'd2'), pred3('b2', 'd1', 'c1') }): 0.1 } for s, prob in states_and_probs.items(): assert s in expected_states assert prob - expected_states[s] < 1e-5 print("Test passed.")
class FOLDTOperatorLearningModule: _NoChange = Predicate("NoChange", 0) def __init__(self, learned_operators): self._learned_operators = learned_operators self._learned_operators_for_action = defaultdict(set) self._Xs = defaultdict(list) self._Ys = defaultdict(list) self._seed = ac.seed self._rand_state = np.random.RandomState(seed=ac.seed) self.learned_dts = {} self._fits_all_data = defaultdict(bool) self._learning_on = True def observe(self, state, action, effects): if not self._learning_on: return x = (state.literals | {action}) y = sorted([Effect(e) for e in effects]) if not y: y.append(Effect(self._NoChange())) self._Xs[action.predicate].append(x) self._Ys[action.predicate].append(y) # print("observed data:") # print(x) # print(y) # input("!!") # Check whether we'll need to relearn if self._fits_all_data[action.predicate]: dt = self.learned_dts[action.predicate] if not self._dt_fits_data(dt, x, y): self._fits_all_data[action.predicate] = False def learn(self): if not self._learning_on: return False is_updated = False for action_predicate in self._fits_all_data: if not self._fits_all_data[action_predicate]: # Learn one main tree. try: dt = self._learn_single_dt(action_predicate, self._seed, self._Xs[action_predicate], self._Ys[action_predicate]) except LearningFailure: # Aggressive!!!!!!!!! self._Xs[action_predicate] = [] self._Ys[action_predicate] = [] continue self.learned_dts[action_predicate] = dt operators = self._foldt_to_operators(dt, action_predicate.name) for op in operators: if not sum(lit.predicate.name == action_predicate for lit in op.preconds.literals) == 1: # Something is wrong...why are multiple action # predicates appearing in the preconditions? import ipdb ipdb.set_trace() # Remove old operators self._learned_operators_for_action[action_predicate].clear() # Add new operators self._learned_operators_for_action[action_predicate].update( operators) self._fits_all_data[action_predicate] = True is_updated = True # Update all learned operators if is_updated: self._learned_operators.clear() for operators in self._learned_operators_for_action.values(): self._learned_operators.update(operators) return is_updated @staticmethod def _learn_single_dt(action_predicate, seed, X, Y, bag_predicates=False, bag_features=False): root_literal = action_predicate( * ['Placeholder{}'.format(i) for i in range(action_predicate.arity)]) dt = FOLDTClassifier( seed=seed, root_feature=root_literal, max_feature_length=ac.max_foldt_feature_length, max_learning_time=ac.max_foldt_learning_time, max_exceeded_strategy=ac.max_foldt_exceeded_strategy, bag_predicates=bag_predicates, bag_features=bag_features) dt.fit(X, Y) return dt def turn_off(self): self._learning_on = False def _dt_fits_data(self, dt, x, y): prediction = self.get_prediction(x, dt) if prediction is None: match = y is None else: match = set(y) == set(prediction) # if not match: # print("Mismatch:") # print("x =", x) # print("y =", y) # print("prediction =", prediction) return match @classmethod def get_prediction(cls, x, dt): prediction = dt.predict(x) if prediction is None: return prediction # Cancel out effects if possible (e.g. At(x) and AntiAt(x)) final_prediction = set() for lit in prediction: if lit.inverted_anti in final_prediction: final_prediction.remove(lit.inverted_anti) else: final_prediction.add(lit) if not final_prediction: final_prediction = [Effect(cls._NoChange())] return sorted(list(final_prediction)) def _foldt_to_operators(self, dt, action_name, suffix=''): op_counter = 0 operators = set() for (path, leaf) in dt.get_conditional_literals(dt.root): if leaf is None: continue if any(lit.predicate == Effect(self._NoChange) for lit in leaf): continue name = "LearnedOperator{}{}{}".format(action_name, op_counter, suffix) op_counter += 1 # NoChange appears only in effects in the training data, so it # should never be in the preconditions of the learned operators. assert not any(lit.predicate.positive == self._NoChange for lit in path) preconds = LiteralConjunction(path) effects = LiteralConjunction([effect_to_literal(l) for l in leaf]) params = self._get_params_from_preconds(preconds) operator = Operator(name, params, preconds, effects) operators.add(operator) return operators def _get_params_from_preconds(self, preconds): param_set = set() for lit in preconds.literals: if lit.negated_as_failure: continue for v in lit.variables: param_set.add(v) return sorted(list(param_set))
def get_sar_successor_state(state, action): """Search and rescue specific successor generation Assumptions: - One robot called robot0 """ # Remake predicates to keep this function self-contained person_type = Type('person') robot_type = Type('robot') location_type = Type('location') direction_type = Type('direction') clear = Predicate('clear', 1, [location_type]) conn = Predicate("conn", 3, [location_type, location_type, direction_type]) robot_at = Predicate('robot-at', 2, [robot_type, location_type]) carrying = Predicate('carrying', 2, [robot_type, person_type]) person_at = Predicate('person-at', 2, [person_type, location_type]) handsfree = Predicate('handsfree', 1, [robot_type]) # Parse the state robot_location = None # location robot_carrying = None # person adjacency_map = {} # (location, direction) -> location people_locs = {} # person -> location clear_locs = set() for lit in state.literals: if lit.predicate.name == "robot-at": assert lit.variables[0] == "robot0" robot_location = lit.variables[1] elif lit.predicate.name == "conn": adjacency_map[(lit.variables[0], lit.variables[2])] = lit.variables[1] elif lit.predicate.name == "carrying": assert lit.variables[0] == "robot0" robot_carrying = lit.variables[1] elif lit.predicate.name == "person-at": people_locs[lit.variables[0]] = lit.variables[1] elif lit.predicate.name == "clear": clear_locs.add(lit.variables[0]) assert robot_location is not None is_valid = False pos_preconds = set() neg_preconds = set() pos_effects = set() neg_effects = set() if action.predicate.name == "move": """ (:action move-robot :parameters (?robot - robot ?from - location ?to - location ?dir - direction) :precondition (and (move ?dir) (conn ?from ?to ?dir) (robot-at ?robot ?from) (clear ?to)) :effect (and (not (robot-at ?robot ?from)) (robot-at ?robot ?to) (not (clear ?to)) (clear ?from)) ) """ direction = action.variables[0] if (robot_location, direction) in adjacency_map: next_robot_location = adjacency_map[(robot_location, direction)] if next_robot_location in clear_locs: is_valid = True pos_preconds = { conn(robot_location, next_robot_location, direction), robot_at("robot0", robot_location), clear(next_robot_location), } pos_effects = { robot_at("robot0", next_robot_location), clear(robot_location) } neg_effects = { robot_at("robot0", robot_location), clear(next_robot_location) } elif action.predicate.name == "pickup": """ (:action pickup-person :parameters (?robot - robot ?person - person ?loc - location) :precondition (and (pickup ?person) (robot-at ?robot ?loc) (person-at ?person ?loc) (handsfree ?robot)) :effect (and (not (person-at ?person ?loc)) (not (handsfree ?robot)) (carrying ?robot ?person)) ) """ if robot_carrying is None: person = action.variables[0] person_loc = people_locs[person] if person_loc == robot_location: is_valid = True pos_preconds = { robot_at("robot0", robot_location), person_at(person, person_loc), handsfree("robot0"), } pos_effects = { carrying("robot0", person), } neg_effects = { person_at(person, person_loc), handsfree("robot0"), } elif action.predicate.name == "dropoff": """ (:action dropoff-person :parameters (?robot - robot ?person - person ?loc - location) :precondition (and (dropoff ) (carrying ?robot ?person) (robot-at ?robot ?loc)) :effect (and (person-at ?person ?loc) (handsfree ?robot) (not (carrying ?robot ?person))) ) """ if robot_carrying is not None: is_valid = True pos_preconds = { robot_at("robot0", robot_location), carrying("robot0", robot_carrying), } pos_effects = { person_at(robot_carrying, robot_location), handsfree("robot0"), } neg_effects = { carrying("robot0", robot_carrying), } else: raise Exception(f"Unrecognized action {action}.") if not is_valid: return state assert state.literals.issuperset(pos_preconds) assert len(state.literals & {Not(p) for p in neg_preconds}) == 0 new_state_literals = set(state.literals) new_state_literals -= neg_effects new_state_literals |= pos_effects return state.with_literals(frozenset(new_state_literals))
def test_prover(): TType = Type('t') atom0, atom1, atom2 = TType('atom0'), TType('atom1'), TType('atom2') var0, var1, var2, var3 = TType('Var0'), TType('Var1'), TType( 'Var2'), TType('Var3') # Single predicate single arity test predicate0 = Predicate('Predicate0', 1, [TType]) predicate1 = Predicate('Predicate1', 2, [TType, TType]) predicate2 = Predicate('Predicate2', 1, [TType]) kb0 = [predicate0(atom0)] assignments = find_satisfying_assignments(kb0, [predicate0(var0)]) assert len(assignments) == 1 assert len(assignments[0]) == 1 assert assignments[0][var0] == atom0 assignments = find_satisfying_assignments( kb0, [predicate0(var0), predicate0(var1)]) assert len(assignments) == 1 kb1 = [predicate0(atom0), predicate0(atom1)] assignments = find_satisfying_assignments(kb1, [predicate0(var0)]) assert len(assignments) == 2 assignments = find_satisfying_assignments( kb1, [predicate0(var0), predicate0(var1)]) assert len(assignments) == 2 assignments = find_satisfying_assignments( kb1, [predicate0(var0), predicate0(var1), predicate0(var2)]) assert len(assignments) == 2 kb2 = [predicate0(atom0), predicate0(atom1), predicate0(atom2)] assignments = find_satisfying_assignments(kb2, [predicate0(var0)]) assert len(assignments) == 2 assignments = find_satisfying_assignments( kb2, [predicate0(var0), predicate0(var1)]) assert len(assignments) == 2 assignments = find_satisfying_assignments( kb2, [predicate0(var0), predicate0(var1), predicate0(var2)]) assert len(assignments) == 2 # Single predicate multiple arity test kb3 = [predicate1(atom0, atom1), predicate1(atom1, atom2)] assignments = find_satisfying_assignments(kb3, [predicate1(var0, var1)]) assert len(assignments) == 2 assignments = find_satisfying_assignments( kb3, [predicate1(var0, var1), predicate1(var1, var2)]) assert len(assignments) == 1 assignments = find_satisfying_assignments( kb3, [predicate1(var0, var1), predicate1(var1, var0)]) assert len(assignments) == 0 assignments = find_satisfying_assignments( kb3, [predicate1(var0, var1), predicate1(var2, var3)]) assert len(assignments) == 2 ## Multiple predicate multiple arity test kb4 = [ predicate0(atom2), predicate1(atom0, atom1), predicate1(atom1, atom2) ] assignments = find_satisfying_assignments( kb4, [predicate1(var0, var1), predicate0(var1), predicate0(var0)]) assert len(assignments) == 0 ## Tricky case! kb6 = [ predicate0(atom0), predicate2(atom1), predicate1(atom0, atom2), predicate1(atom2, atom1) ] assignments = find_satisfying_assignments( kb6, [predicate0(var0), predicate2(var1), predicate1(var0, var1)]) assert len(assignments) == 0 print("Pass.")
from pddlgym.structs import Predicate, ground_literal, LiteralConjunction from pddlgym.parser import Operator from pddlgym.inference import find_satisfying_assignments import numpy as np ### Noisy deictic rules NOISE_OUTCOME = Predicate("noiseoutcome", 0, [])() class MultipleOutcomesPossible(Exception): pass class NDR: """A Noisy Deictic Rule has a lifted action, lifted preconditions, and a distribution over effects. Parameters ---------- action : Literal preconditions : [ Literal ] effect_probs : np.ndarray effects : [Literal] """ def __init__(self, action, preconditions, effect_probs, effects, allow_redundant_variables=False): self._action = action
class SearchAndRescueEnv(PDDLSearchAndRescueEnv): """Changes the state space to just be positions of objects and the identity of the person being carried. """ person_type = Type('person') robot_type = Type('robot') location_type = Type('location') direction_type = Type('direction') wall_type = Type('wall') hospital_type = Type('hospital') clear = Predicate('clear', 1, [location_type]) conn = Predicate("conn", 3, [location_type, location_type, direction_type]) robot_at = Predicate('robot-at', 2, [robot_type, location_type]) person_at = Predicate('person-at', 2, [person_type, location_type]) wall_at = Predicate('wall-at', 2, [wall_type, location_type]) hospital_at = Predicate('hospital-at', 2, [hospital_type, location_type]) carrying = Predicate('carrying', 2, [robot_type, person_type]) handsfree = Predicate('handsfree', 1, [robot_type]) @property def observation_space(self): raise NotImplementedError() def _state_to_internal(self, state): state = dict(state) new_state_literals = set() directions_to_deltas = { self.direction_type('up') : (-1, 0), self.direction_type('down') : (1, 0), self.direction_type('left') : (0, -1), self.direction_type('right') : (0, 1), } # conn height, width = 6, 6 for r in range(height): for c in range(width): loc = self.location_type(f"f{r}-{c}f") for direction, (dr, dc) in directions_to_deltas.items(): next_r, next_c = r + dr, c + dc if not (0 <= next_r < height and 0 <= next_c < width): continue next_loc = self.location_type(f"f{next_r}-{next_c}f") conn_lit = self.conn(loc, next_loc, direction) new_state_literals.add(conn_lit) # at occupied_locs = set() hospital_loc = None for obj_name, loc_tup in state.items(): if obj_name in ["carrying", "rescue"]: continue r, c = loc_tup loc = self.location_type(f"f{r}-{c}f") if obj_name.startswith("person"): at_pred = self.person_at elif obj_name.startswith("robot"): at_pred = self.robot_at occupied_locs.add((r, c)) elif obj_name.startswith("wall"): at_pred = self.wall_at occupied_locs.add((r, c)) elif obj_name.startswith("hospital"): at_pred = self.hospital_at assert hospital_loc is None hospital_loc = loc else: raise Exception(f"Unrecognized object {obj_name}") new_state_literals.add(at_pred(obj_name, loc)) assert hospital_loc is not None # carrying/handsfree if state["carrying"] is None: new_state_literals.add(self.handsfree("robot0")) else: new_state_literals.add(self.carrying("robot0", state["carrying"])) # clear for r in range(height): for c in range(width): if (r, c) not in occupied_locs: loc = self.location_type(f"f{r}-{c}f") clear_lit = self.clear(loc) new_state_literals.add(clear_lit) # objects new_objects = frozenset({o for lit in new_state_literals for o in lit.variables }) # goal new_goal = LiteralConjunction([self.person_at(person, hospital_loc) \ for person in sorted(state["rescue"])]) new_state = State(frozenset(new_state_literals), new_objects, new_goal) return new_state def _internal_to_state(self, internal_state): state = { "carrying" : None } state["rescue"] = set() for lit in internal_state.goal.literals: assert lit.predicate == self.person_at state["rescue"].add(lit.variables[0].name) state["rescue"] = frozenset(state["rescue"]) # make hashable for lit in internal_state.literals: if lit.predicate.name.endswith("at"): obj_name = lit.variables[0].name r, c = self._loc_to_rc(lit.variables[1]) state[obj_name] = (r, c) if lit.predicate.name == "carrying": person_name = lit.variables[1].name state["carrying"] = person_name state = tuple(sorted(state.items())) # make hashable return state def _loc_to_rc(self, loc_str): assert loc_str.startswith("f") and loc_str.endswith("f") r, c = loc_str[1:-1].split('-') return (int(r), int(c)) def set_state(self, state): assert isinstance(state, State), "Do not call set_state" self._state = state def get_state(self): assert isinstance(self._state, State), "Do not call get_state" return self._state def reset(self): internal_state, debug_info = super().reset() return self._internal_to_state(internal_state), debug_info def step(self, action): internal_state, reward, done, debug_info = super().step(action) state = self._internal_to_state(internal_state) return state, reward, done, debug_info def get_successor_state(self, state, action): internal_state = self._state_to_internal(state) next_internal_state = super().get_successor_state(internal_state, action) next_state = self._internal_to_state(next_internal_state) # Sanity checks assert state == self._internal_to_state(internal_state) assert next_internal_state == self._state_to_internal(next_state) return next_state def render_from_state(self, state): internal_state = self._state_to_internal(state) return super().render_from_state(internal_state) def check_goal(self, state): internal_state = self._state_to_internal(state) return super().check_goal(internal_state)
def test_get_all_possible_transitions(): dir_path = os.path.dirname(os.path.realpath(__file__)) domain_file = os.path.join(dir_path, 'pddl', 'test_probabilistic_domain.pddl') problem_dir = os.path.join(dir_path, 'pddl', 'test_probabilistic_domain') env = PDDLEnv(domain_file, problem_dir, raise_error_on_invalid_action=True, dynamic_action_space=True) obs, _ = env.reset() action = env.action_space.all_ground_literals(obs).pop() transitions = env.get_all_possible_transitions(action) transition_list = list(transitions) assert len(transition_list) == 2 state1, state2 = transition_list[0][0], transition_list[1][0] type1 = Type('type1') type2 = Type('type2') pred1 = Predicate('pred1', 1, [type1]) pred2 = Predicate('pred2', 1, [type2]) pred3 = Predicate('pred3', 3, [type1, type2, type2]) state1, state2 = (state1, state2) if pred2('c1') in state2.literals else (state2, state1) assert state1.literals == frozenset({ pred1('b2'), pred3('a1', 'c1', 'd1'), pred3('a2', 'c2', 'd2'), pred3('b2', 'd1', 'c1') }) assert state2.literals == frozenset({ pred1('b2'), pred2('c1'), pred3('a1', 'c1', 'd1'), pred3('a2', 'c2', 'd2'), pred3('b2', 'd1', 'c1') }) # Now test again with return_probs=True. transitions = env.get_all_possible_transitions(action, return_probs=True) transition_list = list(transitions) assert len(transition_list) == 2 assert abs(transition_list[0][1] - 0.3) < 1e-5 or abs(transition_list[0][1] - 0.7) < 1e-5 assert abs(transition_list[1][1] - 0.3) < 1e-5 or abs(transition_list[1][1] - 0.7) < 1e-5 assert abs(transition_list[0][1] - transition_list[1][1]) > 0.3 state1, state2 = transition_list[0][0][0], transition_list[1][0][0] type1 = Type('type1') type2 = Type('type2') pred1 = Predicate('pred1', 1, [type1]) pred2 = Predicate('pred2', 1, [type2]) pred3 = Predicate('pred3', 3, [type1, type2, type2]) state1, state2 = (state1, state2) if pred2('c1') in state2.literals else (state2, state1) assert state1.literals == frozenset({ pred1('b2'), pred3('a1', 'c1', 'd1'), pred3('a2', 'c2', 'd2'), pred3('b2', 'd1', 'c1') }) assert state2.literals == frozenset({ pred1('b2'), pred2('c1'), pred3('a1', 'c1', 'd1'), pred3('a2', 'c2', 'd2'), pred3('b2', 'd1', 'c1') }) print("Test passed.")
def integration_test(): dir_path = os.path.dirname(os.path.realpath(__file__)) domain_file = os.path.join(dir_path, 'pddl', 'test_domain.pddl') problem_file = os.path.join(dir_path, 'pddl', 'test_domain', 'test_problem.pddl') domain = PDDLDomainParser(domain_file) problem = PDDLProblemParser(problem_file, domain.domain_name, domain.types, domain.predicates) ## Check domain type1 = Type('type1') type2 = Type('type2') # Action predicates action_pred = Predicate('actionpred', 1, [type1]) # Predicates pred1 = Predicate('pred1', 1, [type1]) pred2 = Predicate('pred2', 1, [type2]) pred3 = Predicate('pred3', 1, [type1, type2, type2]) assert set(domain.predicates.values()) == { pred1, pred2, pred3, action_pred } assert domain.actions == { action_pred.name } # Operators assert len(domain.operators) == 1 operator1 = Predicate('action1', 4, [type1, type1, type2, type2]) assert operator1 in domain.operators operator = domain.operators[operator1] # Operator parameters assert len(operator.params) == 4 assert operator.params[0] == type1('?a') assert operator.params[1] == type1('?b') assert operator.params[2] == type2('?c') assert operator.params[3] == type2('?d') # Operator preconditions (set of Literals) assert len(operator.preconds.literals) == 4 assert set(operator.preconds.literals) == { action_pred('?b'), pred1('?b'), pred3('?a', '?c', '?d'), Not(pred2('?c')) } # Operator effects (set of Literals) assert len(operator.effects.literals) == 3 assert set(operator.effects.literals) == { Anti(pred2('?d')), pred2('?c'), pred3('?b', '?d', '?c')} ## Check problem # Objects assert set(problem.objects) == {type1('a1'), type1('a2'), type1('b1'), type1('b2'), type1('b3'), type2('c1'), type2('c2'), type2('d1'), type2('d2'), type2('d3')} # Goal assert isinstance(problem.goal, LiteralConjunction) assert set(problem.goal.literals) == {pred2('c2'), pred3('b1', 'c1', 'd1')} # Init assert problem.initial_state == { pred1('b2'), pred2('c1'), pred3('a1', 'c1', 'd1'), pred3('a2', 'c2', 'd2'), action_pred('a1'), action_pred('a2'), action_pred('b1'), action_pred('b2')} print("Test passed.")
def _create_problem_file(self, raw_problem_fname, use_cache=True): if (not use_cache) or (raw_problem_fname not in self._problem_files): problem_fname = os.path.split(raw_problem_fname)[-1] problem_fname = problem_fname.split('.pddl')[0] problem_fname += '_{}_with_diffs_{}.pddl'.format( self.domain_name, random.randint(0, 9999999)) problem_fname = os.path.join('/tmp', problem_fname) # Parse raw problem action_names = [ ] # purposely empty b/c we WANT action literals in there problem_parser = PDDLProblemParser(raw_problem_fname, self.domain_name.lower(), self._types, self._predicates, action_names) # Add action literals (in case they're not already present in the initial state) # which will be true when the original domain uses operators_as_actions init_state = State(problem_parser.initial_state, problem_parser.objects, None) act_lits = self._action_space.all_ground_literals(init_state, valid_only=False) problem_parser.initial_state = frozenset( act_lits | problem_parser.initial_state) # Add 'Different' pairs for each pair of objects Different = Predicate('Different', 2) init_state = set(problem_parser.initial_state) for obj1 in problem_parser.objects: for obj2 in problem_parser.objects: if obj1 == obj2: continue # if obj1.var_type != obj2.var_type: # continue diff_lit = Different(obj1, obj2) init_state.add(diff_lit) problem_parser.initial_state = frozenset(init_state) # Also add 'different' pairs for goal if it's existential if isinstance(problem_parser.goal, Exists): diffs = [] for var1 in problem_parser.goal.variables: for var2 in problem_parser.goal.variables: if var1 == var2: continue diffs.append(Different(var1, var2)) problem_parser.goal = Exists( problem_parser.goal.variables, type(problem_parser.goal.body)( problem_parser.goal.body.literals + diffs)) # If no objects, write a dummy one to make FF not crash. if not problem_parser.objects: problem_parser.objects.append("DUMMY") # Write out new temporary problem file problem_parser.write(problem_fname) # Add to cache self._problem_files[raw_problem_fname] = (problem_fname, problem_parser.objects) return self._problem_files[raw_problem_fname]