everydaytok commited on
Commit
9fe0637
Β·
verified Β·
1 Parent(s): 9f4ec78

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +522 -461
app.py CHANGED
@@ -1,14 +1,16 @@
1
  #!/usr/bin/env python3
2
  """
3
- PRACTICALITY SYSTEM 11.0 β€” AXL/PSL PIPELINE
4
  ═══════════════════════════════════════════════════════════════════
5
- NEW:
6
- Β· PSL 2.0: CONSERVE, WEIGHT, BRANCH, LINK, ORDER, ANNOTATE
7
- Β· AXL layer: hardcoded problem definitions as Python structs
8
- Β· AXL β†’ PSL compiler: deterministic rule table, no LLM
9
- Β· Verify layer: Gate 1 (structural CE) + Gate 2 (invariants)
10
- Β· HypothesisTrace: full survival log per run
11
- Β· Dashboard: collapse view ready for screenshot
 
 
12
  """
13
 
14
  import time, random, math, threading, asyncio, warnings, itertools, json
@@ -30,7 +32,7 @@ import uvicorn
30
  warnings.filterwarnings("ignore")
31
  USE_GPU = torch.cuda.is_available()
32
  DEVICE = torch.device("cuda" if USE_GPU else "cpu")
33
- print(f"[SYSTEM 11.0] Compute: {'GPU' if USE_GPU else 'CPU'}")
34
 
35
  # ══════════════════════════════════════════════════════════════════════
36
  # SECTION 1: CONSTANTS
@@ -127,14 +129,11 @@ def compile_iv(expr,variables):
127
  @dataclass
128
  class PSLVar: name:str; lo:float; hi:float; weight:float=1.0
129
  @dataclass
130
- class PSLConstraint: kind:str; expr:str; scope:str="root"; weight:float=1.0; tolerance:float=SOLVE_THRESHOLD; branches:List[str]=field(default_factory=list); annotation:str=""
131
  @dataclass
132
  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)
133
  @dataclass
134
- class PSLProgram:
135
- name:str; vars:List[PSLVar]; constraints:List[PSLConstraint]
136
- scopes:List[PSLScope]; scope_order:List[str]
137
- annotations:Dict[str,str]=field(default_factory=dict) # var β†’ axl_field
138
 
139
  def parse_psl(text:str) -> PSLProgram:
140
  lines=[l.strip() for l in text.strip().split('\n') if l.strip() and not l.strip().startswith('#')]
@@ -143,18 +142,14 @@ def parse_psl(text:str) -> PSLProgram:
143
  def advance():
144
  nonlocal i; l=lines[i]; i+=1; return l
145
  def peek(): return lines[i] if i<len(lines) else ""
146
- def pvar(rest,wt=1.0):
147
  p=rest.split()
148
- if len(p)>=3:
149
- w=float(p[3]) if len(p)>3 and p[3].replace('.','').replace('-','').isdigit() else wt
150
- return PSLVar(p[0],float(p[1]),float(p[2]),w)
151
- return None
152
- def parse_weight(rest):
153
- """Extract WEIGHT value from end of constraint string."""
154
  if 'WEIGHT' in rest:
155
- parts=rest.split('WEIGHT'); return parts[0].strip(), float(parts[1].split()[0])
156
  return rest.strip(), 1.0
157
- def parse_scope_body(sname, order=0, depends=None):
158
  svars,scons,links=[],[],[]
159
  while i<len(lines):
160
  l=peek(); tk=l.split(None,1)
@@ -164,20 +159,17 @@ def parse_psl(text:str) -> PSLProgram:
164
  if kw=="VAR":
165
  v=pvar(rest)
166
  if v: svars.append(v)
167
- elif kw=="LINK":
168
- links.extend(rest.split())
169
  elif kw in ("EQ","GEQ","LEQ"):
170
- expr,wt=parse_weight(rest)
171
- scons.append(PSLConstraint(kw.lower(),expr,scope=sname,weight=wt))
172
  elif kw=="CONSERVE":
173
- expr,_=parse_weight(rest)
174
- scons.append(PSLConstraint("eq",expr,scope=sname,weight=10.0))
175
  elif kw=="BRANCH":
176
  opts=[]
177
- while i<len(lines) and not peek().startswith("END"):
178
  bl=advance()
179
  if bl.upper().startswith("OPTION"): opts.append(bl.split(None,1)[1].strip())
180
- advance() # END BRANCH
181
  scons.append(PSLConstraint("or_eq","",scope=sname,weight=1.0,branches=opts))
182
  elif kw=="ANNOTATE":
183
  parts=rest.split()
@@ -192,11 +184,9 @@ def parse_psl(text:str) -> PSLProgram:
192
  v=pvar(rest)
193
  if v: prog.vars.append(v)
194
  elif kw in ("EQ","GEQ","LEQ"):
195
- expr,wt=parse_weight(rest)
196
- prog.constraints.append(PSLConstraint(kw.lower(),expr,weight=wt))
197
  elif kw=="CONSERVE":
198
- expr,_=parse_weight(rest)
199
- prog.constraints.append(PSLConstraint("eq",expr,weight=10.0))
200
  elif kw=="BRANCH":
201
  opts=[]
202
  while i<len(lines) and not peek().upper().startswith("END"):
@@ -218,9 +208,8 @@ def parse_psl(text:str) -> PSLProgram:
218
  idx=rest.upper().index("DEPENDS")
219
  depends=rest[idx:].split()[1:]
220
  sc=parse_scope_body(sname,order,depends)
221
- prog.scopes.append(sc)
222
- prog.scope_order.append(sname)
223
- prog.scopes.sort(key=lambda s: s.order)
224
  prog.scope_order=[s.name for s in prog.scopes]
225
  return prog
226
 
@@ -228,8 +217,8 @@ def parse_psl(text:str) -> PSLProgram:
228
  class ExpandedProblem:
229
  variables:List[str]; bounds:Dict[str,Tuple[float,float]]
230
  constraints:List['Constraint']
231
- scope_groups:Dict[str,List[int]]; scope_vars:Dict[str,List[str]]; scope_order:List[str]
232
- annotations:Dict[str,str]=field(default_factory=dict)
233
 
234
  def expand_psl(prog:PSLProgram) -> ExpandedProblem:
235
  variables=[]; bounds={}; constraints=[]
@@ -275,89 +264,60 @@ def expand_psl(prog:PSLProgram) -> ExpandedProblem:
275
  prog.scope_order,prog.annotations)
276
 
277
  # ══════════════════════════════════════════════════════════════════════
278
- # SECTION 4: AXL LAYER β€” DATACLASSES + COMPILER
279
  # ══════════════════════════════════════════════════════════════════════
280
  @dataclass
281
  class AXLInvariant:
282
- name: str
283
- expr: str
284
- tolerance: float = VERIFY_G2_TOLERANCE
285
 
286
  @dataclass
287
  class AXLProblemDef:
288
- name: str
289
- description: str
290
- axioms: Set[str]
291
- variables: List[Dict] # {name, lo, hi, tends, observed}
292
- constraints: List[Dict] # {kind: EQ/GEQ/LEQ/CONSERVE, expr, weight}
293
- scopes: List[Dict] # {name, order, vars, constraints}
294
- anchors: List[AXLInvariant] # verification oracles
295
- observations: Dict[str,float] = field(default_factory=dict)
296
-
297
  def variables_list(self): return [v["name"] for v in self.variables]
298
 
299
  class AXLtoPSLCompiler:
300
- """Deterministic rule-based compiler. No LLM. No judgment calls."""
301
-
302
- TEND_WEIGHT = 0.3
303
-
304
- def compile(self, prob: AXLProblemDef) -> str:
305
- lines = [f"SPACE {prob.name}"]
306
-
307
- # Global variables
308
  for v in prob.variables:
309
- lo, hi = v["lo"], v["hi"]
310
- # Observations β†’ zero-width bound
311
  if v["name"] in prob.observations:
312
- val = prob.observations[v["name"]]
313
- lo = hi = val
314
  lines.append(f"VAR {v['name']} {lo} {hi}")
315
- # ANNOTATE for reverse mapping
316
  lines.append(f"ANNOTATE {v['name']} FROM_AXL {prob.name}.{v['name']}")
317
-
318
- # Global constraints
319
  for c in prob.constraints:
320
- wt = c.get("weight", 1.0)
321
- kw = "CONSERVE" if c["kind"] == "CONSERVE" else c["kind"]
322
- if kw == "CONSERVE":
323
- lines.append(f"CONSERVE {c['expr']}")
324
- else:
325
- lines.append(f"{kw} {c['expr']} WEIGHT {wt}")
326
-
327
- # TEND β†’ soft constraints (global vars)
328
  for v in prob.variables:
329
- tends = v.get("tends", "")
330
- if tends == "MIN":
331
- lines.append(f"EQ {v['name']} - {v['lo']} WEIGHT {self.TEND_WEIGHT}")
332
- elif tends == "MAX":
333
- lines.append(f"EQ {v['name']} - {v['hi']} WEIGHT {self.TEND_WEIGHT}")
334
- elif tends.startswith("TOWARD:"):
335
- val = tends.split(":")[1]
336
- lines.append(f"EQ {v['name']} - {val} WEIGHT {self.TEND_WEIGHT}")
337
-
338
- # Scopes
339
  for sc in prob.scopes:
340
- dep_str = f" DEPENDS {' '.join(sc.get('depends',[]))}" if sc.get('depends') else ""
341
- lines.append(f"SCOPE {sc['name']} ORDER {sc.get('order',0)}{dep_str}")
342
- sv = sc.get("vars", [])
343
- if sv:
344
- lines.append(f" LINK {' '.join(sv)}")
345
- for c in sc.get("constraints", []):
346
- wt = c.get("weight", 1.0)
347
- if c["kind"] == "CONSERVE":
348
- lines.append(f" CONSERVE {c['expr']}")
349
- elif c["kind"] == "BRANCH":
350
  lines.append(f" BRANCH {c.get('name','br')}")
351
- for opt in c.get("options", []):
352
- lines.append(f" OPTION {opt}")
353
  lines.append(" END BRANCH")
354
- else:
355
- lines.append(f" {c['kind']} {c['expr']} WEIGHT {wt}")
356
  lines.append("END")
357
-
358
  return "\n".join(lines)
359
 
360
- AXL_COMPILER = AXLtoPSLCompiler()
361
 
362
  # ══════════════════════════════════════════════════════════════════════
363
  # SECTION 5: PROBLEM & CONSTRAINT COMPILATION
@@ -373,7 +333,6 @@ class MathConstraint:
373
  fast_iv:Optional[Callable]=field(default=None,repr=False)
374
  fast_pt:Optional[Callable]=field(default=None,repr=False)
375
  fast_np:Optional[Callable]=field(default=None,repr=False)
376
- fast_torch:Optional[Callable]=field(default=None,repr=False)
377
  syms_used:List[str]=field(default_factory=list)
378
  parsed:Optional[sp.Expr]=field(default=None,repr=False)
379
  scope:str="root"
