mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-17 10:24:07 +00:00
feat: add cbv_simproc infrastructure for user-extensible cbv simplification procedures (#12597)
This PR adds a `cbv_simproc` system for the `cbv` tactic, mirroring simp's `simproc` infrastructure but tailored to cbv's three-phase pipeline (`↓` pre, `cbv_eval` eval, `↑` post). User-defined simplification procedures are indexed by discrimination tree patterns and dispatched during cbv normalization. New syntax: - `cbv_simproc [↓|↑|cbv_eval] name (pattern) := body` — define and register a cbv simproc - `cbv_simproc_decl name (pattern) := body` — define without registering - `attribute [cbv_simproc [↓|↑|cbv_eval]] name` — register an existing declaration - `builtin_cbv_simproc` variants for the internal use New files: - `src/Init/CbvSimproc.lean` — syntax and macros - `src/Lean/Meta/Tactic/Cbv/CbvSimproc.lean` — types, env extensions, registration, dispatch - `src/Lean/Elab/Tactic/CbvSimproc.lean` — pattern elaboration and command elaborators --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
85d38cba84
commit
9b1973ada7
@@ -30,6 +30,7 @@ public import Init.Hints
|
||||
public import Init.Conv
|
||||
public import Init.Guard
|
||||
public import Init.Simproc
|
||||
public import Init.CbvSimproc
|
||||
public import Init.SizeOfLemmas
|
||||
public import Init.BinderPredicates
|
||||
public import Init.Ext
|
||||
|
||||
71
src/Init/CbvSimproc.lean
Normal file
71
src/Init/CbvSimproc.lean
Normal file
@@ -0,0 +1,71 @@
|
||||
/-
|
||||
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Wojciech Różowski
|
||||
-/
|
||||
module
|
||||
|
||||
prelude
|
||||
public meta import Init.Data.ToString.Name -- shake: keep (transitive public meta dep, fix)
|
||||
public import Init.Tactics
|
||||
import Init.Meta.Defs
|
||||
|
||||
public section
|
||||
|
||||
namespace Lean.Parser
|
||||
|
||||
syntax cbvSimprocEval := "cbv_eval"
|
||||
|
||||
/--
|
||||
A user-defined simplification procedure used by the `cbv` tactic.
|
||||
The body must have type `Lean.Meta.Sym.Simp.Simproc` (`Expr → SimpM Result`).
|
||||
Procedures are indexed by a discrimination tree pattern and fire at one of three phases:
|
||||
`↓` (pre), `cbv_eval` (eval), or `↑` (post, default).
|
||||
-/
|
||||
syntax (docComment)? attrKind "cbv_simproc " (Tactic.simpPre <|> Tactic.simpPost <|> cbvSimprocEval)? ident " (" term ")" " := " term : command
|
||||
|
||||
/--
|
||||
A `cbv_simproc` declaration without automatically adding it to the cbv simproc set.
|
||||
To activate, use `attribute [cbv_simproc]`.
|
||||
-/
|
||||
syntax (docComment)? "cbv_simproc_decl " ident " (" term ")" " := " term : command
|
||||
|
||||
syntax (docComment)? attrKind "builtin_cbv_simproc " (Tactic.simpPre <|> Tactic.simpPost <|> cbvSimprocEval)? ident " (" term ")" " := " term : command
|
||||
|
||||
syntax (docComment)? "builtin_cbv_simproc_decl " ident " (" term ")" " := " term : command
|
||||
|
||||
syntax (name := cbvSimprocPattern) "cbv_simproc_pattern% " term " => " ident : command
|
||||
|
||||
syntax (name := cbvSimprocPatternBuiltin) "builtin_cbv_simproc_pattern% " term " => " ident : command
|
||||
|
||||
namespace Attr
|
||||
|
||||
syntax (name := cbvSimprocAttr) "cbv_simproc" (Tactic.simpPre <|> Tactic.simpPost <|> cbvSimprocEval)? : attr
|
||||
|
||||
syntax (name := cbvSimprocBuiltinAttr) "builtin_cbv_simproc" (Tactic.simpPre <|> Tactic.simpPost <|> cbvSimprocEval)? : attr
|
||||
|
||||
end Attr
|
||||
|
||||
macro_rules
|
||||
| `($[$doc?:docComment]? cbv_simproc_decl $n:ident ($pattern:term) := $body) => do
|
||||
let simprocType := `Lean.Meta.Sym.Simp.Simproc
|
||||
`($[$doc?:docComment]? meta def $n:ident : $(mkIdent simprocType) := $body
|
||||
cbv_simproc_pattern% $pattern => $n)
|
||||
|
||||
macro_rules
|
||||
| `($[$doc?:docComment]? builtin_cbv_simproc_decl $n:ident ($pattern:term) := $body) => do
|
||||
let simprocType := `Lean.Meta.Sym.Simp.Simproc
|
||||
`($[$doc?:docComment]? def $n:ident : $(mkIdent simprocType) := $body
|
||||
builtin_cbv_simproc_pattern% $pattern => $n)
|
||||
|
||||
macro_rules
|
||||
| `($[$doc?:docComment]? $kind:attrKind cbv_simproc $[$phase?]? $n:ident ($pattern:term) := $body) => do
|
||||
`($[$doc?:docComment]? cbv_simproc_decl $n ($pattern) := $body
|
||||
attribute [$kind cbv_simproc $[$phase?]?] $n)
|
||||
|
||||
macro_rules
|
||||
| `($[$doc?:docComment]? $kind:attrKind builtin_cbv_simproc $[$phase?]? $n:ident ($pattern:term) := $body) => do
|
||||
`($[$doc?:docComment]? builtin_cbv_simproc_decl $n ($pattern) := $body
|
||||
attribute [$kind builtin_cbv_simproc $[$phase?]?] $n)
|
||||
|
||||
end Lean.Parser
|
||||
@@ -17,6 +17,7 @@ public import Lean.Elab.Tactic.Location
|
||||
public import Lean.Elab.Tactic.SimpTrace
|
||||
public import Lean.Elab.Tactic.Simp
|
||||
public import Lean.Elab.Tactic.Simproc
|
||||
public import Lean.Elab.Tactic.CbvSimproc
|
||||
public import Lean.Elab.Tactic.BuiltinTactic
|
||||
public import Lean.Elab.Tactic.Split
|
||||
public import Lean.Elab.Tactic.Conv
|
||||
|
||||
75
src/Lean/Elab/Tactic/CbvSimproc.lean
Normal file
75
src/Lean/Elab/Tactic/CbvSimproc.lean
Normal file
@@ -0,0 +1,75 @@
|
||||
/-
|
||||
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Wojciech Różowski
|
||||
-/
|
||||
module
|
||||
|
||||
prelude
|
||||
public import Init.CbvSimproc
|
||||
public import Lean.Meta.Tactic.Cbv.CbvSimproc
|
||||
public import Lean.Elab.Command
|
||||
|
||||
public section
|
||||
|
||||
namespace Lean.Elab
|
||||
open Lean Meta Sym
|
||||
|
||||
-- NB: This duplicates `elabSimprocPattern` from `Lean.Elab.Tactic.Simproc`
|
||||
-- to avoid pulling in simp simproc elaboration as a dependency.
|
||||
def elabCbvSimprocPattern (stx : Syntax) : MetaM Expr := do
|
||||
let go : TermElabM Expr := do
|
||||
let pattern ← Term.elabTerm stx none
|
||||
Term.synthesizeSyntheticMVars
|
||||
return pattern
|
||||
go.run'
|
||||
|
||||
-- **Note**: `abstractMVars` returns a lambda expression, but `mkPatternFromExpr` expects a
|
||||
-- proof term (it calls `inferType` to get the forall type used as the pattern). We convert the
|
||||
-- lambda telescope into a forall telescope, then create a metavariable of that forall type so
|
||||
-- that `mkPatternFromExpr` can recover it via `inferType`. This is a workaround until the
|
||||
-- pattern API supports lambda telescopes directly (see `Sym.mkSimprocPatternFromExpr'`).
|
||||
public def mkSimprocPatternFromExpr (e : Expr) : MetaM Pattern := do
|
||||
let result ← abstractMVars e
|
||||
let forallExpr ← lambdaTelescope result.expr fun args body => mkForallFVars args body
|
||||
let term ← mkFreshExprMVar forallExpr
|
||||
mkPatternFromExpr term result.paramNames.toList
|
||||
|
||||
|
||||
def elabCbvSimprocKeys (stx : Syntax) : MetaM (Array DiscrTree.Key) := do
|
||||
let pattern ← elabCbvSimprocPattern stx
|
||||
let symPattern ← mkSimprocPatternFromExpr pattern
|
||||
return symPattern.mkDiscrTreeKeys
|
||||
|
||||
def checkCbvSimprocType (declName : Name) : CoreM Unit := do
|
||||
let decl ← getConstInfo declName
|
||||
match decl.type with
|
||||
| .const ``Sym.Simp.Simproc _ => pure ()
|
||||
| _ => throwError "Unexpected type for cbv simproc pattern: Expected \
|
||||
`{.ofConstName ``Sym.Simp.Simproc}`, but `{declName}` has type{indentExpr decl.type}"
|
||||
|
||||
namespace Command
|
||||
open Lean.Meta.Tactic.Cbv
|
||||
|
||||
@[builtin_command_elab Lean.Parser.cbvSimprocPattern] def elabCbvSimprocPattern : CommandElab := fun stx => do
|
||||
let `(cbv_simproc_pattern% $pattern => $declName) := stx | throwUnsupportedSyntax
|
||||
liftTermElabM do
|
||||
let declName ← realizeGlobalConstNoOverload declName
|
||||
checkCbvSimprocType declName
|
||||
let keys ← elabCbvSimprocKeys pattern
|
||||
registerCbvSimproc declName keys
|
||||
|
||||
@[builtin_command_elab Lean.Parser.cbvSimprocPatternBuiltin] def elabCbvSimprocPatternBuiltin : CommandElab := fun stx => do
|
||||
let `(builtin_cbv_simproc_pattern% $pattern => $declName) := stx | throwUnsupportedSyntax
|
||||
liftTermElabM do
|
||||
let declName ← realizeGlobalConstNoOverload declName
|
||||
checkCbvSimprocType declName
|
||||
let keys ← elabCbvSimprocKeys pattern
|
||||
let val := mkAppN (mkConst ``registerBuiltinCbvSimproc)
|
||||
#[toExpr declName, toExpr keys, mkConst declName]
|
||||
let initDeclName ← mkFreshUserName (declName ++ `declare)
|
||||
declareBuiltin initDeclName val
|
||||
|
||||
end Command
|
||||
|
||||
end Lean.Elab
|
||||
@@ -17,6 +17,7 @@ import Lean.Meta.Sym.ProofInstInfo
|
||||
import Lean.Meta.Sym.AlphaShareBuilder
|
||||
import Lean.Meta.Sym.Offset
|
||||
import Lean.Meta.Sym.Eta
|
||||
import Lean.Meta.AbstractMVars
|
||||
import Init.Data.List.MapIdx
|
||||
import Init.Data.Nat.Linear
|
||||
import Std.Do.Triple.Basic
|
||||
@@ -260,6 +261,45 @@ public def mkEqPatternFromDecl (declName : Name) : MetaM (Pattern × Expr) := do
|
||||
let_expr Eq _ lhs rhs := type | throwError "conclusion is not a equality{indentExpr type}"
|
||||
return (lhs, rhs)
|
||||
|
||||
/--
|
||||
Like `mkPatternCore` but takes a lambda expression instead of a forall type.
|
||||
Uses `lambdaBoundedTelescope` to open binders and detect instance/proof arguments.
|
||||
-/
|
||||
def mkPatternCoreFromLambda (lam : Expr) (levelParams : List Name)
|
||||
(varTypes : Array Expr) (pattern : Expr) : MetaM Pattern := do
|
||||
let fnInfos ← mkProofInstInfoMapFor pattern
|
||||
let checkTypeMask := mkCheckTypeMask pattern varTypes.size
|
||||
let checkTypeMask? := if checkTypeMask.all (· == false) then none else some checkTypeMask
|
||||
let varInfos? ← lambdaBoundedTelescope lam varTypes.size fun xs _ =>
|
||||
mkProofInstArgInfo? xs
|
||||
return { levelParams, varTypes, pattern, fnInfos, varInfos?, checkTypeMask? }
|
||||
|
||||
def mkPatternFromLambda (levelParams : List Name) (lam : Expr) : MetaM Pattern := do
|
||||
let rec go (pattern : Expr) (varTypes : Array Expr) : MetaM Pattern := do
|
||||
if let .lam _ d b _ := pattern then
|
||||
return (← go b (varTypes.push d))
|
||||
let pattern ← preprocessType pattern
|
||||
mkPatternCoreFromLambda lam levelParams varTypes pattern
|
||||
go lam #[]
|
||||
|
||||
/--
|
||||
Creates a `Pattern` from an expression containing metavariables.
|
||||
|
||||
Metavariables in `e` become pattern variables (wildcards). For example,
|
||||
`Nat.succ ?m` produces a pattern matching `Nat.succ _` with discrimination
|
||||
tree keys `[Nat.succ, *]`.
|
||||
|
||||
This is used for user-registered simproc patterns where the user provides
|
||||
an expression with underscores (elaborated as metavariables) to specify
|
||||
what the simproc should match.
|
||||
-/
|
||||
public def mkSimprocPatternFromExpr (e : Expr) : MetaM Pattern := do
|
||||
let result ← abstractMVars e
|
||||
let levelParams := result.paramNames.mapIdx fun i _ => Name.num uvarPrefix i
|
||||
let us := levelParams.toList.map mkLevelParam
|
||||
let expr := result.expr.instantiateLevelParamsArray result.paramNames us.toArray
|
||||
mkPatternFromLambda levelParams.toList expr
|
||||
|
||||
structure UnifyM.Context where
|
||||
pattern : Pattern
|
||||
unify : Bool := true
|
||||
|
||||
@@ -16,5 +16,6 @@ namespace Lean
|
||||
|
||||
builtin_initialize registerTraceClass `Meta.Tactic.cbv
|
||||
builtin_initialize registerTraceClass `Debug.Meta.Tactic.cbv
|
||||
builtin_initialize registerTraceClass `Debug.Meta.Tactic.cbv.simprocs (inherited := true)
|
||||
|
||||
end Lean
|
||||
|
||||
274
src/Lean/Meta/Tactic/Cbv/CbvSimproc.lean
Normal file
274
src/Lean/Meta/Tactic/Cbv/CbvSimproc.lean
Normal file
@@ -0,0 +1,274 @@
|
||||
/-
|
||||
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Wojciech Różowski
|
||||
-/
|
||||
module
|
||||
prelude
|
||||
public import Lean.Compiler.InitAttr
|
||||
public import Lean.ScopedEnvExtension
|
||||
public import Lean.Meta.Sym.Simp.SimpM
|
||||
public import Lean.Meta.Sym.Simp.Result
|
||||
public import Lean.Meta.Sym.Simp.App
|
||||
public import Lean.Meta.Sym.Simp.DiscrTree
|
||||
public import Lean.Meta.Sym.Pattern
|
||||
|
||||
public section
|
||||
namespace Lean.Meta.Tactic.Cbv
|
||||
open Lean.Meta.Sym
|
||||
open Lean.Meta.Sym.Simp
|
||||
|
||||
/--
|
||||
The phase at which a `cbv_simproc` fires during cbv normalization.
|
||||
|
||||
- `pre` (`↓`): Fires on each subexpression *before* cbv reduction, so arguments are still
|
||||
unreduced. Runs before cbv-specific pre-steps (projection reduction, unfolding, etc.).
|
||||
- `eval` (`cbv_eval`): Fires during the post phase, after ground evaluation but *before*
|
||||
`handleApp` attempts equation lemmas, unfolding, and recursion reduction.
|
||||
Arguments have already been reduced.
|
||||
- `post` (`↑`, default): Fires during the post phase, *after* `handleApp` has attempted
|
||||
standard reduction. Arguments have already been reduced.
|
||||
-/
|
||||
inductive CbvSimprocPhase where
|
||||
| pre | eval | post
|
||||
deriving Inhabited, BEq, Hashable, Repr
|
||||
|
||||
instance : ToExpr CbvSimprocPhase where
|
||||
toExpr
|
||||
| .pre => mkConst ``CbvSimprocPhase.pre
|
||||
| .eval => mkConst ``CbvSimprocPhase.eval
|
||||
| .post => mkConst ``CbvSimprocPhase.post
|
||||
toTypeExpr := mkConst ``CbvSimprocPhase
|
||||
|
||||
structure CbvSimprocOLeanEntry where
|
||||
declName : Name
|
||||
phase : CbvSimprocPhase := .post
|
||||
keys : Array DiscrTree.Key := #[]
|
||||
deriving Inhabited
|
||||
|
||||
structure CbvSimprocEntry extends CbvSimprocOLeanEntry where
|
||||
proc : Simproc
|
||||
|
||||
instance : BEq CbvSimprocEntry where
|
||||
beq e₁ e₂ := e₁.declName == e₂.declName
|
||||
|
||||
instance : ToFormat CbvSimprocEntry where
|
||||
format e := format e.declName
|
||||
|
||||
structure CbvSimprocs where
|
||||
pre : DiscrTree CbvSimprocEntry := {}
|
||||
eval : DiscrTree CbvSimprocEntry := {}
|
||||
post : DiscrTree CbvSimprocEntry := {}
|
||||
simprocNames : PHashSet Name := {}
|
||||
erased : PHashSet Name := {}
|
||||
deriving Inhabited
|
||||
|
||||
def CbvSimprocs.addCore (s : CbvSimprocs) (keys : Array DiscrTree.Key) (declName : Name)
|
||||
(phase : CbvSimprocPhase) (proc : Simproc) : CbvSimprocs :=
|
||||
let entry : CbvSimprocEntry := { declName, phase, keys, proc }
|
||||
let s := { s with simprocNames := s.simprocNames.insert declName, erased := s.erased.erase declName }
|
||||
match phase with
|
||||
| .pre => { s with pre := s.pre.insertKeyValue keys entry }
|
||||
| .eval => { s with eval := s.eval.insertKeyValue keys entry }
|
||||
| .post => { s with post := s.post.insertKeyValue keys entry }
|
||||
|
||||
def CbvSimprocs.erase (s : CbvSimprocs) (declName : Name) : CbvSimprocs :=
|
||||
{ s with erased := s.erased.insert declName, simprocNames := s.simprocNames.erase declName }
|
||||
|
||||
structure BuiltinCbvSimprocs where
|
||||
keys : Std.HashMap Name (Array DiscrTree.Key) := {}
|
||||
procs : Std.HashMap Name Simproc := {}
|
||||
deriving Inhabited
|
||||
|
||||
builtin_initialize builtinCbvSimprocDeclsRef : IO.Ref BuiltinCbvSimprocs ← IO.mkRef {}
|
||||
|
||||
def registerBuiltinCbvSimproc (declName : Name)
|
||||
(keys : Array DiscrTree.Key) (proc : Simproc) : IO Unit := do
|
||||
unless (← initializing) do
|
||||
throw (IO.userError s!"Invalid builtin cbv simproc declaration: \
|
||||
It can only be registered during initialization")
|
||||
if (← builtinCbvSimprocDeclsRef.get).keys.contains declName then
|
||||
throw (IO.userError s!"Invalid builtin cbv simproc declaration \
|
||||
`{privateToUserName declName}`: It has already been declared")
|
||||
builtinCbvSimprocDeclsRef.modify fun { keys := ks, procs } =>
|
||||
{ keys := ks.insert declName keys, procs := procs.insert declName proc }
|
||||
|
||||
structure CbvSimprocDecl where
|
||||
declName : Name
|
||||
keys : Array DiscrTree.Key
|
||||
deriving Inhabited
|
||||
|
||||
def CbvSimprocDecl.lt (d₁ d₂ : CbvSimprocDecl) : Bool :=
|
||||
Name.quickLt d₁.declName d₂.declName
|
||||
|
||||
structure CbvSimprocDeclExtState where
|
||||
builtin : Std.HashMap Name (Array DiscrTree.Key)
|
||||
newEntries : PHashMap Name (Array DiscrTree.Key) := {}
|
||||
deriving Inhabited
|
||||
|
||||
builtin_initialize cbvSimprocDeclExt :
|
||||
PersistentEnvExtension CbvSimprocDecl CbvSimprocDecl CbvSimprocDeclExtState ←
|
||||
registerPersistentEnvExtension {
|
||||
mkInitial := return { builtin := (← builtinCbvSimprocDeclsRef.get).keys }
|
||||
addImportedFn := fun _ => return { builtin := (← builtinCbvSimprocDeclsRef.get).keys }
|
||||
addEntryFn := fun s d =>
|
||||
{ s with newEntries := s.newEntries.insert d.declName d.keys }
|
||||
exportEntriesFn := fun s =>
|
||||
let result := s.newEntries.foldl (init := #[]) fun result declName keys =>
|
||||
result.push { declName, keys }
|
||||
result.qsort CbvSimprocDecl.lt
|
||||
}
|
||||
|
||||
def getCbvSimprocDeclKeys? (declName : Name) : CoreM (Option (Array DiscrTree.Key)) := do
|
||||
let env ← getEnv
|
||||
let keys? ← match env.getModuleIdxFor? declName with
|
||||
| some modIdx => do
|
||||
let some decl :=
|
||||
(cbvSimprocDeclExt.getModuleEntries env modIdx).binSearch { declName, keys := #[] } CbvSimprocDecl.lt
|
||||
| pure none
|
||||
pure (some decl.keys)
|
||||
| none => pure ((cbvSimprocDeclExt.getState env).newEntries.find? declName)
|
||||
if let some keys := keys? then
|
||||
return some keys
|
||||
else
|
||||
return (cbvSimprocDeclExt.getState env).builtin[declName]?
|
||||
|
||||
def isCbvSimproc (declName : Name) : CoreM Bool :=
|
||||
return (← getCbvSimprocDeclKeys? declName).isSome
|
||||
|
||||
def isBuiltinCbvSimproc (declName : Name) : CoreM Bool := do
|
||||
return (cbvSimprocDeclExt.getState (← getEnv)).builtin.contains declName
|
||||
|
||||
def registerCbvSimproc (declName : Name) (keys : Array DiscrTree.Key) : CoreM Unit := do
|
||||
let env ← getEnv
|
||||
unless (env.getModuleIdxFor? declName).isNone do
|
||||
throwError "Invalid cbv simproc declaration `{.ofConstName declName}`: \
|
||||
It is declared in an imported module"
|
||||
if (← isCbvSimproc declName) then
|
||||
throwError "Invalid cbv simproc declaration `{.ofConstName declName}`: \
|
||||
It has already been declared"
|
||||
modifyEnv fun env => cbvSimprocDeclExt.modifyState env fun s =>
|
||||
{ s with newEntries := s.newEntries.insert declName keys }
|
||||
|
||||
abbrev CbvSimprocExtension := ScopedEnvExtension CbvSimprocOLeanEntry CbvSimprocEntry CbvSimprocs
|
||||
|
||||
unsafe def getCbvSimprocFromDeclImpl (declName : Name) : ImportM Simproc := do
|
||||
let ctx ← read
|
||||
match ctx.env.find? declName with
|
||||
| none => throw <| IO.userError ("Unknown constant `" ++ toString declName ++ "`")
|
||||
| some info =>
|
||||
match info.type with
|
||||
| .const ``Simproc _ =>
|
||||
IO.ofExcept <| ctx.env.evalConst Simproc ctx.opts declName
|
||||
| _ => throw <| IO.userError s!"Cbv simproc `{privateToUserName declName}` has an \
|
||||
unexpected type: Expected `Simproc`, but found `{info.type}`"
|
||||
|
||||
@[implemented_by getCbvSimprocFromDeclImpl]
|
||||
opaque getCbvSimprocFromDecl (declName : Name) : ImportM Simproc
|
||||
|
||||
def toCbvSimprocEntry (e : CbvSimprocOLeanEntry) : ImportM CbvSimprocEntry := do
|
||||
return { toCbvSimprocOLeanEntry := e, proc := (← getCbvSimprocFromDecl e.declName) }
|
||||
|
||||
builtin_initialize builtinCbvSimprocsRef : IO.Ref CbvSimprocs ← IO.mkRef {}
|
||||
|
||||
builtin_initialize cbvSimprocExtension : CbvSimprocExtension ←
|
||||
registerScopedEnvExtension {
|
||||
name := `cbvSimprocExt
|
||||
mkInitial := builtinCbvSimprocsRef.get
|
||||
ofOLeanEntry := fun _ => toCbvSimprocEntry
|
||||
toOLeanEntry := fun e => e.toCbvSimprocOLeanEntry
|
||||
addEntry := fun s e => s.addCore e.keys e.declName e.phase e.proc
|
||||
}
|
||||
|
||||
def eraseCbvSimprocAttr (declName : Name) : AttrM Unit := do
|
||||
let s := cbvSimprocExtension.getState (← getEnv)
|
||||
unless s.simprocNames.contains declName do
|
||||
throwError "`{.ofConstName declName}` does not have a [cbv_simproc] attribute"
|
||||
modifyEnv fun env => cbvSimprocExtension.modifyState env fun s => s.erase declName
|
||||
|
||||
def addCbvSimprocAttrCore (declName : Name) (kind : AttributeKind)
|
||||
(phase : CbvSimprocPhase) : CoreM Unit := do
|
||||
let proc ← getCbvSimprocFromDecl declName
|
||||
let some keys ← getCbvSimprocDeclKeys? declName |
|
||||
throwError "Invalid `[cbv_simproc]` attribute: \
|
||||
`{.ofConstName declName}` is not a cbv simproc"
|
||||
cbvSimprocExtension.add { declName, phase, keys, proc } kind
|
||||
|
||||
def parseCbvSimprocPhase (stx : Syntax) : CbvSimprocPhase :=
|
||||
if stx.isNone then .post
|
||||
else
|
||||
let inner := stx[0]
|
||||
if inner.getKind == `Lean.Parser.Tactic.simpPre then .pre
|
||||
else if inner.getKind == `Lean.Parser.cbvSimprocEval then .eval
|
||||
else .post
|
||||
|
||||
def addCbvSimprocAttr (declName : Name) (stx : Syntax)
|
||||
(attrKind : AttributeKind) : AttrM Unit := do
|
||||
ensureAttrDeclIsMeta `cbvSimprocAttr declName attrKind
|
||||
let go : MetaM Unit := do
|
||||
addCbvSimprocAttrCore declName attrKind (parseCbvSimprocPhase stx[1])
|
||||
discard <| go.run {} {}
|
||||
|
||||
def addCbvSimprocBuiltinAttrCore (ref : IO.Ref CbvSimprocs) (declName : Name)
|
||||
(phase : CbvSimprocPhase) (proc : Simproc) : IO Unit := do
|
||||
let some keys := (← builtinCbvSimprocDeclsRef.get).keys[declName]? |
|
||||
throw (IO.userError s!"Invalid `[builtin_cbv_simproc]` attribute: \
|
||||
`{privateToUserName declName}` is not a builtin cbv simproc")
|
||||
ref.modify fun s => s.addCore keys declName phase proc
|
||||
|
||||
def addCbvSimprocBuiltinAttr (declName : Name) (phase : CbvSimprocPhase)
|
||||
(proc : Simproc) : IO Unit :=
|
||||
addCbvSimprocBuiltinAttrCore builtinCbvSimprocsRef declName phase proc
|
||||
|
||||
private def addBuiltinCbvSimproc (declName : Name) (stx : Syntax) : AttrM Unit := do
|
||||
let go : MetaM Unit := do
|
||||
let phase := parseCbvSimprocPhase stx[1]
|
||||
let val := mkAppN (mkConst ``addCbvSimprocBuiltinAttr)
|
||||
#[toExpr declName, toExpr phase, mkConst declName]
|
||||
let initDeclName ← mkFreshUserName (declName ++ `declare)
|
||||
declareBuiltin initDeclName val
|
||||
go.run' {}
|
||||
|
||||
builtin_initialize
|
||||
registerBuiltinAttribute {
|
||||
ref := by exact decl_name%
|
||||
name := `cbvSimprocAttr
|
||||
descr := "Cbv simplification procedure"
|
||||
applicationTime := AttributeApplicationTime.afterCompilation
|
||||
add := fun declName stx attrKind => addCbvSimprocAttr declName stx attrKind
|
||||
erase := fun declName => eraseCbvSimprocAttr declName
|
||||
}
|
||||
|
||||
builtin_initialize
|
||||
registerBuiltinAttribute {
|
||||
ref := by exact decl_name%
|
||||
name := `cbvSimprocBuiltinAttr
|
||||
descr := "Builtin cbv simplification procedure"
|
||||
applicationTime := AttributeApplicationTime.afterCompilation
|
||||
erase := fun _ => throwError "Not implemented yet, [-builtin_cbv_simproc]"
|
||||
add := fun declName stx _ => addBuiltinCbvSimproc declName stx
|
||||
}
|
||||
|
||||
def getCbvSimprocs : CoreM CbvSimprocs :=
|
||||
return cbvSimprocExtension.getState (← getEnv)
|
||||
|
||||
def cbvSimprocDispatch (tree : DiscrTree CbvSimprocEntry)
|
||||
(erased : PHashSet Name) : Simproc := fun e => do
|
||||
let candidates := Sym.getMatchWithExtra tree e
|
||||
if candidates.isEmpty then
|
||||
trace[Debug.Meta.Tactic.cbv.simprocs] "no cbv simprocs found for {e}"
|
||||
return .rfl
|
||||
for (entry, numExtra) in candidates do
|
||||
unless erased.contains entry.declName do
|
||||
let result ← if numExtra == 0 then
|
||||
entry.proc e
|
||||
else
|
||||
simpOverApplied e numExtra entry.proc
|
||||
if result matches .step _ _ _ then
|
||||
trace[Debug.Meta.Tactic.cbv.simprocs] "cbv simproc `{entry.declName}` result {e} => {result.getResultExpr e}"
|
||||
return result
|
||||
if result matches .rfl (done := true) then
|
||||
return result
|
||||
return .rfl
|
||||
|
||||
end Lean.Meta.Tactic.Cbv
|
||||
@@ -13,6 +13,7 @@ public import Lean.Meta.Tactic.Cbv.ControlFlow
|
||||
import Lean.Meta.Tactic.Cbv.Util
|
||||
import Lean.Meta.Tactic.Cbv.TheoremsLookup
|
||||
import Lean.Meta.Tactic.Cbv.CbvEvalExt
|
||||
import Lean.Meta.Tactic.Cbv.CbvSimproc
|
||||
import Lean.Meta.Sym
|
||||
import Lean.Meta.Tactic.Refl
|
||||
import Lean.Meta.Tactic.Replace
|
||||
@@ -272,22 +273,30 @@ def cbvPreStep : Simproc := fun e => do
|
||||
| .forallE .. | .lam .. | .fvar .. | .mvar .. | .bvar .. | .sort .. => return .rfl (done := true)
|
||||
| _ => return .rfl
|
||||
|
||||
/-- Pre-pass: skip builtin values and proofs, then dispatch structurally. -/
|
||||
def cbvPre : Simproc := isBuiltinValue <|> isProofTerm <|> cbvPreStep
|
||||
/-- Pre-pass: skip builtin values and proofs, run pre simprocs, then dispatch structurally. -/
|
||||
def cbvPre (simprocs : CbvSimprocs) : Simproc :=
|
||||
isBuiltinValue <|> isProofTerm <|> cbvSimprocDispatch simprocs.pre simprocs.erased <|> cbvPreStep
|
||||
|
||||
/-- Post-pass: evaluate ground arithmetic, then try unfolding/beta-reducing applications. -/
|
||||
def cbvPost : Simproc := evalGround <|> handleApp
|
||||
/-- Post-pass: evaluate ground arithmetic, then try eval simprocs, then try unfolding/beta-reducing applications and finally run post simprocs -/
|
||||
def cbvPost (simprocs : CbvSimprocs) : Simproc :=
|
||||
evalGround <|> cbvSimprocDispatch simprocs.eval simprocs.erased <|> handleApp <|> cbvSimprocDispatch simprocs.post simprocs.erased
|
||||
|
||||
def cbvCore (e : Expr) (config : Sym.Simp.Config := {}) : Sym.SymM Result :=
|
||||
SimpM.run' (methods := {pre := cbvPre, post := cbvPost}) (config := config)
|
||||
def mkCbvMethods (simprocs : CbvSimprocs) : Methods :=
|
||||
{ pre := cbvPre simprocs, post := cbvPost simprocs }
|
||||
|
||||
def cbvCore (e : Expr) (config : Sym.Simp.Config := {}) : Sym.SymM Result := do
|
||||
let simprocs ← getCbvSimprocs
|
||||
let methods := mkCbvMethods simprocs
|
||||
SimpM.run' (methods := methods) (config := config)
|
||||
<| simp e
|
||||
|
||||
/-- Reduce a single expression. Unfolds reducibles, shares subterms, then runs the
|
||||
simplifier with `cbvPre`/`cbvPost`. Used by `conv => cbv`. -/
|
||||
public def cbvEntry (e : Expr) : MetaM Result := do
|
||||
trace[Meta.Tactic.cbv] "Called cbv tactic to simplify {e}"
|
||||
let simprocs ← getCbvSimprocs
|
||||
let config : Sym.Simp.Config := { maxSteps := cbv.maxSteps.get (← getOptions) }
|
||||
let methods := {pre := cbvPre, post := cbvPost}
|
||||
let methods := mkCbvMethods simprocs
|
||||
let e ← Sym.unfoldReducible e
|
||||
Sym.SymM.run do
|
||||
let e ← Sym.shareCommon e
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "util/options.h"
|
||||
|
||||
//update stage0
|
||||
namespace lean {
|
||||
options get_default_options() {
|
||||
options opts;
|
||||
|
||||
285
tests/elab/cbv_simproc.lean
Normal file
285
tests/elab/cbv_simproc.lean
Normal file
@@ -0,0 +1,285 @@
|
||||
import Lean
|
||||
|
||||
set_option cbv.warning false
|
||||
|
||||
open Lean Meta Sym.Simp
|
||||
|
||||
cbv_simproc_decl myDeclProc (Nat.succ _) := fun _ => do
|
||||
return .rfl
|
||||
|
||||
cbv_simproc myPostProc (Nat.add _ _) := fun _ => do
|
||||
return .rfl
|
||||
|
||||
cbv_simproc ↓ myPreProc (Nat.mul _ _) := fun _ => do
|
||||
return .rfl
|
||||
|
||||
cbv_simproc cbv_eval myEvalProc (Nat.sub _ _) := fun _ => do
|
||||
return .rfl
|
||||
|
||||
cbv_simproc ↑ myExplicitPostProc (Nat.mod _ _) := fun _ => do
|
||||
return .rfl
|
||||
|
||||
cbv_simproc_decl myLateProc (Nat.div _ _) := fun _ => do
|
||||
return .rfl
|
||||
|
||||
attribute [cbv_simproc cbv_eval] myLateProc
|
||||
|
||||
opaque myFun : Nat → Nat
|
||||
|
||||
-- Pre simproc sees unreduced arguments
|
||||
section
|
||||
local cbv_simproc ↓ preTest (myFun _) := fun e => do
|
||||
let .app _f arg := e | return .rfl
|
||||
logInfo m!"pre saw: {arg}"
|
||||
return .rfl
|
||||
|
||||
/--
|
||||
info: pre saw: 1 + 2
|
||||
---
|
||||
info: pre saw: 3
|
||||
-/
|
||||
#guard_msgs in
|
||||
example : myFun (1 + 2) = myFun 3 := by cbv
|
||||
end
|
||||
|
||||
-- Eval simproc sees reduced arguments (fires before handleApp)
|
||||
section
|
||||
local cbv_simproc cbv_eval evalTest (myFun _) := fun e => do
|
||||
let .app _f arg := e | return .rfl
|
||||
logInfo m!"eval saw: {arg}"
|
||||
return .rfl
|
||||
|
||||
/--
|
||||
info: eval saw: 3
|
||||
---
|
||||
info: eval saw: 3
|
||||
---
|
||||
info: eval saw: 4
|
||||
---
|
||||
error: unsolved goals
|
||||
⊢ myFun 3 = myFun 4
|
||||
-/
|
||||
#guard_msgs in
|
||||
example : myFun (1 + 2) = myFun 4 := by cbv
|
||||
end
|
||||
|
||||
-- Post simproc sees reduced arguments (fires after handleApp)
|
||||
section
|
||||
local cbv_simproc ↑ postTest (myFun _) := fun e => do
|
||||
let .app _f arg := e | return .rfl
|
||||
logInfo m!"post saw: {arg}"
|
||||
return .rfl
|
||||
|
||||
/--
|
||||
info: post saw: 3
|
||||
---
|
||||
info: post saw: 3
|
||||
---
|
||||
info: post saw: 4
|
||||
---
|
||||
error: unsolved goals
|
||||
⊢ myFun 3 = myFun 4
|
||||
-/
|
||||
#guard_msgs in
|
||||
example : myFun (1 + 2) = myFun 4 := by cbv
|
||||
end
|
||||
|
||||
section
|
||||
cbv_simproc ↓ stringToList (String.toList _) := fun e => do
|
||||
let_expr String.toList s := e | return .rfl
|
||||
let some str := getStringValue? s | return .rfl
|
||||
let result ← Sym.share <| toExpr str.toList
|
||||
let typeExpr := mkApp (mkConst ``List [Lean.Level.zero]) (mkConst ``Char)
|
||||
return .step result (mkApp2 (mkConst ``Eq.refl [1]) typeExpr result) (done := true)
|
||||
|
||||
theorem simprocTest : "a".toList = ['a'] := by cbv
|
||||
|
||||
/--
|
||||
info: theorem simprocTest : "a".toList = ['a'] :=
|
||||
of_eq_true (Eq.trans (congrFun' (congrArg Eq (Eq.refl ['a'])) ['a']) (eq_self ['a']))
|
||||
-/
|
||||
#guard_msgs in
|
||||
#print simprocTest
|
||||
end
|
||||
|
||||
-- Erase attribute test
|
||||
cbv_simproc_decl erasableProc (Nat.gcd _ _) := fun _ => return .rfl (done := true)
|
||||
|
||||
theorem erasableProcTest : Nat.gcd 1 2 = 1 := by cbv
|
||||
|
||||
/--
|
||||
info: theorem erasableProcTest : Nat.gcd 1 2 = 1 :=
|
||||
of_eq_true (Eq.trans (congrFun' (congrArg Eq (Eq.refl 1)) 1) (eq_self 1))
|
||||
-/
|
||||
#guard_msgs in
|
||||
#print erasableProcTest
|
||||
|
||||
/-- error: `erasableProc` does not have a [cbv_simproc] attribute -/
|
||||
#guard_msgs in
|
||||
attribute [-cbvSimprocAttr] erasableProc
|
||||
|
||||
attribute [cbv_simproc] erasableProc
|
||||
attribute [-cbvSimprocAttr] erasableProc
|
||||
|
||||
|
||||
-- Pattern with @
|
||||
section
|
||||
local cbv_simproc ↓ testAt (@Nat.succ _) := fun e => do
|
||||
let_expr Nat.succ arg := e | return .rfl
|
||||
logInfo m!"@ saw: {arg}"
|
||||
return .rfl
|
||||
|
||||
/-- info: @ saw: 1 + 2 -/
|
||||
#guard_msgs in
|
||||
example : Nat.succ (1 + 2) = 4 := by cbv
|
||||
end
|
||||
|
||||
-- Pattern with explicit type args via @
|
||||
section
|
||||
local cbv_simproc ↓ testExplicitList (@List.cons Nat _ _) := fun _ => do
|
||||
logInfo m!"explicit list cons"
|
||||
return .rfl
|
||||
|
||||
/--
|
||||
info: explicit list cons
|
||||
---
|
||||
info: explicit list cons
|
||||
---
|
||||
info: explicit list cons
|
||||
-/
|
||||
#guard_msgs in
|
||||
example : ([1, 2, 3] : List Nat) = [1, 2, 3] := by cbv
|
||||
end
|
||||
|
||||
-- Implicit args are auto-filled: `List.cons _ _` works the same as `@List.cons Nat _ _`
|
||||
section
|
||||
local cbv_simproc ↓ testImplicitList (List.cons _ _) := fun _ => do
|
||||
logInfo m!"implicit list cons"
|
||||
return .rfl
|
||||
|
||||
/--
|
||||
info: implicit list cons
|
||||
---
|
||||
info: implicit list cons
|
||||
---
|
||||
info: implicit list cons
|
||||
-/
|
||||
#guard_msgs in
|
||||
example : ([1, 2, 3] : List Nat) = [1, 2, 3] := by cbv
|
||||
end
|
||||
|
||||
-- Typeclass-heavy pattern
|
||||
section
|
||||
local cbv_simproc ↓ testHAdd (HAdd.hAdd _ _) := fun _ => do
|
||||
logInfo m!"HAdd matched"
|
||||
return .rfl
|
||||
|
||||
/-- info: HAdd matched -/
|
||||
#guard_msgs in
|
||||
example : (1 : Nat) + 2 = 3 := by cbv
|
||||
end
|
||||
|
||||
-- Nested pattern
|
||||
section
|
||||
local cbv_simproc ↓ testNested (Nat.succ (Nat.succ _)) := fun _ => do
|
||||
logInfo m!"nested succ"
|
||||
return .rfl
|
||||
|
||||
/--
|
||||
info: nested succ
|
||||
---
|
||||
info: nested succ
|
||||
-/
|
||||
#guard_msgs in
|
||||
example : Nat.succ (Nat.succ 0) = 2 := by cbv
|
||||
end
|
||||
|
||||
-- Explicit type args on Prod.mk
|
||||
section
|
||||
local cbv_simproc ↓ testProd (@Prod.mk Nat Nat _ _) := fun _ => do
|
||||
logInfo m!"prod matched"
|
||||
return .rfl
|
||||
|
||||
/--
|
||||
info: prod matched
|
||||
---
|
||||
info: prod matched
|
||||
-/
|
||||
#guard_msgs in
|
||||
example : (1 + 2, 3) = (3, 3) := by cbv
|
||||
end
|
||||
|
||||
-- Scoped simproc: active when namespace is open, inactive when closed
|
||||
namespace ScopedTest
|
||||
scoped cbv_simproc ↓ scopedProc (myFun _) := fun e => do
|
||||
let .app _f arg := e | return .rfl
|
||||
logInfo m!"scoped saw: {arg}"
|
||||
return .rfl
|
||||
|
||||
/-- info: scoped saw: 0 -/
|
||||
#guard_msgs in
|
||||
example : myFun 0 = myFun 0 := by cbv
|
||||
end ScopedTest
|
||||
|
||||
-- Outside the namespace, the simproc should not fire
|
||||
example : myFun 0 = myFun 0 := by cbv
|
||||
|
||||
-- Re-opening the namespace reactivates the simproc
|
||||
open ScopedTest in
|
||||
/-- info: scoped saw: 0 -/
|
||||
#guard_msgs in
|
||||
example : myFun 0 = myFun 0 := by cbv
|
||||
|
||||
-- Over-applied pattern: simproc registered for `myBinFun _` fires on `myBinFun x y`
|
||||
opaque myBinFun : Nat → Nat → Nat
|
||||
|
||||
section
|
||||
local cbv_simproc ↓ overAppliedProc (myBinFun _) := fun e => do
|
||||
logInfo m!"over-applied saw: {e}"
|
||||
return .rfl
|
||||
|
||||
/--
|
||||
info: over-applied saw: myBinFun (1 + 2)
|
||||
---
|
||||
info: over-applied saw: myBinFun 3
|
||||
-/
|
||||
#guard_msgs in
|
||||
example : myBinFun (1 + 2) 4 = myBinFun 3 4 := by cbv
|
||||
end
|
||||
|
||||
-- Multiple simprocs matching the same head: first non-rfl result wins
|
||||
section
|
||||
local cbv_simproc ↓ multiFirst (myFun _) := fun _ => do
|
||||
logInfo m!"first fired"
|
||||
return .rfl (done := true)
|
||||
|
||||
local cbv_simproc ↓ multiSecond (myFun _) := fun _ => do
|
||||
logInfo m!"second fired"
|
||||
return .rfl
|
||||
|
||||
/-- info: first fired -/
|
||||
#guard_msgs in
|
||||
example : myFun 0 = myFun 0 := by cbv
|
||||
end
|
||||
|
||||
-- Multiple simprocs: first returns .rfl (skip), second succeeds with .step
|
||||
section
|
||||
@[irreducible] def myConst : Nat := 42
|
||||
|
||||
local cbv_simproc ↓ skipFirst (myConst) := fun _e => do
|
||||
logInfo m!"first skipped"
|
||||
return .rfl
|
||||
|
||||
local cbv_simproc ↓ succeedSecond (myConst) := fun _e => do
|
||||
logInfo m!"second succeeded"
|
||||
let result := toExpr (42 : Nat)
|
||||
return .step result (mkConst ``myConst.eq_def) (done := true)
|
||||
|
||||
/--
|
||||
info: first skipped
|
||||
---
|
||||
info: second succeeded
|
||||
-/
|
||||
#guard_msgs in
|
||||
example : myConst = 42 := by cbv
|
||||
end
|
||||
34
tests/elab/cbv_simproc_errors.lean
Normal file
34
tests/elab/cbv_simproc_errors.lean
Normal file
@@ -0,0 +1,34 @@
|
||||
import Lean
|
||||
|
||||
open Lean Meta Sym.Simp
|
||||
|
||||
-- Attribute on a def that was never declared as a cbv simproc
|
||||
meta def notASimproc : Simproc := fun _ => return .rfl
|
||||
|
||||
/-- error: Invalid `[cbv_simproc]` attribute: `notASimproc` is not a cbv simproc -/
|
||||
#guard_msgs in
|
||||
attribute [cbv_simproc] notASimproc
|
||||
|
||||
-- Attribute on a def with wrong type
|
||||
def plainNat : Nat := 42
|
||||
|
||||
/-- error: Cbv simproc `plainNat` has an unexpected type: Expected `Simproc`, but found `Nat` -/
|
||||
#guard_msgs in
|
||||
attribute [cbv_simproc] plainNat
|
||||
|
||||
-- Erase from something without the attribute
|
||||
cbv_simproc_decl untaggedProc (Nat.gcd _ _) := fun _ => return .rfl
|
||||
|
||||
/-- error: `untaggedProc` does not have a [cbv_simproc] attribute -/
|
||||
#guard_msgs in
|
||||
attribute [-cbvSimprocAttr] untaggedProc
|
||||
|
||||
-- Wrong type in cbv_simproc_decl pattern registration
|
||||
def wrongType : Nat := 42
|
||||
|
||||
/--
|
||||
error: Unexpected type for cbv simproc pattern: Expected `Simproc`, but `wrongType` has type
|
||||
Nat
|
||||
-/
|
||||
#guard_msgs in
|
||||
cbv_simproc_pattern% Nat.succ _ => wrongType
|
||||
188
tests/elab/cbv_simproc_fib.lean
Normal file
188
tests/elab/cbv_simproc_fib.lean
Normal file
@@ -0,0 +1,188 @@
|
||||
import Lean
|
||||
|
||||
set_option cbv.warning false
|
||||
|
||||
/-! # cbv_simproc for Nat.fib using fast doubling
|
||||
|
||||
This test demonstrates a cbv_simproc ported from Mathlib's `norm_num` extension that evaluates `Nat.fib.
|
||||
-/
|
||||
|
||||
open Lean Meta Sym.Simp
|
||||
|
||||
-- Define fib
|
||||
def Nat.fib : Nat → Nat
|
||||
| 0 => 0
|
||||
| 1 => 1
|
||||
| n + 2 => Nat.fib n + Nat.fib (n + 1)
|
||||
|
||||
theorem Nat.fib_zero : Nat.fib 0 = 0 := rfl
|
||||
theorem Nat.fib_one : Nat.fib 1 = 1 := rfl
|
||||
theorem Nat.fib_two : Nat.fib 2 = 1 := rfl
|
||||
|
||||
def IsFibAux (n a b : Nat) := Nat.fib n = a ∧ Nat.fib (n + 1) = b
|
||||
|
||||
theorem isFibAux_zero : IsFibAux 0 0 1 :=
|
||||
⟨Nat.fib_zero, Nat.fib_one⟩
|
||||
|
||||
theorem isFibAux_one : IsFibAux 1 1 1 :=
|
||||
⟨Nat.fib_one, Nat.fib_two⟩
|
||||
|
||||
theorem fib_add_two {n : Nat} : Nat.fib (n + 2) = Nat.fib n + Nat.fib (n + 1) := by grind [Nat.fib]
|
||||
|
||||
theorem fib_add (m n : Nat) : Nat.fib (m + n + 1) = Nat.fib m * Nat.fib n + Nat.fib (m + 1) * Nat.fib (n + 1) := by
|
||||
induction n generalizing m with
|
||||
| zero => grind [Nat.fib]
|
||||
| succ n ih =>
|
||||
specialize ih (m + 1)
|
||||
rw [Nat.add_assoc m 1 n, Nat.add_comm 1 n] at ih
|
||||
simp only [fib_add_two, ih]
|
||||
grind
|
||||
|
||||
theorem fib_two_mul (n : Nat) : Nat.fib (2 * n) = Nat.fib n * (2 * Nat.fib (n + 1) - Nat.fib n) := by
|
||||
cases n
|
||||
· simp [Nat.fib]
|
||||
· rw [Nat.two_mul, ← Nat.add_assoc, fib_add, fib_add_two, Nat.two_mul]
|
||||
have add_tsub_cancel_right : ∀ (a b : Nat), a + b - b = a := by grind
|
||||
simp only [← Nat.add_assoc, add_tsub_cancel_right]
|
||||
grind
|
||||
|
||||
theorem fib_two_mul_add_one (n : Nat) : Nat.fib (2 * n + 1) = Nat.fib (n + 1) ^ 2 + Nat.fib n ^ 2 := by
|
||||
rw [Nat.two_mul, fib_add]
|
||||
grind [Nat.two_mul, fib_add]
|
||||
|
||||
theorem isFibAux_two_mul {n a b n' a' b' : Nat} (H : IsFibAux n a b)
|
||||
(hn : 2 * n = n') (h1 : a * (2 * b - a) = a') (h2 : a * a + b * b = b') :
|
||||
IsFibAux n' a' b' :=
|
||||
⟨by rw [← hn, fib_two_mul, H.1, H.2, ← h1],
|
||||
by rw [← hn, fib_two_mul_add_one, H.1, H.2, Nat.pow_two, Nat.pow_two, Nat.add_comm, h2]⟩
|
||||
|
||||
theorem fib_two_mul_add_two (n : Nat) :
|
||||
Nat.fib (2 * n + 2) = Nat.fib (n + 1) * (2 * Nat.fib n + Nat.fib (n + 1)) := by
|
||||
rw [fib_add_two, fib_two_mul, fib_two_mul_add_one]
|
||||
induction n <;> grind [Nat.fib]
|
||||
|
||||
theorem isFibAux_two_mul_add_one {n a b n' a' b' : Nat} (H : IsFibAux n a b)
|
||||
(hn : 2 * n + 1 = n') (h1 : a * a + b * b = a') (h2 : b * (2 * a + b) = b') :
|
||||
IsFibAux n' a' b' :=
|
||||
⟨by rw [← hn, fib_two_mul_add_one, H.1, H.2, Nat.pow_two, Nat.pow_two, Nat.add_comm, h1],
|
||||
by rw [← hn, fib_two_mul_add_two, H.1, H.2, h2]⟩
|
||||
|
||||
theorem isFibAux_two_mul_done {n a b n' a' : Nat} (H : IsFibAux n a b)
|
||||
(hn : 2 * n = n') (h : a * (2 * b - a) = a') : Nat.fib n' = a' :=
|
||||
(isFibAux_two_mul H hn h rfl).1
|
||||
|
||||
theorem isFibAux_two_mul_add_one_done {n a b n' a' : Nat} (H : IsFibAux n a b)
|
||||
(hn : 2 * n + 1 = n') (h : a * a + b * b = a') : Nat.fib n' = a' :=
|
||||
(isFibAux_two_mul_add_one H hn h rfl).1
|
||||
|
||||
namespace FibSimproc
|
||||
|
||||
partial def proveNatFibAux (n : Nat) : Nat × Nat × Expr :=
|
||||
match n with
|
||||
| 0 => ⟨0, 1, mkConst ``isFibAux_zero⟩
|
||||
| 1 => ⟨1, 1, mkConst ``isFibAux_one⟩
|
||||
| n =>
|
||||
let half := n / 2
|
||||
let ⟨a, b, H⟩ := proveNatFibAux half
|
||||
let en := mkNatLit half
|
||||
let ea := mkNatLit a
|
||||
let eb := mkNatLit b
|
||||
if n % 2 == 0 then
|
||||
let a' := a * (2 * b - a)
|
||||
let b' := a * a + b * b
|
||||
let en' := mkNatLit n
|
||||
let ea' := mkNatLit a'
|
||||
let eb' := mkNatLit b'
|
||||
let hn := mkApp2 (mkConst ``Eq.refl [1]) (mkConst ``Nat) en'
|
||||
let h1 := mkApp2 (mkConst ``Eq.refl [1]) (mkConst ``Nat) ea'
|
||||
let h2 := mkApp2 (mkConst ``Eq.refl [1]) (mkConst ``Nat) eb'
|
||||
let proof := mkAppN (mkConst ``isFibAux_two_mul) #[en, ea, eb, en', ea', eb', H, hn, h1, h2]
|
||||
⟨a', b', proof⟩
|
||||
else
|
||||
let a' := a * a + b * b
|
||||
let b' := b * (2 * a + b)
|
||||
let en' := mkNatLit n
|
||||
let ea' := mkNatLit a'
|
||||
let eb' := mkNatLit b'
|
||||
let hn := mkApp2 (mkConst ``Eq.refl [1]) (mkConst ``Nat) en'
|
||||
let h1 := mkApp2 (mkConst ``Eq.refl [1]) (mkConst ``Nat) ea'
|
||||
let h2 := mkApp2 (mkConst ``Eq.refl [1]) (mkConst ``Nat) eb'
|
||||
let proof := mkAppN (mkConst ``isFibAux_two_mul_add_one) #[en, ea, eb, en', ea', eb', H, hn, h1, h2]
|
||||
⟨a', b', proof⟩
|
||||
|
||||
def proveNatFib (n : Nat) : Nat × Expr :=
|
||||
match n with
|
||||
| 0 => ⟨0, mkConst ``Nat.fib_zero⟩
|
||||
| 1 => ⟨1, mkConst ``Nat.fib_one⟩
|
||||
| 2 => ⟨1, mkConst ``Nat.fib_two⟩
|
||||
| n =>
|
||||
let half := n / 2
|
||||
let ⟨a, b, H⟩ := proveNatFibAux half
|
||||
let en := mkNatLit half
|
||||
let ea := mkNatLit a
|
||||
let eb := mkNatLit b
|
||||
if n % 2 == 0 then
|
||||
let result := a * (2 * b - a)
|
||||
let en' := mkNatLit n
|
||||
let eresult := mkNatLit result
|
||||
let hn := mkApp2 (mkConst ``Eq.refl [1]) (mkConst ``Nat) en'
|
||||
let h := mkApp2 (mkConst ``Eq.refl [1]) (mkConst ``Nat) eresult
|
||||
let proof := mkAppN (mkConst ``isFibAux_two_mul_done) #[en, ea, eb, en', eresult, H, hn, h]
|
||||
⟨result, proof⟩
|
||||
else
|
||||
let result := a * a + b * b
|
||||
let en' := mkNatLit n
|
||||
let eresult := mkNatLit result
|
||||
let hn := mkApp2 (mkConst ``Eq.refl [1]) (mkConst ``Nat) en'
|
||||
let h := mkApp2 (mkConst ``Eq.refl [1]) (mkConst ``Nat) eresult
|
||||
let proof := mkAppN (mkConst ``isFibAux_two_mul_add_one_done) #[en, ea, eb, en', eresult, H, hn, h]
|
||||
⟨result, proof⟩
|
||||
|
||||
end FibSimproc
|
||||
|
||||
section
|
||||
cbv_simproc cbv_eval evalFib (Nat.fib _) := fun e => do
|
||||
let_expr Nat.fib arg := e | return .rfl
|
||||
let some n := Sym.getNatValue? arg | return .rfl
|
||||
let ⟨result, proof⟩ := FibSimproc.proveNatFib n
|
||||
let resultExpr ← Sym.share (mkNatLit result)
|
||||
return .step resultExpr proof (done := true)
|
||||
|
||||
theorem fib_0 : Nat.fib 0 = 0 := by cbv
|
||||
theorem fib_1 : Nat.fib 1 = 1 := by cbv
|
||||
theorem fib_2 : Nat.fib 2 = 1 := by cbv
|
||||
theorem fib_10 : Nat.fib 10 = 55 := by cbv
|
||||
theorem fib_20 : Nat.fib 20 = 6765 := by cbv
|
||||
theorem fib_30 : Nat.fib 30 = 832040 := by cbv
|
||||
theorem fib_50 : Nat.fib 50 = 12586269025 := by cbv
|
||||
theorem fib_100 : Nat.fib 100 = 354224848179261915075 := by cbv
|
||||
|
||||
theorem fib_sum : Nat.fib 10 + Nat.fib 20 = 6820 := by cbv
|
||||
|
||||
/--
|
||||
info: theorem fib_sum : Nat.fib 10 + Nat.fib 20 = 6820 :=
|
||||
of_eq_true
|
||||
(Eq.trans
|
||||
(congrFun'
|
||||
(congrArg Eq
|
||||
(Eq.trans
|
||||
(congr
|
||||
(congrArg HAdd.hAdd
|
||||
(isFibAux_two_mul_done
|
||||
(isFibAux_two_mul_add_one (isFibAux_two_mul isFibAux_one (Eq.refl 2) (Eq.refl 1) (Eq.refl 2))
|
||||
(Eq.refl 5) (Eq.refl 5) (Eq.refl 8))
|
||||
(Eq.refl 10) (Eq.refl 55)))
|
||||
(isFibAux_two_mul_done
|
||||
(isFibAux_two_mul
|
||||
(isFibAux_two_mul_add_one (isFibAux_two_mul isFibAux_one (Eq.refl 2) (Eq.refl 1) (Eq.refl 2))
|
||||
(Eq.refl 5) (Eq.refl 5) (Eq.refl 8))
|
||||
(Eq.refl 10) (Eq.refl 55) (Eq.refl 89))
|
||||
(Eq.refl 20) (Eq.refl 6765)))
|
||||
(Eq.refl 6820)))
|
||||
6820)
|
||||
(eq_self 6820))
|
||||
-/
|
||||
#guard_msgs in
|
||||
#print fib_sum
|
||||
|
||||
end
|
||||
@@ -1 +1 @@
|
||||
versoDocMissing.lean:10:0: error: unexpected end of input; expected '#guard_msgs', 'abbrev', 'add_decl_doc', 'axiom', 'binder_predicate', 'builtin_dsimproc', 'builtin_dsimproc_decl', 'builtin_grind_propagator', 'builtin_initialize', 'builtin_simproc', 'builtin_simproc_decl', 'class', 'coinductive', 'declare_simp_like_tactic', 'declare_syntax_cat', 'def', 'dsimproc', 'dsimproc_decl', 'elab', 'elab_rules', 'example', 'grind_propagator', 'inductive', 'infix', 'infixl', 'infixr', 'initialize', 'instance', 'macro', 'macro_rules', 'notation', 'opaque', 'postfix', 'prefix', 'recommended_spelling', 'register_error_explanation', 'register_tactic_tag', 'register_try?_tactic', 'simproc', 'simproc_decl', 'structure', 'syntax', 'tactic_extension', 'theorem' or 'unif_hint'
|
||||
versoDocMissing.lean:10:0: error: unexpected end of input; expected '#guard_msgs', 'abbrev', 'add_decl_doc', 'axiom', 'binder_predicate', 'builtin_cbv_simproc', 'builtin_cbv_simproc_decl', 'builtin_dsimproc', 'builtin_dsimproc_decl', 'builtin_grind_propagator', 'builtin_initialize', 'builtin_simproc', 'builtin_simproc_decl', 'cbv_simproc', 'cbv_simproc_decl', 'class', 'coinductive', 'declare_simp_like_tactic', 'declare_syntax_cat', 'def', 'dsimproc', 'dsimproc_decl', 'elab', 'elab_rules', 'example', 'grind_propagator', 'inductive', 'infix', 'infixl', 'infixr', 'initialize', 'instance', 'macro', 'macro_rules', 'notation', 'opaque', 'postfix', 'prefix', 'recommended_spelling', 'register_error_explanation', 'register_tactic_tag', 'register_try?_tactic', 'simproc', 'simproc_decl', 'structure', 'syntax', 'tactic_extension', 'theorem' or 'unif_hint'
|
||||
|
||||
Reference in New Issue
Block a user