effectful 0.0.1__py3-none-any.whl → 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
effectful/ops/types.py CHANGED
@@ -1,8 +1,10 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import abc
4
+ import collections.abc
5
+ import inspect
4
6
  import typing
5
- from typing import Any, Callable, Generic, Mapping, Sequence, Set, Type, TypeVar, Union
7
+ from typing import Any, Callable, Generic, Mapping, Sequence, Type, TypeVar, Union
6
8
 
7
9
  from typing_extensions import ParamSpec
8
10
 
@@ -22,6 +24,9 @@ class Operation(abc.ABC, Generic[Q, V]):
22
24
 
23
25
  """
24
26
 
27
+ __signature__: inspect.Signature
28
+ __name__: str
29
+
25
30
  @abc.abstractmethod
26
31
  def __eq__(self, other):
27
32
  raise NotImplementedError
@@ -38,19 +43,24 @@ class Operation(abc.ABC, Generic[Q, V]):
38
43
  """
39
44
  raise NotImplementedError
40
45
 
41
- @abc.abstractmethod
42
- def __free_rule__(self, *args: Q.args, **kwargs: Q.kwargs) -> "Expr[V]":
43
- """Returns a term for the operation applied to arguments."""
44
- raise NotImplementedError
45
-
46
46
  @abc.abstractmethod
47
47
  def __type_rule__(self, *args: Q.args, **kwargs: Q.kwargs) -> Type[V]:
48
48
  """Returns the type of the operation applied to arguments."""
49
49
  raise NotImplementedError
50
50
 
51
51
  @abc.abstractmethod
52
- def __fvs_rule__(self, *args: Q.args, **kwargs: Q.kwargs) -> Set["Operation"]:
53
- """Returns the free variables of the operation applied to arguments."""
52
+ def __fvs_rule__(self, *args: Q.args, **kwargs: Q.kwargs) -> tuple[
53
+ tuple[collections.abc.Set["Operation"], ...],
54
+ dict[str, collections.abc.Set["Operation"]],
55
+ ]:
56
+ """
57
+ Returns the sets of variables that appear free in each argument and keyword argument
58
+ but not in the result of the operation, i.e. the variables bound by the operation.
59
+
60
+ These are used by :func:`fvsof` to determine the free variables of a term by
61
+ subtracting the results of this method from the free variables of the subterms,
62
+ allowing :func:`fvsof` to be implemented in terms of :func:`evaluate` .
63
+ """
54
64
  raise NotImplementedError
55
65
 
56
66
  @abc.abstractmethod
@@ -77,19 +87,19 @@ class Term(abc.ABC, Generic[T]):
77
87
  @abc.abstractmethod
78
88
  def op(self) -> Operation[..., T]:
79
89
  """Abstract property for the operation."""
80
- pass
90
+ raise NotImplementedError
81
91
 
82
92
  @property
83
93
  @abc.abstractmethod
84
94
  def args(self) -> Sequence["Expr[Any]"]:
85
95
  """Abstract property for the arguments."""
86
- pass
96
+ raise NotImplementedError
87
97
 
88
98
  @property
89
99
  @abc.abstractmethod
90
100
  def kwargs(self) -> Mapping[str, "Expr[Any]"]:
91
101
  """Abstract property for the keyword arguments."""
92
- pass
102
+ raise NotImplementedError
93
103
 
94
104
  def __repr__(self) -> str:
95
105
  from effectful.internals.runtime import interpreter
@@ -106,5 +116,9 @@ Expr = Union[T, Term[T]]
106
116
  Interpretation = Mapping[Operation[..., T], Callable[..., V]]
107
117
 
108
118
 
109
- class ArgAnnotation:
110
- pass
119
+ class Annotation(abc.ABC):
120
+
121
+ @classmethod
122
+ @abc.abstractmethod
123
+ def infer_annotations(cls, sig: inspect.Signature) -> inspect.Signature:
124
+ raise NotImplementedError
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: effectful
3
- Version: 0.0.1
3
+ Version: 0.1.0
4
4
  Summary: Metaprogramming infrastructure
5
5
  Home-page: https://www.basis.ai/
6
6
  Author: Basis
@@ -28,6 +28,12 @@ Provides-Extra: dev
28
28
  Requires-Dist: torch; extra == "dev"
29
29
  Requires-Dist: pyro-ppl; extra == "dev"
30
30
  Requires-Dist: torch; extra == "dev"
