implica 0.3.0__tar.gz → 0.3.2__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.
Potentially problematic release.
This version of implica might be problematic. Click here for more details.
- {implica-0.3.0 → implica-0.3.2}/PKG-INFO +1 -1
- {implica-0.3.0 → implica-0.3.2}/pyproject.toml +1 -1
- {implica-0.3.0 → implica-0.3.2}/src/implica/__init__.py +9 -9
- {implica-0.3.0 → implica-0.3.2}/src/implica/graph/connection.py +78 -0
- {implica-0.3.0 → implica-0.3.2}/src/implica/mutations/__init__.py +8 -0
- implica-0.3.2/src/implica/mutations/try_add_many_edges.py +63 -0
- implica-0.3.2/src/implica/mutations/try_add_many_nodes.py +53 -0
- implica-0.3.2/src/implica/mutations/try_remove_many_edges.py +55 -0
- implica-0.3.2/src/implica/mutations/try_remove_many_nodes.py +58 -0
- {implica-0.3.0 → implica-0.3.2}/LICENSE +0 -0
- {implica-0.3.0 → implica-0.3.2}/README.md +0 -0
- {implica-0.3.0 → implica-0.3.2}/src/implica/core/__init__.py +0 -0
- {implica-0.3.0 → implica-0.3.2}/src/implica/core/combinator.py +0 -0
- {implica-0.3.0 → implica-0.3.2}/src/implica/core/types.py +0 -0
- {implica-0.3.0 → implica-0.3.2}/src/implica/graph/__init__.py +0 -0
- {implica-0.3.0 → implica-0.3.2}/src/implica/graph/elements.py +0 -0
- {implica-0.3.0 → implica-0.3.2}/src/implica/graph/graph.py +0 -0
- {implica-0.3.0 → implica-0.3.2}/src/implica/mutations/add_edge.py +0 -0
- {implica-0.3.0 → implica-0.3.2}/src/implica/mutations/add_many_edges.py +0 -0
- {implica-0.3.0 → implica-0.3.2}/src/implica/mutations/add_many_nodes.py +0 -0
- {implica-0.3.0 → implica-0.3.2}/src/implica/mutations/add_node.py +0 -0
- {implica-0.3.0 → implica-0.3.2}/src/implica/mutations/base.py +0 -0
- {implica-0.3.0 → implica-0.3.2}/src/implica/mutations/remove_edge.py +0 -0
- {implica-0.3.0 → implica-0.3.2}/src/implica/mutations/remove_many_edges.py +0 -0
- {implica-0.3.0 → implica-0.3.2}/src/implica/mutations/remove_many_nodes.py +0 -0
- {implica-0.3.0 → implica-0.3.2}/src/implica/mutations/remove_node.py +0 -0
- {implica-0.3.0 → implica-0.3.2}/src/implica/mutations/try_add_edge.py +0 -0
- {implica-0.3.0 → implica-0.3.2}/src/implica/mutations/try_add_node.py +0 -0
- {implica-0.3.0 → implica-0.3.2}/src/implica/mutations/try_remove_edge.py +0 -0
- {implica-0.3.0 → implica-0.3.2}/src/implica/mutations/try_remove_node.py +0 -0
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Implica - Implicational Logic Graph Library
|
|
3
3
|
|
|
4
4
|
A Python package for working with graphs representing minimal implicational
|
|
5
5
|
logic models. This library provides tools for constructing and manipulating
|
|
6
6
|
type systems based on combinatory logic, with support for transactional
|
|
7
7
|
graph operations.
|
|
8
8
|
|
|
9
|
-
Import as '
|
|
10
|
-
import
|
|
9
|
+
Import as 'imp' for a clean, concise API:
|
|
10
|
+
import implica as imp
|
|
11
11
|
|
|
12
|
-
graph =
|
|
13
|
-
A =
|
|
14
|
-
B =
|
|
12
|
+
graph = imp.Graph()
|
|
13
|
+
A = imp.var("A")
|
|
14
|
+
B = imp.var("B")
|
|
15
15
|
|
|
16
16
|
with graph.connect() as conn:
|
|
17
|
-
conn.add_node(
|
|
18
|
-
conn.add_node(
|
|
17
|
+
conn.add_node(imp.node(A))
|
|
18
|
+
conn.add_node(imp.node(B))
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
21
|
from .core import *
|
|
@@ -41,4 +41,4 @@ __all__ = [
|
|
|
41
41
|
"mutations",
|
|
42
42
|
]
|
|
43
43
|
|
|
44
|
-
__version__ = "0.3.
|
|
44
|
+
__version__ = "0.3.2"
|
|
@@ -23,6 +23,10 @@ from ..mutations import (
|
|
|
23
23
|
RemoveManyEdges,
|
|
24
24
|
TryRemoveNode,
|
|
25
25
|
TryRemoveEdge,
|
|
26
|
+
TryAddManyNodes,
|
|
27
|
+
TryAddManyEdges,
|
|
28
|
+
TryRemoveManyNodes,
|
|
29
|
+
TryRemoveManyEdges,
|
|
26
30
|
)
|
|
27
31
|
|
|
28
32
|
if TYPE_CHECKING:
|
|
@@ -225,6 +229,52 @@ class Connection:
|
|
|
225
229
|
self.mutations.append(TryAddEdge(e))
|
|
226
230
|
return self
|
|
227
231
|
|
|
232
|
+
def try_add_many_nodes(self, nodes: list[Node]) -> "Connection":
|
|
233
|
+
"""
|
|
234
|
+
Add a mutation to create multiple nodes, or do nothing for nodes that already exist.
|
|
235
|
+
|
|
236
|
+
Args:
|
|
237
|
+
nodes: List of nodes to add
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
Connection: Self for method chaining
|
|
241
|
+
"""
|
|
242
|
+
self._check_state()
|
|
243
|
+
self.mutations.append(TryAddManyNodes(nodes))
|
|
244
|
+
return self
|
|
245
|
+
|
|
246
|
+
def try_add_many_edges(self, combinators: list[Combinator]) -> "Connection":
|
|
247
|
+
"""
|
|
248
|
+
Add a mutation to create multiple edges from combinators, or do nothing for edges that already exist.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
combinators: List of combinators for the edges
|
|
252
|
+
|
|
253
|
+
Returns:
|
|
254
|
+
Connection: Self for method chaining
|
|
255
|
+
|
|
256
|
+
Raises:
|
|
257
|
+
ValueError: If any combinator doesn't have an Application type
|
|
258
|
+
"""
|
|
259
|
+
self._check_state()
|
|
260
|
+
|
|
261
|
+
# Import here to avoid circular import
|
|
262
|
+
from .elements import edge
|
|
263
|
+
from ..core.types import Application
|
|
264
|
+
|
|
265
|
+
# Validate all combinators and create edges
|
|
266
|
+
edges = []
|
|
267
|
+
for combinator in combinators:
|
|
268
|
+
if not isinstance(combinator.type, Application):
|
|
269
|
+
raise ValueError(
|
|
270
|
+
f"Combinator must have an Application type to create an edge, "
|
|
271
|
+
f"got {type(combinator.type).__name__}"
|
|
272
|
+
)
|
|
273
|
+
edges.append(edge(combinator))
|
|
274
|
+
|
|
275
|
+
self.mutations.append(TryAddManyEdges(edges))
|
|
276
|
+
return self
|
|
277
|
+
|
|
228
278
|
def remove_many_nodes(self, node_uids: list[str]) -> "Connection":
|
|
229
279
|
"""
|
|
230
280
|
Add a mutation to remove multiple nodes.
|
|
@@ -281,6 +331,34 @@ class Connection:
|
|
|
281
331
|
self.mutations.append(TryRemoveEdge(edge_uid))
|
|
282
332
|
return self
|
|
283
333
|
|
|
334
|
+
def try_remove_many_nodes(self, node_uids: list[str]) -> "Connection":
|
|
335
|
+
"""
|
|
336
|
+
Add a mutation to remove multiple nodes, or do nothing for nodes that don't exist.
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
node_uids: List of node uids to remove
|
|
340
|
+
|
|
341
|
+
Returns:
|
|
342
|
+
Connection: Self for method chaining
|
|
343
|
+
"""
|
|
344
|
+
self._check_state()
|
|
345
|
+
self.mutations.append(TryRemoveManyNodes(node_uids))
|
|
346
|
+
return self
|
|
347
|
+
|
|
348
|
+
def try_remove_many_edges(self, edge_uids: list[str]) -> "Connection":
|
|
349
|
+
"""
|
|
350
|
+
Add a mutation to remove multiple edges, or do nothing for edges that don't exist.
|
|
351
|
+
|
|
352
|
+
Args:
|
|
353
|
+
edge_uids: List of edge uids to remove
|
|
354
|
+
|
|
355
|
+
Returns:
|
|
356
|
+
Connection: Self for method chaining
|
|
357
|
+
"""
|
|
358
|
+
self._check_state()
|
|
359
|
+
self.mutations.append(TryRemoveManyEdges(edge_uids))
|
|
360
|
+
return self
|
|
361
|
+
|
|
284
362
|
def commit(self) -> None:
|
|
285
363
|
"""
|
|
286
364
|
Commit all mutations to the graph.
|
|
@@ -11,6 +11,10 @@ from .add_many_nodes import AddManyNodes
|
|
|
11
11
|
from .add_many_edges import AddManyEdges
|
|
12
12
|
from .remove_many_nodes import RemoveManyNodes
|
|
13
13
|
from .remove_many_edges import RemoveManyEdges
|
|
14
|
+
from .try_add_many_nodes import TryAddManyNodes
|
|
15
|
+
from .try_add_many_edges import TryAddManyEdges
|
|
16
|
+
from .try_remove_many_nodes import TryRemoveManyNodes
|
|
17
|
+
from .try_remove_many_edges import TryRemoveManyEdges
|
|
14
18
|
|
|
15
19
|
__all__ = [
|
|
16
20
|
"Mutation",
|
|
@@ -26,4 +30,8 @@ __all__ = [
|
|
|
26
30
|
"AddManyEdges",
|
|
27
31
|
"RemoveManyNodes",
|
|
28
32
|
"RemoveManyEdges",
|
|
33
|
+
"TryAddManyNodes",
|
|
34
|
+
"TryAddManyEdges",
|
|
35
|
+
"TryRemoveManyNodes",
|
|
36
|
+
"TryRemoveManyEdges",
|
|
29
37
|
]
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
from ..graph.elements import Edge
|
|
4
|
+
from .base import Mutation
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from ..graph.graph import Graph
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TryAddManyEdges(Mutation):
|
|
11
|
+
"""Mutation to add multiple edges to the graph, skipping edges that already exist."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, edges: list[Edge]):
|
|
14
|
+
"""
|
|
15
|
+
Initialize the TryAddManyEdges mutation.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
edges: List of edges to add
|
|
19
|
+
"""
|
|
20
|
+
self.edges = edges
|
|
21
|
+
self._added_edges: list[Edge] = []
|
|
22
|
+
|
|
23
|
+
def forward(self, graph: "Graph") -> None:
|
|
24
|
+
"""
|
|
25
|
+
Try to add all edges to the graph.
|
|
26
|
+
|
|
27
|
+
Edges that already exist in the graph are skipped without raising an error.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
graph: The graph to modify
|
|
31
|
+
|
|
32
|
+
Raises:
|
|
33
|
+
KeyError: If source or destination nodes don't exist for an edge
|
|
34
|
+
"""
|
|
35
|
+
self._added_edges = []
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
for edge in self.edges:
|
|
39
|
+
if not graph.has_edge(edge.uid):
|
|
40
|
+
graph._add_edge(edge)
|
|
41
|
+
self._added_edges.append(edge)
|
|
42
|
+
except Exception:
|
|
43
|
+
# Rollback: remove all edges that were added
|
|
44
|
+
for edge in self._added_edges:
|
|
45
|
+
graph._remove_edge(edge.uid)
|
|
46
|
+
self._added_edges = []
|
|
47
|
+
raise
|
|
48
|
+
|
|
49
|
+
def backward(self, graph: "Graph") -> None:
|
|
50
|
+
"""
|
|
51
|
+
Remove all edges that were added.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
graph: The graph to modify
|
|
55
|
+
"""
|
|
56
|
+
for edge in reversed(self._added_edges):
|
|
57
|
+
graph._remove_edge(edge.uid)
|
|
58
|
+
|
|
59
|
+
def __str__(self) -> str:
|
|
60
|
+
"""Return a string representation."""
|
|
61
|
+
return (
|
|
62
|
+
f"TryAddManyEdges(count={len(self.edges)}, added={len(self._added_edges)})"
|
|
63
|
+
)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
from ..graph.elements import Node
|
|
4
|
+
from .base import Mutation
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from ..graph.graph import Graph
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TryAddManyNodes(Mutation):
|
|
11
|
+
"""Mutation to add multiple nodes to the graph, skipping nodes that already exist."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, nodes: list[Node]):
|
|
14
|
+
"""
|
|
15
|
+
Initialize the TryAddManyNodes mutation.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
nodes: List of nodes to add
|
|
19
|
+
"""
|
|
20
|
+
self.nodes = nodes
|
|
21
|
+
self._added_nodes: list[Node] = []
|
|
22
|
+
|
|
23
|
+
def forward(self, graph: "Graph") -> None:
|
|
24
|
+
"""
|
|
25
|
+
Try to add all nodes to the graph.
|
|
26
|
+
|
|
27
|
+
Nodes that already exist in the graph are skipped without raising an error.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
graph: The graph to modify
|
|
31
|
+
"""
|
|
32
|
+
self._added_nodes = []
|
|
33
|
+
|
|
34
|
+
for node in self.nodes:
|
|
35
|
+
if not graph.has_node(node.uid):
|
|
36
|
+
graph._add_node(node)
|
|
37
|
+
self._added_nodes.append(node)
|
|
38
|
+
|
|
39
|
+
def backward(self, graph: "Graph") -> None:
|
|
40
|
+
"""
|
|
41
|
+
Remove all nodes that were added.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
graph: The graph to modify
|
|
45
|
+
"""
|
|
46
|
+
for node in reversed(self._added_nodes):
|
|
47
|
+
graph._remove_node(node.uid)
|
|
48
|
+
|
|
49
|
+
def __str__(self) -> str:
|
|
50
|
+
"""Return a string representation."""
|
|
51
|
+
return (
|
|
52
|
+
f"TryAddManyNodes(count={len(self.nodes)}, added={len(self._added_nodes)})"
|
|
53
|
+
)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
from ..graph.elements import Edge
|
|
4
|
+
from .base import Mutation
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from ..graph.graph import Graph
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TryRemoveManyEdges(Mutation):
|
|
11
|
+
"""Mutation to remove multiple edges from the graph, skipping edges that don't exist."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, edge_uids: list[str]):
|
|
14
|
+
"""
|
|
15
|
+
Initialize the TryRemoveManyEdges mutation.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
edge_uids: List of edge UIDs to remove
|
|
19
|
+
"""
|
|
20
|
+
self.edge_uids = edge_uids
|
|
21
|
+
self._removed_edges: list[Edge] = []
|
|
22
|
+
|
|
23
|
+
def forward(self, graph: "Graph") -> None:
|
|
24
|
+
"""
|
|
25
|
+
Try to remove all edges from the graph.
|
|
26
|
+
|
|
27
|
+
Edges that don't exist in the graph are skipped without raising an error.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
graph: The graph to modify
|
|
31
|
+
"""
|
|
32
|
+
self._removed_edges = []
|
|
33
|
+
|
|
34
|
+
for edge_uid in self.edge_uids:
|
|
35
|
+
if graph.has_edge(edge_uid):
|
|
36
|
+
# Store the edge for rollback
|
|
37
|
+
removed_edge = graph.get_edge(edge_uid)
|
|
38
|
+
self._removed_edges.append(removed_edge)
|
|
39
|
+
|
|
40
|
+
# Remove the edge
|
|
41
|
+
graph._remove_edge(edge_uid)
|
|
42
|
+
|
|
43
|
+
def backward(self, graph: "Graph") -> None:
|
|
44
|
+
"""
|
|
45
|
+
Re-add all edges that were removed.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
graph: The graph to modify
|
|
49
|
+
"""
|
|
50
|
+
for edge in reversed(self._removed_edges):
|
|
51
|
+
graph._add_edge(edge)
|
|
52
|
+
|
|
53
|
+
def __str__(self) -> str:
|
|
54
|
+
"""Return a string representation."""
|
|
55
|
+
return f"TryRemoveManyEdges(count={len(self.edge_uids)}, removed={len(self._removed_edges)})"
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
from ..graph import Edge, Node
|
|
4
|
+
from .base import Mutation
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from ..graph import Graph
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TryRemoveManyNodes(Mutation):
|
|
11
|
+
"""Mutation to remove multiple nodes from the graph, skipping nodes that don't exist."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, node_uids: list[str]):
|
|
14
|
+
"""
|
|
15
|
+
Initialize the TryRemoveManyNodes mutation.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
node_uids: List of node UIDs to remove
|
|
19
|
+
"""
|
|
20
|
+
self.node_uids = node_uids
|
|
21
|
+
self._removed_data: list[tuple[Node, list[Edge]]] = []
|
|
22
|
+
|
|
23
|
+
def forward(self, graph: "Graph") -> None:
|
|
24
|
+
"""
|
|
25
|
+
Try to remove all nodes from the graph.
|
|
26
|
+
|
|
27
|
+
Nodes that don't exist in the graph are skipped without raising an error.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
graph: The graph to modify
|
|
31
|
+
"""
|
|
32
|
+
self._removed_data = []
|
|
33
|
+
|
|
34
|
+
for node_uid in self.node_uids:
|
|
35
|
+
if graph.has_node(node_uid):
|
|
36
|
+
# Store the node and its edges for rollback
|
|
37
|
+
removed_node = graph.get_node(node_uid)
|
|
38
|
+
removed_edges = graph.get_edges_for_node(node_uid)
|
|
39
|
+
self._removed_data.append((removed_node, removed_edges))
|
|
40
|
+
|
|
41
|
+
# Remove the node
|
|
42
|
+
graph._remove_node(node_uid)
|
|
43
|
+
|
|
44
|
+
def backward(self, graph: "Graph") -> None:
|
|
45
|
+
"""
|
|
46
|
+
Re-add all nodes and edges that were removed.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
graph: The graph to modify
|
|
50
|
+
"""
|
|
51
|
+
for removed_node, removed_edges in reversed(self._removed_data):
|
|
52
|
+
graph._add_node(removed_node)
|
|
53
|
+
for edge in removed_edges:
|
|
54
|
+
graph._add_edge(edge)
|
|
55
|
+
|
|
56
|
+
def __str__(self) -> str:
|
|
57
|
+
"""Return a string representation."""
|
|
58
|
+
return f"TryRemoveManyNodes(count={len(self.node_uids)}, removed={len(self._removed_data)})"
|
|
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
|