Files
timmy-home/experiments/template.py
Hermes Agent 56763740d0
Some checks failed
Self-Healing Smoke / self-healing-smoke (pull_request) Failing after 16s
Agent PR Gate / gate (pull_request) Failing after 33s
Smoke Test / smoke (pull_request) Failing after 16s
Agent PR Gate / report (pull_request) Successful in 21s
feat(MATH-003): add reproducible computation lane — experiments/
Create a repo-local structure for deterministic computational math
experiments with provenance manifests, result hashing, and clear
limitations on what computation can prove vs. suggest.

Additions:
- experiments/README.md: Philosophy, directory structure, manifest schema,
  graduation criteria (experiment→proof), and Sage compatibility notes
- experiments/MANIFEST_SCHEMA.md: JSON schema for experiment result manifests
  (fields: code_hash, output_hash, assumptions, timestamp, runtime, etc.)
- experiments/template.py: Complete starter template with:
    * deterministic seed handling
    * self-hashing (code_hash, output_hash via SHA256)
    * manifest generation boilerplate
    * markdown report writer
    * CLI args placeholder for parameter sweeps
- experiments/riemann_pii.py: Working toy experiment — Riemann sum π approx
  Demonstrates full pattern:
    * n=10000 rectangles → π ≈ 3.141391 (error ~2e-4)
    * produces manifest + markdown report
    * classifies as "suggests" (numerical approximation, not proof)
- experiments/fibonacci_cassini.py: Second toy experiment — Cassini identity
  Verifies F_{n-1}F_{n+1} - F_n² = (-1)^n for n=1..1000
    * integer arithmetic — exact result
    * still "suggests" (finite check, not inductive proof)
    * high confidence due to exact arithmetic
- experiments/.gitignore: ignores manifests/, results/, and *.manifest.json
  so generated artifacts are never committed

Verification:
$ python experiments/riemann_pii.py
  → writes experiments/manifests/riemann_pii.manifest.json + report.md
$ python experiments/fibonacci_cassini.py
  → writes experiments/manifests/fibonacci_cassini.manifest.json + report.md

Both runs are reproducible: code_hash and output_hash match across runs.

Design rationale:
- Minimal dependency: pure Python 3.10+ (no Sage yet; optional future)
- Self-contained: each experiment is a single script
- Deterministic: no randomness unless fixed seed set
- Hash-based integrity: code_hash verifies exact source; output_hash verifies result
- Manifest captures all required metadata per acceptance criteria
- README explicitly explains computational limits: numerical cannot replace
  analytical proof; bounded by assumptions and precision

Closes #879
2026-04-26 14:25:39 -04:00

165 lines
4.7 KiB
Python

#!/usr/bin/env python3
"""
[EXPERIMENT NAME HERE]
Brief description of the mathematical problem and its source.
Problem source: [URL or citation]
Assumptions:
- [Assumption 1]
- [Assumption 2]
"""
from __future__ import annotations
import argparse
import hashlib
import json
import time
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
# ============================================================
# CONFIGURATION
# ============================================================
EXPERIMENT_NAME = "experiment_name" # matches filename
PROBLEM_SOURCE = "https://example.com/problem" # where this problem originates
ASSUMPTIONS = [
"Assumption 1",
"Assumption 2",
]
SUGGESTS_OR_PROVES = "suggests" # or "proves" — see README limitations
CONFIDENCE = "medium" # high | medium | low
# Output directories
OUTPUT_DIR = Path(__file__).parent
MANIFEST_DIR = OUTPUT_DIR / "manifests"
RESULT_DIR = OUTPUT_DIR / "results"
MANIFEST_DIR.mkdir(parents=True, exist_ok=True)
RESULT_DIR.mkdir(parents=True, exist_ok=True)
# ============================================================
# COMPUTATION
# ============================================================
def compute() -> dict[str, Any]:
"""Run the core computation.
Returns:
dict: result data (must be JSON-serializable)
"""
# Set deterministic seed if using randomness
# import random; random.seed(42)
# Your computation here
result_value = None
raise NotImplementedError("Fill in compute()")
return {
"value": result_value,
# add other structured fields as needed
}
# ============================================================
# HASHING
# ============================================================
def hash_file(path: Path) -> str:
"""Return sha256:<hex> of file contents."""
data = path.read_bytes()
return "sha256:" + hashlib.sha256(data).hexdigest()
def hash_json_serializable(obj: Any) -> str:
"""Return sha256:<hex> of JSON-serialized object (sorted keys)."""
serialized = json.dumps(obj, sort_keys=True, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
return "sha256:" + hashlib.sha256(serialized).hexdigest()
# ============================================================
# REPORT WRITING
# ============================================================
def write_markdown_report(result: dict[str, Any], manifest: dict[str, Any]) -> None:
"""Write human-readable computation report to results/."""
report_path = RESULT_DIR / f"{EXPERIMENT_NAME}.report.md"
lines = [
f"# Experiment: {EXPERIMENT_NAME}",
"",
"## Problem Source",
f"- {PROBLEM_SOURCE}",
"",
"## Assumptions",
]
for a in ASSUMPTIONS:
lines.append(f"- {a}")
lines.extend([
"",
"## Computation",
f"- **Regime:** {manifest['suggests_or_proves']}",
f"- **Confidence:** {manifest['confidence']}",
f"- **Runtime:** {manifest['runtime_seconds']:.4f} s",
"",
"## Result",
])
# Pretty-print result
lines.append("```json")
lines.append(json.dumps(result, indent=2))
lines.append("```")
lines.extend([
"",
"## Provenance",
f"- Code hash: `{manifest['code_hash']}`",
f"- Output hash: `{manifest['output_hash']}`",
f"- Timestamp (UTC): `{manifest['timestamp_utc']}`",
])
report_path.write_text("\n".join(lines) + "\n")
print(f"Report: {report_path}")
# ============================================================
# MAIN
# ============================================================
def main() -> None:
start = time.perf_counter()
# Run computation
result = compute()
# Hashes
script_path = Path(__file__)
code_hash = hash_file(script_path)
output_hash = hash_json_serializable(result)
# Build manifest
runtime = time.perf_counter() - start
manifest = {
"experiment_name": EXPERIMENT_NAME,
"problem_source": PROBLEM_SOURCE,
"assumptions": ASSUMPTIONS,
"code_hash": code_hash,
"output_hash": output_hash,
"timestamp_utc": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
"runtime_seconds": round(runtime, 6),
"result": result,
"suggests_or_proves": SUGGESTS_OR_PROVES,
"confidence": CONFIDENCE,
}
# Write manifest
manifest_path = MANIFEST_DIR / f"{EXPERIMENT_NAME}.manifest.json"
manifest_path.write_text(json.dumps(manifest, indent=2, ensure_ascii=False) + "\n")
print(f"Manifest: {manifest_path}")
# Write report
write_markdown_report(result, manifest)
print("\nDone.")
if __name__ == "__main__":
main()