def test_objects_full(): _unify.add((Foo, Foo, dict), unify_object) _unify.add((Bar, Bar, dict), unify_object) _reify.add((Foo, dict), reify_object) _reify.add((Bar, dict), reify_object) assert unify_object(Foo(1, Bar(2)), Foo(1, Bar(var(3))), {}) == {var(3): 2} assert reify(Foo(var('a'), Bar(Foo(var('b'), 3))), {var('a'): 1, var('b'): 2}) == Foo(1, Bar(Foo(2, 3)))
def test_unify_isinstance_list(): class Foo2(Foo): pass x = var('x') y = var('y') f, g = Foo2(1, 2), Foo2(x, y) _unify.add((Foo, Foo, dict), unify_object) _reify.add((Foo, dict), reify_object) assert unify(f, g, {}) assert reify(g, {x: 1, y: 2}) == f
def test_objects_full(): _unify.add((Foo, Foo, Mapping), _unify_object) _unify.add((Bar, Bar, Mapping), _unify_object) _reify.add((Foo, Mapping), _reify_object) _reify.add((Bar, Mapping), _reify_object) x, y = var(), var() assert unify(Foo(1, 2), Bar(1), {}) is False assert unify(Foo(1, Bar(2)), Foo(1, Bar(x)), {}) == {x: 2} assert reify(Foo(x, Bar(Foo(y, 3))), { x: 1, y: 2 }) == Foo(1, Bar(Foo(2, 3))) class SubFoo(Foo): pass assert unify(Foo(1, 2), SubFoo(1, 2), {}) is False
lcons_ = lcons rcons_ = rcons if isinstance(lcons, Iterator): lcons, lcons_ = tee(lcons) if isinstance(rcons, Iterator): rcons, rcons_ = tee(rcons) try: s = yield _unify(car(lcons), car(rcons), s) if s is not False: s = yield _unify(cdr(lcons_), cdr(rcons_), s) except ConsError: yield False else: yield s _unify.add((ConsPair, (ConsPair, MaybeCons), Mapping), _unify_Cons) _unify.add((MaybeCons, ConsPair, Mapping), _unify_Cons) @_reify.register(ConsPair, Mapping) def _reify_Cons(lcons, s): rcar = yield _reify(car(lcons), s) rcdr = yield _reify(cdr(lcons), s) yield construction_sentinel yield cons(rcar, rcdr)
# Unfortunately, `multipledispatch` doesn't use `isinstance` on the arguments, # so it won't use our fancy setup for `isinstance(x, ConsPair)` and we have to # specify--and check--each `cons`-amenable type explicitly. def _cons_unify(lcons, rcons, s): if not is_cons(lcons) or not is_cons(rcons): # One of the arguments is necessarily a `ConsPair` object, # but the other could be an empty iterable, which isn't a # `cons`-derivable object. return False s = unify(car(lcons), car(rcons), s) if s is not False: return unify(cdr(lcons), cdr(rcons), s) return False _unify.add((ConsPair, (ConsPair, list, tuple, Iterator, OrderedDict), dict), _cons_unify) _unify.add(((list, tuple, Iterator, OrderedDict), ConsPair, dict), _cons_unify) @_reify.register(ConsPair, dict) def reify_cons(lcons, s): rcar = reify(car(lcons), s) rcdr = reify(cdr(lcons), s) return cons(rcar, rcdr)
try: # noqa: C901 import unification from packaging import version if version.parse(unification.__version__) < version.parse("0.4.0"): raise ModuleNotFoundError() from unification.core import _reify, _unify, construction_sentinel, isvar except ModuleNotFoundError: pass else: def _unify_ExpressionTuple(u, v, s): yield _unify(getattr(u, "_tuple", u), getattr(v, "_tuple", v), s) _unify.add((ExpressionTuple, ExpressionTuple, Mapping), _unify_ExpressionTuple) _unify.add((tuple, ExpressionTuple, Mapping), _unify_ExpressionTuple) _unify.add((ExpressionTuple, tuple, Mapping), _unify_ExpressionTuple) def _unify_KwdPair(u, v, s): s = yield _unify(u.arg, v.arg, s) if s is not False: s = yield _unify(u.value, v.value, s) yield s _unify.add((KwdPair, KwdPair, Mapping), _unify_KwdPair) def _reify_ExpressionTuple(u, s): # The point of all this: we don't want to lose the expression # tracking/caching information. res = yield _reify(u._tuple, s)
def unifiable_with_term(cls): _reify.add((cls, dict), reify_term) _unify.add((cls, cls, dict), unify_term) return cls
import theano.tensor as tt from kanren.term import term, operator, arguments from unification.core import _reify, _unify, reify from ..meta import metatize from ..unify import unify_MetaSymbol from ..etuple import ExpressionTuple, etuplize from .meta import TheanoMetaSymbol tt_class_abstractions = tuple(c.base for c in TheanoMetaSymbol.base_subclasses()) _unify.add( (TheanoMetaSymbol, tt_class_abstractions, dict), lambda u, v, s: unify_MetaSymbol(u, metatize(v), s), ) _unify.add( (tt_class_abstractions, TheanoMetaSymbol, dict), lambda u, v, s: unify_MetaSymbol(metatize(u), v, s), ) _unify.add( (tt_class_abstractions, tt_class_abstractions, dict), lambda u, v, s: unify_MetaSymbol(metatize(u), metatize(v), s), ) def _reify_TheanoClasses(o, s): meta_obj = metatize(o) return reify(meta_obj, s)
elif u != v: return False if s: # If these two meta objects unified, and one has a logic # variable as its base object, consider the unknown base # object unified by the other's base object (if any). # This way, the original base objects can be recovered during # reification (preserving base object equality and such). if isinstance(u.obj, Var) and v.obj: s[u.obj] = v.obj elif isinstance(v.obj, Var) and u.obj: s[v.obj] = u.obj return s _unify.add((MetaSymbol, MetaSymbol, dict), unify_MetaSymbol) _unify.add((MetaSymbol, tt_class_abstractions, dict), lambda u, v, s: unify_MetaSymbol(u, MetaSymbol.from_obj(v), s)) _unify.add((tt_class_abstractions, MetaSymbol, dict), lambda u, v, s: unify_MetaSymbol(MetaSymbol.from_obj(u), v, s)) _unify.add((tt_class_abstractions, tt_class_abstractions, dict), lambda u, v, s: unify_MetaSymbol(MetaSymbol.from_obj(u), MetaSymbol.from_obj(v), s)) def _reify_MetaSymbol(o, s): if isinstance(o.obj, Var): obj = s.get(o.obj, o.obj) else: obj = None
elif u != v: return False if s: # If these two meta objects unified, and one has a logic # variable as its base object, consider the unknown base # object unified by the other's base object (if any). # This way, the original base objects can be recovered during # reification (preserving base object equality and such). if isinstance(u.obj, Var) and v.obj: s[u.obj] = v.obj elif isinstance(v.obj, Var) and u.obj: s[v.obj] = u.obj return s _unify.add((MetaSymbol, MetaSymbol, Mapping), unify_MetaSymbol) def _reify_MetaSymbol(o, s): if isinstance(o.obj, Var): # We allow reification of the base object field for # a meta object. # TODO: This is a weird thing that we should probably reconsider. # It's part of the functionality that allows base objects to fill-in # as logic variables, though. obj = s.get(o.obj, o.obj) else: # Otherwise, if there's a base object, it should indicate that there # are no logic variables or meta terms. # TODO: Seems like we should be able to skip the reify and comparison # below.
def test_unify_nonstandard_object(): _unify.add((ast.AST, ast.AST, Mapping), _unify_object) x = var() assert unify(ast.Num(n=1), ast.Num(n=1), {}) == {} assert unify(ast.Num(n=1), ast.Num(n=2), {}) is False assert unify(ast.Num(n=1), ast.Num(n=x), {}) == {x: 1}
return res.outputs apply.add((Op, ExpressionTuple), apply_Op_ExpressionTuple) def _unify_etuplize_first_arg(u, v, s): try: u_et = etuplize(u, shallow=True) yield _unify(u_et, v, s) except TypeError: yield False return _unify.add((Op, ExpressionTuple, Mapping), _unify_etuplize_first_arg) _unify.add((ExpressionTuple, Op, Mapping), lambda u, v, s: _unify_etuplize_first_arg(v, u, s)) _unify.add((Type, ExpressionTuple, Mapping), _unify_etuplize_first_arg) _unify.add((ExpressionTuple, Type, Mapping), lambda u, v, s: _unify_etuplize_first_arg(v, u, s)) def _unify_Variable_Variable(u, v, s): # Avoid converting to `etuple`s, when possible if u == v: yield s return if not u.owner and not v.owner:
from kanren.term import operator, arguments from unification.core import _reify, _unify, reify from cons.core import _car, _cdr from etuples import etuplize from .meta import TFlowMetaSymbol from ..meta import metatize from ..dispatch import unify_MetaSymbol tf_class_abstractions = tuple(c.base for c in TFlowMetaSymbol.base_subclasses()) _unify.add( (TFlowMetaSymbol, tf_class_abstractions, Mapping), lambda u, v, s: unify_MetaSymbol(u, metatize(v), s), ) _unify.add( (tf_class_abstractions, TFlowMetaSymbol, Mapping), lambda u, v, s: unify_MetaSymbol(metatize(u), v, s), ) _unify.add( (tf_class_abstractions, tf_class_abstractions, Mapping), lambda u, v, s: unify_MetaSymbol(metatize(u), metatize(v), s), ) def _reify_TFlowClasses(o, s): meta_obj = metatize(o) return reify(meta_obj, s)
elif u != v: return False if s: # If these two meta objects unified, and one has a logic # variable as its base object, consider the unknown base # object unified by the other's base object (if any). # This way, the original base objects can be recovered during # reification (preserving base object equality and such). if isinstance(u.obj, Var) and v.obj: s[u.obj] = v.obj elif isinstance(v.obj, Var) and u.obj: s[v.obj] = u.obj return s _unify.add((MetaSymbol, MetaSymbol, dict), unify_MetaSymbol) _tuple__unify = _unify.dispatch(tuple, tuple, dict) _unify.add((ExpressionTuple, (tuple, ExpressionTuple), dict), lambda x, y, s: _tuple__unify(x, y, s)) _unify.add((tuple, ExpressionTuple, dict), lambda x, y, s: _tuple__unify(x, y, s)) def _reify_MetaSymbol(o, s): if isinstance(o.obj, Var): # We allow reification of the base object field for # a meta object. # TODO: This is a weird thing that we should probably reconsider. # It's part of the functionality that allows base objects to fill-in
def unifiable_with_term(cls): _reify.add((cls, Mapping), reify_term) _unify.add((cls, cls, Mapping), unify_term) return cls
return False # Unfortunately, `multipledispatch` doesn't use `isinstance` on the arguments, # so it won't use our fancy setup for `isinstance(x, ConsPair)` and we have to # specify--and check--each `cons`-amenable type explicitly. def _cons_unify(lcons, rcons, s): if not is_cons(lcons) or not is_cons(rcons): # One of the arguments is necessarily a `ConsPair` object, # but the other could be an empty iterable, which isn't a # `cons`-derivable object. return False s = unify(car(lcons), car(rcons), s) if s is not False: return unify(cdr(lcons), cdr(rcons), s) return False _unify.add((ConsPair, (ConsPair, list, tuple, Iterator, OrderedDict), dict), _cons_unify) _unify.add(((list, tuple, Iterator, OrderedDict), ConsPair, dict), _cons_unify) @_reify.register(ConsPair, dict) def reify_cons(lcons, s): rcar = reify(car(lcons), s) rcdr = reify(cdr(lcons), s) return cons(rcar, rcdr)