31
+ Requires-Dist: setuptools; extra == "dev"
32
+ Requires-Dist: sphinx; extra == "dev"
33
+ Requires-Dist: sphinxcontrib-bibtex; extra == "dev"
34
+ Requires-Dist: sphinx_rtd_theme; extra == "dev"
35
+ Requires-Dist: myst-parser; extra == "dev"
36
+ Requires-Dist: nbsphinx; extra == "dev"
31
37
  Requires-Dist: pytest; extra == "dev"
32
38
  Requires-Dist: pytest-cov; extra == "dev"
33
39
  Requires-Dist: pytest-xdist; extra == "dev"
@@ -36,13 +42,15 @@ Requires-Dist: mypy; extra == "dev"
36
42
  Requires-Dist: black; extra == "dev"
37
43
  Requires-Dist: flake8; extra == "dev"
38
44
  Requires-Dist: isort; extra == "dev"
39
- Requires-Dist: sphinx; extra == "dev"
40
- Requires-Dist: sphinxcontrib-bibtex; extra == "dev"
41
- Requires-Dist: sphinx_rtd_theme; extra == "dev"
42
- Requires-Dist: myst-parser; extra == "dev"
43
- Requires-Dist: nbsphinx; extra == "dev"
44
45
  Requires-Dist: nbval; extra == "dev"
45
46
  Requires-Dist: nbqa; extra == "dev"
47
+ Provides-Extra: docs
48
+ Requires-Dist: setuptools; extra == "docs"
49
+ Requires-Dist: sphinx; extra == "docs"
50
+ Requires-Dist: sphinxcontrib-bibtex; extra == "docs"
51
+ Requires-Dist: sphinx_rtd_theme; extra == "docs"
52
+ Requires-Dist: myst-parser; extra == "docs"
53
+ Requires-Dist: nbsphinx; extra == "docs"
46
54
  Dynamic: author
47
55
  Dynamic: classifier
48
56
  Dynamic: description
@@ -55,6 +63,7 @@ Dynamic: requires-dist
55
63
  Dynamic: requires-python
56
64
  Dynamic: summary
57
65
 
66
+
58
67
  .. index-inclusion-marker
59
68
 
60
69
  Effectful
@@ -75,7 +84,7 @@ Install From Source
75
84
  git clone git@github.com:BasisResearch/effectful.git
76
85
  cd effectful
77
86
  git checkout master
78
- pip install -e .[test]
87
+ pip install -e .[pyro]
79
88
 
80
89
  Install With Optional PyTorch/Pyro Support
81
90
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -103,16 +112,12 @@ Here's an example demonstrating how ``effectful`` can be used to implement a sim
103
112
 
104
113
  .. code:: python
105
114
 
106
- import operator
107
115
  import functools
108
116
 
109
- from effectful.handlers.operator import OPERATORS
110
117
  from effectful.ops.types import Term
111
118
  from effectful.ops.syntax import defop
112
119
  from effectful.ops.semantics import handler, evaluate, coproduct, fwd
113
- import effectful.handlers.operator
114
-
115
- add = OPERATORS[operator.add]
120
+ from effectful.handlers.numbers import add
116
121
 
117
122
  def beta_add(x: int, y: int) -> int:
118
123
  match x, y:
