Skip to content

Commit

Permalink
Merge pull request #105 from brandonwillard/add-scan-meta-op
Browse files Browse the repository at this point in the history
Add scan meta op
  • Loading branch information
brandonwillard committed Mar 27, 2020
2 parents f0d4198 + 565d017 commit 6e319d1
Show file tree
Hide file tree
Showing 29 changed files with 538 additions and 444 deletions.
4 changes: 1 addition & 3 deletions Makefile
Expand Up @@ -64,10 +64,8 @@ coverage: test
diff-cover coverage.xml --compare-branch=master --fail-under=100

docs:
# latesttag=$(git describe --tags `git rev-list --tags --max-count=1`)
# echo checking out ${latesttag}
# git checkout ${latesttag}
pushd docs && \
sphinx-apidoc ../symbolic_pymc/ -o source --force && \
make html && \
ghp-import -n -p -r upstream -b gh-pages build/html && \
popd
Expand Down
16 changes: 0 additions & 16 deletions conftest.py

This file was deleted.

6 changes: 2 additions & 4 deletions docs/source/symbolic-pymc-tour.org
Expand Up @@ -1126,10 +1126,8 @@ like src_python[:eval never]{enable_lvar_defaults}, or by updates to the
defaults of meta object creation.

Likewise, there are tools available in Symbolic PyMC that make it easier to
determine why to objects won't unify
(i.e. src_python[:eval never]{symbolic_pymc.unify.debug_unify}) and exactly
which components are unequal between two meta objects
(i.e. src_python[:eval never]{symbolic_pymc.utils.meta_parts_unequal}).
determine which components are unequal between two meta objects
(e.g. src_python[:eval never]{symbolic_pymc.utils.meta_parts_unequal}).

Symbolic PyMC's library of relevant mathematical and statistical relations is
intended to evolve over time. These relations will reflect useful properties
Expand Down
6 changes: 2 additions & 4 deletions docs/source/symbolic-pymc-tour.rst
Expand Up @@ -1071,10 +1071,8 @@ like \ ``enable_lvar_defaults``\ , or by updates to the
defaults of meta object creation.

Likewise, there are tools available in Symbolic PyMC that make it easier to
determine why to objects won't unify
(i.e. \ ``symbolic_pymc.unify.debug_unify``\ ) and exactly
which components are unequal between two meta objects
(i.e. \ ``symbolic_pymc.utils.meta_parts_unequal``\ ).
determine which components are unequal between two meta objects
(e.g. \ ``symbolic_pymc.utils.meta_parts_unequal``\ ).

Symbolic PyMC's library of relevant mathematical and statistical relations is
intended to evolve over time. These relations will reflect useful properties
Expand Down
12 changes: 6 additions & 6 deletions docs/source/symbolic_pymc.rst
Expand Up @@ -13,18 +13,18 @@ Subpackages
Submodules
----------

symbolic\_pymc.meta module
--------------------------
symbolic\_pymc.dispatch module
------------------------------

.. automodule:: symbolic_pymc.meta
.. automodule:: symbolic_pymc.dispatch
:members:
:undoc-members:
:show-inheritance:

symbolic\_pymc.unify module
---------------------------
symbolic\_pymc.meta module
--------------------------

.. automodule:: symbolic_pymc.unify
.. automodule:: symbolic_pymc.meta
:members:
:undoc-members:
:show-inheritance:
Expand Down
24 changes: 16 additions & 8 deletions docs/source/symbolic_pymc.tensorflow.rst
Expand Up @@ -4,6 +4,22 @@ symbolic\_pymc.tensorflow package
Submodules
----------

symbolic\_pymc.tensorflow.dispatch module
-----------------------------------------

.. automodule:: symbolic_pymc.tensorflow.dispatch
:members:
:undoc-members:
:show-inheritance:

symbolic\_pymc.tensorflow.graph module
--------------------------------------

.. automodule:: symbolic_pymc.tensorflow.graph
:members:
:undoc-members:
:show-inheritance:

symbolic\_pymc.tensorflow.meta module
-------------------------------------

