Spaces:
Sleeping
Sleeping
Create practicality_oracle.py
Browse files- practicality_oracle.py +217 -0
practicality_oracle.py
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
import copy
|
| 3 |
+
import json
|
| 4 |
+
import numpy as np
|
| 5 |
+
from fractions import Fraction
|
| 6 |
+
from typing import Dict, Tuple, Optional, Any, Callable
|
| 7 |
+
from dataclasses import dataclass, field
|
| 8 |
+
from google import genai
|
| 9 |
+
from google.genai import types
|
| 10 |
+
|
| 11 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 12 |
+
# SECTION A: HEISENBERG QUANTUM SIMULATOR (Physics Rules)
|
| 13 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 14 |
+
@dataclass
|
| 15 |
+
class StabilizerTableau:
|
| 16 |
+
n: int
|
| 17 |
+
tableau: np.ndarray = field(default=None)
|
| 18 |
+
def __post_init__(self):
|
| 19 |
+
if self.tableau is None:
|
| 20 |
+
self.tableau = np.zeros((2*self.n, 2*self.n+1), dtype=np.uint8)
|
| 21 |
+
for i in range(self.n):
|
| 22 |
+
self.tableau[i, i] = 1; self.tableau[self.n+i, self.n+i] = 1
|
| 23 |
+
|
| 24 |
+
class HeisenbergSimulator:
|
| 25 |
+
def __init__(self, n_qubits: int):
|
| 26 |
+
self.n = n_qubits
|
| 27 |
+
self.state = StabilizerTableau(n_qubits)
|
| 28 |
+
|
| 29 |
+
def h(self, q: int):
|
| 30 |
+
t = self.state.tableau
|
| 31 |
+
for i in range(2*self.n):
|
| 32 |
+
t[i, 2*self.n] ^= t[i, q] & t[i, self.n+q]
|
| 33 |
+
t[i, q], t[i, self.n+q] = t[i, self.n+q], t[i, q]
|
| 34 |
+
|
| 35 |
+
def cnot(self, c: int, target: int):
|
| 36 |
+
n = self.n; t = self.state.tableau
|
| 37 |
+
for i in range(2*n):
|
| 38 |
+
t[i, 2*n] ^= t[i,c] & t[i,n+target] & (t[i,target] ^ t[i,n+c] ^ 1)
|
| 39 |
+
t[i, target] ^= t[i, c]; t[i, n+c] ^= t[i, n+target]
|
| 40 |
+
|
| 41 |
+
def get_operator_expectation(self, pauli_string: str) -> float:
|
| 42 |
+
n = self.n
|
| 43 |
+
x_part, z_part = np.zeros(n, dtype=np.uint8), np.zeros(n, dtype=np.uint8)
|
| 44 |
+
phase = 0
|
| 45 |
+
for i, p in enumerate(pauli_string):
|
| 46 |
+
if p == 'X': x_part[i] = 1
|
| 47 |
+
elif p == 'Z': z_part[i] = 1
|
| 48 |
+
elif p == 'Y': x_part[i] = 1; z_part[i] = 1; phase += 1
|
| 49 |
+
|
| 50 |
+
t = self.state.tableau
|
| 51 |
+
for i in range(n, 2*n):
|
| 52 |
+
if (np.sum(t[i, :n] * z_part + t[i, n:2*n] * x_part) % 2) == 1: return 0.0
|
| 53 |
+
|
| 54 |
+
test_row = np.concatenate([x_part, z_part, [phase % 2]])
|
| 55 |
+
for i in range(n, 2*n):
|
| 56 |
+
if np.array_equal(t[i, :2*n], test_row[:2*n]): return 1.0 if t[i, 2*n] == 0 else -1.0
|
| 57 |
+
neg_row = test_row.copy(); neg_row[2*n] = 1 - neg_row[2*n]
|
| 58 |
+
if np.array_equal(t[i, :2*n], neg_row[:2*n]): return -1.0 if t[i, 2*n] == 0 else 1.0
|
| 59 |
+
return 1.0
|
| 60 |
+
|
| 61 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 62 |
+
# SECTION B: THE TRUTH ORACLE (Ground Truth Checking)
|
| 63 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 64 |
+
FACTORIZATION_TEST_CASES = [(15, 3, 5), (35, 5, 7), (85, 5, 17), (143, 11, 13)]
|
| 65 |
+
|
| 66 |
+
def extract_factors_from_binding(binding: Dict[str, float], N: int) -> Optional[Tuple[int, int]]:
|
| 67 |
+
for pname, qname in [('p', 'q'), ('factor_p', 'factor_q'), ('x', 'y')]:
|
| 68 |
+
if pname in binding and qname in binding:
|
| 69 |
+
p, q = round(binding[pname]), round(binding[qname])
|
| 70 |
+
if p * q == N and p >= 2 and q >= 2: return (min(p, q), max(p, q))
|
| 71 |
+
if 's' in binding and 'd' in binding:
|
| 72 |
+
s, d = binding['s'], binding['d']
|
| 73 |
+
p, q = round((s + d) / 2), round((s - d) / 2)
|
| 74 |
+
if p * q == N and p >= 2 and q >= 2: return (min(p, q), max(p, q))
|
| 75 |
+
if 't' in binding:
|
| 76 |
+
t = round(binding['t'])
|
| 77 |
+
if t >= 2 and N % t == 0: return (min(t, N//t), max(t, N//t))
|
| 78 |
+
for v, val in binding.items():
|
| 79 |
+
if not math.isfinite(val): continue
|
| 80 |
+
p = round(val)
|
| 81 |
+
if p >= 2 and p * p <= N and N % p == 0: return (p, N // p)
|
| 82 |
+
return None
|
| 83 |
+
|
| 84 |
+
def ground_truth_verify(binding: Dict[str, float], N: int) -> Tuple[bool, str]:
|
| 85 |
+
"""The Oracle: Prevents Hallucinations."""
|
| 86 |
+
result = extract_factors_from_binding(binding, N)
|
| 87 |
+
if result is None: return False, "No valid integer factor pair extractable from binding"
|
| 88 |
+
p, q = result
|
| 89 |
+
if p * q == N: return True, f"{p} Γ {q} = {N} β"
|
| 90 |
+
return False, f"Round-trip failed: extracted ({p}, {q}), {p}Γ{q}={p*q} β {N}"
|
| 91 |
+
|
| 92 |
+
class AdversaryPreFlight:
|
| 93 |
+
def __init__(self, solver_callback: Callable, log_callback: Callable):
|
| 94 |
+
self.solver_callback = solver_callback
|
| 95 |
+
self.log = log_callback
|
| 96 |
+
self.test_cases = FACTORIZATION_TEST_CASES
|
| 97 |
+
self.rejection_log = []
|
| 98 |
+
|
| 99 |
+
def check_isomorphism(self, axl_def: Any) -> Tuple[bool, str]:
|
| 100 |
+
self.log("ADVERSARY PRE-FLIGHT: testing isomorphism on known toy cases...")
|
| 101 |
+
passed_count = 0
|
| 102 |
+
for N, true_p, true_q in self.test_cases:
|
| 103 |
+
new_axl = copy.deepcopy(axl_def)
|
| 104 |
+
if new_axl.observations:
|
| 105 |
+
old_N = list(axl_def.observations.values())[0]
|
| 106 |
+
for key in list(new_axl.observations.keys()):
|
| 107 |
+
if abs(new_axl.observations[key] - old_N) < 1e-6: new_axl.observations[key] = float(N)
|
| 108 |
+
for v in new_axl.variables:
|
| 109 |
+
if abs(v['lo'] - v['hi']) < 1e-6 and abs(v['lo'] - old_N) < 1e-6: v['lo'] = v['hi'] = float(N)
|
| 110 |
+
|
| 111 |
+
binding = self.solver_callback(new_axl, N)
|
| 112 |
+
if not binding:
|
| 113 |
+
self.log(f" N={N}: SKIP (quick-solve failed)")
|
| 114 |
+
continue
|
| 115 |
+
|
| 116 |
+
ok, msg = ground_truth_verify(binding, N)
|
| 117 |
+
self.log(f" N={N}: {'β PASS' if ok else 'β FAIL'} | {msg}")
|
| 118 |
+
if ok: passed_count += 1
|
| 119 |
+
else: self.rejection_log.append({'N': N, 'reason': msg})
|
| 120 |
+
|
| 121 |
+
if passed_count == len(self.test_cases): return True, f"Passed all {passed_count} pre-flight cases"
|
| 122 |
+
elif passed_count == 0: return False, "REJECTED: failed all pre-flight cases β isomorphism invalid"
|
| 123 |
+
else: return False, f"REJECTED: passed {passed_count}/{len(self.test_cases)} β not reliable enough"
|
| 124 |
+
|
| 125 |
+
def failure_taxonomy(self, binding: Dict[str, float], N: int, ce: float) -> str:
|
| 126 |
+
ok, _ = ground_truth_verify(binding, N)
|
| 127 |
+
if ok: return "NONE β solved correctly"
|
| 128 |
+
if ce > 100: return "TYPE_A: Structural infeasibility β constraints contradict, redesign isomorphism"
|
| 129 |
+
result = extract_factors_from_binding(binding, N)
|
| 130 |
+
if result is None:
|
| 131 |
+
if any(abs(v - round(v)) > 0.3 for v in binding.values() if math.isfinite(v)):
|
| 132 |
+
return "TYPE_D: Integrality failure β optimizer stuck between integers"
|
| 133 |
+
return "TYPE_C: Wrong abstraction β inverse map broken, can't extract factors"
|
| 134 |
+
p, q = result
|
| 135 |
+
if p * q != N:
|
| 136 |
+
if abs(p * q - N) / N < 0.1: return "TYPE_D: Precision issue β close but not exact"
|
| 137 |
+
return "TYPE_C: Wrong abstraction β extracted integers don't multiply to N"
|
| 138 |
+
return "TYPE_B: Optimization landscape β increase rays or change initialization"
|
| 139 |
+
|
| 140 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 141 |
+
# SECTION C: LLM PROMPTS & STRUCTURAL TEMPLATES
|
| 142 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 143 |
+
|
| 144 |
+
NOVEL_ISOMORPHISM_DISCOVERY_PROMPT = """\
|
| 145 |
+
You are an isomorphism designer. Your task is to invent a continuous optimization encoding for integer factorization of N.
|
| 146 |
+
HARD REQUIREMENTS:
|
| 147 |
+
1. INVERSE MAP: Write f() and g() explicitly. g() must output exact integers.
|
| 148 |
+
2. MANUAL VERIFICATION: Show your objective evaluates to 0 for N=15, 35, 85 at the exact factor values.
|
| 149 |
+
3. LANDSCAPE: Explain local minima and why descent won't get trapped.
|
| 150 |
+
4. UNIQUENESS: Are the factors the ONLY zeros in [2, sqrt(N)]?
|
| 151 |
+
5. PSL TEMPLATE: Write the full ready-to-run block with tight bounds and GEQ guards.
|
| 152 |
+
Do NOT propose an isomorphism if you cannot answer these 5 constraints.
|
| 153 |
+
"""
|
| 154 |
+
|
| 155 |
+
ENCODER_PROMPT = """You are the Encoder. CRITICAL RULE: Every problem MUST include an explicit INVERSE MAP in the description.
|
| 156 |
+
If variables are x,y: inverse map is p=round(x), q=round(y).
|
| 157 |
+
If variable is t: inverse map is p=round(t), q=N//p.
|
| 158 |
+
OUTPUT SCHEMA (JSON only):
|
| 159 |
+
{"name": string, "description": string, "variables": [{"name": string, "lo": number, "hi": number, "is_int": boolean}], "constraints": [{"kind": "EQ"|"GEQ"|"LEQ", "expr": string, "weight": number}], "anchors": [], "observations": {"variable_name": number}}
|
| 160 |
+
RULES: Use ** for exponents. Variables > 10000 encode in log2 space."""
|
| 161 |
+
|
| 162 |
+
COLLAPSER_PROMPT = """You are the Grounded Hypothesis Collapser.
|
| 163 |
+
HARD RULE: For factorization problems, ALWAYS compute the integers (p, q) and verify p*q=N.
|
| 164 |
+
If product != N: this is UNSOLVED even if CE is low. State this explicitly.
|
| 165 |
+
OUTPUT SCHEMA (JSON only):
|
| 166 |
+
{"scaled_formulas": {"metric": "expr"}, "hypothesis_markdown": "## Markdown output"}"""
|
| 167 |
+
|
| 168 |
+
STRUCTURAL_HYPOTHESIS_TEMPLATES = {
|
| 169 |
+
"Fermat Algebraic (Exact Bijection)": """\
|
| 170 |
+
CONCRETE HYPOTHESIS: Fermat Algebraic Factorization
|
| 171 |
+
====================================================
|
| 172 |
+
N = p*q is equivalent to 4N = s^2 - d^2
|
| 173 |
+
where s = p+q and d = p-q.
|
| 174 |
+
Factors: p = (s+d)/2, q = (s-d)/2.
|
| 175 |
+
|
| 176 |
+
MINIMIZE: d
|
| 177 |
+
|
| 178 |
+
Variables:
|
| 179 |
+
s in [2.0, 172.0]
|
| 180 |
+
d in [0.0, 84.0]
|
| 181 |
+
sum_sq in [340.0, 340.0]
|
| 182 |
+
p in [1.0, 85.0]
|
| 183 |
+
q in [1.0, 85.0]
|
| 184 |
+
|
| 185 |
+
Constraints:
|
| 186 |
+
EQ weight=100: sum_sq - (s**2 - d**2)
|
| 187 |
+
EQ weight=100: sum_sq - 340.0
|
| 188 |
+
EQ weight=10: p - (s + d) / 2.0
|
| 189 |
+
EQ weight=10: q - (s - d) / 2.0
|
| 190 |
+
GEQ: s - d - 2.0
|
| 191 |
+
GEQ: d - 0.0
|
| 192 |
+
GEQ: s - 2.0
|
| 193 |
+
|
| 194 |
+
Anchor: sum_sq - 340.0 = 0 (tolerance 0.001).
|
| 195 |
+
Observation: sum_sq = 340.0.
|
| 196 |
+
""",
|
| 197 |
+
"1D Sine Snap (Fixed Parameterization)": """\
|
| 198 |
+
CONCRETE HYPOTHESIS: 1D Sine-Snap Factorization (Fixed)
|
| 199 |
+
=========================================================
|
| 200 |
+
Key fix: only ONE free variable t. y is derived as N/t.
|
| 201 |
+
Eliminates y=0 collapse entirely.
|
| 202 |
+
|
| 203 |
+
MINIMIZE: snap
|
| 204 |
+
|
| 205 |
+
Variables:
|
| 206 |
+
t in [2.0, 9.0]
|
| 207 |
+
snap in [0.0, 2.0]
|
| 208 |
+
|
| 209 |
+
Constraints:
|
| 210 |
+
EQ weight=10: snap - (sin(t * 3.14159265)**2 + sin(85.0 / t * 3.14159265)**2)
|
| 211 |
+
GEQ: t - 2.0
|
| 212 |
+
LEQ: t - 9.0
|
| 213 |
+
|
| 214 |
+
Anchor: snap = 0 (tolerance 0.01).
|
| 215 |
+
Observation: snap = 0.0.
|
| 216 |
+
"""
|
| 217 |
+
}
|