Compare commits

..

2 Commits

4 changed files with 139 additions and 16 deletions

View File

@ -3,8 +3,11 @@
""" """
# from ..proof import Context, Proof # from ..proof import Context, Proof
from ..props import Eq, Or, Prop from ..props import Eq, Exists, Or, Prop
from ..terms import App, Const, A1, A2, L1, L2, L3, Lambda, Term, Var, register_known_const from ..terms import (
App, Const, A1, A2, L1, L2, L3, Lambda, Term,
register_known_const, register_known_norm
)
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# Identity function # Identity function
@ -59,8 +62,35 @@ def kernel_from_int(n : int) -> Term:
return t return t
# def is_nat(x : Term) -> Prop: def is_nat(x : Term) -> Prop:
# return Or( return Or(
# Eq(x=x, y=zero), Eq(x=x, y=zero),
# Eq(bool_f, bool_t) # TODO: Construct x = Succ n where isNat(n), Exists(lambda n : Eq(x=x, y=A1(Const("Nat.Succ"), n))),
# ) )
def __nat_add(t : Term) -> Term:
match t:
case App(f=Const("Nat.add"), x=Const("Nat.Zero")):
return id_func
case App(f=Const("Nat.add"), x=App(f=Const("Nat.Succ"), x=a)):
return L1(lambda b : A1(succ, A2(Const("Nat.add"), a, b)))
case _:
return t
register_known_norm(__nat_add)
def __nat_leq(t : Term) -> Term:
match t:
case App(f=Const("Nat.leq"), x=Const("Nat.Zero")):
return bool_t
case App(App(f=Const("Nat.leq"), x=_), x=Const("Nat.Zero")):
return bool_f
case App(f=App(f=Const("Nat.leq"), x=App(f=Const("Nat.Succ"), x=a)), x=App(f=Const("Nat.Succ"), x=b)):
return A2(Const("Nat.leq"), a, b)
case _:
return t
register_known_norm(__nat_leq)

View File

@ -3,6 +3,7 @@
""" """
from __future__ import annotations from __future__ import annotations
from typing import Callable
from .terms import Term from .terms import Term
from dataclasses import dataclass from dataclasses import dataclass
@ -65,6 +66,23 @@ class Eq(Prop):
def as_proof(self) -> list[list[tuple[list[Prop], Prop]]]: def as_proof(self) -> list[list[tuple[list[Prop], Prop]]]:
return super().as_proof() return super().as_proof()
@dataclass(frozen=True)
class Exists(Prop):
"""
Create a statement that demonstrates there exists an x such that the
given statement is true.
"""
x : Callable[[Term], Prop]
@dataclass(frozen=True)
class ForAll(Prop):
"""
Create a statement that is true for all x
"""
x : Callable[[Term], Prop]
@dataclass(frozen=True) @dataclass(frozen=True)
class Implies(Prop): class Implies(Prop):
""" """

View File

