implica 0.3.1__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.

Files changed (30) hide show
  1. {implica-0.3.1 → implica-0.3.2}/PKG-INFO +1 -1
  2. {implica-0.3.1 → implica-0.3.2}/pyproject.toml +1 -1
  3. {implica-0.3.1 → implica-0.3.2}/src/implica/__init__.py +9 -9
  4. {implica-0.3.1 → implica-0.3.2}/src/implica/graph/connection.py +78 -0
  5. {implica-0.3.1 → implica-0.3.2}/src/implica/mutations/__init__.py +8 -0
  6. implica-0.3.2/src/implica/mutations/try_add_many_edges.py +63 -0
  7. implica-0.3.2/src/implica/mutations/try_add_many_nodes.py +53 -0
  8. implica-0.3.2/src/implica/mutations/try_remove_many_edges.py +55 -0
  9. implica-0.3.2/src/implica/mutations/try_remove_many_nodes.py +58 -0
  10. {implica-0.3.1 → implica-0.3.2}/LICENSE +0 -0
  11. {implica-0.3.1 → implica-0.3.2}/README.md +0 -0
  12. {implica-0.3.1 → implica-0.3.2}/src/implica/core/__init__.py +0 -0
  13. {implica-0.3.1 → implica-0.3.2}/src/implica/core/combinator.py +0 -0
  14. {implica-0.3.1 → implica-0.3.2}/src/implica/core/types.py +0 -0
  15. {implica-0.3.1 → implica-0.3.2}/src/implica/graph/__init__.py +0 -0
  16. {implica-0.3.1 → implica-0.3.2}/src/implica/graph/elements.py +0 -0
  17. {implica-0.3.1 → implica-0.3.2}/src/implica/graph/graph.py +0 -0
  18. {implica-0.3.1 → implica-0.3.2}/src/implica/mutations/add_edge.py +0 -0
  19. {implica-0.3.1 → implica-0.3.2}/src/implica/mutations/add_many_edges.py +0 -0
  20. {implica-0.3.1 → implica-0.3.2}/src/implica/mutations/add_many_nodes.py +0 -0
  21. {implica-0.3.1 → implica-0.3.2}/src/implica/mutations/add_node.py +0 -0
  22. {implica-0.3.1 → implica-0.3.2}/src/implica/mutations/base.py +0 -0
  23. {implica-0.3.1 → implica-0.3.2}/src/implica/mutations/remove_edge.py +0 -0
  24. {implica-0.3.1 → implica-0.3.2}/src/implica/mutations/remove_many_edges.py +0 -0
  25. {implica-0.3.1 → implica-0.3.2}/src/implica/mutations/remove_many_nodes.py +0 -0
  26. {implica-0.3.1 → implica-0.3.2}/src/implica/mutations/remove_node.py +0 -0
  27. {implica-0.3.1 → implica-0.3.2}/src/implica/mutations/try_add_edge.py +0 -0
  28. {implica-0.3.1 → implica-0.3.2}/src/implica/mutations/try_add_node.py +0 -0
  29. {implica-0.3.1 → implica-0.3.2}/src/implica/mutations/try_remove_edge.py +0 -0
  30. {implica-0.3.1 → implica-0.3.2}/src/implica/mutations/try_remove_node.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: implica
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: A package for working with graphs representing minimal implicational logic models
5
5
  Author: Carlos Fernandez
6
6
  Author-email: carlos.ferlo@outlook.com
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "implica"
3
- version = "0.3.1"
3
+ version = "0.3.2"
4
4
  description = "A package for working with graphs representing minimal implicational logic models"
5
5
  authors = ["Carlos Fernandez <carlos.ferlo@outlook.com>"]
6
6
  readme = "README.md"
@@ -1,21 +1,21 @@
1
1
  """
2
- Guanciale - Implicational Logic Graph Library
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 'gil' for a clean, concise API:
10
- import guanciale as gil
9
+ Import as 'imp' for a clean, concise API:
10
+ import implica as imp
11
11
 
12
- graph = gil.Graph()
13
- A = gil.var("A")
14
- B = gil.var("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(gil.node(A))
18
- conn.add_node(gil.node(B))
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.1"
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