computation-graph 67__tar.gz → 68__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.
Files changed (39) hide show
  1. {computation_graph-67 → computation_graph-68}/PKG-INFO +1 -1
  2. computation_graph-68/computation_graph/composers/duplication.py +154 -0
  3. {computation_graph-67 → computation_graph-68}/computation_graph/graph.py +40 -1
  4. {computation_graph-67 → computation_graph-68}/computation_graph/graph_test.py +27 -0
  5. {computation_graph-67 → computation_graph-68}/computation_graph.egg-info/PKG-INFO +1 -1
  6. {computation_graph-67 → computation_graph-68}/setup.py +1 -1
  7. computation_graph-67/computation_graph/composers/duplication.py +0 -124
  8. {computation_graph-67 → computation_graph-68}/LICENSE.md +0 -0
  9. {computation_graph-67 → computation_graph-68}/README.md +0 -0
  10. {computation_graph-67 → computation_graph-68}/computation_graph/__init__.py +0 -0
  11. {computation_graph-67 → computation_graph-68}/computation_graph/base_types.py +0 -0
  12. {computation_graph-67 → computation_graph-68}/computation_graph/composers/__init__.py +0 -0
  13. {computation_graph-67 → computation_graph-68}/computation_graph/composers/composers_test.py +0 -0
  14. {computation_graph-67 → computation_graph-68}/computation_graph/composers/condition.py +0 -0
  15. {computation_graph-67 → computation_graph-68}/computation_graph/composers/condition_test.py +0 -0
  16. {computation_graph-67 → computation_graph-68}/computation_graph/composers/debug.py +0 -0
  17. {computation_graph-67 → computation_graph-68}/computation_graph/composers/lift.py +0 -0
  18. {computation_graph-67 → computation_graph-68}/computation_graph/composers/lift_test.py +0 -0
  19. {computation_graph-67 → computation_graph-68}/computation_graph/composers/logic.py +0 -0
  20. {computation_graph-67 → computation_graph-68}/computation_graph/composers/memory.py +0 -0
  21. {computation_graph-67 → computation_graph-68}/computation_graph/composers/memory_test.py +0 -0
  22. {computation_graph-67 → computation_graph-68}/computation_graph/graph_runners.py +0 -0
  23. {computation_graph-67 → computation_graph-68}/computation_graph/infer_sink.py +0 -0
  24. {computation_graph-67 → computation_graph-68}/computation_graph/infer_sink_test.py +0 -0
  25. {computation_graph-67 → computation_graph-68}/computation_graph/legacy.py +0 -0
  26. {computation_graph-67 → computation_graph-68}/computation_graph/run.py +0 -0
  27. {computation_graph-67 → computation_graph-68}/computation_graph/signature.py +0 -0
  28. {computation_graph-67 → computation_graph-68}/computation_graph/trace/__init__.py +0 -0
  29. {computation_graph-67 → computation_graph-68}/computation_graph/trace/ascii.py +0 -0
  30. {computation_graph-67 → computation_graph-68}/computation_graph/trace/graphviz.py +0 -0
  31. {computation_graph-67 → computation_graph-68}/computation_graph/trace/graphviz_test.py +0 -0
  32. {computation_graph-67 → computation_graph-68}/computation_graph/trace/mermaid.py +0 -0
  33. {computation_graph-67 → computation_graph-68}/computation_graph/trace/trace_utils.py +0 -0
  34. {computation_graph-67 → computation_graph-68}/computation_graph.egg-info/SOURCES.txt +0 -0
  35. {computation_graph-67 → computation_graph-68}/computation_graph.egg-info/dependency_links.txt +0 -0
  36. {computation_graph-67 → computation_graph-68}/computation_graph.egg-info/requires.txt +0 -0
  37. {computation_graph-67 → computation_graph-68}/computation_graph.egg-info/top_level.txt +0 -0
  38. {computation_graph-67 → computation_graph-68}/pyproject.toml +0 -0
  39. {computation_graph-67 → computation_graph-68}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: computation-graph
3
- Version: 67
3
+ Version: 68
4
4
  Requires-Python: >=3
5
5
  Description-Content-Type: text/markdown
6
6
  License-File: LICENSE.md
