def __init__(self, premise: Term, conclusion: Optional[Term] = None): self.premise = premise self.steps: List[Tuple[RewriteRule, int]] = list() self.current_step = deepcopy(premise) self.conclusion = conclusion if conclusion is not None else Boolean.trueb self.unproven_subgoals: List['Lemma'] = list() self.proven_subgoals: List['Lemma'] = list() self.hypotheses = RewriteSystem() self._auto_load_systems()
def simplify(x: Term, bound: int = -1): """Load in theory systems needed to normalize a term.""" hypotheses = RewriteSystem() sorts = set((v.sort for v in get_vars_or_constants(x, True))) for sort in sorts: tsystem: Optional[TheorySystem] = system_from_sort(sort) if tsystem is not None: print(tsystem.rules) hypotheses.extend(tsystem.rules) return normal(x, hypotheses, bound)
class TheorySystem: """ Contains a sort and the RewriteSystem that governs it. """ sort: Sort = None rules: RewriteSystem = RewriteSystem() definitions: Dict[Function, RewriteSystem] = dict() @classmethod def simplify(cls, x: Term, bound: int = -1) -> Optional[Term]: """ Simplify a term using the convergent rewrite rules known. """ if not isinstance(x, FuncTerm): raise ValueError("simplify function expects a FuncTerm.") return normal(x, cls.rules, bound)[0] @classmethod def signature(cls) -> List[Term]: """List the signature of the system.""" el = [] for term in cls.__dict__.values(): if not isinstance(term, (Constant, Function)): continue el.append(deepcopy(term)) return el @classmethod def __len__(cls) -> int: """Return the number of elements.""" return len(filter(lambda x: isinstance(x, Constant)), cls.__dict__.values()) @classmethod def add_rule(cls, rule: RewriteRule) -> None: """Add a rule to the system.""" if not isinstance(rule, RewriteRule): raise ValueError( f"add_rule expected a RewriteRule not a '{type(rule)}'.") cls.rules.append(rule) @classmethod def define(cls, function: Function, rules: RewriteSystem): """Define a function by a rewrite system.""" setattr(cls, function.symbol, function) # TODO: Make sure RewriteSystem terminates # TODO: Does composition of terminating rewrite systems terminate? for rule in rules: cls.add_rule(rule) cls.definitions[function] = rules
#!/usr/bin/env python3 from symcollab.algebra import Constant, Function, Variable from symcollab.rewrite import RewriteRule, RewriteSystem, narrow, is_finite, Variants f = Function("f", 2) x = Variable("x") a = Constant("a") b = Constant("b") r = RewriteRule(f(x, x), x) r2 = RewriteRule(f(a, x), b) print("Rewrite Rule 1:", r) print("Rewrite Rule 2:", r2) term = f(a, f(b, b)) rs = RewriteSystem({r, r2}) vt = Variants(term, rs) print("Variants of", term, ":", list(vt)) print("Variants Finite?", is_finite(vt, -1)) print("Rewrite rule from", term, "to", f(a, b), narrow(term, f(a,b), rs, -1))
from symcollab.algebra import Function, Variable from symcollab.rewrite import RewriteRule, RewriteSystem from .inductive import Inductive, TheorySystem @Inductive class Prop(TheorySystem): pass A = Variable("A", sort=Prop.sort) B = Variable("B", sort=Prop.sort) C = Variable("C", sort=Prop.sort) Not = Function("not", 1, domain_sort=Prop.sort, range_sort=Prop.sort) Prop.define(Not, RewriteSystem({RewriteRule(Not(Not(A)), A)})) And = Function("and", 2, domain_sort=Prop.sort, range_sort=Prop.sort) Prop.define(And, RewriteSystem({ RewriteRule(And(A, A), A), })) Or = Function("or", 2, domain_sort=Prop.sort, range_sort=Prop.sort) Prop.define(Or, RewriteSystem({ RewriteRule(Or(A, A), A), })) Implies = Function("implies", 2, domain_sort=Prop.sort, range_sort=Prop.sort) Prop.define(Implies, RewriteSystem({RewriteRule(Implies(A, B), Or(Not(A), B))}))
"""Definition and properties for two-arity tuple.""" from symcollab.algebra import Function, Variable from symcollab.rewrite import RewriteRule, RewriteSystem from .inductive import TheorySystem, Inductive @Inductive class Pair(TheorySystem): pair = Function("pair", 2) # Fix domain of pair to take anything Pair.pair.domain_sort = None # Variables for later rules _a = Variable("a", sort=Pair.sort) _b = Variable("b", sort=Pair.sort) fst = Function("fst", 1, domain_sort=Pair.sort) Pair.define(fst, RewriteSystem({ RewriteRule(fst(Pair.pair(_a, _b)), _a), })) lst = Function("lst", 1, domain_sort=Pair.sort) Pair.define(lst, RewriteSystem({ RewriteRule(lst(Pair.pair(_a, _b)), _b), }))
"""Converts a Boolean to an bool.""" if not isinstance(x, FuncTerm) or x != Boolean.trueb or x != Boolean.falseb: raise ValueError("to_bool function expects a simplified Boolean.") return x == Boolean.trueb # Variables for later rules _n = Variable("n", sort=Boolean.sort) _m = Variable("m", sort=Boolean.sort) # Negation neg = Function("neg", 1, domain_sort=Boolean.sort, range_sort=Boolean.sort) Boolean.define( neg, RewriteSystem({ RewriteRule(neg(Boolean.trueb), Boolean.falseb), RewriteRule(neg(Boolean.falseb), Boolean.trueb) }) ) # Boolean And andb = Function("andb", 2, domain_sort=Boolean.sort, range_sort=Boolean.sort) Boolean.define( andb, RewriteSystem({ RewriteRule(andb(Boolean.trueb, _n), _n), RewriteRule(andb(Boolean.falseb, _n), Boolean.falseb) }) ) # Boolean Or orb = Function("orb", 2, domain_sort=Boolean.sort, range_sort=Boolean.sort)
class Ring(TheorySystem): """ A ring is an abelian group with another binary operation that is associative, distributive over the abelian operation, and has an ientity element. """ add = Function("add", 2) mul = Function("mul", 2) negate = Function("neg", 1) zero = Constant("0") _x = Variable("x", sort=Group.sort) _y = Variable("y", sort=Group.sort) _z = Variable("z", sort=Group.sort) # TODO: Make Group.rules.rules match what the function symbols are Ring.rules = RewriteSystem(Group.rules.rules | { ## Associativity Rules # (x * y) * z → x * (y * z) RewriteRule(Ring.mul(Ring.mul(_x, _y), _z), Ring.mul(_x, Ring.mul(_y, _z))), ## Zero rules # 0 * x → 0 RewriteRule(Ring.mul(Ring.zero, _x), Ring.zero), # x * 0 → 0 RewriteRule(Ring.mul(_x, Ring.zero), Ring.zero), ## Distributivity rules # x * (y + z) → (x * y) + (x * z) RewriteRule(Ring.mul(_x, Ring.add(_y, _z)), Ring.add(Ring.mul(_x, _y), Ring.mul(_x, _z))), # (y + z) * x → (y * x) + (z * x) RewriteRule(Ring.mul(Ring.add(_y, _z), _x), Ring.add(Ring.mul(_y, _x), Ring.mul(_z, _x))) })
#!/usr/bin/env python3 """ A thought experiment on how to define recursive MOOs. """ from symcollab.algebra import Constant, Function, Variable from symcollab.rewrite import RewriteRule, RewriteSystem, normal from symcollab.xor import xor from symcollab.theories.nat import Nat C = Function("C", 1, domain_sort=Nat.sort) P = Function("P", 1, domain_sort=Nat.sort) f = Function("f", 1) IV = Constant("IV") n = Variable("n", sort=Nat.sort) r0 = RewriteRule(C(Nat.zero), IV) rn = RewriteRule(C(Nat.S(n)), f(xor(P(Nat.S(n)), C(n)))) moo_system = RewriteSystem({r0, rn}) print("Cipher Block Chaining:", moo_system) three = Nat.from_int(3) print("Simplified form of the 3rd ciphertext:", normal(C(three), moo_system)[0])
class Lemma: """ Holds an implication proof and provides utilities to automate it. """ def __init__(self, premise: Term, conclusion: Optional[Term] = None): self.premise = premise self.steps: List[Tuple[RewriteRule, int]] = list() self.current_step = deepcopy(premise) self.conclusion = conclusion if conclusion is not None else Boolean.trueb self.unproven_subgoals: List['Lemma'] = list() self.proven_subgoals: List['Lemma'] = list() self.hypotheses = RewriteSystem() self._auto_load_systems() def add_hypotheses(self, r: Union[RewriteRule, RewriteSystem, Type[TheorySystem]]): """Add hypotheses via rewrite rules, systems, or theory systems.""" if isinstance(r, RewriteRule): self.hypotheses.append(r) elif isinstance(r, RewriteSystem): self.hypotheses.extend(r) elif isclass(r) and issubclass(r, TheorySystem): self.hypotheses.extend(r.rules) else: raise ValueError("simplify must be passed either\ a RewriteRule, RewriteSystem, or TheorySystem.") @property def proven(self): """ Return whether the current ground term matches the conclusion ground term syntactically. """ return self.current_step == self.conclusion \ and len(self.unproven_subgoals) == 0 def apply(self, r: RewriteRule): """Apply a rewrite rule to the current step.""" if not isinstance(r, RewriteRule): raise ValueError("apply must be given a RewriteRule") x_new = r.apply(self.premise) if x_new is None: return self.current_step = x_new self.steps.append(r) return x_new def simplify(self, bound: int = -1): """Attempt to simplify the current state.""" new_steps = narrow(self.current_step, self.conclusion, self.hypotheses, bound) if new_steps is None: return self.current_step = self.conclusion self.steps.extend(new_steps) return self.conclusion def auto(self, bound: int = -1): """ Automate the proof by rewriting the current step into normal form, and applying the opposite steps that it takes to get the conclusion into normal form. """ for subgoal in self.unproven_subgoals: subgoal.auto(self.hypotheses, bound) self._process_subgoals() # Find normal form of current step and goal term current_normal, steps = normal(self.current_step, self.hypotheses, bound) goal_normal, goal_normal_steps = normal(self.conclusion, self.hypotheses, bound) if current_normal != goal_normal: print("Auto: Normal Term Mismatch.") return # Reverse goal_normal_rules and add to steps list steps.extend([(converse(rule), pos) for rule, pos in goal_normal_steps[::-1]]) self.current_step = self.conclusion self.steps.extend(steps) return self.conclusion def undo(self): # TODO: Not sure how this function will behave with subgoals """Undo the last rewrite rule.""" if len(self.steps) == 0: print("No steps to undo") return last_rule, last_pos = self.steps[-1] converse_rule = converse(last_rule) old_term = converse_rule.apply(self.current_step, last_pos) self.current_step = old_term del self.steps[-1] def _auto_load_systems(self): """ Based on the sorts of constants and variables in the Lemma, auto load those TheorySystems. """ terms = get_vars_or_constants(self.premise, True) | \ get_vars_or_constants(self.conclusion, True) sorts = set((v.sort for v in terms)) for sort in sorts: tsystem: Optional[TheorySystem] = system_from_sort(sort) if tsystem is not None: self.add_hypotheses(tsystem) def _process_subgoals(self): """ Go through all subgoals, if proven then remove subgoal and add to hypothesis list. """ for i, subgoal in reversed(list(enumerate(self.unproven_subgoals))): # Add proven subgoals to internal hypotheses list if subgoal.proven: new_rule = RewriteRule(subgoal.premise, subgoal.conclusion) self.hypotheses.append(new_rule) self.unproven_subgoals.pop(i) self.proven_subgoals.append(subgoal) def __repr__(self): question_str = "?" if not self.proven else "" return f"{self.premise} →{question_str} {self.conclusion}"
return 0 if isinstance(x, FuncTerm) and x.function == Nat.S: return 1 + cls.to_int(x.arguments[0]) raise ValueError("to_int: Only accepts signature {0, S}") # Variables for later rules _n = Variable("n", sort=Nat.sort) _m = Variable("m", sort=Nat.sort) # Decrement dec = Function("dec", 1, domain_sort=Nat.sort, range_sort=Nat.sort) Nat.define( dec, RewriteSystem({ RewriteRule(dec(Nat.S(_n)), _n), RewriteRule(dec(Nat.zero), Nat.zero) })) ## Parity Test even = Function("even", 1, domain_sort=Nat.sort, range_sort=Boolean.sort) Nat.define( even, RewriteSystem({ RewriteRule(even(Nat.zero), Boolean.trueb), RewriteRule(even(Nat.S(Nat.zero)), Boolean.falseb), RewriteRule(even(Nat.S(Nat.S(_n))), even(_n)) })) odd = Function("odd", 1, domain_sort=Nat.sort, range_sort=Boolean.sort) Nat.define(odd, RewriteSystem({RewriteRule(odd(_n), Boolean.neg(even(_n)))}))
# Variables for later rules _element = Variable("element") _list = Variable("alist", sort=Listing.sort) _list2 = Variable("blist", sort=Listing.sort) # Repeat _count = Variable("n", sort=Nat.sort) repeat = Function("repeat", 2, domain_sort=[None, Nat.sort], range_sort=Listing.sort) Listing.define( repeat, RewriteSystem({ RewriteRule(repeat(_element, Nat.zero), Listing.nil), RewriteRule(repeat(_element, Nat.S(_count)), Listing.cons(_element, repeat(_element, _count))) })) # Length length = Function("length", 1, domain_sort=Listing.sort, range_sort=Nat.sort) Listing.define( length, RewriteSystem({ RewriteRule(length(Listing.nil), Nat.zero), RewriteRule(length(Listing.cons(_element, _list)), Nat.S(length(_list))) })) # Extends extend = Function("extend",
_x = Variable("x", sort=Group.sort) _y = Variable("y", sort=Group.sort) _z = Variable("z", sort=Group.sort) # From page 184 of Term Rewriting and All That Group.rules = RewriteSystem({ ## Associativity # (x * y) * z → x * (y * z) RewriteRule(Group.op(Group.op(_x, _y), _z), Group.op(_x, Group.op(_y, _z))), ## Identity Rules # 1 * x → x RewriteRule(Group.op(Group.identity, _x), _x), # x * 1 → x RewriteRule(Group.op(_x, Group.identity), _x), ## Inverse Rules # x * i(x) → 1 RewriteRule(Group.op(_x, Group.inverse(_x)), Group.identity), # i(x) * x → 1 RewriteRule(Group.op(Group.inverse(_x), _x), Group.identity), # i(1) → 1 RewriteRule(Group.inverse(Group.identity), Group.identity), # i(i(x)) → x RewriteRule(Group.inverse(Group.inverse(_x)), _x), # i(x * y) → i(y) * i(x) RewriteRule(Group.inverse(Group.op(_x, _y)), Group.op(Group.inverse(_y), Group.inverse(_x))), ## Interplay between associativity and inverses # x * (i(x) * y) → y RewriteRule(Group.op(_x, Group.op(Group.inverse(_x), _y)), _y), # i(x) * (x * y) → y RewriteRule(Group.op(Group.inverse(_x), Group.op(_x, _y)), _y) })
Field.rules = RewriteSystem( Ring.rules.rules | { ## One and Zero # 1 * 0 -> 0 RewriteRule(Field.mul(Field.unity, Field.zero), Field.zero), # 0 * 1 -> 0 RewriteRule(Field.mul(Field.zero, Field.unity), Field.zero), # 1 + 0 -> 1 RewriteRule(Field.add(Field.unity, Field.zero), Field.unity), # 0 + 1 -> 1 RewriteRule(Field.add(Field.zero, Field.unity), Field.unity), ## Unity Rules # 1 * x → x RewriteRule(Field.mul(Field.unity, _x), _x), # x * 1 → x RewriteRule(Field.mul(_x, Field.unity), _x), # -1 * x → -x RewriteRule(Field.mul(Field.negate(Field.unity), _x), Field.negate(_x) ), # x * -1 → -x RewriteRule(Field.mul(_x, Field.negate(Field.unity)), Field.negate(_x) ), # x * i(x) → 1 RewriteRule(Field.mul(_x, Field.inverse(_x)), Field.unity), # i(x) * x → 1 RewriteRule(Field.mul(Field.inverse(_x), _x), Field.unity), ## Inverse Rules # i(i(x)) → x RewriteRule(Field.inverse(Field.inverse(_x)), _x), # i(x * y) → i(y) * i(x) RewriteRule(Field.inverse(Field.mul(_x, _y)), Field.mul(Field.inverse(_y), Field.inverse(_x))), ## Interplay between associativity and inverses # x * (i(x) * y) → y RewriteRule(Field.mul(_x, Field.mul(Field.inverse(_x), _y)), _y), # i(x) * (x * y) → y RewriteRule(Field.mul(Field.inverse(_x), Field.mul(_x, _y)), _y) })