def get_children(self, action_rule_set, ndr_settings=None): for i in range(len(action_rule_set.ndrs)): for new_lit in self._all_possible_additions: # if new_lit.predicate.name == "start": # import ipdb; ipdb.set_trace() # No use adding a lit that's already there if new_lit in action_rule_set.ndrs[i].preconditions or \ Not(new_lit) in action_rule_set.ndrs[i].preconditions: continue new_rule_set = action_rule_set.copy() pos_ndr = new_rule_set.ndrs[i] pos_ndr.preconditions.append(new_lit) neg_ndr = action_rule_set.ndrs[i].copy() neg_ndr.preconditions.append(Not(new_lit)) new_rule_set.ndrs.insert(i+1, neg_ndr) partitions = new_rule_set.partition_transitions(self.transitions_for_action) # Induce new outcomes for modified ndrs induce_outcomes(pos_ndr, partitions[i], ndr_settings=ndr_settings) induce_outcomes(neg_ndr, partitions[i+1], ndr_settings=ndr_settings) # Update default rule parameters learn_parameters(new_rule_set.default_ndr, partitions[-1], ndr_settings=ndr_settings) score = score_action_rule_set(new_rule_set, self.transitions_for_action, ndr_settings=ndr_settings) yield score, new_rule_set
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 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 _prolog_goal_line(self, lit): """ """ if isinstance(lit, LiteralConjunction): inner_str = ",".join( self._prolog_goal_line(l) for l in lit.literals) return "({})".format(inner_str) if isinstance(lit, LiteralDisjunction): inner_str = ";".join( self._prolog_goal_line(l) for l in lit.literals) return "({})".format(inner_str) if lit.is_negative: pos_pred_str = self._prolog_goal_line(lit.positive) pred_str = "\\+({})".format(pos_pred_str) return pred_str if isinstance(lit, Literal): pred_name = self._clean_predicate_name(lit.predicate.name) variables = ",".join( [self._clean_variable_name(a.name) for a in lit.variables]) pred_str = "{}({})".format(pred_name, variables) return pred_str if isinstance(lit, ForAll): variables = ",".join( [self._clean_variable_name(a.name) for a in lit.variables]) assert len(variables ) == 1, "TODO: support ForAlls over multiple variables" variable = variables[0] var_type = lit.variables[0].var_type objects_of_type = self._type_to_atomnames[var_type] objects_str = "[" + ",".join(objects_of_type) + "]" pred_str_body = self._prolog_goal_line(lit.body) pred_str = "forall(member({}, {}), {})".format( variable, objects_str, pred_str_body) return pred_str if isinstance(lit, Exists): return self._prolog_goal_line( Not(ForAll(Not(lit.body), lit.variables))) raise NotImplementedError()
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 _parse_into_literal(self, string, params, is_effect=False): """Parse the given string (representing either preconditions or effects) into a literal. Check against params to make sure typing is correct. """ assert string[0] == "(" assert string[-1] == ")" if string.startswith("(and") and string[4] in (" ", "\n", "("): clauses = self._find_all_balanced_expressions(string[4:-1].strip()) return LiteralConjunction([ self._parse_into_literal(clause, params, is_effect=is_effect) for clause in clauses ]) if string.startswith("(or") and string[3] in (" ", "\n", "("): clauses = self._find_all_balanced_expressions(string[3:-1].strip()) return LiteralDisjunction([ self._parse_into_literal(clause, params, is_effect=is_effect) for clause in clauses ]) if string.startswith("(forall") and string[7] in (" ", "\n", "("): new_binding, clause = self._find_all_balanced_expressions( string[7:-1].strip()) new_name, new_type_name = new_binding.strip()[1:-1].split("-") new_name = new_name.strip() new_type_name = new_type_name.strip() assert new_name not in params, "ForAll variable {} already exists".format( new_name) params[new_name] = self.types[new_type_name] result = ForAll( self._parse_into_literal(clause, params, is_effect=is_effect), TypedEntity(new_name, params[new_name])) del params[new_name] return result if string.startswith("(exists") and string[7] in (" ", "\n", "("): new_binding, clause = self._find_all_balanced_expressions( string[7:-1].strip()) if new_binding[1:-1] == "": # Handle existential goal with no arguments. body = self._parse_into_literal(clause, params, is_effect=is_effect) return body variables = self._parse_objects(new_binding[1:-1]) for v in variables: params[v.name] = v.var_type body = self._parse_into_literal(clause, params, is_effect=is_effect) result = Exists(variables, body) for v in variables: del params[v.name] return result if string.startswith("(probabilistic") and string[14] in (" ", "\n", "("): assert is_effect, "We only support probabilistic effects" lits = [] probs = [] expr = string[14:-1].strip() for match in re.finditer("(\d*\.?\d+)", expr): prob = float(match.group()) subexpr = self._find_balanced_expression( expr[match.end():].strip(), 0) lit = self._parse_into_literal(subexpr, params, is_effect=is_effect) lits.append(lit) probs.append(prob) return ProbabilisticEffect(lits, probs) if string.startswith("(not") and string[4] in (" ", "\n", "("): clause = string[4:-1].strip() if is_effect: return Anti( self._parse_into_literal(clause, params, is_effect=is_effect)) else: return Not( self._parse_into_literal(clause, params, is_effect=is_effect)) string = string[1:-1].split() pred, args = string[0], string[1:] typed_args = [] # Validate types against the given params dict. assert pred in self.predicates, "Predicate {} is not defined".format( pred) assert self.predicates[pred].arity == len(args), pred for i, arg in enumerate(args): if arg not in params: raise Exception("Argument {} not in params {}".format( arg, params)) assert arg in params, "Argument {} is not in the params".format( arg) if isinstance(params, dict): typed_arg = TypedEntity(arg, params[arg]) else: typed_arg = params[params.index(arg)] typed_args.append(typed_arg) return self.predicates[pred](*typed_args)
def _parse_into_literal(self, string, params, is_effect=False): """Parse the given string (representing either preconditions or effects) into a literal. Check against params to make sure typing is correct. """ assert string[0] == "(" assert string[-1] == ")" if string.startswith("(and") and string[4] in (" ", "\n", "("): clauses = self._find_all_balanced_expressions(string[4:-1].strip()) return LiteralConjunction([ self._parse_into_literal(clause, params, is_effect=is_effect) for clause in clauses ]) if string.startswith("(or") and string[3] in (" ", "\n", "("): clauses = self._find_all_balanced_expressions(string[3:-1].strip()) return LiteralDisjunction([ self._parse_into_literal(clause, params, is_effect=is_effect) for clause in clauses ]) if string.startswith("(forall") and string[7] in (" ", "\n", "("): new_binding, clause = self._find_all_balanced_expressions( string[7:-1].strip()) new_name, new_type_name = new_binding.strip()[1:-1].split("-") new_name = new_name.strip() new_type_name = new_type_name.strip() assert new_name not in params, "ForAll variable {} already exists".format( new_name) params[new_name] = self.types[new_type_name] result = ForAll( self._parse_into_literal(clause, params, is_effect=is_effect), TypedEntity(new_name, params[new_name])) del params[new_name] return result if string.startswith("(exists") and string[7] in (" ", "\n", "("): new_binding, clause = self._find_all_balanced_expressions( string[7:-1].strip()) variables = self._parse_objects(new_binding[1:-1]) for v in variables: params[v.name] = v.var_type body = self._parse_into_literal(clause, params, is_effect=is_effect) result = Exists(variables, body) for v in variables: del params[v.name] return result if string.startswith("(not") and string[4] in (" ", "\n", "("): clause = string[4:-1].strip() if is_effect: return Anti( self._parse_into_literal(clause, params, is_effect=is_effect)) else: return Not( self._parse_into_literal(clause, params, is_effect=is_effect)) string = string[1:-1].split() pred, args = string[0], string[1:] # Validate types against the given params dict. assert pred in self.predicates, "Predicate {} is not defined".format( pred) assert self.predicates[pred].arity == len(args), pred for i, arg in enumerate(args): if arg not in params: import ipdb ipdb.set_trace() assert arg in params, "Argument {} is not in the params".format( arg) return self.predicates[pred](*args)
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))