@@ -0,0 +1,154 @@
1
+ import asyncio
2
+ import dataclasses
3
+ import functools
4
+ from typing import Dict
5
+
6
+ import gamla
7
+
8
+ from computation_graph import base_types, graph
9
+ from computation_graph.composers import debug
10
+
11
+
12
+ def duplicate_function(func):
13
+ if asyncio.iscoroutinefunction(func):
14
+
15
+ @functools.wraps(func)
16
+ async def inner(*args, **kwargs):
17
+ return await func(*args, **kwargs)
18
+
19
+ else:
20
+
21
+ @functools.wraps(func)
22
+ def inner(*args, **kwargs):
23
+ return func(*args, **kwargs)
24
+
25
+ return debug.name_callable(inner, f"duplicate of {func.__name__}")
26
+
27
+
28
+ def _duplicate_computation_edge(get_duplicated_node):
29
+ return gamla.compose_left(
30
+ gamla.dataclass_transform("source", get_duplicated_node),
31
+ gamla.dataclass_transform("destination", get_duplicated_node),
32
+ gamla.dataclass_transform(
33
+ "args", gamla.compose_left(gamla.map(get_duplicated_node), tuple)
34
+ ),
35
+ )
36
+
37
+
38
+ def _signature_is_empty(signature: base_types.NodeSignature) -> bool:
39
+ return not (
40
+ signature.kwargs
41
+ or signature.optional_kwargs
42
+ or signature.is_args
43
+ or signature.is_kwargs
44
+ )
45
+
46
+
47
+ def _duplicate_node(node: base_types.ComputationNode) -> base_types.ComputationNode:
48
+ if _signature_is_empty(node.signature):
49
+ return node
50
+ inner = duplicate_function(node.func)
51
+ return dataclasses.replace(node, name=inner.__name__, func=inner)
52
+
53
+
54
+ def _node_to_duplicated_node(nodes):
55
+ return {node: _duplicate_node(node) for node in nodes if not node.is_terminal}.get
56
+
57
+
58
+ def duplicate_graph(original: base_types.GraphType) -> base_types.GraphType:
59
+
60
+ _node_to_duplicated_node_index = gamla.pipe(
61
+ original.edges, graph.get_all_nodes, _node_to_duplicated_node
62
+ )
63
+ get_duplicated_node = gamla.when(
64
+ _node_to_duplicated_node_index, _node_to_duplicated_node_index
65
+ )
66
+ return base_types.GraphType(
67
+ edges=gamla.pipe(
68
+ original.edges,
69
+ gamla.map(
70
+ _duplicate_computation_edge(
71
+ gamla.when(get_duplicated_node, get_duplicated_node)
72
+ )
73
+ ),
74
+ frozenset,
75
+ ),
76
+ sink=get_duplicated_node(original.sink),
77
+ )
78
+
79
+
80
+ duplicate_function_or_graph = gamla.ternary(
81
+ gamla.is_instance(base_types.GraphType), duplicate_graph, duplicate_function
82
+ )
83
+
84
+
85
+ def safe_replace_sources(
86
+ source_to_replacement_dict: Dict[
87
+ base_types.CallableOrNode, base_types.CallableOrNodeOrGraph
88
+ ],
89
+ cg: base_types.GraphType,
90
+ ) -> base_types.GraphType:
91
+ if not len(source_to_replacement_dict):
92
+ return cg
93
+
94
+ source_to_replacement_dict = gamla.pipe(
95
+ source_to_replacement_dict,
96
+ gamla.keymap(graph.make_computation_node),
97
+ gamla.valmap(
98
+ gamla.unless(
99
+ gamla.is_instance(base_types.GraphType), graph.make_computation_node
100
+ )
101
+ ),
102
+ )
103
+ get_duplicate = gamla.pipe(
104
+ gamla.graph_traverse_many(
105
+ source_to_replacement_dict.keys(), graph.traverse_forward(cg.edges)
106
+ ),
107
+ gamla.remove(gamla.contains(source_to_replacement_dict.keys())),
108
+ _node_to_duplicated_node,
109
+ )
110
+
111
+ def node_replacement(node):
112
+ replacement = source_to_replacement_dict.get(node)
113
+ if replacement is not None:
114
+ return replacement
115
+ duplicate = get_duplicate(node)
116
+ return duplicate if duplicate is not None else node
117
+
118
+ replacement_graphs_edges = {}
119
+
120
+ def source_replacement(node):
121
+ replacement = node_replacement(node)
122
+ if isinstance(replacement, base_types.GraphType):
123
+ replacement_graphs_edges[id(replacement)] = replacement.edges
124
+ return replacement.sink
125
+ return replacement
126
+
127
+ new_edges = []
128
+ for edge in cg.edges:
129
+ destination = node_replacement(edge.destination)
130
+ if edge.args:
131
+ args = tuple(source_replacement(arg) for arg in edge.args)
132
+ if destination is edge.destination and all(
133
+ new is old for new, old in zip(args, edge.args)
134
+ ):
135
+ new_edges.append(edge)
136
+ else:
137
+ new_edges.append(
138
+ dataclasses.replace(edge, args=args, destination=destination)
139
+ )
140
+ else:
141
+ source = source_replacement(edge.source)
142
+ if source is edge.source and destination is edge.destination:
143
+ new_edges.append(edge)
144
+ else:
145
+ new_edges.append(
146
+ dataclasses.replace(edge, source=source, destination=destination)
147
+ )
148
+
149
+ return base_types.GraphType(
150
+ edges=base_types.merge_edges(
151
+ frozenset(new_edges), *replacement_graphs_edges.values()
152
+ ),
153
+ sink=node_replacement(cg.sink),
154
+ )
@@ -1,6 +1,6 @@
1
1
  import dataclasses
