Metadata-Version: 2.4
Name: effectful
Version: 0.2.0
Summary: Metaprogramming infrastructure
Author: Basis
License-Expression: Apache-2.0
Project-URL: Homepage, https://www.basis.ai/
Project-URL: Source, https://github.com/BasisResearch/effectful
Project-URL: Bug Tracker, https://github.com/BasisResearch/effectful/issues
Keywords: machine learning,statistics,probabilistic programming,bayesian modeling,pytorch
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Education
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.12
Description-Content-Type: text/x-rst
License-File: LICENSE.md
Provides-Extra: torch
Requires-Dist: torch; extra == "torch"
Requires-Dist: dm-tree; extra == "torch"
Provides-Extra: pyro
Requires-Dist: pyro-ppl>=1.9.1; extra == "pyro"
Requires-Dist: dm-tree; extra == "pyro"
Provides-Extra: jax
Requires-Dist: jax; extra == "jax"
Requires-Dist: dm-tree; extra == "jax"
Provides-Extra: numpyro
Requires-Dist: numpyro>=0.19; extra == "numpyro"
Requires-Dist: dm-tree; extra == "numpyro"
Provides-Extra: docs
Requires-Dist: effectful[jax,numpyro,pyro,torch]; extra == "docs"
Requires-Dist: sphinx; extra == "docs"
Requires-Dist: sphinxcontrib-bibtex; extra == "docs"
Requires-Dist: sphinx_rtd_theme; extra == "docs"
Requires-Dist: myst-parser; extra == "docs"
Requires-Dist: nbsphinx; extra == "docs"
Requires-Dist: sphinx_autodoc_typehints; extra == "docs"
Requires-Dist: pypandoc_binary; extra == "docs"
Provides-Extra: test
Requires-Dist: effectful[docs,jax,numpyro,pyro,torch]; extra == "test"
Requires-Dist: pytest; extra == "test"
Requires-Dist: pytest-cov; extra == "test"
Requires-Dist: pytest-xdist; extra == "test"
Requires-Dist: pytest-benchmark; extra == "test"
Requires-Dist: mypy; extra == "test"
Requires-Dist: ruff; extra == "test"
Requires-Dist: nbval; extra == "test"
Requires-Dist: nbqa; extra == "test"
Dynamic: license-file

.. index-inclusion-marker

Effectful
=========

Effectful is an algebraic effect system for Python, intended for use in the
implementation of probabilistic programming languages. It is a core component of
the `ChiRho <https://basisresearch.github.io/chirho/getting_started.html>`_
causal modeling language.

Installation
------------

Install From Source
^^^^^^^^^^^^^^^^^^^^
.. code:: sh

   git clone git@github.com:BasisResearch/effectful.git
   cd effectful
   git checkout master
   pip install -e .[pyro]

Install With Optional PyTorch/Pyro Support
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

``effectful`` has optional support for:

- `PyTorch <https://pytorch.org/>`_ (tensors with named dimensions)
- `Pyro <https://pyro.ai/>`_ (wrappers for Pyro effects)
- `Jax <https://docs.jax.dev/en/latest/index.html>`_ (tensors with named dimensions)
- `Numpyro <https://num.pyro.ai>`_ (operations for Numpyro distributions)

To enable PyTorch support:

.. code:: sh

   pip install effectful[torch]

Pyro support (which includes PyTorch support):

.. code:: sh

   pip install effectful[pyro]

Jax support:

.. code:: sh

   pip install effectful[jax]

Numpyro support (which includes Jax support):

.. code:: sh

   pip install effectful[numpyro]

Getting Started
---------------

Here's an example demonstrating how ``effectful`` can be used to implement a simple DSL that performs arithmetic on terms with free variables.

.. code:: python

   import functools

   from effectful.ops.types import Term
   from effectful.ops.syntax import defdata, defop
   from effectful.ops.semantics import handler, evaluate, coproduct, fwd

   add = defdata.dispatch(int).__add__

   def beta_add(x: int, y: int) -> int:
       match x, y:
           case int(), int():
               return x + y
           case _:
               return fwd()

   def commute_add(x: int, y: int) -> int:
       match x, y:
           case Term(), int():
               return y + x
           case _:
               return fwd()

   def assoc_add(x: int, y: int) -> int:
       match x, y:
           case _, Term(op, (a, b)) if op == add:
               return (x + a) + b
           case _:
               return fwd()

   beta_rules = {add: beta_add}
   commute_rules = {add: commute_add}
   assoc_rules = {add: assoc_add}

   eager_mixed = functools.reduce(coproduct, (beta_rules, commute_rules, assoc_rules))

We can represent free variables as operations with no arguments, generated using ``defop``:

.. code:: python

   >>> x = defop(int, name="x")
   >>> y = defop(int, name="y")

If we evaluate an expression containing free variables, we get a term:

.. code:: python

   >>> e = 1 + 1 + (x() + 1) + (5 + y())
   >>> print(e)
   add(2, add(add(x(), 1), add(5, y())))

We can make the evaluation strategy smarter by taking advantage of the commutativity and associativity of addition, as expressed by the ``commute_add`` and ``assoc_add`` handlers.

.. code:: python

   >>> with handler(eager_mixed):
   >>>     print(evaluate(e))
   add(8, add(x(), y()))

Learn More
----------

More examples and API documentation can be found in the `docs <https://basisresearch.github.io/effectful/index.html>`_.