@@ -405,10 +364,6 @@ def compile_mc(kind,expr_str,direction,variables,weight=1.0,scope="root",branche
405
  mc.fast_iv=compile_iv(parsed,variables)
406
  mc.fast_pt=sp.lambdify([sp.Symbol(v) for v in mc.syms_used],parsed,modules="math")
407
  mc.fast_np=sp.lambdify([sp.Symbol(v) for v in mc.syms_used],parsed,modules="numpy")
408
- if USE_GPU:
409
- _tops={"sin":torch.sin,"cos":torch.cos,"exp":torch.exp,"sqrt":torch.sqrt,"log":torch.log,"Abs":torch.abs}
410
- try: mc.fast_torch=sp.lambdify([sp.Symbol(v) for v in mc.syms_used],parsed,modules=[_tops,"math"])
411
- except: pass
412
  fast_jac={}
413
  for v in mc.syms_used:
414
  try:
@@ -422,8 +377,7 @@ def compile_mc(kind,expr_str,direction,variables,weight=1.0,scope="root",branche
422
  for sym in parsed.free_symbols:
423
  v_str=str(sym)
424
  try:
425
- sols=sp.solve(parsed,sym)
426
- pm[v_str]=[]
427
  for sol in sols:
428
  fs=list(sol.free_symbols)
429
  pm[v_str].append({"syms":[str(s) for s in fs],"func":sp.lambdify(fs,sol,modules="math")})
@@ -435,8 +389,7 @@ def compile_mc(kind,expr_str,direction,variables,weight=1.0,scope="root",branche
435
 
436
  @dataclass
437
  class Problem:
438
- pid:str; variables:List[str]
439
- constraints:List[Constraint]; bounds:Dict[str,Tuple[float,float]]
440
  tier:str="hard"
441
  scope_groups:Dict[str,List[int]]=field(default_factory=dict)
442
  scope_vars:Dict[str,List[str]]=field(default_factory=dict)
@@ -462,9 +415,9 @@ class Problem:
462
  if mc.fast_pt is None: total+=1.0; continue
463
  try:
464
  val=float(mc.fast_pt(*[b.get(v,0.0) for v in mc.syms_used]))
465
- if mc.kind=="equality": total+=abs(val)*mc.weight
466
- elif mc.direction=="geq": total+=max(0.0,-val)*mc.weight
467
- else: total+=max(0.0,val)*mc.weight
468
  except: total+=1.0
469
  for v,(lo,hi) in self.bounds.items():
470
  bv=b.get(v,0.0); total+=max(0.0,lo-bv)+max(0.0,bv-hi)
@@ -515,7 +468,8 @@ class Problem:
515
  for bmc in mc.branches:
516
  if bmc.fast_np:
517
  try:
518
- args=[b_matrix[:,vti[v]] for v in bmc.syms_used]
 
519
  val=bmc.fast_np(*args)
520
  if not isinstance(val,np.ndarray): val=np.full(N,float(val),dtype=np.float32)
521
  bv.append(np.abs(val))
@@ -525,7 +479,8 @@ class Problem:
525
  else:
526
  if mc.fast_np is None: total+=1.0; continue
527
  try:
528
- args=[b_matrix[:,vti[v]] for v in mc.syms_used]
 
529
  val=mc.fast_np(*args)
530
  if not isinstance(val,np.ndarray): val=np.full(N,float(val),dtype=np.float32)
531
  if mc.kind=="equality": total+=np.abs(val)*mc.weight
@@ -542,46 +497,123 @@ class Problem:
542
  # ══════════════════════════════════════════════════════════════════════
543
  @dataclass
544
  class VerifyResult:
545
- gate1_ce: float
546
- gate1_pass: bool
547
- gate2_results: Dict[str,Tuple[float,bool]] # name β†’ (error, pass)
548
- gate2_pass: bool
549
- overall_pass: bool
550
- failed_gates: List[str] = field(default_factory=list)
551
 
552
  class VerifyLayer:
553
- def run(self, binding:Dict[str,float], base_problem:Problem,
554
- anchors:List[AXLInvariant]) -> VerifyResult:
555
-
556
- # Gate 1 β€” structural CE against base problem
557
- g1_ce = base_problem.constraint_energy(binding)
558
- g1_pass = g1_ce < VERIFY_G1_THRESHOLD
559
-
560
- # Gate 2 β€” invariant checks
561
- g2 = {}
562
  for inv in anchors:
563
  try:
564
- syms = {v: sp.Symbol(v) for v in binding}
565
- expr = parse_expr(inv.expr, local_dict=syms)
566
- func = sp.lambdify(list(expr.free_symbols), expr, modules="math")
567
- args = [binding.get(str(s), 0.0) for s in expr.free_symbols]
568
- err = abs(float(func(*args)))
569
- g2[inv.name] = (round(err, 6), err < inv.tolerance)
570
- except Exception as e:
571
- g2[inv.name] = (999.0, False)
 
 
 
 
 
 
572
 
573
- g2_pass = all(v[1] for v in g2.values()) if g2 else True
574
- failed = (["Gate1"] if not g1_pass else []) + [k for k,v in g2.items() if not v[1]]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
575
 
576
- return VerifyResult(
577
- gate1_ce=round(g1_ce, 6), gate1_pass=g1_pass,
578
- gate2_results=g2, gate2_pass=g2_pass,
579
- overall_pass=g1_pass and g2_pass, failed_gates=failed)
 
 
 
 
580
 
581
- VERIFY_LAYER = VerifyLayer()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
582
 
583
  # ══════════════════════════════════════════════════════════════════════
584
- # SECTION 7: ORACLES
585
  # ══════════════════════════════════════════════════════════════════════
586
  @dataclass
587
  class L7Certificate: topology_sig:str; hub_vars:List[str]; solve_order:List[str]
@@ -644,10 +676,12 @@ class ResidualOracle:
644
  re=[e for e,c in sorted(de,key=lambda x:-x[1])[:5]]
645
  return L9Certificate(round(ce,6),rv,re)
646
 
647
- TOPOLOGY_ORACLE=TopologyOracle(); COHERENCE_ORACLE=CoherenceOracle(); RESIDUAL_ORACLE=ResidualOracle()
 
 
648
 
649
  # ══════════════════════════════════════════════════════════════════════
650
- # SECTION 8: CORE SOLVERS
651
  # ══════════════════════════════════════════════════════════════════════
652
  def _hc4(box,constraints):
653
  cur=dict(box)
@@ -666,7 +700,8 @@ def _hc4(box,constraints):
666
  try:
667
  riv=mc.fast_iv(cur)
668
  if ((mc.kind=="equality" and not riv.contains_zero()) or
669
- (mc.kind=="inequality" and ((mc.direction=="geq" and riv.hi<-1e-10) or (mc.direction=="leq" and riv.lo>1e-10)))):
 
670
  return None
671
  except: pass
672
  return cur
@@ -758,8 +793,12 @@ def _adam_polish(problem,binding,steps=ADAM_STEPS):
758
  if nce<best_ce: best_ce=nce; best_b=dict(b)
759
  return best_b,best_ce
760
 
761
- def solve_by_hypothesis(problem, budget=TOTAL_BUDGET):
 
762
  t0=time.time()
 
 
 
763
  init_b={v:max(lo,min(hi,(lo+hi)/2)) for v,(lo,hi) in problem.bounds.items()}
764
  tb=_global_hc4_tighten(problem)
765
  full_box={v:IV(tb[v][0],tb[v][1]) for v in problem.variables if v in tb}
@@ -767,26 +806,17 @@ def solve_by_hypothesis(problem, budget=TOTAL_BUDGET):
767
  if mprt_ce<problem.constraint_energy(init_b): init_b=mprt_b
768
  snap_b,snap_ce=_algebraic_snap(problem,init_b)
769
  if snap_ce<problem.constraint_energy(init_b): init_b=snap_b
770
- best_b=dict(init_b); best_ce=problem.constraint_energy(init_b)
771
-
772
- from dataclasses import dataclass as _dc
773
- @_dc
774
- class _SM:
775
- best_binding:dict; best_ce:float
776
- tested:list=field(default_factory=list)
777
- failure_patterns:set=field(default_factory=set)
778
- scope_witnesses:dict=field(default_factory=dict)
779
- confirmed_ranges:dict=field(default_factory=dict)
780
- conflict_vars:set=field(default_factory=set)
781
-
782
- model=_SM(best_binding=init_b,best_ce=problem.constraint_energy(init_b))
783
  active_scopes=[s for s in problem.scope_order if s!="root" and problem.scope_vars.get(s)]
 
784
  if model.best_ce<SOLVE_THRESHOLD:
785
  l7=TOPOLOGY_ORACLE.evaluate(problem,active_scopes)
786
  l9=RESIDUAL_ORACLE.evaluate(model.best_binding,problem,l7.hub_vars)
787
- return {"binding":model.best_binding,"ce":model.best_ce,"solved":True,"l7":l7,"l9":l9,"time":round(time.time()-t0,3)}
 
788
 
789
- oracle=HypothesisOracle(); consec=0; explore_trigger=4
790
  for round_idx in range(HYPOTHESIS_MAX_ROUNDS):
791
  if model.best_ce<SOLVE_THRESHOLD: break
792
  l7=TOPOLOGY_ORACLE.evaluate(problem,active_scopes)
@@ -810,12 +840,17 @@ def solve_by_hypothesis(problem, budget=TOTAL_BUDGET):
810
  eb,ece=_mprt_sample(problem,work_box,N_MPRT_EXPLORE)
811
  if ece<model.best_ce: model.best_ce=ece; model.best_binding=dict(eb); consec=0
812
 
813
- if SOLVE_THRESHOLD<model.best_ce<2.0:
 
814
  gb,gce=_gradient_polish(problem,model.best_binding)
815
  if gce<model.best_ce: model.best_ce=gce; model.best_binding=gb
816
- if SOLVE_THRESHOLD<model.best_ce<1.0:
817
  ab,ace=_adam_polish(problem,model.best_binding)
818
  if ace<model.best_ce: model.best_ce=ace; model.best_binding=ab
 
 
 
 
819
 
820
  final_l7=TOPOLOGY_ORACLE.evaluate(problem,active_scopes)
821
  final_l9=RESIDUAL_ORACLE.evaluate(model.best_binding,problem,final_l7.hub_vars)
@@ -823,36 +858,12 @@ def solve_by_hypothesis(problem, budget=TOTAL_BUDGET):
823
  "l7":final_l7,"l9":final_l9,"time":round(time.time()-t0,3)}
824
 
825
  # ══════════════════════════════════════════════════════════════════════
826
- # SECTION 9: HYPOTHESIS ENGINE + TRACE LOGGING
827
  # ══════════════════════════════════════════════════════════════════════
828
- @dataclass
829
- class Hypothesis:
830
- hid:str; binding:Dict[str,float]; h_type:str; claim:str
831
- derivation:List[str]; pinned_vars:Dict[str,float]; free_vars:List[str]
832
- confidence:float; ce:float=float('inf')
833
-
834
- @dataclass
835
- class HypothesisTrace:
836
- hid: str
837
- round_idx: int
838
- domain_name: str
839
- action: str
840
- ce_before: float
841
- ce_after: float
842
- verify: Optional[VerifyResult]
843
- survived: bool
844
- elapsed_ms: float
845
-
846
- @dataclass
847
- class StructuralModel:
848
- best_binding:Dict[str,float]; best_ce:float
849
- tested:List[Hypothesis]=field(default_factory=list)
850
- failure_patterns:Set[str]=field(default_factory=set)
851
- scope_witnesses:Dict[str,Dict[str,Tuple]]=field(default_factory=dict)
852
- confirmed_ranges:Dict[str,Tuple[float,float]]=field(default_factory=dict)
853
- conflict_vars:Set[str]=field(default_factory=set)
854
-
855
  class HypothesisOracle:
 
 
 
856
  def generate(self,problem,model,l7,l8,l9,round_idx):
857
  hyps=[]
858
  hyps.extend(self._branch_hypotheses(problem,model,l9))
@@ -860,6 +871,11 @@ class HypothesisOracle:
860
  hyps.extend(self._coherence_hypotheses(problem,model,l8))
861
  hyps.extend(self._residual_chain_hypotheses(problem,model,l9))
862
  if round_idx>=3: hyps.extend(self._manifold_tangent_hypotheses(problem,model,l9))
 
 
 
 
 
863
  hyps=[h for h in hyps if h.hid not in model.failure_patterns]
864
  return sorted(hyps,key=lambda h:-h.confidence)
865
 
@@ -875,9 +891,10 @@ class HypothesisOracle:
875
  val=float(proj["func"](*[ref.get(s,0.0) for s in proj["syms"]]))
876
  if not math.isfinite(val) or abs(val)>1e6 or not(lo<=val<=hi): continue
877
  b=dict(ref); b[v]=val
878
- hyps.append(Hypothesis(hid=f"br_{hash(expr_str)%9999}_{v}_{pi}",binding=b,h_type="branch",
879
- claim=f"AlgBranch:{v}={val:.4f}",derivation=[expr_str],
880
- pinned_vars={v:val},free_vars=[u for u in problem.variables if u!=v],
 
881
  confidence=0.85 if pi==0 else 0.70))
882
  except: pass
883
  return hyps
@@ -890,16 +907,18 @@ class HypothesisOracle:
890
  if not svars: continue
891
  scope_mcs=[problem.compiled_constraints[i] for i in problem.scope_groups.get(sn,[])]
892
  if not scope_mcs: continue
893
- start_box={v:IV(*tb.get(v,problem.bounds[v])) for v in svars if v in problem.bounds}
894
  cont=_hc4(start_box,scope_mcs)
895
  if cont:
896
  for v,iv in cont.items():
897
  b[v]=iv.mid()
898
  chain_steps.append(sn)
899
  if chain_steps:
900
- hyps.append(Hypothesis(hid=f"chain_{'_'.join(c[:4] for c in chain_steps)}",
901
- binding=b,h_type="chain",claim=f"TopologyChain({' β†’ '.join(chain_steps)})",
902
- derivation=chain_steps,pinned_vars={v:b[v] for sn in chain_steps for v in problem.scope_vars.get(sn,[]) if v in b},
 
 
903
  free_vars=[v for v in problem.variables if not any(v in problem.scope_vars.get(sn,[]) for sn in chain_steps)],
904
  confidence=0.80))
905
  return hyps
@@ -910,10 +929,11 @@ class HypothesisOracle:
910
  for v,(lo,hi) in l8.tightened_bounds.items():
911
  if v in problem.bounds: b[v]=(lo+hi)/2.0
912
  return [Hypothesis(hid=f"coh_{hash(tuple(sorted(l8.incoherent_vars)))%9999}",
913
- binding=b,h_type="coherence",claim=f"CoherenceMeet({', '.join(list(l8.incoherent_vars)[:3])})",
914
  derivation=list(l8.incoherent_vars),
915
  pinned_vars={v:b[v] for v in l8.incoherent_vars if v in b},
916
- free_vars=[v for v in problem.variables if v not in l8.incoherent_vars],confidence=0.70)]
 
917
 
918
  def _residual_chain_hypotheses(self,problem,model,l9):
919
  if not l9.dominant_vars: return []
@@ -988,7 +1008,11 @@ def _test_hypothesis(problem,hyp):
988
  return best_b,best_ce
989
 
990
  # ══════════════════════════════════════════════════════════════════════
991
- # SECTION 10: WISDOM LAYER + DEBATE (with trace logging)
 
 
 
 
992
  # ══════════════════════════════════════════════════════════════════════
993
  class Axiom:
994
  DISCRETE="DISCRETE"; CONTINUOUS="CONTINUOUS"; METRIC="METRIC"; NETWORK="NETWORK"
@@ -996,15 +1020,15 @@ class Axiom:
996
  QUADRATIC="QUADRATIC"; BILINEAR="BILINEAR"; ORDERED="ORDERED"; SYMMETRIC="SYMMETRIC"
997
  TRANSITIVE="TRANSITIVE"; ATOMIC="ATOMIC"; COMPOSITE="COMPOSITE"
998
 
999
- AXIOM_CONTRADICTIONS=[{Axiom.DISCRETE,Axiom.CONTINUOUS},{Axiom.INJECTIVE,Axiom.SURJECTIVE},
1000
- {Axiom.ORDERED,Axiom.SYMMETRIC},{Axiom.ATOMIC,Axiom.COMPOSITE},{Axiom.CONSERVED,Axiom.MUTABLE}]
 
 
1001
 
1002
  @dataclass
1003
  class AbstractDomain:
1004
  name:str; axioms:Set[str]; derivation:str="Base"; confidence:float=1.0
1005
- def is_consistent(self):
1006
- return not any(p.issubset(self.axioms) for p in AXIOM_CONTRADICTIONS)
1007
- def __str__(self): return f"[{self.name}|{self.derivation}] {{{', '.join(sorted(self.axioms))}}}"
1008
 
1009
  BASE_LIBRARY=[
1010
  AbstractDomain("GraphSpace",{Axiom.DISCRETE,Axiom.NETWORK,Axiom.SYMMETRIC,Axiom.ATOMIC}),
@@ -1024,19 +1048,18 @@ class WisdomOps:
1024
  @staticmethod
1025
  def union(d1,d2):
1026
  merged=d1.axioms|d2.axioms
1027
- c=AbstractDomain(WisdomOps._fn(f"{d1.name}βˆͺ{d2.name}"),merged,"Union",(d1.confidence+d2.confidence)/2)
1028
  for pair in AXIOM_CONTRADICTIONS:
1029
  if pair.issubset(c.axioms):
1030
  counts={ax:sum(ax in d.axioms for d in BASE_LIBRARY) for ax in pair}
1031
  c.axioms.discard(min(counts,key=counts.get))
1032
  return c
1033
  @staticmethod
1034
- def relax(d,axioms): return AbstractDomain(WisdomOps._fn(f"{d.name}_Rlx"),d.axioms-axioms,f"Relax",d.confidence*0.85)
1035
  @staticmethod
1036
  def inject(d,axioms):
1037
- merged=d.axioms|axioms
1038
- ax_str="".join(a[:2] for a in axioms)
1039
- c=AbstractDomain(WisdomOps._fn(f"{d.name}+{ax_str}"),merged,f"Inject",d.confidence*0.9)
1040
  for pair in AXIOM_CONTRADICTIONS:
1041
  if pair.issubset(c.axioms):
1042
  to_rm=pair-d.axioms
@@ -1052,60 +1075,107 @@ class WisdomOps:
1052
  return AbstractDomain(WisdomOps._fn(f"{d.name}*"),{fm.get(ax,ax) for ax in d.axioms},"Dual",d.confidence*0.7)
1053
 
1054
  class PSLTranslator:
1055
- def compile(self,domain:AbstractDomain,variables:List[str],description:str="") -> str:
1056
- ax=domain.axioms; n=len(variables); lines=[f"SPACE wisdom_{domain.name.replace(' ','_').replace('βˆͺ','U')}"]
1057
- if Axiom.CONTINUOUS in ax and Axiom.QUADRATIC in ax:
1058
- for v in variables: lines.append(f"VAR {v} -2.0 2.0")
1059
- elif Axiom.CONTINUOUS in ax:
1060
- for v in variables: lines.append(f"VAR {v} -5.0 5.0")
1061
- elif Axiom.ORDERED in ax:
1062
- for v in variables: lines.append(f"VAR {v} -2.0 5.0")
1063
- else:
1064
- for v in variables: lines.append(f"VAR {v} 0.0 3.0")
1065
- si=0
1066
- if Axiom.CONSERVED in ax and n>=2:
1067
- lines.append(f"SCOPE conserved_{si} ORDER {si}")
1068
- lines.append(f" LINK {' '.join(variables)}")
1069
- lines.append(f" CONSERVE {' + '.join(variables)} - {n/2.0:.1f}")
1070
- lines.append("END"); si+=1
1071
- if Axiom.QUADRATIC in ax and Axiom.CONSERVED in ax and n>=2:
1072
- lines.append(f"SCOPE norm_{si} ORDER {si}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1073
  lines.append(f" LINK {' '.join(variables)}")
1074
- lines.append(f" CONSERVE {' + '.join(f'{v}**2' for v in variables)} - {n/2.0:.1f}")
 
1075
  lines.append("END"); si+=1
1076
- if Axiom.BILINEAR in ax and n>=2:
1077
- w=min(3,n)
1078
- for i in range(0,n-1,max(1,w-1)):
1079
- grp=variables[i:i+w]
1080
- if len(grp)<2: continue
1081
- lines.append(f"SCOPE bilinear_{si} ORDER {si}")
1082
- lines.append(f" LINK {' '.join(grp)}")
1083
- for j in range(len(grp)-1):
1084
- lines.append(f" EQ {grp[j]}**2 + {grp[j+1]}**2 - 1.0")
1085
- lines.append(f" EQ {grp[j]}*{grp[j+1]} - 0.0")
1086
- lines.append("END"); si+=1
1087
- if Axiom.ORDERED in ax and Axiom.BILINEAR not in ax:
1088
- lines.append(f"SCOPE ordered_{si} ORDER {si}")
1089
- lines.append(f" LINK {' '.join(variables)}")
1090
- for i in range(n-1): lines.append(f" GEQ {variables[i+1]} - {variables[i]}")
1091
  lines.append("END"); si+=1
 
1092
  if Axiom.METRIC in ax and Axiom.INJECTIVE in ax and n>=2:
1093
- lines.append(f"SCOPE separation_{si} ORDER {si}")
1094
- lines.append(f" LINK {' '.join(variables[:min(6,n)])}")
1095
- for i,j in itertools.combinations(range(min(6,n)),2):
1096
- lines.append(f" GEQ ({variables[i]} - {variables[j]})**2 - 0.25")
1097
  lines.append("END"); si+=1
1098
- if Axiom.SYMMETRIC in ax and Axiom.NETWORK in ax and n>=2:
1099
- lines.append(f"SCOPE symmetric_{si} ORDER {si}")
1100
- lines.append(f" LINK {' '.join(variables)}")
1101
- for i in range(0,n-1,2): lines.append(f" EQ {variables[i]} + {variables[i+1]} - 1.0")
 
 
 
1102
  lines.append("END"); si+=1
 
 
 
 
 
 
 
 
 
 
1103
  return "\n".join(lines)
1104
 
 
 
 
 
1105
  @dataclass
1106
  class DebateRound:
1107
  round_idx:int; domain:AbstractDomain; psl_text:str
1108
- ce:float; solved:bool; action:str
1109
  traces:List[HypothesisTrace]=field(default_factory=list)
1110
 
1111
  class WisdomDebate:
@@ -1125,19 +1195,20 @@ class WisdomDebate:
1125
 
1126
  def _candidates(self,current,round_idx):
1127
  cands=[(d,"Library") for d in self.library]
1128
- for d1,d2 in itertools.combinations(self.library[:5],2): cands.append((WisdomOps.union(d1,d2),"Union"))
 
1129
  for d in self.library[:4]: cands.append((WisdomOps.dual(d),"Dual"))
1130
  if round_idx>2:
1131
  chosen=random.sample([Axiom.BILINEAR,Axiom.QUADRATIC,Axiom.CONSERVED,Axiom.METRIC,Axiom.ORDERED],2)
1132
- cands.append((WisdomOps.inject(current,set(chosen)),f"RandInject"))
1133
  return cands
1134
 
1135
  def debate(self,axl_def:AXLProblemDef,base_problem:Problem) -> Tuple[Dict,float,List[DebateRound]]:
1136
- problem_axioms=axl_def.axioms; variables=axl_def.variables_list()
1137
  current=min(self.library,key=lambda d:self._critic_score(problem_axioms,d)[0])
1138
  self.history=[]; self.best_ce=float('inf'); self.best_binding={}; self.all_traces=[]
1139
 
1140
- print(f"\n{'═'*55}\nDEBATE: {axl_def.name}\nAxioms: {problem_axioms}\n{'═'*55}")
1141
 
1142
  for round_idx in range(self.MAX_ROUNDS):
1143
  cands=self._candidates(current,round_idx)
@@ -1145,99 +1216,110 @@ class WisdomDebate:
1145
  scored.sort(key=lambda x:x[0])
1146
  tried={r.domain.name for r in self.history}
1147
  score,winner,action=next((s for s in scored if s[1].name not in tried),scored[0])
 
1148
 
1149
- print(f"\n[R{round_idx+1}] Domain: {winner.name} | Action: {action} | Score: {score:.1f}")
1150
- psl_text=self.translator.compile(winner,variables,axl_def.description)
 
 
 
1151
  round_traces=[]
1152
 
1153
  try:
1154
  prog=parse_psl(psl_text); ep=expand_psl(prog)
1155
  if not ep.variables:
1156
- print(f" βœ— No variables compiled"); current=winner; continue
 
1157
  dom_prob=Problem(pid=f"dom_{winner.name}",variables=ep.variables,
1158
  constraints=ep.constraints,bounds=ep.bounds,
1159
  scope_groups=ep.scope_groups,scope_vars=ep.scope_vars,
1160
  scope_order=ep.scope_order)
1161
  t_start=time.time()
1162
- result=solve_by_hypothesis(dom_prob,budget=TOTAL_BUDGET)
 
1163
  elapsed=round((time.time()-t_start)*1000,1)
1164
  binding=result["binding"]; ce=result["ce"]; solved=result["solved"]
1165
 
1166
  # Verify against base problem + anchors
1167
  verify=VERIFY_LAYER.run(binding,base_problem,axl_def.anchors)
 
1168
 
1169
  trace=HypothesisTrace(
1170
  hid=f"R{round_idx+1}_{winner.name[:10]}",
1171
- round_idx=round_idx+1,
1172
- domain_name=winner.name,
1173
- action=action,
1174
- ce_before=self.best_ce if self.best_ce<float('inf') else 99.0,
1175
- ce_after=ce,
1176
- verify=verify,
1177
- survived=verify.overall_pass,
1178
- elapsed_ms=elapsed)
1179
  round_traces.append(trace); self.all_traces.append(trace)
1180
 
1181
  status="βœ“ SURVIVED" if verify.overall_pass else "βœ— FAILED"
1182
- print(f" CE={ce:.5f} | G1={'βœ“' if verify.gate1_pass else 'βœ—'} CE={verify.gate1_ce:.4f} | G2={'βœ“' if verify.gate2_pass else 'βœ—'} | {status}")
 
1183
  for inv_name,(err,passed) in verify.gate2_results.items():
1184
- print(f" [{'+' if passed else '-'}] {inv_name}: |err|={err:.6f}")
1185
 
1186
- if ce<self.best_ce:
1187
- self.best_ce=ce; self.best_binding=dict(binding); self.best_domain=winner
1188
- if verify.overall_pass and solved:
1189
- self.history.append(DebateRound(round_idx,winner,psl_text,ce,solved,action,round_traces))
 
 
1190
  print(f"\nβœ“ VERIFIED + SOLVED in round {round_idx+1}"); break
 
1191
  except Exception as e:
1192
- print(f" ERROR: {e}")
1193
  trace=HypothesisTrace(hid=f"R{round_idx+1}_ERR",round_idx=round_idx+1,
1194
  domain_name=winner.name,action=action,ce_before=99.0,ce_after=99.0,
1195
  verify=None,survived=False,elapsed_ms=0)
1196
  round_traces.append(trace); self.all_traces.append(trace)
1197
 
1198
  self.history.append(DebateRound(round_idx,winner,psl_text,
1199
- self.best_ce,self.best_ce<SOLVE_THRESHOLD,action,round_traces))
1200
  current=winner
1201
 
1202
- print(f"\n{'─'*55}\nRESULT: CE={self.best_ce:.5f} | Domain={self.best_domain.name if self.best_domain else '?'}")
1203
  return self.best_binding,self.best_ce,self.history
1204
 
1205
  # ══════════════════════════════════════════════════════════════════════
1206
- # SECTION 11: HARDCODED AXL PROBLEMS
1207
  # ══════════════════════════════════════════════════════════════════════
1208
- @dataclass
1209
- class AXLProblemDef:
1210
- name:str; description:str; axioms:Set[str]
1211
- variables:List[Dict]; constraints:List[Dict]
1212
- scopes:List[Dict]; anchors:List[AXLInvariant]
1213
- observations:Dict[str,float]=field(default_factory=dict)
1214
-
1215
- def variables_list(self): return [v["name"] for v in self.variables]
1216
 
1217
- AXL_PROBLEMS = [
 
 
1218
  AXLProblemDef(
1219
  name="NormManifold4D",
1220
- description="Four coupled variables on unit sphere with zero-sum constraint",
1221
  axioms={Axiom.CONTINUOUS,Axiom.QUADRATIC,Axiom.CONSERVED,Axiom.METRIC,Axiom.SYMMETRIC},
1222
  variables=[
1223
- {"name":"x1","lo":-1.0,"hi":1.0},{"name":"x2","lo":-1.0,"hi":1.0},
1224
- {"name":"x3","lo":-1.0,"hi":1.0},{"name":"x4","lo":-1.0,"hi":1.0},
 
 
1225
  ],
1226
  constraints=[
1227
  {"kind":"CONSERVE","expr":"x1**2 + x2**2 + x3**2 + x4**2 - 1.0"},
1228
- {"kind":"EQ", "expr":"x1 + x2 + x3 + x4","weight":1.0},
1229
  ],
1230
  scopes=[{"name":"sphere","order":0,"vars":["x1","x2","x3","x4"],
1231
- "constraints":[{"kind":"EQ","expr":"x1**2 + x2**2 + x3**2 + x4**2 - 1.0","weight":5.0}]}],
 
 
1232
  anchors=[
1233
  AXLInvariant("unit_sphere","x1**2 + x2**2 + x3**2 + x4**2 - 1.0",1e-3),
1234
  AXLInvariant("zero_sum","x1 + x2 + x3 + x4",1e-3),
 
1235
  ],
1236
  observations={"x1":0.5},
1237
  ),
 
 
 
1238
  AXLProblemDef(
1239
  name="BilinearChain6",
1240
- description="6-variable bilinear chain with conservation and ordering",
1241
  axioms={Axiom.DISCRETE,Axiom.ORDERED,Axiom.BILINEAR,Axiom.CONSERVED,Axiom.NETWORK},
1242
  variables=[{"name":f"t{i}","lo":0.0,"hi":3.0} for i in range(1,7)],
1243
  constraints=[
@@ -1248,77 +1330,90 @@ AXL_PROBLEMS = [
1248
  {"kind":"GEQ","expr":"t5 - t4","weight":1.0},
1249
  {"kind":"GEQ","expr":"t6 - t5","weight":1.0},
1250
  ],
1251
- scopes=[
1252
- {"name":"chain","order":0,"vars":[f"t{i}" for i in range(1,7)],
1253
- "constraints":[
1254
- {"kind":"EQ","expr":"t1*t2 - 0.5","weight":1.0},
1255
- {"kind":"EQ","expr":"t3*t4 - 2.0","weight":1.0},
1256
- {"kind":"EQ","expr":"t5*t6 - 4.5","weight":1.0},
1257
- ]},
1258
- ],
1259
  anchors=[
1260
  AXLInvariant("conservation","t1 + t2 + t3 + t4 + t5 + t6 - 9.0",1e-3),
1261
- AXLInvariant("ordering_t1_t2","t1 - t2",1.0), # t2 >= t1
1262
- AXLInvariant("bilinear_mid","t3*t4 - 2.0",0.1),
 
1263
  ],
1264
  ),
 
 
 
 
 
1265
  AXLProblemDef(
1266
  name="PhaseCollider",
1267
- description="4 variables with phase constraints β€” must satisfy one of two collision configurations",
1268
  axioms={Axiom.DISCRETE,Axiom.MUTABLE,Axiom.SYMMETRIC,Axiom.NETWORK,Axiom.CONSERVED},
1269
  variables=[
1270
- {"name":"p1x","lo":-2.0,"hi":2.0},{"name":"p2x","lo":-2.0,"hi":2.0},
1271
- {"name":"p1y","lo":-2.0,"hi":2.0},{"name":"p2y","lo":-2.0,"hi":2.0},
 
 
1272
  ],
1273
  constraints=[
1274
- {"kind":"CONSERVE","expr":"p1x + p2x"}, # momentum x conserved = 0
1275
- {"kind":"CONSERVE","expr":"p1y + p2y"}, # momentum y conserved = 0
1276
- ],
1277
- scopes=[
1278
- {"name":"phase","order":0,"vars":["p1x","p2x","p1y","p2y"],
1279
- "constraints":[
1280
- {"kind":"BRANCH","name":"collision_mode","options":[
1281
- "p1x**2 + p1y**2 - 1.0", # phase A: unit circle
1282
- "p1x**2 + p1y**2 - 2.0", # phase B: larger circle
1283
- ]},
1284
- {"kind":"EQ","expr":"(p1x - p2x)**2 + (p1y - p2y)**2 - 1.0","weight":2.0},
1285
- ]},
1286
  ],
 
 
 
 
 
 
 
 
 
 
 
1287
  anchors=[
1288
  AXLInvariant("momentum_x","p1x + p2x",1e-3),
1289
  AXLInvariant("momentum_y","p1y + p2y",1e-3),
1290
- AXLInvariant("separation","(p1x - p2x)**2 + (p1y - p2y)**2 - 1.0",0.1),
 
 
1291
  ],
1292
  ),
 
 
 
 
1293
  AXLProblemDef(
1294
  name="MetricPacking3D",
1295
- description="3 variables packed in metric space with minimum separation and norm bound",
1296
  axioms={Axiom.CONTINUOUS,Axiom.METRIC,Axiom.INJECTIVE,Axiom.CONSERVED,Axiom.SYMMETRIC},
1297
  variables=[
1298
- {"name":"a","lo":-3.0,"hi":3.0},{"name":"b","lo":-3.0,"hi":3.0},{"name":"c","lo":-3.0,"hi":3.0},
 
 
1299
  ],
1300
  constraints=[
 
1301
  {"kind":"GEQ","expr":"(a - b)**2 - 1.0","weight":2.0},
1302
  {"kind":"GEQ","expr":"(b - c)**2 - 1.0","weight":2.0},
1303
  {"kind":"GEQ","expr":"(a - c)**2 - 4.0","weight":2.0},
1304
- {"kind":"CONSERVE","expr":"a + b + c"}, # centred at 0
1305
- ],
1306
- scopes=[
1307
- {"name":"packing","order":0,"vars":["a","b","c"],
1308
- "constraints":[
1309
- {"kind":"EQ","expr":"a**2 + b**2 + c**2 - 6.0","weight":1.5},
1310
- ]},
1311
  ],
 
 
 
 
1312
  anchors=[
1313
  AXLInvariant("centred","a + b + c",1e-3),
1314
- AXLInvariant("sep_ab","(a - b)**2 - 1.0",0.2),
1315
- AXLInvariant("sep_bc","(b - c)**2 - 1.0",0.2),
 
1316
  ],
1317
  ),
1318
  ]
1319
 
1320
  def build_base_problem(axl_def:AXLProblemDef) -> Problem:
1321
- """Compile the AXL definition directly to a Problem for use as verification oracle."""
1322
  psl_text=AXL_COMPILER.compile(axl_def)
1323
  prog=parse_psl(psl_text); ep=expand_psl(prog)
1324
  return Problem(pid=f"base_{axl_def.name}",variables=ep.variables,
@@ -1327,10 +1422,10 @@ def build_base_problem(axl_def:AXLProblemDef) -> Problem:
1327
  scope_order=ep.scope_order,annotations=ep.annotations)
1328
 
1329
  # ══════════════════════════════════════════════════════════════════════
1330
- # SECTION 12: GLOBAL STATE + RUNNER
1331
  # ════════════════════════════════════════════════════════════════���═════
1332
  STATE_LOCK=threading.Lock()
1333
- RUN_LOG:List[Dict]=[] # full run records for dashboard
1334
  DEBATE_ENGINE=WisdomDebate()
1335
  POOL=ThreadPoolExecutor(max_workers=2)
1336
 
@@ -1338,33 +1433,30 @@ def _run_problem(axl_def:AXLProblemDef):
1338
  t0=time.time()
1339
  try:
1340
  base_prob=build_base_problem(axl_def)
1341
- binding, domain_ce, rounds=DEBATE_ENGINE.debate(axl_def,base_prob)
1342
  elapsed=round(time.time()-t0,2)
1343
-
1344
  survived=[t for t in DEBATE_ENGINE.all_traces if t.survived]
1345
  failed=[t for t in DEBATE_ENGINE.all_traces if not t.survived]
1346
  best_trace=min((t for t in survived),key=lambda t:t.ce_after,default=None)
1347
-
1348
- # FIX: Calculate actual CE against the original base problem
1349
- actual_base_ce = base_prob.constraint_energy(binding)
1350
- is_truly_solved = len(survived) > 0
1351
-
1352
  record={
1353
  "problem":axl_def.name,
1354
  "description":axl_def.description,
1355
  "axioms":sorted(axl_def.axioms),
1356
- "ce":round(actual_base_ce, 6), # Use REAL energy
1357
- "domain_ce":round(domain_ce, 6), # Keep domain CE for reference
1358
- "solved":is_truly_solved, # ONLY true if verified
1359
  "elapsed":elapsed,
1360
  "binding":{k:round(v,5) for k,v in binding.items()},
 
1361
  "traces":[{
1362
  "hid":t.hid,"round":t.round_idx,"domain":t.domain_name,
1363
- "action":t.action,"ce":round(t.ce_after,5),
 
1364
  "g1_ce":round(t.verify.gate1_ce,5) if t.verify else None,
1365
  "g1":t.verify.gate1_pass if t.verify else False,
1366
  "g2":t.verify.gate2_pass if t.verify else False,
1367
- "g2_detail":{k:(round(err,6),ok) for k,(err,ok) in t.verify.gate2_results.items()} if t.verify else {},
 
1368
  "survived":t.survived,"ms":t.elapsed_ms,
1369
  } for t in DEBATE_ENGINE.all_traces],
1370
  "survived_count":len(survived),
@@ -1375,6 +1467,7 @@ def _run_problem(axl_def:AXLProblemDef):
1375
  RUN_LOG.append(record)
1376
  if len(RUN_LOG)>40: RUN_LOG.pop(0)
1377
  except Exception as e:
 
1378
  print(f"[RUN ERROR] {axl_def.name}: {e}")
1379
 
1380
  async def run_loop():
@@ -1382,7 +1475,7 @@ async def run_loop():
1382
  while True:
1383
  prob=random.choice(AXL_PROBLEMS)
1384
  await loop.run_in_executor(POOL,_run_problem,prob)
1385
- await asyncio.sleep(0.5)
1386
 
1387
  @asynccontextmanager
1388
  async def lifespan(app):
@@ -1391,167 +1484,135 @@ async def lifespan(app):
1391
  POOL.shutdown(wait=False)
1392
 
1393
  # ══════════════════════════════════════════════════════════════════════
1394
- # SECTION 13: FASTAPI + DASHBOARD
1395
  # ══════════════════════════════════════════════════════════════════════
1396
- app=FastAPI(title="Practicality 11.0",lifespan=lifespan)
1397
-
1398
- def _render_trace_table(traces:List[Dict]) -> str:
1399
- rows=""
1400
- for t in traces:
1401
- g1c="#4CAF50" if t["g1"] else "#EF5350"
1402
- g2c="#4CAF50" if t["g2"] else "#EF5350"
1403
- sc="#4CAF50" if t["survived"] else "#EF5350"
1404
- star=" β—€" if t["survived"] else ""
1405
- g2d=" ".join(f"{k}={v[0]:.4f}{'βœ“' if v[1] else 'βœ—'}" for k,v in t.get("g2_detail",{}).items())
1406
- rows+=(f"<tr>"
1407
- f"<td style='color:#888'>{t['round']}</td>"
1408
- f"<td style='color:#FF9800'>{t['domain']}</td>"
1409
- f"<td style='color:#555;font-size:0.75em'>{t['action']}</td>"
1410
- f"<td style='color:#26C6DA'>{t['ce']:.5f}</td>"
1411
- f"<td style='color:{g1c}'>{t['g1_ce']:.4f}{'βœ“' if t['g1'] else 'βœ—'}</td>"
1412
- f"<td style='color:{g2c}'>{'βœ“' if t['g2'] else 'βœ—'} {g2d}</td>"
1413
- f"<td style='color:{sc};font-weight:700'>{'SURVIVED'+star if t['survived'] else 'failed'}</td>"
1414
- f"<td style='color:#444'>{t['ms']}ms</td>"
1415
- f"</tr>")
1416
- return rows
1417
 
1418
  def _render_collapse(record:Dict) -> str:
1419
- b=record["binding"]
1420
- traces=record["traces"]
1421
  survived=[t for t in traces if t["survived"]]
1422
-
1423
- # FIX: If nothing survived, pick the best attempt to show WHY it failed
1424
  best=min(survived,key=lambda t:t["ce"],default=None)
1425
- if not best and traces:
1426
- best=min(traces,key=lambda t:t.get("g1_ce",999.0))
1427
-
1428
- obs_vars=set()
1429
- for axl in AXL_PROBLEMS:
1430
- if axl.name==record["problem"]: obs_vars=set(axl.observations.keys()); break
1431
 
1432
- # FIX: Ensure it correctly marks it as UNSOLVED when CE is high or unverified
1433
- ce_color="#4CAF50" if record["solved"] else ("#FF9800" if record["ce"]<1.0 else "#EF5350")
1434
- status="βœ“ SOLVED + VERIFIED" if record["solved"] else ("~ PARTIAL" if record["ce"]<1.0 else "βœ— UNSOLVED")
 
1435
 
1436
  binding_rows="".join(
1437
- f"<tr><td style='color:#888;width:80px'>{v}</td>"
1438
  f"<td style='color:#26C6DA;font-weight:700'>{val:+.5f}</td>"
1439
- f"<td style='color:#555;font-size:0.8em'>{'[OBS]' if v in obs_vars else ''}</td></tr>"
1440
- for v,val in b.items()
1441
- )
1442
 
1443
  inv_rows=""
1444
  if best and best.get("g2_detail"):
1445
  for inv,(err,ok) in best["g2_detail"].items():
1446
  c="#4CAF50" if ok else "#EF5350"
1447
- inv_rows+=f"<tr><td style='color:{c}'>{'βœ“' if ok else 'βœ—'}</td><td style='color:#aaa'>{inv}</td><td style='color:{c}'>{err:.6f}</td></tr>"
1448
-
1449
- trail="".join(
1450
- f"<span style='color:{'#4CAF50' if t['survived'] else '#444'};margin-right:8px;font-size:0.8em'>"
1451
- f"[R{t['round']}] {t['domain'][:12]} CE={t['ce']:.4f} {'β—€' if t['survived'] else ''}"
1452
- f"</span>"
1453
- for t in traces
1454
- )
1455
-
1456
- dom_ce_str = f" | DomCE={record.get('domain_ce', 0.0):.6f}" if 'domain_ce' in record else ""
 
 
 
 
 
 
 
1457
 
1458
  return f"""
1459
- <div style='border:1px solid #2a2a2a;border-radius:8px;margin-bottom:20px;overflow:hidden'>
1460
- <div style='background:#111;padding:10px 14px;border-bottom:1px solid #222'>
1461
- <span style='color:{ce_color};font-weight:700;font-size:1.1em'>{status}</span>
1462
- <span style='color:#555;margin-left:16px;font-size:0.85em'>{record['problem']}</span>
1463
- <span style='color:#333;margin-left:12px;font-size:0.8em'>BaseCE={record['ce']:.6f}{dom_ce_str}</span>
1464
- <span style='color:#555;margin-left:12px;font-size:0.8em'>{record['elapsed']}s</span>
 
1465
  </div>
1466
- <div style='display:flex;gap:0'>
1467
- <div style='flex:1;padding:12px 14px;border-right:1px solid #1a1a1a'>
1468
- <div style='color:#333;font-size:0.75em;margin-bottom:6px'>BINDING</div>
1469
- <table style='width:100%;border-collapse:collapse'>
1470
- <tr><th style='color:#333;font-size:0.75em;text-align:left'>VAR</th>
1471
- <th style='color:#333;font-size:0.75em;text-align:left'>VALUE</th>
1472
- <th></th></tr>
1473
- {binding_rows}
1474
- </table>
1475
  </div>
1476
- <div style='flex:1;padding:12px 14px;border-right:1px solid #1a1a1a'>
1477
- <div style='color:#333;font-size:0.75em;margin-bottom:6px'>INVARIANTS (Gate 2)</div>
1478
- <table style='width:100%;border-collapse:collapse'>
1479
- <tr><th style='color:#333;font-size:0.75em;text-align:left'>βœ“</th>
1480
- <th style='color:#333;font-size:0.75em;text-align:left'>NAME</th>
1481
- <th style='color:#333;font-size:0.75em;text-align:left'>|ERR|</th></tr>
1482
  {inv_rows or "<tr><td colspan='3' style='color:#222'>β€”</td></tr>"}
1483
  </table>
1484
  </div>
1485
- <div style='flex:1;padding:12px 14px'>
1486
- <div style='color:#333;font-size:0.75em;margin-bottom:6px'>
1487
- HYPOTHESIS TRAIL β€” {record['survived_count']} survived / {record['failed_count']} failed
1488
- </div>
1489
- <div style='line-height:2'>{trail}</div>
1490
- <div style='color:#555;font-size:0.8em;margin-top:8px'>
1491
- Best domain: <span style='color:#FF9800'>{record['best_domain']}</span>
1492
  </div>
 
1493
  </div>
1494
  </div>
1495
  </div>"""
1496
 
1497
  @app.get("/",response_class=HTMLResponse)
1498
  async def dashboard():
1499
- with STATE_LOCK: log=list(reversed(RUN_LOG[-12:]))
1500
-
1501
- runs=len(RUN_LOG) if RUN_LOG else 0
1502
- solved=sum(1 for r in RUN_LOG if r.get("solved",False))
1503
  avg_ce=round(sum(r["ce"] for r in RUN_LOG)/max(1,runs),5) if RUN_LOG else 0
1504
 
1505
- collapses="".join(_render_collapse(r) for r in log) if log else \
1506
- "<div style='color:#222;padding:20px'>Warming up β€” first run starting…</div>"
1507
-
1508
- # Hypothesis survival summary table across all recent runs
1509
  all_traces=[t for r in log for t in r.get("traces",[])]
1510
- domain_stats=defaultdict(lambda:{"tried":0,"survived":0,"total_ce":0.0})
1511
  for t in all_traces:
1512
- domain_stats[t["domain"]]["tried"]+=1
1513
- domain_stats[t["domain"]]["total_ce"]+=t["ce"]
1514
- if t["survived"]: domain_stats[t["domain"]]["survived"]+=1
 
1515
  domain_rows="".join(
1516
  f"<tr><td style='color:#FF9800'>{d}</td>"
1517
  f"<td style='color:#aaa'>{s['tried']}</td>"
1518
  f"<td style='color:#4CAF50'>{s['survived']}</td>"
1519
  f"<td style='color:#EF5350'>{s['tried']-s['survived']}</td>"
1520
- f"<td style='color:#26C6DA'>{round(s['total_ce']/s['tried'],4) if s['tried'] else 0}</td>"
1521
- f"<td style='color:#555'>{round(s['survived']/s['tried']*100,0):.0f}%</td></tr>"
1522
- for d,s in sorted(domain_stats.items(),key=lambda x:-x[1]["survived"])
1523
- ) or "<tr><td colspan='6' style='color:#222'>β€”</td></tr>"
 
 
1524
 
1525
- return f"""<!DOCTYPE html><html><head><title>Practicality 11.0</title>
 
 
 
1526
  <meta http-equiv="refresh" content="4">
1527
  <style>
1528
  body{{background:#090909;color:#e0e0e0;font-family:monospace;padding:20px;max-width:1400px}}
1529
- h1{{color:#26C6DA;border-bottom:1px solid #1a1a1a;padding-bottom:6px;font-size:1.2em}}
1530
- h3{{color:#333;margin-top:24px;font-size:0.9em;letter-spacing:1px}}
1531
  table{{width:100%;border-collapse:collapse;margin-bottom:10px}}
1532
- th,td{{padding:5px 8px;text-align:left;border-bottom:1px solid #111;font-size:0.85em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}
1533
- th{{color:#333;font-size:0.75em}}
1534
- .badge{{display:inline-block;padding:3px 10px;border-radius:4px;background:#111;margin:2px;font-size:0.8em}}
1535
  </style></head><body>
1536
- <h1>βš— Practicality System 11.0 β€” AXL/PSL Pipeline</h1>
1537
- <div style='color:#444;font-size:0.8em;margin-bottom:14px'>
1538
- AXL (hardcoded) β†’ PSL 2.0 β†’ Solver β†’ Gate1 (structural CE) β†’ Gate2 (invariants) β†’ Collapse View
1539
  </div>
1540
- <div style='margin-bottom:18px'>
1541
  <span class='badge' style='color:#aaa'>Runs: {runs}</span>
1542
  <span class='badge' style='color:#4CAF50'>Solved: {solved}</span>
1543
  <span class='badge' style='color:#26C6DA'>Avg BaseCE: {avg_ce}</span>
1544
  <span class='badge' style='color:#FF9800'>Problems: {len(AXL_PROBLEMS)}</span>
1545
  <span class='badge' style='color:#5C6BC0'>Library: {len(DEBATE_ENGINE.library)}</span>
1546
  </div>
1547
-
1548
- <h3>DOMAIN SURVIVAL SUMMARY</h3>
1549
  <table>
1550
- <tr><th>Domain</th><th>Tried</th><th>Survived</th><th>Failed</th><th>Avg CE</th><th>Survival%</th></tr>
1551
  {domain_rows}
1552
  </table>
1553
-
1554
- <h3>COLLAPSE VIEW β€” Recent Runs</h3>
1555
  {collapses}
1556
  </body></html>"""
1557
 
 
1
  #!/usr/bin/env python3
2
  """
3
+ PRACTICALITY SYSTEM 11.1 β€” AXL/PSL PIPELINE
4
  ═══════════════════════════════════════════════════════════════════
5
+ FIXED IN 11.1:
6
+ Β· PSLTranslator now grounds domain PSL in actual base constraints
7
+ Domain adds SOFT structural hints (weight 0.15), never replaces base
8
+ Β· Observations pin variables in domain PSL (zero-width VAR bounds)
9
+ Β· DomainStrategy: wisdom layer informs HOW to search, not WHAT to search
10
+ Each domain maps to hypothesis weights + polish preference + explore trigger
11
+ Β· PhaseCollider redesigned to be provably feasible
12
+ Β· StructuralModel moved before solve_by_hypothesis (no more _SM hack)
13
+ Β· HypothesisOracle accepts DomainStrategy, applies confidence weights
14
  """
15
 
16
  import time, random, math, threading, asyncio, warnings, itertools, json
 
32
  warnings.filterwarnings("ignore")
33
  USE_GPU = torch.cuda.is_available()
34
  DEVICE = torch.device("cuda" if USE_GPU else "cpu")
35
+ print(f"[SYSTEM 11.1] Compute: {'GPU' if USE_GPU else 'CPU'}")
36
 
37
  # ══════════════════════════════════════════════════════════════════════
38
  # SECTION 1: CONSTANTS
 
129
  @dataclass
130
  class PSLVar: name:str; lo:float; hi:float; weight:float=1.0
131
  @dataclass
132
+ class PSLConstraint: kind:str; expr:str; scope:str="root"; weight:float=1.0; branches:List[str]=field(default_factory=list)
133
  @dataclass
134
  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)
135
  @dataclass
136
+ 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)
 
 
 
137
 
138
  def parse_psl(text:str) -> PSLProgram:
139
  lines=[l.strip() for l in text.strip().split('\n') if l.strip() and not l.strip().startswith('#')]
 
142
  def advance():
143
  nonlocal i; l=lines[i]; i+=1; return l
144
  def peek(): return lines[i] if i<len(lines) else ""
145
+ def pvar(rest):
146
  p=rest.split()
147
+ return PSLVar(p[0],float(p[1]),float(p[2])) if len(p)>=3 else None
148
+ def pw(rest):
 
 
 
 
149
  if 'WEIGHT' in rest:
150
+ p=rest.split('WEIGHT'); return p[0].strip(), float(p[1].split()[0])
151
  return rest.strip(), 1.0
152
+ def parse_scope_body(sname,order=0,depends=None):
153
  svars,scons,links=[],[],[]
154
  while i<len(lines):
155
  l=peek(); tk=l.split(None,1)
 
159
  if kw=="VAR":
160
  v=pvar(rest)
161
  if v: svars.append(v)
162
+ elif kw=="LINK": links.extend(rest.split())
 
163
  elif kw in ("EQ","GEQ","LEQ"):
164
+ expr,wt=pw(rest); scons.append(PSLConstraint(kw.lower(),expr,scope=sname,weight=wt))
 
165
  elif kw=="CONSERVE":
166
+ expr,_=pw(rest); scons.append(PSLConstraint("eq",expr,scope=sname,weight=10.0))
 
167
  elif kw=="BRANCH":
168
  opts=[]
169
+ while i<len(lines) and not peek().upper().startswith("END"):
170
  bl=advance()
171
  if bl.upper().startswith("OPTION"): opts.append(bl.split(None,1)[1].strip())
172
+ advance()
173
  scons.append(PSLConstraint("or_eq","",scope=sname,weight=1.0,branches=opts))
174
  elif kw=="ANNOTATE":
175
  parts=rest.split()
 
184
  v=pvar(rest)
185
  if v: prog.vars.append(v)
186
  elif kw in ("EQ","GEQ","LEQ"):
187
+ expr,wt=pw(rest); prog.constraints.append(PSLConstraint(kw.lower(),expr,weight=wt))
 
188
  elif kw=="CONSERVE":
189
+ expr,_=pw(rest); prog.constraints.append(PSLConstraint("eq",expr,weight=10.0))
 
190
  elif kw=="BRANCH":
191
  opts=[]
192
  while i<len(lines) and not peek().upper().startswith("END"):
 
208
  idx=rest.upper().index("DEPENDS")
209
  depends=rest[idx:].split()[1:]
210
  sc=parse_scope_body(sname,order,depends)
211
+ prog.scopes.append(sc); prog.scope_order.append(sname)
212
+ prog.scopes.sort(key=lambda s:s.order)
 
213
  prog.scope_order=[s.name for s in prog.scopes]
214
  return prog
215
 
 
217
  class ExpandedProblem:
218
  variables:List[str]; bounds:Dict[str,Tuple[float,float]]
219
  constraints:List['Constraint']
220
+ scope_groups:Dict[str,List[int]]; scope_vars:Dict[str,List[str]]
221
+ scope_order:List[str]; annotations:Dict[str,str]=field(default_factory=dict)
222
 
223
  def expand_psl(prog:PSLProgram) -> ExpandedProblem:
224
  variables=[]; bounds={}; constraints=[]
 
264
  prog.scope_order,prog.annotations)
265
 
266
  # ══════════════════════════════════════════════════════════════════════
267
+ # SECTION 4: AXL LAYER
268
  # ══════════════════════════════════════════════════════════════════════
269
  @dataclass
270
  class AXLInvariant:
271
+ name:str; expr:str; tolerance:float=VERIFY_G2_TOLERANCE
 
 
272
 
273
  @dataclass
274
  class AXLProblemDef:
275
+ name:str; description:str; axioms:Set[str]
276
+ variables:List[Dict]; constraints:List[Dict]
277
+ scopes:List[Dict]; anchors:List[AXLInvariant]
278
+ observations:Dict[str,float]=field(default_factory=dict)
 
 
 
 
 
279
  def variables_list(self): return [v["name"] for v in self.variables]
280
 
281
  class AXLtoPSLCompiler:
282
+ """Compile AXL directly to PSL β€” used only for the BASE problem oracle."""
283
+ TEND_WEIGHT=0.3
284
+ def compile(self,prob:AXLProblemDef) -> str:
285
+ lines=[f"SPACE base_{prob.name}"]
 
 
 
 
286
  for v in prob.variables:
287
+ lo,hi=v["lo"],v["hi"]
 
288
  if v["name"] in prob.observations:
289
+ val=prob.observations[v["name"]]; lo=hi=val
 
290
  lines.append(f"VAR {v['name']} {lo} {hi}")
 
291
  lines.append(f"ANNOTATE {v['name']} FROM_AXL {prob.name}.{v['name']}")
 
 
292
  for c in prob.constraints:
293
+ wt=c.get("weight",1.0)
294
+ if c["kind"]=="CONSERVE": lines.append(f"CONSERVE {c['expr']}")
295
+ elif c["kind"]=="BRANCH":
296
+ lines.append(f"BRANCH {c.get('name','br')}")
297
+ for opt in c.get("options",[]): lines.append(f" OPTION {opt}")
298
+ lines.append("END BRANCH")
299
+ else: lines.append(f"{c['kind']} {c['expr']} WEIGHT {wt}")
 
300
  for v in prob.variables:
301
+ tends=v.get("tends","")
302
+ if tends=="MIN": lines.append(f"EQ {v['name']} - {v['lo']} WEIGHT {self.TEND_WEIGHT}")
303
+ elif tends=="MAX": lines.append(f"EQ {v['name']} - {v['hi']} WEIGHT {self.TEND_WEIGHT}")
304
+ elif tends.startswith("TOWARD:"): lines.append(f"EQ {v['name']} - {tends.split(':')[1]} WEIGHT {self.TEND_WEIGHT}")
 
 
 
 
 
 
305
  for sc in prob.scopes:
306
+ dep=f" DEPENDS {' '.join(sc.get('depends',[]))}" if sc.get('depends') else ""
307
+ lines.append(f"SCOPE {sc['name']} ORDER {sc.get('order',0)}{dep}")
308
+ if sc.get("vars"): lines.append(f" LINK {' '.join(sc['vars'])}")
309
+ for c in sc.get("constraints",[]):
310
+ wt=c.get("weight",1.0)
311
+ if c["kind"]=="CONSERVE": lines.append(f" CONSERVE {c['expr']}")
312
+ elif c["kind"]=="BRANCH":
 
 
 
313
  lines.append(f" BRANCH {c.get('name','br')}")
314
+ for opt in c.get("options",[]): lines.append(f" OPTION {opt}")
 
315
  lines.append(" END BRANCH")
316
+ else: lines.append(f" {c['kind']} {c['expr']} WEIGHT {wt}")
 
317
  lines.append("END")
 
318
  return "\n".join(lines)
319
 
320
+ AXL_COMPILER=AXLtoPSLCompiler()
321
 
322
  # ══════════════════════════════════════════════════════════════════════
323
  # SECTION 5: PROBLEM & CONSTRAINT COMPILATION
 
333
  fast_iv:Optional[Callable]=field(default=None,repr=False)
334
  fast_pt:Optional[Callable]=field(default=None,repr=False)
335
  fast_np:Optional[Callable]=field(default=None,repr=False)
 
336
  syms_used:List[str]=field(default_factory=list)
337
  parsed:Optional[sp.Expr]=field(default=None,repr=False)
338
  scope:str="root"
 
364
  mc.fast_iv=compile_iv(parsed,variables)
365
  mc.fast_pt=sp.lambdify([sp.Symbol(v) for v in mc.syms_used],parsed,modules="math")
366
  mc.fast_np=sp.lambdify([sp.Symbol(v) for v in mc.syms_used],parsed,modules="numpy")
 
 
 
 
367
  fast_jac={}
368
  for v in mc.syms_used:
369
  try:
 
377
  for sym in parsed.free_symbols:
378
  v_str=str(sym)
379
  try:
380
+ sols=sp.solve(parsed,sym); pm[v_str]=[]
 
381
  for sol in sols:
382
  fs=list(sol.free_symbols)
383
  pm[v_str].append({"syms":[str(s) for s in fs],"func":sp.lambdify(fs,sol,modules="math")})
 
389
 
390
  @dataclass
391
  class Problem:
392
+ pid:str; variables:List[str]; constraints:List[Constraint]; bounds:Dict[str,Tuple[float,float]]
 
393
  tier:str="hard"
394
  scope_groups:Dict[str,List[int]]=field(default_factory=dict)
395
  scope_vars:Dict[str,List[str]]=field(default_factory=dict)
 
415
  if mc.fast_pt is None: total+=1.0; continue
416
  try:
417
  val=float(mc.fast_pt(*[b.get(v,0.0) for v in mc.syms_used]))
418
+ if mc.kind=="equality": total+=abs(val)*mc.weight
419
+ elif mc.direction=="geq": total+=max(0.0,-val)*mc.weight
420
+ else: total+=max(0.0,val)*mc.weight
421
  except: total+=1.0
422
  for v,(lo,hi) in self.bounds.items():
423
  bv=b.get(v,0.0); total+=max(0.0,lo-bv)+max(0.0,bv-hi)
 
468
  for bmc in mc.branches:
469
  if bmc.fast_np:
470
  try:
471
+ args=[b_matrix[:,vti[v]] for v in bmc.syms_used if v in vti]
472
+ if len(args)!=len(bmc.syms_used): continue
473
  val=bmc.fast_np(*args)
474
  if not isinstance(val,np.ndarray): val=np.full(N,float(val),dtype=np.float32)
475
  bv.append(np.abs(val))
 
479
  else:
480
  if mc.fast_np is None: total+=1.0; continue
481
  try:
482
+ args=[b_matrix[:,vti[v]] for v in mc.syms_used if v in vti]
483
+ if len(args)!=len(mc.syms_used): total+=1.0; continue
484
  val=mc.fast_np(*args)
485
  if not isinstance(val,np.ndarray): val=np.full(N,float(val),dtype=np.float32)
486
  if mc.kind=="equality": total+=np.abs(val)*mc.weight
 
497
  # ══════════════════════════════════════════════════════════════════════
498
  @dataclass
499
  class VerifyResult:
500
+ gate1_ce:float; gate1_pass:bool
501
+ gate2_results:Dict[str,Tuple[float,bool]]
502
+ gate2_pass:bool; overall_pass:bool
503
+ failed_gates:List[str]=field(default_factory=list)
 
 
504
 
505
  class VerifyLayer:
506
+ def run(self,binding:Dict[str,float],base_problem:Problem,anchors:List[AXLInvariant]) -> VerifyResult:
507
+ g1_ce=base_problem.constraint_energy(binding)
508
+ g1_pass=g1_ce<VERIFY_G1_THRESHOLD
509
+ g2={}
 
 
 
 
 
510
  for inv in anchors:
511
  try:
512
+ syms={v:sp.Symbol(v) for v in binding}
513
+ expr=parse_expr(inv.expr,local_dict=syms)
514
+ func=sp.lambdify(list(expr.free_symbols),expr,modules="math")
515
+ args=[binding.get(str(s),0.0) for s in expr.free_symbols]
516
+ err=abs(float(func(*args)))
517
+ g2[inv.name]=(round(err,6),err<inv.tolerance)
518
+ except: g2[inv.name]=(999.0,False)
519
+ g2_pass=all(v[1] for v in g2.values()) if g2 else True
520
+ failed=(["Gate1"] if not g1_pass else [])+[k for k,v in g2.items() if not v[1]]
521
+ return VerifyResult(gate1_ce=round(g1_ce,6),gate1_pass=g1_pass,
522
+ gate2_results=g2,gate2_pass=g2_pass,
523
+ overall_pass=g1_pass and g2_pass,failed_gates=failed)
524
+
525
+ VERIFY_LAYER=VerifyLayer()
526
 
527
+ # ══════════════════════════════════════════════════════════════════════
528
+ # SECTION 7: DOMAIN STRATEGY ← NEW
529
+ # Wisdom layer informs HOW to search, not WHAT to search.
530
+ # ══════════════════════════════════════════════════════════════════════
531
+ @dataclass
532
+ class DomainStrategy:
533
+ name: str
534
+ hyp_weights: Dict[str,float] # h_type β†’ confidence multiplier
535
+ polish: str # "adam" | "gradient" | "both"
536
+ explore_early: bool # True β†’ explore_trigger=2, False β†’ 4
537
+ scope_priority: str # "ordered" | "hub" | "natural"
538
+ note: str=""
539
+
540
+ DOMAIN_STRATEGIES: Dict[str,DomainStrategy] = {
541
+ "NormManifold": DomainStrategy("NormManifold",
542
+ {"branch":1.3,"manifold":2.0,"chain":0.8,"residual_chain":1.1},
543
+ "adam", False, "hub",
544
+ "Quadratic manifolds β†’ manifold tangents most useful; Adam navigates smooth bowl"),
545
+ "MetricPacking": DomainStrategy("MetricPacking",
546
+ {"branch":1.2,"manifold":0.8,"chain":1.5,"residual_chain":1.2},
547
+ "gradient", True, "hub",
548
+ "Separation constraints β†’ algebraic branches + early exploration"),
549
+ "SequentialChain": DomainStrategy("SequentialChain",
550
+ {"branch":0.9,"manifold":0.5,"chain":2.0,"residual_chain":1.5},
551
+ "gradient", False, "ordered",
552
+ "Ordered chains β†’ topology chain hypotheses dominate"),
553
+ "BilinearCoupling":DomainStrategy("BilinearCoupling",
554
+ {"branch":1.3,"manifold":1.6,"chain":1.0,"residual_chain":1.0},
555
+ "adam", False, "natural",
556
+ "Bilinear products β†’ manifold + branch both useful; Adam for coupling"),
557
+ "PhaseJump": DomainStrategy("PhaseJump",
558
+ {"branch":2.0,"manifold":1.0,"chain":0.7,"residual_chain":0.9},
559
+ "adam", True, "natural",
560
+ "OR constraints β†’ branch hypotheses highest priority; explore early"),
561
+ "GraphSpace": DomainStrategy("GraphSpace",
562
+ {"branch":1.0,"manifold":0.7,"chain":1.3,"residual_chain":1.0},
563
+ "gradient", False, "hub",
564
+ "Network hubs β†’ chain + gradient works well"),
565
+ "HierarchicalTree":DomainStrategy("HierarchicalTree",
566
+ {"branch":0.9,"manifold":0.6,"chain":1.8,"residual_chain":1.2},
567
+ "gradient", False, "ordered",
568
+ "Hierarchical ordering β†’ chain hypotheses + ordered scope priority"),
569
+ "DiscreteSchedule":DomainStrategy("DiscreteSchedule",
570
+ {"branch":1.0,"manifold":0.5,"chain":2.0,"residual_chain":1.3},
571
+ "gradient", False, "ordered",
572
+ "Scheduling β†’ chain + ordering; manifold less useful for discrete"),
573
+ "FluidContinuum": DomainStrategy("FluidContinuum",
574
+ {"branch":1.1,"manifold":1.8,"chain":0.8,"residual_chain":1.0},
575
+ "adam", True, "natural",
576
+ "Continuous flow β†’ manifold tangents + Adam; explore broadly"),
577
+ }
578
+
579
+ DEFAULT_STRATEGY=DomainStrategy("Default",
580
+ {"branch":1.0,"manifold":1.0,"chain":1.0,"residual_chain":1.0},
581
+ "both", False, "natural")
582
+
583
+ def get_strategy(domain_name:str) -> DomainStrategy:
584
+ # Check exact match, then prefix match for derived domains (unions, duals)
585
+ if domain_name in DOMAIN_STRATEGIES: return DOMAIN_STRATEGIES[domain_name]
586
+ for k,v in DOMAIN_STRATEGIES.items():
587
+ if k in domain_name: return v
588
+ return DEFAULT_STRATEGY
589
 
590
+ # ══════════════════════════════════════════════════════════════════════
591
+ # SECTION 8: SHARED TYPES (before solve_by_hypothesis to avoid forward refs)
592
+ # ══════════════════════════════════════════════════════════════════════
593
+ @dataclass
594
+ class Hypothesis:
595
+ hid:str; binding:Dict[str,float]; h_type:str; claim:str
596
+ derivation:List[str]; pinned_vars:Dict[str,float]; free_vars:List[str]
597
+ confidence:float; ce:float=float('inf')
598
 
599
+ @dataclass
600
+ class HypothesisTrace:
601
+ hid:str; round_idx:int; domain_name:str; action:str
602
+ ce_before:float; ce_after:float
603
+ verify:Optional[VerifyResult]; survived:bool; elapsed_ms:float
604
+ strategy_note:str=""
605
+
606
+ @dataclass
607
+ class StructuralModel:
608
+ best_binding:Dict[str,float]; best_ce:float
609
+ tested:List[Hypothesis]=field(default_factory=list)
610
+ failure_patterns:Set[str]=field(default_factory=set)
611
+ scope_witnesses:Dict[str,Dict[str,Tuple]]=field(default_factory=dict)
612
+ confirmed_ranges:Dict[str,Tuple[float,float]]=field(default_factory=dict)
613
+ conflict_vars:Set[str]=field(default_factory=set)
614
 
615
  # ══════════════════════════════════════════════════════════════════════
616
+ # SECTION 9: ORACLES
617
  # ══════════════════════════════════════════════════════════════════════
618
  @dataclass
619
  class L7Certificate: topology_sig:str; hub_vars:List[str]; solve_order:List[str]
 
676
  re=[e for e,c in sorted(de,key=lambda x:-x[1])[:5]]
677
  return L9Certificate(round(ce,6),rv,re)
678
 
679
+ TOPOLOGY_ORACLE=TopologyOracle()
680
+ COHERENCE_ORACLE=CoherenceOracle()
681
+ RESIDUAL_ORACLE=ResidualOracle()
682
 
683
  # ══════════════════════════════════════════════════════════════════════
684
+ # SECTION 10: CORE SOLVERS
685
  # ══════════════════════════════════════════════════════════════════════
686
  def _hc4(box,constraints):
687
  cur=dict(box)
 
700
  try:
701
  riv=mc.fast_iv(cur)
702
  if ((mc.kind=="equality" and not riv.contains_zero()) or
703
+ (mc.kind=="inequality" and ((mc.direction=="geq" and riv.hi<-1e-10) or
704
+ (mc.direction=="leq" and riv.lo>1e-10)))):
705
  return None
706
  except: pass
707
  return cur
 
793
  if nce<best_ce: best_ce=nce; best_b=dict(b)
794
  return best_b,best_ce
795
 
796
+ def solve_by_hypothesis(problem:Problem, budget:int=TOTAL_BUDGET,
797
+ strategy:Optional[DomainStrategy]=None) -> Dict:
798
  t0=time.time()
799
+ explore_trigger = 2 if (strategy and strategy.explore_early) else 4
800
+ polish_pref = strategy.polish if strategy else "both"
801
+
802
  init_b={v:max(lo,min(hi,(lo+hi)/2)) for v,(lo,hi) in problem.bounds.items()}
803
  tb=_global_hc4_tighten(problem)
804
  full_box={v:IV(tb[v][0],tb[v][1]) for v in problem.variables if v in tb}
 
806
  if mprt_ce<problem.constraint_energy(init_b): init_b=mprt_b
807
  snap_b,snap_ce=_algebraic_snap(problem,init_b)
808
  if snap_ce<problem.constraint_energy(init_b): init_b=snap_b
809
+
810
+ model=StructuralModel(best_binding=dict(init_b),best_ce=problem.constraint_energy(init_b))
 
 
 
 
 
 
 
 
 
 
 
811
  active_scopes=[s for s in problem.scope_order if s!="root" and problem.scope_vars.get(s)]
812
+
813
  if model.best_ce<SOLVE_THRESHOLD:
814
  l7=TOPOLOGY_ORACLE.evaluate(problem,active_scopes)
815
  l9=RESIDUAL_ORACLE.evaluate(model.best_binding,problem,l7.hub_vars)
816
+ return {"binding":model.best_binding,"ce":model.best_ce,"solved":True,
817
+ "l7":l7,"l9":l9,"time":round(time.time()-t0,3)}
818
 
819
+ oracle=HypothesisOracle(strategy=strategy); consec=0
820
  for round_idx in range(HYPOTHESIS_MAX_ROUNDS):
821
  if model.best_ce<SOLVE_THRESHOLD: break
822
  l7=TOPOLOGY_ORACLE.evaluate(problem,active_scopes)
 
840
  eb,ece=_mprt_sample(problem,work_box,N_MPRT_EXPLORE)
841
  if ece<model.best_ce: model.best_ce=ece; model.best_binding=dict(eb); consec=0
842
 
843
+ # Polish β€” strategy-directed
844
+ if polish_pref in ("gradient","both") and SOLVE_THRESHOLD<model.best_ce<2.0:
845
  gb,gce=_gradient_polish(problem,model.best_binding)
846
  if gce<model.best_ce: model.best_ce=gce; model.best_binding=gb
847
+ if polish_pref in ("adam","both") and SOLVE_THRESHOLD<model.best_ce<1.0:
848
  ab,ace=_adam_polish(problem,model.best_binding)
849
  if ace<model.best_ce: model.best_ce=ace; model.best_binding=ab
850
+ # Always try Adam as last resort if still not solved
851
+ if SOLVE_THRESHOLD<model.best_ce<0.5 and polish_pref=="gradient":
852
+ ab,ace=_adam_polish(problem,model.best_binding,steps=60)
853
+ if ace<model.best_ce: model.best_ce=ace; model.best_binding=ab
854
 
855
  final_l7=TOPOLOGY_ORACLE.evaluate(problem,active_scopes)
856
  final_l9=RESIDUAL_ORACLE.evaluate(model.best_binding,problem,final_l7.hub_vars)
 
858
  "l7":final_l7,"l9":final_l9,"time":round(time.time()-t0,3)}
859
 
860
  # ══════════════════════════════════════════════════════════════════════
861
+ # SECTION 11: HYPOTHESIS ENGINE
862
  # ══════════════════════════════════════════════════════════════════════
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
863
  class HypothesisOracle:
864
+ def __init__(self, strategy:Optional[DomainStrategy]=None):
865
+ self.strategy=strategy
866
+
867
  def generate(self,problem,model,l7,l8,l9,round_idx):
868
  hyps=[]
869
  hyps.extend(self._branch_hypotheses(problem,model,l9))
 
871
  hyps.extend(self._coherence_hypotheses(problem,model,l8))
872
  hyps.extend(self._residual_chain_hypotheses(problem,model,l9))
873
  if round_idx>=3: hyps.extend(self._manifold_tangent_hypotheses(problem,model,l9))
874
+ # Apply strategy confidence weights
875
+ if self.strategy:
876
+ for hyp in hyps:
877
+ w=self.strategy.hyp_weights.get(hyp.h_type,1.0)
878
+ hyp.confidence=min(0.99,hyp.confidence*w)
879
  hyps=[h for h in hyps if h.hid not in model.failure_patterns]
880
  return sorted(hyps,key=lambda h:-h.confidence)
881
 
 
891
  val=float(proj["func"](*[ref.get(s,0.0) for s in proj["syms"]]))
892
  if not math.isfinite(val) or abs(val)>1e6 or not(lo<=val<=hi): continue
893
  b=dict(ref); b[v]=val
894
+ hyps.append(Hypothesis(hid=f"br_{hash(expr_str)%9999}_{v}_{pi}",
895
+ binding=b,h_type="branch",claim=f"AlgBranch:{v}={val:.4f}",
896
+ derivation=[expr_str],pinned_vars={v:val},
897
+ free_vars=[u for u in problem.variables if u!=v],
898
  confidence=0.85 if pi==0 else 0.70))
899
  except: pass
900
  return hyps
 
907
  if not svars: continue
908
  scope_mcs=[problem.compiled_constraints[i] for i in problem.scope_groups.get(sn,[])]
909
  if not scope_mcs: continue
910
+ start_box={v:IV(*tb.get(v,problem.bounds.get(v,(-10,10)))) for v in svars if v in problem.bounds}
911
  cont=_hc4(start_box,scope_mcs)
912
  if cont:
913
  for v,iv in cont.items():
914
  b[v]=iv.mid()
915
  chain_steps.append(sn)
916
  if chain_steps:
917
+ hyps.append(Hypothesis(
918
+ hid=f"chain_{'_'.join(c[:4] for c in chain_steps)}",
919
+ binding=b,h_type="chain",claim=f"TopoChain({' β†’ '.join(chain_steps)})",
920
+ derivation=chain_steps,
921
+ pinned_vars={v:b[v] for sn in chain_steps for v in problem.scope_vars.get(sn,[]) if v in b},
922
  free_vars=[v for v in problem.variables if not any(v in problem.scope_vars.get(sn,[]) for sn in chain_steps)],
923
  confidence=0.80))
924
  return hyps
 
929
  for v,(lo,hi) in l8.tightened_bounds.items():
930
  if v in problem.bounds: b[v]=(lo+hi)/2.0
931
  return [Hypothesis(hid=f"coh_{hash(tuple(sorted(l8.incoherent_vars)))%9999}",
932
+ binding=b,h_type="coherence",claim=f"CoherMeet({', '.join(list(l8.incoherent_vars)[:3])})",
933
  derivation=list(l8.incoherent_vars),
934
  pinned_vars={v:b[v] for v in l8.incoherent_vars if v in b},
935
+ free_vars=[v for v in problem.variables if v not in l8.incoherent_vars],
936
+ confidence=0.70)]
937
 
938
  def _residual_chain_hypotheses(self,problem,model,l9):
939
  if not l9.dominant_vars: return []
 
1008
  return best_b,best_ce
1009
 
1010
  # ══════════════════════════════════════════════════════════════════════
1011
+ # SECTION 12: WISDOM LAYER
1012
+ # The PSLTranslator is the core fix:
1013
+ # domain PSL = base constraints (real, full weight) +
1014
+ # soft domain structural hints (weight 0.15)
1015
+ # observations pin variables via zero-width VAR bounds
1016
  # ══════════════════════════════════════════════════════════════════════
1017
  class Axiom:
1018
  DISCRETE="DISCRETE"; CONTINUOUS="CONTINUOUS"; METRIC="METRIC"; NETWORK="NETWORK"
 
1020
  QUADRATIC="QUADRATIC"; BILINEAR="BILINEAR"; ORDERED="ORDERED"; SYMMETRIC="SYMMETRIC"
1021
  TRANSITIVE="TRANSITIVE"; ATOMIC="ATOMIC"; COMPOSITE="COMPOSITE"
1022
 
1023
+ AXIOM_CONTRADICTIONS=[
1024
+ {Axiom.DISCRETE,Axiom.CONTINUOUS},{Axiom.INJECTIVE,Axiom.SURJECTIVE},
1025
+ {Axiom.ORDERED,Axiom.SYMMETRIC},{Axiom.ATOMIC,Axiom.COMPOSITE},
1026
+ {Axiom.CONSERVED,Axiom.MUTABLE}]
1027
 
1028
  @dataclass
1029
  class AbstractDomain:
1030
  name:str; axioms:Set[str]; derivation:str="Base"; confidence:float=1.0
1031
+ def is_consistent(self): return not any(p.issubset(self.axioms) for p in AXIOM_CONTRADICTIONS)
 
 
1032
 
1033
  BASE_LIBRARY=[
1034
  AbstractDomain("GraphSpace",{Axiom.DISCRETE,Axiom.NETWORK,Axiom.SYMMETRIC,Axiom.ATOMIC}),
 
1048
  @staticmethod
1049
  def union(d1,d2):
1050
  merged=d1.axioms|d2.axioms
1051
+ c=AbstractDomain(WisdomOps._fn(f"{d1.name}U{d2.name}"),merged,"Union",(d1.confidence+d2.confidence)/2)
1052
  for pair in AXIOM_CONTRADICTIONS:
1053
  if pair.issubset(c.axioms):
1054
  counts={ax:sum(ax in d.axioms for d in BASE_LIBRARY) for ax in pair}
1055
  c.axioms.discard(min(counts,key=counts.get))
1056
  return c
1057
  @staticmethod
1058
+ def relax(d,axioms): return AbstractDomain(WisdomOps._fn(f"{d.name}_Rlx"),d.axioms-axioms,"Relax",d.confidence*0.85)
1059
  @staticmethod
1060
  def inject(d,axioms):
1061
+ merged=d.axioms|axioms; ax_str="".join(a[:2] for a in axioms)
1062
+ c=AbstractDomain(WisdomOps._fn(f"{d.name}+{ax_str}"),merged,"Inject",d.confidence*0.9)
 
1063
  for pair in AXIOM_CONTRADICTIONS:
1064
  if pair.issubset(c.axioms):
1065
  to_rm=pair-d.axioms
 
1075
  return AbstractDomain(WisdomOps._fn(f"{d.name}*"),{fm.get(ax,ax) for ax in d.axioms},"Dual",d.confidence*0.7)
1076
 
1077
  class PSLTranslator:
1078
+ """
1079
+ THE CORE FIX.
1080
+ Generates domain PSL = base constraints (full weight) + soft domain hints (0.15).
1081
+ Observations are pinned via zero-width VAR bounds.
1082
+ Domain NEVER replaces the base problem β€” it only adds soft guidance.
1083
+ """
1084
+ SOFT = 0.15
1085
+
1086
+ def compile(self, domain:AbstractDomain, axl_def:AXLProblemDef) -> str:
1087
+ ax=domain.axioms; variables=axl_def.variables_list(); n=len(variables)
1088
+ lines=[f"SPACE domain_{domain.name.replace(' ','_')}"]
1089
+
1090
+ # ── 1. Variables: use actual bounds, pin observations ──────────
1091
+ for v in axl_def.variables:
1092
+ lo,hi=v["lo"],v["hi"]
1093
+ if v["name"] in axl_def.observations:
1094
+ val=axl_def.observations[v["name"]]; lo=hi=val # PINNED
1095
+ lines.append(f"VAR {v['name']} {lo} {hi}")
1096
+
1097
+ # ── 2. Base global constraints β€” ALWAYS, full weight ───────────
1098
+ for c in axl_def.constraints:
1099
+ wt=c.get("weight",1.0)
1100
+ if c["kind"]=="CONSERVE":
1101
+ lines.append(f"CONSERVE {c['expr']}")
1102
+ elif c["kind"]=="BRANCH":
1103
+ lines.append(f"BRANCH {c.get('name','br')}")
1104
+ for opt in c.get("options",[]): lines.append(f" OPTION {opt}")
1105
+ lines.append("END BRANCH")
1106
+ else:
1107
+ lines.append(f"{c['kind']} {c['expr']} WEIGHT {wt}")
1108
+
1109
+ # ── 3. Base scopes β€” ALWAYS, at their original ORDER ──────────
1110
+ for sc in axl_def.scopes:
1111
+ order=sc.get("order",0)
1112
+ dep=f" DEPENDS {' '.join(sc.get('depends',[]))}" if sc.get('depends') else ""
1113
+ lines.append(f"SCOPE {sc['name']} ORDER {order}{dep}")
1114
+ if sc.get("vars"): lines.append(f" LINK {' '.join(sc['vars'])}")
1115
+ for c in sc.get("constraints",[]):
1116
+ wt=c.get("weight",1.0)
1117
+ if c["kind"]=="CONSERVE":
1118
+ lines.append(f" CONSERVE {c['expr']}")
1119
+ elif c["kind"]=="BRANCH":
1120
+ lines.append(f" BRANCH {c.get('name','br')}")
1121
+ for opt in c.get("options",[]): lines.append(f" OPTION {opt}")
1122
+ lines.append(" END BRANCH")
1123
+ else:
1124
+ lines.append(f" {c['kind']} {c['expr']} WEIGHT {wt}")
1125
+ lines.append("END")
1126
+
1127
+ # ── 4. Soft domain structural hints β€” ORDER 100+, weight 0.15 ─
1128
+ # These nudge the search without overriding base constraints.
1129
+ si=100
1130
+
1131
+ if Axiom.ORDERED in ax and Axiom.BILINEAR not in ax and n>=2:
1132
+ lines.append(f"SCOPE domain_order ORDER {si}")
1133
  lines.append(f" LINK {' '.join(variables)}")
1134
+ for i in range(n-1):
1135
+ lines.append(f" GEQ {variables[i+1]} - {variables[i]} WEIGHT {self.SOFT}")
1136
  lines.append("END"); si+=1
1137
+
1138
+ if Axiom.SYMMETRIC in ax and Axiom.NETWORK in ax and n>=2:
1139
+ lines.append(f"SCOPE domain_sym ORDER {si}")
1140
+ lines.append(f" LINK {' '.join(variables[:min(4,n)])}")
1141
+ for i in range(0,min(4,n)-1,2):
1142
+ lines.append(f" EQ {variables[i]} + {variables[i+1]} WEIGHT {self.SOFT}")
 
 
 
 
 
 
 
 
 
1143
  lines.append("END"); si+=1
1144
+
1145
  if Axiom.METRIC in ax and Axiom.INJECTIVE in ax and n>=2:
1146
+ lines.append(f"SCOPE domain_sep ORDER {si}")
1147
+ lines.append(f" LINK {' '.join(variables[:min(4,n)])}")
1148
+ for a,b in itertools.combinations(range(min(4,n)),2):
1149
+ lines.append(f" GEQ ({variables[a]} - {variables[b]})**2 - 0.04 WEIGHT {self.SOFT}")
1150
  lines.append("END"); si+=1
1151
+
1152
+ if Axiom.BILINEAR in ax and n>=2:
1153
+ lines.append(f"SCOPE domain_bilinear ORDER {si}")
1154
+ grp=variables[:min(3,n)]
1155
+ lines.append(f" LINK {' '.join(grp)}")
1156
+ for j in range(len(grp)-1):
1157
+ lines.append(f" EQ {grp[j]}*{grp[j+1]} WEIGHT {self.SOFT}")
1158
  lines.append("END"); si+=1
1159
+
1160
+ if Axiom.MANIFOLD_HINT(ax) and n>=2:
1161
+ # Soft quadratic regularisation β€” only if base has no quadratic conserve
1162
+ has_quad=any("**2" in c.get("expr","") and c["kind"]=="CONSERVE" for c in axl_def.constraints)
1163
+ if not has_quad:
1164
+ lines.append(f"SCOPE domain_quad ORDER {si}")
1165
+ lines.append(f" LINK {' '.join(variables)}")
1166
+ lines.append(f" EQ {' + '.join(f'{v}**2' for v in variables)} - {float(n)*0.25:.2f} WEIGHT {self.SOFT}")
1167
+ lines.append("END"); si+=1
1168
+
1169
  return "\n".join(lines)
1170
 
1171
+ # Small helper β€” avoid if-chain sprawl
1172
+ def _manifold_hint(ax): return Axiom.QUADRATIC in ax and Axiom.CONSERVED not in ax
1173
+ Axiom.MANIFOLD_HINT = staticmethod(_manifold_hint)
1174
+
1175
  @dataclass
1176
  class DebateRound:
1177
  round_idx:int; domain:AbstractDomain; psl_text:str
1178
+ ce:float; solved:bool; action:str; strategy_name:str=""
1179
  traces:List[HypothesisTrace]=field(default_factory=list)
1180
 
1181
  class WisdomDebate:
 
1195
 
1196
  def _candidates(self,current,round_idx):
1197
  cands=[(d,"Library") for d in self.library]
1198
+ for d1,d2 in itertools.combinations(self.library[:5],2):
1199
+ cands.append((WisdomOps.union(d1,d2),"Union"))
1200
  for d in self.library[:4]: cands.append((WisdomOps.dual(d),"Dual"))
1201
  if round_idx>2:
1202
  chosen=random.sample([Axiom.BILINEAR,Axiom.QUADRATIC,Axiom.CONSERVED,Axiom.METRIC,Axiom.ORDERED],2)
1203
+ cands.append((WisdomOps.inject(current,set(chosen)),"RandInject"))
1204
  return cands
1205
 
1206
  def debate(self,axl_def:AXLProblemDef,base_problem:Problem) -> Tuple[Dict,float,List[DebateRound]]:
1207
+ problem_axioms=axl_def.axioms
1208
  current=min(self.library,key=lambda d:self._critic_score(problem_axioms,d)[0])
1209
  self.history=[]; self.best_ce=float('inf'); self.best_binding={}; self.all_traces=[]
1210
 
1211
+ print(f"\n{'═'*60}\nDEBATE: {axl_def.name} | Axioms: {problem_axioms}\n{'═'*60}")
1212
 
1213
  for round_idx in range(self.MAX_ROUNDS):
1214
  cands=self._candidates(current,round_idx)
 
1216
  scored.sort(key=lambda x:x[0])
1217
  tried={r.domain.name for r in self.history}
1218
  score,winner,action=next((s for s in scored if s[1].name not in tried),scored[0])
1219
+ strategy=get_strategy(winner.name)
1220
 
1221
+ print(f"\n[R{round_idx+1}] {winner.name} | {action} | score={score:.1f} | strategy={strategy.name}")
1222
+ print(f" hint: {strategy.note[:60]}")
1223
+
1224
+ # ── KEY FIX: translate generates base + soft hints ─────────
1225
+ psl_text=self.translator.compile(winner,axl_def)
1226
  round_traces=[]
1227
 
1228
  try:
1229
  prog=parse_psl(psl_text); ep=expand_psl(prog)
1230
  if not ep.variables:
1231
+ print(" βœ— No variables"); current=winner; continue
1232
+
1233
  dom_prob=Problem(pid=f"dom_{winner.name}",variables=ep.variables,
1234
  constraints=ep.constraints,bounds=ep.bounds,
1235
  scope_groups=ep.scope_groups,scope_vars=ep.scope_vars,
1236
  scope_order=ep.scope_order)
1237
  t_start=time.time()
1238
+ # ── KEY FIX: solver receives domain strategy ───────────
1239
+ result=solve_by_hypothesis(dom_prob,budget=TOTAL_BUDGET,strategy=strategy)
1240
  elapsed=round((time.time()-t_start)*1000,1)
1241
  binding=result["binding"]; ce=result["ce"]; solved=result["solved"]
1242
 
1243
  # Verify against base problem + anchors
1244
  verify=VERIFY_LAYER.run(binding,base_problem,axl_def.anchors)
1245
+ base_ce=verify.gate1_ce # this IS the true base CE now
1246
 
1247
  trace=HypothesisTrace(
1248
  hid=f"R{round_idx+1}_{winner.name[:10]}",
1249
+ round_idx=round_idx+1, domain_name=winner.name,
1250
+ action=action, ce_before=self.best_ce if self.best_ce<float('inf') else 99.0,
1251
+ ce_after=base_ce, verify=verify,
1252
+ survived=verify.overall_pass, elapsed_ms=elapsed,
1253
+ strategy_note=strategy.polish)
 
 
 
1254
  round_traces.append(trace); self.all_traces.append(trace)
1255
 
1256
  status="βœ“ SURVIVED" if verify.overall_pass else "βœ— FAILED"
1257
+ print(f" DomCE={ce:.5f} BaseCE={base_ce:.5f} | "
1258
+ f"G1={'βœ“' if verify.gate1_pass else 'βœ—'} G2={'βœ“' if verify.gate2_pass else 'βœ—'} | {status}")
1259
  for inv_name,(err,passed) in verify.gate2_results.items():
1260
+ print(f" [{'βœ“' if passed else 'βœ—'}] {inv_name}: |err|={err:.6f}")
1261
 
1262
+ if base_ce<self.best_ce:
1263
+ self.best_ce=base_ce; self.best_binding=dict(binding); self.best_domain=winner
1264
+
1265
+ if verify.overall_pass:
1266
+ self.history.append(DebateRound(round_idx,winner,psl_text,base_ce,
1267
+ verify.overall_pass,action,strategy.name,round_traces))
1268
  print(f"\nβœ“ VERIFIED + SOLVED in round {round_idx+1}"); break
1269
+
1270
  except Exception as e:
1271
+ import traceback; traceback.print_exc()
1272
  trace=HypothesisTrace(hid=f"R{round_idx+1}_ERR",round_idx=round_idx+1,
1273
  domain_name=winner.name,action=action,ce_before=99.0,ce_after=99.0,
1274
  verify=None,survived=False,elapsed_ms=0)
1275
  round_traces.append(trace); self.all_traces.append(trace)
1276
 
1277
  self.history.append(DebateRound(round_idx,winner,psl_text,
1278
+ self.best_ce,self.best_ce<SOLVE_THRESHOLD,action,strategy.name,round_traces))
1279
  current=winner
1280
 
1281
+ print(f"\n{'─'*60}\nRESULT: BaseCE={self.best_ce:.5f} | Domain={self.best_domain.name if self.best_domain else '?'}")
1282
  return self.best_binding,self.best_ce,self.history
1283
 
1284
  # ══════════════════════════════════════════════════════════════════════
1285
+ # SECTION 13: HARDCODED AXL PROBLEMS (all verified feasible)
1286
  # ══════════════════════════════════════════════════════════════════════
1287
+ AXL_PROBLEMS=[
 
 
 
 
 
 
 
1288
 
1289
+ # ── PROBLEM 1: NormManifold4D ─────────────────────────────────────
1290
+ # x1 pinned at 0.5. Find x2,x3,x4 on unit sphere summing to -0.5.
1291
+ # Known solution family: x2=-0.809, x3=0.309, x4=0 (and rotations)
1292
  AXLProblemDef(
1293
  name="NormManifold4D",
1294
+ description="4 vars on unit sphere, zero sum, x1 observed at 0.5",
1295
  axioms={Axiom.CONTINUOUS,Axiom.QUADRATIC,Axiom.CONSERVED,Axiom.METRIC,Axiom.SYMMETRIC},
1296
  variables=[
1297
+ {"name":"x1","lo":-1.0,"hi":1.0},
1298
+ {"name":"x2","lo":-1.0,"hi":1.0},
1299
+ {"name":"x3","lo":-1.0,"hi":1.0},
1300
+ {"name":"x4","lo":-1.0,"hi":1.0},
1301
  ],
1302
  constraints=[
1303
  {"kind":"CONSERVE","expr":"x1**2 + x2**2 + x3**2 + x4**2 - 1.0"},
1304
+ {"kind":"EQ", "expr":"x1 + x2 + x3 + x4","weight":2.0},
1305
  ],
1306
  scopes=[{"name":"sphere","order":0,"vars":["x1","x2","x3","x4"],
1307
+ "constraints":[
1308
+ {"kind":"EQ","expr":"x1**2 + x2**2 + x3**2 + x4**2 - 1.0","weight":5.0},
1309
+ ]}],
1310
  anchors=[
1311
  AXLInvariant("unit_sphere","x1**2 + x2**2 + x3**2 + x4**2 - 1.0",1e-3),
1312
  AXLInvariant("zero_sum","x1 + x2 + x3 + x4",1e-3),
1313
+ AXLInvariant("x1_pinned","x1 - 0.5",1e-3),
1314
  ],
1315
  observations={"x1":0.5},
1316
  ),
1317
+
1318
+ # ── PROBLEM 2: BilinearChain6 ─────────────────────────────────────
1319
+ # Known exact solution: t1=0.5,t2=1.0,t3=1.0,t4=2.0,t5=1.5,t6=3.0
1320
  AXLProblemDef(
1321
  name="BilinearChain6",
1322
+ description="6-var ordered chain with bilinear products and sum conservation",
1323
  axioms={Axiom.DISCRETE,Axiom.ORDERED,Axiom.BILINEAR,Axiom.CONSERVED,Axiom.NETWORK},
1324
  variables=[{"name":f"t{i}","lo":0.0,"hi":3.0} for i in range(1,7)],
1325
  constraints=[
 
1330
  {"kind":"GEQ","expr":"t5 - t4","weight":1.0},
1331
  {"kind":"GEQ","expr":"t6 - t5","weight":1.0},
1332
  ],
1333
+ scopes=[{"name":"chain","order":0,"vars":[f"t{i}" for i in range(1,7)],
1334
+ "constraints":[
1335
+ {"kind":"EQ","expr":"t1*t2 - 0.5","weight":2.0},
1336
+ {"kind":"EQ","expr":"t3*t4 - 2.0","weight":2.0},
1337
+ {"kind":"EQ","expr":"t5*t6 - 4.5","weight":2.0},
1338
+ ]}],
 
 
1339
  anchors=[
1340
  AXLInvariant("conservation","t1 + t2 + t3 + t4 + t5 + t6 - 9.0",1e-3),
1341
+ AXLInvariant("bilinear_lo","t1*t2 - 0.5",0.05),
1342
+ AXLInvariant("bilinear_mid","t3*t4 - 2.0",0.05),
1343
+ AXLInvariant("bilinear_hi","t5*t6 - 4.5",0.05),
1344
  ],
1345
  ),
1346
+
1347
+ # ── PROBLEM 3: PhaseCollider (FIXED β€” provably feasible) ──────────
1348
+ # Conservation: p2 = -p1 (momentum = 0)
1349
+ # Phase A: |p1|=0.5, separation=1.0. Phase B: |p1|=1.0, separation=4.0
1350
+ # Branch on p1 radius; separation follows from conservation automatically.
1351
  AXLProblemDef(
1352
  name="PhaseCollider",
1353
+ description="2-particle symmetric collision: conservation + phase branch",
1354
  axioms={Axiom.DISCRETE,Axiom.MUTABLE,Axiom.SYMMETRIC,Axiom.NETWORK,Axiom.CONSERVED},
1355
  variables=[
1356
+ {"name":"p1x","lo":-2.0,"hi":2.0},
1357
+ {"name":"p2x","lo":-2.0,"hi":2.0},
1358
+ {"name":"p1y","lo":-2.0,"hi":2.0},
1359
+ {"name":"p2y","lo":-2.0,"hi":2.0},
1360
  ],
1361
  constraints=[
1362
+ {"kind":"CONSERVE","expr":"p1x + p2x"}, # total momentum x = 0
1363
+ {"kind":"CONSERVE","expr":"p1y + p2y"}, # total momentum y = 0
 
 
 
 
 
 
 
 
 
 
1364
  ],
1365
+ scopes=[{"name":"phase","order":0,"vars":["p1x","p2x","p1y","p2y"],
1366
+ "constraints":[
1367
+ # Phase branch: p1 at radius 0.5 OR radius 1.0
1368
+ {"kind":"BRANCH","name":"phase_select","options":[
1369
+ "p1x**2 + p1y**2 - 0.25", # phase A: r=0.5
1370
+ "p1x**2 + p1y**2 - 1.0", # phase B: r=1.0
1371
+ ]},
1372
+ # Symmetry: p2 = -p1 (follows from conservation, reinforced)
1373
+ {"kind":"EQ","expr":"p1x + p2x","weight":3.0},
1374
+ {"kind":"EQ","expr":"p1y + p2y","weight":3.0},
1375
+ ]}],
1376
  anchors=[
1377
  AXLInvariant("momentum_x","p1x + p2x",1e-3),
1378
  AXLInvariant("momentum_y","p1y + p2y",1e-3),
1379
+ # Phase satisfied: product of both phase residuals near 0
1380
+ AXLInvariant("phase_A_or_B",
1381
+ "(p1x**2 + p1y**2 - 0.25)*(p1x**2 + p1y**2 - 1.0)",0.05),
1382
  ],
1383
  ),
1384
+
1385
+ # ── PROBLEM 4: MetricPacking3D ────────────────────────────────────
1386
+ # Known solution: a=-sqrt(3), b=0, c=sqrt(3) β‰ˆ (-1.732, 0, 1.732)
1387
+ # centred: 0 βœ“, sep_ab: 3 β‰₯ 1 βœ“, sep_bc: 3 β‰₯ 1 βœ“, sep_ac: 12 β‰₯ 4 βœ“, norm: 6 βœ“
1388
  AXLProblemDef(
1389
  name="MetricPacking3D",
1390
+ description="3 vars packed with min separation, centred, norm=6",
1391
  axioms={Axiom.CONTINUOUS,Axiom.METRIC,Axiom.INJECTIVE,Axiom.CONSERVED,Axiom.SYMMETRIC},
1392
  variables=[
1393
+ {"name":"a","lo":-3.0,"hi":3.0},
1394
+ {"name":"b","lo":-3.0,"hi":3.0},
1395
+ {"name":"c","lo":-3.0,"hi":3.0},
1396
  ],
1397
  constraints=[
1398
+ {"kind":"CONSERVE","expr":"a + b + c"}, # centred at 0
1399
  {"kind":"GEQ","expr":"(a - b)**2 - 1.0","weight":2.0},
1400
  {"kind":"GEQ","expr":"(b - c)**2 - 1.0","weight":2.0},
1401
  {"kind":"GEQ","expr":"(a - c)**2 - 4.0","weight":2.0},
 
 
 
 
 
 
 
1402
  ],
1403
+ scopes=[{"name":"packing","order":0,"vars":["a","b","c"],
1404
+ "constraints":[
1405
+ {"kind":"EQ","expr":"a**2 + b**2 + c**2 - 6.0","weight":2.0},
1406
+ ]}],
1407
  anchors=[
1408
  AXLInvariant("centred","a + b + c",1e-3),
1409
+ AXLInvariant("sep_ab","(a - b)**2 - 1.0",0.5), # β‰₯ 1 (tolerance on residual)
1410
+ AXLInvariant("sep_bc","(b - c)**2 - 1.0",0.5),
1411
+ AXLInvariant("norm_6","a**2 + b**2 + c**2 - 6.0",0.05),
1412
  ],
1413
  ),
1414
  ]
1415
 
1416
  def build_base_problem(axl_def:AXLProblemDef) -> Problem:
 
1417
  psl_text=AXL_COMPILER.compile(axl_def)
1418
  prog=parse_psl(psl_text); ep=expand_psl(prog)
1419
  return Problem(pid=f"base_{axl_def.name}",variables=ep.variables,
 
1422
  scope_order=ep.scope_order,annotations=ep.annotations)
1423
 
1424
  # ══════════════════════════════════════════════════════════════════════
1425
+ # SECTION 14: GLOBAL STATE + RUNNER
1426
  # ════════════════════════════════════════════════════════════════���═════
1427
  STATE_LOCK=threading.Lock()
1428
+ RUN_LOG:List[Dict]=[]
1429
  DEBATE_ENGINE=WisdomDebate()
1430
  POOL=ThreadPoolExecutor(max_workers=2)
1431
 
 
1433
  t0=time.time()
1434
  try:
1435
  base_prob=build_base_problem(axl_def)
1436
+ binding,ce,rounds=DEBATE_ENGINE.debate(axl_def,base_prob)
1437
  elapsed=round(time.time()-t0,2)
 
1438
  survived=[t for t in DEBATE_ENGINE.all_traces if t.survived]
1439
  failed=[t for t in DEBATE_ENGINE.all_traces if not t.survived]
1440
  best_trace=min((t for t in survived),key=lambda t:t.ce_after,default=None)
1441
+ actual_base_ce=base_prob.constraint_energy(binding)
 
 
 
 
1442
  record={
1443
  "problem":axl_def.name,
1444
  "description":axl_def.description,
1445
  "axioms":sorted(axl_def.axioms),
1446
+ "ce":round(actual_base_ce,6),
1447
+ "solved":len(survived)>0 and actual_base_ce<SOLVE_THRESHOLD,
 
1448
  "elapsed":elapsed,
1449
  "binding":{k:round(v,5) for k,v in binding.items()},
1450
+ "observations":axl_def.observations,
1451
  "traces":[{
1452
  "hid":t.hid,"round":t.round_idx,"domain":t.domain_name,
1453
+ "action":t.action,"strategy":t.strategy_note,
1454
+ "ce":round(t.ce_after,5),
1455
  "g1_ce":round(t.verify.gate1_ce,5) if t.verify else None,
1456
  "g1":t.verify.gate1_pass if t.verify else False,
1457
  "g2":t.verify.gate2_pass if t.verify else False,
1458
+ "g2_detail":{k:(round(err,6),ok)
1459
+ for k,(err,ok) in t.verify.gate2_results.items()} if t.verify else {},
1460
  "survived":t.survived,"ms":t.elapsed_ms,
1461
  } for t in DEBATE_ENGINE.all_traces],
1462
  "survived_count":len(survived),
 
1467
  RUN_LOG.append(record)
1468
  if len(RUN_LOG)>40: RUN_LOG.pop(0)
1469
  except Exception as e:
1470
+ import traceback; traceback.print_exc()
1471
  print(f"[RUN ERROR] {axl_def.name}: {e}")
1472
 
1473
  async def run_loop():
 
1475
  while True:
1476
  prob=random.choice(AXL_PROBLEMS)
1477
  await loop.run_in_executor(POOL,_run_problem,prob)
1478
+ await asyncio.sleep(0.3)
1479
 
1480
  @asynccontextmanager
1481
  async def lifespan(app):
 
1484
  POOL.shutdown(wait=False)
1485
 
1486
  # ══════════════════════════════════════════════════════════════════════
1487
+ # SECTION 15: FASTAPI + DASHBOARD
1488
  # ══════════════════════════════════════════════════════════════════════
1489
+ app=FastAPI(title="Practicality 11.1",lifespan=lifespan)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1490
 
1491
  def _render_collapse(record:Dict) -> str:
1492
+ b=record["binding"]; traces=record["traces"]
 
1493
  survived=[t for t in traces if t["survived"]]
 
 
1494
  best=min(survived,key=lambda t:t["ce"],default=None)
1495
+ if not best and traces: best=min(traces,key=lambda t:t.get("g1_ce") or 999.0)
1496
+ obs_vars=set(record.get("observations",{}).keys())
 
 
 
 
1497
 
1498
+ solved=record["solved"]
1499
+ ce=record["ce"]
1500
+ ce_color="#4CAF50" if solved else ("#FF9800" if ce<0.5 else "#EF5350")
1501
+ status="βœ“ SOLVED + VERIFIED" if solved else ("~ PARTIAL" if ce<0.5 else "βœ— UNSOLVED")
1502
 
1503
  binding_rows="".join(
1504
+ f"<tr><td style='color:#888;width:70px'>{v}</td>"
1505
  f"<td style='color:#26C6DA;font-weight:700'>{val:+.5f}</td>"
1506
+ f"<td style='color:#4CAF50;font-size:0.75em'>{'[OBS]' if v in obs_vars else ''}</td></tr>"
1507
+ for v,val in b.items())
 
1508
 
1509
  inv_rows=""
1510
  if best and best.get("g2_detail"):
1511
  for inv,(err,ok) in best["g2_detail"].items():
1512
  c="#4CAF50" if ok else "#EF5350"
1513
+ inv_rows+=(f"<tr><td style='color:{c}'>{'βœ“' if ok else 'βœ—'}</td>"
1514
+ f"<td style='color:#aaa'>{inv}</td>"
1515
+ f"<td style='color:{c}'>{err:.6f}</td></tr>")
1516
+
1517
+ def trail_span(t):
1518
+ c="#4CAF50" if t["survived"] else "#333"
1519
+ star=" β—€ SURVIVED" if t["survived"] else ""
1520
+ strat=f" [{t.get('strategy','')}]" if t.get("strategy") else ""
1521
+ return (f"<div style='color:{c};font-size:0.78em;margin-bottom:3px'>"
1522
+ f"[R{t['round']}] <span style='color:#FF9800'>{t['domain'][:14]}</span>"
1523
+ f" CE={t['ce']:.5f}"
1524
+ f" G1={'βœ“' if t['g1'] else 'βœ—'}{t.get('g1_ce',0):.4f}"
1525
+ f" G2={'βœ“' if t['g2'] else 'βœ—'}"
1526
+ f"<span style='color:#555'>{strat}</span>"
1527
+ f"<span style='color:#4CAF50;font-weight:700'>{star}</span></div>")
1528
+
1529
+ trail="".join(trail_span(t) for t in traces)
1530
 
1531
  return f"""
1532
+ <div style='border:1px solid #222;border-radius:8px;margin-bottom:18px;overflow:hidden'>
1533
+ <div style='background:#0e0e0e;padding:9px 14px;border-bottom:1px solid #1a1a1a;display:flex;align-items:center;gap:16px'>
1534
+ <span style='color:{ce_color};font-weight:700;font-size:1.05em'>{status}</span>
1535
+ <span style='color:#444;font-size:0.85em'>{record["problem"]}</span>
1536
+ <span style='color:#333;font-size:0.8em'>BaseCE={ce:.6f}</span>
1537
+ <span style='color:#555;font-size:0.75em'>{record["elapsed"]}s</span>
1538
+ <span style='color:#26C6DA;font-size:0.75em'>{record["survived_count"]} survived / {record["failed_count"]} failed</span>
1539
  </div>
1540
+ <div style='display:flex'>
1541
+ <div style='flex:0 0 180px;padding:10px 12px;border-right:1px solid #1a1a1a'>
1542
+ <div style='color:#333;font-size:0.7em;margin-bottom:5px'>BINDING</div>
1543
+ <table style='border-collapse:collapse;width:100%'>{binding_rows}</table>
 
 
 
 
 
1544
  </div>
1545
+ <div style='flex:0 0 200px;padding:10px 12px;border-right:1px solid #1a1a1a'>
1546
+ <div style='color:#333;font-size:0.7em;margin-bottom:5px'>INVARIANTS (Gate 2)</div>
1547
+ <table style='border-collapse:collapse;width:100%'>
 
 
 
1548
  {inv_rows or "<tr><td colspan='3' style='color:#222'>β€”</td></tr>"}
1549
  </table>
1550
  </div>
1551
+ <div style='flex:1;padding:10px 12px'>
1552
+ <div style='color:#333;font-size:0.7em;margin-bottom:5px'>
1553
+ HYPOTHESIS TRAIL β€” best domain: <span style='color:#FF9800'>{record["best_domain"]}</span>
 
 
 
 
1554
  </div>
1555
+ {trail}
1556
  </div>
1557
  </div>
1558
  </div>"""
1559
 
1560
  @app.get("/",response_class=HTMLResponse)
1561
  async def dashboard():
1562
+ with STATE_LOCK: log=list(reversed(RUN_LOG[-10:]))
1563
+ runs=len(RUN_LOG); solved=sum(1 for r in RUN_LOG if r.get("solved",False))
 
 
1564
  avg_ce=round(sum(r["ce"] for r in RUN_LOG)/max(1,runs),5) if RUN_LOG else 0
1565
 
 
 
 
 
1566
  all_traces=[t for r in log for t in r.get("traces",[])]
1567
+ ds=defaultdict(lambda:{"tried":0,"survived":0,"total_ce":0.0,"strategy":""})
1568
  for t in all_traces:
1569
+ ds[t["domain"]]["tried"]+=1
1570
+ ds[t["domain"]]["total_ce"]+=t["ce"]
1571
+ ds[t["domain"]]["strategy"]=t.get("strategy","")
1572
+ if t["survived"]: ds[t["domain"]]["survived"]+=1
1573
  domain_rows="".join(
1574
  f"<tr><td style='color:#FF9800'>{d}</td>"
1575
  f"<td style='color:#aaa'>{s['tried']}</td>"
1576
  f"<td style='color:#4CAF50'>{s['survived']}</td>"
1577
  f"<td style='color:#EF5350'>{s['tried']-s['survived']}</td>"
1578
+ f"<td style='color:#26C6DA'>{round(s['total_ce']/s['tried'],5) if s['tried'] else 0}</td>"
1579
+ f"<td style='color:#555'>{s['strategy']}</td>"
1580
+ f"<td style='color:{'#4CAF50' if s['survived']>0 else '#333'}'>"
1581
+ f"{round(s['survived']/s['tried']*100):.0f}%</td></tr>"
1582
+ for d,s in sorted(ds.items(),key=lambda x:-x[1]["survived"])
1583
+ ) or "<tr><td colspan='7' style='color:#222'>Warming up…</td></tr>"
1584
 
1585
+ collapses="".join(_render_collapse(r) for r in log) if log else \
1586
+ "<div style='color:#222;padding:20px'>Warming up…</div>"
1587
+
1588
+ return f"""<!DOCTYPE html><html><head><title>Practicality 11.1</title>
1589
  <meta http-equiv="refresh" content="4">
1590
  <style>
1591
  body{{background:#090909;color:#e0e0e0;font-family:monospace;padding:20px;max-width:1400px}}
1592
+ h1{{color:#26C6DA;border-bottom:1px solid #1a1a1a;padding-bottom:6px;font-size:1.15em}}
1593
+ h3{{color:#2a2a2a;margin-top:20px;font-size:0.85em;letter-spacing:2px;text-transform:uppercase}}
1594
  table{{width:100%;border-collapse:collapse;margin-bottom:10px}}
1595
+ th,td{{padding:4px 8px;text-align:left;border-bottom:1px solid #0f0f0f;font-size:0.82em}}
1596
+ th{{color:#2a2a2a;font-size:0.72em}}
1597
+ .badge{{display:inline-block;padding:3px 10px;border-radius:3px;background:#0e0e0e;margin:2px;font-size:0.78em;border:1px solid #1a1a1a}}
1598
  </style></head><body>
1599
+ <h1>βš— Practicality 11.1 β€” AXL/PSL Pipeline (fixed)</h1>
1600
+ <div style='color:#333;font-size:0.75em;margin-bottom:12px'>
1601
+ AXL β†’ PSL (base + soft domain hints) β†’ Solver [strategy-directed] β†’ Gate1 β†’ Gate2 β†’ Collapse
1602
  </div>
1603
+ <div style='margin-bottom:16px'>
1604
  <span class='badge' style='color:#aaa'>Runs: {runs}</span>
1605
  <span class='badge' style='color:#4CAF50'>Solved: {solved}</span>
1606
  <span class='badge' style='color:#26C6DA'>Avg BaseCE: {avg_ce}</span>
1607
  <span class='badge' style='color:#FF9800'>Problems: {len(AXL_PROBLEMS)}</span>
1608
  <span class='badge' style='color:#5C6BC0'>Library: {len(DEBATE_ENGINE.library)}</span>
1609
  </div>
1610
+ <h3>Domain Survival + Strategy</h3>
 
1611
  <table>
1612
+ <tr><th>Domain</th><th>Tried</th><th>Survived</th><th>Failed</th><th>Avg BaseCE</th><th>Polish</th><th>Rate</th></tr>
1613
  {domain_rows}
1614
  </table>
1615
+ <h3>Collapse View β€” Recent Runs</h3>
 
1616
  {collapses}
1617
  </body></html>"""
1618