procfunc 0.36.0__tar.gz → 0.36.1__tar.gz
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.
- {procfunc-0.36.0/src/procfunc.egg-info → procfunc-0.36.1}/PKG-INFO +1 -1
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/__init__.py +1 -1
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/codegen/codegen.py +18 -14
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/random.py +17 -17
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/tracer/trace.py +5 -6
- {procfunc-0.36.0 → procfunc-0.36.1/src/procfunc.egg-info}/PKG-INFO +1 -1
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc.egg-info/SOURCES.txt +1 -0
- procfunc-0.36.1/tests/test_random_mutation.py +92 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/LICENSE.md +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/README.md +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/pyproject.toml +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/setup.cfg +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/cli/__init__.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/cli/main.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/codegen/__init__.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/codegen/identifiers.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/codegen/repr.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/color.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/compute_graph/__init__.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/compute_graph/compute_graph.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/compute_graph/node.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/compute_graph/operators_info.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/compute_graph/proxy.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/compute_graph/util.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/context.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/control/__init__.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/nodes/__init__.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/nodes/color.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/nodes/compositor.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/nodes/execute/construct_nodes.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/nodes/execute/construct_operator.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/nodes/execute/construct_special_cases.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/nodes/execute/construct_standard.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/nodes/execute/execute.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/nodes/execute/infer_runtime_data_type.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/nodes/execute/realize.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/nodes/execute/util.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/nodes/func.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/nodes/geo.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/nodes/manifest.json +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/nodes/math.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/nodes/shader.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/nodes/texture.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/nodes/types.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/nodes/util/__init__.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/nodes/util/bindings_util.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/nodes/util/bpy_node_info.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/nodes/util/node_function.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/ops/__init__.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/ops/_util.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/ops/addons.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/ops/attr.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/ops/collection.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/ops/curve.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/ops/file.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/ops/manifest.json +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/ops/mesh.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/ops/modifier.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/ops/object.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/ops/primitives/__init__.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/ops/primitives/camera.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/ops/primitives/curve.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/ops/primitives/light.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/ops/primitives/mesh.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/ops/uv.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/tracer/__init__.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/tracer/decorator.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/tracer/patch.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/tracer/proxy.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/transforms/__init__.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/transforms/cleanup.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/transforms/convert.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/transforms/distribution.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/transforms/extract_materials.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/transforms/infer_distribution.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/transforms/parameters.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/transforms/util.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/transpiler/__init__.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/transpiler/bpy_to_computegraph.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/transpiler/main.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/transpiler/parse_attrs.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/transpiler/parse_default_values.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/transpiler/parse_special_cases.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/types.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/util/__init__.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/util/bpy_data.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/util/bpy_info.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/util/camera.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/util/keyframe.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/util/log.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/util/manifest.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/util/pytree.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc/util/teardown.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc.egg-info/dependency_links.txt +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc.egg-info/entry_points.txt +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc.egg-info/requires.txt +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/src/procfunc.egg-info/top_level.txt +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/tests/test_asset.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/tests/test_bpy_data_cleanup.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/tests/test_cli_transpile.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/tests/test_codegen.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/tests/test_codegen_matrix.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/tests/test_compute_graph.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/tests/test_control.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/tests/test_node_function.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/tests/test_ops.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/tests/test_pytree.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/tests/test_random.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/tests/test_teardown.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/tests/test_trace.py +0 -0
- {procfunc-0.36.0 → procfunc-0.36.1}/tests/test_transforms.py +0 -0
|
@@ -606,7 +606,8 @@ def _codegen_for_assignment(
|
|
|
606
606
|
def _expressions_scope_for_graph(
|
|
607
607
|
graph: cg.ComputeGraph,
|
|
608
608
|
scope_expressions: dict[int, str | list[str]],
|
|
609
|
-
|
|
609
|
+
fold_map: dict[int, bool],
|
|
610
|
+
) -> dict[int, str | list[str]]:
|
|
610
611
|
expressions: dict[int, str | list[str]] = {
|
|
611
612
|
**scope_expressions.copy(),
|
|
612
613
|
**_check_graph_input_names(graph, scope_expressions),
|
|
@@ -615,9 +616,6 @@ def _expressions_scope_for_graph(
|
|
|
615
616
|
# when we want to refer to a value, what string should we insert?
|
|
616
617
|
# - for most nodes: refer to a variable name
|
|
617
618
|
# - for inlined expressions: emplace a expression string
|
|
618
|
-
usages = cg.usages_per_node(graph)
|
|
619
|
-
fold_map = _expression_fold_map(graph, expressions, usages=usages)
|
|
620
|
-
|
|
621
619
|
node_names = identifiers.nodenames_from_fixed_and_infill(
|
|
622
620
|
graph,
|
|
623
621
|
fold_map=fold_map,
|
|
@@ -630,7 +628,7 @@ def _expressions_scope_for_graph(
|
|
|
630
628
|
raise ValueError(f"Scope and node names had overlap: {intersection=}")
|
|
631
629
|
expressions.update(node_names)
|
|
632
630
|
|
|
633
|
-
return expressions
|
|
631
|
+
return expressions
|
|
634
632
|
|
|
635
633
|
|
|
636
634
|
def _codegen_for_graph(
|
|
@@ -646,17 +644,13 @@ def _codegen_for_graph(
|
|
|
646
644
|
if add_version_comment:
|
|
647
645
|
code_lines.append(f"# Code generated by procfunc v{pf.__version__}")
|
|
648
646
|
|
|
649
|
-
|
|
647
|
+
usages = cg.usages_per_node(graph)
|
|
648
|
+
fold_map = _expression_fold_map(graph, scope_expressions, usages)
|
|
649
|
+
expressions = _expressions_scope_for_graph(graph, scope_expressions, fold_map)
|
|
650
650
|
_input_ids = set(id(node) for node in graph.inputs.values()) # noqa: F841
|
|
651
|
-
|
|
651
|
+
output_ids = {id(node) for node in graph.outputs.values()}
|
|
652
652
|
last_varname: str = ""
|
|
653
653
|
|
|
654
|
-
# Collect mutator call nodes so they emit as bare statements (no assignment)
|
|
655
|
-
mutator_call_ids = set()
|
|
656
|
-
for node in cg.traverse_depth_first(graph):
|
|
657
|
-
if isinstance(node, cg.MutatedArgumentNode):
|
|
658
|
-
mutator_call_ids.add(id(node.args[1]))
|
|
659
|
-
|
|
660
654
|
for node in cg.traverse_depth_first(graph):
|
|
661
655
|
if isinstance(node, cg.InputPlaceholderNode):
|
|
662
656
|
continue # arguments are defined in _codegen_graph_inputs
|
|
@@ -673,7 +667,17 @@ def _codegen_for_graph(
|
|
|
673
667
|
expressions[id(node)] = node_code
|
|
674
668
|
continue
|
|
675
669
|
|
|
676
|
-
|
|
670
|
+
node_usages = usages.get(id(node), [])
|
|
671
|
+
mutation_usages = [
|
|
672
|
+
usage
|
|
673
|
+
for usage in node_usages
|
|
674
|
+
if isinstance(usage, cg.MutatedArgumentNode) and usage.args[1] is node
|
|
675
|
+
]
|
|
676
|
+
if (
|
|
677
|
+
mutation_usages
|
|
678
|
+
and mutation_usages == node_usages
|
|
679
|
+
and id(node) not in output_ids
|
|
680
|
+
):
|
|
677
681
|
code_lines.extend(node_code if isinstance(node_code, list) else [node_code])
|
|
678
682
|
continue
|
|
679
683
|
|
|
@@ -10,7 +10,7 @@ T = TypeVar("T")
|
|
|
10
10
|
logger = logging.getLogger(__name__)
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
@pf.tracer.primitive(allow_exec=True)
|
|
13
|
+
@pf.tracer.primitive(allow_exec=True, mutates=["rng"])
|
|
14
14
|
def uniform(
|
|
15
15
|
rng: np.random.Generator,
|
|
16
16
|
low: float,
|
|
@@ -19,7 +19,7 @@ def uniform(
|
|
|
19
19
|
return rng.uniform(low, high)
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
@pf.tracer.primitive(allow_exec=True)
|
|
22
|
+
@pf.tracer.primitive(allow_exec=True, mutates=["rng"])
|
|
23
23
|
def normal(
|
|
24
24
|
rng: np.random.Generator,
|
|
25
25
|
mean: float,
|
|
@@ -28,7 +28,7 @@ def normal(
|
|
|
28
28
|
return rng.normal(mean, std)
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
@pf.tracer.primitive(allow_exec=True)
|
|
31
|
+
@pf.tracer.primitive(allow_exec=True, mutates=["rng"])
|
|
32
32
|
def randint(
|
|
33
33
|
rng: np.random.Generator,
|
|
34
34
|
low: int,
|
|
@@ -37,7 +37,7 @@ def randint(
|
|
|
37
37
|
return rng.integers(low, high)
|
|
38
38
|
|
|
39
39
|
|
|
40
|
-
@pf.tracer.primitive(allow_exec=True)
|
|
40
|
+
@pf.tracer.primitive(allow_exec=True, mutates=["rng"])
|
|
41
41
|
def uniform_tails(
|
|
42
42
|
rng: np.random.Generator,
|
|
43
43
|
low: float,
|
|
@@ -50,7 +50,7 @@ def uniform_tails(
|
|
|
50
50
|
return rng.uniform(high - (high - low) * tail_pct, high)
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
@pf.tracer.primitive(allow_exec=True)
|
|
53
|
+
@pf.tracer.primitive(allow_exec=True, mutates=["rng"])
|
|
54
54
|
def clip_gaussian(
|
|
55
55
|
rng: np.random.Generator,
|
|
56
56
|
mean: float,
|
|
@@ -75,7 +75,7 @@ def clip_gaussian(
|
|
|
75
75
|
)
|
|
76
76
|
|
|
77
77
|
|
|
78
|
-
@pf.tracer.primitive(allow_exec=True)
|
|
78
|
+
@pf.tracer.primitive(allow_exec=True, mutates=["rng"])
|
|
79
79
|
def wrap_gaussian(
|
|
80
80
|
rng: np.random.Generator,
|
|
81
81
|
mean: float,
|
|
@@ -93,7 +93,7 @@ def wrap_gaussian(
|
|
|
93
93
|
return x
|
|
94
94
|
|
|
95
95
|
|
|
96
|
-
@pf.tracer.primitive(allow_exec=True)
|
|
96
|
+
@pf.tracer.primitive(allow_exec=True, mutates=["rng"])
|
|
97
97
|
def exponential(
|
|
98
98
|
rng: np.random.Generator,
|
|
99
99
|
scale: float,
|
|
@@ -101,7 +101,7 @@ def exponential(
|
|
|
101
101
|
return rng.exponential(scale)
|
|
102
102
|
|
|
103
103
|
|
|
104
|
-
@pf.tracer.primitive(allow_exec=True)
|
|
104
|
+
@pf.tracer.primitive(allow_exec=True, mutates=["rng"])
|
|
105
105
|
def log_uniform(
|
|
106
106
|
rng: np.random.Generator,
|
|
107
107
|
low: float,
|
|
@@ -111,7 +111,7 @@ def log_uniform(
|
|
|
111
111
|
return np.exp(rng.uniform(np.log(low), np.log(high), size=size))
|
|
112
112
|
|
|
113
113
|
|
|
114
|
-
@pf.tracer.primitive(allow_exec=True)
|
|
114
|
+
@pf.tracer.primitive(allow_exec=True, mutates=["rng"])
|
|
115
115
|
def log_normal(
|
|
116
116
|
rng: np.random.Generator,
|
|
117
117
|
mean: float,
|
|
@@ -121,7 +121,7 @@ def log_normal(
|
|
|
121
121
|
return np.exp(rng.normal(np.log(mean), std, size=size))
|
|
122
122
|
|
|
123
123
|
|
|
124
|
-
@pf.tracer.primitive(allow_exec=True)
|
|
124
|
+
@pf.tracer.primitive(allow_exec=True, mutates=["rng"])
|
|
125
125
|
def spherical_sample(
|
|
126
126
|
rng: np.random.Generator,
|
|
127
127
|
min_elevation: float | None = None,
|
|
@@ -142,7 +142,7 @@ def spherical_sample(
|
|
|
142
142
|
)
|
|
143
143
|
|
|
144
144
|
|
|
145
|
-
@pf.tracer.primitive(allow_exec=True)
|
|
145
|
+
@pf.tracer.primitive(allow_exec=True, mutates=["rng"])
|
|
146
146
|
def mixture_of_gaussian(
|
|
147
147
|
rng: np.random.Generator,
|
|
148
148
|
means: np.ndarray,
|
|
@@ -167,7 +167,7 @@ def mixture_of_gaussian(
|
|
|
167
167
|
return res
|
|
168
168
|
|
|
169
169
|
|
|
170
|
-
@pf.tracer.primitive(allow_exec=True)
|
|
170
|
+
@pf.tracer.primitive(allow_exec=True, mutates=["rng"])
|
|
171
171
|
def beta(
|
|
172
172
|
rng: np.random.Generator,
|
|
173
173
|
a: float,
|
|
@@ -176,7 +176,7 @@ def beta(
|
|
|
176
176
|
return rng.beta(a, b)
|
|
177
177
|
|
|
178
178
|
|
|
179
|
-
@pf.tracer.primitive(allow_exec=True)
|
|
179
|
+
@pf.tracer.primitive(allow_exec=True, mutates=["rng"])
|
|
180
180
|
def poisson(
|
|
181
181
|
rng: np.random.Generator,
|
|
182
182
|
lam: float,
|
|
@@ -184,7 +184,7 @@ def poisson(
|
|
|
184
184
|
return rng.poisson(lam)
|
|
185
185
|
|
|
186
186
|
|
|
187
|
-
@pf.tracer.primitive(allow_exec=True)
|
|
187
|
+
@pf.tracer.primitive(allow_exec=True, mutates=["rng"])
|
|
188
188
|
def triangular(
|
|
189
189
|
rng: np.random.Generator,
|
|
190
190
|
low: float,
|
|
@@ -194,7 +194,7 @@ def triangular(
|
|
|
194
194
|
return rng.triangular(low, mode, high)
|
|
195
195
|
|
|
196
196
|
|
|
197
|
-
@pf.tracer.primitive(allow_exec=True)
|
|
197
|
+
@pf.tracer.primitive(allow_exec=True, mutates=["rng"])
|
|
198
198
|
def gamma(
|
|
199
199
|
rng: np.random.Generator,
|
|
200
200
|
shape: float,
|
|
@@ -203,7 +203,7 @@ def gamma(
|
|
|
203
203
|
return rng.gamma(shape, scale)
|
|
204
204
|
|
|
205
205
|
|
|
206
|
-
@pf.tracer.primitive(allow_exec=True)
|
|
206
|
+
@pf.tracer.primitive(allow_exec=True, mutates=["rng"])
|
|
207
207
|
def binomial(
|
|
208
208
|
rng: np.random.Generator,
|
|
209
209
|
n: int,
|
|
@@ -212,7 +212,7 @@ def binomial(
|
|
|
212
212
|
return rng.binomial(n, p)
|
|
213
213
|
|
|
214
214
|
|
|
215
|
-
@pf.tracer.primitive(allow_exec=True)
|
|
215
|
+
@pf.tracer.primitive(allow_exec=True, mutates=["rng"])
|
|
216
216
|
def geometric(
|
|
217
217
|
rng: np.random.Generator,
|
|
218
218
|
p: float,
|
|
@@ -181,6 +181,11 @@ def trace(
|
|
|
181
181
|
name = func.__name__
|
|
182
182
|
|
|
183
183
|
proxy_args = _map_args(func, **inputs)
|
|
184
|
+
input_nodes = {
|
|
185
|
+
k: v.node
|
|
186
|
+
for k, v in proxy_args.items()
|
|
187
|
+
if isinstance(v, cg.Proxy) and isinstance(v.node, cg.InputPlaceholderNode)
|
|
188
|
+
}
|
|
184
189
|
|
|
185
190
|
if pf.context.globals.current_trace_level is not None:
|
|
186
191
|
# TODO we can lift this restriction fairly(?) easily by having a global patcher & saving/restoring this state
|
|
@@ -222,12 +227,6 @@ def trace(
|
|
|
222
227
|
outputs = pytree.PyTree(func_result)
|
|
223
228
|
outputs = outputs.map(extract_node)
|
|
224
229
|
|
|
225
|
-
input_nodes = {
|
|
226
|
-
k: v.node
|
|
227
|
-
for k, v in proxy_args.items()
|
|
228
|
-
if isinstance(v, cg.Proxy) and isinstance(v.node, cg.InputPlaceholderNode)
|
|
229
|
-
}
|
|
230
|
-
|
|
231
230
|
compgraph = cg.ComputeGraph(
|
|
232
231
|
inputs=pytree.PyTree(input_nodes),
|
|
233
232
|
outputs=outputs,
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
import procfunc as pf
|
|
8
|
+
from procfunc.codegen import codegen
|
|
9
|
+
|
|
10
|
+
RANDOM_CALLS = [
|
|
11
|
+
("uniform", (0.0, 1.0)),
|
|
12
|
+
("normal", (0.0, 1.0)),
|
|
13
|
+
("randint", (0, 10)),
|
|
14
|
+
("uniform_tails", (0.0, 1.0)),
|
|
15
|
+
("clip_gaussian", (0.0, 1.0)),
|
|
16
|
+
("wrap_gaussian", (0.0, 1.0, -1.0, 1.0)),
|
|
17
|
+
("exponential", (1.0,)),
|
|
18
|
+
("log_uniform", (0.4, 2.0)),
|
|
19
|
+
("log_normal", (1.0, 0.5)),
|
|
20
|
+
("spherical_sample", ()),
|
|
21
|
+
("mixture_of_gaussian", ([0.0, 5.0], [1.0, 1.0], [1.0, 1.0])),
|
|
22
|
+
("beta", (2.0, 5.0)),
|
|
23
|
+
("poisson", (3.0,)),
|
|
24
|
+
("triangular", (0.0, 1.0, 0.5)),
|
|
25
|
+
("gamma", (2.0, 1.0)),
|
|
26
|
+
("binomial", (10, 0.5)),
|
|
27
|
+
("geometric", (0.3,)),
|
|
28
|
+
]
|
|
29
|
+
TRACE_LEVELS = [
|
|
30
|
+
pf.tracer.TraceLevel.RANDOM_PARAMS,
|
|
31
|
+
pf.tracer.TraceLevel.RANDOM_CONTROL,
|
|
32
|
+
pf.tracer.TraceLevel.PRIMITIVES,
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def generated_function(
|
|
37
|
+
generator: Callable, level: pf.tracer.TraceLevel, concrete: bool
|
|
38
|
+
) -> Callable:
|
|
39
|
+
inputs = {"rng": np.random.default_rng(17)} if concrete else {}
|
|
40
|
+
graph = pf.trace(generator, trace_level=level, **inputs)
|
|
41
|
+
source = codegen.to_python(graph, toplevel_as_maincall=False)
|
|
42
|
+
source = source.replace("np.random.default_rng()", "np.random.default_rng(17)")
|
|
43
|
+
namespace = {}
|
|
44
|
+
exec(source, namespace) # noqa: S102
|
|
45
|
+
return namespace[generator.__name__]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_unused_mutator_return_emits_bare_call() -> None:
|
|
49
|
+
def generator(rng):
|
|
50
|
+
pf.random.uniform(rng, 0.0, 1.0)
|
|
51
|
+
return pf.random.uniform(rng, 0.0, 1.0)
|
|
52
|
+
|
|
53
|
+
graph = pf.trace(generator, trace_level=pf.tracer.TraceLevel.RANDOM_PARAMS)
|
|
54
|
+
tree = ast.parse(codegen.to_python(graph, toplevel_as_maincall=False))
|
|
55
|
+
function = next(node for node in tree.body if isinstance(node, ast.FunctionDef))
|
|
56
|
+
assert isinstance(function.body[0], ast.Expr)
|
|
57
|
+
assert isinstance(function.body[1], ast.Assign)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@pytest.mark.parametrize("name,args", RANDOM_CALLS, ids=[x[0] for x in RANDOM_CALLS])
|
|
61
|
+
@pytest.mark.parametrize("level", TRACE_LEVELS, ids=lambda level: level.name)
|
|
62
|
+
@pytest.mark.parametrize("concrete", [False, True], ids=["input", "seeded"])
|
|
63
|
+
def test_discarded_random_draw_advances_generated_rng(
|
|
64
|
+
name: str, args: tuple, level: pf.tracer.TraceLevel, concrete: bool
|
|
65
|
+
) -> None:
|
|
66
|
+
def generator(rng):
|
|
67
|
+
getattr(pf.random, name)(rng, *args)
|
|
68
|
+
return pf.random.uniform(rng, 0.0, 1.0)
|
|
69
|
+
|
|
70
|
+
generated = generated_function(generator, level, concrete)
|
|
71
|
+
expected = generator(np.random.default_rng(17))
|
|
72
|
+
actual = generated() if concrete else generated(np.random.default_rng(17))
|
|
73
|
+
tolerance = 1e-7 if concrete and level < pf.tracer.TraceLevel.RANDOM_PARAMS else 0
|
|
74
|
+
assert actual == pytest.approx(expected, rel=tolerance, abs=0)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@pytest.mark.parametrize("level", TRACE_LEVELS, ids=lambda level: level.name)
|
|
78
|
+
@pytest.mark.parametrize("concrete", [False, True], ids=["input", "seeded"])
|
|
79
|
+
def test_reversed_outputs_preserve_aliased_rng_order(
|
|
80
|
+
level: pf.tracer.TraceLevel, concrete: bool
|
|
81
|
+
) -> None:
|
|
82
|
+
def generator(rng):
|
|
83
|
+
other = rng
|
|
84
|
+
width = pf.random.uniform(rng, 1.0, 2.0)
|
|
85
|
+
height = pf.random.uniform(other, 3.0, 4.0)
|
|
86
|
+
return {"height": height, "width": width}
|
|
87
|
+
|
|
88
|
+
generated = generated_function(generator, level, concrete)
|
|
89
|
+
expected = generator(np.random.default_rng(17))
|
|
90
|
+
actual = generated() if concrete else generated(np.random.default_rng(17))
|
|
91
|
+
tolerance = 1e-7 if concrete and level < pf.tracer.TraceLevel.RANDOM_PARAMS else 0
|
|
92
|
+
assert actual == pytest.approx(expected, rel=tolerance, abs=0)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|