selector-complexity 0.1.0__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.
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: selector-complexity
3
+ Version: 0.1.0
4
+ Summary: Selector Complexity Framework for IPS proof complexity
5
+ Author-email: Carmen Esteban <carmen@research.dev>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/iafiscal1212/Selector-Complexity-Framework
8
+ Requires-Python: >=3.8
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: numpy>=1.20
11
+ Requires-Dist: scipy>=1.7
@@ -0,0 +1,22 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "selector-complexity"
7
+ version = "0.1.0"
8
+ description = "Selector Complexity Framework for IPS proof complexity"
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ authors = [{name = "Carmen Esteban", email = "carmen@research.dev"}]
12
+ requires-python = ">=3.8"
13
+ dependencies = [
14
+ "numpy>=1.20",
15
+ "scipy>=1.7",
16
+ ]
17
+
18
+ [project.urls]
19
+ Homepage = "https://github.com/iafiscal1212/Selector-Complexity-Framework"
20
+
21
+ [tool.setuptools.packages.find]
22
+ include = ["selector_complexity*"]
@@ -0,0 +1,28 @@
1
+ """
2
+ Selector Complexity Framework
3
+ ==============================
4
+
5
+ Classifies tautologies by the complexity of their selector
6
+ families in IPS (Ideal Proof Systems).
7
+
8
+ Levels:
9
+ 0 - Polynomial certificates, no selectors needed (e.g. PHP)
10
+ 1 - Efficient selectors in auxiliary variables (e.g. PHP-E)
11
+ 2+- Selectors require original variables, expensive (e.g. PHP-C)
12
+
13
+ Author: Carmen Esteban
14
+ License: MIT
15
+ """
16
+
17
+ __version__ = "0.1.0"
18
+ __author__ = "Carmen Esteban"
19
+
20
+ from selector_complexity.core import PolynomialSystem, SelectorFamily
21
+ from selector_complexity.php import php_axioms, phpe_axioms, phpc_axioms
22
+ from selector_complexity.solvers import build_matrix, find_certificate
23
+ from selector_complexity.selectors import (
24
+ build_phpe_selectors,
25
+ build_phpc_explicit_selectors,
26
+ enumerate_vc,
27
+ test_s_only_feasibility,
28
+ )
@@ -0,0 +1,77 @@
1
+ """Core definitions: PolynomialSystem, SelectorFamily."""
2
+
3
+ import numpy as np
4
+ from itertools import combinations
5
+
6
+
7
+ class PolynomialSystem:
8
+ """A system of polynomial equations over boolean variables."""
9
+
10
+ def __init__(self, name, num_vars, axioms):
11
+ self.name = name
12
+ self.num_vars = num_vars
13
+ self.axioms = axioms
14
+
15
+ def evaluate(self, assignment):
16
+ values = []
17
+ for ax in self.axioms:
18
+ val = 0.0
19
+ for coef, monom in ax:
20
+ prod = coef
21
+ for v in monom:
22
+ prod *= assignment.get(v, 0)
23
+ val += prod
24
+ values.append(val)
25
+ return values
26
+
27
+ def is_unsatisfiable(self, max_vars=15):
28
+ if self.num_vars > max_vars:
29
+ return None
30
+ for bits in range(2 ** self.num_vars):
31
+ assignment = {v: (bits >> v) & 1 for v in range(self.num_vars)}
32
+ vals = self.evaluate(assignment)
33
+ if all(abs(v) < 1e-10 for v in vals):
34
+ return False
35
+ return True
36
+
37
+ def __repr__(self):
38
+ return "PolynomialSystem({}, {} vars, {} axioms)".format(
39
+ self.name, self.num_vars, len(self.axioms))
40
+
41
+
42
+ class SelectorFamily:
43
+ """A family of selector polynomials."""
44
+
45
+ def __init__(self, selectors, var_assignments_generator):
46
+ self.selectors = selectors
47
+ self.var_gen = var_assignments_generator
48
+
49
+ def verify(self):
50
+ labels = list(self.selectors.keys())
51
+ partition_ok = exclusivity_ok = boolean_ok = total = 0
52
+ for assignment in self.var_gen():
53
+ total += 1
54
+ vals = {}
55
+ for label in labels:
56
+ v = sum(c * np.prod([assignment.get(var, 0) for var in m])
57
+ for c, m in self.selectors[label])
58
+ vals[label] = v
59
+ if all(abs(v) < 1e-10 or abs(v - 1) < 1e-10 for v in vals.values()):
60
+ boolean_ok += 1
61
+ if abs(sum(vals.values()) - 1.0) < 1e-10:
62
+ partition_ok += 1
63
+ excl = all(abs(vals[l1] * vals[l2]) < 1e-10
64
+ for i, l1 in enumerate(labels)
65
+ for l2 in labels[i+1:])
66
+ if excl:
67
+ exclusivity_ok += 1
68
+ return {"total": total, "partition": partition_ok,
69
+ "exclusivity": exclusivity_ok, "boolean": boolean_ok,
70
+ "all_pass": partition_ok == total == exclusivity_ok == boolean_ok}
71
+
72
+ def size(self):
73
+ return sum(len(p) for p in self.selectors.values())
74
+
75
+ def max_degree(self):
76
+ return max(max((len(m) for _, m in p), default=0)
77
+ for p in self.selectors.values())
@@ -0,0 +1,154 @@
1
+ from itertools import combinations
2
+
3
+ def php_axioms(n):
4
+ pigeons = list(range(1, n + 2))
5
+ holes = list(range(1, n + 1))
6
+ var_x = {}
7
+ idx = 0
8
+ for p in pigeons:
9
+ for h in holes:
10
+ var_x[(p, h)] = idx
11
+ idx += 1
12
+ num_vars = idx
13
+ axioms = []
14
+ for p in pigeons:
15
+ terms = []
16
+ hvars = [var_x[(p, h)] for h in holes]
17
+ for k in range(len(hvars) + 1):
18
+ sign = (-1.0) ** k
19
+ for subset in combinations(hvars, k):
20
+ terms.append((sign, frozenset(subset)))
21
+ axioms.append(terms)
22
+ for h in holes:
23
+ for i, p in enumerate(pigeons):
24
+ for p2 in pigeons[i + 1:]:
25
+ axioms.append([(1.0, frozenset([var_x[(p, h)], var_x[(p2, h)]]))])
26
+ for p in pigeons:
27
+ for j, h in enumerate(holes):
28
+ for h2 in holes[j + 1:]:
29
+ axioms.append([(1.0, frozenset([var_x[(p, h)], var_x[(p, h2)]]))])
30
+ return axioms, num_vars, var_x
31
+
32
+ def phpe_axioms(n):
33
+ pigeons = list(range(1, n + 2))
34
+ holes = list(range(1, n + 1))
35
+ var_x, var_y = {}, {}
36
+ idx = 0
37
+ for p in pigeons:
38
+ for h in holes:
39
+ var_x[(p, h)] = idx
40
+ idx += 1
41
+ for i, p in enumerate(pigeons):
42
+ for p2 in pigeons[i + 1:]:
43
+ var_y[(p, p2)] = idx
44
+ idx += 1
45
+ num_vars = idx
46
+ axioms = []
47
+ for p in pigeons:
48
+ terms = []
49
+ hvars = [var_x[(p, h)] for h in holes]
50
+ for k in range(len(hvars) + 1):
51
+ sign = (-1.0) ** k
52
+ for subset in combinations(hvars, k):
53
+ terms.append((sign, frozenset(subset)))
54
+ axioms.append(terms)
55
+ for h in holes:
56
+ for i, p in enumerate(pigeons):
57
+ for p2 in pigeons[i + 1:]:
58
+ axioms.append([(1.0, frozenset([var_x[(p, h)], var_x[(p2, h)]]))])
59
+ for p in pigeons:
60
+ for j, h in enumerate(holes):
61
+ for h2 in holes[j + 1:]:
62
+ axioms.append([(1.0, frozenset([var_x[(p, h)], var_x[(p, h2)]]))])
63
+ for i_p, p in enumerate(pigeons):
64
+ for p2 in pigeons[i_p + 1:]:
65
+ y_idx = var_y[(p, p2)]
66
+ for h in holes:
67
+ for h2 in holes:
68
+ if h == h2: continue
69
+ x1, x2 = var_x[(p, h)], var_x[(p2, h2)]
70
+ if h < h2:
71
+ axioms.append([(1.0, frozenset([x1, x2])), (-1.0, frozenset([x1, x2,
72
+ y_idx]))])
73
+ else:
74
+ axioms.append([(1.0, frozenset([x1, x2, y_idx]))])
75
+ for i_p, p in enumerate(pigeons):
76
+ for j_p, p2 in enumerate(pigeons[i_p + 1:], i_p + 1):
77
+ for p3 in pigeons[j_p + 1:]:
78
+ y12, y23, y13 = var_y[(p, p2)], var_y[(p2, p3)], var_y[(p, p3)]
79
+ axioms.append([(1.0, frozenset([y12, y23])), (-1.0, frozenset([y12, y23,
80
+ y13]))])
81
+ axioms.append([(1.0, frozenset([y13])), (-1.0, frozenset([y12, y13])),
82
+ (-1.0, frozenset([y23, y13])), (1.0, frozenset([y12, y23,
83
+ y13]))])
84
+ return axioms, num_vars, var_x, var_y
85
+
86
+ def phpc_axioms(n):
87
+ pigeons = list(range(1, n + 2))
88
+ holes = list(range(1, n + 1))
89
+ var_x, var_s = {}, {}
90
+ idx = 0
91
+ for p in pigeons:
92
+ for h in holes:
93
+ var_x[(p, h)] = idx
94
+ idx += 1
95
+ for p in pigeons:
96
+ for q in pigeons:
97
+ if p != q:
98
+ var_s[(p, q)] = idx
99
+ idx += 1
100
+ num_vars = idx
101
+ axioms = []
102
+ for p in pigeons:
103
+ terms = []
104
+ hvars = [var_x[(p, h)] for h in holes]
105
+ for k in range(len(hvars) + 1):
106
+ sign = (-1.0) ** k
107
+ for subset in combinations(hvars, k):
108
+ terms.append((sign, frozenset(subset)))
109
+ axioms.append(terms)
110
+ for h in holes:
111
+ for i, p in enumerate(pigeons):
112
+ for p2 in pigeons[i + 1:]:
113
+ axioms.append([(1.0, frozenset([var_x[(p, h)], var_x[(p2, h)]]))])
114
+ for p in pigeons:
115
+ for j, h in enumerate(holes):
116
+ for h2 in holes[j + 1:]:
117
+ axioms.append([(1.0, frozenset([var_x[(p, h)], var_x[(p, h2)]]))])
118
+ for p in pigeons:
119
+ terms = []
120
+ svars = [var_s[(p, q)] for q in pigeons if q != p]
121
+ for k in range(len(svars) + 1):
122
+ sign = (-1.0) ** k
123
+ for subset in combinations(svars, k):
124
+ terms.append((sign, frozenset(subset)))
125
+ axioms.append(terms)
126
+ for p in pigeons:
127
+ others = [q for q in pigeons if q != p]
128
+ for i, q in enumerate(others):
129
+ for q2 in others[i + 1:]:
130
+ axioms.append([(1.0, frozenset([var_s[(p, q)], var_s[(p, q2)]]))])
131
+ for q in pigeons:
132
+ terms = []
133
+ svars = [var_s[(p, q)] for p in pigeons if p != q]
134
+ for k in range(len(svars) + 1):
135
+ sign = (-1.0) ** k
136
+ for subset in combinations(svars, k):
137
+ terms.append((sign, frozenset(subset)))
138
+ axioms.append(terms)
139
+ for q in pigeons:
140
+ others = [p for p in pigeons if p != q]
141
+ for i, p in enumerate(others):
142
+ for p2 in others[i + 1:]:
143
+ axioms.append([(1.0, frozenset([var_s[(p, q)], var_s[(p2, q)]]))])
144
+ def succ_hole(h):
145
+ return (h % n) + 1
146
+ for p in pigeons:
147
+ for q in pigeons:
148
+ if p == q: continue
149
+ s_idx = var_s[(p, q)]
150
+ for h in holes:
151
+ for h2 in holes:
152
+ if h2 == succ_hole(h): continue
153
+ axioms.append([(1.0, frozenset([s_idx, var_x[(p, h)], var_x[(q, h2)]]))])
154
+ return axioms, num_vars, var_x, var_s
@@ -0,0 +1,68 @@
1
+ import numpy as np
2
+ from itertools import combinations, permutations
3
+
4
+ def build_phpe_selectors(n):
5
+ pigeons = list(range(1, n + 2))
6
+ var_y = {}
7
+ idx = 0
8
+ for i, p in enumerate(pigeons):
9
+ for p2 in pigeons[i + 1:]:
10
+ var_y[(p, p2)] = idx
11
+ idx += 1
12
+ indicators = {}
13
+ for p in pigeons:
14
+ factors = []
15
+ for q in pigeons:
16
+ if q == p: continue
17
+ if q < p:
18
+ factors.append([(1.0, frozenset([var_y[(q, p)]]))])
19
+ else:
20
+ factors.append([(1.0, frozenset()), (-1.0, frozenset([var_y[(p, q)]]))])
21
+ result = [(1.0, frozenset())]
22
+ for factor in factors:
23
+ new_result = []
24
+ for c1, m1 in result:
25
+ for c2, m2 in factor:
26
+ new_result.append((c1 * c2, m1 | m2))
27
+ combined = {}
28
+ for c, m in new_result:
29
+ combined[m] = combined.get(m, 0) + c
30
+ result = [(c, m) for m, c in combined.items() if abs(c) > 1e-15]
31
+ indicators[p] = result
32
+ return indicators, var_y
33
+
34
+ def test_s_only_feasibility(n, max_degree=3):
35
+ pigeons = list(range(1, n + 2))
36
+ var_s = {}
37
+ idx = 0
38
+ for p in pigeons:
39
+ for q in pigeons:
40
+ if p != q:
41
+ var_s[(p, q)] = idx
42
+ idx += 1
43
+ num_s_vars = idx
44
+ others = pigeons[1:]
45
+ cycle_assignments = []
46
+ for perm in permutations(others):
47
+ cycle = (pigeons[0],) + perm
48
+ assignment = {}
49
+ for i in range(len(cycle)):
50
+ p, q = cycle[i], cycle[(i + 1) % len(cycle)]
51
+ for p2 in pigeons:
52
+ for q2 in pigeons:
53
+ if p2 != q2:
54
+ assignment[var_s[(p2, q2)]] = 1 if (p2 == p and q2 == q) else 0
55
+ cycle_assignments.append(assignment)
56
+ monoms = [frozenset()]
57
+ for d in range(1, max_degree + 1):
58
+ for combo in combinations(range(num_s_vars), d):
59
+ monoms.append(frozenset(combo))
60
+ eval_matrix = np.zeros((len(cycle_assignments), len(monoms)))
61
+ for i, assign in enumerate(cycle_assignments):
62
+ for j, monom in enumerate(monoms):
63
+ prod = 1.0
64
+ for v in monom:
65
+ prod *= assign.get(v, 0)
66
+ eval_matrix[i, j] = prod
67
+ unique_rows = np.unique(eval_matrix, axis=0)
68
+ return len(unique_rows) == 1, len(cycle_assignments), len(unique_rows)
@@ -0,0 +1,49 @@
1
+ import numpy as np
2
+ from scipy import sparse
3
+ from scipy.sparse.linalg import lsqr
4
+ from itertools import combinations
5
+
6
+ def build_matrix(axioms, num_vars, d_max):
7
+ all_monoms = []
8
+ monom_to_idx = {}
9
+ for d in range(d_max + 1):
10
+ for combo in combinations(range(num_vars), d):
11
+ m = frozenset(combo)
12
+ monom_to_idx[m] = len(all_monoms)
13
+ all_monoms.append(m)
14
+ num_monoms = len(all_monoms)
15
+ rows, cols, vals = [], [], []
16
+ total_unknowns = 0
17
+ for ax in axioms:
18
+ deg_ax = max(len(m) for c, m in ax)
19
+ deg_mult = max(0, d_max - deg_ax)
20
+ for d in range(deg_mult + 1):
21
+ for combo in combinations(range(num_vars), d):
22
+ m_mult = frozenset(combo)
23
+ col = total_unknowns
24
+ total_unknowns += 1
25
+ for coef_ax, m_ax in ax:
26
+ m_prod = m_mult | m_ax
27
+ if len(m_prod) <= d_max and m_prod in monom_to_idx:
28
+ rows.append(monom_to_idx[m_prod])
29
+ cols.append(col)
30
+ vals.append(coef_ax)
31
+ if total_unknowns == 0:
32
+ return sparse.csr_matrix((num_monoms, 0)), np.zeros(num_monoms), num_monoms, 0
33
+ A = sparse.csr_matrix((vals, (rows, cols)), shape=(num_monoms, total_unknowns))
34
+ b = np.zeros(num_monoms)
35
+ b[monom_to_idx[frozenset()]] = 1.0
36
+ return A, b, num_monoms, total_unknowns
37
+
38
+ def find_certificate(axioms, num_vars, d_max, atol=1e-12):
39
+ A, b, nm, nu = build_matrix(axioms, num_vars, d_max)
40
+ if nu == 0:
41
+ return None
42
+ res = lsqr(A, b, atol=atol, btol=atol, iter_lim=10000)
43
+ x = res[0]
44
+ residual = np.linalg.norm(A @ x - b)
45
+ if residual < 1e-6:
46
+ size = int(np.sum(np.abs(x) > 1e-8))
47
+ return {"degree": d_max, "size": size, "num_monoms": nm,
48
+ "num_unknowns": nu, "residual": float(residual), "coefficients": x}
49
+ return None
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: selector-complexity
3
+ Version: 0.1.0
4
+ Summary: Selector Complexity Framework for IPS proof complexity
5
+ Author-email: Carmen Esteban <carmen@research.dev>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/iafiscal1212/Selector-Complexity-Framework
8
+ Requires-Python: >=3.8
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: numpy>=1.20
11
+ Requires-Dist: scipy>=1.7
@@ -0,0 +1,11 @@
1
+ pyproject.toml
2
+ selector_complexity/__init__.py
3
+ selector_complexity/core.py
4
+ selector_complexity/php.py
5
+ selector_complexity/selectors.py
6
+ selector_complexity/solvers.py
7
+ selector_complexity.egg-info/PKG-INFO
8
+ selector_complexity.egg-info/SOURCES.txt
9
+ selector_complexity.egg-info/dependency_links.txt
10
+ selector_complexity.egg-info/requires.txt
11
+ selector_complexity.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ numpy>=1.20
2
+ scipy>=1.7
@@ -0,0 +1 @@
1
+ selector_complexity
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+