I am done

This commit is contained in:
2024-10-30 22:14:35 +01:00
parent 720dc28c09
commit 40e2a747cf
36901 changed files with 5011519 additions and 0 deletions

View File

@ -0,0 +1,14 @@
from . import traverse
from .core import (
condition, debug, multiplex, exhaust, notempty,
chain, onaction, sfilter, yieldify, do_one, identity)
from .tools import canon
__all__ = [
'traverse',
'condition', 'debug', 'multiplex', 'exhaust', 'notempty', 'chain',
'onaction', 'sfilter', 'yieldify', 'do_one', 'identity',
'canon',
]

View File

@ -0,0 +1,116 @@
""" Generic SymPy-Independent Strategies """
def identity(x):
yield x
def exhaust(brule):
""" Apply a branching rule repeatedly until it has no effect """
def exhaust_brl(expr):
seen = {expr}
for nexpr in brule(expr):
if nexpr not in seen:
seen.add(nexpr)
yield from exhaust_brl(nexpr)
if seen == {expr}:
yield expr
return exhaust_brl
def onaction(brule, fn):
def onaction_brl(expr):
for result in brule(expr):
if result != expr:
fn(brule, expr, result)
yield result
return onaction_brl
def debug(brule, file=None):
""" Print the input and output expressions at each rule application """
if not file:
from sys import stdout
file = stdout
def write(brl, expr, result):
file.write("Rule: %s\n" % brl.__name__)
file.write("In: %s\nOut: %s\n\n" % (expr, result))
return onaction(brule, write)
def multiplex(*brules):
""" Multiplex many branching rules into one """
def multiplex_brl(expr):
seen = set()
for brl in brules:
for nexpr in brl(expr):
if nexpr not in seen:
seen.add(nexpr)
yield nexpr
return multiplex_brl
def condition(cond, brule):
""" Only apply branching rule if condition is true """
def conditioned_brl(expr):
if cond(expr):
yield from brule(expr)
else:
pass
return conditioned_brl
def sfilter(pred, brule):
""" Yield only those results which satisfy the predicate """
def filtered_brl(expr):
yield from filter(pred, brule(expr))
return filtered_brl
def notempty(brule):
def notempty_brl(expr):
yielded = False
for nexpr in brule(expr):
yielded = True
yield nexpr
if not yielded:
yield expr
return notempty_brl
def do_one(*brules):
""" Execute one of the branching rules """
def do_one_brl(expr):
yielded = False
for brl in brules:
for nexpr in brl(expr):
yielded = True
yield nexpr
if yielded:
return
return do_one_brl
def chain(*brules):
"""
Compose a sequence of brules so that they apply to the expr sequentially
"""
def chain_brl(expr):
if not brules:
yield expr
return
head, tail = brules[0], brules[1:]
for nexpr in head(expr):
yield from chain(*tail)(nexpr)
return chain_brl
def yieldify(rl):
""" Turn a rule into a branching rule """
def brl(expr):
yield rl(expr)
return brl

View File

@ -0,0 +1,117 @@
from sympy.strategies.branch.core import (
exhaust, debug, multiplex, condition, notempty, chain, onaction, sfilter,
yieldify, do_one, identity)
def posdec(x):
if x > 0:
yield x - 1
else:
yield x
def branch5(x):
if 0 < x < 5:
yield x - 1
elif 5 < x < 10:
yield x + 1
elif x == 5:
yield x + 1
yield x - 1
else:
yield x
def even(x):
return x % 2 == 0
def inc(x):
yield x + 1
def one_to_n(n):
yield from range(n)
def test_exhaust():
brl = exhaust(branch5)
assert set(brl(3)) == {0}
assert set(brl(7)) == {10}
assert set(brl(5)) == {0, 10}
def test_debug():
from io import StringIO
file = StringIO()
rl = debug(posdec, file)
list(rl(5))
log = file.getvalue()
file.close()
assert posdec.__name__ in log
assert '5' in log
assert '4' in log
def test_multiplex():
brl = multiplex(posdec, branch5)
assert set(brl(3)) == {2}
assert set(brl(7)) == {6, 8}
assert set(brl(5)) == {4, 6}
def test_condition():
brl = condition(even, branch5)
assert set(brl(4)) == set(branch5(4))
assert set(brl(5)) == set()
def test_sfilter():
brl = sfilter(even, one_to_n)
assert set(brl(10)) == {0, 2, 4, 6, 8}
def test_notempty():
def ident_if_even(x):
if even(x):
yield x
brl = notempty(ident_if_even)
assert set(brl(4)) == {4}
assert set(brl(5)) == {5}
def test_chain():
assert list(chain()(2)) == [2] # identity
assert list(chain(inc, inc)(2)) == [4]
assert list(chain(branch5, inc)(4)) == [4]
assert set(chain(branch5, inc)(5)) == {5, 7}
assert list(chain(inc, branch5)(5)) == [7]
def test_onaction():
L = []
def record(fn, input, output):
L.append((input, output))
list(onaction(inc, record)(2))
assert L == [(2, 3)]
list(onaction(identity, record)(2))
assert L == [(2, 3)]
def test_yieldify():
yinc = yieldify(lambda x: x + 1)
assert list(yinc(3)) == [4]
def test_do_one():
def bad(expr):
raise ValueError
assert list(do_one(inc)(3)) == [4]
assert list(do_one(inc, bad)(3)) == [4]
assert list(do_one(inc, posdec)(3)) == [4]