@ -4,7 +4,7 @@
""" """
from __future__ import annotations from __future__ import annotations
from typing import Callable from typing import Callable, Generator
import copy import copy
@ -24,6 +24,24 @@ USABLE_VAR_NAMES = [
'μ', 'ν', 'ξ', 'ο', 'π', 'ρ', 'σ', 'τ', 'υ', 'φ', 'χ', 'ψ', 'ω' 'μ', 'ν', 'ξ', 'ο', 'π', 'ρ', 'σ', 'τ', 'υ', 'φ', 'χ', 'ψ', 'ω'
] ]
def contains(haystack : Term, needle : Term) -> bool:
"""
Determine whether a given small term value is contained in a bigger
term.
:param haystack: The term to search through.
:type haystack: Term
:param needle: The term to find.
:type needle: Term
:return: Whether the needle was found in the haystack.
:rtype: bool
"""
for term in haystack.rec_iter():
if term == needle:
return True
else:
return False
def normalize(term : Term) -> Term: def normalize(term : Term) -> Term:
""" """
Create a normalization function. This function simplifies functions as Create a normalization function. This function simplifies functions as
@ -78,6 +96,21 @@ def std_norm_iter(term : Term) -> Term:
return term return term
_known_norms.append(std_norm_iter) _known_norms.append(std_norm_iter)
def replace(term : Term, old : Term, new : Term) -> Term:
"""
Find and replace occurrences of a sub-term in a given term.
:param term: The original term to update.
:type term: Term
:param old: The term to find and replace.
:type old: Term
:param new: The new term to replace the old one with.
:type new: Term
:return: The replaced term result.
:rtype: Term
"""
return traverse_bottom_up(f=lambda t : new if t == old else t, term=term)
def traverse(f : Callable[[Term], Term], term : Term) -> Term: def traverse(f : Callable[[Term], Term], term : Term) -> Term:
""" """
Traverse through the tree back-and-forth, aiming to run the given Traverse through the tree back-and-forth, aiming to run the given
@ -96,7 +129,7 @@ def traverse(f : Callable[[Term], Term], term : Term) -> Term:
a, b = Term(), term a, b = Term(), term
while a != b: while a != b:
a, b = b, traverse_bottom_up(f=f, term=traverse_top_down(f=f, term=term)) a, b = b, traverse_bottom_up(f=f, term=traverse_top_down(f=f, term=b))
return b return b
@ -171,12 +204,6 @@ class Term:
Base class for all terms. Base class for all terms.
""" """
def __eq__(self, other) -> bool:
"""
Determine whether two terms are equal.
"""
return str(self) == str(other)
def __repr__(self) -> str: def __repr__(self) -> str:
""" """
Create a representation of a term. Create a representation of a term.
@ -185,6 +212,12 @@ class Term:
def normalize(self) -> Term: def normalize(self) -> Term:
return normalize(term=self) return normalize(term=self)
def replace(self, old : Term, new : Term) -> Term:
return replace(term=self, old=old, new=new)
def rec_iter(self) -> Generator[Term, None, None]:
yield self
def to_str(self, vars : dict[str, Var]) -> str: def to_str(self, vars : dict[str, Var]) -> str:
return "<undefined>" return "<undefined>"
@ -204,6 +237,11 @@ class App(Term):
Create a representation of a term. Create a representation of a term.
""" """
return self.to_str(vars={}) return self.to_str(vars={})
def rec_iter(self) -> Generator[Term, None, None]:
yield self
yield from self.f.rec_iter()
yield from self.x.rec_iter()
def to_str(self, vars: dict[str, Var]) -> str: def to_str(self, vars: dict[str, Var]) -> str:
f_str = self.f.to_str(vars=vars) f_str = self.f.to_str(vars=vars)
@ -246,6 +284,18 @@ class Lambda(Term):
out : Term out : Term
var : Var var : Var
def __eq__(self, value: object) -> bool:
if not isinstance(value, Lambda):
return False
for c in USABLE_VAR_NAMES:
if not contains(self.out, Const(c)):
return self.substitute(Const(c)) == value.substitute((Const(c)))
else:
raise ValueError(
"Ran out of usable variables for comparing lambda structures"
)
def __init__(self, f : Callable[[Var], Term]) -> None: def __init__(self, f : Callable[[Var], Term]) -> None:
self.var = Var() self.var = Var()
self.out = f(self.var) self.out = f(self.var)
@ -256,6 +306,10 @@ class Lambda(Term):
""" """
return self.to_str(vars={}) return self.to_str(vars={})
def rec_iter(self) -> Generator[Term, None, None]:
yield self
yield from self.out.rec_iter()
def replace_content(self, term : Term) -> Lambda: def replace_content(self, term : Term) -> Lambda:
""" """
Replace the content of the lambda function with the given term. Replace the content of the lambda function with the given term.
@ -427,3 +481,15 @@ if __name__ == "__main__":
g = L1(lambda b : A2(b, bool_f, bool_t)) g = L1(lambda b : A2(b, bool_f, bool_t))
print(g) print(g)
print(g.normalize()) print(g.normalize())
print("\nFIND & REPLACE")
print(30 * "=")
h = A3(Const("f"), Const("x"), Const("y"), Const("z"))
i = A3(Const("f"), Const("x"), Const("b"), Const("z")).replace(
old=Const("b"), new=Const("y"),
)
print(h)
print(i)
assert h == i

View File

@ -1,5 +1,6 @@
from checker import lib, proof, props from checker import lib, proof, props
from checker.terms import App, Const from checker.terms import A2, App, Const
from checker.lib.basic import kernel_from_int
# Prove : true == not false # Prove : true == not false
p = proof.reflexivity( p = proof.reflexivity(
@ -18,3 +19,11 @@ p = proof.reflexivity(
) )
) )
p.check() p.check()
# Prove : 4 + 1 == 2 + 3
p = proof.reflexivity(
props.Eq(
x=A2(Const("Nat.add"), kernel_from_int(4), kernel_from_int(1)),
y=A2(Const("Nat.add"), kernel_from_int(2), kernel_from_int(3)),
)
)