Skip to content
Snippets Groups Projects
Commit 8fc081ff authored by chrg's avatar chrg
Browse files

Changes from the paper

parent 0edd0b2e
No related branches found
No related tags found
No related merge requests found
from dataclasses import dataclass
from functools import partial
import rtree
@dataclass
class Let:
var: str
assignee: "Expr"
body: "Expr"
def replace(self, reps):
return Let(
self.var,
self.assignee.replace(reps),
self.body.replace(reps),
)
def eval(self, vals=None):
vals = vals or dict()
r = self.assignee.eval(vals)
vals = vals.copy()
vals[self.var] = r
return self.body.eval(vals)
def subterms(self):
yield self
yield from self.assignee.subterms()
yield from self.body.subterms()
def pretty(self, prec=0):
p = f"{self.var} := {self.assignee.pretty(2)}; {self.body.pretty(1)}"
if prec >= 1:
p = f"({p})"
return p
@dataclass
class Add:
lhs: "Expr"
rhs: "Expr"
def replace(self, reps):
return Add(
self.lhs.replace(reps),
self.rhs.replace(reps),
)
def subterms(self):
yield self
yield from self.lhs.subterms()
yield from self.rhs.subterms()
def pretty(self, n=0):
p = f"{self.lhs.pretty(4)} + {self.rhs.pretty(3)}"
if n >= 3:
p = f"({p})"
return p
def eval(self, vals=None):
vals = vals or dict()
lv = self.lhs.eval(vals)
rv = self.rhs.eval(vals)
return lv + rv
@dataclass
class Const:
n: int
def replace(self, _):
return self
def subterms(self):
yield self
def pretty(self, _=0):
return str(self.n)
def eval(self, _vals=None):
return self.n
@dataclass
class Var:
name: str
def replace(self, reps):
return reps.get(self.name, self)
def subterms(self):
yield self
def pretty(self, _=0):
return self.name
def eval(self, vals=None):
vals = vals or dict()
return vals.get(self.name, -1)
Expr = Let | Add | Const | Var
def reduce_expr(expr: Expr, check) -> Expr:
if isinstance(expr, Var):
return expr
elif isinstance(expr, Const):
if not expr.n == 0 and check("1) make zero"):
return Const(0)
return expr
elif isinstance(expr, Add):
if check("2) reduce to lhs"):
return reduce_expr(expr.lhs, check)
if check("3) reduce to rhs"):
return reduce_expr(expr.rhs, check)
lhs_ = reduce_expr(expr.lhs, check)
rhs_ = reduce_expr(expr.rhs, check)
return Add(lhs_, rhs_)
elif isinstance(expr, Let):
assignee_ = reduce_expr(expr.assignee, check)
if check("4) reduce to lhs"):
return assignee_
if check(f"5) inline {expr.var!r}"):
return reduce_expr(expr.body.replace({expr.var: assignee_}), check)
body_ = reduce_expr(expr.body, check)
return Let(expr.var, assignee_, body_)
if __name__ == "__main__":
expr = Let(
"x", Const(2), Add(Const(1), Let("y", Var("x"), Add(Const(3), Var("y"))))
)
def input_format(a):
return f"$\\syn{{{a.pretty()}}}$"
p = rtree.latex(
lambda e: any(isinstance(a, Add) for a in e.subterms()),
query_format=str,
input_format=input_format,
start_count=0,
)
rt = partial(reduce_expr, expr)
p(rtree.Probe(rt, []))
rp = rtree.reduce(p, rt)
print(f"&& {input_format(rp.input)} & true \\\\")
print()
p = rtree.latex(
lambda e: e.eval() == expr.eval(),
query_format=str,
input_format=input_format,
start_count=0,
)
rt = partial(reduce_expr, expr)
p(rtree.Probe(rt, []))
rp = rtree.reduce(p, rt)
print(f"& {rtree.pretty{rp}} & {input_format(rp.input)} & true \\\\")
from dataclasses import dataclass from dataclasses import dataclass
@dataclass class Probe:
class ReducePath: def __init__(self, rtree, path, guess_depth=0):
path: list[bool] """Test an rtree with a path, and optional number of guesses"""
reasons: list[str] self.path = path + [True] * guess_depth
self.depth = guess_depth
self.reasons = []
@classmethod def check(reason: str):
def empty(cls): """Check the path or default to false"""
return cls([], []) self.reasons.append(reason)
return self.undecided() <= 0 and self.path[len(self.reasons) - 1]
def explore(self, choice): self.input = rtree(check)
self.reasons = []
self.path.append(choice)
return self
def left(self): def undecided(self):
"""The number of choices left on the path"""
return len(self.reasons) - len(self.path) return len(self.reasons) - len(self.path)
def check(self, reason: str):
self.reasons.append(reason) def reduce(predicate, rtree):
try: # Extract the initial reduction probe, from the rightmost branch.
return self.path[len(self.reasons) - 1] rp = Probe(rtree, [])
except IndexError: # Run exponential search after the depest sequence of trues
return False # that can be appended to the path without failing the predicate
depth = 1
# Invariant: predicate(rp) == True
def reduce(predicate, reducer, i): while rp.undecided() > 0:
r = ReducePath.empty() # Try to probe the with current path extended by trues trues.
j = i if predicate(rp_ := Probe(rtree, rp.path, depth)):
ix = reducer(i, r.explore(True)) rp, depth = rp_, depth * 2
# While we don't consume all choices going down the true branch continue
while r.left() >= 0: # Adjust the depth so that none of them are wasted.
print(r.reasons[len(r.path) - 1], "...", end="") depth += min(0, rp_.undecided())
if predicate(ix): # Perform a binary search, accepting the nessarary trues, and
# If true update the valid input # reducing the unknown trues to 1:
print(" Yes") while depth > 1:
j = ix # Prope the rtree with the current path extended by half the depth.
if predicate(rp_ := Probe(rtree, rp.path, depth // 2)):
rp = rp_ # Accept the current path.
depth -= depth // 2 # And try the remaining half
else: else:
# If false we have to go down the left branch. depth //= 2 # Half the current trues
print(" No") # Store that the next element in the path has to be false
r.path[-1] = False rp.path.append(False)
ix = reducer(i, r.explore(True)) # return the input.
return j return rp
def debug(predicate): def debug(predicate):
def newpred(i): counter = 0
t = predicate(i)
print(" test", i, "...", end="") def newpred(rp):
nonlocal counter
counter += 1
t = predicate(rp.input)
print(
f"{counter:02})",
", ".join(rp.reasons[len(rp.path) - rp.depth : len(rp.path)]),
)
print(f"... P({rp.input!r}) = {t}")
return t
return newpred
def latex(
predicate,
query_format="Remove {}?".format,
input_format="\\verb|{}|".format,
start_count=0,
):
counter = start_count - 1
def newpred(rp):
nonlocal counter
counter += 1
t = predicate(rp.input)
query = ", ".join(rp.reasons[len(rp.path) - rp.depth : len(rp.path)])
theck = "true" if t else "false"
print(
f"{counter:02} & \\verb|{pretty(rp)}| & {query_format(query)} & {input_format(rp.input)} & {theck} \\\\"
)
return t return t
return newpred return newpred
def reduce_set(i: set, r) -> set: def pretty(rp):
result: set = set() from itertools import zip_longest
for e in sorted(i):
if not r.check(f"remove {e}?"): def binary(a):
result.add(e) return "1" if a else "0"
return "".join(
a for a, _ in zip_longest(map(binary, rp.path), rp.reasons, fillvalue="*")
)
def reduce_abc(check) -> str:
result = ""
for x in "abcdefghijklmnopqrstuvxyz":
if not check(f"{x}"):
result += x
else:
result += " "
return result return result
if __name__ == "__main__": if __name__ == "__main__":
reduce(debug(lambda a: "a" in a), reduce_set, {"a", "b", "c"}) p = latex(
lambda e: "a" in e or "p" in e,
start_count=0,
)
input_format = "\\verb|{}|".format
p(Probe(reduce_abc, []))
rp = reduce(p, reduce_abc)
print(f"&& {input_format(rp.input)} & true \\\\")
--failure-report .hspec-failures --failure-report .hspec-failures
--rerun
--rerun-all-on-success
...@@ -2,3 +2,6 @@ ...@@ -2,3 +2,6 @@
1***: y := 2; 1 + y True 1***: y := 2; 1 + y True
11***: 1 + 2 True 11***: 1 + 2 True
111: 1 False 111: 1 False
1101: 2 False
11001: 3 False
11000: 1 + 2 True
...@@ -2,3 +2,6 @@ ...@@ -2,3 +2,6 @@
1***: x := 2; x + x True 1***: x := 2; x + x True
11***: 2 + 2 True 11***: 2 + 2 True
111: 2 False 111: 2 False
1101: 2 False
11001: 4 False
11000: 2 + 2 True
***: x := 1; 2 + x True ***: x := 1; 2 + x True
1***: 2 + 1 True 1***: 2 + 1 True
11: 2 False 11: 2 False
101: 1 False
1001: 3 False
1000: 2 + 1 True
***: 1 + 2 True ***: 1 + 2 True
1: 1 False 1: 1 False
01: 2 False
001: 3 False
000: 1 + 2 True
┳━┳━┳━┳━┳━┳━ "ABc"
┃ ┃ ┃ ┃ ┃ ┗━ "aBc"
┃ ┃ ┃ ┃ ┗━┳━ "Abc"
┃ ┃ ┃ ┃ ┗━ "abc"
┃ ┃ ┃ ┗━┳━┳━ "ABc"
┃ ┃ ┃ ┃ ┗━ "aBc"
┃ ┃ ┃ ┗━┳━ "Abc"
┃ ┃ ┃ ┗━ "abc"
┃ ┃ ┗━┳━┳━ "Bc"
┃ ┃ ┃ ┗━ "bc"
┃ ┃ ┗━┳━ "Bc"
┃ ┃ ┗━ "bc"
┃ ┗━┳━┳━┳━ "Ac"
┃ ┃ ┃ ┗━ "ac"
┃ ┃ ┗━┳━ "Ac"
┃ ┃ ┗━ "ac"
┃ ┗━┳━ "c"
┃ ┗━ "c"
┗━┳━┳━┳━┳━ "AB"
┃ ┃ ┃ ┗━ "aB"
┃ ┃ ┗━┳━ "Ab"
┃ ┃ ┗━ "ab"
┃ ┗━┳━ "B"
┃ ┗━ "b"
┗━┳━┳━ "A"
┃ ┗━ "a"
┗━ ""
┳━┳━┳━┳━┳━┳━ "ABc"
┃ ┃ ┃ ┃ ┃ ┗━ "Bc"
┃ ┃ ┃ ┃ ┗━┳━ "Ac"
┃ ┃ ┃ ┃ ┗━ "c"
┃ ┃ ┃ ┗━┳━┳━ "AB"
┃ ┃ ┃ ┃ ┗━ "B"
┃ ┃ ┃ ┗━┳━ "A"
┃ ┃ ┃ ┗━ ""
┃ ┃ ┗━┳━┳━┳━ "aBc"
┃ ┃ ┃ ┃ ┗━ "Bc"
┃ ┃ ┃ ┗━┳━ "ac"
┃ ┃ ┃ ┗━ "c"
┃ ┃ ┗━┳━┳━ "aB"
┃ ┃ ┃ ┗━ "B"
┃ ┃ ┗━┳━ "a"
┃ ┃ ┗━ ""
┃ ┗━┳━┳━┳━┳━ "Abc"
┃ ┃ ┃ ┃ ┗━ "bc"
┃ ┃ ┃ ┗━┳━ "Ac"
┃ ┃ ┃ ┗━ "c"
┃ ┃ ┗━┳━┳━ "Ab"
┃ ┃ ┃ ┗━ "b"
┃ ┃ ┗━┳━ "A"
┃ ┃ ┗━ ""
┃ ┗━┳━┳━┳━ "abc"
┃ ┃ ┃ ┗━ "bc"
┃ ┃ ┗━┳━ "ac"
┃ ┃ ┗━ "c"
┃ ┗━┳━┳━ "ab"
┃ ┃ ┗━ "b"
┃ ┗━┳━ "a"
┃ ┗━ ""
┗━┳━┳━┳━┳━┳━ "ABc"
┃ ┃ ┃ ┃ ┗━ "Bc"
┃ ┃ ┃ ┗━┳━ "Ac"
┃ ┃ ┃ ┗━ "c"
┃ ┃ ┗━┳━┳━ "AB"
┃ ┃ ┃ ┗━ "B"
┃ ┃ ┗━┳━ "A"
┃ ┃ ┗━ ""
┃ ┗━┳━┳━┳━ "aBc"
┃ ┃ ┃ ┗━ "Bc"
┃ ┃ ┗━┳━ "ac"
┃ ┃ ┗━ "c"
┃ ┗━┳━┳━ "aB"
┃ ┃ ┗━ "B"
┃ ┗━┳━ "a"
┃ ┗━ ""
┗━┳━┳━┳━┳━ "Abc"
┃ ┃ ┃ ┗━ "bc"
┃ ┃ ┗━┳━ "Ac"
┃ ┃ ┗━ "c"
┃ ┗━┳━┳━ "Ab"
┃ ┃ ┗━ "b"
┃ ┗━┳━ "A"
┃ ┗━ ""
┗━┳━┳━┳━ "abc"
┃ ┃ ┗━ "bc"
┃ ┗━┳━ "ac"
┃ ┗━ "c"
┗━┳━┳━ "ab"
┃ ┗━ "b"
┗━┳━ "a"
┗━ ""
...@@ -33,8 +33,8 @@ expsearchSpec = describe "expsearch" do ...@@ -33,8 +33,8 @@ expsearchSpec = describe "expsearch" do
it "should guess a number between 0 and 10" do it "should guess a number between 0 and 10" do
expsearch (\a -> do pure (a <= 6, 10 - a)) `shouldReturn` (6, True) expsearch (\a -> do pure (a <= 6, 10 - a)) `shouldReturn` (6, True)
expsearch (\a -> do pure (a <= 3, 10 - a)) `shouldReturn` (3, True) expsearch (\a -> do pure (a <= 3, 10 - a)) `shouldReturn` (3, True)
expsearch (\a -> do pure (a <= 9, 10 - a)) `shouldReturn` (9, False) expsearch (\a -> do pure (a <= 9, 10 - a)) `shouldReturn` (9, True)
expsearch (\a -> do pure (a <= 12, 10 - a)) `shouldReturn` (9, False) expsearch (\a -> do pure (a <= 12, 10 - a)) `shouldReturn` (9, True)
examplesSpec :: Spec examplesSpec :: Spec
examplesSpec = describe "examples" do examplesSpec = describe "examples" do
......
...@@ -8,6 +8,7 @@ module Control.Monad.RTreeSpec where ...@@ -8,6 +8,7 @@ module Control.Monad.RTreeSpec where
import Control.Monad.Identity (Identity (runIdentity)) import Control.Monad.Identity (Identity (runIdentity))
import Control.Monad.RTree import Control.Monad.RTree
import Control.Monad.Reader import Control.Monad.Reader
import qualified Data.Char as Char
import Data.Expr as Expr import Data.Expr as Expr
import Data.Foldable import Data.Foldable
import Data.Functor import Data.Functor
...@@ -30,6 +31,7 @@ spec = do ...@@ -30,6 +31,7 @@ spec = do
rtreeSpec rtreeSpec
rtreeTSpec rtreeTSpec
examplesSpec examplesSpec
paperSpec
rtreeTSpec :: Spec rtreeTSpec :: Spec
rtreeTSpec = describe "RTreeT" do rtreeTSpec = describe "RTreeT" do
...@@ -116,3 +118,22 @@ examplesSpec = describe "example" do ...@@ -116,3 +118,22 @@ examplesSpec = describe "example" do
forM_ (iinputs re) \(ii, e') -> do forM_ (iinputs re) \(ii, e') -> do
p <- toPredicate ii p <- toPredicate ii
reduce p re `shouldReturn` e' reduce p re `shouldReturn` e'
listR :: [a] -> RTree String [a]
listR [] = pure []
listR (a : as) = listR as >>= \as' -> pure as' <| pure (a : as')
lowerR :: [Char] -> RTree String [Char]
lowerR [] = pure []
lowerR (a : as) = lowerR as >>= \as' -> pure (Char.toLower a : as') <| pure (a : as')
paperSpec :: Spec
paperSpec = do
onGlitter
"test/paper-examples/words"
(`writeFile` drawRTree showString shows (listR "ABc" >>= lowerR))
(pure ())
onGlitter
"test/paper-examples/words'"
(`writeFile` drawRTree showString shows (lowerR "ABc" >>= listR))
(pure ())
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment