def ensure_conjunction_sat(system, *parts): """Modifies the constraint system such that it is only solvable if the conjunction of all parts is satisfiable. Each part must be an iterator, generator, or an iterable over literals.""" pos = defaultdict(set) neg = defaultdict(set) for literal in itertools.chain(*parts): if literal.predicate == "=": # use (in)equalities in conditions if literal.negated: n = constraints.NegativeClause([literal.args]) system.add_negative_clause(n) else: a = constraints.Assignment([literal.args]) system.add_assignment_disjunction([a]) else: if literal.negated: neg[literal.predicate].add(literal) else: pos[literal.predicate].add(literal) for pred, posatoms in pos.items(): if pred in neg: for posatom in posatoms: for negatom in neg[pred]: parts = list(zip(negatom.args, posatom.args)) if parts: negative_clause = constraints.NegativeClause(parts) system.add_negative_clause(negative_clause)
def minimal_covering_renamings(self, action, add_effect, inv_vars): """computes the minimal renamings of the action parameters such that the add effect is covered by the action. Each renaming is an constraint system""" # add_effect must be covered assigs = self.get_covering_assignments(inv_vars, add_effect.literal) # renaming of operator parameters must be minimal minimal_renamings = [] params = [p.name for p in action.parameters] for assignment in assigs: system = constraints.ConstraintSystem() system.add_assignment(assignment) mapping = assignment.get_mapping() if params: if len(params) == 2: n1 = params[0] n2 = params[1] if mapping.get(n1, n1) != mapping.get(n2, n2): negative_clause = constraints.NegativeClause([(n1, n2) ]) system.add_negative_clause(negative_clause) else: for (n1, n2) in itertools.combinations(params, 2): if mapping.get(n1, n1) != mapping.get(n2, n2): negative_clause = constraints.NegativeClause([ (n1, n2) ]) system.add_negative_clause(negative_clause) minimal_renamings.append(system) return minimal_renamings
def ensure_inequality(system, literal1, literal2): """Modifies the constraint system such that it is only solvable if the literal instantiations are not equal (ignoring whether one is negated and the other is not)""" if (literal1.predicate == literal2.predicate and literal1.args): parts = list(zip(literal1.args, literal2.args)) system.add_negative_clause(constraints.NegativeClause(parts))
def conditions_require_weight_1(self, action, add_effect): inv_vars = find_unique_variables(action, self) minimal_renamings = self.minimal_covering_renamings(action, add_effect, inv_vars) relevant_conditions = set(get_literals(action.condition[0])) relevant_conditions |= set(get_literals(add_effect.condition[0])) relevant_conditions = [atom for atom in relevant_conditions if not atom.negated and self.predicate_to_part.get(atom.predicate)] negative_clauses = [] for atom in relevant_conditions: a = self.get_covering_assignments(inv_vars, atom)[0] if not len(a.equalities): return True negative_clauses.append(constraints.NegativeClause(a.equalities)) for renaming in minimal_renamings: for clause in negative_clauses: system = renaming.copy() system.add_negative_clause(clause) if not system.is_solvable(): break else: return False return True
def unbalanced_renamings(self, del_effect, add_effect, inv_vars, lhs_by_pred, unbalanced_renamings): """returns the renamings from unbalanced renamings for which the del_effect does not balance the add_effect.""" system = constraints.ConstraintSystem() ensure_cover(system, del_effect.literal, self, inv_vars) # Since we may only rename the quantified variables of the delete effect # we need to check that "renamings" of constants are already implied by # the unbalanced_renaming (of the of the operator parameters). The # following system is used as a helper for this. It builds a conjunction # that formulates that the constants are NOT renamed accordingly. We # below check that this is impossible with each unbalanced renaming. check_constants = False constant_test_system = constraints.ConstraintSystem() for a, b in system.combinatorial_assignments[0][0].equalities: # first 0 because the system was empty before we called ensure_cover # second 0 because ensure_cover only adds assignments with one entry if b[0] != "?": check_constants = True neg_clause = constraints.NegativeClause([(a, b)]) constant_test_system.add_negative_clause(neg_clause) ensure_inequality(system, add_effect.literal, del_effect.literal) still_unbalanced = [] for renaming in unbalanced_renamings: if check_constants: new_sys = constant_test_system.combine(renaming) if new_sys.is_solvable(): # it is possible that the operator arguments are not # mapped to constants as required for covering the delete # effect still_unbalanced.append(renaming) continue new_sys = system.combine(renaming) if self.lhs_satisfiable(renaming, lhs_by_pred): implies_system = self.imply_del_effect(del_effect, lhs_by_pred) if not implies_system: still_unbalanced.append(renaming) continue new_sys = new_sys.combine(implies_system) if not new_sys.is_solvable(): still_unbalanced.append(renaming) return still_unbalanced