def simp(x): "Simplify the expression x." if isnumber(x) or not x.args: return x args = list(map(simp, x.args)) u, op, v = args[0], x.op, args[-1] if op == '+': if v == 0: return u if u == 0: return v if u == v: return 2 * u if u == -v or v == -u: return 0 elif op == '-' and len(args) == 1: if u.op == '-' and len(u.args) == 1: return u.args[0] # --y ==> y elif op == '-': if v == 0: return u if u == 0: return -v if u == v: return 0 if u == -v or v == -u: return 0 elif op == '*': if u == 0 or v == 0: return 0 if u == 1: return v if v == 1: return u if u == v: return u**2 elif op == '/': if u == 0: return 0 if v == 0: return Expr('Undefined') if u == v: return 1 if u == -v or v == -u: return 0 elif op == '**': if u == 0: return 0 if v == 0: return 1 if u == 1: return 1 if v == 1: return u elif op == 'log': if u == 1: return 0 else: raise ValueError("Unknown op: " + op) return Expr(op, *args)
def translate_to_SAT(init, transition, goal, time): clauses = [] states = [state for state in transition] #Symbol claiming state s at time t state_counter = itertools.count() for s in states: for t in range(time + 1): state_sym[(s, t)] = Expr("State_{}".format(next(state_counter))) #Add initial state axiom clauses.append(state_sym[init, 0]) #Add goal state axiom clauses.append(state_sym[goal, time]) #All possible transitions transition_counter = itertools.count() for s in states: for action in transition[s]: s_ = transition[s][action] for t in range(time): #Action 'action' taken from state 's' at time 't' to reach 's_' action_sym[(s, action, t)] = Expr("Transition_{}".format( next(transition_counter))) # Change the state from s to s_ clauses.append(action_sym[s, action, t] | '==>' | state_sym[s, t]) clauses.append(action_sym[s, action, t] | '==>' | state_sym[s_, t + 1]) #Allow only one state at any time for t in range(time + 1): #must be a state at any time clauses.append(associate('|', [state_sym[s, t] for s in states])) for s in states: for s_ in states[states.index(s) + 1:]: #for each pair of states s, s_ only one is possible at time t clauses.append((~state_sym[s, t]) | (~state_sym[s_, t])) #Restrict to one transition per timestep for t in range(time): #list of possible transitions at time t transitions_t = [tr for tr in action_sym if tr[2] == t] #make sure atleast one of the transition happens clauses.append( associate('|', [action_sym[tr] for tr in transitions_t])) for tr in transitions_t: for tr_ in transitions_t[transitions_t.index(tr) + 1:]: #there cannot be two transitions tr and tr_ at time t clauses.append((~action_sym[tr]) | (~action_sym[tr_])) #Combine the clauses to form the cnf return associate('&', clauses)
def standardize_variables(sentence, dic=None): if dic is None: dic = {} if not isinstance(sentence, Expr): return sentence elif is_var_symbol(sentence.op): if sentence in dic: return dic[sentence] else: v = Expr('v_{}'.format(next(standardize_variables.counter))) dic[sentence] = v return v else: return Expr(sentence.op, *[standardize_variables(a, dic) for a in sentence.args])
def diff(y, x): if y == x: return 1 elif not y.args: return 0 else: u, op, v = y.args[0], y.op, y.args[-1] if op == '+': return diff(u, x) + diff(v, x) elif op == '-' and len(y.args) == 1: return -diff(u, x) elif op == '-': return diff(u, x) - diff(v, x) elif op == '*': return u * diff(v, x) + v * diff(u, x) elif op == '/': return (v * diff(u, x) - u * diff(v, x)) / (v * v) elif op == '**' and isnumber(x.op): return (v * u**(v - 1) * diff(u, x)) elif op == '**': return (v * u**(v - 1) * diff(u, x) + u**v * Expr('log')(u) * diff(v, x)) elif op == 'log': return diff(u, x) / u else: raise ValueError("Unknown op: {} in diff({}, {})".format(op, y, x))
def refinements( hla, state, library ): # TODO - refinements may be (multiple) HLA themselves ... """ state is a Problem, containing the current state kb library is a dictionary containing details for every possible refinement. eg: { "HLA": [ "Go(Home,SFO)", "Go(Home,SFO)", "Drive(Home, SFOLongTermParking)", "Shuttle(SFOLongTermParking, SFO)", "Taxi(Home, SFO)" ], "steps": [ ["Drive(Home, SFOLongTermParking)", "Shuttle(SFOLongTermParking, SFO)"], ["Taxi(Home, SFO)"], [], # empty refinements ie primitive action [], [] ], "precond_pos": [ ["At(Home), Have(Car)"], ["At(Home)"], ["At(Home)", "Have(Car)"] ["At(SFOLongTermParking)"] ["At(Home)"] ], "precond_neg": [[],[],[],[],[]], "effect_pos": [ ["At(SFO)"], ["At(SFO)"], ["At(SFOLongTermParking)"], ["At(SFO)"], ["At(SFO)"] ], "effect_neg": [ ["At(Home)"], ["At(Home)"], ["At(Home)"], ["At(SFOLongTermParking)"], ["At(Home)"] ] } """ e = Expr(hla.name, hla.args) indices = [ i for i, x in enumerate(library["HLA"]) if expr(x).op == hla.name ] for i in indices: action = HLA( expr(library["steps"][i][0]), [ # TODO multiple refinements [expr(x) for x in library["precond_pos"][i]], [expr(x) for x in library["precond_neg"][i]] ], [[expr(x) for x in library["effect_pos"][i]], [expr(x) for x in library["effect_neg"][i]]]) if action.check_precond(state.kb, action.args): yield action
def S_generator(n, m, k, q): """ :param n: the number of distinct propositional symbols in S :param m: the number of clauses in S :param k: the maximum number of literals in a clause in S :param q: 0.4 <= q <= 0.6, q is the probability that a literal in a clause in a negative literal :return: a random set of clauses """ if q < 0.4 or q > 0.6: raise ValueError("q should be between 0.4 and 0.6(inclusive)") letters = string.ascii_uppercase len_letters = len(letters) if n > len_letters: raise ValueError("n should be less than or equal to " + str(len_letters)) unique_symbols = list() for i in range(n): unique_symbols.append(Expr(letters[i])) S = list() for i in range(m): n_lt = random.randint(1, k) clause = None for j in range(n_lt): idx = random.randint(0, n - 1) sym = unique_symbols[idx] if probability(q): sym = sym.__invert__() if clause is None: clause = sym else: clause = clause.__or__(sym) S.append(clause) print("clauses S: " + str(S) + "\n") return S
def substitute(self, e, args): """Replaces variables in expression with their respective Propostional symbol""" new_args = [ args[i] for x in e.args for i in range(len(self.args)) if self.args[i] == x ] return Expr(e.op, *new_args)
def diff(y, x): """Return the symbolic derivative, dy/dx, as an Expr. However, you probably want to simplify the results with simp. >>> diff(x * x, x) ((x * 1) + (x * 1)) """ if y == x: return 1 elif not y.args: return 0 else: u, op, v = y.args[0], y.op, y.args[-1] if op == '+': return diff(u, x) + diff(v, x) elif op == '-' and len(y.args) == 1: return -diff(u, x) elif op == '-': return diff(u, x) - diff(v, x) elif op == '*': return u * diff(v, x) + v * diff(u, x) elif op == '/': return (v * diff(u, x) - u * diff(v, x)) / (v * v) elif op == '**' and isnumber(x.op): return (v * u**(v - 1) * diff(u, x)) elif op == '**': return (v * u**(v - 1) * diff(u, x) + u**v * Expr('log')(u) * diff(v, x)) elif op == 'log': return diff(u, x) / u else: raise ValueError("Unknown op: {} in diff({}, {})".format(op, y, x))
def substitute(self, e, args): """Replaces variables in expression with their respective Propostional symbol""" new_args = list(e.args) for num, x in enumerate(e.args): for i in range(len(self.args)): if self.args[i] == x: new_args[num] = args[i] return Expr(e.op, *new_args)
def translate_to_SAT(init, transition, goal, time): clauses = [] states = [state for state in transition] state_counter = itertools.count() for s in states: for t in range(time + 1): state_sym[s, t] = Expr("State_{}".format(next(state_counter))) clauses.append(state_sym[init, 0]) clauses.append(state_sym[goal, time]) transition_counter = itertools.count() for s in states: for action in transition[s]: s_ = transition[s][action] for t in range(time): action_sym[s, action, t] = Expr("Transition_{}".format( next(transition_counter))) clauses.append(action_sym[s, action, t] | '==>' | state_sym[s, t]) clauses.append(action_sym[s, action, t] | '==>' | state_sym[s_, t + 1]) for t in range(time + 1): clauses.append(associate('|', [state_sym[s, t] for s in states])) for s in states: for s_ in states[states.index(s) + 1:]: clauses.append((~state_sym[s, t]) | (~state_sym[s_, t])) for t in range(time): transitions_t = [tr for tr in action_sym if tr[2] == t] clauses.append( associate('|', [action_sym[tr] for tr in transitions_t])) for tr in transitions_t: for tr_ in transitions_t[transitions_t.index(tr) + 1:]: clauses.append(~action_sym[tr] | ~action_sym[tr_]) return associate('&', clauses)
def build(self, actions, objects): """Populates the lists and dictionaries containing the state action dependencies""" for clause in self.current_state: p_expr = Expr('P' + clause.op, *clause.args) self.current_action_links[p_expr] = [clause] self.next_action_links[p_expr] = [clause] self.current_state_links[clause] = [p_expr] self.next_state_links[clause] = [p_expr] for a in actions: num_args = len(a.args) possible_args = tuple(itertools.permutations(objects, num_args)) for arg in possible_args: if a.check_precond(self.kb, arg): for num, symbol in enumerate(a.args): if not symbol.op.islower(): arg = list(arg) arg[num] = symbol arg = tuple(arg) new_action = a.substitute(Expr(a.name, *a.args), arg) self.current_action_links[new_action] = [] for clause in a.precond: new_clause = a.substitute(clause, arg) self.current_action_links[new_action].append(new_clause) if new_clause in self.current_state_links: self.current_state_links[new_clause].append(new_action) else: self.current_state_links[new_clause] = [new_action] self.next_action_links[new_action] = [] for clause in a.effect: new_clause = a.substitute(clause, arg) self.next_action_links[new_action].append(new_clause) if new_clause in self.next_state_links: self.next_state_links[new_clause].append(new_action) else: self.next_state_links[new_clause] = [new_action]
def subst(s, x): if isinstance(x, list): return [subst(s, xi) for xi in x] elif isinstance(x, tuple): return tuple([subst(s, xi) for xi in x]) elif not isinstance(x, Expr): return x elif is_var_symbol(x.op): return s.get(x, x) else: return Expr(x.op, *[subst(s, arg) for arg in x.args])
def associate(op, args): # Given an associative op, return an expression with the same # meaning as Expr(op, *args) args = dissociate(op, args) if len(args) == 0: return _op_identity[op] elif len(args) == 1: return args[0] else: return Expr(op, *args)
def find_mutex(self): """Finds mutually exclusive actions""" # Inconsistent effects pos_nsl, neg_nsl = self.separate(self.next_state_links) for negeff in neg_nsl: new_negeff = Expr(negeff.op[3:], *negeff.args) for poseff in pos_nsl: if new_negeff == poseff: for a in self.next_state_links[poseff]: for b in self.next_state_links[negeff]: if {a, b} not in self.mutex: self.mutex.append({a, b}) # Interference will be calculated with the last step pos_csl, neg_csl = self.separate(self.current_state_links) # Competing needs for posprecond in pos_csl: for negprecond in neg_csl: new_negprecond = Expr(negprecond.op[3:], *negprecond.args) if new_negprecond == posprecond: for a in self.current_state_links[posprecond]: for b in self.current_state_links[negprecond]: if {a, b} not in self.mutex: self.mutex.append({a, b}) # Inconsistent support state_mutex = [] for pair in self.mutex: next_state_0 = self.next_action_links[list(pair)[0]] if len(pair) == 2: next_state_1 = self.next_action_links[list(pair)[1]] else: next_state_1 = self.next_action_links[list(pair)[0]] if (len(next_state_0) == 1) and (len(next_state_1) == 1): state_mutex.append({next_state_0[0], next_state_1[0]}) self.mutex = self.mutex + state_mutex
def act(self, kb, args): """Executes the action on the state's knowledge base""" if isinstance(kb, list): kb = FolKB(kb) if not self.check_precond(kb, args): raise Exception('Action pre-conditions not satisfied') for clause in self.effect: kb.tell(self.substitute(clause, args)) if clause.op[:3] == 'Not': new_clause = Expr(clause.op[3:], *clause.args) if kb.ask(self.substitute(new_clause, args)) is not False: kb.retract(self.substitute(new_clause, args)) else: new_clause = Expr('Not' + clause.op, *clause.args) if kb.ask(self.substitute(new_clause, args)) is not False: kb.retract(self.substitute(new_clause, args)) return kb
def distribute_and_over_or_withbug(s): """Given a sentence s consisting of conjunctions and disjunctions of literals, return an equivalent sentence in CNF. >>> distribute_and_over_or((A & B) | C) ((A | B) & (B | C)) """ s = expr(s) if not isinstance(s, Expr): return s #firstParseArgs = tuple([distribute_and_over_or(arg) for arg in s.args]) #s = Expr(s.op, firstParseArgs) if s.op == '|': for subarg in s.args: if subarg.op == '&': otherArgs = [ otherArg for otherArg in s.args if otherArg != subarg] newArgs = [] if len(otherArgs) > 1: otherExpr = Expr('|', tuple(otherArgs)) for t in subarg.args: newArgs.append(Expr('|', t, otherExpr)) else: otherExpr = otherArgs[0] for t in subarg.args: newArgs.append(Expr('|', t, otherExpr)) toReturn = Expr('&') toReturn.args = tuple(newArgs) return toReturn else: newArgs = [] for subarg in s.args: newArgs.append(distribute_and_over_or(subarg)) s.args = tuple(newArgs) return s
def subst(s, x): """Substitute the substitution s into the expression x. >>> subst({x: 42, y:0}, F(x) + y) (F(42) + 0) """ if isinstance(x, list): return [subst(s, xi) for xi in x] elif isinstance(x, tuple): return tuple([subst(s, xi) for xi in x]) elif not isinstance(x, Expr): return x elif is_var_symbol(x.op): return s.get(x, x) else: return Expr(x.op, *[subst(s, arg) for arg in x.args])
def associate(op, args): """Dada uma op associativa, retornar uma expressão com o mesmo significado como Expr (op, * args), ou seja, com instâncias aninhadas do mesmo grupo promovido ao nível superior. >>> associate('&', [(A&B),(B|C),(B&C)]) (A & B & (B | C) & B & C) >>> associate('|', [A|(B|(C|(A&B)))]) (A | B | C | (A & B)) """ args = dissociate(op, args) if len(args) == 0: return _op_identity[op] elif len(args) == 1: return args[0] else: return Expr(op, *args)
def associate(op, args): """Given an associative op, return an expression with the same meaning as Expr(op, *args), but flattened -- that is, with nested instances of the same op promoted to the top level. >>> associate('&', [(A&B),(B|C),(B&C)]) (A & B & (B | C) & B & C) >>> associate('|', [A|(B|(C|(A&B)))]) (A | B | C | (A & B)) """ args = dissociate(op, args) if len(args) == 0: return _op_identity[op] elif len(args) == 1: return args[0] else: return Expr(op, *args)
def eliminate_implications(s): "Altera as implicações em forma equivalente com apenas &, |, e ~ como operadores lógicos." s = expr(s) if not s.args or is_symbol(s.op): return s args = list(map(eliminate_implications, s.args)) a, b = args[0], args[-1] if s.op == '==>': return b | ~a elif s.op == '<==': return a | ~b elif s.op == '<=>': return (a | ~b) & (b | ~a) elif s.op == '^': assert len(args) == 2 return (a & ~b) | (~a & b) else: assert s.op in ('&', '|', '~') return Expr(s.op, *args)
def eliminate_implications(s): """Change implications into equivalent form with only &, |, and ~ as logical operators.""" s = expr(s) if not s.args or is_symbol(s.op): return s # Atoms are unchanged. args = list(map(eliminate_implications, s.args)) a, b = args[0], args[-1] if s.op == '==>': return b | ~a elif s.op == '<==': return a | ~b elif s.op == '<=>': return (a | ~b) & (b | ~a) elif s.op == '^': assert len(args) == 2 # TODO: relax this restriction return (a & ~b) | (~a & b) else: assert s.op in ('&', '|', '~') return Expr(s.op, *args)
def move_not_inwards(s): """Rewrite sentence s by moving negation sign inward. >>> move_not_inwards(~(A | B)) (~A & ~B)""" s = expr(s) if s.op == '~': def NOT(b): return move_not_inwards(~b) a = s.args[0] if a.op == '~': return move_not_inwards(a.args[0]) # ~~A ==> A if a.op == '&': return associate('|', list(map(NOT, a.args))) if a.op == '|': return associate('&', list(map(NOT, a.args))) return s elif is_symbol(s.op) or not s.args: return s else: return Expr(s.op, *list(map(move_not_inwards, s.args)))
def move_not_inwards(s): """Reescreva sentenças s movendo sinal de negação. >>> move_not_inwards(~(A | B)) (~A & ~B)""" s = expr(s) if s.op == '~': def NOT(b): return move_not_inwards(~b) a = s.args[0] if a.op == '~': return move_not_inwards(a.args[0]) # ~~A ==> A if a.op == '&': return associate('|', list(map(NOT, a.args))) if a.op == '|': return associate('&', list(map(NOT, a.args))) return s elif is_symbol(s.op) or not s.args: return s else: return Expr(s.op, *list(map(move_not_inwards, s.args)))
def build(self, actions, objects): #Add persistence actions for positive states for clause in self.current_state_pos: self.current_action_links_pos[Expr('Persistence', clause)] = [clause] self.next_action_links[Expr('Persistence', clause)] = [clause] self.current_state_links_pos[clause] = [ Expr('Persistence', clause) ] self.next_state_links_pos[clause] = [Expr('Persistence', clause)] #Add persistence actions for negative states for clause in self.current_state_neg: self.current_action_links_neg[Expr( 'Persistence', Expr('not' + clause.op, clause.args))] = [clause] self.next_action_links[Expr('Persistence', Expr('not' + clause.op, clause.args))] = [clause] self.current_state_links_neg[clause] = [ Expr('Persistence', Expr('not' + clause.op, clause.args)) ] self.next_state_links_neg[clause] = [ Expr('Persistence', Expr('not' + clause.op, clause.args)) ] for a in actions: num_args = len(a.args) possible_args = tuple(itertools.permutations(objects, num_args)) for arg in possible_args: if a.check_precond(self.poskb, arg): for num, symbol in enumerate(a.args): if not symbol.op.islower(): arg = list(arg) arg[num] = symbol arg = tuple(arg) new_action = a.substitute(Expr(a.name, *a.args), arg) self.current_action_links_pos[new_action] = [] self.current_action_links_neg[new_action] = [] for clause in a.precond_pos: new_clause = a.substitute(clause, arg) self.current_action_links_pos[new_action].append( new_clause) if new_clause in self.current_state_links_pos: self.current_state_links_pos[new_clause].append( new_action) else: self.current_state_links_pos[new_clause] = [ new_action ] for clause in a.precond_neg: new_clause = a.substitute(clause, arg) #new_clause = Expr('not'+new_clause.op, new_clause.arg) self.current_action_links_neg[new_action].append( new_clause) if new_clause in self.current_state_links_neg: self.current_state_links_neg[new_clause].append( new_action) else: self.current_state_links_neg[new_clause] = [ new_action ] self.next_action_links[new_action] = [] for clause in a.effect_add: new_clause = a.substitute(clause, arg) self.next_action_links[new_action].append(new_clause) if new_clause in self.next_state_links_pos: self.next_state_links_pos[new_clause].append( new_action) else: self.next_state_links_pos[new_clause] = [ new_action ] for clause in a.effect_rem: new_clause = a.substitute(clause, arg) self.next_action_links[new_action].append(new_clause) if new_clause in self.next_state_links_neg: self.next_state_links_neg[new_clause].append( new_action) else: self.next_state_links_neg[new_clause] = [ new_action ]
def ask_generator(self, query): """Yield the empty substitution {} if KB entails query; else no results.""" if tt_entails(Expr('&', *self.clauses), query): yield {}
def implies_and_implies(lhs, rhs): return Expr('<=>', lhs, rhs)
def implies(lhs, rhs): return Expr('==>', lhs, rhs)
def location(x, y, time=None): if time is None: return Expr('L', x, y) else: return Expr('L', x, y, time)
def ok_to_move(x, y, time): return Expr('OK', x, y, time)
def turn_right(time): return Expr('TurnRight', time)
def turn_left(time): return Expr('TurnLeft', time)