everydaytok commited on
Commit
9b549b2
Β·
verified Β·
1 Parent(s): d204951

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +1511 -1
app.py CHANGED
@@ -1 +1,1511 @@
1
- h
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ PRACTICALITY UNIFIED SERVER 22.1 β€” LATEST GEMINI SDK & STRICT BOUNDARY
4
+ ═══════════════════════════════════════════════════════════════════
5
+ ARCHITECTURE:
6
+ User Text β†’ [GEMINI 3.1 ENCODER] β†’ AXLProblemDef (Strict Math Representation)
7
+ β†’ [WISDOM LAYER] β†’ Hypothesis Engine (CreativeSeeder) generates Axiom rays
8
+ β†’ PyTorch Masked Deduction + Oracle solves topology
9
+ β†’ Verification (Gate 1 & Gate 2)
10
+ β†’ [Self-Correcting Feedback Loop if G2 fails]
11
+ β†’ [GEMINI 3.1 COLLAPSER] β†’ Domain-language explanation
12
+
13
+ LATEST UPDATES (22.1):
14
+ - Migrated to the new `google-genai` SDK.
15
+ - Implemented `gemini-3.1-pro-preview` with `ThinkingConfig` for maximum semantic routing.
16
+ - Top-level API key configuration for rapid deployment.
17
+ """
18
+
19
+ import os, time, random, math, threading, warnings, queue, json, textwrap
20
+ from functools import reduce
21
+ from typing import Optional, Callable, List, Dict, Tuple, Any, Set
22
+ from dataclasses import dataclass, field
23
+ from collections import defaultdict, deque
24
+
25
+ import numpy as np
26
+ import sympy as sp
27
+ from sympy.parsing.sympy_parser import parse_expr
28
+ import torch
29
+ import gradio as gr
30
+
31
+ # ── NEW GOOGLE GENAI SDK ──────────────────────────────────────────
32
+ from google import genai
33
+ from google.genai import types
34
+
35
+ # ══════════════════════════════════════════════════════════════════
36
+ # AI CONFIGURATION (Set your key here!)
37
+ # ══════════════════════════════════════════════════════════════════
38
+ DEFAULT_GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY", "")
39
+ GEMINI_MODEL = "gemini-3.1-pro-preview"
40
+
41
+ warnings.filterwarnings("ignore")
42
+ USE_GPU = torch.cuda.is_available()
43
+ DEVICE = torch.device("cuda" if USE_GPU else "cpu")
44
+ print(f"[UNIFIED SERVER] Compute: {DEVICE.type.upper()} | System 22.1 (GenAI SDK + Neuro-Symbolic Boundary)")
45
+
46
+ # ══════════════════════════════════════════════════════════════════════
47
+ # SECTION 1: CONSTANTS
48
+ # ══════════════════════════════════════════════════════════════════════
49
+ SOLVE_THRESHOLD = 0.05
50
+ VERIFY_G1_THRESHOLD = 0.08
51
+ VERIFY_G2_TOLERANCE = 1e-3
52
+ DEDUCE_ADAM_STEPS = 25
53
+ N_MPRT_EXPLORE = 1000
54
+ PROJECTION_CACHE: Dict = {}
55
+
56
+ MAX_TOTAL_RAYS = 1_500_000
57
+ RAY_BATCH_SIZE = 12288
58
+ CE_EARN_RATIO = 0.90
59
+ SCOUT_CE_THRESHOLD = 0.5
60
+ BRANCH_WIDTH = 12
61
+ BRANCH_WIDTH_SYSTEMIC = 16
62
+ BATON_REGISTRY_THRESHOLD = 2.0
63
+ BATON_REGISTRY_MAX = 5000
64
+
65
+ # ══════════════════════════════════════════════════════════════════════
66
+ # SECTION 2: INTERVAL ARITHMETIC (HC4)
67
+ # ══════════════════════════════════════════════════════════════════════
68
+ class IV:
69
+ __slots__ = ("lo","hi")
70
+ def __init__(self,lo,hi): self.lo=float(lo); self.hi=float(hi)
71
+ def __add__(self,o): return IV(self.lo+o,self.hi+o) if isinstance(o,(int,float)) else IV(self.lo+o.lo,self.hi+o.hi)
72
+ __radd__=__add__
73
+ def __sub__(self,o): return IV(self.lo-o,self.hi-o) if isinstance(o,(int,float)) else IV(self.lo-o.hi,self.hi-o.lo)
74
+ def __rsub__(self,o): return IV(o-self.hi,o-self.lo) if isinstance(o,(int,float)) else o.__sub__(self)
75
+ def __mul__(self,o):
76
+ if isinstance(o,(int,float)): a,b=self.lo*o,self.hi*o; return IV(min(a,b),max(a,b))
77
+ p=(self.lo*o.lo,self.lo*o.hi,self.hi*o.lo,self.hi*o.hi); return IV(min(p),max(p))
78
+ __rmul__=__mul__
79
+ def __truediv__(self,o):
80
+ if isinstance(o,(int,float)):
81
+ if abs(o)<1e-15: return IV(-1e18,1e18)
82
+ a,b=self.lo/o,self.hi/o; return IV(min(a,b),max(a,b))
83
+ if o.lo<=0<=o.hi: return IV(-1e18,1e18)
84
+ return self*IV(1.0/o.hi,1.0/o.lo)
85
+ def __rtruediv__(self,o):
86
+ if self.lo<=0<=self.hi: return IV(-1e18,1e18)
87
+ return IV(o/self.hi,o/self.lo) if o>=0 else IV(o/self.lo,o/self.hi)
88
+ def __neg__(self): return IV(-self.hi,-self.lo)
89
+ def __pow__(self,n):
90
+ if isinstance(n,int):
91
+ if n==0: return IV(1.0,1.0)
92
+ if n%2==0:
93
+ if self.lo>=0: return IV(self.lo**n,self.hi**n)
94
+ if self.hi<=0: return IV(self.hi**n,self.lo**n)
95
+ return IV(0.0,max(abs(self.lo)**n,abs(self.hi)**n))
96
+ return IV(self.lo**n if self.lo>=0 else -((-self.lo)**n),
97
+ self.hi**n if self.hi>=0 else -((-self.hi)**n))
98
+ if self.lo<0: return IV(0.0,max(abs(self.lo)**n,self.hi**n))
99
+ return IV(self.lo**n,self.hi**n)
100
+ def contains_zero(self): return self.lo<=0.0<=self.hi
101
+ def width(self): return max(0.0,self.hi-self.lo)
102
+ def mid(self): return (self.lo+self.hi)*0.5
103
+
104
+ def compile_iv(expr,variables):
105
+ def _c(e):
106
+ if e.is_Number: v=float(e); return lambda box,_v=v: IV(_v,_v)
107
+ if e.is_Symbol: n=str(e); return lambda box,_n=n: box.get(_n,IV(-1e18,1e18))
108
+ if e.is_Add: fs=[_c(a) for a in e.args]; return lambda box,_fs=fs: reduce(lambda a,b:a+b,(_f(box) for _f in _fs))
109
+ if e.is_Mul: fs=[_c(a) for a in e.args]; return lambda box,_fs=fs: reduce(lambda a,b:a*b,(_f(box) for _f in _fs))
110
+ if e.is_Pow:
111
+ bc=_c(e.args[0]); ex=e.args[1]
112
+ if ex.is_Number: return lambda box,_bc=bc,_ex=float(ex): _bc(box)**_ex
113
+ exc=_c(ex); return lambda box,_bc=bc,_exc=exc: _bc(box)**_exc(box).mid()
114
+ return lambda box: IV(-1e18,1e18)
115
+ return _c(expr)
116
+
117
+ def _hc4(box,constraints):
118
+ cur=dict(box)
119
+ for mc in constraints:
120
+ if getattr(mc,'weight',1.0)==0.0: continue
121
+ if mc.kind=="or_eq":
122
+ valid=False
123
+ for bmc in mc.branches:
124
+ if bmc.fast_iv is None: valid=True; break
125
+ try:
126
+ if bmc.fast_iv(cur).contains_zero(): valid=True; break
127
+ except: valid=True; break
128
+ if not valid: return None
129
+ else:
130
+ if mc.fast_iv is None: continue
131
+ try:
132
+ riv=mc.fast_iv(cur)
133
+ if ((mc.kind=="equality" and not riv.contains_zero()) or
134
+ (mc.kind=="inequality" and ((mc.direction=="geq" and riv.hi<-1e-10) or
135
+ (mc.direction=="leq" and riv.lo>1e-10)))): return None
136
+ except: pass
137
+ return cur
138
+
139
+ def _global_hc4_tighten_bounds(problem):
140
+ gb={v:IV(lo,hi) for v,(lo,hi) in problem.bounds.items()}
141
+ c=_hc4(gb,problem.compiled_constraints)
142
+ if c is None: return dict(problem.bounds)
143
+ return {v:(max(problem.bounds[v][0],c[v].lo),min(problem.bounds[v][1],c[v].hi))
144
+ for v in problem.bounds if v in c}
145
+
146
+ # ══════════════════════════════════════════════════════════════════════
147
+ # SECTION 3: PSL PARSER & PRE-COMPILATION
148
+ # ══════════════════════════════════════════════════════════════════════
149
+ @dataclass
150
+ class PSLVar: name:str; lo:float; hi:float
151
+ @dataclass
152
+ class PSLConstraint: kind:str; expr:str; scope:str="root"; weight:float=1.0; branches:List[str]=field(default_factory=list)
153
+ @dataclass
154
+ class PSLScope: name:str; order:int=0; depends:List[str]=field(default_factory=list); vars:List[PSLVar]=field(default_factory=list); links:List[str]=field(default_factory=list); constraints:List[PSLConstraint]=field(default_factory=list)
155
+ @dataclass
156
+ class PSLProgram: name:str; vars:List[PSLVar]; constraints:List[PSLConstraint]; scopes:List[PSLScope]; scope_order:List[str]; annotations:Dict[str,str]=field(default_factory=dict)
157
+
158
+ def parse_psl(text:str) -> PSLProgram:
159
+ lines=[l.strip() for l in text.strip().split('\n') if l.strip() and not l.strip().startswith('#')]
160
+ prog=PSLProgram("unnamed",[],[],[],[]); i=0
161
+ def advance(): nonlocal i; l=lines[i]; i+=1; return l
162
+ def peek(): return lines[i] if i<len(lines) else ""
163
+ def pvar(rest): p=rest.split(); return PSLVar(p[0],float(p[1]),float(p[2])) if len(p)>=3 else None
164
+ def pw(rest): return (rest.split('WEIGHT')[0].strip(),float(rest.split('WEIGHT')[1].split()[0])) if 'WEIGHT' in rest else (rest.strip(),1.0)
165
+ def parse_scope_body(sname,order=0,depends=None):
166
+ svars,scons,links=[],[],[]
167
+ while i<len(lines):
168
+ l=peek(); tk=l.split(None,1); kw=tk[0].upper() if tk else ""; rest=tk[1].strip() if len(tk)>1 else ""
169
+ if kw in ("END","END SCOPE"): advance(); break
170
+ advance()
171
+ if kw=="VAR": v=pvar(rest); svars.append(v) if v else None
172
+ elif kw=="LINK": links.extend(rest.split())
173
+ elif kw in ("EQ","GEQ","LEQ"): expr,wt=pw(rest); scons.append(PSLConstraint(kw.lower(),expr,scope=sname,weight=wt))
174
+ elif kw=="CONSERVE": expr,_=pw(rest); scons.append(PSLConstraint("eq",expr,scope=sname,weight=10.0))
175
+ elif kw=="BRANCH":
176
+ opts=[]
177
+ while i<len(lines) and not peek().upper().startswith("END"):
178
+ bl=advance(); opts.append(bl.split(None,1)[1].strip()) if bl.upper().startswith("OPTION") else None
179
+ advance(); scons.append(PSLConstraint("or_eq","",scope=sname,weight=1.0,branches=opts))
180
+ return PSLScope(sname,order,depends or [],svars,links,scons)
181
+ while i<len(lines):
182
+ l=advance(); tk=l.split(None,1); kw=tk[0].upper() if tk else ""; rest=tk[1].strip() if len(tk)>1 else ""
183
+ if kw=="SPACE": prog.name=rest.split()[0] if rest else "unnamed"
184
+ elif kw=="VAR": v=pvar(rest); prog.vars.append(v) if v else None
185
+ elif kw in ("EQ","GEQ","LEQ"): expr,wt=pw(rest); prog.constraints.append(PSLConstraint(kw.lower(),expr,weight=wt))
186
+ elif kw=="CONSERVE": expr,_=pw(rest); prog.constraints.append(PSLConstraint("eq",expr,weight=10.0))
187
+ elif kw=="BRANCH":
188
+ opts=[]
189
+ while i<len(lines) and not peek().upper().startswith("END"):
190
+ bl=advance(); opts.append(bl.split(None,1)[1].strip()) if bl.upper().startswith("OPTION") else None
191
+ advance(); prog.constraints.append(PSLConstraint("or_eq","",weight=1.0,branches=opts))
192
+ elif kw=="SCOPE":
193
+ parts=rest.split(); sname=parts[0] if parts else f"s{len(prog.scopes)}"; order=0; depends=[]
194
+ if "ORDER" in rest.upper():
195
+ try: order=int(rest[rest.upper().index("ORDER"):].split()[1])
196
+ except: pass
197
+ if "DEPENDS" in rest.upper(): depends=rest[rest.upper().index("DEPENDS"):].split()[1:]
198
+ sc=parse_scope_body(sname,order,depends); prog.scopes.append(sc); prog.scope_order.append(sname)
199
+ prog.scopes.sort(key=lambda s:s.order); prog.scope_order=[s.name for s in prog.scopes]
200
+ return prog
201
+
202
+ @dataclass
203
+ class ExpandedProblem:
204
+ variables:List[str]; bounds:Dict[str,Tuple[float,float]]; constraints:List['Constraint']
205
+ scope_groups:Dict[str,List[int]]; scope_vars:Dict[str,List[str]]; scope_order:List[str]
206
+
207
+ def expand_psl(prog:PSLProgram) -> ExpandedProblem:
208
+ variables=[]; bounds={}; constraints=[]; scope_groups=defaultdict(list); scope_vars=defaultdict(list)
209
+ def add_var(name,lo,hi,scope="root"):
210
+ if name not in bounds: variables.append(name); bounds[name]=(lo,hi)
211
+ if scope!="root" and name not in scope_vars[scope]: scope_vars[scope].append(name)
212
+ def add_con(kind,expr,direction,scope="root",weight=1.0,branches=None):
213
+ constraints.append(Constraint(kind,expr,direction,weight,scope,branches or []))
214
+ scope_groups[scope].append(len(constraints)-1)
215
+ try:
216
+ syms={v:sp.Symbol(v) for v in variables}
217
+ parsed=parse_expr(expr,local_dict=syms) if kind!="or_eq" else None
218
+ if parsed:
219
+ for v in variables:
220
+ if sp.Symbol(v) in parsed.free_symbols and scope!="root" and v not in scope_vars[scope]:
221
+ scope_vars[scope].append(v)
222
+ except: pass
223
+ for v in prog.vars: add_var(v.name,v.lo,v.hi)
224
+ for c in prog.constraints:
225
+ add_con("or_eq" if c.kind=="or_eq" else "equality" if c.kind=="eq" else "inequality",
226
+ c.expr,c.kind,"root",c.weight,c.branches)
227
+ for sc in prog.scopes:
228
+ for v in sc.vars: add_var(v.name,v.lo,v.hi,sc.name)
229
+ for c in sc.constraints:
230
+ add_con("or_eq" if c.kind=="or_eq" else "equality" if c.kind=="eq" else "inequality",
231
+ c.expr,c.kind,sc.name,c.weight,c.branches)
232
+ return ExpandedProblem(variables,bounds,constraints,dict(scope_groups),dict(scope_vars),prog.scope_order)
233
+
234
+ @dataclass
235
+ class AXLInvariant:
236
+ name:str; expr:str; tolerance:float=VERIFY_G2_TOLERANCE; mode:str="eq"
237
+ compiled_func: Optional[Callable] = field(default=None, repr=False)
238
+ syms_used: List[str] = field(default_factory=list)
239
+
240
+ def compile(self, variables: List[str]):
241
+ syms = {v: sp.Symbol(v) for v in variables}
242
+ parsed = parse_expr(self.expr, local_dict=syms)
243
+ self.syms_used = [str(s) for s in parsed.free_symbols if str(s) in variables]
244
+ self.compiled_func = sp.lambdify([sp.Symbol(v) for v in self.syms_used], parsed, modules="math")
245
+
246
+ @dataclass
247
+ class AXLProblemDef:
248
+ name:str; description:str; axioms:Set[str]; variables:List[Dict]; constraints:List[Dict]
249
+ scopes:List[Dict]; anchors:List[AXLInvariant]; observations:Dict[str,float]=field(default_factory=dict)
250
+
251
+ class AXLtoPSLCompiler:
252
+ def compile(self,prob:AXLProblemDef) -> str:
253
+ lines=[f"SPACE base_{prob.name}"]
254
+ for v in prob.variables:
255
+ lo,hi=v["lo"],v["hi"]
256
+ if v["name"] in prob.observations: val=prob.observations[v["name"]]; lo=hi=val
257
+ lines.append(f"VAR {v['name']} {lo} {hi}")
258
+ for c in prob.constraints:
259
+ wt=c.get("weight",1.0)
260
+ if c["kind"]=="CONSERVE": lines.append(f"CONSERVE {c['expr']}")
261
+ elif c["kind"]=="BRANCH":
262
+ lines.append(f"BRANCH {c.get('name','br')}")
263
+ for opt in c.get("options",[]): lines.append(f" OPTION {opt}")
264
+ lines.append("END BRANCH")
265
+ else: lines.append(f"{c['kind']} {c['expr']} WEIGHT {wt}")
266
+ for sc in prob.scopes:
267
+ lines.append(f"SCOPE {sc['name']} ORDER {sc.get('order',0)}")
268
+ for c in sc.get("constraints",[]):
269
+ lines.append(f" {c['kind']} {c['expr']} WEIGHT {c.get('weight',1.0)}")
270
+ lines.append("END")
271
+ return "\n".join(lines)
272
+
273
+ AXL_COMPILER=AXLtoPSLCompiler()
274
+
275
+ # ══════════════════════════════════════════════════════════════════════
276
+ # SECTION 4: PROBLEM COMPILATION
277
+ # ══════════════════════════════════════════════════════════════════════
278
+ @dataclass
279
+ class Constraint:
280
+ kind:str; expr:str; direction:str; weight:float=1.0; scope:str="root"
281
+ branches:List[str]=field(default_factory=list)
282
+
283
+ @dataclass
284
+ class MathConstraint:
285
+ kind:str; expr_str:str; direction:str; weight:float=1.0
286
+ fast_iv:Optional[Callable]=field(default=None,repr=False)
287
+ fast_pt:Optional[Callable]=field(default=None,repr=False)
288
+ torch_func:Optional[Callable]=field(default=None,repr=False)
289
+ syms_used:List[str]=field(default_factory=list)
290
+ parsed:Optional[sp.Expr]=field(default=None,repr=False)
291
+ scope:str="root"
292
+ branches:List['MathConstraint']=field(default_factory=list)
293
+ projections:Dict[str,List[Dict]]=field(default_factory=dict)
294
+
295
+ def compile_mc(kind,expr_str,direction,variables,weight=1.0,scope="root",branches=None):
296
+ mc=MathConstraint(kind=kind,expr_str=expr_str,direction=direction,weight=weight,scope=scope)
297
+ if kind=="or_eq" and branches:
298
+ for b_str in branches:
299
+ b_mc=compile_mc("equality",b_str,"eq",variables,weight,scope)
300
+ mc.branches.append(b_mc); mc.syms_used.extend(b_mc.syms_used)
301
+ mc.syms_used=list(dict.fromkeys(mc.syms_used))
302
+ def _or_iv(box,_mcs=mc.branches):
303
+ rivs=[]
304
+ for b in _mcs:
305
+ if b.fast_iv:
306
+ try: rivs.append(b.fast_iv(box))
307
+ except: pass
308
+ if not rivs: return IV(-1e18,1e18)
309
+ return IV(min(r.lo for r in rivs),max(r.hi for r in rivs))
310
+ mc.fast_iv=_or_iv; return mc
311
+ syms={v:sp.Symbol(v) for v in variables}
312
+ try:
313
+ parsed=parse_expr(expr_str,local_dict=syms); mc.parsed=parsed
314
+ mc.syms_used=[v for v in variables if sp.Symbol(v) in parsed.free_symbols]
315
+ mc.fast_iv=compile_iv(parsed,variables)
316
+ mc.fast_pt=sp.lambdify([sp.Symbol(v) for v in mc.syms_used],parsed,modules="math")
317
+ pt_map={'sin':torch.sin,'cos':torch.cos,'exp':torch.exp,'sqrt':torch.sqrt,'Abs':torch.abs}
318
+ t_func_raw=sp.lambdify([sp.Symbol(v) for v in mc.syms_used],parsed,modules=[pt_map,"math"])
319
+ def _t_wrapper(*args):
320
+ val=t_func_raw(*args)
321
+ return torch.tensor(val,device=DEVICE,dtype=torch.float32) if isinstance(val,(int,float)) else val
322
+ mc.torch_func=_t_wrapper
323
+ if kind=="equality":
324
+ if expr_str not in PROJECTION_CACHE:
325
+ pm={}
326
+ for sym in parsed.free_symbols:
327
+ v_str=str(sym)
328
+ try:
329
+ sols=sp.solve(parsed,sym); pm[v_str]=[]
330
+ for sol in sols:
331
+ fs=list(sol.free_symbols)
332
+ pm[v_str].append({"syms":[str(s) for s in fs],
333
+ "func":sp.lambdify(fs,sol,modules="math")})
334
+ except: pass
335
+ PROJECTION_CACHE[expr_str]=pm
336
+ mc.projections=PROJECTION_CACHE.get(expr_str,{})
337
+ except: pass
338
+ return mc
339
+
340
+ def _extract_ordering_pair(mc: MathConstraint, variables: List[str]) -> Optional[Tuple[str,str]]:
341
+ if mc.kind != "inequality" or mc.direction != "geq": return None
342
+ if mc.parsed is None: return None
343
+ parsed = mc.parsed
344
+ free = [str(s) for s in parsed.free_symbols if str(s) in variables]
345
+ if len(free) != 2: return None
346
+ v0, v1 = free[0], free[1]
347
+ try:
348
+ c0 = float(parsed.coeff(sp.Symbol(v0)))
349
+ c1 = float(parsed.coeff(sp.Symbol(v1)))
350
+ except Exception:
351
+ return None
352
+ if c0 > 0 and c1 < 0: return (v1, v0)
353
+ elif c1 > 0 and c0 < 0: return (v0, v1)
354
+ return None
355
+
356
+ @dataclass
357
+ class Problem:
358
+ pid:str; variables:List[str]; constraints:List[Constraint]
359
+ bounds:Dict[str,Tuple[float,float]]
360
+ scope_groups:Dict[str,List[int]]=field(default_factory=dict)
361
+ scope_vars:Dict[str,List[str]]=field(default_factory=dict)
362
+ scope_order:List[str]=field(default_factory=list)
363
+
364
+ bilinear_pairs: List[Tuple[str,str]] = field(default_factory=list)
365
+ monotone_targets: List[Tuple[str,str,float]] = field(default_factory=list)
366
+ ordering_pairs: List[Tuple[str,str]] = field(default_factory=list)
367
+
368
+ def __post_init__(self):
369
+ self.compiled_constraints=[
370
+ compile_mc(c.kind,c.expr,c.direction,self.variables,c.weight,c.scope,c.branches)
371
+ for c in self.constraints]
372
+ self.var_idx={v:i for i,v in enumerate(self.variables)}
373
+
374
+ for mc in self.compiled_constraints:
375
+ pair = _extract_ordering_pair(mc, self.variables)
376
+ if pair and pair not in self.ordering_pairs:
377
+ self.ordering_pairs.append(pair)
378
+
379
+ for mc in self.compiled_constraints:
380
+ if mc.parsed and mc.parsed.is_Add:
381
+ for term in mc.parsed.args:
382
+ if term.is_Mul:
383
+ syms_in = [str(s) for s in term.free_symbols if str(s) in self.variables]
384
+ if len(syms_in) == 2:
385
+ va, vb = syms_in
386
+ if (va, vb) not in self.bilinear_pairs and (vb, va) not in self.bilinear_pairs:
387
+ self.bilinear_pairs.append((va, vb))
388
+
389
+ for mc in self.compiled_constraints:
390
+ if mc.kind == "equality" and mc.parsed is not None:
391
+ for va, vb in self.bilinear_pairs:
392
+ sa, sb = sp.Symbol(va), sp.Symbol(vb)
393
+ if sa in mc.parsed.free_symbols and sb in mc.parsed.free_symbols:
394
+ try:
395
+ k_expr = -(mc.parsed - sa*sb)
396
+ k = float(k_expr.evalf())
397
+ if k > 0:
398
+ for vs, vl in self.ordering_pairs:
399
+ if set([va, vb]) == set([vs, vl]):
400
+ target = (vs, vl, k)
401
+ if target not in self.monotone_targets:
402
+ self.monotone_targets.append(target)
403
+ except Exception:
404
+ pass
405
+
406
+ def tensor_energy(self,X:torch.Tensor) -> torch.Tensor:
407
+ is_batched=(X.dim()==2)
408
+ total=torch.zeros(X.shape[0] if is_batched else 1,device=DEVICE,dtype=torch.float32)
409
+ for mc in self.compiled_constraints:
410
+ if getattr(mc,'weight',1.0)==0.0: continue
411
+ if mc.kind=="or_eq":
412
+ b_vals=[]
413
+ for bmc in mc.branches:
414
+ if bmc.torch_func:
415
+ args=[X[:,self.var_idx[v]] if is_batched else X[self.var_idx[v]] for v in bmc.syms_used]
416
+ b_vals.append(torch.abs(bmc.torch_func(*args)))
417
+ if b_vals: total+=torch.stack(b_vals,dim=0).min(dim=0)[0]*mc.weight
418
+ else:
419
+ if mc.torch_func is None: continue
420
+ args=[X[:,self.var_idx[v]] if is_batched else X[self.var_idx[v]] for v in mc.syms_used]
421
+ val=mc.torch_func(*args)
422
+ if mc.kind=="equality": total+=torch.abs(val)*mc.weight
423
+ elif mc.direction=="geq": total+=torch.relu(-val)*mc.weight
424
+ else: total+=torch.relu(val)*mc.weight
425
+ for i,v in enumerate(self.variables):
426
+ lo,hi=self.bounds[v]
427
+ col=X[:,i] if is_batched else X[i]
428
+ total+=torch.relu(lo-col)+torch.relu(col-hi)
429
+ return total.squeeze()
430
+
431
+ def scalar_energy(self,b:Dict[str,float]) -> float:
432
+ total=0.0
433
+ for mc in self.compiled_constraints:
434
+ if getattr(mc,'weight',1.0)==0.0: continue
435
+ if mc.kind=="or_eq":
436
+ vals=[]
437
+ for bmc in mc.branches:
438
+ if bmc.fast_pt:
439
+ try: vals.append(abs(float(bmc.fast_pt(*[b.get(v,0.0) for v in bmc.syms_used]))))
440
+ except: pass
441
+ total+=(min(vals)*mc.weight) if vals else 1.0
442
+ else:
443
+ if mc.fast_pt is None: total+=1.0; continue
444
+ try:
445
+ val=float(mc.fast_pt(*[b.get(v,0.0) for v in mc.syms_used]))
446
+ if mc.kind=="equality": total+=abs(val)*mc.weight
447
+ elif mc.direction=="geq": total+=max(0.0,-val)*mc.weight
448
+ else: total+=max(0.0,val)*mc.weight
449
+ except: total+=1.0
450
+ for v,(lo,hi) in self.bounds.items():
451
+ bv=b.get(v,0.0); total+=max(0.0,lo-bv)+max(0.0,bv-hi)
452
+ return total
453
+
454
+ def build_base_problem(axl_def:AXLProblemDef) -> Problem:
455
+ psl_text=AXL_COMPILER.compile(axl_def)
456
+ prog=parse_psl(psl_text); ep=expand_psl(prog)
457
+ return Problem(pid=f"base_{axl_def.name}",variables=ep.variables,
458
+ constraints=ep.constraints,bounds=ep.bounds,
459
+ scope_groups=ep.scope_groups,scope_vars=ep.scope_vars,
460
+ scope_order=ep.scope_order)
461
+
462
+ # ══════════════════════════════════════════════════════════════════════
463
+ # SECTION 5: BATCHED MASKED DEDUCTION & ORACLE
464
+ # ══════════════════════════════════════════════════════════════════════
465
+ @dataclass
466
+ class L9Certificate:
467
+ residual_ce:float; dominant_vars:List[str]; dominant_exprs:List[str]
468
+ tension_class:str="unknown"
469
+
470
+ def _batched_deduce_and_evaluate(
471
+ problem:Problem,
472
+ hyps:List['Hypothesis']) -> List[Tuple[Dict,float,List[str],str]]:
473
+ if not hyps: return []
474
+ B=len(hyps); V=len(problem.variables)
475
+
476
+ x_data = []
477
+ mask_data = []
478
+ target_data = []
479
+ for hyp in hyps:
480
+ xr, mr, tr = [], [], []
481
+ for v in problem.variables:
482
+ if v in hyp.pinned_vars:
483
+ val = hyp.pinned_vars[v]
484
+ xr.append(val); mr.append(0.0); tr.append(val)
485
+ else:
486
+ val = hyp.binding.get(v, (problem.bounds[v][0]+problem.bounds[v][1])/2)
487
+ xr.append(val); mr.append(1.0); tr.append(0.0)
488
+ x_data.append(xr); mask_data.append(mr); target_data.append(tr)
489
+
490
+ X = torch.tensor(x_data, device=DEVICE, dtype=torch.float32, requires_grad=True)
491
+ mask = torch.tensor(mask_data, device=DEVICE, dtype=torch.float32)
492
+ target = torch.tensor(target_data, device=DEVICE, dtype=torch.float32)
493
+
494
+ optimizer=torch.optim.Adam([X],lr=0.05)
495
+
496
+ for _ in range(DEDUCE_ADAM_STEPS):
497
+ optimizer.zero_grad()
498
+ ce=problem.tensor_energy(X)
499
+ if isinstance(ce,torch.Tensor) and (ce<SOLVE_THRESHOLD).all(): break
500
+ loss=ce.sum(); loss.backward()
501
+ with torch.no_grad():
502
+ X.grad*=mask
503
+ optimizer.step()
504
+ X.data=torch.where(mask==0.0,target,X.data)
505
+ for j,v in enumerate(problem.variables):
506
+ lo,hi=problem.bounds[v]
507
+ X.data[:,j]=torch.clamp(X.data[:,j],lo,hi)
508
+
509
+ optimizer.zero_grad()
510
+ final_ce=problem.tensor_energy(X)
511
+ final_ce.sum().backward()
512
+
513
+ ce_vals = final_ce.detach().cpu().numpy()
514
+ X_vals = X.detach().cpu().numpy()
515
+
516
+ grads_abs = X.grad.abs()
517
+ _, topk_idx = torch.topk(grads_abs, k=min(3, V), dim=1)
518
+ topk_idx_np = topk_idx.cpu().numpy()
519
+ grads_np = grads_abs.cpu().numpy()
520
+
521
+ results=[]
522
+ for i in range(B):
523
+ final_b={problem.variables[j]:float(X_vals[i,j]) for j in range(V)}
524
+ dom_vars = [problem.variables[idx] for idx in topk_idx_np[i] if float(grads_np[i, idx]) > 1e-4]
525
+ n_dom = len(dom_vars)
526
+ if n_dom<=1: tension_class="isolated"
527
+ elif n_dom==2: tension_class="relational"
528
+ else: tension_class="systemic"
529
+ results.append((final_b,float(ce_vals[i]),dom_vars,tension_class))
530
+ return results
531
+
532
+ def _mprt_sample(problem,work_box,N):
533
+ var_list=problem.variables; V=len(var_list)
534
+ lo_t=torch.tensor([max(problem.bounds.get(v,(-10.0,10.0))[0],
535
+ work_box[v].lo if v in work_box else problem.bounds.get(v,(-10,10))[0])
536
+ for v in var_list],device=DEVICE,dtype=torch.float32)
537
+ hi_t=torch.tensor([min(problem.bounds.get(v,(-10.0,10.0))[1],
538
+ work_box[v].hi if v in work_box else problem.bounds.get(v,(-10,10))[1])
539
+ for v in var_list],device=DEVICE,dtype=torch.float32)
540
+ for i in range(V):
541
+ 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
542
+ X=lo_t.unsqueeze(0)+(hi_t-lo_t).unsqueeze(0)*torch.rand((N,V),device=DEVICE)
543
+ ce_batch=problem.tensor_energy(X)
544
+ best_idx=torch.argmin(ce_batch).item()
545
+ return {v:float(X[best_idx,i].item()) for i,v in enumerate(var_list)},ce_batch[best_idx].item()
546
+
547
+ # ══════════════════════════════════════════════════════════════════════
548
+ # SECTION 6: AXIOMS
549
+ # ══════════════════════════════════════════════════════════════════════
550
+ class Axiom:
551
+ CONTINUOUS="CONTINUOUS"; DISCRETE="DISCRETE"; QUADRATIC="QUADRATIC"
552
+ BILINEAR="BILINEAR"; METRIC="METRIC"; SYMMETRIC="SYMMETRIC"
553
+ ORDERED="ORDERED"; MONOTONE="MONOTONE"; CONVEX="CONVEX"
554
+ MONOTONE_PRODUCT="MONOTONE_PRODUCT"
555
+ CONSERVED="CONSERVED"; MUTABLE="MUTABLE"; NETWORK="NETWORK"
556
+ EQUILIBRIUM="EQUILIBRIUM"; SUPERPOSITION="SUPERPOSITION"
557
+ LOCALITY="LOCALITY"; EXTREMAL="EXTREMAL"; ENTROPY="ENTROPY"
558
+ INJECTIVE="INJECTIVE"; SURJECTIVE="SURJECTIVE"; TRANSITIVE="TRANSITIVE"
559
+ ATOMIC="ATOMIC"; IMPLICATION="IMPLICATION"; NEGATION="NEGATION"
560
+ COMPOSITE="COMPOSITE"; HOLISM="HOLISM"; PARSIMONY="PARSIMONY"; DUALITY="DUALITY"
561
+
562
+ ALL_AXIOMS=[getattr(Axiom,a) for a in dir(Axiom) if not a.startswith("__")]
563
+ AXIOM_ABBR={a:a[:3] for a in ALL_AXIOMS}; AXIOM_ABBR[Axiom.MONOTONE_PRODUCT]="MPR"
564
+
565
+ ADJACENT_CONTRADICTIONS:List[Tuple[str,str]]=[
566
+ (Axiom.DISCRETE,Axiom.CONTINUOUS),(Axiom.INJECTIVE,Axiom.SURJECTIVE),
567
+ (Axiom.ORDERED,Axiom.SYMMETRIC),(Axiom.ATOMIC,Axiom.COMPOSITE),
568
+ (Axiom.CONSERVED,Axiom.MUTABLE),(Axiom.PARSIMONY,Axiom.ENTROPY),
569
+ (Axiom.HOLISM,Axiom.LOCALITY),(Axiom.EXTREMAL,Axiom.ENTROPY),
570
+ (Axiom.NEGATION,Axiom.IMPLICATION),(Axiom.MONOTONE_PRODUCT,Axiom.SYMMETRIC),
571
+ ]
572
+ _CONTRA_SET={frozenset(p) for p in ADJACENT_CONTRADICTIONS}
573
+
574
+ def sequence_valid(seq:List[str]) -> bool:
575
+ for i in range(len(seq)-1):
576
+ if seq[i]==seq[i+1]: return False
577
+ if frozenset([seq[i],seq[i+1]]) in _CONTRA_SET: return False
578
+ return True
579
+
580
+ # ══════════════════════════════════════════════════════════════════════
581
+ # SECTION 7: SHARED DATACLASSES
582
+ # ══════════════════════════════════════════════════════════════════════
583
+ RAY_TYPES=["seed","branch","tension","scout","recombine","invert","target"]
584
+
585
+ @dataclass
586
+ class Hypothesis:
587
+ hid:str; binding:Dict[str,float]; h_type:str; claim:str
588
+ derivation:List[str]; pinned_vars:Dict[str,float]; free_vars:List[str]
589
+ confidence:float; ce:float=float('inf')
590
+
591
+ @dataclass
592
+ class Baton:
593
+ binding:Dict[str,float]; ce:float
594
+ ray_id:str=""; depth:int=0; l9:Optional[L9Certificate]=None
595
+
596
+ @dataclass
597
+ class AxiomRay:
598
+ sequence:List[str]; ray_id:str; ray_type:str="seed"
599
+ depth:int=0; parent_id:Optional[str]=None
600
+ baton:Optional[Baton]=None; ce_prior:float=float('inf')
601
+
602
+ @property
603
+ def name(self) -> str:
604
+ return "β†’".join(AXIOM_ABBR.get(a,a[:3]) for a in self.sequence)
605
+
606
+ def extend(self,axiom:str,rtype:str,new_prior:float=float('inf')) -> Optional['AxiomRay']:
607
+ new_seq=self.sequence+[axiom]
608
+ if sequence_valid(new_seq):
609
+ return AxiomRay(sequence=new_seq,
610
+ ray_id=f"{self.ray_id}+{AXIOM_ABBR.get(axiom,'?')}",
611
+ ray_type=rtype,depth=self.depth+1,
612
+ parent_id=self.ray_id,ce_prior=new_prior)
613
+ return None
614
+
615
+ @dataclass
616
+ class HypothesisTrace:
617
+ hid:str; round_idx:int; ray_name:str; ray_type:str
618
+ ce_before:float; ce_after:float
619
+ survived:bool; elapsed_ms:float
620
+ g1_pass:bool=False; g2_pass:bool=False
621
+ binding:Dict[str,float]=field(default_factory=dict)
622
+ invariant_results:Dict[str,Tuple[float,bool]]=field(default_factory=dict)
623
+ tension_class:str="unknown"
624
+ depth:int=0
625
+
626
+ @dataclass
627
+ class StructuralModel:
628
+ best_binding:Dict[str,float]; best_ce:float
629
+ failure_patterns:Set[str]=field(default_factory=set)
630
+ resonant_pairs:List[Tuple[str,str]]=field(default_factory=list)
631
+
632
+ # ══════════════════════════════════════════════════════════════════════
633
+ # SECTION 8: HYPOTHESIS CONSTRUCTORS
634
+ # ══════════════════════════════════════════════════════════════════════
635
+ def _make_hyp(hid,binding,h_type,claim,derivation,pinned,free,conf):
636
+ return Hypothesis(hid=hid,binding=binding,h_type=h_type,claim=claim,
637
+ derivation=derivation,pinned_vars=pinned,free_vars=free,confidence=conf)
638
+
639
+ def _hyp_continuous(p,bt,a,m): return [_make_hyp("cnt",bt.binding,"continuous","Manifold",[],{},p.variables,0.45)]
640
+ def _hyp_discrete(p,bt,a,m):
641
+ b=dict(bt.binding); pinned={}
642
+ for sn in p.scope_order:
643
+ svars=p.scope_vars.get(sn,[]); smcs=[p.compiled_constraints[i] for i in p.scope_groups.get(sn,[])]
644
+ if svars and smcs:
645
+ box={v:IV(b.get(v,0)-1,b.get(v,0)+1) for v in svars if v in p.bounds}
646
+ cont=_hc4(box,smcs)
647
+ if cont:
648
+ for v,iv in cont.items(): b[v]=iv.mid(); (pinned.update({v:b[v]}) if iv.width()<1e-2 else None)
649
+ return [_make_hyp("dis",b,"discrete","TopoScope",[],pinned,p.variables,0.80)]
650
+ def _hyp_quadratic(p,bt,a,m):
651
+ hyps=[]; b=dict(bt.binding)
652
+ if bt.l9 and bt.l9.dominant_vars:
653
+ 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))
654
+ return hyps
655
+ def _hyp_bilinear(p,bt,a,m):
656
+ hyps=[]; b=dict(bt.binding)
657
+ for vi, vj in p.bilinear_pairs:
658
+ lo_i,hi_i=p.bounds.get(vi,(0,10)); lo_j,hi_j=p.bounds.get(vj,(0,10))
659
+ product=abs(b.get(vi,1)*b.get(vj,1))
660
+ if product<=0: continue
661
+ gm=math.sqrt(product)
662
+ if lo_i<=gm<=hi_i and lo_j<=gm<=hi_j:
663
+ nb=dict(b); nb[vi]=nb[vj]=gm
664
+ hyps.append(_make_hyp(f"bil_eq_{vi}_{vj}",nb,"bilinear",f"GeoMean({vi}={vj}={gm:.3f})",["bilinear"],{vi:gm,vj:gm},[v for v in p.variables if v not in [vi,vj]],0.70))
665
+ for ratio in [0.4, 0.7071, 0.9]:
666
+ vs=gm*ratio; vl=product/(vs+1e-9)
667
+ if lo_i<=vs<=hi_i and lo_j<=vl<=hi_j and vs<vl:
668
+ nb2=dict(b); nb2[vi]=vs; nb2[vj]=vl
669
+ hyps.append(_make_hyp(f"bil_asc_{int(ratio*100)}_{vi}_{vj}",nb2,"bilinear",f"BilAsc({vi}={vs:.3f}<{vj}={vl:.3f})",["bilinear"],{vi:vs,vj:vl},[v for v in p.variables if v not in [vi,vj]],0.75))
670
+ if lo_i<=vl<=hi_i and lo_j<=vs<=hi_j and vl>vs:
671
+ nb3=dict(b); nb3[vi]=vl; nb3[vj]=vs
672
+ hyps.append(_make_hyp(f"bil_desc_{int(ratio*100)}_{vi}_{vj}",nb3,"bilinear",f"BilDesc({vi}={vl:.3f}>{vj}={vs:.3f})",["bilinear"],{vi:vl,vj:vs},[v for v in p.variables if v not in [vi,vj]],0.75))
673
+ return hyps
674
+ def _hyp_monotone_product(p,bt,a,m):
675
+ hyps=[]; b=dict(bt.binding)
676
+ if not p.monotone_targets: return hyps
677
+ tb=_global_hc4_tighten_bounds(p)
678
+ for v_small, v_large, k in p.monotone_targets:
679
+ lo_s,hi_s=p.bounds.get(v_small,(-1e18,1e18)); lo_l,hi_l=p.bounds.get(v_large,(-1e18,1e18))
680
+ for ratio in [0.1,0.2,0.3,0.4,0.5,0.6,0.707,0.8,0.9,0.95]:
681
+ vs_sq=k*ratio
682
+ if vs_sq<=0: continue
683
+ vs=math.sqrt(vs_sq); vl=k/vs
684
+ if not(lo_s<=vs<=hi_s and lo_l<=vl<=hi_l and vs<=vl): continue
685
+ nb=dict(b); nb[v_small]=vs; nb[v_large]=vl
686
+ box={u:IV(*tb.get(u,p.bounds.get(u,(-10,10)))) for u in p.variables}
687
+ box[v_small]=IV(vs-1e-6,vs+1e-6); box[v_large]=IV(vl-1e-6,vl+1e-6)
688
+ cont=_hc4(box,p.compiled_constraints)
689
+ pinned={v_small:vs,v_large:vl}
690
+ if cont:
691
+ for u,iv in cont.items():
692
+ if u in p.bounds: nb[u]=iv.mid()
693
+ pinned={u:nb[u] for u in p.variables if cont[u].width()<1e-2}
694
+ hyps.append(_make_hyp(f"mpr_{v_small}_{v_large}_r{int(ratio*100)}",nb,"monotone_product",f"MonoProd({v_small}={vs:.4f}≀{v_large}={vl:.4f})",["ordered","hc4"],pinned,[u for u in p.variables if u not in pinned],0.88))
695
+ return hyps
696
+ def _hyp_metric(p,bt,a,m): return [_make_hyp("met",bt.binding,"metric","Radial",[],{},p.variables,0.75)]
697
+ def _hyp_symmetric(p,bt,a,m):
698
+ hyps=[]; b=dict(bt.binding); vl=p.variables
699
+ if len(vl)>=2:
700
+ mid=(b.get(vl[0],0)+b.get(vl[1],0))/2; nb=dict(b); nb[vl[0]]=nb[vl[1]]=mid
701
+ hyps.append(_make_hyp("sym",nb,"symmetric","AveragePin",[],{vl[0]:mid,vl[1]:mid},p.variables,0.60))
702
+ return hyps
703
+ def _hyp_ordered(p,bt,a,m): return [_make_hyp("ord",bt.binding,"ordered","OrderBal",[],{},p.variables,0.67)]
704
+ def _hyp_monotone(p,bt,a,m): return [_make_hyp("mon",bt.binding,"monotone","MonoPath",[],{},p.variables,0.67)]
705
+ def _hyp_convex(p,bt,a,m): return [_make_hyp("cvx",bt.binding,"convex","ConvMix",[],{},p.variables,0.63)]
706
+ def _hyp_conserved(p,bt,a,m): return [_make_hyp("csv",bt.binding,"conserved","Conserve",[],{},p.variables,0.82)]
707
+ def _hyp_mutable(p,bt,a,m):
708
+ b=dict(bt.binding)
709
+ if bt.l9 and bt.l9.dominant_vars:
710
+ v=bt.l9.dominant_vars[0]; lo,hi=p.bounds.get(v,(-10,10))
711
+ b[v]=max(lo,min(hi,b.get(v,0)+random.gauss(0,(hi-lo)*0.05)))
712
+ return [_make_hyp("mut",b,"mutable","Perturb",[],{},p.variables,0.40)]
713
+ def _hyp_network(p,bt,a,m): return [_make_hyp("net",bt.binding,"network","HubProp",[],{},p.variables,0.65)]
714
+ def _hyp_equilibrium(p,bt,a,m): return [_make_hyp("eql",bt.binding,"equilibrium","GradBal",[],{},p.variables,0.72)]
715
+ def _hyp_superposition(p,bt,a,m):
716
+ if a and len(a)>0:
717
+ other=random.choice(a); lam=0.5
718
+ nb={v:lam*bt.binding.get(v,0)+(1-lam)*other.binding.get(v,0) for v in p.variables}
719
+ return [_make_hyp("sup",nb,"superposition","Superpose",[],{},p.variables,0.58)]
720
+ return []
721
+ def _hyp_locality(p,bt,a,m): return [_make_hyp("loc",bt.binding,"locality","LocalFirst",[],{},p.variables,0.72)]
722
+ def _hyp_extremal(p,bt,a,m):
723
+ hyps=[]; b=dict(bt.binding)
724
+ if bt.l9 and bt.l9.dominant_vars:
725
+ v=bt.l9.dominant_vars[0]; lo,hi=p.bounds.get(v,(0,1))
726
+ for val in [lo,hi,(lo+hi)/2]:
727
+ nb=dict(b); nb[v]=val
728
+ hyps.append(_make_hyp(f"ext_{v}_{val:.3f}",nb,"extremal",f"PinBound({v}={val:.3f})",[],{v:val},p.variables,0.65))
729
+ return hyps
730
+ 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)]
731
+ def _hyp_injective(p,bt,a,m): return [_make_hyp("inj",bt.binding,"injective","Distinct",[],{},p.variables,0.50)]
732
+ def _hyp_surjective(p,bt,a,m): return [_make_hyp("sur",bt.binding,"surjective","Sweep",[],{},p.variables,0.45)]
733
+ def _hyp_transitive(p,bt,a,m): return [_make_hyp("tra",bt.binding,"transitive","TransSnap",[],{},p.variables,0.77)]
734
+ def _hyp_atomic(p,bt,a,m):
735
+ hyps=[]; b=dict(bt.binding)
736
+ for mc in p.compiled_constraints:
737
+ if not mc.projections: continue
738
+ for v,projs in mc.projections.items():
739
+ for proj in projs:
740
+ try:
741
+ val=float(proj["func"](*[b.get(s,0.0) for s in proj["syms"]]))
742
+ lo,hi=p.bounds.get(v,(-1e18,1e18))
743
+ if math.isfinite(val) and lo<=val<=hi:
744
+ nb=dict(b); nb[v]=val
745
+ hyps.append(_make_hyp(f"ato_{v}",nb,"atomic",f"Solve({v})",[],{v:val},p.variables,0.70))
746
+ break
747
+ except: pass
748
+ if hyps: break
749
+ return hyps
750
+ def _hyp_implication(p,bt,a,m): return [_make_hyp("imp",bt.binding,"implication","Imply",[],{},p.variables,0.78)]
751
+ def _hyp_negation(p,bt,a,m):
752
+ nb={v:max(p.bounds[v][0],min(p.bounds[v][1],(p.bounds[v][0]+p.bounds[v][1])-bt.binding.get(v,(p.bounds[v][0]+p.bounds[v][1])/2))) for v in p.variables}
753
+ return [_make_hyp("neg",nb,"negation","Mirror",[],{},p.variables,0.42)]
754
+ def _hyp_composite(p,bt,a,m): return [_make_hyp("cmp",bt.binding,"composite","Enz",[],{},p.variables,0.90)]
755
+ def _hyp_holism(p,bt,a,m): return [_make_hyp("hol",bt.binding,"holism","Global",[],{},p.variables,0.73)]
756
+ def _hyp_parsimony(p,bt,a,m):
757
+ b=dict(bt.binding); nb={}
758
+ max_val=max((abs(v) for v in b.values()),default=1.0)
759
+ for v in p.variables:
760
+ val=b[v]; lo,hi=p.bounds[v]
761
+ if abs(val)<0.1*max_val and lo<=0.0<=hi: nb[v]=0.0
762
+ else:
763
+ candidates=[round(val*m)/m for m in [1,2,4] if lo<=round(val*m)/m<=hi]
764
+ nb[v]=min(candidates,key=lambda c:abs(c-val)) if candidates else val
765
+ return [_make_hyp("par",nb,"parsimony","Occam",[],{},p.variables,0.58)]
766
+ def _hyp_duality(p,bt,a,m): return [_make_hyp("dua",bt.binding,"duality","Tight",[],{},p.variables,0.68)]
767
+
768
+ AXIOM_CONSTRUCTORS={
769
+ Axiom.CONTINUOUS:_hyp_continuous, Axiom.DISCRETE:_hyp_discrete,
770
+ Axiom.QUADRATIC:_hyp_quadratic, Axiom.BILINEAR:_hyp_bilinear,
771
+ Axiom.METRIC:_hyp_metric, Axiom.SYMMETRIC:_hyp_symmetric,
772
+ Axiom.ORDERED:_hyp_ordered, Axiom.MONOTONE:_hyp_monotone,
773
+ Axiom.CONVEX:_hyp_convex, Axiom.MONOTONE_PRODUCT:_hyp_monotone_product,
774
+ Axiom.CONSERVED:_hyp_conserved, Axiom.MUTABLE:_hyp_mutable,
775
+ Axiom.NETWORK:_hyp_network, Axiom.EQUILIBRIUM:_hyp_equilibrium,
776
+ Axiom.SUPERPOSITION:_hyp_superposition, Axiom.LOCALITY:_hyp_locality,
777
+ Axiom.EXTREMAL:_hyp_extremal, Axiom.ENTROPY:_hyp_entropy,
778
+ Axiom.INJECTIVE:_hyp_injective, Axiom.SURJECTIVE:_hyp_surjective,
779
+ Axiom.TRANSITIVE:_hyp_transitive, Axiom.ATOMIC:_hyp_atomic,
780
+ Axiom.IMPLICATION:_hyp_implication, Axiom.NEGATION:_hyp_negation,
781
+ Axiom.COMPOSITE:_hyp_composite, Axiom.HOLISM:_hyp_holism,
782
+ Axiom.PARSIMONY:_hyp_parsimony, Axiom.DUALITY:_hyp_duality,
783
+ }
784
+
785
+ # ══════════════════════════════════════════════════════════════════════
786
+ # SECTION 9: CREATIVE SEEDER
787
+ # ══════════════════════════════════════════════════════════════════════
788
+ class CreativeSeeder:
789
+ def target_seeds(self,anchors:List[AXLInvariant]) -> List[AxiomRay]:
790
+ seeds=[]
791
+ for a in anchors:
792
+ if "*" in a.expr and "**" not in a.expr:
793
+ seeds.append(AxiomRay([Axiom.BILINEAR],f"TGT_BIL_{a.name}","target"))
794
+ seeds.append(AxiomRay([Axiom.MONOTONE_PRODUCT],f"TGT_MPR_{a.name}","target"))
795
+ elif "**2" in a.expr:
796
+ seeds.append(AxiomRay([Axiom.QUADRATIC],f"TGT_QUA_{a.name}","target"))
797
+ seeds.append(AxiomRay([Axiom.METRIC],f"TGT_MET_{a.name}","target"))
798
+ elif "+" in a.expr:
799
+ seeds.append(AxiomRay([Axiom.CONSERVED],f"TGT_CSV_{a.name}","target"))
800
+ return seeds
801
+
802
+ def intelligent_branch(self,ray,out_baton,remaining_axioms,branch_width) -> List[AxiomRay]:
803
+ dom_vars=out_baton.l9.dominant_vars if out_baton.l9 else []
804
+ tension_class=out_baton.l9.tension_class if out_baton.l9 else "unknown"
805
+ n_dom=len(dom_vars)
806
+ effective_width = branch_width
807
+ if tension_class == "systemic" and out_baton.ce > 0.1:
808
+ effective_width = BRANCH_WIDTH_SYSTEMIC
809
+
810
+ isolated ={Axiom.ATOMIC,Axiom.EXTREMAL,Axiom.MUTABLE,Axiom.DUALITY,Axiom.LOCALITY}
811
+ relational ={Axiom.SYMMETRIC,Axiom.BILINEAR,Axiom.MONOTONE_PRODUCT,
812
+ Axiom.ORDERED,Axiom.METRIC,Axiom.INJECTIVE}
813
+ systemic ={Axiom.CONSERVED,Axiom.NETWORK,Axiom.HOLISM,Axiom.COMPOSITE,Axiom.ENTROPY}
814
+
815
+ scored=[]
816
+ for ax in remaining_axioms:
817
+ w=1.0
818
+ if n_dom==1 and ax in isolated: w+=5.0
819
+ elif n_dom==2 and ax in relational: w+=5.0
820
+ elif n_dom>=3 and ax in systemic: w+=5.0
821
+ scored.append((w,ax))
822
+ scored.sort(key=lambda x:x[0]*random.random(),reverse=True)
823
+
824
+ children=[]
825
+ for _,axiom in scored[:effective_width]:
826
+ child=ray.extend(axiom,"branch",new_prior=out_baton.ce)
827
+ if child: child.baton=out_baton; children.append(child)
828
+ return children
829
+
830
+ def tension_seeds(self,max_n:int=3000) -> List[AxiomRay]:
831
+ results=[]
832
+ for (a,c) in ADJACENT_CONTRADICTIONS:
833
+ for b1 in ALL_AXIOMS:
834
+ if b1 in (a,c) or frozenset([a,b1]) in _CONTRA_SET: continue
835
+ for mid in ALL_AXIOMS:
836
+ if mid in (a,b1,c) or frozenset([b1,mid]) in _CONTRA_SET: continue
837
+ for b2 in ALL_AXIOMS:
838
+ if b2 in (a,b1,mid,c): continue
839
+ if frozenset([mid,b2]) not in _CONTRA_SET and frozenset([b2,c]) not in _CONTRA_SET:
840
+ results.append(AxiomRay([a,b1,mid,b2,c],
841
+ f"T5_{AXIOM_ABBR.get(a,'?')}_{AXIOM_ABBR.get(c,'?')}","tension"))
842
+ random.shuffle(results); return results[:max_n]
843
+
844
+ def random_scouts(self,n:int=600) -> List[AxiomRay]:
845
+ scouts=[]; attempts=0
846
+ while len(scouts)<n and attempts<n*30:
847
+ attempts+=1; length=random.randint(3,12)
848
+ pool=list(ALL_AXIOMS); random.shuffle(pool); seq=[pool[0]]
849
+ for ax in pool[1:]:
850
+ if len(seq)>=length: break
851
+ if ax!=seq[-1] and frozenset([seq[-1],ax]) not in _CONTRA_SET: seq.append(ax)
852
+ if len(seq)>=3:
853
+ scouts.append(AxiomRay(seq,f"SC_{''.join(AXIOM_ABBR.get(a,a[:2]) for a in seq)}","scout"))
854
+ return scouts
855
+
856
+ def recombine(self,ray_a,ray_b,ba,bb) -> List[AxiomRay]:
857
+ results=[]
858
+ for lam in [0.25,0.5,0.75]:
859
+ ib={v:lam*ba.binding.get(v,0)+(1-lam)*bb.binding.get(v,0)
860
+ for v in set(list(ba.binding.keys())+list(bb.binding.keys()))}
861
+ ib_ce=lam*ba.ce+(1-lam)*bb.ce
862
+ ib_baton=Baton(binding=ib,ce=ib_ce,ray_id="INTERP")
863
+ cut=min(len(ray_a.sequence),len(ray_b.sequence))//2
864
+ if cut>0:
865
+ seq=ray_a.sequence[:cut]+[x for x in ray_b.sequence[cut:] if x not in ray_a.sequence[:cut]]
866
+ if sequence_valid(seq):
867
+ results.append(AxiomRay(seq,f"RC_l{int(lam*100)}","recombine",
868
+ baton=ib_baton,ce_prior=ib_ce))
869
+ return results
870
+
871
+ def invert(self,ray,parent_ce) -> Optional[AxiomRay]:
872
+ inv=list(reversed(ray.sequence))
873
+ if sequence_valid(inv) and inv!=ray.sequence:
874
+ return AxiomRay(inv,f"INV_{ray.ray_id}","invert",parent_id=ray.ray_id,ce_prior=parent_ce)
875
+ return None
876
+
877
+ def resonance_extensions(self,ray,parent_ce,resonant_pairs) -> List[AxiomRay]:
878
+ if not ray.sequence or not resonant_pairs: return []
879
+ last=ray.sequence[-1]; results=[]
880
+ nexts=[b for (a,b) in resonant_pairs
881
+ if a==last and b!=last and frozenset([a,b]) not in _CONTRA_SET
882
+ and b not in ray.sequence]
883
+ for b in nexts:
884
+ child=ray.extend(b,"branch",new_prior=parent_ce)
885
+ if child: child.ray_id=f"RES_{child.ray_id}"; results.append(child)
886
+ return results
887
+
888
+ CREATIVE_SEEDER=CreativeSeeder()
889
+
890
+ # ══════════════════════════════════════════════════════════════════════
891
+ # SECTION 10: VERIFY LAYER
892
+ # ══════════════════════════════════════════════════════════════════════
893
+ def _verify(binding:Dict[str,float],base_problem:Problem,
894
+ anchors:List[AXLInvariant]) -> Tuple[bool,bool,Dict[str,Tuple[float,bool]],float]:
895
+ g1_ce=base_problem.scalar_energy(binding)
896
+ g1_pass=g1_ce<VERIFY_G1_THRESHOLD
897
+ g2={}
898
+ for inv in anchors:
899
+ try:
900
+ if inv.compiled_func: val = float(inv.compiled_func(*[binding.get(v, 0.0) for v in inv.syms_used]))
901
+ else: val = 999.0
902
+ err=abs(val)
903
+ if inv.mode=="eq": passed=err<inv.tolerance
904
+ elif inv.mode=="geq": passed=val>=-inv.tolerance
905
+ elif inv.mode=="leq": passed=val<=inv.tolerance
906
+ else: passed=err<inv.tolerance
907
+ g2[inv.name]=(round(val if inv.mode in ("geq","leq") else err,6),passed)
908
+ except: g2[inv.name]=(999.0,False)
909
+ g2_pass=all(v[1] for v in g2.values()) if g2 else True
910
+ return g1_pass,g2_pass,g2,round(g1_ce,6)
911
+
912
+ # ══════════════════════════════════════════════════════════════════════
913
+ # SECTION 11: SEQUENCE RAY TRACER
914
+ # ══════════════════════════════════════════════════════════════════════
915
+ class SequenceRayTracer:
916
+ def trace(self,axl_def:AXLProblemDef,base_problem:Problem):
917
+ hero_traces:List[HypothesisTrace]=[]
918
+ baton_registry:Dict[str,Baton]={}
919
+ baton_registry_keys:deque=deque()
920
+ successful_rays:List[AxiomRay]=[]
921
+ resonant_pairs:List[Tuple[str,str]]=[]
922
+
923
+ for inv in axl_def.anchors:
924
+ if not inv.compiled_func:
925
+ inv.compile(base_problem.variables)
926
+
927
+ init_b,init_ce=_mprt_sample(base_problem,
928
+ {v:IV(*base_problem.bounds[v]) for v in base_problem.variables},N_MPRT_EXPLORE)
929
+ seed_baton=Baton(binding=init_b,ce=init_ce,ray_id="SEED",
930
+ l9=L9Certificate(init_ce,base_problem.variables[:3],[],"systemic"))
931
+
932
+ queues={
933
+ "core":[AxiomRay([a],f"S{i}","seed",ce_prior=init_ce) for i,a in enumerate(ALL_AXIOMS)],
934
+ "explore":CREATIVE_SEEDER.tension_seeds(3000)+CREATIVE_SEEDER.random_scouts(600),
935
+ "genetic":CREATIVE_SEEDER.target_seeds(axl_def.anchors),
936
+ }
937
+ for q in queues.values(): random.shuffle(q)
938
+
939
+ queue_order=["core","explore","genetic"]; q_idx=0
940
+ best_ce=init_ce; best_binding=dict(init_b); total_fired=0
941
+ model=StructuralModel(best_binding,best_ce)
942
+ allosteric_counts={"isolated":0,"relational":0,"systemic":0}
943
+
944
+ while total_fired<MAX_TOTAL_RAYS:
945
+ batch_rays:List[AxiomRay]=[]
946
+ for _ in range(RAY_BATCH_SIZE):
947
+ start_idx=q_idx
948
+ while not queues[queue_order[q_idx]]:
949
+ q_idx=(q_idx+1)%3
950
+ if q_idx==start_idx: break
951
+ if not any(queues[k] for k in queue_order): break
952
+ ray=queues[queue_order[q_idx]].pop(0); q_idx=(q_idx+1)%3
953
+ batch_rays.append(ray); total_fired+=1
954
+ if not batch_rays: break
955
+
956
+ t0=time.time()
957
+ historical_batons=list(baton_registry.values())
958
+
959
+ batch_hyps:List[Tuple[int,Hypothesis]]=[]
960
+ for i,ray in enumerate(batch_rays):
961
+ p_baton=ray.baton or seed_baton
962
+ constructor=AXIOM_CONSTRUCTORS.get(ray.sequence[-1])
963
+ hyps=constructor(base_problem,p_baton,historical_batons,model) if constructor else []
964
+ if not hyps: hyps=[_make_hyp("fb",p_baton.binding,"fallback","Pass",[],{},base_problem.variables,0.1)]
965
+ batch_hyps.append((i,hyps[0]))
966
+
967
+ eval_results=_batched_deduce_and_evaluate(base_problem,[h for _,h in batch_hyps])
968
+ elapsed_ms=(time.time()-t0)*1000/max(1,len(batch_rays))
969
+
970
+ solved=False
971
+ for (ray_idx,hyp),(final_b,final_ce,dom_vars,tension_class) in zip(batch_hyps,eval_results):
972
+ ray=batch_rays[ray_idx]
973
+ parent_ce=(ray.baton or seed_baton).ce
974
+ allosteric_counts[tension_class]=allosteric_counts.get(tension_class,0)+1
975
+
976
+ g1_pass,g2_pass,inv_results,g1_ce=_verify(final_b,base_problem,axl_def.anchors)
977
+ overall_pass=g1_pass and g2_pass
978
+
979
+ improved=final_ce<best_ce
980
+ if improved or overall_pass or random.random()<0.002:
981
+ hero_traces.append(HypothesisTrace(
982
+ hid=ray.ray_id,round_idx=total_fired-len(batch_rays)+ray_idx,
983
+ ray_name=ray.name,ray_type=ray.ray_type,
984
+ ce_before=parent_ce,ce_after=final_ce,
985
+ survived=overall_pass,elapsed_ms=elapsed_ms,
986
+ g1_pass=g1_pass,g2_pass=g2_pass,
987
+ binding={k:round(v,5) for k,v in final_b.items()},
988
+ invariant_results=inv_results,
989
+ tension_class=tension_class,depth=ray.depth))
990
+
991
+ out_baton=Baton(binding=final_b,ce=final_ce,ray_id=ray.ray_id,
992
+ l9=L9Certificate(final_ce,dom_vars,[],tension_class))
993
+
994
+ if final_ce<BATON_REGISTRY_THRESHOLD:
995
+ if ray.ray_id in baton_registry:
996
+ baton_registry[ray.ray_id]=out_baton
997
+ else:
998
+ if len(baton_registry)>=BATON_REGISTRY_MAX:
999
+ old_key=baton_registry_keys.popleft()
1000
+ baton_registry.pop(old_key,None)
1001
+ baton_registry[ray.ray_id]=out_baton
1002
+ baton_registry_keys.append(ray.ray_id)
1003
+
1004
+ if improved:
1005
+ best_ce=final_ce; best_binding=dict(final_b)
1006
+ successful_rays.append(ray)
1007
+ model.best_binding=best_binding; model.best_ce=best_ce
1008
+
1009
+ if overall_pass: solved=True
1010
+
1011
+ if improved and len(ray.sequence)>=2:
1012
+ pair=(ray.sequence[-2],ray.sequence[-1])
1013
+ if pair not in resonant_pairs: resonant_pairs.append(pair)
1014
+ model.resonant_pairs=resonant_pairs
1015
+
1016
+ earned=(final_ce<parent_ce*CE_EARN_RATIO or ray.depth<1)
1017
+ scout_earned=(ray.ray_type=="scout" and final_ce<SCOUT_CE_THRESHOLD)
1018
+
1019
+ if earned or scout_earned:
1020
+ remaining=[a for a in ALL_AXIOMS
1021
+ if a!=ray.sequence[-1]
1022
+ and frozenset([ray.sequence[-1],a]) not in _CONTRA_SET
1023
+ and a not in ray.sequence]
1024
+
1025
+ new_children:List[AxiomRay]=[]
1026
+ new_children.extend(
1027
+ CREATIVE_SEEDER.intelligent_branch(ray,out_baton,remaining,BRANCH_WIDTH))
1028
+ inv=CREATIVE_SEEDER.invert(ray,final_ce)
1029
+ if inv: new_children.append(inv)
1030
+
1031
+ if len(successful_rays)>=2:
1032
+ candidates=[r for r in successful_rays if r.ray_id!=ray.ray_id]
1033
+ if candidates:
1034
+ partner=random.choice(candidates)
1035
+ ba=baton_registry.get(ray.ray_id,out_baton)
1036
+ bb=baton_registry.get(partner.ray_id)
1037
+ if bb is not None:
1038
+ new_children.extend(CREATIVE_SEEDER.recombine(ray,partner,ba,bb)[:3])
1039
+
1040
+ new_children.extend(
1041
+ CREATIVE_SEEDER.resonance_extensions(ray,final_ce,resonant_pairs))
1042
+
1043
+ for child in new_children:
1044
+ if child.ray_type in ("seed","branch","target"): queues["core"].append(child)
1045
+ elif child.ray_type in ("scout","tension"): queues["explore"].append(child)
1046
+ elif child.ray_type in ("recombine","invert"): queues["genetic"].append(child)
1047
+
1048
+ queues["core"].sort(key=lambda r:r.ce_prior+(r.depth*0.0001))
1049
+ queues["genetic"].sort(key=lambda r:r.ce_prior+(r.depth*0.0001))
1050
+ if solved: break
1051
+
1052
+ return best_binding,best_ce,hero_traces,total_fired,allosteric_counts
1053
+
1054
+
1055
+ # ══════════════════════════════════════════════════════════════════
1056
+ # SECTION 12: LLM BRIDGE (GOOGLE GENAI 3.1)
1057
+ # ══════════════════════════════════════════════════════════════════
1058
+
1059
+ ENCODER_SYSTEM_PROMPT = """You are the Encoder for the Practicality Wisdom Layer β€” a neuro-symbolic
1060
+ constraint satisfaction engine. Your job is STRICT SLOT-FILLING, not reasoning.
1061
+ You read a natural language problem description and produce a structured
1062
+ JSON object that represents its mathematical constraints.
1063
+
1064
+ You do NOT solve the problem. You do NOT guess the structural topology. You only translate.
1065
+
1066
+ ══════════════════════════════════════════════════════════════════
1067
+ PART 1: THE AXL STRUCTURE
1068
+ ══════════════════════════════════════════════════════════════════
1069
+
1070
+ You must output a single JSON object matching this schema exactly:
1071
+
1072
+ {
1073
+ "name": string, // CamelCase, no spaces
1074
+ "description": string, // one sentence
1075
+ "variables": [
1076
+ {
1077
+ "name": string, // short lowercase identifier (x, v1, theta, etc.)
1078
+ "lo": number, // OPTIONAL: ONLY provide if the problem explicitly limits this (e.g., > 0)
1079
+ "hi": number // OPTIONAL: ONLY provide if the problem explicitly limits this
1080
+ }
1081
+ ],
1082
+ "constraints": [
1083
+ {
1084
+ "kind": "EQ" | "GEQ" | "LEQ" | "CONSERVE",
1085
+ "expr": string, // valid Python/sympy expression using variable names
1086
+ "weight": number // 1.0 default; 5.0 for hard constraints; 10.0 for conservation laws
1087
+ }
1088
+ ],
1089
+ "anchors": [
1090
+ {
1091
+ "name": string, // snake_case identifier for this condition
1092
+ "expr": string, // sympy expression that equals 0 when satisfied
1093
+ "tolerance": number, // how close to 0 counts as solved (default 0.001)
1094
+ "mode": "eq" | "geq" | "leq"
1095
+ }
1096
+ ],
1097
+ "observations": {
1098
+ "variable_name": number // variables whose values are STRICTLY KNOWN/FIXED in the prompt
1099
+ }
1100
+ }
1101
+
1102
+ CRITICAL: Output ONLY the JSON object. No preamble, no markdown fences.
1103
+
1104
+ ══════════════════════════════════════════════════════════════════
1105
+ PART 2: ENCODING RULES
1106
+ ══════════════════════════════════════════════════════════════════
1107
+
1108
+ VARIABLES & BOUNDS:
1109
+ β€’ Use short names: x, y, v, w1, cb
1110
+ β€’ DO NOT INVENT BOUNDS. If the text says "probabilities", set lo=0.0, hi=1.0.
1111
+ If the text says nothing, omit "lo" and "hi" entirely.
1112
+ β€’ Fixed values go in "observations", not as tight bounds.
1113
+ β€’ DO include observed variables in the variables list. The engine handles pinning.
1114
+
1115
+ CONSTRAINTS & ANCHORS:
1116
+ β€’ EQ: expr = 0 (e.g., "x**2 + y**2 - 1.0")
1117
+ β€’ GEQ: expr β‰₯ 0
1118
+ β€’ LEQ: expr ≀ 0
1119
+ β€’ CONSERVE: inviolable laws (weight 10.0 automatically)
1120
+ β€’ Expressions must be valid Python/sympy: use **, *, +, -, /
1121
+ β€’ Do NOT use =, >=, <= in expressions β€” only the expression that evaluates against 0.
1122
+ β€’ Anchors are the verification conditions. What MUST be true to accept the solution?
1123
+
1124
+ ══════════════════════════════════════════════════════════════════
1125
+ PART 3: FAILURE HANDLING
1126
+ ══════════════════════════════════════════════════════════════════
1127
+
1128
+ If a previous solve attempt is included in the user message showing
1129
+ failed invariants, revise the encoding to better capture those conditions.
1130
+ Common fixes:
1131
+ β€’ Invariant fails with large value β†’ increase constraint weight
1132
+ β€’ Wrong sign β†’ swap GEQ to LEQ or negate expr
1133
+ β€’ Missing constraint β†’ add missing physical law as EQ or CONSERVE
1134
+
1135
+ Output ONLY the JSON. No preamble.
1136
+ """
1137
+
1138
+ COLLAPSER_SYSTEM_PROMPT = """You are the Collapser for the Practicality Wisdom Layer. You receive
1139
+ a JSON object containing:
1140
+ β€’ The original natural language problem
1141
+ β€’ The problem name and description
1142
+ β€’ Whether the solver succeeded (solved/partial/unsolved)
1143
+ β€’ The verified variable binding (the numerical solution)
1144
+ β€’ Gate 1 result: global constraint energy (mathematical validity)
1145
+ β€’ Gate 2 results: named invariant checks (domain correctness)
1146
+ β€’ The best axiom ray sequence that found the solution
1147
+ β€’ Total rays fired
1148
+
1149
+ Your job is to translate this back into clear, domain-appropriate
1150
+ natural language that a human expert in the problem domain would
1151
+ recognize as correct and useful.
1152
+
1153
+ ══════════════════════════════════════════════════════════════════
1154
+ TONE AND FORMAT RULES
1155
+ ══════════════════════════════════════════════════════════════════
1156
+ β€’ Write for a domain expert, not a general audience
1157
+ β€’ Use domain-appropriate variable names and units if discernible
1158
+ β€’ Lead with the status: SOLVED / PARTIAL / UNSOLVED
1159
+ β€’ Report the binding values in the order they appear in the problem
1160
+ β€’ For each Gate 2 invariant:
1161
+ - If passed: briefly confirm what physical condition is satisfied
1162
+ - If failed: explain specifically what condition was NOT met
1163
+ and by how much (the numerical value is provided)
1164
+ β€’ Keep it under 250 words
1165
+ β€’ End with the axiom sequence that found the solution, formatted
1166
+ as a one-line note
1167
+ """
1168
+
1169
+ def encode_problem_to_axl(problem_text: str, api_key: str) -> AXLProblemDef:
1170
+ """Uses Google GenAI to slot-fill the text into the AXL structural JSON."""
1171
+ if not api_key:
1172
+ raise ValueError("Gemini API Key is missing. Please provide it in the UI or set it globally.")
1173
+
1174
+ client = genai.Client(api_key=api_key)
1175
+
1176
+ config = types.GenerateContentConfig(
1177
+ system_instruction=[types.Part.from_text(text=ENCODER_SYSTEM_PROMPT)],
1178
+ response_mime_type="application/json",
1179
+ temperature=0.1,
1180
+ )
1181
+
1182
+ response = client.models.generate_content(
1183
+ model=GEMINI_MODEL,
1184
+ contents=problem_text,
1185
+ config=config,
1186
+ )
1187
+
1188
+ return _parse_axl_json(response.text)
1189
+
1190
+ def _parse_axl_json(raw_json: str) -> AXLProblemDef:
1191
+ """Parses Gemini JSON output into the strict AXL structural definition."""
1192
+ text = raw_json.strip()
1193
+ if text.startswith("```"):
1194
+ text = text.split("```")[1]
1195
+ if text.startswith("json"):
1196
+ text = text[4:]
1197
+ text = text.strip().rstrip("`").strip()
1198
+
1199
+ d = json.loads(text)
1200
+ anchors = [
1201
+ AXLInvariant(
1202
+ name=a["name"], expr=a["expr"],
1203
+ tolerance=a.get("tolerance", VERIFY_G2_TOLERANCE),
1204
+ mode=a.get("mode", "eq")
1205
+ )
1206
+ for a in d.get("anchors", [])
1207
+ ]
1208
+
1209
+ variables = []
1210
+ for v in d["variables"]:
1211
+ variables.append({
1212
+ "name": v["name"],
1213
+ "lo": float(v.get("lo", -1e18)),
1214
+ "hi": float(v.get("hi", 1e18))
1215
+ })
1216
+
1217
+ return AXLProblemDef(
1218
+ name=d["name"],
1219
+ description=d.get("description", ""),
1220
+ axioms=set(), # Engine owns Axioms, not the LLM
1221
+ variables=variables,
1222
+ constraints=d.get("constraints", []),
1223
+ scopes=d.get("scopes", []),
1224
+ anchors=anchors,
1225
+ observations=d.get("observations", {}),
1226
+ )
1227
+
1228
+ def collapse_solution(
1229
+ problem_text: str,
1230
+ axl_def: AXLProblemDef,
1231
+ binding: Dict[str, float],
1232
+ g1_pass: bool,
1233
+ g2_pass: bool,
1234
+ invariant_results: Dict[str, Tuple[float, bool]],
1235
+ best_ray: str,
1236
+ total_fired: int,
1237
+ api_key: str
1238
+ ) -> str:
1239
+ """Uses Google GenAI with high-level thinking to collapse PyTorch findings to Natural Language."""
1240
+ if not api_key:
1241
+ return "ERROR: Gemini API Key required for semantic collapsing."
1242
+
1243
+ client = genai.Client(api_key=api_key)
1244
+
1245
+ payload = {
1246
+ "original_problem": problem_text,
1247
+ "problem_name": axl_def.name,
1248
+ "solved": g1_pass and g2_pass,
1249
+ "gate1_pass": g1_pass,
1250
+ "gate2_pass": g2_pass,
1251
+ "binding": {k: round(v, 6) for k, v in binding.items()},
1252
+ "invariant_results":invariant_results,
1253
+ "best_ray_sequence":best_ray,
1254
+ "rays_fired": total_fired,
1255
+ }
1256
+
1257
+ config = types.GenerateContentConfig(
1258
+ thinking_config=types.ThinkingConfig(thinking_level="HIGH"),
1259
+ system_instruction=[types.Part.from_text(text=COLLAPSER_SYSTEM_PROMPT)],
1260
+ temperature=0.2,
1261
+ )
1262
+
1263
+ response = client.models.generate_content(
1264
+ model=GEMINI_MODEL,
1265
+ contents=json.dumps(payload, indent=2),
1266
+ config=config,
1267
+ )
1268
+
1269
+ return response.text
1270
+
1271
+ # ══════════════════════════════════════════════════════════════════
1272
+ # SECTION 13: STREAMING SOLVER RUNNER WITH FEEDBACK LOOP
1273
+ # ══════════════════════════════════════════════════════════════════
1274
+
1275
+ def run_solve_streaming(original_problem_text: str, api_key: str):
1276
+ """
1277
+ Generator that wires Encode β†’ Compile β†’ PyTorch Solve β†’ Verify β†’ Collapse.
1278
+ Includes the self-correcting feedback loop if Gate 2 (Invariants) fails.
1279
+ """
1280
+ log_lines: List[str] = []
1281
+ answer_text = ""
1282
+ MAX_ATTEMPTS = 3
1283
+
1284
+ def emit(line: str):
1285
+ log_lines.append(line)
1286
+ return "\n".join(log_lines), answer_text
1287
+
1288
+ current_text = original_problem_text
1289
+ used_key = api_key or DEFAULT_GEMINI_API_KEY
1290
+
1291
+ for attempt in range(1, MAX_ATTEMPTS + 1):
1292
+ yield emit(f"πŸ” **Encoding problem (Attempt {attempt}/{MAX_ATTEMPTS})…**\n")
1293
+ t0 = time.time()
1294
+
1295
+ try:
1296
+ axl_def = encode_problem_to_axl(current_text, used_key)
1297
+ except Exception as e:
1298
+ yield emit(f"βœ— Encoding failed: {e}\n")
1299
+ return
1300
+
1301
+ enc_ms = round((time.time() - t0) * 1000)
1302
+ yield emit(
1303
+ f"βœ“ **Encoded** as `{axl_def.name}` ({enc_ms} ms)\n"
1304
+ f" Variables: `{', '.join(v['name'] for v in axl_def.variables)}`\n"
1305
+ f" Anchors: {len(axl_def.anchors)}\n\n"
1306
+ f"βš™ **Compiling AXL β†’ PSL…**\n"
1307
+ )
1308
+
1309
+ try:
1310
+ base_prob = build_base_problem(axl_def)
1311
+ except Exception as e:
1312
+ yield emit(f"βœ— Compilation failed: {e}\n")
1313
+ return
1314
+
1315
+ yield emit(
1316
+ f"βœ“ **Compiled**: {len(base_prob.variables)} vars, "
1317
+ f"{len(base_prob.compiled_constraints)} constraints\n"
1318
+ f" Bilinear pairs: {len(base_prob.bilinear_pairs)}\n"
1319
+ f" Monotone targets: {len(base_prob.monotone_targets)}\n\n"
1320
+ f"πŸš€ **Running wisdom layer (PyTorch Search)…**\n"
1321
+ )
1322
+
1323
+ result_q: queue.Queue = queue.Queue()
1324
+
1325
+ def _worker():
1326
+ try:
1327
+ tracer = SequenceRayTracer()
1328
+ binding, ce, traces, total_fired, allosteric = tracer.trace(axl_def, base_prob)
1329
+ result_q.put(("ok", binding, ce, traces, total_fired, allosteric))
1330
+ except Exception as e:
1331
+ result_q.put(("err", str(e)))
1332
+
1333
+ worker_thread = threading.Thread(target=_worker, daemon=True)
1334
+ worker_thread.start()
1335
+
1336
+ dots = 0
1337
+ while worker_thread.is_alive():
1338
+ worker_thread.join(timeout=3.0)
1339
+ if worker_thread.is_alive():
1340
+ dots += 1
1341
+ yield emit(f" Β·{'Β·' * dots} still searching ({dots * 3}s)…\n")
1342
+
1343
+ if result_q.empty():
1344
+ yield emit("βœ— Solver returned no result.\n")
1345
+ return
1346
+
1347
+ result = result_q.get()
1348
+ if result[0] == "err":
1349
+ yield emit(f"βœ— Solver error: {result[1]}\n")
1350
+ return
1351
+
1352
+ _, binding, ce, traces, total_fired, allosteric = result
1353
+ survived = [t for t in traces if t.survived]
1354
+ best_t = min(survived, key=lambda t: t.ce_after, default=None)
1355
+
1356
+ for inv in axl_def.anchors:
1357
+ if not inv.compiled_func:
1358
+ inv.compile(base_prob.variables)
1359
+
1360
+ g1_ce = base_prob.scalar_energy(binding)
1361
+ g1_pass = g1_ce < VERIFY_G1_THRESHOLD
1362
+ inv_results: Dict[str, Tuple[float, bool]] = {}
1363
+ for inv in axl_def.anchors:
1364
+ try:
1365
+ val = float(inv.compiled_func(*[binding.get(v, 0.0) for v in inv.syms_used]))
1366
+ err = abs(val)
1367
+ if inv.mode == "eq": passed = err < inv.tolerance
1368
+ elif inv.mode == "geq": passed = val >= -inv.tolerance
1369
+ else: passed = val <= inv.tolerance
1370
+ inv_results[inv.name] = (round(val, 6), passed)
1371
+ except:
1372
+ inv_results[inv.name] = (999.0, False)
1373
+ g2_pass = all(v[1] for v in inv_results.values())
1374
+
1375
+ solved_str = "βœ“ SOLVED" if (g1_pass and g2_pass) else "~ PARTIAL" if g1_pass else "βœ— UNSOLVED"
1376
+ g2_detail = " | ".join(f"{'βœ“' if ok else 'βœ—'} {k}" for k, (_, ok) in inv_results.items())
1377
+ best_ray = best_t.ray_name if best_t else "β€”"
1378
+
1379
+ yield emit(
1380
+ f"\n{solved_str}\n"
1381
+ f" CE = {ce:.6f} | Gate 1: {'βœ“' if g1_pass else 'βœ—'} | Gate 2: {'βœ“' if g2_pass else 'βœ—'}\n"
1382
+ f" {g2_detail}\n"
1383
+ f" Best ray: `{best_ray}`\n"
1384
+ f" Rays fired: {total_fired:,} | Survived: {len(survived)}\n"
1385
+ f" Allosteric: isolated {allosteric.get('isolated',0):,} | relational {allosteric.get('relational',0):,} | systemic {allosteric.get('systemic',0):,}\n\n"
1386
+ )
1387
+
1388
+ # ── Self-Correcting Feedback Loop (Gate 2 Failure) ───────────────
1389
+ if not g2_pass and attempt < MAX_ATTEMPTS:
1390
+ failed_invs = {k: v for k, (v, ok) in inv_results.items() if not ok}
1391
+ current_text = original_problem_text + f"\n\nPREVIOUS ATTEMPT FAILED:\nGate 2 invariants that did not pass:\n{json.dumps(failed_invs, indent=2)}\n\nPlease revise the constraint encoding to better capture these conditions."
1392
+ yield emit(f"⚠ **Gate 2 Failed.** Retrying ({len(failed_invs)} invariants failed) with structural feedback...\n\n---\n")
1393
+ continue
1394
+
1395
+ yield emit(f"πŸ”€ **Collapsing to domain language…**\n")
1396
+ try:
1397
+ answer_text = collapse_solution(
1398
+ original_problem_text, axl_def, binding,
1399
+ g1_pass, g2_pass, inv_results,
1400
+ best_ray, total_fired, used_key
1401
+ )
1402
+ yield emit("βœ“ Done.\n")
1403
+ except Exception as e:
1404
+ answer_text = f"Error during collapsing: {e}"
1405
+ yield emit("βœ— Collapsing failed.\n")
1406
+
1407
+ break
1408
+
1409
+ # ══════════════════════════════════════════════════════════════════
1410
+ # SECTION 14: GRADIO INTERFACE
1411
+ # ══════════════════════════════════════════════════════════════════
1412
+
1413
+ CSS = """
1414
+ body { background: #090909; color: #e0e0e0; font-family: monospace; }
1415
+ .solver-log { font-size: 0.75em; background: #0d0d0d; border: 1px solid #181818; }
1416
+ .answer-box { font-size: 0.85em; background: #0d0d0d; border: 1px solid #222; }
1417
+ .status-bar { font-size: 0.70em; color: #555; padding: 4px 0; }
1418
+ """
1419
+
1420
+ PLACEHOLDER_HINT = textwrap.dedent("""
1421
+ Try any problem description. Gemini will extract the mathematical structure.
1422
+
1423
+ Example inputs:
1424
+ β€’ "Find points on the elliptic curve yΒ²=xΒ³-x+1 where x=0"
1425
+ β€’ "Solve a DC power flow problem with 3 buses where p1+p2+p3=1"
1426
+ β€’ "Markowitz portfolio with 4 assets, returns [10%, 12%, 8%, 15%], target return 11%"
1427
+ """).strip()
1428
+
1429
+ with gr.Blocks(css=CSS, title="Practicality Unified Server") as demo:
1430
+
1431
+ gr.Markdown(
1432
+ "## βš— Practicality Unified Server\n"
1433
+ "`[Gemini 3.1 Encoder] β†’ AXL/PSL β†’ [PyTorch Ray Solver] β†’ [Gemini 3.1 Collapser]`"
1434
+ )
1435
+
1436
+ with gr.Row():
1437
+ with gr.Column(scale=2):
1438
+ api_key_input = gr.Textbox(
1439
+ label="Gemini API Key",
1440
+ type="password",
1441
+ placeholder="AIza...",
1442
+ value=DEFAULT_GEMINI_API_KEY,
1443
+ elem_classes=["solver-log"]
1444
+ )
1445
+ problem_input = gr.Textbox(
1446
+ label="Problem Description",
1447
+ placeholder=PLACEHOLDER_HINT,
1448
+ lines=5,
1449
+ elem_classes=["solver-log"],
1450
+ )
1451
+ with gr.Row():
1452
+ solve_btn = gr.Button("βš— Solve", variant="primary")
1453
+ clear_btn = gr.Button("Clear")
1454
+
1455
+ with gr.Column(scale=1):
1456
+ gr.Markdown("**Device & Mode**")
1457
+ gr.Textbox(
1458
+ value=f"Compute: {DEVICE.type.upper()}\n"
1459
+ f"Axioms: {len(ALL_AXIOMS)}\n"
1460
+ f"Max rays: {MAX_TOTAL_RAYS:,}\n"
1461
+ f"Batch: {RAY_BATCH_SIZE:,}\n"
1462
+ f"LLM: {GEMINI_MODEL}",
1463
+ lines=5, interactive=False,
1464
+ elem_classes=["solver-log"],
1465
+ )
1466
+
1467
+ with gr.Row():
1468
+ solver_log = gr.Textbox(
1469
+ label="Solver Log",
1470
+ lines=18,
1471
+ interactive=False,
1472
+ elem_classes=["solver-log"],
1473
+ )
1474
+ answer_box = gr.Textbox(
1475
+ label="Answer",
1476
+ lines=18,
1477
+ interactive=False,
1478
+ elem_classes=["answer-box"],
1479
+ )
1480
+
1481
+ def _solve(problem_text: str, api_key: str):
1482
+ if not problem_text.strip():
1483
+ yield "No problem text provided.", ""
1484
+ return
1485
+ for log_chunk, answer_chunk in run_solve_streaming(problem_text, api_key):
1486
+ yield log_chunk, answer_chunk
1487
+
1488
+ solve_btn.click(
1489
+ fn=_solve,
1490
+ inputs=[problem_input, api_key_input],
1491
+ outputs=[solver_log, answer_box],
1492
+ show_progress=False,
1493
+ )
1494
+
1495
+ clear_btn.click(
1496
+ fn=lambda: ("", "", ""),
1497
+ inputs=[],
1498
+ outputs=[problem_input, solver_log, answer_box],
1499
+ )
1500
+
1501
+ gr.Markdown(
1502
+ "Built with Gradio Β· Practicality 22.1 Β· Strict Neuro-Symbolic Boundary",
1503
+ elem_classes=["status-bar"]
1504
+ )
1505
+
1506
+ demo.queue()
1507
+
1508
+ if __name__ == "__main__":
1509
+ print(f"[UNIFIED SERVER] Compute: {DEVICE.type.upper()}")
1510
+ print(f"[UNIFIED SERVER] System 22.1: GenAI SDK wired. Model: {GEMINI_MODEL}")
1511
+ demo.launch(server_name="0.0.0.0", server_port=7860, share=False)