Research-Stack/5-Applications/scripts/hutter_compression_shim.py
allaun 00e9eed399 fix(lean): complete projectionOrdering proof in GeometricCompressionWorkspace
Replace the TODO(lean-port) sorry with a complete proof of the
projectionOrdering theorem: for positive SourceValue pairs s1 < s2
with s2 ≤ maxExpected, projectToCoding preserves strict ordering
of the Q0_64 values.

The proof uses Nat-only arithmetic (no Float) and handles two cases:
  - a2 < d: both values fit in Q0_64 range, ordering follows from
    monotonicity of integer division
  - a2 = d: a2*s/d = s clamped to q0_64MaxRaw; a1*s/d < q0_64MaxRaw
    via the key inequality (d-1)*s < (s-1)*d

Build: 8598 jobs, 0 errors (lake build)
2026-06-18 15:06:50 -05:00

349 lines
12 KiB
Python

#!/usr/bin/env python3
"""
Hutter Prize Compression Shim
Python shim for file I/O and benchmarking of the Lean Hutter Maximum Compression module.
Per AGENTS.md rules:
- This shim only handles file I/O, JSON serialization, and benchmarking
- Core compression logic lives in Lean (Semantics.HutterMaximumCompression)
- No compression decisions or cost calculations in Python
Usage:
python 5-Applications/scripts/hutter_compression_shim.py input.txt output.bin
"""
import sys
import json
import time
import subprocess
from pathlib import Path
from typing import Optional
# ─── BindServer binary discovery ─────────────────────────────────────────────
# The Lean bindserver exe is declared in lakefile.toml as:
# [[lean_exe]] name = "bindserver" root = "BindServer"
# Built with: cd 0-Core-Formalism/lean/Semantics && lake build bindserver
# Binary lands at: .lake/build/bin/bindserver
_REPO_ROOT = Path(__file__).resolve().parents[3]
_SEMANTICS_DIR = _REPO_ROOT / "0-Core-Formalism" / "lean" / "Semantics"
def _find_bindserver_binary() -> Optional[Path]:
"""Locate the compiled Lean bindserver binary.
Search order:
1. Canonical Semantics tree (.lake/build/bin/bindserver)
2. Legacy tools/lean/Semantics path (used by bind_engine.py, server.js)
3. CWD-relative fallback
"""
candidates = [
_SEMANTICS_DIR / ".lake" / "build" / "bin" / "bindserver",
_REPO_ROOT / "tools" / "lean" / "Semantics" / ".lake" / "build" / "bin" / "bindserver",
Path.cwd() / "0-Core-Formalism" / "lean" / "Semantics" / ".lake" / "build" / "bin" / "bindserver",
]
for c in candidates:
if c.exists() and c.is_file():
return c
return None
def _build_bindserver_request(metrics: dict) -> dict:
"""Construct a BindServer JSON-lines request for Hutter compression verification.
Uses the "informational" metric kind (KL-divergence cost), the natural
information-theoretic bound for compression lawfulness.
BindServer.handleInformational checks lawfulness as JSON structural equality
(invL == invR via json.compress). We exploit this by sending:
left = actual compression metrics
right = lawful reference (emittedSymbols forced to lawfulSymbols)
So lawful=True iff no unlawful drift (emittedSymbols == lawfulSymbols).
The KL-divergence cost quantifies the information-theoretic distance.
"""
left = {
"totalPositions": float(metrics["totalPositions"]),
"emittedSymbols": float(metrics["emittedSymbols"]),
"lawfulSymbols": float(metrics["lawfulSymbols"]),
"totalCost": float(metrics["totalCost"]),
"compressionRatio": float(metrics["compressionRatio"]),
}
right = {
"totalPositions": float(metrics["totalPositions"]),
"emittedSymbols": float(metrics["lawfulSymbols"]),
"lawfulSymbols": float(metrics["lawfulSymbols"]),
"totalCost": float(metrics["totalCost"]),
"compressionRatio": float(metrics["compressionRatio"]),
}
return {
"metricKind": "informational",
"left": left,
"right": right,
"useHistory": False,
"historyLen": 0,
"historyCost": 0,
"historyTorsion": 0,
}
def _unverified_bind_result(metrics: dict, reason: str) -> dict:
"""Return a clearly-marked unverified result when bindserver is unavailable.
Per AGENTS.md programming-choice flow: no Python-side lawfulness computation
is permitted. lawful=False + trace_hash="error:..." signals the gap.
cost=0xFFFFFFFF (max Q16_16) marks the result as formally unbounded.
"""
return {
"left": metrics,
"right": "hutter_compressed_output",
"metric": {
"cost": 0xFFFFFFFF,
"tensor": "informational",
"torsion": 0x00000000,
"reference": "hutter_maximum_compression",
"history_len": 0,
},
"cost": 0xFFFFFFFF,
"witness": {
"left_invariant": metrics.get("invariant", "unknown"),
"right_invariant": "unverified",
"conserved": False,
"trace_hash": f"error:{reason}",
},
"lawful": False,
"error": reason,
}
def read_input_file(filepath: str) -> bytes:
"""Read input file (enwik8, enwik9, or test data)."""
with open(filepath, 'rb') as f:
return f.read()
def write_output_file(filepath: str, data: bytes) -> None:
"""Write compressed output file."""
with open(filepath, 'wb') as f:
f.write(data)
def call_lean_bindserver(metrics: dict) -> dict:
"""
Call Lean BindServer to get compression bind result.
Protocol: JSON-lines over stdin/stdout with the compiled `bindserver` exe
(defined in 0-Core-Formalism/lean/Semantics/BindServer.lean).
Request (BindRequest): metricKind, left, right, useHistory, historyLen,
historyCost, historyTorsion
Response (BindResponse): cost, lawful, leftInvariant, rightInvariant,
traceHash, metricTensor, metricTorsion,
metricHistoryLen
Error: {"error": "..."}
The server reads one JSON line, writes one JSON response line, then loops.
On EOF (stdin closed) it exits cleanly, so subprocess.run with input=
works for one-shot calls.
Per AGENTS.md: Python owns only I/O; Lean owns the lawfulness decision.
If bindserver is unavailable, returns an unverified result (lawful=False)
rather than computing lawfulness in Python.
"""
binary = _find_bindserver_binary()
if binary is None:
return _unverified_bind_result(
metrics,
"bindserver binary not found — run: cd 0-Core-Formalism/lean/Semantics && lake build bindserver",
)
request = _build_bindserver_request(metrics)
request_line = json.dumps(request, separators=(",", ":")) + "\n"
try:
proc = subprocess.run(
[str(binary)],
input=request_line,
capture_output=True,
text=True,
timeout=30,
)
except subprocess.TimeoutExpired:
return _unverified_bind_result(metrics, "bindserver timed out (30s)")
except Exception as e:
return _unverified_bind_result(metrics, f"bindserver spawn failed: {e}")
resp_line = proc.stdout.strip()
if not resp_line:
stderr_snippet = proc.stderr.strip()[:200] if proc.stderr else ""
return _unverified_bind_result(
metrics,
f"bindserver empty stdout (stderr={stderr_snippet})",
)
try:
resp = json.loads(resp_line)
except json.JSONDecodeError as e:
return _unverified_bind_result(metrics, f"bindserver invalid JSON: {e}")
if "error" in resp:
return _unverified_bind_result(metrics, f"bindserver error: {resp['error']}")
# Map BindResponse → shim-expected bind_result format
return {
"left": metrics,
"right": "hutter_compressed_output",
"metric": {
"cost": resp["cost"],
"tensor": resp["metricTensor"],
"torsion": resp["metricTorsion"],
"reference": "hutter_maximum_compression",
"history_len": resp["metricHistoryLen"],
},
"cost": resp["cost"],
"witness": {
"left_invariant": resp["leftInvariant"],
"right_invariant": resp["rightInvariant"],
"conserved": resp["lawful"],
"trace_hash": resp["traceHash"],
},
"lawful": resp["lawful"],
}
def simulate_lean_compression(input_data: bytes) -> dict:
"""
Simulate Lean compression pipeline for benchmarking.
This is a placeholder that demonstrates the expected interface.
Real implementation would call the actual Lean functions via BindServer.
Returns:
Dictionary with compression metrics
"""
total_positions = len(input_data)
# Mock compression: emit symbols at square positions (demonstration)
emitted_symbols = 0
lawful_symbols = 0
total_cost = 0
for pos in range(total_positions):
k = int(pos ** 0.5)
a = pos - k*k
is_square = (a == 0)
# Simplified emission rule: emit at squares
if is_square:
emitted_symbols += 1
lawful_symbols += 1
total_cost += 0x00000100 # Minimal cost
compression_ratio = emitted_symbols / total_positions if total_positions > 0 else 0
metrics = {
"totalPositions": total_positions,
"emittedSymbols": emitted_symbols,
"lawfulSymbols": lawful_symbols,
"totalCost": total_cost,
"avgCost": total_cost / emitted_symbols if emitted_symbols > 0 else 0,
"compressionRatio": compression_ratio,
"invariant": "lawful_hutter_compression" if compression_ratio > 0.618 else "unlawful_hutter_drift"
}
return metrics
def benchmark_compression(input_file: str, output_file: str) -> dict:
"""
Run compression benchmark.
Args:
input_file: Path to input file (enwik8, enwik9, etc.)
output_file: Path for compressed output
Returns:
Benchmark results dictionary
"""
print(f"Reading input: {input_file}")
input_data = read_input_file(input_file)
input_size = len(input_data)
print("Running Lean compression pipeline...")
start_time = time.time()
# Get compression metrics from Lean (simulated for now)
metrics = simulate_lean_compression(input_data)
# Call Lean BindServer for formal verification
bind_result = call_lean_bindserver(metrics)
compression_time = time.time() - start_time
# Write compressed output (placeholder)
output_data = json.dumps({
"metrics": metrics,
"bind": bind_result
}).encode('utf-8')
write_output_file(output_file, output_data)
output_size = len(output_data)
# Calculate benchmark results
results = {
"input_file": input_file,
"output_file": output_file,
"input_size": input_size,
"output_size": output_size,
"compression_ratio": metrics["compressionRatio"],
"compression_time": compression_time,
"throughput_mbps": (input_size / (1024 * 1024)) / compression_time if compression_time > 0 else 0,
"metrics": metrics,
"bind_result": bind_result,
"lawful": bind_result["lawful"]
}
return results
def main():
if len(sys.argv) != 3:
print("Usage: python hutter_compression_shim.py <input_file> <output_file>")
sys.exit(1)
input_file = sys.argv[1]
output_file = sys.argv[2]
if not Path(input_file).exists():
print(f"Error: Input file not found: {input_file}")
sys.exit(1)
print("=== Hutter Prize Maximum Compression Benchmark ===")
print()
results = benchmark_compression(input_file, output_file)
print()
print("=== Benchmark Results ===")
print(f"Input: {results['input_file']} ({results['input_size']:,} bytes)")
print(f"Output: {results['output_file']} ({results['output_size']:,} bytes)")
print(f"Compression Ratio: {results['compression_ratio']:.6f}")
print(f"Compression Time: {results['compression_time']:.3f}s")
print(f"Throughput: {results['throughput_mbps']:.2f} MB/s")
print(f"Lawful: {results['lawful']}")
print()
print(f"Emitted Symbols: {results['metrics']['emittedSymbols']:,} / {results['metrics']['totalPositions']:,}")
print(f"Lawful Symbols: {results['metrics']['lawfulSymbols']:,}")
print(f"Total Cost: {results['metrics']['totalCost']:,}")
print(f"Invariant: {results['metrics']['invariant']}")
# Save results to JSON
results_file = output_file + ".json"
with open(results_file, 'w') as f:
json.dump(results, f, indent=2)
print()
print(f"Results saved to: {results_file}")
if __name__ == "__main__":
main()