feat: AlphaProof batch mode with copy-if pre-filter

New CLI mode: python alphaproof_loop.py --batch <lean_dir>

Pipeline:
1. Pre-filter scans Lean codebase (11,434 theorems)
2. Classifies trivial (63.5%) vs non-trivial (36.5%)
3. Prioritizes non-trivial by tactic count + sorry weight
4. Feeds hardest problems to Ollama LLM first
5. Skips trivial theorems entirely (zero deltas)

Usage:
  python alphaproof_loop.py --batch ../../0-Core-Formalism/lean/Semantics/Semantics
  python alphaproof_loop.py --batch . --max-iter 20 --model deepseek-coder-v2:16b

Same pattern as vectorized copy_if:
  - Skip zero deltas → 40x faster (blog post)
  - Skip trivial theorems → 2.7x faster (pre-filter)
  - Focus solver on residuals → fewer iterations
This commit is contained in:
Brandon Schneider 2026-05-29 02:42:04 -05:00
parent 42b5f7ea4f
commit 48eb892f49

View file

@ -16,7 +16,7 @@ import tempfile
import time import time
from datetime import datetime, timezone from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Optional, List, Dict
try: try:
import requests import requests
@ -24,6 +24,13 @@ try:
except ImportError: except ImportError:
HAS_REQUESTS = False HAS_REQUESTS = False
# Import pre-filter for copy-if pattern
try:
from lean_proof_prefilter import analyze_file, classify_theorem, extract_theorems
HAS_PREFILTER = True
except ImportError:
HAS_PREFILTER = False
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Configuration # Configuration
@ -260,6 +267,138 @@ def fpga_accelerate(candidates: list[dict]) -> list[dict]:
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Proof prioritization (copy-if pre-filter)
# ---------------------------------------------------------------------------
def prioritize_problems(lean_dir: str, max_problems: int = 20) -> List[Dict]:
"""Pre-filter and prioritize proof problems using the copy-if pattern.
Scans the Lean codebase, classifies theorems as trivial/non-trivial,
and returns the highest-priority non-trivial problems for the LLM.
This is the Lean equivalent of the vectorized copy_if:
- Trivial theorems (zero deltas) = skip
- Non-trivial theorems (non-zero deltas) = feed to solver
Args:
lean_dir: Path to Lean source directory.
max_problems: Maximum number of problems to return.
Returns:
List of prioritized problem dicts with file, theorem, and priority.
"""
if not HAS_PREFILTER:
print("[alphaproof] pre-filter not available, using default order")
return []
problems = []
lean_path = Path(lean_dir)
for lean_file in sorted(lean_path.rglob('*.lean')):
if '.lake' in str(lean_file):
continue
try:
analysis = analyze_file(str(lean_file))
for thm in analysis['theorems']:
if thm['classification'] == 'non_trivial':
# Priority: more tactics = harder = higher priority
tactic_count = len(thm.get('category', '').split(','))
has_sorry = 'sorry' in thm.get('category', '')
priority = tactic_count + (10 if has_sorry else 0)
problems.append({
'file': str(lean_file),
'theorem': thm['name'],
'kind': thm['kind'],
'line': thm['line'],
'category': thm['category'],
'priority': priority,
'block': thm['block'],
})
except Exception:
continue
# Sort by priority (highest first = hardest problems first)
problems.sort(key=lambda p: p['priority'], reverse=True)
# Deduplicate by theorem name
seen = set()
unique = []
for p in problems:
if p['theorem'] not in seen:
seen.add(p['theorem'])
unique.append(p)
return unique[:max_problems]
def batch_proof_search(lean_dir: str, max_iterations: int = 10,
model: str = DEFAULT_MODEL,
lake_project: Optional[str] = None) -> Dict:
"""Run AlphaProof on prioritized non-trivial theorems.
Uses the copy-if pre-filter to skip trivial theorems and focus
the LLM solver on the hardest problems first.
"""
problems = prioritize_problems(lean_dir)
if not problems:
print("[alphaproof] no non-trivial problems found")
return {'problems': [], 'results': []}
print(f"[alphaproof] found {len(problems)} non-trivial problems")
print(f"[alphaproof] top 5: {[p['theorem'] for p in problems[:5]]}")
results = []
for i, problem in enumerate(problems):
print(f"\n[alphaproof] [{i+1}/{len(problems)}] {problem['theorem']}"
f" (priority={problem['priority']}, category={problem['category']})")
# Build prompt from the theorem block
prompt = (
f"Prove this Lean 4 theorem. The file is {problem['file']}, "
f"line {problem['line']}.\n\n"
f"```lean\n{problem['block']}\n```\n\n"
f"Replace the sorry with a complete proof. "
f"Use omega, simp, decide, norm_num, ring, or linarith as appropriate."
)
result = search_loop(
problem=prompt,
max_iterations=max_iterations,
model=model,
lake_project=lake_project,
)
results.append({
'theorem': problem['theorem'],
'file': problem['file'],
'priority': problem['priority'],
'success': result['success'],
'iterations': result['iterations'],
'solution': result.get('solution', ''),
})
if result['success']:
print(f"[alphaproof] SOLVED {problem['theorem']} "
f"in {result['iterations']} iterations")
else:
print(f"[alphaproof] FAILED {problem['theorem']} "
f"after {result['iterations']} iterations")
# Summary
solved = sum(1 for r in results if r['success'])
print(f"\n[alphaproof] summary: {solved}/{len(results)} solved")
return {
'problems': problems,
'results': results,
'solved': solved,
'total': len(results),
}
# Main search loop # Main search loop
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@ -372,9 +511,33 @@ if __name__ == '__main__':
if len(sys.argv) < 2: if len(sys.argv) < 2:
print("Usage: python alphaproof_loop.py <problem_file_or_text> " print("Usage: python alphaproof_loop.py <problem_file_or_text> "
"[--max-iter N] [--model MODEL]") "[--max-iter N] [--model MODEL]")
print(" python alphaproof_loop.py --batch <lean_dir> "
"[--max-iter N] [--model MODEL]")
sys.exit(1) sys.exit(1)
problem_arg = sys.argv[1] problem_arg = sys.argv[1]
# Batch mode: run pre-filter on Lean codebase
if problem_arg == '--batch':
lean_dir = sys.argv[2] if len(sys.argv) > 2 else '.'
max_iter = 10
model_name = DEFAULT_MODEL
for i, arg in enumerate(sys.argv[3:], 3):
if arg == '--max-iter' and i + 1 < len(sys.argv):
max_iter = int(sys.argv[i + 1])
elif arg == '--model' and i + 1 < len(sys.argv):
model_name = sys.argv[i + 1]
result = batch_proof_search(lean_dir, max_iterations=max_iter,
model=model_name)
print(f"\n{'='*60}")
print(f"Solved: {result['solved']}/{result['total']}")
print(f"{'='*60}")
sys.exit(0 if result['solved'] > 0 else 1)
# Single problem mode
if os.path.isfile(problem_arg): if os.path.isfile(problem_arg):
problem_text = Path(problem_arg).read_text() problem_text = Path(problem_arg).read_text()
else: else: