Research-Stack/4-Infrastructure/shim/ingest_flexure_joints.py
allaun 1f7ec15f12 feat(lean): add logarithmic viscosity coordinates to NKHodgeFAMM
Adds logViscosityRatio, log_viscosity_monotone, and ν_eff_monotone
to Semantics/NKHodgeFAMM.lean section 6b. The adaptive viscosity law
ν_eff = ν₀*(1+μ) is multiplicative in ν₀ and additive in scar density μ;
taking λ = log(ν_eff/ν₀) = log(1+μ) turns the multiplicative feedback into
an additive coordinate. This gives nlinarith a direct handle on viscosity
monotonicity and connects the module to Kritchevsky's "Everything Is
Logarithms" framing (SilverSight CITATION.cff).

Also marks a few pre-existing unused variables with underscores to silence
the linter.

Build: 8316 jobs, 0 errors (lake build Semantics.NKHodgeFAMM)
2026-06-22 01:21:58 -05:00

337 lines
13 KiB
Python

#!/usr/bin/env python3
# INFRA:DEAD rds -- AWS RDS is gone. Any file referencing rds_connect.py or this hostname is stale and must be ported.
"""Ingest Tier 2B trace flexure joints into ene.flexures.
For each theorem trace, extracts per-step transitions as flexure joints,
inserts them into ene.flexures, and mines recurring patterns.
"""
import glob
import hashlib
import json
import math
import os
import subprocess
import sys
import uuid
from collections import Counter, defaultdict
from pathlib import Path
TRACES_DIR = os.path.join(os.path.dirname(__file__), "../..", "shared-data/proof_traces")
REPORT_PATH = os.path.join(os.path.dirname(__file__), "../..", "shared-data/pist_flexure_library_report.json")
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "."))
from trace_canary_theorems import CANARY_THEOREMS
def power_iteration(matrix, max_iter=100):
n = len(matrix)
if n == 0: return 0.0, [0.0]
v = [1.0 / math.sqrt(n)] * n
for _ in range(max_iter):
vn = [sum(matrix[i][j] * v[j] for j in range(n)) for i in range(n)]
nm = math.sqrt(sum(x*x for x in vn))
if nm < 1e-12: return 0.0, v
v = [x / nm for x in vn]
num = sum(v[i] * sum(matrix[i][j] * v[j] for j in range(n)) for i in range(n))
den = sum(v[i]*v[i] for i in range(n))
return num / den if den > 0 else 0.0, v
def symmetrize(matrix):
n = len(matrix)
return [[(matrix[i][j] + matrix[j][i]) / 2.0 for j in range(n)] for i in range(n)]
def build_laplacian(sym):
n = len(sym)
lap = [[0.0]*n for _ in range(n)]
for i in range(n):
d = sum(sym[i])
for j in range(n):
lap[i][j] = d if i == j else -sym[i][j]
return lap
def compute_spectral(matrix):
if not matrix or len(matrix) == 0: return {}
n = len(matrix)
sym = symmetrize(matrix)
lap_mat = build_laplacian(sym)
ev_max, _ = power_iteration(sym)
shifted = [[sym[i][j] - 0.9*ev_max*(1 if i==j else 0) for j in range(n)] for i in range(n)]
ev_shift, _ = power_iteration(shifted)
ev_second = max(0, ev_max - ev_shift) if ev_shift < ev_max else ev_max
gap = ev_max - ev_second
lap_max, _ = power_iteration(lap_mat)
neg_lap = [[-lap_mat[i][j] for j in range(n)] for i in range(n)]
neg_max, _ = power_iteration(neg_lap)
lap_min = -neg_max
ata = [[sum(matrix[k][i]*matrix[k][j] for k in range(n)) for j in range(n)] for i in range(n)]
sv_max, _ = power_iteration(ata)
rank = sum(1 for row in matrix if sum(row) > 0)
total = sum(sum(row) for row in matrix)
frob = math.sqrt(sum(cell*cell for row in matrix for cell in row))
lap_zero = sum(1 for i in range(n) if abs(sum(matrix[i]) - matrix[i][i]) < 1e-9)
return {
"matrix_size": n, "rank": rank, "density": round(total/max(n*n,1), 6),
"spectral_gap": round(gap, 6), "frobenius_norm": round(frob, 6),
"adjacency_eigenvalue_max": round(ev_max, 6),
"adjacency_eigenvalue_second": round(ev_second, 6),
"laplacian_eigenvalue_max": round(lap_max, 6),
"laplacian_eigenvalue_min": round(lap_min, 6),
"laplacian_zero_count": lap_zero,
"singular_value_max": round(math.sqrt(max(0, sv_max)), 6),
"trace": sum(matrix[i][i] for i in range(n)),
}
TACTIC_FAMILIES = {
"rw": "rewrite", "simp": "normalization", "omega": "arithmetic",
"induction": "induction", "ring": "algebraic", "cases": "case_analysis",
"constructor": "constructor", "apply": "discharge", "exact": "discharge",
"intro": "introduction", "have": "lemma_introduction",
"calc": "calculation", "rfl": "reflexivity",
"nlinarith": "algebraic", "native_decide": "arithmetic",
}
def classify_tactic(tactic: str) -> str:
tl = tactic.lower()
for key, val in TACTIC_FAMILIES.items():
if key in tl:
return val
return "unknown"
def build_joint_label(tactic: str, delta_score: float, status: str) -> str:
"""Derive a joint label from the tactic and delta."""
fam = classify_tactic(tactic)
if status == "failed":
return f"failed_{fam}"
if delta_score > 5:
return f"high_delta_{fam}"
elif delta_score > 2:
return f"med_delta_{fam}"
else:
return f"low_delta_{fam}"
def get_theorem_labels(name: str) -> dict:
"""Look up ground-truth labels and tactic text from the theorem definitions."""
for t in CANARY_THEOREMS:
if t["name"] == name:
code = t.get("code", "")
# Extract tactics from the by-block
tactics = []
by_start = code.find(":= by")
if by_start >= 0:
body = code[by_start + 6:].strip()
else:
body = code
for line in body.split("\n"):
stripped = line.strip()
if stripped and not stripped.startswith("--"):
tactics.append(stripped)
return {
"domain": t.get("domain", "?"),
"proof_method": t.get("proof_method", "?"),
"joint": t.get("joint", "?"),
"obstruction": t.get("obstruction"),
"rrc_shape": t.get("rrc_shape", "?"),
"tactics": tactics,
}
return {"domain": "?", "proof_method": "?", "joint": "?", "obstruction": None,
"rrc_shape": "?", "tactics": []}
def ingest_flexures():
"""Main ingestion pass."""
trace_files = sorted(glob.glob(os.path.join(TRACES_DIR, "v2_canary_*.json")))
if not trace_files:
print("No trace files found", flush=True)
return
print(f"Found {len(trace_files)} trace files", flush=True)
# Connect using purified helper
from rds_connect import connect_rds
conn = connect_rds()
cur = conn.cursor()
# Create a session for this batch
session_id = str(uuid.uuid4())
cur.execute(
"INSERT INTO ene.sessions (id, title, event_type, content, metadata) "
"VALUES (%s, %s, 'flexure_ingest', 'Tier 2B flexure joint batch ingestion', %s::jsonb)",
(session_id, "Tier 2B Flexure Ingestion",
json.dumps({"source": "lean_trace_v2_canary", "trace_count": len(trace_files)})),
)
all_joints = []
total_inserted = 0
total_skipped = 0
for fpath in trace_files:
with open(fpath) as f:
trace = json.load(f)
name = trace.get("name", Path(fpath).stem)
status = trace.get("status", "?")
tags = trace.get("trace_tags", [])
matrix = trace.get("transition_matrix", [])
n_unique = trace.get("n_unique", 0)
labels = get_theorem_labels(name)
tactics = labels.get("tactics", [])
# Compute full spectral features from the transition matrix
spectral = compute_spectral(matrix)
# Build flexure joints from tag pairs
joints = []
for i in range(0, len(tags) - 1, 2):
if i + 1 >= len(tags):
break
step = i // 2
tactic = tactics[step] if step < len(tactics) else f"step_{step}"
tactic_family = classify_tactic(tactic)
bh = hashlib.sha256(tags[i].encode()).hexdigest()[:16]
ah = hashlib.sha256(tags[i+1].encode()).hexdigest()[:16]
delta_score = abs(int(bh[:4], 16) - int(ah[:4], 16)) % 10
joint_label = build_joint_label(tactic, delta_score, status)
joint = {
"step": step,
"tactic": tactic,
"tactic_family": tactic_family,
"before_hash": bh,
"after_hash": ah,
"delta_score": delta_score,
"joint_label": joint_label,
"domain": labels["domain"],
"proof_method": labels["proof_method"],
"rrc_shape": labels["rrc_shape"],
"obstruction": labels.get("obstruction"),
"matrix_rank": max(0, n_unique - 1),
"n_unique": n_unique,
"status": status,
"spectral": spectral, # full spectral profile
}
joints.append(joint)
# Insert joints
for j in joints:
flex_id = str(uuid.uuid4())
signals = json.dumps({
"tactic": j["tactic"],
"tactic_family": j["tactic_family"],
"delta_score": j["delta_score"],
"joint_label": j["joint_label"],
"domain": j["domain"],
"proof_method": j["proof_method"],
"rrc_shape": j["rrc_shape"],
"obstruction": j.get("obstruction"),
"matrix_rank": j["matrix_rank"],
"n_unique_states": j["n_unique"],
"spectral": j.get("spectral", {}),
"feature_version": "flexure-spectrum-v2",
})
chosen = json.dumps({"tactic_applied": j["tactic"], "joint_type": j["joint_label"]})
available = json.dumps([{"step": j["step"], "tactic_family": j["tactic_family"]}])
try:
cur.execute(
"""INSERT INTO ene.flexures
(id, session_id, step_index, pre_sidon_label, pre_residual,
available_crossings, chosen_crossing, decision_signals,
post_sidon_label, post_residual, converged)
VALUES (%s, %s, %s, %s, %s, %s::jsonb, %s::jsonb, %s::jsonb,
%s, %s, %s)""",
(flex_id, session_id, j["step"],
int(j["before_hash"][:4], 16) % 255, # hash as Sidon-like label
1.0 - j["delta_score"] / 10.0, # residual = 1 - normalized delta
available, chosen, signals,
int(j["after_hash"][:4], 16) % 255,
j["delta_score"] / 10.0,
j["status"] == "verified"),
)
total_inserted += 1
except Exception as e:
total_skipped += 1
all_joints.append(j)
print(f" {name:30s}: {len(joints)} joints", flush=True)
conn.commit()
# ── Motif extraction ──
print(f"\nInserted: {total_inserted} flexures, Skipped: {total_skipped}", flush=True)
# Extract recurring joint patterns from all_joints
motif_counter = Counter()
for j in all_joints:
motif = (j["tactic_family"], j["joint_label"], j["status"])
motif_counter[motif] += 1
print(f"\nRecurring joint motifs (frequency ≥ 2):", flush=True)
motif_count = 0
for (fam, jl, status), count in sorted(motif_counter.items(), key=lambda x: -x[1]):
if count >= 2:
print(f" {fam:20s} {jl:30s} {status:10s}: {count:3d}x", flush=True)
# Insert into flexure_patterns
pat_id = str(uuid.uuid4())
sig = hashlib.sha256(f"{fam}_{jl}_{status}".encode()).hexdigest()[:16]
cur.execute(
"""INSERT INTO ene.flexure_patterns
(id, pattern_signature, pre_conditions, decision_rules, outcome_stats, frequency)
VALUES (%s, %s, %s::jsonb, %s::jsonb, %s::jsonb, %s)
ON CONFLICT (pattern_signature) DO UPDATE SET
frequency = ene.flexure_patterns.frequency + %s,
last_seen = now()""",
(pat_id, sig,
json.dumps({"tactic_family": fam}),
json.dumps({"joint_type": jl}),
json.dumps({"status": status, "count": count}),
count, count),
)
motif_count += 1
conn.commit()
print(f"Patterns inserted: {motif_count}", flush=True)
# ── Summary ──
print(f"\nFlexure joint library summary:", flush=True)
statuses = Counter(j["status"] for j in all_joints)
print(f" Total joints: {len(all_joints)}", flush=True)
print(f" Status: {dict(statuses)}", flush=True)
families = Counter(j["tactic_family"] for j in all_joints)
print(f" Families: {dict(families.most_common(5))}", flush=True)
joint_labels = Counter(j["joint_label"] for j in all_joints)
print(f" Joint labels: {dict(joint_labels.most_common(5))}", flush=True)
report = {
"session_id": session_id,
"total_flexures_inserted": total_inserted,
"total_skipped": total_skipped,
"motifs_discovered": motif_count,
"joint_library_summary": {
"total_joints": len(all_joints),
"status": dict(statuses),
"tactic_families": dict(families.most_common(5)),
"joint_labels": dict(joint_labels.most_common(5)),
"motifs": {f"{fam}_{jl}_{st}": cnt for (fam, jl, st), cnt in
sorted(motif_counter.items(), key=lambda x: -x[1])},
},
}
with open(REPORT_PATH, "w") as f:
json.dump(report, f, indent=2)
print(f"\nReport: {REPORT_PATH}", flush=True)
cur.close()
conn.close()
print(f"\nSession ID: {session_id} (use with flexure_analyze)", flush=True)
if __name__ == "__main__":
ingest_flexures()