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.
- {implica-0.3.3 → implica-0.3.4}/PKG-INFO +136 -1
- {implica-0.3.3 → implica-0.3.4}/README.md +135 -0
- {implica-0.3.3 → implica-0.3.4}/pyproject.toml +1 -1
- {implica-0.3.3 → implica-0.3.4}/src/implica/__init__.py +1 -1
- {implica-0.3.3 → implica-0.3.4}/LICENSE +0 -0
- {implica-0.3.3 → implica-0.3.4}/src/implica/core/__init__.py +0 -0
- {implica-0.3.3 → implica-0.3.4}/src/implica/core/combinator.py +0 -0
- {implica-0.3.3 → implica-0.3.4}/src/implica/core/types.py +0 -0
- {implica-0.3.3 → implica-0.3.4}/src/implica/graph/__init__.py +0 -0
- {implica-0.3.3 → implica-0.3.4}/src/implica/graph/connection.py +0 -0
- {implica-0.3.3 → implica-0.3.4}/src/implica/graph/elements.py +0 -0
- {implica-0.3.3 → implica-0.3.4}/src/implica/graph/graph.py +0 -0
- {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/__init__.py +0 -0
- {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/add_edge.py +0 -0
- {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/add_many_edges.py +0 -0
- {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/add_many_nodes.py +0 -0
- {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/add_node.py +0 -0
- {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/base.py +0 -0
- {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/remove_edge.py +0 -0
- {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/remove_many_edges.py +0 -0
- {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/remove_many_nodes.py +0 -0
- {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/remove_node.py +0 -0
- {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/try_add_edge.py +0 -0
- {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/try_add_many_edges.py +0 -0
- {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/try_add_many_nodes.py +0 -0
- {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/try_add_node.py +0 -0
- {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/try_remove_edge.py +0 -0
- {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/try_remove_many_edges.py +0 -0
- {implica-0.3.3 → implica-0.3.4}/src/implica/mutations/try_remove_many_nodes.py +0 -0
- {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
|
+
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
|
|
|
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
|