Spaces:
Sleeping
Sleeping
Update practicality_axioms.py
Browse files- practicality_axioms.py +344 -151
practicality_axioms.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
| 1 |
import math
|
| 2 |
import random
|
| 3 |
import torch
|
|
|
|
| 4 |
from typing import Dict, List, Tuple, Set, Optional, Any
|
| 5 |
from dataclasses import dataclass, field
|
| 6 |
-
|
| 7 |
|
| 8 |
@dataclass
|
| 9 |
class Hypothesis:
|
|
@@ -20,172 +21,364 @@ class Hypothesis:
|
|
| 20 |
|
| 21 |
@dataclass
|
| 22 |
class L9Certificate:
|
| 23 |
-
residual_ce: float; dominant_vars: List[str]; dominant_exprs: List[str]; tension_class: str="unknown"
|
| 24 |
|
| 25 |
@dataclass
|
| 26 |
class Baton:
|
| 27 |
-
binding: Dict[str, float]; ce: float; ray_id: str=""; depth: int=0; l9: Optional[L9Certificate]=None
|
| 28 |
|
| 29 |
@dataclass
|
| 30 |
class AxiomRay:
|
| 31 |
-
sequence: List[str]; ray_id: str; ray_type: str="seed"
|
| 32 |
-
depth: int=0; parent_id: Optional[str]=None
|
| 33 |
-
baton: Optional[Baton]=None; ce_prior: float=float('inf')
|
| 34 |
@property
|
| 35 |
def name(self) -> str: return "β".join(a[:3] for a in self.sequence)
|
| 36 |
def extend(self, axiom, rtype, new_prior=float('inf')):
|
| 37 |
return AxiomRay(sequence=self.sequence+[axiom], ray_id=f"{self.ray_id}+{axiom[:3]}",
|
| 38 |
ray_type=rtype, depth=self.depth+1, parent_id=self.ray_id, ce_prior=new_prior)
|
| 39 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
class Axiom:
|
| 41 |
-
CONTINUOUS="CONTINUOUS"; DISCRETE="DISCRETE"; QUADRATIC="QUADRATIC"
|
| 42 |
-
BILINEAR="BILINEAR"; METRIC="METRIC"; SYMMETRIC="SYMMETRIC"
|
| 43 |
-
MUTABLE="MUTABLE"; EXTREMAL="EXTREMAL"; ENTROPY="ENTROPY"
|
| 44 |
-
ATOMIC="ATOMIC"; PARSIMONY="PARSIMONY"; DUALITY="DUALITY"
|
|
|
|
| 45 |
|
| 46 |
ALL_AXIOMS = [getattr(Axiom, a) for a in dir(Axiom) if not a.startswith("__")]
|
| 47 |
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
else:
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
for step in range(steps):
|
| 146 |
-
optimizer.zero_grad()
|
| 147 |
-
step_ratio = min(1.0, step / (steps * 0.8))
|
| 148 |
-
X_orig = _param_to_orig(P)
|
| 149 |
-
ce = problem.tensor_energy(X_orig, step_ratio, is_optimizing=True)
|
| 150 |
-
if isinstance(ce, torch.Tensor) and (ce < SOLVE_THRESHOLD).all() and step_ratio == 1.0: break
|
| 151 |
-
ce.sum().backward()
|
| 152 |
-
with torch.no_grad():
|
| 153 |
-
P.grad.clamp_(-10.0, 10.0)
|
| 154 |
-
P.grad *= mask
|
| 155 |
-
optimizer.step()
|
| 156 |
-
P.data = torch.where(mask == 0.0, target, P.data)
|
| 157 |
-
margin = 0.1 * (1.0 - step_ratio)
|
| 158 |
-
lo_m = lo_param_t - (hi_param_t - lo_param_t) * margin
|
| 159 |
-
hi_m = hi_param_t + (hi_param_t - lo_param_t) * margin
|
| 160 |
-
P.data = torch.clamp(P.data, lo_m.unsqueeze(0), hi_m.unsqueeze(0))
|
| 161 |
-
|
| 162 |
-
X_orig_final = _param_to_orig(P)
|
| 163 |
-
final_ce = problem.tensor_energy(X_orig_final, 1.0, is_optimizing=False).view(-1)
|
| 164 |
-
ce_vals = final_ce.detach().cpu().numpy()
|
| 165 |
-
X_vals = X_orig_final.detach().cpu().numpy()
|
| 166 |
-
|
| 167 |
-
for b_idx, orig_idx in enumerate(solve_indices):
|
| 168 |
-
final_b = {problem.variables[j]: float(X_vals[b_idx, j]) for j in range(V)}
|
| 169 |
-
results[orig_idx] = (final_b, float(ce_vals[b_idx]), [], "systemic")
|
| 170 |
-
|
| 171 |
-
return [r for r in results if r is not None]
|
| 172 |
-
|
| 173 |
-
def _mprt_sample(problem: Problem, N: int):
|
| 174 |
-
var_list = problem.variables; V = len(var_list)
|
| 175 |
-
lo_t = torch.tensor([_c15(problem.bounds.get(v, (-10.0, 10.0))[0]) for v in var_list], device=DEVICE, dtype=torch.float32)
|
| 176 |
-
hi_t = torch.tensor([_c15(problem.bounds.get(v, (-10.0, 10.0))[1]) for v in var_list], device=DEVICE, dtype=torch.float32)
|
| 177 |
-
for i in range(V):
|
| 178 |
-
if lo_t[i] >= hi_t[i]: m = (lo_t[i]+hi_t[i])/2; lo_t[i] = m - 1e-6; hi_t[i] = m + 1e-6
|
| 179 |
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 187 |
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import math
|
| 2 |
import random
|
| 3 |
import torch
|
| 4 |
+
import itertools
|
| 5 |
from typing import Dict, List, Tuple, Set, Optional, Any
|
| 6 |
from dataclasses import dataclass, field
|
| 7 |
+
import practicality_core as core
|
| 8 |
|
| 9 |
@dataclass
|
| 10 |
class Hypothesis:
|
|
|
|
| 21 |
|
| 22 |
@dataclass
|
| 23 |
class L9Certificate:
|
| 24 |
+
residual_ce: float; dominant_vars: List[str]; dominant_exprs: List[str]; tension_class: str = "unknown"
|
| 25 |
|
| 26 |
@dataclass
|
| 27 |
class Baton:
|
| 28 |
+
binding: Dict[str, float]; ce: float; ray_id: str = ""; depth: int = 0; l9: Optional[L9Certificate] = None
|
| 29 |
|
| 30 |
@dataclass
|
| 31 |
class AxiomRay:
|
| 32 |
+
sequence: List[str]; ray_id: str; ray_type: str = "seed"
|
| 33 |
+
depth: int = 0; parent_id: Optional[str] = None
|
| 34 |
+
baton: Optional[Baton] = None; ce_prior: float = float('inf')
|
| 35 |
@property
|
| 36 |
def name(self) -> str: return "β".join(a[:3] for a in self.sequence)
|
| 37 |
def extend(self, axiom, rtype, new_prior=float('inf')):
|
| 38 |
return AxiomRay(sequence=self.sequence+[axiom], ray_id=f"{self.ray_id}+{axiom[:3]}",
|
| 39 |
ray_type=rtype, depth=self.depth+1, parent_id=self.ray_id, ce_prior=new_prior)
|
| 40 |
|
| 41 |
+
@dataclass
|
| 42 |
+
class HypothesisTrace:
|
| 43 |
+
hid: str; round_idx: int; ray_name: str; ray_type: str
|
| 44 |
+
ce_before: float; ce_after: float; survived: bool; elapsed_ms: float
|
| 45 |
+
g1_pass: bool = False; g2_pass: bool = False
|
| 46 |
+
binding: Dict[str, float] = field(default_factory=dict)
|
| 47 |
+
invariant_results: Dict[str, Tuple[float, bool]] = field(default_factory=dict)
|
| 48 |
+
tension_class: str = "unknown"; depth: int = 0
|
| 49 |
+
|
| 50 |
+
@dataclass
|
| 51 |
+
class StructuralModel:
|
| 52 |
+
best_binding: Dict[str, float]; best_ce: float; best_ray: str = "β"
|
| 53 |
+
failure_patterns: Set[str] = field(default_factory=set)
|
| 54 |
+
resonant_pairs: List[Tuple[str, str]] = field(default_factory=list)
|
| 55 |
+
|
| 56 |
class Axiom:
|
| 57 |
+
CONTINUOUS = "CONTINUOUS"; DISCRETE = "DISCRETE"; QUADRATIC = "QUADRATIC"
|
| 58 |
+
BILINEAR = "BILINEAR"; METRIC = "METRIC"; SYMMETRIC = "SYMMETRIC"
|
| 59 |
+
MUTABLE = "MUTABLE"; EXTREMAL = "EXTREMAL"; ENTROPY = "ENTROPY"
|
| 60 |
+
ATOMIC = "ATOMIC"; PARSIMONY = "PARSIMONY"; DUALITY = "DUALITY"
|
| 61 |
+
MONOTONE_PRODUCT = "MONOTONE_PRODUCT"; ORDERED = "ORDERED"
|
| 62 |
|
| 63 |
ALL_AXIOMS = [getattr(Axiom, a) for a in dir(Axiom) if not a.startswith("__")]
|
| 64 |
|
| 65 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 66 |
+
# AXIOM HYPOTHESIS CONSTRUCTORS
|
| 67 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 68 |
+
def _make_hyp(hid, binding, h_type, claim, derivation, pinned, free, conf, is_fd=False):
|
| 69 |
+
return Hypothesis(hid=hid, binding=binding, h_type=h_type, claim=claim,
|
| 70 |
+
derivation=derivation, pinned_vars=pinned, free_vars=free,
|
| 71 |
+
confidence=conf, is_fully_determined=is_fd)
|
| 72 |
+
|
| 73 |
+
def _hyp_continuous(p, bt, a, m): return [_make_hyp("cnt", bt.binding, "continuous", "Manifold", [], {}, p.variables, 0.45)]
|
| 74 |
+
def _hyp_discrete(p, bt, a, m):
|
| 75 |
+
b = dict(bt.binding); pinned = {}
|
| 76 |
+
box = {v: core.IV(b.get(v, 0)-max(0.05*(p.bounds[v][1]-p.bounds[v][0]), 1e-4),
|
| 77 |
+
b.get(v, 0)+max(0.05*(p.bounds[v][1]-p.bounds[v][0]), 1e-4)) for v in p.variables if v in p.bounds}
|
| 78 |
+
cont = core._hc4(box, p.compiled_constraints)
|
| 79 |
+
if cont:
|
| 80 |
+
for v, iv in cont.items():
|
| 81 |
+
b[v] = iv.mid()
|
| 82 |
+
if iv.width() < 1e-2: pinned[v] = b[v]
|
| 83 |
+
return [_make_hyp("dis", b, "discrete", "TopoScope", [], pinned, p.variables, 0.80)]
|
| 84 |
+
def _hyp_quadratic(p, bt, a, m):
|
| 85 |
+
hyps = []; b = dict(bt.binding)
|
| 86 |
+
if bt.l9 and bt.l9.dominant_vars:
|
| 87 |
+
for v in bt.l9.dominant_vars[:2]: hyps.append(_make_hyp(f"qua_{v}", b, "quadratic", f"Root({v})", [], {v: b[v]}, p.variables, 0.70))
|
| 88 |
+
return hyps if hyps else [_make_hyp("qua", b, "quadratic", "Root", [], {}, p.variables, 0.55)]
|
| 89 |
+
def _hyp_bilinear(p, bt, a, m):
|
| 90 |
+
hyps = []; b = dict(bt.binding)
|
| 91 |
+
for vi, vj in p.bilinear_pairs:
|
| 92 |
+
lo_i, hi_i = p.bounds.get(vi, (0, 10)); lo_j, hi_j = p.bounds.get(vj, (0, 10))
|
| 93 |
+
product = abs(b.get(vi, 1)*b.get(vj, 1))
|
| 94 |
+
if product <= 0: continue
|
| 95 |
+
gm = math.sqrt(product)
|
| 96 |
+
if lo_i <= gm <= hi_i and lo_j <= gm <= hi_j:
|
| 97 |
+
nb = dict(b); nb[vi] = nb[vj] = gm
|
| 98 |
+
hyps.append(_make_hyp(f"bil_eq_{vi}_{vj}", nb, "bilinear", "GeoMean", ["bilinear"], {vi: gm, vj: gm}, [v for v in p.variables if v not in [vi, vj]], 0.70))
|
| 99 |
+
return hyps
|
| 100 |
+
def _hyp_monotone_product(p, bt, a, m):
|
| 101 |
+
hyps = []; b = dict(bt.binding)
|
| 102 |
+
if not p.monotone_targets: return hyps
|
| 103 |
+
try: tb = core._global_hc4_tighten_bounds(p)
|
| 104 |
+
except: tb = dict(p.bounds)
|
| 105 |
+
for v_small, v_large, k in p.monotone_targets:
|
| 106 |
+
lo_s, hi_s = p.bounds.get(v_small, (-1e9, 1e9)); lo_l, hi_l = p.bounds.get(v_large, (-1e9, 1e9))
|
| 107 |
+
for ratio in [0.25, 0.5, 0.707, 0.9]:
|
| 108 |
+
vs_sq = k*ratio
|
| 109 |
+
if vs_sq <= 0: continue
|
| 110 |
+
vs = math.sqrt(vs_sq); vl = k/vs
|
| 111 |
+
if not(lo_s <= vs <= hi_s and lo_l <= vl <= hi_l and vs <= vl): continue
|
| 112 |
+
nb = dict(b); nb[v_small] = vs; nb[v_large] = vl
|
| 113 |
+
box = {u: core.IV(*tb.get(u, p.bounds.get(u, (-10, 10)))) for u in p.variables}
|
| 114 |
+
box[v_small] = core.IV(vs-1e-6, vs+1e-6); box[v_large] = core.IV(vl-1e-6, vl+1e-6)
|
| 115 |
+
cont = core._hc4(box, p.compiled_constraints); pinned = {v_small: vs, v_large: vl}
|
| 116 |
+
if cont:
|
| 117 |
+
for u, iv in cont.items():
|
| 118 |
+
if u in p.bounds: nb[u] = iv.mid()
|
| 119 |
+
pinned = {u: nb[u] for u in p.variables if cont[u].width() < 1e-2}
|
| 120 |
+
hyps.append(_make_hyp(f"mpr_{v_small}_{v_large}_r{int(ratio*100)}", nb, "monotone_product",
|
| 121 |
+
"MonoProd", ["ordered", "hc4"], pinned, [u for u in p.variables if u not in pinned], 0.88))
|
| 122 |
+
return hyps
|
| 123 |
+
def _hyp_metric(p, bt, a, m):
|
| 124 |
+
hyps = []; b = dict(bt.binding)
|
| 125 |
+
for mc in p.compiled_constraints:
|
| 126 |
+
if mc.kind != "equality" or not mc.parsed or not mc.parsed.is_Add: continue
|
| 127 |
+
sq_terms = [t for t in mc.parsed.args if t.is_Pow and t.args[1] == 2]
|
| 128 |
+
if len(sq_terms) < 1: continue
|
| 129 |
+
vars_in = [str(s) for s in mc.parsed.free_symbols if str(s) in p.variables]
|
| 130 |
+
if not vars_in: continue
|
| 131 |
+
const_terms = [float(t) for t in mc.parsed.args if t.is_Number]
|
| 132 |
+
if not const_terms: continue
|
| 133 |
+
target = -const_terms[0]
|
| 134 |
+
if target <= 0: continue
|
| 135 |
+
curr_val = sum(b.get(v, 0)**2 for v in vars_in)
|
| 136 |
+
if curr_val < 1e-8: continue
|
| 137 |
+
scale = math.sqrt(target/curr_val); nb = dict(b)
|
| 138 |
+
for v in vars_in:
|
| 139 |
+
lo, hi = p.bounds.get(v, (-10, 10)); nb[v] = max(lo, min(hi, b.get(v, 0)*scale))
|
| 140 |
+
hyps.append(_make_hyp(f"met_{hash(mc.expr_str)%9999}", nb, "metric", "RadialProject", ["metric", mc.expr_str], {}, list(p.variables), 0.75))
|
| 141 |
+
return hyps
|
| 142 |
+
def _hyp_symmetric(p, bt, a, m):
|
| 143 |
+
hyps = []; b = dict(bt.binding)
|
| 144 |
+
vl = p.variables
|
| 145 |
+
for i in range(min(len(vl), 8)):
|
| 146 |
+
for j in range(i+1, min(len(vl), 8)):
|
| 147 |
+
vi, vj = vl[i], vl[j]
|
| 148 |
+
lo_i, hi_i = p.bounds.get(vi, (-1e9, 1e9)); lo_j, hi_j = p.bounds.get(vj, (-1e9, 1e9))
|
| 149 |
+
bvi, bvj = b.get(vi, 0), b.get(vj, 0)
|
| 150 |
+
if lo_i <= bvj <= hi_i and lo_j <= bvi <= hi_j:
|
| 151 |
+
nb = dict(b); nb[vi], nb[vj] = bvj, bvi
|
| 152 |
+
hyps.append(_make_hyp(f"sym_{vi}_{vj}", nb, "symmetric", "Swap", ["symmetric"], {vi: nb[vi], vj: nb[vj]}, [v for v in p.variables if v not in [vi, vj]], 0.60))
|
| 153 |
+
return hyps[:5]
|
| 154 |
+
def _hyp_ordered(p, bt, a, m): return [_make_hyp("ord", bt.binding, "ordered", "OrderBal", [], {}, p.variables, 0.67)]
|
| 155 |
+
def _hyp_monotone(p, bt, a, m): return [_make_hyp("mon", bt.binding, "monotone", "MonoPath", [], {}, p.variables, 0.67)]
|
| 156 |
+
def _hyp_mutable(p, bt, a, m):
|
| 157 |
+
b = dict(bt.binding); pinned = {}
|
| 158 |
+
if bt.l9 and bt.l9.dominant_vars:
|
| 159 |
+
v = bt.l9.dominant_vars[0]; lo, hi = p.bounds.get(v, (-10, 10))
|
| 160 |
+
b[v] = max(lo, min(hi, b.get(v, 0)+random.gauss(0, (hi-lo)*0.05))); pinned = {v: b[v]}
|
| 161 |
+
return [_make_hyp("mut", b, "mutable", "Perturb", [], pinned, p.variables, 0.40)]
|
| 162 |
+
def _hyp_extremal(p, bt, a, m):
|
| 163 |
+
hyps = []; b = dict(bt.binding)
|
| 164 |
+
if bt.l9 and bt.l9.dominant_vars:
|
| 165 |
+
v = bt.l9.dominant_vars[0]; lo, hi = p.bounds.get(v, (0, 1))
|
| 166 |
+
for val in [lo, hi]: nb = dict(b); nb[v] = val; hyps.append(_make_hyp(f"ext_{v}_{val:.3f}", nb, "extremal", "PinBound", [], {v: val}, p.variables, 0.65))
|
| 167 |
+
return hyps
|
| 168 |
+
def _hyp_entropy(p, bt, a, m): return [_make_hyp("ent", {v: random.uniform(*p.bounds[v]) for v in p.variables}, "entropy", "MaxEnt", [], {}, p.variables, 0.48)]
|
| 169 |
+
def _hyp_atomic(p, bt, a, m):
|
| 170 |
+
hyps = []; b = dict(bt.binding)
|
| 171 |
+
for mc in p.compiled_constraints:
|
| 172 |
+
if not mc.projections: continue
|
| 173 |
+
for v, projs in mc.projections.items():
|
| 174 |
+
for proj in projs:
|
| 175 |
+
try:
|
| 176 |
+
val = float(proj["func"](*[b.get(s, 0.0) for s in proj["syms"]])); lo, hi = p.bounds.get(v, (-1e9, 1e9))
|
| 177 |
+
if math.isfinite(val) and lo <= val <= hi:
|
| 178 |
+
nb = dict(b); nb[v] = val; hyps.append(_make_hyp(f"ato_{v}", nb, "atomic", f"Solve({v})", [], {v: val}, p.variables, 0.70)); break
|
| 179 |
+
except: pass
|
| 180 |
+
if hyps: break
|
| 181 |
+
return hyps
|
| 182 |
+
def _hyp_parsimony(p, bt, a, m):
|
| 183 |
+
b = dict(bt.binding); nb = {}
|
| 184 |
+
max_val = max((abs(v) for v in b.values() if math.isfinite(v)), default=1.0)
|
| 185 |
+
for v in p.variables:
|
| 186 |
+
val = b[v]; lo, hi = p.bounds[v]
|
| 187 |
+
if not math.isfinite(val): nb[v] = 0.0; continue
|
| 188 |
+
if abs(val) < 0.1 * max_val and lo <= 0.0 <= hi: nb[v] = 0.0
|
| 189 |
else:
|
| 190 |
+
candidates = [round(val*m)/m for m in [1, 2, 4] if lo <= round(val*m)/m <= hi]
|
| 191 |
+
nb[v] = min(candidates, key=lambda c: abs(c-val)) if candidates else val
|
| 192 |
+
return [_make_hyp("par", nb, "parsimony", "Occam", [], {}, p.variables, 0.58)]
|
| 193 |
+
def _hyp_duality(p, bt, a, m): return [_make_hyp("dua", bt.binding, "duality", "Tight", [], {}, p.variables, 0.68)]
|
| 194 |
+
|
| 195 |
+
AXIOM_CONSTRUCTORS = {
|
| 196 |
+
Axiom.CONTINUOUS: _hyp_continuous, Axiom.DISCRETE: _hyp_discrete,
|
| 197 |
+
Axiom.QUADRATIC: _hyp_quadratic, Axiom.BILINEAR: _hyp_bilinear,
|
| 198 |
+
Axiom.METRIC: _hyp_metric, Axiom.SYMMETRIC: _hyp_symmetric,
|
| 199 |
+
Axiom.ORDERED: _hyp_ordered, Axiom.MONOTONE: _hyp_monotone,
|
| 200 |
+
Axiom.MUTABLE: _hyp_mutable, Axiom.EXTREMAL: _hyp_extremal,
|
| 201 |
+
Axiom.ENTROPY: _hyp_entropy, Axiom.ATOMIC: _hyp_atomic,
|
| 202 |
+
Axiom.PARSIMONY: _hyp_parsimony, Axiom.DUALITY: _hyp_duality,
|
| 203 |
+
Axiom.MONOTONE_PRODUCT: _hyp_monotone_product
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 207 |
+
# SEED & TEMPLATE SYSTEM
|
| 208 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 209 |
+
def compute_sketch(problem: core.Problem) -> Dict[str, Any]:
|
| 210 |
+
notes = []
|
| 211 |
+
affinities = {a: 1.0 for a in ALL_AXIOMS}
|
| 212 |
+
|
| 213 |
+
if problem.bilinear_pairs:
|
| 214 |
+
affinities[Axiom.BILINEAR] += 4.0
|
| 215 |
+
notes.append(f"Found bilinear pairs: {problem.bilinear_pairs}")
|
| 216 |
+
if problem.monotone_targets:
|
| 217 |
+
affinities[Axiom.MONOTONE_PRODUCT] += 5.0
|
| 218 |
+
notes.append("Boosted Monotone Product based on algebraic layout.")
|
| 219 |
+
if any(mc.kind == "equality" and mc.parsed is not None and mc.parsed.is_Add for mc in problem.compiled_constraints):
|
| 220 |
+
affinities[Axiom.METRIC] += 3.0
|
| 221 |
+
notes.append("System contains sum-of-squares style constraints.")
|
| 222 |
+
|
| 223 |
+
return {"notes": notes, "affinities": affinities}
|
| 224 |
+
|
| 225 |
+
def generate_templates(problem: core.Problem, sketch: Dict[str, Any]) -> List[Hypothesis]:
|
| 226 |
+
templates = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 227 |
|
| 228 |
+
# 1. INT Grid Sweep & Algebraic Propagation
|
| 229 |
+
tight_int_vars = [v for v in problem.variables if v in problem.int_vars
|
| 230 |
+
and math.isfinite(problem.bounds[v][0])
|
| 231 |
+
and problem.bounds[v][1] - problem.bounds[v][0] <= 100]
|
| 232 |
+
|
| 233 |
+
if tight_int_vars:
|
| 234 |
+
grids = []
|
| 235 |
+
for v in tight_int_vars:
|
| 236 |
+
lo = int(math.ceil(problem.bounds[v][0]))
|
| 237 |
+
hi = int(math.floor(problem.bounds[v][1]))
|
| 238 |
+
grids.append([(v, float(val)) for val in range(lo, hi+1)])
|
| 239 |
|
| 240 |
+
combos = list(itertools.product(*grids))[:1000] # Cap search width
|
| 241 |
+
for combo in combos:
|
| 242 |
+
pinned = dict(combo)
|
| 243 |
+
resolved, log = core.algebraic_propagate_pinned(problem, pinned)
|
| 244 |
+
templates.append(
|
| 245 |
+
Hypothesis(
|
| 246 |
+
hid=f"grid_{hash(str(combo))%100000}", binding=resolved,
|
| 247 |
+
h_type="int_grid", claim="Integer Grid Sweep", derivation=log,
|
| 248 |
+
pinned_vars=pinned, free_vars=[v for v in problem.variables if v not in resolved],
|
| 249 |
+
confidence=0.95, is_fully_determined=(len(resolved) == len(problem.variables))
|
| 250 |
+
)
|
| 251 |
+
)
|
| 252 |
+
|
| 253 |
+
# 2. Transcendental Space Sweep (Fallback)
|
| 254 |
+
trans_vars = [v for v in problem.variables if v not in tight_int_vars and math.isfinite(problem.bounds[v][0])]
|
| 255 |
+
if trans_vars and len(trans_vars) <= 2:
|
| 256 |
+
for v in trans_vars:
|
| 257 |
+
lo, hi = problem.bounds[v]
|
| 258 |
+
samples = [lo + (hi-lo)*r for r in [0.15, 0.5, 0.85]]
|
| 259 |
+
for s in samples:
|
| 260 |
+
pinned = {v: s}
|
| 261 |
+
resolved, log = core.algebraic_propagate_pinned(problem, pinned)
|
| 262 |
+
templates.append(
|
| 263 |
+
Hypothesis(
|
| 264 |
+
hid=f"trans_{v}_{int(s*100)%1000}", binding=resolved,
|
| 265 |
+
h_type="grid_sweep", claim="Transcendental Sweep", derivation=log,
|
| 266 |
+
pinned_vars=pinned, free_vars=[u for u in problem.variables if u not in resolved],
|
| 267 |
+
confidence=0.75
|
| 268 |
+
)
|
| 269 |
+
)
|
| 270 |
+
return templates
|
| 271 |
+
|
| 272 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 273 |
+
# THE DECOUPLED SEQUENCE RAY TRACER
|
| 274 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 275 |
+
class SequenceRayTracer:
|
| 276 |
+
def __init__(self, log_callback: Callable, update_state_callback: Callable):
|
| 277 |
+
self.log = log_callback
|
| 278 |
+
self.update_state = update_state_callback
|
| 279 |
+
self.bandit = UCB1BanditSeeder()
|
| 280 |
+
self.baton_registry = {}
|
| 281 |
+
|
| 282 |
+
def trace(self, problem: core.Problem, init_b: Dict[str, float], init_ce: float, templates: List[Hypothesis], max_rays: int = 1500) -> Tuple[Dict[str, float], float, List[HypothesisTrace], str]:
|
| 283 |
+
self.log(f"Initializing Axiom Ray Tracer on {problem.pid}...")
|
| 284 |
+
sketch = compute_sketch(problem)
|
| 285 |
+
for note in sketch["notes"]:
|
| 286 |
+
self.log(f" [Sketch] {note}")
|
| 287 |
+
|
| 288 |
+
best_ce = init_ce
|
| 289 |
+
best_binding = dict(init_b)
|
| 290 |
+
best_ray_name = "Zero-Shot"
|
| 291 |
+
traces = []
|
| 292 |
+
solved = False
|
| 293 |
+
|
| 294 |
+
# Build initial queue from verified templates
|
| 295 |
+
queues = {"core": []}
|
| 296 |
+
for idx, t in enumerate(templates):
|
| 297 |
+
binding = dict(init_b); binding.update(t.pinned_vars)
|
| 298 |
+
results = _batched_deduce_and_evaluate(problem, [t], steps=60)
|
| 299 |
+
if results:
|
| 300 |
+
final_b, final_ce, dom_vars, tc = results[0]
|
| 301 |
+
if final_ce < best_ce:
|
| 302 |
+
best_ce = final_ce
|
| 303 |
+
best_binding = dict(final_b)
|
| 304 |
+
best_ray_name = f"template:{t.h_type}"
|
| 305 |
+
|
| 306 |
+
traces.append(HypothesisTrace(
|
| 307 |
+
hid=t.hid, round_idx=idx, ray_name=f"template:{t.h_type}", ray_type="template",
|
| 308 |
+
ce_before=init_ce, ce_after=final_ce, survived=True, elapsed_ms=0.0,
|
| 309 |
+
g1_pass=(final_ce < core.SOLVE_THRESHOLD), binding=final_b, tension_class=tc
|
| 310 |
+
))
|
| 311 |
+
|
| 312 |
+
# Register batons for structural branch mapping
|
| 313 |
+
self.baton_registry[t.hid] = Baton(
|
| 314 |
+
binding=final_b, ce=final_ce, ray_id=t.hid,
|
| 315 |
+
l9=L9Certificate(final_ce, dom_vars, [], tc)
|
| 316 |
+
)
|
| 317 |
+
|
| 318 |
+
# Build seed rays based on UCB1 Bandit weights
|
| 319 |
+
affinities = sketch["affinities"]
|
| 320 |
+
seeds_scored = sorted([(affinities.get(a, 1.0), idx, a) for idx, a in enumerate(ALL_AXIOMS)], key=lambda x: -x[0])
|
| 321 |
+
seed_rays = [AxiomRay([a], f"S{idx}", "seed", ce_prior=best_ce) for _, idx, a in seeds_scored[:150]]
|
| 322 |
+
queues["core"].extend(seed_rays)
|
| 323 |
+
|
| 324 |
+
model = StructuralModel(best_binding, best_ce)
|
| 325 |
+
total_fired = 0
|
| 326 |
+
seed_baton = Baton(binding=best_binding, ce=best_ce, ray_id="SEED")
|
| 327 |
+
|
| 328 |
+
while total_fired < max_rays and queues["core"]:
|
| 329 |
+
batch_rays = queues["core"][:64]
|
| 330 |
+
queues["core"] = queues["core"][64:]
|
| 331 |
+
total_fired += len(batch_rays)
|
| 332 |
+
|
| 333 |
+
batch_hyps = []
|
| 334 |
+
for idx, ray in enumerate(batch_rays):
|
| 335 |
+
p_baton = ray.baton or seed_baton
|
| 336 |
+
constructor = AXIOM_CONSTRUCTORS.get(ray.sequence[-1])
|
| 337 |
+
constructed_hyps = constructor(problem, p_baton, list(self.baton_registry.values()), model) if constructor else []
|
| 338 |
+
if not constructed_hyps:
|
| 339 |
+
constructed_hyps = [_make_hyp("fb", p_baton.binding, "fallback", "Pass", [], {}, problem.variables, 0.1)]
|
| 340 |
+
batch_hyps.append((idx, constructed_hyps[0]))
|
| 341 |
+
|
| 342 |
+
eval_results = _batched_deduce_and_evaluate(problem, [h for _, h in batch_hyps], steps=80)
|
| 343 |
+
|
| 344 |
+
for (ray_idx, hyp), (final_b, final_ce, dom_vars, tension_class) in zip(batch_hyps, eval_results):
|
| 345 |
+
ray = batch_rays[ray_idx]
|
| 346 |
+
parent_ce = (ray.baton or seed_baton).ce
|
| 347 |
+
self.bandit.record_reward(ray.sequence[-1], parent_ce, final_ce)
|
| 348 |
+
|
| 349 |
+
improved = final_ce < best_ce
|
| 350 |
+
passed_pruning = final_ce < parent_ce * 0.98
|
| 351 |
+
|
| 352 |
+
if improved:
|
| 353 |
+
best_ce = final_ce
|
| 354 |
+
best_binding = dict(final_b)
|
| 355 |
+
best_ray_name = ray.name
|
| 356 |
+
model.best_binding = dict(final_b)
|
| 357 |
+
model.best_ce = final_ce
|
| 358 |
+
model.best_ray = ray.name
|
| 359 |
+
self.update_state(best_ce=best_ce, total_fired=total_fired, best_ray=best_ray_name)
|
| 360 |
+
|
| 361 |
+
if final_ce < core.SOLVE_THRESHOLD:
|
| 362 |
+
solved = True
|
| 363 |
+
|
| 364 |
+
out_baton = Baton(
|
| 365 |
+
binding=final_b, ce=final_ce, ray_id=ray.ray_id,
|
| 366 |
+
l9=L9Certificate(final_ce, dom_vars, [], tension_class)
|
| 367 |
+
)
|
| 368 |
+
|
| 369 |
+
if final_ce < max(2.0, best_ce * 1.2):
|
| 370 |
+
self.baton_registry[ray.ray_id] = out_baton
|
| 371 |
+
|
| 372 |
+
if (passed_pruning or improved or ray.depth < 1) and ray.depth < 8:
|
| 373 |
+
remaining_axioms = [a for a in ALL_AXIOMS if a not in ray.sequence]
|
| 374 |
+
queues["core"].extend(self.bandit.intelligent_branch(ray, out_baton, remaining_axioms, branch_width=6))
|
| 375 |
+
|
| 376 |
+
if solved:
|
| 377 |
+
self.log("Exact structural resonance found!")
|
| 378 |
+
break
|
| 379 |
+
|
| 380 |
+
return best_binding, best_ce, traces, best_ray_name
|
| 381 |
+
|
| 382 |
+
# Re-expose batch optimization so axioms engine remains self-contained
|
| 383 |
+
_batched_deduce_and_evaluate = axioms._batched_deduce_and_evaluate
|
| 384 |
+
_mprt_sample = axioms._mprt_sample
|