feat: check Sym.simp thresholds (#11890)

This PR ensures that `Sym.simp` checks thresholds for maximum recursion
depth and maximum number of steps. It also invokes `checkSystem`.
Additionally, this PR simplifies the main loop. Assigned metavariables
and `zetaDelta` reduction are now handled by installing `pre`/`post`
methods.
This commit is contained in:
Leonardo de Moura 2026-01-03 20:27:46 -08:00 committed by GitHub
parent a2cf78ac4a
commit 78c9a01bb2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 11 additions and 16 deletions

View file

@ -130,7 +130,7 @@ def getKey (e : Expr) : Key :=
| .lit v => .lit v
| .const declName _ => .const declName e.getAppNumArgs
| .fvar fvarId => .fvar fvarId e.getAppNumArgs
| .forallE _ _ _ _ => .arrow
| .forallE .. => .arrow
| _ => .other
/-- Push `e` arguments/children into the `todo` stack. -/

View file

@ -25,20 +25,12 @@ def simpLet (_ : Expr) : SimpM Result := do
-- **TODO**
return .rfl
def simpFVar (_ : Expr) : SimpM Result := do
-- **TODO**
return .rfl
def simpMVar (_ : Expr) : SimpM Result := do
-- **TODO**
return .rfl
def simpApp (e : Expr) : SimpM Result := do
congrArgs e
def simpStep : Simproc := fun e => do
match e with
| .lit _ | .sort _ | .bvar _ | .const .. => return .rfl
| .lit _ | .sort _ | .bvar _ | .const .. | .fvar _ | .mvar _ => return .rfl
| .proj .. =>
reportIssue! "unexpected kernel projection term during simplification{indentExpr e}\npre-process and fold them as projection applications"
return .rfl
@ -50,8 +42,6 @@ def simpStep : Simproc := fun e => do
| .lam .. => simpLambda e
| .forallE .. => simpForall e
| .letE .. => simpLet e
| .fvar .. => simpFVar e
| .mvar .. => simpMVar e
| .app .. => simpApp e
abbrev cacheResult (e : Expr) (r : Result) : SimpM Result := do
@ -59,11 +49,16 @@ abbrev cacheResult (e : Expr) (r : Result) : SimpM Result := do
return r
@[export lean_sym_simp]
def simpImpl (e₁ : Expr) : SimpM Result := do
if (← get).numSteps >= (← getConfig).maxSteps then
def simpImpl (e₁ : Expr) : SimpM Result := withIncRecDepth do
let numSteps := (← get).numSteps
if numSteps >= (← getConfig).maxSteps then
throwError "`simp` failed: maximum number of steps exceeded"
if let some result := (← getCache).find? { expr := e₁ } then
return result
let numSteps := numSteps + 1
if numSteps % 1000 == 0 then
checkSystem "simp"
modify fun s => { s with numSteps }
match (← pre e₁) with
| .step e₂ h₁ => cacheResult e₁ (← mkEqTransResult e₁ e₂ h₁ (← simp e₂))
| .rfl =>

View file

@ -100,8 +100,6 @@ invalidating the cache and causing O(2^n) behavior on conditional trees.
/-- Configuration options for the structural simplifier. -/
structure Config where
/-- If `true`, unfold let-bindings (zeta reduction) during simplification. -/
zetaDelta : Bool := true
/-- Maximum number of steps that can be performed by the simplifier. -/
maxSteps : Nat := 0
-- **TODO**: many are still missing

View file

@ -101,6 +101,8 @@ def benchManyRewrites (n : Nat) : MetaM Unit := do
let proofSize ← getProofSize r
IO.println s!"many_rewrites_{n}: {timeMs}ms, proof_size={proofSize}"
set_option maxRecDepth 100000
/-! ## Run all benchmarks -/
def runAllBenchmarks : MetaM Unit := do
IO.println "=== Simplifier Stress Tests ==="