implica 0.3.3__tar.gz → 0.3.4__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.3 → implica-0.3.4}/PKG-INFO +136 -1
  2. {implica-0.3.3 → implica-0.3.4}/README.md +135 -0
  3. {implica-0.3.3 → implica-0.3.4}/pyproject.toml +1 -1
  4. {implica-0.3.3 → implica-0.3.4}/src/implica/__init__.py +1 -1
  5. {implica-0.3.3 → implica-0.3.4}/LICENSE +0 -0
  6. {implica-0.3.3 → implica-0.3.4}/src/implica/core/__init__.py +0 -0
  7. {implica-0.3.3 → implica-0.3.4}/src/implica/core/combinator.py +0 -0
  8. {implica-0.3.3 → implica-0.3.4}/src/implica/core/types.py +0 -0
  9. {implica-0.3.3 → implica-0.3.4}/src/implica/graph/__init__.py +0 -0
  10. {implica-0.3.3 → implica-0.3.4}/src/implica/graph/connection.py +0 -0
  11. {implica-0.3.3 → implica-0.3.4}/src/implica/graph/elements.py +0 -0
  12. {implica-0.3.3 → implica-0.3.4}/src/implica/graph/graph.py +0 -0
  13. {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/__init__.py +0 -0
  14. {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/add_edge.py +0 -0
  15. {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/add_many_edges.py +0 -0
  16. {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/add_many_nodes.py +0 -0
  17. {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/add_node.py +0 -0
  18. {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/base.py +0 -0
  19. {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/remove_edge.py +0 -0
  20. {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/remove_many_edges.py +0 -0
  21. {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/remove_many_nodes.py +0 -0
  22. {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/remove_node.py +0 -0
  23. {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/try_add_edge.py +0 -0
  24. {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/try_add_many_edges.py +0 -0
  25. {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/try_add_many_nodes.py +0 -0
  26. {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/try_add_node.py +0 -0
  27. {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/try_remove_edge.py +0 -0
  28. {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/try_remove_many_edges.py +0 -0
  29. {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/try_remove_many_nodes.py +0 -0
  30. {implica-0.3.3 → implica-0.3.4}/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.3
3
+ Version: 0.3.4
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
@@ -24,6 +24,7 @@ A Python package for working with graphs representing minimal implicational logi
24
24
  ## Features
25
25
 
26
26
  - 🎯 **Type System**: Build complex type expressions using variables and applications (function types)
27
+ - 🔍 **Variable Extraction**: Recursively extract all variables from any type expression
27
28
  - 🧩 **Combinators**: Work with S and K combinators from combinatory logic
28
29
  - 📊 **Graph Structure**: Represent type transformations as nodes and edges in a directed graph
29
30
  - 🔄 **Transactional Operations**: Safely modify graphs with automatic rollback on failure
@@ -106,6 +107,43 @@ print(complex_function) # Output: (A -> B) -> C
106
107
  print(nested_function) # Output: A -> B -> C
107
108
  ```
108
109
 
110
+ #### Extracting Variables from Types
111
+
112
+ Every type has a `variables` property that returns a list of all variables contained in that type. This is computed recursively and may include duplicate variables if they appear multiple times:
113
+
114
+ ```python
115
+ import implica as imp
116
+
117
+ # Simple variable returns itself
118
+ A = imp.var("A")
119
+ print(A.variables) # Output: [Variable(name='A')]
120
+
121
+ # Application returns all variables from both input and output
122
+ B = imp.var("B")
123
+ func = imp.app(A, B)
124
+ print([v.name for v in func.variables]) # Output: ['A', 'B']
125
+
126
+ # Nested types return all variables recursively
127
+ C = imp.var("C")
128
+ nested = imp.app(imp.app(A, B), C) # (A -> B) -> C
129
+ print([v.name for v in nested.variables]) # Output: ['A', 'B', 'C']
130
+
131
+ # Duplicates are included
132
+ same_var = imp.app(A, A) # A -> A
133
+ print([v.name for v in same_var.variables]) # Output: ['A', 'A']
134
+
135
+ # Complex example with duplicates
136
+ complex = imp.app(imp.app(A, B), A) # (A -> B) -> A
137
+ print([v.name for v in complex.variables]) # Output: ['A', 'B', 'A']
138
+ ```
139
+
140
+ **Use cases:**
141
+
142
+ - Analyze which variables are used in a complex type expression
143
+ - Count occurrences of specific variables in a type
144
+ - Validate that certain variables are present or absent
145
+ - Generate variable lists for quantification or substitution operations
146
+
109
147
  ### Combinators
110
148
 
111
149
  Combinators represent transformations between types. The library includes the S and K combinators from combinatory logic:
@@ -800,6 +838,96 @@ ensure_graph_state(
800
838
  print(f"Final state: {graph.node_count()} nodes") # Output: 2 (X and Y)
801
839
  ```
802
840
 
841
+ ### Example 10: Analyzing Type Variables
842
+
843
+ Extract and analyze variables from complex type expressions:
844
+
845
+ ```python
846
+ import implica as imp
847
+
848
+ # Create a complex type expression
849
+ A = imp.var("A")
850
+ B = imp.var("B")
851
+ C = imp.var("C")
852
+
853
+ # ((A -> B) -> C) -> (A -> B)
854
+ inner = imp.app(A, B)
855
+ middle = imp.app(inner, C)
856
+ complex_type = imp.app(middle, inner)
857
+
858
+ print(f"Type: {complex_type}")
859
+ # Output: ((A -> B) -> C) -> A -> B
860
+
861
+ # Get all variables (including duplicates)
862
+ variables = complex_type.variables
863
+ print(f"All variables: {[v.name for v in variables]}")
864
+ # Output: ['A', 'B', 'C', 'A', 'B']
865
+
866
+ # Count unique variables
867
+ unique_vars = {v.name for v in variables}
868
+ print(f"Unique variables: {unique_vars}")
869
+ # Output: {'A', 'B', 'C'}
870
+
871
+ # Count occurrences
872
+ from collections import Counter
873
+ var_counts = Counter(v.name for v in variables)
874
+ print(f"Variable counts: {dict(var_counts)}")
875
+ # Output: {'A': 2, 'B': 2, 'C': 1}
876
+
877
+ # Check if a specific variable is used
878
+ def uses_variable(type_expr, var_name):
879
+ """Check if a type expression uses a specific variable."""
880
+ return any(v.name == var_name for v in type_expr.variables)
881
+
882
+ print(f"Uses A: {uses_variable(complex_type, 'A')}") # Output: True
883
+ print(f"Uses D: {uses_variable(complex_type, 'D')}") # Output: False
884
+
885
+ # Find all types in a graph that use a specific variable
886
+ graph = imp.Graph()
887
+
888
+ # Create various types
889
+ types_to_add = [
890
+ imp.var("X"),
891
+ imp.var("Y"),
892
+ imp.app(A, B),
893
+ imp.app(B, C),
894
+ imp.app(A, imp.app(B, C)),
895
+ ]
896
+
897
+ with graph.connect() as conn:
898
+ for t in types_to_add:
899
+ conn.add_node(imp.node(t))
900
+
901
+ # Find all nodes containing variable "B"
902
+ nodes_with_B = [
903
+ n for n in graph.nodes()
904
+ if any(v.name == "B" for v in n.type.variables)
905
+ ]
906
+
907
+ print(f"\nNodes containing variable B:")
908
+ for n in nodes_with_B:
909
+ print(f" - {n.type}")
910
+ # Output:
911
+ # - A -> B
912
+ # - B -> C
913
+ # - A -> B -> C
914
+
915
+ # Calculate the "complexity" of a type by counting its variables
916
+ def type_complexity(type_expr):
917
+ """Calculate complexity as the total number of variables."""
918
+ return len(type_expr.variables)
919
+
920
+ print(f"\nType complexities:")
921
+ for n in graph.nodes():
922
+ print(f" {n.type}: {type_complexity(n.type)}")
923
+ # Output:
924
+ # X: 1
925
+ # Y: 1
926
+ # A -> B: 2
927
+ # B -> C: 2
928
+ # A -> B -> C: 3
929
+ ```
930
+
803
931
  ## API Reference
804
932
 
805
933
  ### Core Module (`implica.core`)
@@ -809,7 +937,14 @@ print(f"Final state: {graph.node_count()} nodes") # Output: 2 (X and Y)
809
937
  - `var(name: str) -> Variable`: Create a type variable
810
938
  - `app(input_type: BaseType, output_type: BaseType) -> Application`: Create a function type
811
939
  - `Variable`: Atomic type variable
940
+ - `name: str`: The name of the variable
941
+ - `uid: str`: Unique identifier (SHA256 hash)
942
+ - `variables: list[Variable]`: Returns `[self]`
812
943
  - `Application`: Function application type
944
+ - `input_type: BaseType`: Input type of the function
945
+ - `output_type: BaseType`: Output type of the function
946
+ - `uid: str`: Unique identifier (SHA256 hash)
947
+ - `variables: list[Variable]`: Returns all variables from input and output types (may include duplicates)
813
948
 
814
949
  **Combinators:**
815
950
 
@@ -10,6 +10,7 @@ A Python package for working with graphs representing minimal implicational logi
10
10
  ## Features
11
11
 
12
12
  - 🎯 **Type System**: Build complex type expressions using variables and applications (function types)
13
+ - 🔍 **Variable Extraction**: Recursively extract all variables from any type expression
13
14
  - 🧩 **Combinators**: Work with S and K combinators from combinatory logic
14
15
  - 📊 **Graph Structure**: Represent type transformations as nodes and edges in a directed graph
15
16
  - 🔄 **Transactional Operations**: Safely modify graphs with automatic rollback on failure
@@ -92,6 +93,43 @@ print(complex_function) # Output: (A -> B) -> C
92
93
  print(nested_function) # Output: A -> B -> C
93
94
  ```
94
95
 
96
+ #### Extracting Variables from Types
97
+
98
+ Every type has a `variables` property that returns a list of all variables contained in that type. This is computed recursively and may include duplicate variables if they appear multiple times:
99
+
100
+ ```python
101
+ import implica as imp
102
+
103
+ # Simple variable returns itself
104
+ A = imp.var("A")
105
+ print(A.variables) # Output: [Variable(name='A')]
106
+
107
+ # Application returns all variables from both input and output
108
+ B = imp.var("B")
109
+ func = imp.app(A, B)
110
+ print([v.name for v in func.variables]) # Output: ['A', 'B']
111
+
112
+ # Nested types return all variables recursively
113
+ C = imp.var("C")
114
+ nested = imp.app(imp.app(A, B), C) # (A -> B) -> C
115
+ print([v.name for v in nested.variables]) # Output: ['A', 'B', 'C']
116
+
117
+ # Duplicates are included
118
+ same_var = imp.app(A, A) # A -> A
119
+ print([v.name for v in same_var.variables]) # Output: ['A', 'A']
120
+
121
+ # Complex example with duplicates
122
+ complex = imp.app(imp.app(A, B), A) # (A -> B) -> A
123
+ print([v.name for v in complex.variables]) # Output: ['A', 'B', 'A']
124
+ ```
125
+
126
+ **Use cases:**
127
+
128
+ - Analyze which variables are used in a complex type expression
129
+ - Count occurrences of specific variables in a type
130
+ - Validate that certain variables are present or absent
131
+ - Generate variable lists for quantification or substitution operations
132
+
95
133
  ### Combinators
96
134
 
97
135
  Combinators represent transformations between types. The library includes the S and K combinators from combinatory logic:
@@ -786,6 +824,96 @@ ensure_graph_state(
786
824
  print(f"Final state: {graph.node_count()} nodes") # Output: 2 (X and Y)
787
825
  ```
788
826
 
827
+ ### Example 10: Analyzing Type Variables
828
+
829
+ Extract and analyze variables from complex type expressions:
830
+
831
+ ```python
832
+ import implica as imp
833
+
834
+ # Create a complex type expression
835
+ A = imp.var("A")
836
+ B = imp.var("B")
837
+ C = imp.var("C")
838
+
839
+ # ((A -> B) -> C) -> (A -> B)
840
+ inner = imp.app(A, B)
841
+ middle = imp.app(inner, C)
842
+ complex_type = imp.app(middle, inner)
843
+
844
+ print(f"Type: {complex_type}")
845
+ # Output: ((A -> B) -> C) -> A -> B
846
+
847
+ # Get all variables (including duplicates)
848
+ variables = complex_type.variables
849
+ print(f"All variables: {[v.name for v in variables]}")
850
+ # Output: ['A', 'B', 'C', 'A', 'B']
851
+
852
+ # Count unique variables
853
+ unique_vars = {v.name for v in variables}
854
+ print(f"Unique variables: {unique_vars}")
855
+ # Output: {'A', 'B', 'C'}
856
+
857
+ # Count occurrences
858
+ from collections import Counter
859
+ var_counts = Counter(v.name for v in variables)
860
+ print(f"Variable counts: {dict(var_counts)}")
861
+ # Output: {'A': 2, 'B': 2, 'C': 1}
862
+
863
+ # Check if a specific variable is used
864
+ def uses_variable(type_expr, var_name):
865
+ """Check if a type expression uses a specific variable."""
866
+ return any(v.name == var_name for v in type_expr.variables)
867
+
868
+ print(f"Uses A: {uses_variable(complex_type, 'A')}") # Output: True
869
+ print(f"Uses D: {uses_variable(complex_type, 'D')}") # Output: False
870
+
871
+ # Find all types in a graph that use a specific variable
872
+ graph = imp.Graph()
873
+
874
+ # Create various types
875
+ types_to_add = [
876
+ imp.var("X"),
877
+ imp.var("Y"),
878
+ imp.app(A, B),
879
+ imp.app(B, C),
880
+ imp.app(A, imp.app(B, C)),
881
+ ]
882
+
883
+ with graph.connect() as conn:
884
+ for t in types_to_add:
885
+ conn.add_node(imp.node(t))
886
+
887
+ # Find all nodes containing variable "B"
888
+ nodes_with_B = [
889
+ n for n in graph.nodes()
890
+ if any(v.name == "B" for v in n.type.variables)
891
+ ]
892
+
893
+ print(f"\nNodes containing variable B:")
894
+ for n in nodes_with_B:
895
+ print(f" - {n.type}")
896
+ # Output:
897
+ # - A -> B
898
+ # - B -> C
899
+ # - A -> B -> C
900
+
901
+ # Calculate the "complexity" of a type by counting its variables
902
+ def type_complexity(type_expr):
903
+ """Calculate complexity as the total number of variables."""
904
+ return len(type_expr.variables)
905
+
906
+ print(f"\nType complexities:")
907
+ for n in graph.nodes():
908
+ print(f" {n.type}: {type_complexity(n.type)}")
909
+ # Output:
910
+ # X: 1
911
+ # Y: 1
912
+ # A -> B: 2
913
+ # B -> C: 2
914
+ # A -> B -> C: 3
915
+ ```
916
+
789
917
  ## API Reference
790
918
 
791
919
  ### Core Module (`implica.core`)
@@ -795,7 +923,14 @@ print(f"Final state: {graph.node_count()} nodes") # Output: 2 (X and Y)
795
923
  - `var(name: str) -> Variable`: Create a type variable
796
924
  - `app(input_type: BaseType, output_type: BaseType) -> Application`: Create a function type
797
925
  - `Variable`: Atomic type variable
926
+ - `name: str`: The name of the variable
927
+ - `uid: str`: Unique identifier (SHA256 hash)
928
+ - `variables: list[Variable]`: Returns `[self]`
798
929
  - `Application`: Function application type
930
+ - `input_type: BaseType`: Input type of the function
931
+ - `output_type: BaseType`: Output type of the function
932
+ - `uid: str`: Unique identifier (SHA256 hash)
933
+ - `variables: list[Variable]`: Returns all variables from input and output types (may include duplicates)
799
934
 
800
935
  **Combinators:**
801
936
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "implica"
3
- version = "0.3.3"
3
+ version = "0.3.4"
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"
@@ -41,4 +41,4 @@ __all__ = [
41
41
  "mutations",
42
42
  ]
43
43
 
44
- __version__ = "0.3.3"
44
+ __version__ = "0.3.4"
File without changes