2
2
  import functools
3
- from typing import Callable, FrozenSet, Optional, Tuple
3
+ from typing import Callable, Dict, FrozenSet, Optional, Tuple
4
4
 
5
5
  import gamla
6
6
  from gamla.optimized import sync as opt_gamla
@@ -231,6 +231,45 @@ def replace_node(
231
231
  )
232
232
 
233
233
 
234
+ def replace_nodes(
235
+ node_map: Dict[base_types.ComputationNode, base_types.ComputationNode]
236
+ ) -> Callable[[base_types.GraphType], base_types.GraphType]:
237
+ """Replace many nodes at once (sources, destinations and sink) in a single pass."""
238
+
239
+ def replace_nodes(g: base_types.GraphType) -> base_types.GraphType:
240
+ if not node_map:
241
+ return g
242
+ new_edges = []
243
+ for edge in g.edges:
244
+ destination = node_map.get(edge.destination, edge.destination)
245
+ if edge.args:
246
+ args = tuple(node_map.get(arg, arg) for arg in edge.args)
247
+ if destination is edge.destination and all(
248
+ new is old for new, old in zip(args, edge.args)
249
+ ):
250
+ new_edges.append(edge)
251
+ else:
252
+ new_edges.append(
253
+ dataclasses.replace(edge, args=args, destination=destination)
254
+ )
255
+ else:
256
+ assert edge.source is not None
257
+ source = node_map.get(edge.source, edge.source)
258
+ if source is edge.source and destination is edge.destination:
259
+ new_edges.append(edge)
260
+ else:
261
+ new_edges.append(
262
+ dataclasses.replace(
263
+ edge, source=source, destination=destination
264
+ )
265
+ )
266
+ return base_types.GraphType(
267
+ edges=frozenset(new_edges), sink=node_map.get(g.sink, g.sink)
268
+ )
269
+
270
+ return replace_nodes
271
+
272
+
234
273
  def transform_edges(
235
274
  query: Callable[[base_types.ComputationEdge], bool],
236
275
  edge_mapper: Callable[[base_types.ComputationEdge], base_types.ComputationEdge],
@@ -1000,6 +1000,33 @@ def test_replace_node():
1000
1000
  )
1001
1001
 
1002
1002
 
1003
+ @pytest.mark.parametrize(
1004
+ "node_map",
1005
+ [
1006
+ pytest.param({}, id="empty map is a no-op"),
1007
+ pytest.param({_node1: _node1_async}, id="single node"),
1008
+ pytest.param({_node1: _node1_async, _node2: _next_int}, id="multiple nodes"),
1009
+ pytest.param({(lambda x: x): _node1_async}, id="node not in graph is a no-op"),
1010
+ ],
1011
+ )
1012
+ def test_replace_nodes(node_map: Dict):
1013
+ a = graph.make_source()
1014
+ g = graph.merge_graphs(
1015
+ composers.compose_source_unary(_node1, a),
1016
+ composers.compose_left_unary(_node1, _node2),
1017
+ sink_node_or_graph=graph.make_computation_node(_node2),
1018
+ )
1019
+ node_map = gamla.pipe(
1020
+ node_map,
1021
+ gamla.keymap(graph.make_computation_node),
1022
+ gamla.valmap(graph.make_computation_node),
1023
+ )
1024
+ expected = g
1025
+ for original, replacement in node_map.items():
1026
+ expected = graph.replace_node(original, replacement)(expected)
1027
+ assert graph.replace_nodes(node_map)(g) == expected
1028
+
1029
+
1003
1030
  def test_ambig_edges_assertion_in_merge_graphs_active_only_when_env_var_is_active(
1004
1031
  monkeypatch,
1005
1032
  ):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: computation-graph
3
- Version: 67
3
+ Version: 68
4
4
  Requires-Python: >=3
5
5
  Description-Content-Type: text/markdown
6
6
  License-File: LICENSE.md