View File

@ -0,0 +1,42 @@
from sympy.strategies.branch.tools import canon
from sympy.core.basic import Basic
from sympy.core.numbers import Integer
from sympy.core.singleton import S
def posdec(x):
if isinstance(x, Integer) and x > 0:
yield x - 1
else:
yield x
def branch5(x):
if isinstance(x, Integer):
if 0 < x < 5:
yield x - 1
elif 5 < x < 10:
yield x + 1
elif x == 5:
yield x + 1
yield x - 1
else:
yield x
def test_zero_ints():
expr = Basic(S(2), Basic(S(5), S(3)), S(8))
expected = {Basic(S(0), Basic(S(0), S(0)), S(0))}
brl = canon(posdec)
assert set(brl(expr)) == expected
def test_split5():
expr = Basic(S(2), Basic(S(5), S(3)), S(8))
expected = {
Basic(S(0), Basic(S(0), S(0)), S(10)),
Basic(S(0), Basic(S(10), S(0)), S(10))}
brl = canon(branch5)
assert set(brl(expr)) == expected

View File

@ -0,0 +1,53 @@
from sympy.core.basic import Basic
from sympy.core.numbers import Integer
from sympy.core.singleton import S
from sympy.strategies.branch.traverse import top_down, sall
from sympy.strategies.branch.core import do_one, identity
def inc(x):
if isinstance(x, Integer):
yield x + 1
def test_top_down_easy():
expr = Basic(S(1), S(2))
expected = Basic(S(2), S(3))
brl = top_down(inc)
assert set(brl(expr)) == {expected}
def test_top_down_big_tree():
expr = Basic(S(1), Basic(S(2)), Basic(S(3), Basic(S(4)), S(5)))
expected = Basic(S(2), Basic(S(3)), Basic(S(4), Basic(S(5)), S(6)))
brl = top_down(inc)
assert set(brl(expr)) == {expected}
def test_top_down_harder_function():
def split5(x):
if x == 5:
yield x - 1
yield x + 1
expr = Basic(Basic(S(5), S(6)), S(1))
expected = {Basic(Basic(S(4), S(6)), S(1)), Basic(Basic(S(6), S(6)), S(1))}
brl = top_down(split5)
assert set(brl(expr)) == expected
def test_sall():
expr = Basic(S(1), S(2))
expected = Basic(S(2), S(3))
brl = sall(inc)
assert list(brl(expr)) == [expected]
expr = Basic(S(1), S(2), Basic(S(3), S(4)))
expected = Basic(S(2), S(3), Basic(S(3), S(4)))
brl = sall(do_one(inc, identity))
assert list(brl(expr)) == [expected]

View File

@ -0,0 +1,12 @@
from .core import exhaust, multiplex
from .traverse import top_down
def canon(*rules):
""" Strategy for canonicalization
Apply each branching rule in a top-down fashion through the tree.
Multiplex through all branching rule traversals
Keep doing this until there is no change.
"""
return exhaust(multiplex(*map(top_down, rules)))

View File

@ -0,0 +1,25 @@
""" Branching Strategies to Traverse a Tree """
from itertools import product
from sympy.strategies.util import basic_fns
from .core import chain, identity, do_one
def top_down(brule, fns=basic_fns):
""" Apply a rule down a tree running it on the top nodes first """
return chain(do_one(brule, identity),
lambda expr: sall(top_down(brule, fns), fns)(expr))
def sall(brule, fns=basic_fns):
""" Strategic all - apply rule to args """
op, new, children, leaf = map(fns.get, ('op', 'new', 'children', 'leaf'))
def all_rl(expr):
if leaf(expr):
yield expr
else:
myop = op(expr)
argss = product(*map(brule, children(expr)))
for args in argss:
yield new(myop, *args)
return all_rl