Expand All @@ -20,14 +36,6 @@ symbolic\_pymc.tensorflow.printing module
:undoc-members:
:show-inheritance:

symbolic\_pymc.tensorflow.unify module
--------------------------------------

.. automodule:: symbolic_pymc.tensorflow.unify
:members:
:undoc-members:
:show-inheritance:


Module contents
---------------
Expand Down
16 changes: 8 additions & 8 deletions docs/source/symbolic_pymc.theano.rst
Expand Up @@ -4,6 +4,14 @@ symbolic\_pymc.theano package
Submodules
----------

symbolic\_pymc.theano.dispatch module
-------------------------------------

.. automodule:: symbolic_pymc.theano.dispatch
:members:
:undoc-members:
:show-inheritance:

symbolic\_pymc.theano.meta module
---------------------------------

Expand Down Expand Up @@ -52,14 +60,6 @@ symbolic\_pymc.theano.random\_variables module
:undoc-members:
:show-inheritance:

symbolic\_pymc.theano.unify module
----------------------------------

.. automodule:: symbolic_pymc.theano.unify
:members:
:undoc-members:
:show-inheritance:

symbolic\_pymc.theano.utils module
----------------------------------

Expand Down
2 changes: 2 additions & 0 deletions requirements.txt
@@ -1,3 +1,5 @@
setuptools>=45.2.0
six>=1.14.0
-e ./
sympy>=1.3
coveralls
Expand Down
1 change: 1 addition & 0 deletions setup.py
Expand Up @@ -32,6 +32,7 @@ def get_long_description():
author_email=AUTHOR_EMAIL,
url=URL,
install_requires=[
"numpy>=1.18.1",
"scipy>=1.4.0",
"Theano>=1.0.4",
"tf-estimator-nightly==2.1.0.dev2020012309",
Expand Down
2 changes: 1 addition & 1 deletion symbolic_pymc/__init__.py
@@ -1,5 +1,5 @@
# We need this so that `multipledispatch` initialization occurs
from .unify import *
from .dispatch import *

from ._version import get_versions

Expand Down
34 changes: 0 additions & 34 deletions symbolic_pymc/unify.py → symbolic_pymc/dispatch.py
@@ -1,4 +1,3 @@
from functools import wraps
from collections.abc import Mapping

from cons.core import _car, _cdr, ConsError
Expand All @@ -13,36 +12,6 @@
from .meta import MetaSymbol, MetaVariable


class UnificationFailure(Exception):
pass


def debug_unify(enable=True): # pragma: no cover
"""Wrap unify functions so that they raise a `UnificationFailure` exception when unification fails."""
if enable:

def set_debug(f):
@wraps(f)
def wrapper(*args, **kwargs):
s = f(*args, **kwargs)
if s is False:
import pdb

pdb.set_trace()
raise UnificationFailure()
return s

return wrapper

_unify.funcs = {
sig: set_debug(getattr(f, "__wrapped__", f)) for sig, f in _unify.funcs.items()
}
_unify._cache.clear()
else:
_unify.funcs = {sig: getattr(f, "__wrapped__", f) for sig, f in _unify.funcs.items()}
_unify._cache.clear()


def unify_MetaSymbol(u, v, s):
if type(u) != type(v):
return False
Expand Down Expand Up @@ -157,6 +126,3 @@ def cdr_MetaVariable(x):

# arguments.add((MetaSymbol,), cdr_MetaSymbol)
arguments.add((MetaVariable,), cdr_MetaVariable)


__all__ = ["debug_unify"]
7 changes: 6 additions & 1 deletion symbolic_pymc/meta.py
Expand Up @@ -335,7 +335,12 @@ def __ne__(self, other):
return not self.__eq__(other)

def __hash__(self):
return hash((self.base, self.rands))
try:
rands = self.rands
except NotImplementedError: # pragma: no cover
return NotImplemented

return hash((self.base, rands))

def __str__(self):
return self.__repr__(show_obj=False, _repr=str)
Expand Down
38 changes: 36 additions & 2 deletions symbolic_pymc/relations/theano/__init__.py
@@ -1,22 +1,56 @@
import numpy as np
import theano.tensor as tt

from unification import var
from unification.utils import transitive_get as walk

from kanren import eq
from kanren.core import lall, Zzz
from kanren.facts import fact
from kanren.graph import applyo, walko
from kanren.assoccomm import commutative, associative
from kanren.constraints import neq

from etuples import etuple

from ...theano.meta import mt

from ...utils import HashableNDArray
from ...theano.meta import TheanoMetaConstant, mt

# Establish some Theano `Op`s as commutative and/or associative
fact(commutative, mt.add)
fact(commutative, mt.mul)
fact(associative, mt.add)
fact(associative, mt.mul)


def constant_neq(lvar, val):
"""Assert that a constant graph variable is not equal to a specific value.
Scalar values are broadcast across arrays.
XXX: This goal is non-relational.
TODO: Rewrite this as an actual constraint.
"""

if isinstance(val, np.ndarray):
val = val.view(HashableNDArray)

def constant_neq_goal(S):
lvar_rf = walk(lvar, S)
if isinstance(lvar_rf, (tt.Constant, TheanoMetaConstant)):
# Although `neq` is an actual constraint, the preceding type check
# and alternative success--when not a constant type--make the
# entire goal non-relational/not a true constraint, since the
# aforementioned check will only occur once per goal-stream/state
# and never again.
yield from neq(lvar_rf.data, val)(S)
else:
# When the value isn't a Theano constant, consider it satisfied
yield S

return constant_neq_goal


def non_obs_walko(relation, a, b):
"""Construct a goal that applies a relation to all nodes above an observed random variable.
Expand Down
31 changes: 2 additions & 29 deletions symbolic_pymc/relations/theano/distributions.py
@@ -1,19 +1,14 @@
"""Relations pertaining to probability distributions."""
import numpy as np
import theano.tensor as tt

from unification import var
from unification.utils import transitive_get as walk

from etuples import etuple

from kanren import conde, eq
from kanren.facts import fact, Relation
from kanren.constraints import neq

from . import constant_neq
from .. import concat
from ...utils import HashableNDArray
from ...theano.meta import TheanoMetaConstant, mt
from ...theano.meta import mt


derived_dist = Relation("derived_dist")
Expand Down Expand Up @@ -57,28 +52,6 @@
# None)


def constant_neq(lvar, val):
"""Assert that a constant graph variable is not equal to a specific value.
Scalar values are broadcast across arrays.
XXX: This goal is non-relational
"""

if isinstance(val, np.ndarray):
val = val.view(HashableNDArray)

def constant_neq_goal(S):
lvar_rf = walk(lvar, S)
if isinstance(lvar_rf, (tt.Constant, TheanoMetaConstant)):
# TODO: Can we get away with `neq(lvar_rf, val)` alone?
yield from neq(lvar_rf.data, val)(S)
else:
yield S

return constant_neq_goal


def scale_loc_transform(in_expr, out_expr):
"""Create relations for lifting and sinking scale and location parameters of distributions.
Expand Down
2 changes: 1 addition & 1 deletion symbolic_pymc/tensorflow/__init__.py
@@ -1,2 +1,2 @@
# Needed to register generic functions
from .unify import *
from .dispatch import *
Expand Up @@ -12,7 +12,7 @@

from .meta import TFlowMetaSymbol
from ..meta import metatize
from ..unify import unify_MetaSymbol
from ..dispatch import unify_MetaSymbol

tf_class_abstractions = tuple(c.base for c in TFlowMetaSymbol.base_subclasses())

Expand Down
2 changes: 1 addition & 1 deletion symbolic_pymc/theano/__init__.py
@@ -1,2 +1,2 @@
# Needed to register generic functions
from .unify import *
from .dispatch import *
Expand Up @@ -13,7 +13,7 @@

from .meta import TheanoMetaSymbol
from ..meta import metatize
from ..unify import unify_MetaSymbol
from ..dispatch import unify_MetaSymbol


tt_class_abstractions = tuple(c.base for c in TheanoMetaSymbol.base_subclasses())
Expand Down

0 comments on commit 6e319d1

Please sign in to comment.