@@ -7,7 +7,7 @@ with open("README.md", "r") as fh:
7
7
  setuptools.setup(
8
8
  name="computation-graph",
9
9
  python_requires=">=3",
10
- version="67",
10
+ version="68",
11
11
  long_description=_LONG_DESCRIPTION,
12
12
  long_description_content_type="text/markdown",
13
13
  packages=setuptools.find_namespace_packages(),
@@ -1,124 +0,0 @@
1
- import asyncio
2
- import functools
3
- import inspect
4
- from typing import Dict
5
-
6
- import gamla
7
-
8
- from computation_graph import base_types, graph
9
- from computation_graph.composers import debug
10
-
11
-
12
- def duplicate_function(func):
13
- if asyncio.iscoroutinefunction(func):
14
-
15
- @functools.wraps(func)
16
- async def inner(*args, **kwargs):
17
- return await func(*args, **kwargs)
18
-
19
- else:
20
-
21
- @functools.wraps(func)
22
- def inner(*args, **kwargs):
23
- return func(*args, **kwargs)
24
-
25
- return debug.name_callable(inner, f"duplicate of {func.__name__}")
26
-
27
-
28
- def _duplicate_computation_edge(get_duplicated_node):
29
- return gamla.compose_left(
30
- gamla.dataclass_transform("source", get_duplicated_node),
31
- gamla.dataclass_transform("destination", get_duplicated_node),
32
- gamla.dataclass_transform(
33
- "args", gamla.compose_left(gamla.map(get_duplicated_node), tuple)
34
- ),
35
- )
36
-
37
-
38
- _duplicate_node = gamla.compose_left(
39
- base_types.node_implementation,
40
- gamla.when(
41
- gamla.compose_left(inspect.signature, gamla.attrgetter("parameters"), len),
42
- duplicate_function,
43
- ),
44
- graph.make_computation_node,
45
- )
46
-
47
- duplicate_node = _duplicate_node
48
-
49
- _node_to_duplicated_node = gamla.compose_left(
50
- gamla.remove(base_types.node_is_terminal),
51
- gamla.map(gamla.pair_right(_duplicate_node)),
52
- dict,
53
- gamla.dict_to_getter_with_default(None),
54
- )
55
-
56
-
57
- def duplicate_graph(original: base_types.GraphType) -> base_types.GraphType:
58
-
59
- _node_to_duplicated_node_index = gamla.pipe(
60
- original.edges, graph.get_all_nodes, _node_to_duplicated_node
61
- )
62
- get_duplicated_node = gamla.when(
63
- _node_to_duplicated_node_index, _node_to_duplicated_node_index
64
- )
65
- return base_types.GraphType(
66
- edges=gamla.pipe(
67
- original.edges,
68
- gamla.map(
69
- _duplicate_computation_edge(
70
- gamla.when(get_duplicated_node, get_duplicated_node)
71
- )
72
- ),
73
- frozenset,
74
- ),
75
- sink=get_duplicated_node(original.sink),
76
- )
77
-
78
-
79
- duplicate_function_or_graph = gamla.ternary(
80
- gamla.is_instance(base_types.GraphType), duplicate_graph, duplicate_function
81
- )
82
-
83
-
84
- def safe_replace_sources(
85
- source_to_replacement_dict: Dict[
86
- base_types.CallableOrNode, base_types.CallableOrNodeOrGraph
87
- ],
88
- cg: base_types.GraphType,
89
- ) -> base_types.GraphType:
90
- if not len(source_to_replacement_dict):
91
- return cg
92
-
93
- source_to_replacement_dict = gamla.keymap(graph.make_computation_node)(
94
- source_to_replacement_dict
95
- )
96
- reachable_to_duplicate_map = gamla.pipe(
97
- gamla.graph_traverse_many(
98
- source_to_replacement_dict.keys(), graph.traverse_forward(cg.edges)
99
- ),
100
- gamla.remove(gamla.contains(source_to_replacement_dict.keys())),
101
- _node_to_duplicated_node,
102
- )
103
- get_node_replacement = gamla.compose_left(
104
- gamla.lazyjuxt(
105
- gamla.dict_to_getter_with_default(None)(source_to_replacement_dict),
106
- reachable_to_duplicate_map,
107
- gamla.identity,
108
- ),
109
- gamla.find(gamla.identity),
110
- )
111
-
112
- def update_edge(e: base_types.ComputationEdge) -> base_types.GraphType:
113
- dest = base_types.edge_destination(e)
114
- g = graph.replace_node(dest, get_node_replacement(dest))(
115
- base_types.GraphType(edges=frozenset((e,)), sink=dest)
116
- )
117
- for source in base_types.edge_sources(e):
118
- g = graph.replace_source(source, get_node_replacement(source), g)
119
- return g
120
-
121
- return base_types.GraphType(
122
- edges=frozenset(edge for e in cg.edges for edge in update_edge(e).edges),
123
- sink=get_node_replacement(cg.sink),
124
- )
File without changes
File without changes