@@ -0,0 +1,18 @@
1
+ effectful/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ effectful/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ effectful/handlers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ effectful/handlers/indexed.py,sha256=k98C-Gtm8rQPfCYRCUVEPlElwiD3PwWGZ5hwNTTaIt8,12651
5
+ effectful/handlers/numbers.py,sha256=X-RI9IcOahSrwDNF0xZtQwxBvgKbNQpJRB_m9Vzy-Ew,6656
6
+ effectful/handlers/pyro.py,sha256=r0Zyv9nNDaAdwujI3J-nlsIrlAHp-AAuLQ5DfG8S2q4,15294
7
+ effectful/handlers/torch.py,sha256=cQxrIJUiC2Zmiz6e_OS-DgtVsHGr_MCsoTY2kvZ05Y4,18945
8
+ effectful/internals/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ effectful/internals/runtime.py,sha256=E0ce5mfG0-DlTeALYZePWtdxr0AK3y0CPl_LE5pp_0k,1908
10
+ effectful/ops/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ effectful/ops/semantics.py,sha256=w5UWCkUH1UznIB4NKaQE1TCA2cOLlnbRKd0PNma30Rg,10252
12
+ effectful/ops/syntax.py,sha256=U2URezErfQPySVpLNd7Wzx6ZXZwHDo4LZOvhalaXeBI,38241
13
+ effectful/ops/types.py,sha256=vTg6eQQN5Xq1KIelUfEUvpofO2iJ3x8YbiyPzNESTjQ,3823
14
+ effectful-0.1.0.dist-info/LICENSE.md,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
15
+ effectful-0.1.0.dist-info/METADATA,sha256=mBVk7m_Lf3GkcR1WVQaKCMJ5f4AeAykMYCocITL7cko,5165
16
+ effectful-0.1.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
17
+ effectful-0.1.0.dist-info/top_level.txt,sha256=gtuJfrE2nXil_lZLCnqWF2KAbOnJs9ILNvK8WnkRzbs,10
18
+ effectful-0.1.0.dist-info/RECORD,,
@@ -1,259 +0,0 @@
1
- import collections
2
- import functools
3
- import inspect
4
- import typing
5
- from typing import Callable, Generic, Mapping, Sequence, Set, Type, TypeVar
6
-
7
- import tree
8
- from typing_extensions import ParamSpec
9
-
10
- from effectful.ops.types import Expr, Operation, Term
11
-
12
- P = ParamSpec("P")
13
- Q = ParamSpec("Q")
14
- S = TypeVar("S")
15
- T = TypeVar("T")
16
- V = TypeVar("V")
17
-
18
-
19
- def rename(
20
- subs: Mapping[Operation[..., S], Operation[..., S]],
21
- leaf_value: V, # Union[Term[V], Operation[..., V], V],
22
- ) -> V: # Union[Term[V], Operation[..., V], V]:
23
- from effectful.internals.runtime import interpreter
24
- from effectful.ops.semantics import apply, evaluate
25
-
26
- if isinstance(leaf_value, Operation):
27
- return subs.get(leaf_value, leaf_value) # type: ignore
28
- elif isinstance(leaf_value, Term):
29
- with interpreter(
30
- {apply: lambda _, op, *a, **k: op.__free_rule__(*a, **k), **subs}
31
- ):
32
- return evaluate(leaf_value) # type: ignore
33
- else:
34
- return leaf_value
35
-
36
-
37
- class _BaseOperation(Generic[Q, V], Operation[Q, V]):
38
- signature: Callable[Q, V]
39
-
40
- def __init__(self, signature: Callable[Q, V]):
41
- functools.update_wrapper(self, signature)
42
- self.signature = signature
43
-
44
- def __eq__(self, other):
45
- if not isinstance(other, Operation):
46
- return NotImplemented
47
- return self.signature == other.signature
48
-
49
- def __hash__(self):
50
- return hash(self.signature)
51
-
52
- def __default_rule__(self, *args: Q.args, **kwargs: Q.kwargs) -> "Expr[V]":
53
- from effectful.ops.syntax import NoDefaultRule
54
-
55
- try:
56
- return self.signature(*args, **kwargs)
57
- except NoDefaultRule:
58
- return self.__free_rule__(*args, **kwargs)
59
-
60
- def __free_rule__(self, *args: Q.args, **kwargs: Q.kwargs) -> "Expr[V]":
61
- from effectful.ops.syntax import Bound, Scoped, defdata, defop
62
-
63
- sig = inspect.signature(self.signature)
64
- bound_sig = sig.bind(*args, **kwargs)
65
- bound_sig.apply_defaults()
66
-
67
- bound_vars: dict[int, set[Operation]] = collections.defaultdict(set)
68
- scoped_args: dict[int, set[str]] = collections.defaultdict(set)
69
- unscoped_args: set[str] = set()
70
- for param_name, param in bound_sig.signature.parameters.items():
71
- if typing.get_origin(param.annotation) is typing.Annotated:
72
- for anno in param.annotation.__metadata__:
73
- if isinstance(anno, Bound):
74
- scoped_args[anno.scope].add(param_name)
75
- if param.kind is inspect.Parameter.VAR_POSITIONAL:
76
- assert isinstance(bound_sig.arguments[param_name], tuple)
77
- for bound_var in bound_sig.arguments[param_name]:
78
- bound_vars[anno.scope].add(bound_var)
79
- elif param.kind is inspect.Parameter.VAR_KEYWORD:
80
- assert isinstance(bound_sig.arguments[param_name], dict)
81
- for bound_var in bound_sig.arguments[param_name].values():
82
- bound_vars[anno.scope].add(bound_var)
83
- else:
84
- bound_vars[anno.scope].add(bound_sig.arguments[param_name])
85
- elif isinstance(anno, Scoped):
86
- scoped_args[anno.scope].add(param_name)
87
- else:
88
- unscoped_args.add(param_name)
89
-
90
- # TODO replace this temporary check with more general scope level propagation
91
- if bound_vars:
92
- min_scope = min(bound_vars.keys(), default=0)
93
- scoped_args[min_scope] |= unscoped_args
94
- max_scope = max(bound_vars.keys(), default=0)
95
- assert all(s in bound_vars or s > max_scope for s in scoped_args.keys())
96
-
97
- # recursively rename bound variables from innermost to outermost scope
98
- for scope in sorted(bound_vars.keys()):
99
- # create fresh variables for each bound variable in the scope
100
- renaming_map = {var: defop(var) for var in bound_vars[scope]}
101
- # get just the arguments that are in the scope
102
- for name in scoped_args[scope]:
103
- bound_sig.arguments[name] = tree.map_structure(
104
- lambda a: rename(renaming_map, a),
105
- bound_sig.arguments[name],
106
- )
107
-
108
- return defdata(_BaseTerm(self, bound_sig.args, bound_sig.kwargs))
109
-
110
- def __type_rule__(self, *args: Q.args, **kwargs: Q.kwargs) -> Type[V]:
111
- sig = inspect.signature(self.signature)
112
- bound_sig = sig.bind(*args, **kwargs)
113
- bound_sig.apply_defaults()
114
-
115
- anno = sig.return_annotation
116
- if anno is inspect.Signature.empty:
117
- return typing.cast(Type[V], object)
118
- elif isinstance(anno, typing.TypeVar):
119
- # rudimentary but sound special-case type inference sufficient for syntax ops:
120
- # if the return type annotation is a TypeVar,
121
- # look for a parameter with the same annotation and return its type,
122
- # otherwise give up and return Any/object
123
- for name, param in bound_sig.signature.parameters.items():
124
- if param.annotation is anno and param.kind not in (
125
- inspect.Parameter.VAR_POSITIONAL,
126
- inspect.Parameter.VAR_KEYWORD,
127
- ):
128
- arg = bound_sig.arguments[name]
129
- tp: Type[V] = type(arg) if not isinstance(arg, type) else arg
130
- return tp
131
- return typing.cast(Type[V], object)
132
- elif typing.get_origin(anno) is typing.Annotated:
133
- tp = typing.get_args(anno)[0]
134
- if not typing.TYPE_CHECKING:
135
- tp = tp if typing.get_origin(tp) is None else typing.get_origin(tp)
136
- return tp
137
- elif typing.get_origin(anno) is not None:
138
- return typing.get_origin(anno)
139
- else:
140
- return anno
141
-
142
- def __fvs_rule__(self, *args: Q.args, **kwargs: Q.kwargs) -> Set[Operation]:
143
- from effectful.ops.syntax import Bound
144
-
145
- sig = inspect.signature(self.signature)
146
- bound_sig = sig.bind(*args, **kwargs)
147
- bound_sig.apply_defaults()
148
-
149
- bound_vars: Set[Operation] = set()
150
- for param_name, param in bound_sig.signature.parameters.items():
151
- if typing.get_origin(param.annotation) is typing.Annotated:
152
- for anno in param.annotation.__metadata__:
153
- if isinstance(anno, Bound):
154
- if param.kind is inspect.Parameter.VAR_POSITIONAL:
155
- for bound_var in bound_sig.arguments[param_name]:
156
- bound_vars.add(bound_var)
157
- elif param.kind is inspect.Parameter.VAR_KEYWORD:
158
- for bound_var in bound_sig.arguments[param_name].values():
159
- bound_vars.add(bound_var)
160
- else:
161
- bound_var = bound_sig.arguments[param_name]
162
- bound_vars.add(bound_var)
163
-
164
- return bound_vars
165
-
166
- def __repr_rule__(self, *args: Q.args, **kwargs: Q.kwargs) -> str:
167
- args_str = ", ".join(map(str, args)) if args else ""
168
- kwargs_str = (
169
- ", ".join(f"{k}={str(v)}" for k, v in kwargs.items()) if kwargs else ""
170
- )
171
-
172
- ret = f"{self.signature.__name__}({args_str}"
173
- if kwargs:
174
- ret += f"{', ' if args else ''}"
175
- ret += f"{kwargs_str})"
176
- return ret
177
-
178
- def __repr__(self):
179
- return self.signature.__name__
180
-
181
-
182
- class _BaseTerm(Generic[T], Term[T]):
183
- _op: Operation[..., T]
184
- _args: Sequence[Expr]
185
- _kwargs: Mapping[str, Expr]
186
-
187
- def __init__(
188
- self,
189
- op: Operation[..., T],
190
- args: Sequence[Expr],
191
- kwargs: Mapping[str, Expr],
192
- ):
193
- self._op = op
194
- self._args = args
195
- self._kwargs = kwargs
196
-
197
- def __eq__(self, other) -> bool:
198
- from effectful.ops.syntax import syntactic_eq
199
-
200
- return syntactic_eq(self, other)
201
-
202
- @property
203
- def op(self):
204
- return self._op
205
-
206
- @property
207
- def args(self):
208
- return self._args
209
-
210
- @property
211
- def kwargs(self):
212
- return self._kwargs
213
-
214
-
215
- class _CallableTerm(Generic[P, T], _BaseTerm[collections.abc.Callable[P, T]]):
216
- def __call__(self, *args: Expr, **kwargs: Expr) -> Expr[T]:
217
- from effectful.ops.semantics import call
218
-
219
- return call(self, *args, **kwargs) # type: ignore
220
-
221
-
222
- def _unembed_callable(value: Callable[P, T]) -> Expr[Callable[P, T]]:
223
- from effectful.internals.runtime import interpreter
224
- from effectful.ops.semantics import apply, call
225
- from effectful.ops.syntax import deffn, defop
226
-
227
- assert not isinstance(value, Term)
228
-
229
- try:
230
- sig = inspect.signature(value)
231
- except ValueError:
232
- return value
233
-
234
- for name, param in sig.parameters.items():
235
- if param.kind in (
236
- inspect.Parameter.VAR_POSITIONAL,
237
- inspect.Parameter.VAR_KEYWORD,
238
- ):
239
- raise NotImplementedError(
240
- f"cannot unembed {value}: parameter {name} is variadic"
241
- )
242
-
243
- bound_sig = sig.bind(
244
- **{name: defop(param.annotation) for name, param in sig.parameters.items()}
245
- )
246
- bound_sig.apply_defaults()
247
-
248
- with interpreter(
249
- {
250
- apply: lambda _, op, *a, **k: op.__free_rule__(*a, **k),
251
- call: call.__default_rule__,
252
- }
253
- ):
254
- body = value(
255
- *[a() for a in bound_sig.args],
256
- **{k: v() for k, v in bound_sig.kwargs.items()},
257
- )
258
-
259
- return deffn(body, *bound_sig.args, **bound_sig.kwargs)
@@ -1,19 +0,0 @@
1
- effectful/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- effectful/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- effectful/handlers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- effectful/handlers/indexed.py,sha256=lNqlHZqD8JBs-KaC3YdCoQDAXHuQ9KhAH0ZhpIqMdFM,13184
5
- effectful/handlers/numbers.py,sha256=aKVCJ-l3ISvOGHTfXsQIEp4uEsQsYy7bP-YUCsSO_3o,6557
6
- effectful/handlers/pyro.py,sha256=s_CAXe65gqXmKiYLUXJ0_uLQzEEgyQbZv0hQWMMdQJ4,15312
7
- effectful/handlers/torch.py,sha256=LJYwud6Dq3xg7A6_6aaDEyP1hT0IB4G_55Gki6izRvo,18401
8
- effectful/internals/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- effectful/internals/base_impl.py,sha256=6Yxxg5tS_R2RLlHvtOhj_fuA_9hEa_YKf0LhQOX2448,9671
10
- effectful/internals/runtime.py,sha256=E0ce5mfG0-DlTeALYZePWtdxr0AK3y0CPl_LE5pp_0k,1908
11
- effectful/ops/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- effectful/ops/semantics.py,sha256=Ihaj2ycildtMg18GSujSJSaAAKsArKoyMmR-gBoWx8E,10208
13
- effectful/ops/syntax.py,sha256=nRfguHAol5lDnUv0UMOB0AL6y6By2DCW7hlCYsN3OnM,15331
14
- effectful/ops/types.py,sha256=DmVqX8naWNQEW1A6w7ShrXp_LZV6qoK4p_OAM3NitTI,3241
15
- effectful-0.0.1.dist-info/LICENSE.md,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
16
- effectful-0.0.1.dist-info/METADATA,sha256=WN7j_mJwZao1T1MTWyAIo_1hP6YUCS4L6rPI9wWCScA,4930
17
- effectful-0.0.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
18
- effectful-0.0.1.dist-info/top_level.txt,sha256=gtuJfrE2nXil_lZLCnqWF2KAbOnJs9ILNvK8WnkRzbs,10
19
- effectful-0.0.1.dist-info/RECORD,,