def _process_rule(self, a, b): # right part first if isinstance(b, Logic): # a -> b & c --> a -> b ; a -> c # (?) FIXME this is only correct when b & c != null ! if b.op == '&': for barg in b.args: self.process_rule(a, barg) # a -> b | c --> !b & !c -> !a # --> a & !b -> c & !b # --> a & !c -> b & !c # # NB: the last two rewrites add 1 term, so the rule *grows* in size. # NB: without catching terminating conditions this could continue infinitely elif b.op == '|': # detect tautology first if not isinstance(a, Logic): # Atom # tautology: a -> a|c|... if a in b.args: raise TautologyDetected(a, b, 'a -> a|c|...') self.process_rule(And(*[Not(barg) for barg in b.args]), Not(a)) for bidx in range(len(b.args)): barg = b.args[bidx] brest = b.args[:bidx] + b.args[bidx + 1:] self.process_rule(And(a, Not(barg)), And(b.__class__(*brest), Not(barg))) else: raise ValueError('unknown b.op %r' % b.op) # left part elif isinstance(a, Logic): # a & b -> c --> IRREDUCIBLE CASE -- WE STORE IT AS IS # (this will be the basis of beta-network) if a.op == '&': assert not isinstance(b, Logic) if b in a.args: raise TautologyDetected(a, b, 'a & b -> a') self.proved_rules.append((a, b)) # XXX NOTE at present we ignore !c -> !a | !b elif a.op == '|': if b in a.args: raise TautologyDetected(a, b, 'a | b -> a') for aarg in a.args: self.process_rule(aarg, b) else: raise ValueError('unknown a.op %r' % a.op) else: # both `a` and `b` are atoms na, nb = name_not(a), name_not(b) self.proved_rules.append((a, b)) # a -> b self.proved_rules.append((nb, na)) # !b -> !a
def get_edge_facts(self, edge, source, target, filter_polarity=None): """ Returns a list of facts used by gather If filter_polarity is None, returns both positive and negative facts. If filter_polarity is True/False, returns only positive/negative. """ a = dict(self.abstract_value) x = (edge, source, target) if ('edge_info', 'all_to_all') + x not in a: # not a well-sorted edge return [] if a[('edge_info', 'none_to_none') + x]: polarity = False elif a[('edge_info', 'all_to_all') + x]: polarity = True else: # not a definite edge return [] if filter_polarity is not None and filter_polarity != polarity: return [] facts = [] edge_concept = self.domain.concepts[edge] source_witnesses = self._get_witnesses(source) target_witnesses = self._get_witnesses(target) for source_c, target_c in product(source_witnesses, target_witnesses): f = edge_concept(source_c, target_c) facts.append(f if polarity else Not(f)) return _normalize_facts(facts)
def z3_implies(f1, f2, timeout=False): """ Use z3 to test if f1 imples f2 """ print "<bhavya> z3_implies called" key = (f1, f2) if key in _implies_cache: return _implies_cache[key] s = z3.Solver() if timeout: s.set("timeout", 2000) # 2 seconds #s.set("timeout", 60000) # 1 minute s.add(to_z3(f1)) s.add(to_z3(Not(f2))) res = s.check() if res == z3.sat: _implies_cache[key] = False return False elif res == z3.unsat: _implies_cache[key] = True return True else: # no caching of unknown results print "z3 returned: {}".format(res) assert False return None
def suppose_empty(self, concept): self.push() f = self.domain.concepts[concept].formula self.suppose_constraints.append( ForAll(free_variables(f), Not(f)) ) self.recompute()
def simp_not(x): if isinstance(x, Not): return x.args[0] if is_true(x): return Or() if is_false(x): return And() return Not(x)
def _process_rule(self, a, b): # right part first # a -> b & c --> a -> b ; a -> c # (?) FIXME this is only correct when b & c != null ! if isinstance(b, And): for barg in b.args: self.process_rule(a, barg) # a -> b | c --> !b & !c -> !a # --> a & !b -> c # --> a & !c -> b elif isinstance(b, Or): # detect tautology first if not isinstance(a, Logic): # Atom # tautology: a -> a|c|... if a in b.args: raise TautologyDetected(a,b, 'a -> a|c|...') self.process_rule(And(*[Not(barg) for barg in b.args]), Not(a)) for bidx in range(len(b.args)): barg = b.args[bidx] brest= b.args[:bidx] + b.args[bidx+1:] self.process_rule(And(a, Not(barg)), Or(*brest)) # left part # a & b -> c --> IRREDUCIBLE CASE -- WE STORE IT AS IS # (this will be the basis of beta-network) elif isinstance(a, And): if b in a.args: raise TautologyDetected(a,b, 'a & b -> a') self.proved_rules.append((a,b)) # XXX NOTE at present we ignore !c -> !a | !b elif isinstance(a, Or): if b in a.args: raise TautologyDetected(a,b, 'a | b -> a') for aarg in a.args: self.process_rule(aarg, b) else: # both `a` and `b` are atoms self.proved_rules.append((a,b)) # a -> b self.proved_rules.append((Not(b), Not(a))) # !b -> !a
def _materialize_edge(self, edge, source, target, polarity): edge_concept = self.domain.concepts[edge] source_c = self._materialize_node(source) if source == target: target_c = source_c else: target_c = self._materialize_node(target) f = edge_concept(source_c, target_c) self.suppose(f if polarity else Not(f)) return (source_c, target_c)
def _get_edge_fact(self, edge, source, target, polarity): facts = [] edge_concept = self.domain.concepts[edge] source_witnesses = self._get_witnesses(source) target_witnesses = self._get_witnesses(target) for source_c, target_c in product(source_witnesses, target_witnesses): f = edge_concept(source_c, target_c) facts.append(f if polarity else Not(f)) return _normalize_facts(facts)
def exclusivity(sort,variants): # partial funciton def pto(s): return Symbol('*>',RelationSort([sort,s])) excs = [partial_function(pto(s)) for s in variants] for s in enumerate(variants): x,y,z = [Variable(n,s) for n,s in [('X',sort),('Y',sort),('Z',s)]] excs.append(Implies(And(pto(s)(x,z),pto(s)(y,z)),Equals(x,y))) for i1,s1 in enumerate(variants): for s2 in variants[:i1]: x,y,z = [Variable(n,s) for n,s in [('X',sort),('Y',s1),('Z',s2)]] excs.append(Not(And(pto(s1)(x,y),pto(s2)(x,z)))) return And(*excs)
def materialize_edge(self, edge, source, target, polarity): """ Materialize an edge to either positive or negative """ self.push() edge_concept = self.domain.concepts[edge] source_c = self._materialize_node(source) if source == target: target_c = source_c else: target_c = self._materialize_node(target) f = edge_concept(source_c, target_c) self.suppose_constraints.append(f if polarity else Not(f)) self.recompute()
def get_node_facts(self, node): """ Returns a list of facts used by gather """ a = dict(self.abstract_value) facts = [] if a[('node_info', 'at_least_one', node)]: for c in self._get_witnesses(node): facts.append(self.domain.concepts[node](c)) for nl in self.domain.concepts['node_labels']: if a.get(('node_label', 'node_necessarily', node, nl)): facts.append(self.domain.concepts[nl](c)) elif a.get(('node_label', 'node_necessarily_not', node, nl)): facts.append(Not(self.domain.concepts[nl](c))) return _normalize_facts(facts)
def deduce_alpha_implications(implications): """deduce all implications Description by example ---------------------- given set of logic rules: a -> b b -> c we deduce all possible rules: a -> b, c b -> c implications: [] of (a,b) return: {} of a -> set([b, c, ...]) """ res = defaultdict(set) for a, b in implications: if a == b: continue # skip a->a cyclic input res[a].add(b) # (x >> a) & (a >> b) => x >> b for fact in res: implied = res[fact] if a in implied: implied.add(b) # (a >> b) & (b >> x) => a >> x if b in res: res[a] |= res[b] # Clean up tautologies and check consistency for a, impl in res.iteritems(): impl.discard(a) na = Not(a) if na in impl: raise ValueError('implications are inconsistent: %s -> %s %s' % (a, na, impl)) return res
def split(self, concept, split_by): """ concept and split_by are concept names. This splits concept into 2 concepts: (concept+split_by) and (concept-split_by) """ c1 = self.concepts[concept] c2 = self.concepts[split_by] variables = c1.variables formula_plus = And(c1(*variables), c2(*variables)) formula_minus = And(c1(*variables), Not(c2(*variables))) new_names = '({}+{})'.format(concept, split_by), '({}-{})'.format(concept, split_by) new_concepts = Concept(variables, formula_plus), Concept(variables, formula_minus) names = self.concepts.keys() self.concepts.update(zip(new_names, new_concepts)) self.concepts = OrderedDict((k, self.concepts[k]) for k in _replace_name(names, concept, new_names)) self.replace_concept(concept, new_names)
def alpha(concept_domain, state, cache=None, projection=None): """ Right now, state is just a plain formula This is a *very* unoptimized implementation """ time1 = time.time() facts = concept_domain.get_facts(projection) time2 = time.time() solver = slvr.new_solver() slvr.solver_add(solver, state) # iu.dbg('state') if cache is None: cache = dict() result = [] for tag, formula in facts: if tag in cache: value = cache[tag] # print "cached: {} = {}".format(tag,value) else: # assert len(cache) == 0, tag solver.push() slvr.solver_add(solver, Not(formula)) value = not slvr.is_sat(solver) solver.pop() cache[tag] = value # print "computed: {} = {}".format(tag,value) result.append((tag, value)) time3 = time.time() # print 'alpha times: get_facts: {} check: {}'.format(time2-time1,time3-time2) return result
def split(self, concept, split_by): """ concept and split_by are concept names. This splits concept into 2 concepts: (concept+split_by) and (concept-split_by) """ c1 = self.concepts[concept] c2 = self.concepts[split_by] variables = c1.variables if isinstance(c2,ConceptSet): formulas = [self.concepts[n](*variables) for n in c2] new_names = ['({}+{})'.format(concept,n) for n in c2] else: formulas = [c2(*variables),Not(c2(*variables))] new_names = ['({}+{})'.format(concept, split_by), '({}-{})'.format(concept, split_by)] new_concepts = [Concept(n,variables,And(c1.formula,f)) for n,f in zip(new_names,formulas)] names = self.concepts.keys() self.concepts.update(zip(new_names, new_concepts)) self.concepts = ConceptDict((k, self.concepts[k]) for k in _replace_name(names, concept, new_names)) self.replace_concept(concept, new_names)
def z3_implies_batch(premise, formulas, timeout=False): """ Use z3 to test if premise implies each formula in formulas Equivalent to: [z3_implies(premise, f) for f in formulas] but more efficient """ #import IPython #IPython.embed() print "<bhavya> z3_implies_batch called" s = z3.Solver() if timeout: s.set("timeout", 2000) # 2 seconds #s.set("timeout", 60000) # 1 minute s.add(to_z3(premise)) result = [] for f in formulas: key = (premise, f) if key in _implies_cache: result.append(_implies_cache[key]) else: s.push() s.add(to_z3(Not(f))) res = s.check() s.pop() if res == z3.sat: _implies_cache[key] = False result.append(False) elif res == z3.unsat: _implies_cache[key] = True result.append(True) else: # no caching of unknown results print "z3 returned: {}".format(res) assert False result.append(None) return result
# no caching of unknown results print "z3 returned: {}".format(res) assert False result.append(None) return result if __name__ == '__main__': S = UninterpretedSort('S') X, Y, Z = (Var(n, S) for n in ['X', 'Y', 'Z']) BinRel = FunctionSort(S, S, Boolean) leq = Const('leq', BinRel) transitive1 = ForAll((X, Y, Z), Implies(And(leq(X, Y), leq(Y, Z)), leq(X, Z))) transitive2 = ForAll((X, Y, Z), Or(Not(leq(X, Y)), Not(leq(Y, Z)), leq(X, Z))) transitive3 = Not( Exists((X, Y, Z), And(leq(X, Y), leq(Y, Z), Not(leq(X, Z))))) antisymmetric = ForAll((X, Y), Implies(And(leq(X, Y), leq(Y, X), true), Eq(Y, X))) print z3_implies(transitive1, transitive2) print z3_implies(transitive2, transitive3) print z3_implies(transitive3, transitive1) print z3_implies(transitive3, antisymmetric) print print z3_implies(true, Iff(transitive1, transitive2)) print x, y = (Const(n, S) for n in ['x', 'y'])
def refuted_goal(goal): from z3_utils import z3_implies axioms = _ivy_interp.background_theory() premise = (and_clauses(axioms, goal.node.clauses)).to_formula() f = Not(goal.formula.to_formula()) return z3_implies(premise, f)
def check_knowledge(knowledge): for symbol in symbols: if model_check(knowledge, symbol): termcolor.cprint(f"{symbol}: YES", "green") elif not model_check(knowledge, Not(symbol)): print(f"{symbol}: MAYBE")
BinaryRelation = FunctionSort(S, S, Boolean) X, Y, Z = (Var(n, S) for n in ['X', 'Y', 'Z']) U = Var('U', UnaryRelation) U1 = Var('U1', UnaryRelation) U2 = Var('U2', UnaryRelation) B = Var('B', BinaryRelation) B1 = Var('B1', BinaryRelation) B2 = Var('B2', BinaryRelation) nstar = Const('nstar', BinaryRelation) x = Const('x', S) y = Const('y', S) c11 = Concept('xy', [X], And(Eq(x, X), Eq(y, X))) c10 = Concept('x', [X], And(Eq(x, X), Not(Eq(y, X)))) c01 = Concept('y', [X], And(Not(Eq(x, X)), Eq(y, X))) c00 = Concept('other', [X], And(Not(Eq(x, X)), Not(Eq(y, X)))) cnstar = Concept('nstar', [X, Y], nstar(X, Y)) cnplus = Concept('nplus', [X, Y], And(nstar(X, Y), Not(Eq(X, Y)))) notexists = ConceptCombiner([U], Not(Exists([X], U(X)))) exists = ConceptCombiner([U], Exists([X], U(X))) singleton = ConceptCombiner([U], ForAll([X, Y], Implies(And(U(X), U(Y)), Eq(X, Y)))) all_to_all = ConceptCombiner([U1, U2, B], ForAll([X, Y], Implies(And(U1(X), U2(Y)), B(X, Y))))
def _suppose_empty(self, concept): f = self.domain.concepts[concept].formula self.suppose_constraints.append(ForAll(free_variables(f), Not(f)))
AKnight = Symbol("A is a Knight") AKnave = Symbol("A is a Knave") BKnight = Symbol("B is a Knight") BKnave = Symbol("B is a Knave") CKnight = Symbol("C is a Knight") CKnave = Symbol("C is a Knave") # Puzzle 0 # A says "I am both a knight and a knave." knowledge0 = And( # Structure of the generic problem Or(AKnight, AKnave), Not(And(AKnight, AKnave)), # Information about what the characters said Implication(AKnight, And(AKnight, AKnave)), Implication(AKnave, Not(And(AKnight, AKnave))) ) # Puzzle 1 # A says "We are both knaves." # B says nothing. knowledge1 = And( # Structure of the generic problem Or(AKnight, AKnave), Or(BKnight, BKnave), Not(And(AKnight, AKnave)), Not(And(BKnight, BKnave)),
Or(CKnave, CKnight)) # Puzzle 0 # A says "I am both a knight and a knave." knowledge0 = And(OnlyOneKindForEach, Implication(AKnight, And(AKnight, AKnave))) # Puzzle 1 # A says "We are both knaves." # B says nothing. WereBothKnaves = And(BKnave, AKnave) knowledge1 = And( OnlyOneKindForEach, Implication(AKnight, WereBothKnaves), Implication(AKnave, Not(WereBothKnaves)), ) # Puzzle 2 # A says "We are the same kind." # B says "We are of different kinds." WereTheSameKind = Or(And(BKnave, AKnave), And(BKnight, AKnight)) WereDifferentKinds = Or(And(BKnave, AKnight), And(BKnight, AKnave)) knowledge2 = And( OnlyOneKindForEach, Implication(AKnight, WereTheSameKind), Implication(AKnave, Not(WereTheSameKind)), Implication(BKnight, WereDifferentKinds), Implication(BKnave, Not(WereDifferentKinds)), )
def get_standard_combiners(): T = TopSort() UnaryRelation = FunctionSort(T, Boolean) BinaryRelation = FunctionSort(T, T, Boolean) X, Y, Z = (Var(n, T) for n in ['X', 'Y', 'Z']) U = Var('U', UnaryRelation) U1 = Var('U1', UnaryRelation) U2 = Var('U2', UnaryRelation) B = Var('B', BinaryRelation) B1 = Var('B1', BinaryRelation) B2 = Var('B2', BinaryRelation) result = OrderedDict() result['none'] = ConceptCombiner([U], Not(Exists([X], U(X)))) result['at_least_one'] = ConceptCombiner([U], Exists([X], U(X))) result['at_most_one'] = ConceptCombiner([U], ForAll([X,Y], Implies(And(U(X), U(Y)), Eq(X,Y)))) result['node_necessarily'] = ConceptCombiner( [U1, U2], ForAll([X], Implies(U1(X), U2(X))), ) result['node_necessarily_not'] = ConceptCombiner( [U1, U2], ForAll([X], Implies(U1(X), Not(U2(X)))), ) result['mutually_exclusive'] = ConceptCombiner( [U1, U2], ForAll([X, Y], Not(And(U1(X), U2(Y)))) ) result['all_to_all'] = ConceptCombiner( [B, U1, U2], ForAll([X,Y], Implies(And(U1(X), U2(Y)), B(X,Y))) ) result['none_to_none'] = ConceptCombiner( [B, U1, U2], ForAll([X,Y], Implies(And(U1(X), U2(Y)), Not(B(X,Y)))) ) result['total'] = ConceptCombiner( [B, U1, U2], ForAll([X], Implies(U1(X), Exists([Y], And(U2(Y), B(X,Y))))) ) result['functional'] = ConceptCombiner( [B, U1, U2], ForAll([X, Y, Z], Implies(And(U1(X), U2(Y), U2(Z), B(X,Y), B(X,Z)), Eq(Y,Z))) ) result['surjective'] = ConceptCombiner( [B, U1, U2], ForAll([Y], Implies(U2(Y), Exists([X], And(U1(X), B(X,Y))))) ) result['injective'] = ConceptCombiner( [B, U1, U2], ForAll([X, Y, Z], Implies(And(U1(X), U1(Y), U2(Z), B(X,Z), B(Y,Z)), Eq(X,Y))) ) result['node_info'] = ['none', 'at_least_one', 'at_most_one'] if False: # this just slows us down, and it's not clear it's needed # later this should be made customizable by the user result['edge_info'] = ['all_to_all', 'none_to_none', 'total', 'functional', 'surjective', 'injective'] else: result['edge_info'] = ['all_to_all', 'none_to_none'] result['node_label'] = ['node_necessarily', 'node_necessarily_not'] return result
UnaryRelationS = FunctionSort(S, Boolean) BinaryRelationS = FunctionSort(S, S, Boolean) UnaryRelationT = FunctionSort(T, Boolean) BinaryRelationT = FunctionSort(T, T, Boolean) X, Y, Z = (Var(n, S) for n in ['X', 'Y', 'Z']) r = Const('r', BinaryRelationS) n = Const('n', BinaryRelationS) p = Const('p', UnaryRelationS) q = Const('q', UnaryRelationS) u = Const('u', UnaryRelationS) c11 = Concept([X], And(p(X), q(X))) c10 = Concept([X], And(p(X), Not(q(X)))) c01 = Concept([X], And(Not(p(X)), q(X))) c00 = Concept([X], And(Not(p(X)), Not(q(X)))) cu = Concept([X], u(X)) crn = Concept([X, Y], Or(r(X,Y), n(X,Y))) combiners = get_standard_combiners() combinations = get_standard_combinations() test('c11') test('c10') test('c01') test('c00') test('cu') test('crn')
def apply_beta_to_alpha_route(alpha_implications, beta_rules): """apply additional beta-rules (And conditions) to already-built alpha implication tables TODO: write about - static extension of alpha-chains - attaching refs to beta-nodes to alpha chains e.g. alpha_implications: a -> [b, !c, d] b -> [d] ... beta_rules: &(b,d) -> e then we'll extend a's rule to the following a -> [b, !c, d, e] """ x_impl = {} for x in alpha_implications.keys(): x_impl[x] = (set(alpha_implications[x]), []) for bcond, bimpl in beta_rules: for bk in bcond.args: if bk in x_impl: continue x_impl[bk] = (set(), []) # static extensions to alpha rules: # A: x -> a,b B: &(a,b) -> c ==> A: x -> a,b,c seen_static_extension = True while seen_static_extension: seen_static_extension = False for bcond, bimpl in beta_rules: assert isinstance(bcond, And) bargs = set(bcond.args) for x, (ximpls, bb) in x_impl.iteritems(): x_all = ximpls | set([x]) # A: ... -> a B: &(...) -> a is non-informative if bimpl not in x_all and bargs.issubset(x_all): ximpls.add(bimpl) # we introduced new implication - now we have to restore # completness of the whole set. bimpl_impl = x_impl.get(bimpl) if bimpl_impl is not None: ximpls |= bimpl_impl[0] seen_static_extension=True # attach beta-nodes which can be possibly triggered by an alpha-chain for bidx, (bcond,bimpl) in enumerate(beta_rules): bargs = set(bcond.args) for x, (ximpls, bb) in x_impl.iteritems(): x_all = ximpls | set([x]) # A: ... -> a B: &(...) -> a (non-informative) if bimpl in x_all: continue # A: x -> a... B: &(!a,...) -> ... (will never trigger) # A: x -> a... B: &(...) -> !a (will never trigger) if any(Not(xi) in bargs or Not(xi) == bimpl for xi in x_all): continue if bargs & x_all: bb.append(bidx) return x_impl
UnaryRelationS = FunctionSort(S, Boolean) BinaryRelationS = FunctionSort(S, S, Boolean) UnaryRelationT = FunctionSort(T, Boolean) BinaryRelationT = FunctionSort(T, T, Boolean) X, Y, Z = (Var(n, S) for n in ['X', 'Y', 'Z']) r = Const('r', BinaryRelationS) n = Const('n', BinaryRelationS) p = Const('p', UnaryRelationS) q = Const('q', UnaryRelationS) u = Const('u', UnaryRelationS) c11 = Concept('both',[X], And(p(X), q(X))) c10 = Concept('onlyp',[X], And(p(X), Not(q(X)))) c01 = Concept('onlyq',[X], And(Not(p(X)), q(X))) c00 = Concept('none',[X], And(Not(p(X)), Not(q(X)))) cu = Concept('u',[X], u(X)) crn = Concept('r_or_n',[X, Y], Or(r(X,Y), n(X,Y))) combiners = get_standard_combiners() combinations = get_standard_combinations() test('c11') test('c10') test('c01') test('c00') test('cu') test('crn')
Or(knife, revolver, wrench) ) # Initial cards knowledge.add(And( Not(mustard), Not(kitchen), Not(revolver) )) # Unknown card knowledge.add(Or( Not(scarlet), Not(library), Not(wrench) )) """ # Known cards knowledge.add(Not(Black)) knowledge.add(Not(Rosa)) knowledge.add(Not(cano)) knowledge.add(Not(hall)) knowledge.add(Not(cozinha)) knowledge.add(Not(corda)) knowledge.add( And( Or(Not(Violeta), Not(biblioteca), Not(chave)), Or(Not(Branca), Not(jantar), Not(revolver)), Or(Not(Branca), Not(estar), Not(chave)), )) knowledge.add( And(
def apply_beta_to_alpha_route(alpha_implications, beta_rules): """apply additional beta-rules (And conditions) to already-built alpha implication tables TODO: write about - static extension of alpha-chains - attaching refs to beta-nodes to alpha chains e.g. alpha_implications: a -> [b, !c, d] b -> [d] ... beta_rules: &(b,d) -> e then we'll extend a's rule to the following a -> [b, !c, d, e] """ x_impl = {} for x in alpha_implications.keys(): x_impl[x] = (alpha_implications[x][:], []) for bcond, bimpl in beta_rules: for bk in bcond.args: if bk in x_impl: continue x_impl[bk] = ([], []) # we do it in 2 phases: # # 1st phase -- only do static extensions to alpha rules # 2nd phase -- attach beta-nodes which can be possibly triggered by an # alpha-chain phase = 1 while True: seen_static_extension = False for bidx, (bcond, bimpl) in enumerate(beta_rules): assert isinstance(bcond, And) for x, (ximpls, bb) in x_impl.iteritems(): # A: x -> a B: &(...) -> x (non-informative) if x == bimpl: # XXX bimpl may become a list continue # A: ... -> a B: &(...) -> a (non-informative) if bimpl in ximpls: continue # A: x -> a B: &(a,!x) -> ... (will never trigger) if Not(x) in bcond.args: continue # A: x -> a... B: &(!a,...) -> ... (will never trigger) # A: x -> a... B: &(...) -> !a (will never trigger) if any( Not(xi) in bcond.args or Not(xi) == bimpl for xi in ximpls): continue # A: x -> a,b B: &(a,b) -> c (static extension) # | # A: x -> a,b,c <-----------------------+ for barg in bcond.args: if not ((barg == x) or (barg in ximpls)): break else: assert phase == 1 list_populate(ximpls, bimpl) # XXX bimpl may become a list # we introduced new implication - now we have to restore # completness of the whole set. bimpl_impl = x_impl.get(bimpl) if bimpl_impl is not None: for _ in bimpl_impl[0]: list_populate(ximpls, _) seen_static_extension = True continue # does this beta-rule even has a chance to be triggered ? if phase == 2: for barg in bcond.args: if (barg == x) or (barg in ximpls): bb.append(bidx) break # no static extensions was seen at this pass -- lets move to phase2 if phase == 1 and (not seen_static_extension): phase = 2 continue # let's finish at the end of phase2 if phase == 2: break return x_impl
from logic import And, Or, Symbol, Biconditional, Implication, Not, model_check AKnight = Symbol("A is a Knight") AKnave = Symbol("A is a Knave") BKnight = Symbol("B is a Knight") BKnave = Symbol("B is a Knave") CKnight = Symbol("C is a Knight") CKnave = Symbol("C is a Knave") # Puzzle 0 # A says "I am both a knight and a knave." knowledge0 = And(Or(AKnight, AKnave), Implication(AKnight, Not(AKnave)), Implication(AKnave, Not(AKnight)), Biconditional(AKnight, And(AKnight, AKnave))) # Puzzle 1 # A says "We are both knaves." # B says nothing. knowledge1 = And(Or(AKnight, AKnave), Implication(AKnight, Not(AKnave)), Implication(AKnave, Not(AKnight)), Or(BKnight, BKnave), Implication(BKnight, Not(BKnave)), Implication(BKnave, Not(BKnight)), Biconditional(AKnight, And(AKnave, BKnave))) # Puzzle 2 # A says "We are the same kind." # B says "We are of different kinds." knowledge2 = And( Or(AKnight, AKnave), Implication(AKnight, Not(AKnave)),