From cc68bd476be125a767620bd4ba87889f99f1652a Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Fri, 14 Aug 2026 23:29:21 -0400 Subject: [PATCH 01/38] v1 start --- Mathlib.lean | 21 ++ Mathlib/Lean/Meta/DiscrTreeExt.lean | 73 +++++ Mathlib/Tactic.lean | 20 ++ Mathlib/Tactic/Inclusion/Core/Core.lean | 85 ++++++ Mathlib/Tactic/Inclusion/Core/Elab.lean | 110 +++++++ Mathlib/Tactic/Inclusion/Core/Expr.lean | 126 ++++++++ Mathlib/Tactic/Inclusion/Core/Extensions.lean | 151 +++++++++ Mathlib/Tactic/Inclusion/Core/Inclusion.lean | 145 +++++++++ Mathlib/Tactic/Inclusion/Core/ToSet.lean | 146 +++++++++ Mathlib/Tactic/Inclusion/Core/Types.lean | 166 ++++++++++ .../Inclusion/Extension/Core/Family.lean | 23 ++ .../Inclusion/Extension/Core/Hypotheses.lean | 38 +++ .../Inclusion/Extension/DyadicReal/Basic.lean | 226 ++++++++++++++ .../Extension/DyadicReal/Dyadic.lean | 31 ++ .../Extension/DyadicReal/Extensions.lean | 39 +++ .../Extension/DyadicReal/Family.lean | 22 ++ .../Extension/DyadicReal/Hypotheses.lean | 90 ++++++ .../Extension/DyadicReal/Splitting.lean | 82 +++++ .../Inclusion/Extension/Extensions.lean | 16 + .../Tactic/Inclusion/Extension/Interval.lean | 154 ++++++++++ .../Tactic/Inclusion/Extension/Splitter.lean | 16 + .../Tactic/Inclusion/ExtensionAPI/Attr.lean | 287 ++++++++++++++++++ .../Tactic/Inclusion/ExtensionAPI/Basic.lean | 254 ++++++++++++++++ MathlibTest/Tactic/Inclusion/Basic.lean | 48 +++ MathlibTest/Tactic/Inclusion/Splitting.lean | 32 ++ 25 files changed, 2401 insertions(+) create mode 100644 Mathlib/Lean/Meta/DiscrTreeExt.lean create mode 100644 Mathlib/Tactic/Inclusion/Core/Core.lean create mode 100644 Mathlib/Tactic/Inclusion/Core/Elab.lean create mode 100644 Mathlib/Tactic/Inclusion/Core/Expr.lean create mode 100644 Mathlib/Tactic/Inclusion/Core/Extensions.lean create mode 100644 Mathlib/Tactic/Inclusion/Core/Inclusion.lean create mode 100644 Mathlib/Tactic/Inclusion/Core/ToSet.lean create mode 100644 Mathlib/Tactic/Inclusion/Core/Types.lean create mode 100644 Mathlib/Tactic/Inclusion/Extension/Core/Family.lean create mode 100644 Mathlib/Tactic/Inclusion/Extension/Core/Hypotheses.lean create mode 100644 Mathlib/Tactic/Inclusion/Extension/DyadicReal/Basic.lean create mode 100644 Mathlib/Tactic/Inclusion/Extension/DyadicReal/Dyadic.lean create mode 100644 Mathlib/Tactic/Inclusion/Extension/DyadicReal/Extensions.lean create mode 100644 Mathlib/Tactic/Inclusion/Extension/DyadicReal/Family.lean create mode 100644 Mathlib/Tactic/Inclusion/Extension/DyadicReal/Hypotheses.lean create mode 100644 Mathlib/Tactic/Inclusion/Extension/DyadicReal/Splitting.lean create mode 100644 Mathlib/Tactic/Inclusion/Extension/Extensions.lean create mode 100644 Mathlib/Tactic/Inclusion/Extension/Interval.lean create mode 100644 Mathlib/Tactic/Inclusion/Extension/Splitter.lean create mode 100644 Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean create mode 100644 Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean create mode 100644 MathlibTest/Tactic/Inclusion/Basic.lean create mode 100644 MathlibTest/Tactic/Inclusion/Splitting.lean diff --git a/Mathlib.lean b/Mathlib.lean index 3076dcb5539486..6064e4f3c00ff7 100644 --- a/Mathlib.lean +++ b/Mathlib.lean @@ -4921,6 +4921,7 @@ public import Mathlib.Lean.Meta public import Mathlib.Lean.Meta.Basic public import Mathlib.Lean.Meta.CongrTheorems public import Mathlib.Lean.Meta.DiscrTree +public import Mathlib.Lean.Meta.DiscrTreeExt public import Mathlib.Lean.Meta.KAbstractPositions public import Mathlib.Lean.Meta.RefinedDiscrTree public import Mathlib.Lean.Meta.RefinedDiscrTree.Basic @@ -7363,6 +7364,26 @@ public import Mathlib.Tactic.HaveI public import Mathlib.Tactic.HigherOrder public import Mathlib.Tactic.Hint public import Mathlib.Tactic.ITauto +public import Mathlib.Tactic.Inclusion.Core.Core +public import Mathlib.Tactic.Inclusion.Core.Elab +public import Mathlib.Tactic.Inclusion.Core.Expr +public import Mathlib.Tactic.Inclusion.Core.Extensions +public import Mathlib.Tactic.Inclusion.Core.Inclusion +public import Mathlib.Tactic.Inclusion.Core.ToSet +public import Mathlib.Tactic.Inclusion.Core.Types +public import Mathlib.Tactic.Inclusion.Extension.Core.Family +public import Mathlib.Tactic.Inclusion.Extension.Core.Hypotheses +public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Basic +public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Dyadic +public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Extensions +public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Family +public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Hypotheses +public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Splitting +public import Mathlib.Tactic.Inclusion.Extension.Extensions +public import Mathlib.Tactic.Inclusion.Extension.Interval +public import Mathlib.Tactic.Inclusion.Extension.Splitter +public import Mathlib.Tactic.Inclusion.ExtensionAPI.Attr +public import Mathlib.Tactic.Inclusion.ExtensionAPI.Basic public import Mathlib.Tactic.InferParam public import Mathlib.Tactic.Inhabit public import Mathlib.Tactic.IntervalCases diff --git a/Mathlib/Lean/Meta/DiscrTreeExt.lean b/Mathlib/Lean/Meta/DiscrTreeExt.lean new file mode 100644 index 00000000000000..db513e986b4cfe --- /dev/null +++ b/Mathlib/Lean/Meta/DiscrTreeExt.lean @@ -0,0 +1,73 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public import Mathlib.Init +public meta import Lean.Elab.Term.TermElabM +public meta import Lean.Meta.DiscrTree + +/-! +# Discrimination-tree-indexed environment extensions + +This file provides an API for scoped environment extensions whose declarations are indexed by +elaborated expression patterns in a `DiscrTree`. +-/ + +public meta section + +open Lean Elab Term Lean.Meta + +namespace DiscrTreeExt + +/-- Evaluate `declName` as a value of type `α`, checking that its Lean type is `typeName`. -/ +def evalDecl (α : Type) (typeName declName : Name) : ImportM α := do + let { env, opts, .. } ← read + IO.ofExcept <| unsafe env.evalConstCheck α opts typeName declName + +/-- The discrimination-tree paths and declaration name stored in an `.olean` entry. -/ +abbrev Entry := Array (Array DiscrTree.Key) × Name + +/-- The state of a discrimination-tree environment extension. -/ +structure State (α : Type) where + /-- The discrimintation-tree of the extension. -/ + tree : DiscrTree α := {} + deriving Inhabited + +/-- A scoped environment extension containing declaration values indexed by expression patterns. -/ +abbrev EnvExt (α : Type) := ScopedEnvExtension Entry (Entry × α) (State α) + +variable {α : Type} + +/-- Return an the array of the declaration values whose `DiscrTree` key matches `e`. -/ +def State.getMatch (state : State α) (e : Expr) : MetaM (Array α) := state.tree.getMatch e + +/-- Create a scoped environment extension whose declarations have type `typeName`. By default, the +environment extension is named after the declaration in which this function is called. -/ +def initializeEnvExt (typeName : Name) + (envExtName : Name := by exact decl_name%) : IO (EnvExt α) := do + -- we only need this to deduplicate entries in the DiscrTree + have : BEq α := ⟨fun _ _ ↦ false⟩ + let insert kss v dt := kss.foldl (fun dt ks ↦ dt.insertKeyValue ks v) dt + registerScopedEnvExtension { + name := envExtName + mkInitial := pure {} + ofOLeanEntry := fun _ e@(_, n) ↦ return (e, ← evalDecl α typeName n) + toOLeanEntry := (·.1) + addEntry := fun state ((kss, _), ext) ↦ + { tree := insert kss ext state.tree } + } + +/-- Elaborate expression patterns into `DiscrTree` paths. -/ +def elabExtKeys (patterns : Array Syntax) : CoreM (Array (Array DiscrTree.Key)) := + MetaM.run' <| patterns.mapM fun stx => do + let e ← TermElabM.run' <| withSaveInfoContext <| withAutoBoundImplicit <| + withReader ({ · with ignoreTCFailures := true }) do + let e ← elabTerm stx none + let (_, _, e) ← lambdaMetaTelescope (← mkLambdaFVars (← getLCtx).getFVars e) + return e + DiscrTree.mkPath e + +end DiscrTreeExt diff --git a/Mathlib/Tactic.lean b/Mathlib/Tactic.lean index 6e239fb555cc45..a02fb203d9ec5a 100644 --- a/Mathlib/Tactic.lean +++ b/Mathlib/Tactic.lean @@ -154,6 +154,26 @@ public import Mathlib.Tactic.HaveI public import Mathlib.Tactic.HigherOrder public import Mathlib.Tactic.Hint public import Mathlib.Tactic.ITauto +public import Mathlib.Tactic.Inclusion.Core.Core +public import Mathlib.Tactic.Inclusion.Core.Elab +public import Mathlib.Tactic.Inclusion.Core.Expr +public import Mathlib.Tactic.Inclusion.Core.Extensions +public import Mathlib.Tactic.Inclusion.Core.Inclusion +public import Mathlib.Tactic.Inclusion.Core.ToSet +public import Mathlib.Tactic.Inclusion.Core.Types +public import Mathlib.Tactic.Inclusion.Extension.Core.Family +public import Mathlib.Tactic.Inclusion.Extension.Core.Hypotheses +public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Basic +public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Dyadic +public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Extensions +public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Family +public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Hypotheses +public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Splitting +public import Mathlib.Tactic.Inclusion.Extension.Extensions +public import Mathlib.Tactic.Inclusion.Extension.Interval +public import Mathlib.Tactic.Inclusion.Extension.Splitter +public import Mathlib.Tactic.Inclusion.ExtensionAPI.Attr +public import Mathlib.Tactic.Inclusion.ExtensionAPI.Basic public import Mathlib.Tactic.InferParam public import Mathlib.Tactic.Inhabit public import Mathlib.Tactic.IntervalCases diff --git a/Mathlib/Tactic/Inclusion/Core/Core.lean b/Mathlib/Tactic/Inclusion/Core/Core.lean new file mode 100644 index 00000000000000..046ede27c20fdb --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Core/Core.lean @@ -0,0 +1,85 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public meta import Mathlib.Tactic.Inclusion.Core.Inclusion +public meta import Lean.Meta.Native + +/-! +# Core implementation of the `inclusion` tactic + +This file defines the `TacticM` core of the `inclusion` tactic. +-/ + +@[expose] public meta section + +open Lean Meta + +namespace Inclusion + +/-- Configuration for the `inclusion` tactic. -/ +structure InclusionConfig where + /-- If `kernel == true` then skip the compiled check. -/ + kernel : Bool := false + /-- If `native == true` then use compiled computation in the proof (warning: this adds the lean + compiler to the trusted codebase). -/ + native : Bool := false + /-- A map from inclusion parameter names to their user-supplied values. -/ + paramSettings : NameMap Expr := {} + /-- The names of the enabled inclusion extension families. -/ + families : Array Name := #[] + +/-- Compile and evaluate the closed `IntervalBool` expression `inclusionExpr`. -/ +def compileInclusionCheck (inclusionExpr : Expr) : MetaM IntervalBool := + unsafe evalExpr IntervalBool (mkConst ``IntervalBool) inclusionExpr + +/-- Check that `inclusionExpr` equals `IntervalBool.true` using a compiled computation, and then +pass the proof term to the kernel (where it will be verified again by reflection). -/ +def mkInclusionTrueProof (inclusionExpr : Expr) : MetaM Expr := do + match ← compileInclusionCheck inclusionExpr with + | .true => return mkIntervalBoolRefl inclusionExpr + | .false => throwError "The proposition is provably false" + | .undetermined => throwError "The proposition was not proven true or false." + +/-- Prove that `inclusionExpr` equals `IntervalBool.true` using kernel reduction +(without any prior compiled check). -/ +def mkKernelInclusionTrueProof (inclusionExpr : Expr) : MetaM Expr := do + let expectedType ← mkEq inclusionExpr (mkConst ``IntervalBool.true) + let lemmaLevels := (collectLevelParams {} expectedType).params.toList + try + let lemmaName ← withOptions (Elab.async.set · false) do + mkAuxLemma lemmaLevels expectedType (mkIntervalBoolRefl inclusionExpr) + return mkConst lemmaName (lemmaLevels.map .param) + catch _ => + throwError "The kernel failed to verify the proposition." + +/-- Use native evaluation to prove that `inclusionExpr` equals `IntervalBool.true`. -/ +def mkNativeInclusionTrueProof (inclusionExpr : Expr) : MetaM Expr := do + let result := mkApp (mkConst ``IntervalBool.isTrue) inclusionExpr + match ← nativeEqTrue `inclusion result (axiomDeclRange? := (← getRef)) with + | .success proof => mkAppM ``IntervalBool.eq_true_of_isTrue_eq_true #[proof] + | .notTrue => throwError "Native computation could not verify the proposition." + +/-- Prove `goal`, by constructing an `exprInclusion` for it and verifying that +`exprInclusion.inclusion` evaluates to `IntervalBool.true`. -/ +def inclusionCore (goal : Expr) (config : InclusionConfig) : MetaM Expr := do + if config.kernel && config.native then + throwError "Cannot simultaneously enable +kernel and +native" + let goal ← instantiateMVars goal + unless ← isProp goal do + throwError "The goal is not a proposition" + let exprInclusion ← (toExprInclusion goal).run config.paramSettings config.families + let inclusionExpr := exprInclusion.inclusion + let inclusionProof ← + if config.native then + mkNativeInclusionTrueProof inclusionExpr + else if config.kernel then + mkKernelInclusionTrueProof inclusionExpr + else + mkInclusionTrueProof inclusionExpr + return exprInclusion.mkGoalProof goal inclusionProof + +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Core/Elab.lean b/Mathlib/Tactic/Inclusion/Core/Elab.lean new file mode 100644 index 00000000000000..33229333d29f73 --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Core/Elab.lean @@ -0,0 +1,110 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public meta import Mathlib.Tactic.Inclusion.Core.Core +meta import Lean.Elab.ConfigEval +meta import Mathlib.Tactic.Linter.UnusedTacticExtension + +/-! +# Elaboration of the `inclusion` tactic + +This file defines the syntax and elaborator for the `inclusion` tactic. +-/ + +public meta section + +open Lean Meta Elab Tactic +open Lean.Parser.Tactic + +namespace Inclusion + +/-- Configuration elaborator for `inclusion`; families and parameter values use custom syntax. -/ +declare_config_elab elabInclusionConfig InclusionConfig where + omit paramSettings, families + +/-- Syntax for assigning an inclusion parameter. -/ +declare_syntax_cat inclusionParam + +syntax ident " := " term : inclusionParam + +/-- Elaborate and validate the list of inclusion families enabled by a tactic invocation. -/ +def elabInclusionFamilies (config : InclusionConfig) (familyStxs : Array Syntax) : + TacticM InclusionConfig := do + if familyStxs.isEmpty then + throwError "At least one inclusion family must be specified" + let mut families := #[] + for familyStx in familyStxs do + let family := familyStx.getId + unless (← getInclusionFamily? family).isSome do + throwError "Unknown inclusion family '{family}'" + if families.contains family then + throwError "Inclusion family '{family}' was enabled more than once" + families := families.push family + return { config with families } + +/-- Elaborate an inclusion-parameter value against its registered type. -/ +private def elabParamTerm (stx : Syntax) (expectedType : Expr) : TacticM Expr := do + let value ← elabTerm stx expectedType + Term.synthesizeSyntheticMVarsNoPostponing + instantiateMVars value + +/-- Elaborate inclusion parameters. -/ +def elabInclusionParams (config : InclusionConfig) (paramStxs : Array Syntax) : + TacticM InclusionConfig := do + let mut config := config + let registeredParams := inclusionParamExt.getState (← getEnv) + for paramStx in paramStxs do + let (name, valueStx) ← match paramStx with + | `(inclusionParam| $name:ident := $value:term) => + pure (name.getId, value) + | _ => throwUnsupportedSyntax + let some decl := registeredParams.find? name + | throwError "Unknown inclusion parameter '{name}'" + let value ← elabParamTerm valueStx decl.type + if config.paramSettings.contains name then + throwError "Inclusion parameter '{name}' was specified more than once" + config := { config with paramSettings := config.paramSettings.insert name value } + return config + +syntax (name := inclusionTacStx) "inclusion" optConfig " [" ident,* "]" + (" (" inclusionParam,* ")")? : tactic + +/-- `inclusion` tactic for proving "inclusion" propositions. -/ +@[tactic inclusionTacStx] +def inclusionTac : Tactic + | `(tactic| inclusion $cfg:optConfig [$families,*] $[($paramStxs,*)]?) => do + let config ← elabInclusionConfig cfg + let config ← elabInclusionFamilies config families.getElems + let params := paramStxs.map (·.getElems) |>.getD #[] + let config ← elabInclusionParams config params + closeMainGoalUsing `inclusion fun goal _ => inclusionCore goal config + | _ => throwUnsupportedSyntax + +syntax (name := inclusion?TacStx) "inclusion?" optConfig " [" ident,* "]" + (" (" inclusionParam,* ")")? : tactic + +/-- Tactic for quickly checking if the `inclusion` tactic will succeed. -/ +@[tactic inclusion?TacStx] +def inclusion?Tac : Tactic + | `(tactic| inclusion? $cfg:optConfig [$families,*] $[($paramStxs,*)]?) => do + let config ← elabInclusionConfig cfg + let config ← elabInclusionFamilies config families.getElems + let params := paramStxs.map (·.getElems) |>.getD #[] + let config ← elabInclusionParams config params + withoutModifyingStateWithInfoAndMessages <| withMainContext do + try + discard <| inclusionCore (← getMainTarget) + { config with kernel := false, native := false } + logInfo "The inclusion check succeeded." + catch err => + logInfo m!"The inclusion check failed:\n{err.toMessageData}" + | _ => throwUnsupportedSyntax + +initialize + Mathlib.Linter.UnusedTactic.allowedRef.modify (·.insert `Inclusion.inclusion?TacStx) + +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Core/Expr.lean b/Mathlib/Tactic/Inclusion/Core/Expr.lean new file mode 100644 index 00000000000000..b2d72ef5f79691 --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Core/Expr.lean @@ -0,0 +1,126 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public import Mathlib.Tactic.Inclusion.Core.ToSet +public meta import Mathlib.Tactic.Inclusion.Core.Types + +/-! +# Expr helpers for the `inclusion` tactic + +This file defines helpers for matching or building certain expressions that are used in the +core of the `inclusion` tactic. +-/ + +public meta section + +open Lean Meta + +namespace Inclusion + +/-- If `e` is an `Expr` of the form `x ∈ s` using a `ToSet` instance, return +`some (x, s, toSetInst)`. -/ +def toSetMem? (e : Expr) : Option (Expr × Expr × Expr) := do + let (``Membership.mem, #[_, _, membershipInst, s, x]) := e.getAppFnArgs | none + let (``instMembershipOfToSet, #[_, _, toSetInst]) := membershipInst.getAppFnArgs | none + return (x, s, toSetInst) + +/-- Given expressions `x : xType`, `s : setType`, and `toSetInst : ToSet setType xType`, create +the expression `x ∈ s`. -/ +def mkToSetMem (xType setType x s toSetInst : Expr) : MetaM Expr := do + let membershipInst ← mkAppOptM ``instMembershipOfToSet #[setType, xType, toSetInst] + mkAppOptM ``Membership.mem #[xType, setType, membershipInst, s, x] + +/-- Given `iExpr : IExpr` and `set : iExpr.iType.setType`, create the expression +`iExpr.expr ∈ set`. -/ +def IExpr.mkMem (iExpr : IExpr) (set : Expr) : MetaM Expr := + mkToSetMem iExpr.iType.elemType iExpr.iType.setType iExpr.expr set iExpr.iType.toSetInst + +/-- Given + +· `source : iVar.type.setType`, +· `outputType : IType`, +· `cover : Cover iVar.type.setType iVar.type.elemType`, +· `coarsen : Coarsen outputType.setType outputType.elemType`, and +· `inclusion : iVar.type.setType → outputType.setType`, + +create the expression `cover.coverMap source inclusion : outputType.setType`. -/ +def IVar.mkCoverMap (iVar : IVar) (outputType : IType) + (source cover coarsen inclusion : Expr) : MetaM Expr := + mkAppOptM ``Cover.coverMap + #[iVar.type.setType, iVar.type.elemType, iVar.type.toSetInst, cover, + outputType.setType, outputType.elemType, outputType.toSetInst, coarsen, + source, inclusion] + +/-- Given + +· a source inclusion body for `iVar`, +· `output : IExpr`, +· `cover : Cover iVar.type.setType iVar.type.elemType`, +· `coarsen : Coarsen output.iType.setType output.iType.elemType`, +· `inclusion : iVar.type.setType → output.iType.setType`, and +· `proof : ∀ s, iVar.expr ∈ s → output.expr ∈ inclusion s`, + +create a proof of `output.expr ∈ cover.coverMap source.inclusionBody inclusion`. -/ +def IVar.mkCoverMapProof (iVar : IVar) (output : IExpr) + (source : ExprInclusionBody) (cover coarsen inclusion proof : Expr) : MetaM Expr := do + let outputLevel ← getDecLevel output.iType.setType + let setLevel ← getDecLevel iVar.type.setType + let elemLevel ← getDecLevel iVar.type.elemType + return mkAppN (mkConst ``Cover.mem_coverMap [outputLevel, setLevel, elemLevel]) + #[iVar.type.setType, iVar.type.elemType, iVar.type.toSetInst, cover, + output.iType.setType, output.iType.elemType, output.iType.toSetInst, coarsen, + source.inclusionBody, inclusion, iVar.expr, output.expr, source.proofBody, proof] + +/-- Given `iType : IType`, synthesize an expression of type +`Coarsen iType.setType iType.elemType`. -/ +def IType.synthCoarsen (iType : IType) : MetaM Expr := do + let type ← mkAppOptM ``Coarsen #[iType.setType, iType.elemType, iType.toSetInst] + try synthInstance type catch _ => + throwError "No `Coarsen` instance is registered for {iType.setType}" + +/-- Given `iType : IType`, `refiner : Refine iType.setType iType.elemType`, and expressions +`left right : iType.setType`, create the expression `refiner.refine left right`. -/ +def IType.mkRefine (iType : IType) (refiner left right : Expr) : MetaM Expr := + mkAppOptM ``Refine.refine #[iType.setType, iType.elemType, iType.toSetInst, refiner, left, right] + +/-- Given `iType : IType`, synthesize an expression of type +`Refine iType.setType iType.elemType`. -/ +def IType.synthRefine (iType : IType) : MetaM Expr := do + let type ← mkAppOptM ``Refine #[iType.setType, iType.elemType, iType.toSetInst] + try synthInstance type catch _ => + throwError "No `Refine` instance is registered for {iType.setType}" + +/-- Given `iType : IType` and `univ : Univ iType.setType iType.elemType`, create the expression +`univ.univ : iType.setType`. -/ +def IType.mkUniv (iType : IType) (univ : Expr) : MetaM Expr := + mkAppOptM ``Univ.univ #[iType.setType, iType.elemType, iType.toSetInst, univ] + +/-- Given `iExpr : IExpr` and `univ : Univ iExpr.iType.setType iExpr.iType.elemType`, create a +proof of `iExpr.expr ∈ univ.univ`. -/ +def IExpr.mkMemUniv (iExpr : IExpr) (univ : Expr) : MetaM Expr := do + let setLevel ← getDecLevel iExpr.iType.setType + let elemLevel ← getDecLevel iExpr.iType.elemType + return mkAppN (mkConst ``Univ.mem_univ [setLevel, elemLevel]) + #[iExpr.iType.setType, iExpr.iType.elemType, iExpr.iType.toSetInst, univ, iExpr.expr] + +/-- Given `iType : IType`, synthesize an expression of type `Univ iType.setType iType.elemType`. -/ +def IType.synthUniv (iType : IType) : MetaM Expr := do + let type ← mkAppOptM ``Univ #[iType.setType, iType.elemType, iType.toSetInst] + try synthInstance type catch _ => + throwError "No `Univ` instance is registered for {iType.setType}" + +/-- Given an expression `b : IntervalBool`, create the expression proving `b = b`. -/ +def mkIntervalBoolRefl (b : Expr) : Expr := + mkApp2 (mkConst ``Eq.refl [.succ .zero]) (mkConst ``IntervalBool) b + +/-- Given an `ExprInclusion` `inc` for `goal`, and a proof +`inclusionProof : inc.inclusion = IntervalBool.true` create a proof of `goal`. -/ +def ExprInclusion.mkGoalProof (inc : ExprInclusion) (goal inclusionProof : Expr) : Expr := + mkAppN (mkConst ``true_of_mem_intervalBool_eq_true) + #[goal, inc.inclusion, inc.proof, inclusionProof] + +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Core/Extensions.lean b/Mathlib/Tactic/Inclusion/Core/Extensions.lean new file mode 100644 index 00000000000000..c68f77f05245c6 --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Core/Extensions.lean @@ -0,0 +1,151 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public meta import Mathlib.Tactic.Inclusion.Core.Types +public meta import Mathlib.Lean.Meta.DiscrTreeExt + +/-! +# Environment extensions for the `inclusion` tactic + +This file defines the environment extensions used in the `inclusion` tactic. +-/ + +public meta section + +open Lean Meta DiscrTreeExt + +namespace Inclusion + +/-- An extension used by the `inclusion` tactic to construct `ExprInclusionBody`s. -/ +structure InclusionExt where + /-- Name of the declaration of the extension. -/ + declName : Name := by exact decl_name% + /-- User-facing name of the extension. -/ + userName : Name := by exact decl_name% + /-- Attempt to construct an `ExprInclusionBody` for `e`. -/ + derive (e : Expr) : InclusionM ExprInclusionBody + /-- The priority of the extension. Extensions with higher priority are tried first. -/ + priority : Nat := eval_prio default + +/-- An extension used by the `inclusion` tactic to construct inclusion hypotheses from local +declarations. -/ +structure HypothesisExt where + /-- Name of the declaration of the extension. -/ + declName : Name := by exact decl_name% + /-- User-facing name of the extension. -/ + userName : Name := by exact decl_name% + /-- Attempt to construct inclusion hypotheses from `h`. -/ + derive (h : Expr) : HypothesisM Unit + /-- The priority of the extension. Extensions with higher priority are tried first. -/ + priority : Nat := eval_prio default + +/-- A named family of inclusion and hypothesis extensions. -/ +structure InclusionFamily where + /-- The name of an inclusion family. -/ + name : Name + /-- The `DiscrTree` key indexed family of inclusion extensions. -/ + inclusionExt : EnvExt InclusionExt + /-- The `DiscrTree` key indexed family of hypothesis extensions. -/ + hypothesisExt : EnvExt HypothesisExt + deriving Nonempty + +/-- A map from family names to registered inclusion families. -/ +abbrev InclusionFamilies := Std.HashMap Name InclusionFamily + +initialize inclusionFamiliesRef : IO.Ref InclusionFamilies ← IO.mkRef {} + +/-- Register an inclusion family containing a separate inclusion and hypothesis extension. -/ +def registerInclusionFamily (name : Name) (ref : Name := by exact decl_name%) : + IO InclusionFamily := do + if (← inclusionFamiliesRef.get).contains name then + throw <| IO.userError s!"Inclusion family '{name}' is already registered" + let inclusionExt ← initializeEnvExt ``InclusionExt (ref.str "inclusionExt") + let hypothesisExt ← initializeEnvExt ``HypothesisExt (ref.str "hypothesisExt") + let family := { name, inclusionExt, hypothesisExt } + inclusionFamiliesRef.modify (·.insert name family) + return family + +/-- If `name` is the name of an `InclusionFamily` `family` then return `some family`, +otherwise return `none`. -/ +def getInclusionFamily? (name : Name) : CoreM (Option InclusionFamily) := do + let family? := (← inclusionFamiliesRef.get)[name]? + if let some family := family? then + recordExtraModUseFromDecl (isMeta := true) family.inclusionExt.ext.name + return family? + +/-- Return the registered inclusion family named `name`, or fail if it is not registered. -/ +def getInclusionFamily (name : Name) : CoreM InclusionFamily := do + let some family ← getInclusionFamily? name + | throwError "Unknown inclusion family '{name}'" + return family + +/-- Return an array of the inclusion extensions in `families` whose `DiscrTree` key matches `e`, +sorted in order of highest to lowest priority. -/ +def getInclusionExtMatches (families : Array Name) (e : Expr) : + MetaM (Array (Name × InclusionExt)) := do + let env ← getEnv + let mut matched := #[] + for familyName in families do + let family ← getInclusionFamily familyName + for ext in ← family.inclusionExt.getState env |>.getMatch e do + matched := matched.push (familyName, ext) + return matched.qsort fun (_, a) (_, b) => a.priority > b.priority + +/-- Return an array of the hypothesis extensions in `families` whose `DiscrTree` key matches `e`, +sorted in order of highest to lowest priority. -/ +def getHypothesisExtMatches (families : Array Name) (e : Expr) : + MetaM (Array (Name × HypothesisExt)) := do + let env ← getEnv + let mut matched := #[] + for familyName in families do + let family ← getInclusionFamily familyName + for ext in ← family.hypothesisExt.getState env |>.getMatch e do + matched := matched.push (familyName, ext) + return matched.qsort fun (_, a) (_, b) => a.priority > b.priority + +section InclusionParam + +/-- A registered, named parameter, that can be set by the user and used across inclusion and +hypothesis extensions. -/ +structure InclusionParamDecl where + /-- The name of the parameter. -/ + name : Name + /-- The type of the parameter (as an expression). -/ + type : Expr + /-- The default value of the parameter (if `some`). -/ + defaultValue? : Option Expr := none + +/-- The collection of registered inclusion parameters, indexed by name. -/ +structure InclusionParams where + /-- Map from names to their inclusion parameter declaration. -/ + decls : NameMap InclusionParamDecl := {} + deriving Inhabited + +/-- If `name` is the name of an `InclusionParamDecl` `param` then return `some param`, +otherwise return `none`. -/ +def InclusionParams.find? (params : InclusionParams) (name : Name) : + Option InclusionParamDecl := + params.decls.find? name + +/-- Evaluate the declaration `n` as an `InclusionParamDecl`. -/ +def mkInclusionParamDecl (name : Name) : ImportM InclusionParamDecl := do + let { env, opts, .. } ← read + IO.ofExcept <| unsafe env.evalConstCheck InclusionParamDecl opts ``InclusionParamDecl name + +initialize inclusionParamExt : + ScopedEnvExtension Name (Name × InclusionParamDecl) InclusionParams ← + registerScopedEnvExtension { + mkInitial := pure {} + ofOLeanEntry := fun _ name => return (name, ← mkInclusionParamDecl name) + toOLeanEntry := (·.1) + addEntry := fun state (_, decl) => + { state with decls := state.decls.insert decl.name decl } + } + +end InclusionParam + +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Core/Inclusion.lean b/Mathlib/Tactic/Inclusion/Core/Inclusion.lean new file mode 100644 index 00000000000000..aaf04814e6a821 --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Core/Inclusion.lean @@ -0,0 +1,145 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public meta import Mathlib.Tactic.Inclusion.Core.Expr +public meta import Mathlib.Tactic.Inclusion.Core.Extensions +public meta import Mathlib.Lean.Meta.Basic + +/-! +# Constructing inclusions + +This file defines the two main drivers of the `inclusion` tactic. + +Given an expression `e` + +`mkExprInclusionBody` constructs an `ExprInclusionBody` for `e`, by +(often recursively) matching and applying `InclusionExt`s to `e`. + +then + +`mkExprInclusion (e : Expr)` constructs an `ExprInclusion` for `e` from the +body by constructing inclusion hypotheses for each of the `IVars` in the body +by applying `HypothesisExt`s to local declarations, and then "closing" the body. +-/ + +public meta section + +open Lean Meta Elab Term + +namespace Inclusion + +initialize registerTraceClass `Tactic.inclusion + +/-- Construct an `ExprInclusionBody` for `e`. -/ +def mkExprInclusionBody (e : Expr) : InclusionM ExprInclusionBody := do + if let some iVar := (← get).iVars[e]? then + trace[Tactic.inclusion] "Reusing ivar for {e}" + return iVar.toExprInclusionBody + let savedState ← saveState + let matchedExts ← getInclusionExtMatches (← read).families e + for (family, ext) in matchedExts do + try + let body ← ext.derive e + recordExtraModUseFromDecl (isMeta := true) ext.declName + trace[Tactic.inclusion] "[{family}] {ext.userName} applied to {e}" + return body + catch err => + trace[Tactic.inclusion] + "Failed to apply [{family}] {ext.userName} to {e} : {err.toMessageData}" + restoreState savedState + throwError "No inclusion extension applies to {e}" + +/-- Check that `body.proofBody` is a proof of `e ∈ body.inclusionBody` and infer its `IType`. -/ +def ExprInclusionBody.inferIType (body : ExprInclusionBody) (e : Expr) : MetaM IType := do + let proofBodyType ← inferType body.proofBody + let invalidProof := m!"{proofBodyType} is not a proof of `{e} ∈ {body.inclusionBody}`" + let some (e', s, toSetInst) := toSetMem? proofBodyType | throwError invalidProof + unless ← isDefEq e' e do throwError invalidProof + unless ← isDefEq s body.inclusionBody do throwError invalidProof + return ⟨← inferType e, ← inferType body.inclusionBody, toSetInst⟩ + +/-- Run hypothesis extensions on hypothesis `h`. -/ +def runHypothesisExts (h : Expr) : HypothesisM Unit := do + let type ← instantiateMVars (← inferType h) + let matchedExts ← getHypothesisExtMatches (← read).families type + for (family, ext) in matchedExts do + let saved ← saveState + try + ext.derive h + recordExtraModUseFromDecl (isMeta := true) ext.declName + trace[Tactic.inclusion] "[{family}] {ext.userName} processed {type}" + catch err => + trace[Tactic.inclusion] + "Failed to apply [{family}] {ext.userName} to {type} : {err.toMessageData}" + restoreState saved + +/-- Run hypothesis extensions on all declarations in the local context. -/ +def collectHyps : HypothesisM Unit := do + let context ← read + if context.iVars.isEmpty then + return () + for ldecl in context.localContext do + unless ldecl.isImplementationDetail do + runHypothesisExts ldecl.toExpr + +/-- Construct the universal inclusion body for `iExpr`. -/ +def mkUniversalHypBody (iExpr : IExpr) : MetaM ExprInclusionBody := do + let univ ← iExpr.iType.synthUniv + return ⟨← iExpr.iType.mkUniv univ, ← iExpr.mkMemUniv univ⟩ + +/-- Combine the candidate hypothesis bodies for `iExpr` using `Refine`, or use its `Univ` +instance when there are no candidates. -/ +def combineHypBodies (iExpr : IExpr) (bodies : Array ExprInclusionBody) : + MetaM ExprInclusionBody := do + if bodies.isEmpty then + return ← mkUniversalHypBody iExpr + let first := bodies[0]! + if bodies.size = 1 then + return first + let refiner ← iExpr.iType.synthRefine + let mut set := first.inclusionBody + let mut proof := first.proofBody + for h : i in [1:bodies.size] do + let next := bodies[i] + set ← iExpr.iType.mkRefine refiner set next.inclusionBody + proof ← mkAppM ``Refine.mem_refine #[proof, next.proofBody] + return ⟨set, proof⟩ + +/-- Given an `output : IExpr` and a `body : ExprInclusionBody`, construct an `ExprInclusion` for +`output.expr` by collecting inclusion hypotheses from the local context and closing the body. -/ +def mkExprInclusion (output : IExpr) (body : ExprInclusionBody) : HypothesisM ExprInclusion := do + collectHyps + let context ← read + let state ← get + let coarsen? ← match context.iVars.any (·.cover.isSome) with + | true => some <$> output.iType.synthCoarsen + | false => pure none + let body ← context.iVars.foldrM (init := body) fun iVar body => do + let hypBody ← combineHypBodies iVar.iExpr (state.inclusions[iVar.expr]?.getD #[]) + let inclusion ← mkLambdaFVars #[iVar.setVar] body.inclusionBody + (binderInfoForMVars := .default) + let proof ← mkLambdaFVars #[iVar.setVar, iVar.hypVar] body.proofBody + (binderInfoForMVars := .default) + match iVar.cover with + | none => + let inclusionBody := mkApp inclusion hypBody.inclusionBody + let proofBody := mkAppN proof #[hypBody.inclusionBody, hypBody.proofBody] + return { inclusionBody, proofBody } + | some cover => + let coarsen := coarsen?.get! + let inclusionBody ← iVar.mkCoverMap output.iType hypBody.inclusionBody cover coarsen inclusion + let proofBody ← iVar.mkCoverMapProof output hypBody cover coarsen inclusion proof + return { inclusionBody, proofBody } + return ⟨output.iType, body.inclusionBody, body.proofBody⟩ + +/-- Construct an `ExprInclusion` for `e`. -/ +def toExprInclusion (e : Expr) : InclusionM ExprInclusion := do + let body ← mkExprInclusionBody e + let iType ← body.inferIType e + HypothesisM.run <| mkExprInclusion ⟨iType, e⟩ body + +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Core/ToSet.lean b/Mathlib/Tactic/Inclusion/Core/ToSet.lean new file mode 100644 index 00000000000000..660d22d9d30ecb --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Core/ToSet.lean @@ -0,0 +1,146 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public import Mathlib.Data.Set.Insert + +/-! +# Definition of `ToSet` and basic API + +This file defines the `ToSet` class and its API needed for the `inclusion` tactic. + +-/ + +@[expose] public section + +namespace Inclusion + +/-- A `ToSet Iα α` instance provides a way of interpreting elements of `Iα` as sets of `α`, +through a function `toSet : Iα → Set α`. In its use in the `inclusion` tactic, `Iα` will be +a type with good computational properties (such as `Interval Dyadic`) and `α` will be some +type that appears in the user's expression, such as `ℝ`. -/ +class ToSet (Iα : Type*) (α : outParam Type*) where + /-- The mapping of elements of `Iα` to sets in `α`. -/ + toSet : Iα → Set α + +instance {Iα α : Type*} [ToSet Iα α] : CoeTC Iα (Set α) where + coe := ToSet.toSet + +instance {Iα α : Type*} [ToSet Iα α] : Membership α Iα where + mem s a := ToSet.toSet s a + +@[simp] +lemma mem_set_iff_mem_toSet {Iα α : Type*} [ToSet Iα α] (a : α) (s : Iα) : + a ∈ s ↔ a ∈ (s : Set α) := Iff.rfl + +lemma ToSet.mem_of_eq_of_mem {Iα α : Type*} [ToSet Iα α] {x y : α} {s : Iα} + (hxy : x = y) (hy : y ∈ s) : x ∈ s := hxy ▸ hy + +lemma ToSet.mem_of_mem_of_eq {Iα α : Type*} [ToSet Iα α] {x y : α} {s : Iα} + (hxy : x = y) (hx : x ∈ s) : y ∈ s := hxy ▸ hx + +/-- A `Univ Iα α` instance is a specification of an element `univ : Iα` such that +every element of `α` belongs to `univ`. This is useful for assigning a container to +inclusion variables that have no inclusion hypotheses. -/ +class Univ (Iα α : Type*) [ToSet Iα α] where + /-- A (computational) representative of the universal set. -/ + univ : Iα + /-- Every element of `α` belongs to `univ`. -/ + mem_univ (x : α) : x ∈ univ + +/-- A `Refine Iα α` instance is a specification of a (computable) function `refine : Iα → Iα → Iα` +such that for any `s t : Iα`, `s ∩ t ⊆ refine s t` as sets of `α`. This is useful for merging +multiple inclusion hypotheses of a single inclusion variable. -/ +class Refine (Iα α : Type*) [ToSet Iα α] where + /-- A (computable) function to refine two inclusion hypotheses. -/ + refine : Iα → Iα → Iα + /-- If `x ∈ s` and `x ∈ t` then `x ∈ refine s t`. -/ + mem_refine {x : α} {s t : Iα} (hs : x ∈ s) (ht : x ∈ t) : x ∈ refine s t + +/-- A `Coarsen Iα α` instance is a specification of a (computable) function `coarsen : Iα → Iα → Iα` +such that for any `s t : Iα`, `s ∪ t ⊆ coarsen s t`. This is useful for applying an inclusion +function to a cover of the input and then merging the results. -/ +class Coarsen (Iα α : Type*) [ToSet Iα α] where + /-- A represented set containing both input sets. -/ + coarsen : Iα → Iα → Iα + /-- If `x ∈ s` then `x ∈ coarsen s t`. -/ + mem_coarsen_left {x : α} {s t : Iα} (hx : x ∈ s) : x ∈ coarsen s t + /-- If `x ∈ t` then `x ∈ coarsen s t`. -/ + mem_coarsen_right {x : α} {s t : Iα} (hx : x ∈ t) : x ∈ coarsen s t + +universe u + +/-- A `Cover Iα α` specifies a function `coverMap` to compute a "refined" inclusion of `F s` +for `s : Iα` and an inclusion function `F : Iα → Iβ`, by computing `F` on each element of a +cover of `s` and then using `coarsen` to merge the results. Schematically + +`coverMap s F = fold coarsen (map F (cover s))` + +where `cover : Iα → Array Iα` would specify the underlying cover, but the `coverMap` formulation +allows this function to be implemented more efficiently for kernel reduction. -/ +structure Cover (Iα α : Type*) [ToSet Iα α] where + /-- Compute an inclusion for `F s` using a cover of `s`. -/ + coverMap {Iβ β : Type u} [ToSet Iβ β] [Coarsen Iβ β] (s : Iα) (F : Iα → Iβ) : Iβ + /-- If `x ∈ s` and `∀ t, x ∈ t → y ∈ F t` then `y ∈ coverMap s F`. -/ + mem_coverMap {Iβ β : Type u} [ToSet Iβ β] [Coarsen Iβ β] {s : Iα} {F : Iα → Iβ} {x : α} {y : β} + (hx : x ∈ s) (hy : ∀ t, x ∈ t → y ∈ F t) : y ∈ coverMap s F + +open ToSet + +section IntervalBool + +/-- An `IntervalBool` represents the result of a `Prop` inclusion and is either +`true` (if the proposition is computed true), `false` (if the proposition is computed false), +or `undetermined` (if the computation is indeterminate). -/ +inductive IntervalBool + | true + | false + | undetermined + +/-- The mapping from `IntervalBool` to `Set Prop` which identifies each option +(`true`, `false`, `undetermined`) with its set of possible outcomes +(`{True}`, `{False}`, `{True, False}` respectively). -/ +def IntervalBool.toPropSet : IntervalBool → Set Prop + | true => {True} + | false => {False} + | undetermined => {True, False} + +instance : ToSet IntervalBool Prop := ⟨IntervalBool.toPropSet⟩ + +theorem true_of_mem_intervalBool_true {p : Prop} (hp : p ∈ IntervalBool.true) : p := by + simpa [mem_set_iff_mem_toSet, toSet, IntervalBool.toPropSet] using hp + +theorem true_of_mem_intervalBool_eq_true {p : Prop} {b : IntervalBool} (hp : p ∈ b) + (hb : b = IntervalBool.true) : p := + true_of_mem_intervalBool_true (hb ▸ hp) + +/-- Return `true` when `IntervalBool = true` otherwise return `false`. -/ +def IntervalBool.isTrue : IntervalBool → Bool + | .true => Bool.true + | .false | .undetermined => Bool.false + +theorem IntervalBool.eq_true_of_isTrue_eq_true {b : IntervalBool} + (h : b.isTrue = Bool.true) : b = .true := by + cases b <;> simp_all [IntervalBool.isTrue] + +/-- Union of two `IntervalBool`s. -/ +def IntervalBool.union : IntervalBool → IntervalBool → IntervalBool + | .true, .true => .true + | .false, .false => .false + | _, _ => .undetermined + +instance : Coarsen IntervalBool Prop where + coarsen := IntervalBool.union + mem_coarsen_left := by + intro p s t hp + cases s <;> cases t <;> simp_all [IntervalBool.union, ToSet.toSet, IntervalBool.toPropSet] + mem_coarsen_right := by + intro p s t hp + cases s <;> cases t <;> simp_all [IntervalBool.union, ToSet.toSet, IntervalBool.toPropSet] + +end IntervalBool + +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Core/Types.lean b/Mathlib/Tactic/Inclusion/Core/Types.lean new file mode 100644 index 00000000000000..916052fa798058 --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Core/Types.lean @@ -0,0 +1,166 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public import Mathlib.Init +public meta import Lean.Meta.Basic + +/-! +# Datatypes for the `inclusion` tactic + +This file defines several datatypes and monads (and some basic API for them) that are used +throughout the core of the `inclusion` tactic. +-/ + +public meta section + +open Lean Meta + +namespace Inclusion + +/-- An `IType` is a structure that holds the types of an inclusion expression `x ∈ s`, where +the type of `x` is `elemType`, the type of `s` is `setType` and the `ToSet setType elemType` +instance used is `toSetInst`. -/ +structure IType where + /-- The element type of an inclusion expression. -/ + elemType : Expr + /-- The set type of an inclusion expression. -/ + setType : Expr + /-- The `ToSet setType elemType` instance of an inclusion expression. -/ + toSetInst : Expr + deriving Inhabited + +/-- An `IExpr` is an expression `expr` together with a choice of `IType` used to represent +inclusion expressions of the form `expr ∈ s`. -/ +structure IExpr where + /-- The types of an `IExpr`. -/ + iType : IType + /-- The underlying expression of an `IExpr`. -/ + expr : Expr + deriving Inhabited + +/-- An `IVar` is a structure that holds the data of a "free inclusion variable" associated to an +inclusion expression `iExpr`. This includes a pair of variables `setVar`, `hypVar` (which are +sometimes free variables but often synthetic opaque metavariables), where `setVar` is a variable for +an inclusion set and `hypVar` is a (variable) proof of `iExpr.expr ∈ setVar`. -/ +structure IVar where + /-- The inclusion expression represented by the inclusion variable. -/ + iExpr : IExpr + /-- The inclusion set variable. -/ + setVar : Expr + /-- The variable `hypVar : iExpr.expr ∈ setVar`. -/ + hypVar : Expr + /-- An optional expression of type `Cover iVar.type.setType iVar.type.elemType`. When present, + the inclusion computation is mapped over this cover to reduce the "dependency effect". -/ + cover : Option Expr + +/-- The `IType` of an `IVar`. -/ +def IVar.type (iVar : IVar) : IType := iVar.iExpr.iType + +/-- The associated expression of an `IVar`. -/ +def IVar.expr (iVar : IVar) : Expr := iVar.iExpr.expr + +/-- An `ExprInclusion` is a structure associated with an expression `e`, containing a computed +inclusion set for `e` and a proof that this inclusion is correct. -/ +structure ExprInclusion where + /-- The types of the inclusion result. -/ + iType : IType + /-- The expression computing an inclusion set for `e`. -/ + inclusion : Expr + /-- A proof of `e ∈ inclusion`, where `e` is the represented expression. -/ + proof : Expr + deriving Inhabited + +/-- An `ExprInclusionBody` is an intermediate structure used in the process of building the +`ExprInclusion` associated to an expression `e`. It contains an `inclusionBody` and `proofBody` +which contain the (possibly partially completed) body of the `inclusion` and `proof` expressions +of the `ExprInclusion` respectively. -/ +structure ExprInclusionBody where + /-- The (possibly partially completed) body of the inclusion expression. -/ + inclusionBody : Expr + /-- The (possibly partially completed) proof of `e ∈ inclusionBody`. -/ + proofBody : Expr + deriving Inhabited + +/-- Convert an `IVar` to an `ExprInclusionBody`. -/ +def IVar.toExprInclusionBody (iVar : IVar) : ExprInclusionBody := ⟨iVar.setVar, iVar.hypVar⟩ + +section InclusionM + +/-- The fixed context of the `InclusionM` monad. -/ +structure InclusionM.Context where + /-- The initial `LocalContext`. -/ + localContext : LocalContext + /-- The `LocalInstances` associated with `localContext`. -/ + localInstances : LocalInstances + /-- A map from inclusion parameter names to their user-supplied values. -/ + paramSettings : NameMap Expr + /-- The names of the inclusion extension families to use. -/ + families : Array Name + +/-- The mutable state of the `InclusionM` monad. -/ +structure InclusionM.State where + /-- The inclusion variables registered for expressions encountered during construction. -/ + iVars : ExprMap IVar := {} + +/-- The monad used by the `inclusion` tactic during the construction of `ExprInclusion`s. -/ +abbrev InclusionM := ReaderT InclusionM.Context <| StateT InclusionM.State MetaM + +instance : MonadBacktrack (Meta.SavedState × InclusionM.State) InclusionM where + saveState := do return ⟨← Meta.saveState, ← get⟩ + restoreState s := do + s.1.restore + set s.2 + +/-- Run the `InclusionM` monad with an explicit context and initial state. -/ +def InclusionM.runWith {α : Type} (x : InclusionM α) (context : InclusionM.Context) + (state : InclusionM.State := {}) : MetaM (α × InclusionM.State) := + StateT.run (ReaderT.run x context) state + +/-- Run the `InclusionM` monad using the current local context. -/ +def InclusionM.run {α : Type} (x : InclusionM α) (paramSettings : NameMap Expr := {}) + (families : Array Name := #[]) : MetaM α := do + let localContext ← getLCtx + let localInstances ← getLocalInstances + return (← x.runWith { localContext, localInstances, paramSettings, families }).1 + +end InclusionM + +section HypothesisM + +/-- The fixed context of the `HypothesisM` monad. -/ +structure HypothesisM.Context extends InclusionM.Context where + /-- The inclusion variables indexed by their associated expressions. -/ + iVarsMap : ExprMap IVar + /-- The inclusion variables whose hypotheses are being constructed. -/ + iVars : Array IVar + +/-- The mutable state of the `HypothesisM` monad. -/ +structure HypothesisM.State where + /-- The candidate inclusion bodies derived for each requested expression. -/ + inclusions : ExprMap (Array ExprInclusionBody) := {} + +/-- The monad used by the `inclusion` tactic to construct initial inclusion hypotheses. -/ +abbrev HypothesisM := ReaderT HypothesisM.Context <| StateT HypothesisM.State MetaM + +instance : MonadBacktrack (Meta.SavedState × HypothesisM.State) HypothesisM where + saveState := do return ⟨← Meta.saveState, ← get⟩ + restoreState s := do + s.1.restore + set s.2 + +/-- Run the `HypothesisM` monad using the context and state of the current `InclusionM` +computation. -/ +def HypothesisM.run {α : Type} (x : HypothesisM α) : InclusionM α := do + let inclusionContext ← read + let inclusionState ← get + let iVarsMap := inclusionState.iVars + let iVars := iVarsMap.valuesArray + liftM <| StateT.run' (ReaderT.run x { toContext := inclusionContext, iVarsMap, iVars }) {} + +end HypothesisM + +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/Core/Family.lean b/Mathlib/Tactic/Inclusion/Extension/Core/Family.lean new file mode 100644 index 00000000000000..a7390c602b38b3 --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Extension/Core/Family.lean @@ -0,0 +1,23 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public meta import Mathlib.Tactic.Inclusion.Core.Extensions + +/-! +# Core extension family for the `inclusion` tactic + +This file registers the extension family containing rules that are independent of the represented +set implementation. +-/ + +public meta section + +namespace Inclusion + +initialize coreFamily : InclusionFamily ← registerInclusionFamily `core + +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/Core/Hypotheses.lean b/Mathlib/Tactic/Inclusion/Extension/Core/Hypotheses.lean new file mode 100644 index 00000000000000..5dab232efd6c73 --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Extension/Core/Hypotheses.lean @@ -0,0 +1,38 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public meta import Mathlib.Tactic.Inclusion.Extension.Core.Family +public meta import Mathlib.Tactic.Inclusion.ExtensionAPI.Attr + +/-! +# Generic hypothesis extensions for the `inclusion` tactic + +This file registers hypothesis rules that work for every represented-set implementation. +-/ + +public meta section + +open Lean Meta + +namespace Inclusion + +attribute [hypothesisOp core] ToSet.mem_of_eq_of_mem ToSet.mem_of_mem_of_eq + +/-- The generic hypothesis extension that uses a closed `ToSet` membership hypothesis directly as +an inclusion hypothesis. -/ +@[hypothesisExt core | _ ∈ _] +meta def directMembershipHyp : HypothesisExt where + derive h := do + let type ← instantiateMVars (← inferType h) + let some (expr, set, _) := toSetMem? type | failure + if set.hasFVar || set.hasMVar then + trace[Tactic.inclusion] "Ignoring non-closed direct hypothesis {type}" + failure + let some iExpr ← requestedIVar? expr | return + addInclusionHyp iExpr ⟨set, h⟩ + +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Basic.lean b/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Basic.lean new file mode 100644 index 00000000000000..0664dee3f0270d --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Basic.lean @@ -0,0 +1,226 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public import Mathlib.Tactic.Inclusion.Extension.Interval +public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Dyadic +public meta import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Family +public meta import Mathlib.Tactic.Inclusion.ExtensionAPI.Attr + +/-! +# Basic dyadic interval operations for real expressions + +This file defines the core operations for the `real.dyadic` inclusion family whose computational +implementations are suitable for general use. +-/ + +set_option linter.style.header false + +@[expose] public section + +namespace Inclusion + +instance : ToSet (Interval Dyadic) ℝ where + toSet I := (I.map Dyadic.toReal).toSet + +def ofNat (n : ℕ) : Interval Dyadic := Interval.singleton Dyadic n + +def add (x y : Interval Dyadic) : Interval Dyadic where + lb := match x.lb, y.lb with + | some a, some b => some (a + b) + | _, _ => ⊥ + ub := match x.ub, y.ub with + | some a, some b => some (a + b) + | _, _ => ⊤ + +def neg (x : Interval Dyadic) : Interval Dyadic where + lb := match x.ub with + | some a => some (-a) + | ⊤ => ⊥ + ub := match x.lb with + | some a => some (-a) + | ⊥ => ⊤ + +def sub (x y : Interval Dyadic) : Interval Dyadic where + lb := match x.lb, y.ub with + | some a, some b => some (a - b) + | _, _ => ⊥ + ub := match x.ub, y.lb with + | some a, some b => some (a - b) + | _, _ => ⊤ + +def le (x y : Interval Dyadic) : IntervalBool := + match x.ub, y.lb with + | some xu, some yl => if xu ≤ yl then .true else .undetermined + | _, _ => .undetermined + +theorem mem_univ (r : ℝ) : r ∈ Interval.univ Dyadic := by + constructor <;> simp [Interval.univ, Interval.map] + +@[simp] +lemma toReal_add (a b : Dyadic) : + Dyadic.toReal (a + b) = Dyadic.toReal a + Dyadic.toReal b := by + simp [Dyadic.toReal, Dyadic.toRat_add] + +@[simp] +lemma toReal_neg (a : Dyadic) : Dyadic.toReal (-a) = -Dyadic.toReal a := by + simp [Dyadic.toReal, Dyadic.toRat_neg] + +@[simp] +lemma toReal_sub (a b : Dyadic) : + Dyadic.toReal (a - b) = Dyadic.toReal a - Dyadic.toReal b := by + simp [Dyadic.toReal, Dyadic.toRat_sub] + +lemma toReal_le_toReal {a b : Dyadic} : Dyadic.toReal a ≤ Dyadic.toReal b ↔ a ≤ b := by + simp [Dyadic.toReal] + +@[simp] +lemma toReal_min (a b : Dyadic) : + Dyadic.toReal (min a b) = min (Dyadic.toReal a) (Dyadic.toReal b) := by + rcases le_total a b with h | h + · rw [min_eq_left h, min_eq_left (toReal_le_toReal.mpr h)] + · rw [min_eq_right h, min_eq_right (toReal_le_toReal.mpr h)] + +@[simp] +lemma toReal_max (a b : Dyadic) : + Dyadic.toReal (max a b) = max (Dyadic.toReal a) (Dyadic.toReal b) := by + rcases le_total a b with h | h + · rw [max_eq_right h, max_eq_right (toReal_le_toReal.mpr h)] + · rw [max_eq_left h, max_eq_left (toReal_le_toReal.mpr h)] + +theorem map_inter (I J : Interval Dyadic) : + (I.inter J).map Dyadic.toReal = (I.map Dyadic.toReal).inter (J.map Dyadic.toReal) := by + rcases I with ⟨il, iu⟩ + rcases J with ⟨jl, ju⟩ + cases il <;> cases iu <;> cases jl <;> cases ju <;> + simp [Interval.inter, Interval.map, toReal_min, toReal_max] + +theorem inter_mem {r : ℝ} {I J : Interval Dyadic} (hI : r ∈ I) (hJ : r ∈ J) : + r ∈ I.inter J := by + change r ∈ (I.inter J).map Dyadic.toReal + rw [map_inter] + exact Refine.mem_refine (Iα := Interval ℝ) + (hI : r ∈ I.map Dyadic.toReal) (hJ : r ∈ J.map Dyadic.toReal) + +instance : Univ (Interval Dyadic) ℝ where + univ := Interval.univ Dyadic + mem_univ := mem_univ + +instance : Refine (Interval Dyadic) ℝ where + refine := Interval.inter + mem_refine := inter_mem + +theorem map_hull (I J : Interval Dyadic) : + (I.hull J).map Dyadic.toReal = (I.map Dyadic.toReal).hull (J.map Dyadic.toReal) := by + rcases I with ⟨il, iu⟩ + rcases J with ⟨jl, ju⟩ + cases il <;> cases iu <;> cases jl <;> cases ju <;> + simp [Interval.hull, Interval.map, toReal_min, toReal_max] + +theorem hull_mem_left {r : ℝ} {I J : Interval Dyadic} (hI : r ∈ I) : r ∈ I.hull J := by + change r ∈ (I.hull J).map Dyadic.toReal + rw [map_hull] + exact Coarsen.mem_coarsen_left (Iα := Interval ℝ) (hI : r ∈ I.map Dyadic.toReal) + +theorem hull_mem_right {r : ℝ} {I J : Interval Dyadic} (hJ : r ∈ J) : r ∈ I.hull J := by + change r ∈ (I.hull J).map Dyadic.toReal + rw [map_hull] + exact Coarsen.mem_coarsen_right (Iα := Interval ℝ) (hJ : r ∈ J.map Dyadic.toReal) + +instance : Coarsen (Interval Dyadic) ℝ where + coarsen := Interval.hull + mem_coarsen_left := hull_mem_left + mem_coarsen_right := hull_mem_right + +@[inclusionOp real.dyadic] +theorem ofNat_mem (n : ℕ) : (OfNat.ofNat n : ℝ) ∈ ofNat n := by + constructor + · exact WithBot.coe_le_coe.mpr <| by + simp [Dyadic.toReal, Dyadic.toRat_natCast, Semiring.toGrindSemiring_ofNat ℝ n] + · exact WithTop.coe_le_coe.mpr <| by + simp [Dyadic.toReal, Dyadic.toRat_natCast, Semiring.toGrindSemiring_ofNat ℝ n] + +@[inclusionOp real.dyadic] +theorem add_mem {r s : ℝ} {x y : Interval Dyadic} + (hrx : r ∈ x) (hsy : s ∈ y) : r + s ∈ add x y := by + match x, y with + | ⟨xl, xu⟩, ⟨yl, yu⟩ => + constructor + · match xl, yl with + | ⊥, _ => simp [add, Interval.map] + | xl, ⊥ => cases xl <;> simp [add, Interval.map] + | some a, some b => + exact WithBot.coe_le_coe.mpr <| by + rw [toReal_add] + exact add_le_add (WithBot.coe_le_coe.mp hrx.1) (WithBot.coe_le_coe.mp hsy.1) + · match xu, yu with + | ⊤, _ => simp [add, Interval.map] + | xu, ⊤ => cases xu <;> simp [add, Interval.map] + | some a, some b => + exact WithTop.coe_le_coe.mpr <| by + rw [toReal_add] + exact add_le_add (WithTop.coe_le_coe.mp hrx.2) (WithTop.coe_le_coe.mp hsy.2) + +@[inclusionOp real.dyadic] +theorem neg_mem {r : ℝ} {x : Interval Dyadic} (hrx : r ∈ x) : -r ∈ neg x := by + match x with + | ⟨xl, xu⟩ => + constructor + · match xu with + | ⊤ => simp [neg, Interval.map] + | some a => + exact WithBot.coe_le_coe.mpr <| by + rw [toReal_neg] + exact neg_le_neg (WithTop.coe_le_coe.mp hrx.2) + · match xl with + | ⊥ => simp [neg, Interval.map] + | some a => + exact WithTop.coe_le_coe.mpr <| by + rw [toReal_neg] + exact neg_le_neg (WithBot.coe_le_coe.mp hrx.1) + +@[inclusionOp real.dyadic] +theorem sub_mem {r s : ℝ} {x y : Interval Dyadic} + (hrx : r ∈ x) (hsy : s ∈ y) : r - s ∈ sub x y := by + match x, y with + | ⟨xl, xu⟩, ⟨yl, yu⟩ => + constructor + · match xl, yu with + | ⊥, _ => simp [sub, Interval.map] + | xl, ⊤ => cases xl <;> simp [sub, Interval.map] + | some a, some b => + exact WithBot.coe_le_coe.mpr <| by + rw [toReal_sub] + exact sub_le_sub (WithBot.coe_le_coe.mp hrx.1) (WithTop.coe_le_coe.mp hsy.2) + · match xu, yl with + | ⊤, _ => simp [sub, Interval.map] + | xu, ⊥ => cases xu <;> simp [sub, Interval.map] + | some a, some b => + exact WithTop.coe_le_coe.mpr <| by + rw [toReal_sub] + exact sub_le_sub (WithTop.coe_le_coe.mp hrx.2) (WithBot.coe_le_coe.mp hsy.1) + +theorem mem_intervalBool_true {p : Prop} (hp : p) : p ∈ IntervalBool.true := by + simpa [ToSet.toSet, IntervalBool.toPropSet] using hp + +theorem mem_intervalBool_undetermined (p : Prop) : p ∈ IntervalBool.undetermined := by + classical + by_cases hp : p <;> simp [ToSet.toSet, IntervalBool.toPropSet, hp] + +@[inclusionOp real.dyadic] +theorem le_mem {r s : ℝ} {x y : Interval Dyadic} + (hrx : r ∈ x) (hsy : s ∈ y) : (r ≤ s) ∈ le x y := by + match x, y with + | ⟨_, some xu⟩, ⟨some yl, _⟩ => + simp only [le] + split_ifs with h + · apply mem_intervalBool_true + exact (WithTop.coe_le_coe.mp hrx.2).trans + ((Monotone.dyadicToReal h).trans (WithBot.coe_le_coe.mp hsy.1)) + · exact mem_intervalBool_undetermined _ + | ⟨_, ⊤⟩, _ | ⟨_, some _⟩, ⟨⊥, _⟩ => exact mem_intervalBool_undetermined _ + +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Dyadic.lean b/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Dyadic.lean new file mode 100644 index 00000000000000..b0f84ad6c6f897 --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Dyadic.lean @@ -0,0 +1,31 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public import Mathlib.Data.Real.Basic +public import Mathlib.Data.Rat.Cast.Order + +set_option linter.style.header false + +@[expose] public section + +namespace Inclusion + +instance : LinearOrder Dyadic where + le_refl := Std.IsPreorder.le_refl + le_trans := Std.IsPreorder.le_trans + le_antisymm := Std.IsPartialOrder.le_antisymm + lt_iff_le_not_ge := Std.LawfulOrderLT.lt_iff + le_total := Std.IsLinearOrder.le_total + toDecidableLE := Dyadic.instDecidableLE + +def Dyadic.toReal (d : Dyadic) : ℝ := d.toRat + +theorem Monotone.dyadicToReal : Monotone Dyadic.toReal := by + intro _ _ h + simp [Dyadic.toReal, h] + +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Extensions.lean b/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Extensions.lean new file mode 100644 index 00000000000000..3a1a6c0b53dde8 --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Extensions.lean @@ -0,0 +1,39 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Basic +public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Splitting +public meta import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Basic +public meta import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Splitting +public meta import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Hypotheses +public meta import Mathlib.Tactic.Inclusion.ExtensionAPI.Basic + +/-! +# Inclusion extensions for dyadic real intervals +-/ + +public meta section + +open Lean Meta + +namespace Inclusion + +@[inclusionParam] +meta def binSplitParam : InclusionParamDecl where + name := `binSplit + type := mkConst ``Nat + +private def mkRealCover (iExpr : IExpr) : InclusionM (Option Expr) := do + let some depth ← getParam? `binSplit | return none + return some (← mkAppOptM ``Splitter.cover + #[iExpr.iType.setType, iExpr.iType.elemType, iExpr.iType.toSetInst, none, depth]) + +@[inclusionExt real.dyadic | (_ : ℝ)] +meta def mkRealIVar : InclusionExt := + mkNDIVarExt (mkConst ``Real) (mkAppM ``Interval #[mkConst ``Dyadic]) mkRealCover + +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Family.lean b/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Family.lean new file mode 100644 index 00000000000000..281f7d59d2d961 --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Family.lean @@ -0,0 +1,22 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public meta import Mathlib.Tactic.Inclusion.Core.Extensions + +/-! +# Dyadic real extension family for the `inclusion` tactic + +This file registers the family using dyadic intervals to enclose real expressions. +-/ + +public meta section + +namespace Inclusion + +initialize realDyadicFamily : InclusionFamily ← registerInclusionFamily `real.dyadic + +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Hypotheses.lean b/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Hypotheses.lean new file mode 100644 index 00000000000000..12648195fa39e3 --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Hypotheses.lean @@ -0,0 +1,90 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Basic + +/-! +# Hypothesis operations for dyadic real intervals +-/ + +@[expose] public section + +namespace Inclusion + +private theorem mem_bounds {a b x : ℝ} {I J : Interval Dyadic} + (ha : a ∈ I) (hax : a ≤ x) (hxb : x ≤ b) (hb : b ∈ J) : + x ∈ (⟨I.lb, J.ub⟩ : Interval Dyadic) := + ⟨ha.1.trans (WithBot.coe_le_coe.mpr hax), + (WithTop.coe_le_coe.mpr hxb).trans hb.2⟩ + +@[hypothesisOp real.dyadic] +theorem downwardClosure_mem {x y : ℝ} {I : Interval Dyadic} + (hxy : x ≤ y) (hy : y ∈ I) : x ∈ I.downwardClosure := + ⟨by simp [Interval.downwardClosure, Interval.map], + (WithTop.coe_le_coe.mpr hxy).trans hy.2⟩ + +@[hypothesisOp real.dyadic] +theorem upwardClosure_mem {x y : ℝ} {I : Interval Dyadic} + (hxy : x ≤ y) (hx : x ∈ I) : y ∈ I.upwardClosure := + ⟨hx.1.trans (WithBot.coe_le_coe.mpr hxy), + by simp [Interval.upwardClosure, Interval.map]⟩ + +@[hypothesisOp real.dyadic] +theorem downwardClosure_mem_of_lt {x y : ℝ} {I : Interval Dyadic} + (hxy : x < y) (hy : y ∈ I) : x ∈ I.downwardClosure := + downwardClosure_mem hxy.le hy + +@[hypothesisOp real.dyadic] +theorem upwardClosure_mem_of_lt {x y : ℝ} {I : Interval Dyadic} + (hxy : x < y) (hx : x ∈ I) : y ∈ I.upwardClosure := + upwardClosure_mem hxy.le hx + +@[hypothesisOp real.dyadic] +theorem upwardClosure_mem_of_mem_Ici {a x : ℝ} {I : Interval Dyadic} + (hx : x ∈ Set.Ici a) (ha : a ∈ I) : x ∈ I.upwardClosure := + upwardClosure_mem (Set.mem_Ici.mp hx) ha + +@[hypothesisOp real.dyadic] +theorem upwardClosure_mem_of_mem_Ioi {a x : ℝ} {I : Interval Dyadic} + (hx : x ∈ Set.Ioi a) (ha : a ∈ I) : x ∈ I.upwardClosure := + upwardClosure_mem (Set.mem_Ioi.mp hx).le ha + +@[hypothesisOp real.dyadic] +theorem downwardClosure_mem_of_mem_Iic {b x : ℝ} {I : Interval Dyadic} + (hx : x ∈ Set.Iic b) (hb : b ∈ I) : x ∈ I.downwardClosure := + downwardClosure_mem (Set.mem_Iic.mp hx) hb + +@[hypothesisOp real.dyadic] +theorem downwardClosure_mem_of_mem_Iio {b x : ℝ} {I : Interval Dyadic} + (hx : x ∈ Set.Iio b) (hb : b ∈ I) : x ∈ I.downwardClosure := + downwardClosure_mem (Set.mem_Iio.mp hx).le hb + +@[hypothesisOp real.dyadic] +theorem bounds_mem_of_mem_Ico {a b x : ℝ} {I J : Interval Dyadic} + (hx : x ∈ Set.Ico a b) (ha : a ∈ I) (hb : b ∈ J) : + x ∈ (⟨I.lb, J.ub⟩ : Interval Dyadic) := + mem_bounds ha hx.1 hx.2.le hb + +@[hypothesisOp real.dyadic] +theorem bounds_mem_of_mem_Ioc {a b x : ℝ} {I J : Interval Dyadic} + (hx : x ∈ Set.Ioc a b) (ha : a ∈ I) (hb : b ∈ J) : + x ∈ (⟨I.lb, J.ub⟩ : Interval Dyadic) := + mem_bounds ha hx.1.le hx.2 hb + +@[hypothesisOp real.dyadic] +theorem bounds_mem_of_mem_Icc {a b x : ℝ} {I J : Interval Dyadic} + (hx : x ∈ Set.Icc a b) (ha : a ∈ I) (hb : b ∈ J) : + x ∈ (⟨I.lb, J.ub⟩ : Interval Dyadic) := + mem_bounds ha hx.1 hx.2 hb + +@[hypothesisOp real.dyadic] +theorem bounds_mem_of_mem_Ioo {a b x : ℝ} {I J : Interval Dyadic} + (hx : x ∈ Set.Ioo a b) (ha : a ∈ I) (hb : b ∈ J) : + x ∈ (⟨I.lb, J.ub⟩ : Interval Dyadic) := + mem_bounds ha hx.1.le hx.2.le hb + +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Splitting.lean b/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Splitting.lean new file mode 100644 index 00000000000000..b9e2152d74238f --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Splitting.lean @@ -0,0 +1,82 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Basic +public import Mathlib.Tactic.Inclusion.Extension.Splitter + +/-! +# Binary splitting of dyadic real intervals + +This file defines a `Splitter` instance that repeatedly bisects bounded dyadic intervals. +-/ + +set_option linter.style.header false + +@[expose] public section + +namespace Inclusion + +namespace BinarySplit + +/-- Divide a dyadic number by two. -/ +def half (x : Dyadic) : Dyadic := + match x with + | .zero => 0 + | .ofOdd n k _ => Dyadic.ofIntWithPrec n (k + 1) + +/-- The dyadic midpoint of `a` and `b`. -/ +def midpoint (a b : Dyadic) : Dyadic := half (a + b) + +/-- Map `F` over the intervals produced by bisecting `I` to depth `n`, coarsening the results. -/ +@[specialize] +def coverMap {Iβ β : Type*} [ToSet Iβ β] [Coarsen Iβ β] : + ℕ → Interval Dyadic → (Interval Dyadic → Iβ) → Iβ + | 0, I, F => F I + | n + 1, I, F => + match I with + | ⟨some l, some u⟩ => + let m := midpoint l u + Coarsen.coarsen (Iα := Iβ) (α := β) (coverMap n ⟨l, m⟩ F) (coverMap n ⟨m, u⟩ F) + | _ => coverMap n I F + +theorem mem_coverMap {Iβ β : Type*} [ToSet Iβ β] [Coarsen Iβ β] + (n : ℕ) (I : Interval Dyadic) (F : Interval Dyadic → Iβ) {y : β} {r : ℝ} + (hr : r ∈ I) (hy : ∀ J, r ∈ J → y ∈ F J) : y ∈ coverMap n I F := by + induction n generalizing I with + | zero => exact hy I hr + | succ n ih => + rcases I with ⟨lb, ub⟩ + cases lb with + | bot => exact ih ⟨⊥, ub⟩ hr + | coe l => + cases ub with + | top => exact ih ⟨l, ⊤⟩ hr + | coe u => + let m := midpoint l u + let left : Interval Dyadic := ⟨l, m⟩ + let right : Interval Dyadic := ⟨m, u⟩ + change y ∈ Coarsen.coarsen (Iα := Iβ) (α := β) + (coverMap n left F) (coverMap n right F) + by_cases hl : r ≤ Dyadic.toReal m + · apply Coarsen.mem_coarsen_left (Iα := Iβ) (α := β) + exact ih left ⟨hr.1, WithTop.coe_le_coe.mpr hl⟩ + · apply Coarsen.mem_coarsen_right (Iα := Iβ) (α := β) + exact ih right ⟨WithBot.coe_le_coe.mpr (le_of_not_ge hl), hr.2⟩ + +/-- Cover a dyadic interval by repeatedly bisecting it to depth `n`. -/ +def cover (n : ℕ) : Cover (Interval Dyadic) ℝ where + coverMap := fun I F ↦ coverMap n I F + mem_coverMap := by + intro Iβ β _ _ I F x y hx hy + exact mem_coverMap n I F hx hy + +end BinarySplit + +instance : Splitter (Interval Dyadic) ℝ where + cover := BinarySplit.cover + +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/Extensions.lean b/Mathlib/Tactic/Inclusion/Extension/Extensions.lean new file mode 100644 index 00000000000000..70b043cef9a6a5 --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Extension/Extensions.lean @@ -0,0 +1,16 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public meta import Mathlib.Tactic.Inclusion.Extension.Core.Hypotheses +public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Extensions +public meta import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Extensions + +/-! +# Standard extensions for the `inclusion` tactic + +This file imports the polished extension families currently provided with the tactic. +-/ diff --git a/Mathlib/Tactic/Inclusion/Extension/Interval.lean b/Mathlib/Tactic/Inclusion/Extension/Interval.lean new file mode 100644 index 00000000000000..72e75b67d5edb1 --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Extension/Interval.lean @@ -0,0 +1,154 @@ +module + +public import Mathlib.Tactic.Inclusion.Core.ToSet +public import Mathlib.Order.WithBot + +set_option linter.style.header false + +@[expose] public section + +open Set + +namespace Inclusion + +variable {α β : Type*} + +structure Interval (α : Type*) where + lb : WithBot α + ub : WithTop α + deriving Inhabited + +def Interval.map (I : Interval α) (f : α → β) : Interval β := + let lb := match I.lb with + | some a => some (f a) + | ⊥ => ⊥ + let ub := match I.ub with + | some a => some (f a) + | ⊤ => ⊤ + ⟨lb, ub⟩ + +def Interval.univ (α : Type*) : Interval α := ⟨⊥, ⊤⟩ + +def Interval.singleton (α : Type*) (a : α) : Interval α := ⟨a, a⟩ + +/-- The exact intersection of two intervals. -/ +def Interval.inter [LinearOrder α] (I J : Interval α) : Interval α := + let lb := match I.lb, J.lb with + | ⊥, lb | lb, ⊥ => lb + | some a, some b => some (max a b) + let ub := match I.ub, J.ub with + | ⊤, ub | ub, ⊤ => ub + | some a, some b => some (min a b) + ⟨lb, ub⟩ + +/-- The smallest interval whose endpoints contain both input intervals. -/ +def Interval.hull [LinearOrder α] (I J : Interval α) : Interval α := + let lb := match I.lb, J.lb with + | ⊥, _ | _, ⊥ => ⊥ + | some a, some b => some (min a b) + let ub := match I.ub, J.ub with + | ⊤, _ | _, ⊤ => ⊤ + | some a, some b => some (max a b) + ⟨lb, ub⟩ + +/-- Forget the lower endpoint of an interval. -/ +def Interval.downwardClosure (I : Interval α) : Interval α := ⟨⊥, I.ub⟩ + +/-- Forget the upper endpoint of an interval. -/ +def Interval.upwardClosure (I : Interval α) : Interval α := ⟨I.lb, ⊤⟩ + +def Interval.toSet [Preorder α] (I : Interval α) : Set α := {a | I.lb ≤ a ∧ a ≤ I.ub} + +instance [Preorder α] : ToSet (Interval α) α := ⟨Interval.toSet⟩ + +instance [Preorder α] : Univ (Interval α) α where + univ := Interval.univ α + mem_univ x := + ⟨show (⊥ : WithBot α) ≤ x from bot_le, show (x : WithTop α) ≤ ⊤ from le_top⟩ + +instance [LinearOrder α] : Refine (Interval α) α where + refine := Interval.inter + mem_refine := by + intro x s t hs ht + constructor + · rcases hI : s.lb with _ | sl + · simpa [Interval.inter, hI] using ht.1 + rcases hJ : t.lb with _ | tl + · simpa [Interval.inter, hI, hJ] using hs.1 + have hs' := hs.1 + have ht' := ht.1 + rw [hI] at hs' + rw [hJ] at ht' + simp only [Interval.inter, hI, hJ] + exact WithBot.coe_le_coe.mpr (max_le + (WithBot.coe_le_coe.mp hs') (WithBot.coe_le_coe.mp ht')) + · rcases hI : s.ub with _ | su + · simpa [Interval.inter, hI] using ht.2 + rcases hJ : t.ub with _ | tu + · simpa [Interval.inter, hI, hJ] using hs.2 + have hs' := hs.2 + have ht' := ht.2 + rw [hI] at hs' + rw [hJ] at ht' + simp only [Interval.inter, hI, hJ] + exact WithTop.coe_le_coe.mpr (le_min + (WithTop.coe_le_coe.mp hs') (WithTop.coe_le_coe.mp ht')) + +theorem Interval.mem_hull_left [LinearOrder α] {x : α} {s t : Interval α} (hx : x ∈ s) : + x ∈ s.hull t := by + constructor + · rcases hs : s.lb with _ | sl + · simp [Interval.hull, hs] + rcases ht : t.lb with _ | tl + · simp [Interval.hull, hs, ht] + have hx' := hx.1 + rw [hs] at hx' + simp only [Interval.hull, hs, ht] + exact WithBot.coe_le_coe.mpr <| + (min_le_left sl tl).trans (WithBot.coe_le_coe.mp hx') + · rcases hs : s.ub with _ | su + · simp [Interval.hull, hs] + rcases ht : t.ub with _ | tu + · simp [Interval.hull, hs, ht] + have hx' := hx.2 + rw [hs] at hx' + simp only [Interval.hull, hs, ht] + exact WithTop.coe_le_coe.mpr <| + (WithTop.coe_le_coe.mp hx').trans (le_max_left su tu) + +theorem Interval.mem_hull_right [LinearOrder α] {x : α} {s t : Interval α} (hx : x ∈ t) : + x ∈ s.hull t := by + constructor + · rcases hs : s.lb with _ | sl + · simp [Interval.hull, hs] + rcases ht : t.lb with _ | tl + · simp [Interval.hull, hs, ht] + have hx' := hx.1 + rw [ht] at hx' + simp only [Interval.hull, hs, ht] + exact WithBot.coe_le_coe.mpr <| + (min_le_right sl tl).trans (WithBot.coe_le_coe.mp hx') + · rcases hs : s.ub with _ | su + · simp [Interval.hull, hs] + rcases ht : t.ub with _ | tu + · simp [Interval.hull, hs, ht] + have hx' := hx.2 + rw [ht] at hx' + simp only [Interval.hull, hs, ht] + exact WithTop.coe_le_coe.mpr <| + (WithTop.coe_le_coe.mp hx').trans (le_max_right su tu) + +instance [LinearOrder α] : Coarsen (Interval α) α where + coarsen := Interval.hull + mem_coarsen_left := Interval.mem_hull_left + mem_coarsen_right := Interval.mem_hull_right + +theorem Interval.mem_downwardClosure_of_le [Preorder α] {x y : α} {I : Interval α} + (hxy : x ≤ y) (hy : y ∈ I) : x ∈ I.downwardClosure := + ⟨by simp [Interval.downwardClosure], (WithTop.coe_le_coe.mpr hxy).trans hy.2⟩ + +theorem Interval.mem_upwardClosure_of_le [Preorder α] {x y : α} {I : Interval α} + (hxy : x ≤ y) (hx : x ∈ I) : y ∈ I.upwardClosure := + ⟨hx.1.trans (WithBot.coe_le_coe.mpr hxy), by simp [Interval.upwardClosure]⟩ + +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/Splitter.lean b/Mathlib/Tactic/Inclusion/Extension/Splitter.lean new file mode 100644 index 00000000000000..bfac4deef18f84 --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Extension/Splitter.lean @@ -0,0 +1,16 @@ +module + +public import Mathlib.Tactic.Inclusion.Core.ToSet + +set_option linter.style.header false + +@[expose] public section + +namespace Inclusion + +/-- A procedure for covering a represented set by sufficiently many refinements. -/ +class Splitter (Iα α : Type*) [ToSet Iα α] where + /-- The cover obtained by refining a represented set to depth `n`. -/ + cover (n : ℕ) : Cover.{0} Iα α + +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean b/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean new file mode 100644 index 00000000000000..736f35dc5440dd --- /dev/null +++ b/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean @@ -0,0 +1,287 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public meta import Mathlib.Tactic.Inclusion.ExtensionAPI.Basic + +/-! +# Attributes for `inclusion` extensions + +This file defines the attributes used to register inclusion extensions, inclusion and hypothesis +operations, hypothesis extensions, and inclusion parameters. +-/ + +public meta section + +open Lean Meta Elab Term DiscrTreeExt + +namespace Inclusion + +private def analyzeTheoremArgs (declName : Name) (pattern outputSet : Expr) + (args : Array Expr) (binderInfos : Array BinderInfo) (sourceIdx : Option Nat := none) : + MetaM (Array InclusionHypothesisArg × Array ParamArg) := do + let registeredParams := inclusionParamExt.getState (← getEnv) + let mut inputs := #[] + let mut params := #[] + for h : i in [:args.size] do + let arg := args[i] + let argDecl ← arg.mvarId!.getDecl + let argType := argDecl.type + if let some (inputExpr, inputSet, _) := toSetMem? argType then + let some exprIndex := args.findIdx? (· == inputExpr) + | throwError "The member '{inputExpr}' in premise '{argType}' of theorem \ + '{declName}' is not a theorem variable" + let some setIndex := args.findIdx? (· == inputSet) + | throwError "The set '{inputSet}' in premise '{argType}' of theorem \ + '{declName}' is not a theorem variable" + let exprId := args[exprIndex]!.mvarId! + let setId := args[setIndex]!.mvarId! + let proofId := arg.mvarId! + unless (pattern.findMVar? (· == exprId)).isSome do + throwError "The recursive input '{inputExpr}' of theorem '{declName}' does not occur in \ + the matched expression" + if (outputSet.findMVar? (· == exprId)).isSome then + throwError "The recursive input '{inputExpr}' of theorem '{declName}' occurs in its \ + output inclusion" + if (pattern.findMVar? (· == setId)).isSome then + throwError "The input set '{inputSet}' of theorem '{declName}' occurs in the matched \ + expression" + unless (outputSet.findMVar? (· == setId)).isSome do + throwError "The input set '{inputSet}' of theorem '{declName}' does not occur in its \ + output inclusion" + if (pattern.findMVar? (· == proofId)).isSome then + throwError "The inclusion hypothesis '{argType}' of theorem '{declName}' occurs in the \ + matched expression" + if (outputSet.findMVar? (· == proofId)).isSome then + throwError "The inclusion hypothesis '{argType}' of theorem '{declName}' occurs in its \ + output inclusion" + if inputs.any fun input => input.exprIdx == exprIndex then + throwError "The recursive input '{inputExpr}' occurs in more than one premise of theorem \ + '{declName}'" + if inputs.any fun input => input.setIdx == setIndex then + throwError "The input set '{inputSet}' occurs in more than one premise of theorem \ + '{declName}'" + inputs := inputs.push { exprIdx := exprIndex, setIdx := setIndex, proofIdx := i } + else if sourceIdx != some i then + let userName := argDecl.userName + if let some param := registeredParams.find? userName then + unless ← isDefEq argType param.type do + throwError "Inclusion parameter '{userName}' in '{declName}' has \ + type {argType}, expected {param.type}" + if (pattern.findMVar? fun mvarId => mvarId == arg.mvarId!).isSome then + throwError "Inclusion parameter '{userName}' in '{declName}' occurs in the matched \ + expression" + if params.any (·.name == userName) then + throwError "Inclusion parameter '{userName}' occurs more than once in '{declName}'" + params := params.push { name := userName, idx := i } + else if ← isProp argType then + unless binderInfos[i]!.isInstImplicit || + (pattern.findMVar? fun mvarId => mvarId == arg.mvarId!).isSome do + throwError "Unsupported premise '{argType}' in theorem '{declName}'" + return (inputs, params) + +section InclusionExt + +syntax (name := inclusionExtAttr) "inclusionExt " ident " | " term,+ : attr + +syntax (name := inclusionOpAttr) "inclusionOp " ident (prio)? : attr + +/-- Add the inclusion extension `declName` to `familyName` under `keys`. -/ +def addInclusionExt (familyName declName : Name) (keys : Array (Array DiscrTree.Key)) + (kind : AttributeKind) : AttrM Unit := do + let family ← getInclusionFamily familyName + let ext ← evalDecl InclusionExt ``InclusionExt declName + family.inclusionExt.add ((keys, declName), ext) kind + +private def analyzeInclusionTheorem (declName : Name) : + MetaM (Array DiscrTree.Key × Array InclusionHypothesisArg × Array ParamArg) := do + let theoremExpr ← mkConstWithFreshMVarLevels declName + let (args, binderInfos, conclusion) ← + forallMetaTelescopeReducing (← inferType theoremExpr) + let some (pattern, outputSet, _) := toSetMem? conclusion + | throwError "The conclusion of '{declName}' is not an inclusion using a `ToSet` instance" + let (inputs, params) ← analyzeTheoremArgs declName pattern outputSet args binderInfos + return (← DiscrTree.mkPath pattern, inputs, params) + +private def addInclusionOp (theoremName familyName : Name) (priority : Nat) + (kind : AttributeKind) : AttrM Unit := do + let (path, inputs, params) ← MetaM.run' <| analyzeInclusionTheorem theoremName + let declName := Name.str ((← getEnv).mainModule ++ theoremName) "_inclusionExt" + unless (← getEnv).contains declName do + let derive := mkAppN (mkConst ``deriveInclusionOp) + #[toExpr theoremName, toExpr inputs, toExpr params] + let value := mkAppN (mkConst ``InclusionExt.mk) + #[toExpr declName, toExpr theoremName, derive, toExpr priority] + let decl ← mkDefinitionValInferringUnsafe declName [] (mkConst ``InclusionExt) value .opaque + addAndCompile (markMeta := true) (.defnDecl decl) + addInclusionExt familyName declName #[path] kind + +/-- The `inclusionExt` attribute registers a handwritten inclusion extension. -/ +initialize registerBuiltinAttribute { + name := `inclusionExtAttr + descr := "adds an inclusion-function extension" + applicationTime := .afterCompilation + add := fun declName stx kind => do + let env ← getEnv + if (IR.getSorryDep env declName).isSome then return + match stx with + | `(attr| inclusionExt $familyName:ident | $es,*) => do + unless (env.getModuleIdxFor? declName).isNone do + throwError "invalid attribute `inclusionExt`, declaration is in an imported module" + ensureAttrDeclIsMeta `inclusionExt declName kind + let keys ← elabExtKeys (es.getElems.map (·.raw)) + addInclusionExt familyName.getId declName keys kind + | _ => throwUnsupportedSyntax + erase := fun _ => throwError "Inclusion extensions cannot be erased by declaration" +} + +/-- The `inclusionOp` attribute generates an inclusion extension from an inclusion theorem. -/ +initialize registerBuiltinAttribute { + name := `inclusionOpAttr + descr := "adds an inclusion operation" + applicationTime := .afterCompilation + add := fun declName stx kind => do + if (IR.getSorryDep (← getEnv) declName).isSome then return + match stx with + | `(attr| inclusionOp $familyName:ident $[$_prio:prio]?) => + addInclusionOp declName familyName.getId (← getAttrParamOptPrio stx[2]) kind + | _ => throwUnsupportedSyntax + erase := fun _ => throwError "Inclusion operations cannot be erased by declaration" +} + +end InclusionExt + +section HypothesisExt + +syntax (name := hypothesisExtAttr) "hypothesisExt " ident " | " term,+ : attr + +syntax (name := hypothesisOpAttr) "hypothesisOp " ident (prio)? : attr + +/-- Add the hypothesis extension `declName` to `familyName` under `keys`. -/ +def addHypothesisExt (familyName declName : Name) (keys : Array (Array DiscrTree.Key)) + (kind : AttributeKind) : AttrM Unit := do + let family ← getInclusionFamily familyName + let ext ← evalDecl HypothesisExt ``HypothesisExt declName + family.hypothesisExt.add ((keys, declName), ext) kind + +private def analyzeHypothesisTheorem (declName : Name) : + MetaM (Array DiscrTree.Key × Nat × Array InclusionHypothesisArg × Array ParamArg) := do + let theoremExpr ← mkConstWithFreshMVarLevels declName + let (args, binderInfos, conclusion) ← + forallMetaTelescopeReducing (← inferType theoremExpr) + let some (outputExpr, outputSet, _) := toSetMem? conclusion + | throwError "The conclusion of '{declName}' is not an inclusion using a `ToSet` instance" + let mut sourceIdx? := none + for h : i in [:args.size] do + let argType ← args[i].mvarId!.getType + if binderInfos[i]!.isExplicit && (← isProp argType) && (toSetMem? argType).isNone then + if sourceIdx?.isSome then + throwError "Hypothesis theorem '{declName}' has more than one non-inclusion premise" + sourceIdx? := some i + let some sourceIdx := sourceIdx? + | throwError "Hypothesis theorem '{declName}' has no non-inclusion premise" + let sourceId := args[sourceIdx]!.mvarId! + if (outputExpr.findMVar? (· == sourceId)).isSome || + (outputSet.findMVar? (· == sourceId)).isSome then + throwError "The source hypothesis of '{declName}' occurs in its output inclusion" + let pattern ← args[sourceIdx]!.mvarId!.getType + let (inputs, params) ← + analyzeTheoremArgs declName pattern outputSet args binderInfos (some sourceIdx) + for ⟨name, idx⟩ in params do + if (outputExpr.findMVar? (· == args[idx]!.mvarId!)).isSome then + throwError "Inclusion parameter '{name}' in '{declName}' occurs in the output expression" + return (← DiscrTree.mkPath pattern, sourceIdx, inputs, params) + +private def addHypothesisOp (theoremName familyName : Name) (priority : Nat) + (kind : AttributeKind) : AttrM Unit := do + let (path, sourceIdx, inputs, params) ← + MetaM.run' <| analyzeHypothesisTheorem theoremName + let declName := Name.str ((← getEnv).mainModule ++ theoremName) "_hypothesisExt" + unless (← getEnv).contains declName do + let derive := mkAppN (mkConst ``deriveHypothesisOp) + #[toExpr theoremName, toExpr sourceIdx, toExpr inputs, toExpr params] + let value := mkAppN (mkConst ``HypothesisExt.mk) + #[toExpr declName, toExpr theoremName, derive, toExpr priority] + let decl ← mkDefinitionValInferringUnsafe declName [] (mkConst ``HypothesisExt) value .opaque + addAndCompile (markMeta := true) (.defnDecl decl) + addHypothesisExt familyName declName #[path] kind + +/-- The `hypothesisExt` attribute registers a hypothesis extension. -/ +initialize registerBuiltinAttribute { + name := `hypothesisExtAttr + descr := "adds a hypothesis extension" + applicationTime := .afterCompilation + add := fun declName stx kind => do + let env ← getEnv + if (IR.getSorryDep env declName).isSome then return + match stx with + | `(attr| hypothesisExt $familyName:ident | $es,*) => do + unless (env.getModuleIdxFor? declName).isNone do + throwError "invalid attribute `hypothesisExt`, declaration is in an imported module" + ensureAttrDeclIsMeta `hypothesisExt declName kind + let keys ← elabExtKeys (es.getElems.map (·.raw)) + addHypothesisExt familyName.getId declName keys kind + | _ => throwUnsupportedSyntax + erase := fun _ => throwError "Hypothesis extensions cannot be erased by declaration" +} + +/-- The `hypothesisOp` attribute generates a hypothesis extension from an inclusion theorem. -/ +initialize registerBuiltinAttribute { + name := `hypothesisOpAttr + descr := "adds an inclusion-hypothesis operation" + applicationTime := .afterCompilation + add := fun declName stx kind => do + if (IR.getSorryDep (← getEnv) declName).isSome then return + match stx with + | `(attr| hypothesisOp $familyName:ident $[$_prio:prio]?) => + addHypothesisOp declName familyName.getId (← getAttrParamOptPrio stx[2]) kind + | _ => throwUnsupportedSyntax + erase := fun _ => throwError "Hypothesis operations cannot be erased by declaration" +} + +end HypothesisExt + +section Param + +syntax (name := inclusionParamAttr) "inclusionParam" : attr + +private def validateInclusionParamDecl (decl : InclusionParamDecl) : MetaM Unit := do + if decl.type.hasFVar || decl.type.hasMVar then + throwError "The type of inclusion parameter '{decl.name}' is not closed" + unless (← inferType decl.type).isSort do + throwError "The declared type {decl.type} of inclusion parameter '{decl.name}' is not a type" + if let some value := decl.defaultValue? then + if value.hasFVar || value.hasMVar then + throwError "The default value of inclusion parameter '{decl.name}' is not closed" + unless ← isDefEq (← inferType value) decl.type do + throwError "The default value {value} of inclusion parameter '{decl.name}' does not have \ + type {decl.type}" + +/-- Add the inclusion parameter declared by `declName`. -/ +def addInclusionParam (declName : Name) (kind : AttributeKind) : AttrM Unit := do + let env ← getEnv + ensureAttrDeclIsMeta `inclusionParam declName kind + unless (env.getModuleIdxFor? declName).isNone do + throwError "invalid attribute `inclusionParam`, declaration is in an imported module" + if (IR.getSorryDep env declName).isSome then return + let decl ← mkInclusionParamDecl declName + MetaM.run' <| validateInclusionParamDecl decl + let params := inclusionParamExt.getState env + if params.decls.contains decl.name then + throwError "Inclusion parameter '{decl.name}' is already registered" + inclusionParamExt.add (declName, decl) kind + +/-- The `inclusionParam` attribute registers a typed inclusion-tactic parameter. -/ +initialize registerBuiltinAttribute { + name := `inclusionParamAttr + descr := "registers an inclusion-tactic parameter" + applicationTime := .afterCompilation + add := fun declName _ kind => addInclusionParam declName kind +} + +end Param + +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean b/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean new file mode 100644 index 00000000000000..f63093e22d089e --- /dev/null +++ b/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean @@ -0,0 +1,254 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public meta import Mathlib.Tactic.Inclusion.Core.Elab + +/-! +# Basic API for `inclusion` extensions + +This file defines helpers for safely interacting with the `InclusionM` and `HypothesisM` monads +when constructing extensions for the `inclusion` tactic. +-/ + +public meta section + +open Lean Meta + +namespace Inclusion + +private def getParamDecl (name : Name) : MetaM InclusionParamDecl := do + let some decl := (inclusionParamExt.getState (← getEnv)).find? name + | throwError "Unknown inclusion parameter '{name}'" + return decl + +private def InclusionM.Context.resolveParam? (context : InclusionM.Context) (name : Name) : + MetaM (Option Expr) := do + let decl ← getParamDecl name + if let some value := context.paramSettings.find? name then + return some value + return decl.defaultValue? + +section InclusionM + +/-- Return the value of parameter `name`, if it was supplied or has a default. -/ +def getParam? (name : Name) : InclusionM (Option Expr) := do + (← read).resolveParam? name + +/-- Return the value of parameter `name`, or report that it was not supplied. -/ +def getParam (name : Name) : InclusionM Expr := do + let some value ← getParam? name + | throwError "No value was supplied for inclusion parameter '{name}'" + return value + +private def checkIVarWellFormed (localContext : LocalContext) (iExpr : IExpr) : MetaM Unit := do + let ⟨iType, e⟩ := iExpr + unless ← MetavarContext.isWellFormed localContext e do + throwError "Cannot create an inclusion variable for {e} because it depends on variables \ + introduced while constructing the inclusion" + unless ← MetavarContext.isWellFormed localContext iType.elemType do + throwError "Cannot create an inclusion variable for {e} because its type depends on \ + variables introduced while constructing the inclusion" + unless ← MetavarContext.isWellFormed localContext iType.setType do + throwError "Cannot use set type {iType.setType} for {e} because it depends on \ + variables introduced while constructing the inclusion" + unless ← MetavarContext.isWellFormed localContext iType.toSetInst do + throwError "Cannot use the `ToSet` instance for {e} because it depends on variables \ + introduced while constructing the inclusion" + +/-- Create and register an inclusion variable for `iExpr`. -/ +def mkIVar (iExpr : IExpr) (cover : Option Expr := none) : InclusionM IVar := do + let ctx ← read + checkIVarWellFormed ctx.localContext iExpr + let setVar ← + mkFreshExprMVarAt ctx.localContext ctx.localInstances iExpr.iType.setType .syntheticOpaque + let hypType ← iExpr.mkMem setVar + let hypVar ← mkFreshExprMVarAt ctx.localContext ctx.localInstances hypType .syntheticOpaque + let iVar := { iExpr, setVar, hypVar, cover } + modify fun state => { state with iVars := state.iVars.insert iVar.expr iVar } + return iVar + +/-- Construct an inclusion extension that treats matching expressions as inclusion variables. +`mkSetType` chooses the computational set type from the inferred element type, and `mkCover` may +attach a cover to the resulting inclusion variable. -/ +def mkIVarExt (mkSetType : Expr → InclusionM Expr) + (mkCover : IExpr → InclusionM (Option Expr) := fun _ ↦ pure none) + (priority : Nat := eval_prio low) (name : Name := by exact decl_name%) : InclusionExt where + declName := name + userName := name + priority := priority + derive e := do + let elemType ← inferType e + let setType ← mkSetType elemType + let toSetInst ← synthInstance (← mkAppM ``ToSet #[setType, elemType]) + let iExpr : IExpr := ⟨⟨elemType, setType, toSetInst⟩, e⟩ + return (← mkIVar iExpr (← mkCover iExpr)).toExprInclusionBody + +/-- Construct an inclusion extension for a nondependent inclusion variable with fixed element and +represented-set types. -/ +def mkNDIVarExt (elemType : Expr) (setType : MetaM Expr) + (mkCover : IExpr → InclusionM (Option Expr) := fun _ ↦ pure none) + (priority : Nat := eval_prio low) (name : Name := by exact decl_name%) : InclusionExt := + mkIVarExt + (fun actualType => do + unless ← isDefEq actualType elemType do failure + liftM setType) + mkCover (priority := priority) (name := name) + +structure InclusionHypothesisArg where + exprIdx : Nat + setIdx : Nat + proofIdx : Nat + deriving Inhabited, ToExpr + +structure ParamArg where + name : Name + idx : Nat + deriving Inhabited, ToExpr + +def deriveInclusionOp (theoremName : Name) (hypArgs : Array InclusionHypothesisArg) + (paramArgs : Array ParamArg) (e : Expr) : InclusionM ExprInclusionBody := do + let theoremExpr ← mkConstWithFreshMVarLevels theoremName + let (args, binderInfos, conclusion) ← forallMetaTelescopeReducing (← inferType theoremExpr) + let some (expr, inclusionBody, _) := toSetMem? conclusion | failure + unless ← isDefEq expr e do failure + for ⟨name, idx⟩ in paramArgs do + unless ← isDefEq args[idx]! (← getParam name) do failure + for ⟨exprIdx, setIdx, proofIdx⟩ in hypArgs do + let arg ← instantiateMVars args[exprIdx]! + let body ← mkExprInclusionBody arg + unless ← isDefEq args[setIdx]! body.inclusionBody do failure + unless ← isDefEq args[proofIdx]! body.proofBody do failure + for h : i in [:args.size] do + let argId := args[i].mvarId! + unless ← argId.isAssigned do + if binderInfos[i]!.isInstImplicit then + argId.assign (← synthInstance (← argId.getType)) + else + throwError "Could not infer theorem argument '{(← argId.getDecl).userName}' in inclusion \ + extension generated from '{theoremName}'" + return ⟨← instantiateMVars inclusionBody, ← instantiateMVars (mkAppN theoremExpr args)⟩ + +end InclusionM + +section HypothesisM + +/-- Return the value of parameter `name`, or report that it was not supplied. -/ +def HypothesisM.getParam (name : Name) : HypothesisM Expr := do + let some value ← (← read).toContext.resolveParam? name + | throwError "No value was supplied for inclusion parameter '{name}'" + return value + +/-- Find the canonical goal inclusion variable definitionally equal to `e`. Exact expression +matching is attempted first and does not invoke the elaborator. -/ +def requestedIVar? (e : Expr) : HypothesisM (Option IExpr) := do + -- Hypothesis processing uses the fixed collection of variables requested by the goal body. + let ctx ← read + -- Exact `ExprMap` lookup handles the overwhelmingly common case without unification. + if let some iVar := ctx.iVarsMap[e]? then + return some iVar.iExpr + -- Fall back to definitional equality when the hypothesis uses a reducibly different expression. + for iVar in ctx.iVars do + if ← pureIsDefEq e iVar.expr then + return some iVar.iExpr + -- Hypotheses about expressions not requested by the goal are irrelevant. + return none + +/-- Check that two inclusion types are definitionally equal, including their chosen `ToSet` +instances. -/ +def ensureOutputType (actual expected : IType) : MetaM Unit := do + -- The represented element types must agree, for example both must be `Real`. + unless ← pureIsDefEq actual.elemType expected.elemType do + -- Report the component that differs instead of a generic type mismatch. + throwError "Inclusion has expression type {actual.elemType}, expected \ + {expected.elemType}" + -- The computational set types must agree, for example both must be `Interval Dyadic`. + unless ← pureIsDefEq actual.setType expected.setType do + -- A hypothesis using a different backend cannot be substituted into the main function. + throwError "Inclusion has set type {actual.setType}, expected {expected.setType}" + -- Even equal element and set types may be interpreted by definitionally different `ToSet`s. + unless ← pureIsDefEq actual.toSetInst expected.toSetInst do + -- Require the same interpretation so that the two membership propositions agree. + throwError "Inclusion uses an unexpected `ToSet` instance" + +/-- Construct an inclusion body for a hypothesis endpoint using the same parameters as the goal +computation. -/ +def mkHypInclusionBody (e : Expr) (expected : IType) : HypothesisM ExprInclusionBody := do + -- Read the fixed hypothesis context inherited from the enclosing goal computation. + let ctx ← read + -- Recursively construct the endpoint's inclusion body and retain the resulting inclusion state. + let (body, inclusionState) ← + (mkExprInclusionBody e).runWith ctx.toContext + -- Read the represented-set type and interpretation actually produced by the endpoint body. + let iType ← body.inferIType e + -- A hypothesis endpoint must close without introducing further unknown inclusion expressions. + unless inclusionState.iVars.isEmpty do + throwError "The inclusion for {e} depends on inclusion variables" + -- Ensure that its resulting set can be used as a hypothesis for the requested expression. + ensureOutputType iType expected + -- Any remaining free variable would escape from the eventual closed inclusion. + if body.inclusionBody.hasFVar then + throwError "The computational inclusion for {e} contains a free variable" + -- Any remaining metavariable would leave the computational result under-specified. + if body.inclusionBody.hasMVar then + throwError "The computational inclusion for {e} contains a metavariable" + return body + +/-- Validate and add an inclusion hypothesis body for a requested inclusion expression. -/ +def addInclusionHyp (iExpr : IExpr) (body : ExprInclusionBody) : HypothesisM Unit := do + -- Reject candidates whose element type, represented-set type, or `ToSet` instance is unsuitable. + ensureOutputType (← body.inferIType iExpr.expr) iExpr.iType + -- Append the candidate to the array associated with the canonical requested expression. + modify fun state => { state with inclusions := state.inclusions.alter iExpr.expr fun + -- Preserve earlier candidates because they will later be combined with `Refine`. + | some hyps => hyps.push body + -- Create the candidate array when this is the first useful hypothesis for the expression. + | none => #[body] } + +/-- Apply the hypothesis extension generated from `theoremName` to hypothesis `h`. -/ +def deriveHypothesisOp (theoremName : Name) (sourceIdx : Nat) + (hypArgs : Array InclusionHypothesisArg) (paramArgs : Array ParamArg) + (h : Expr) : HypothesisM Unit := do + let type ← instantiateMVars (← inferType h) + let theoremExpr ← mkConstWithFreshMVarLevels theoremName + let (args, binderInfos, conclusion) ← forallMetaTelescopeReducing (← inferType theoremExpr) + let sourceId := args[sourceIdx]!.mvarId! + unless ← isDefEq (← sourceId.getType) type do failure + sourceId.assign h + let some (outputExpr, outputSet, outputToSetInst) := toSetMem? conclusion | failure + let outputExpr ← instantiateMVars outputExpr + let some iExpr ← requestedIVar? outputExpr | return + for ⟨name, idx⟩ in paramArgs do + unless ← isDefEq args[idx]! (← HypothesisM.getParam name) do failure + unless ← isDefEq (← inferType outputExpr) iExpr.iType.elemType do failure + unless ← isDefEq (← inferType outputSet) iExpr.iType.setType do failure + unless ← isDefEq outputToSetInst iExpr.iType.toSetInst do failure + for ⟨exprIdx, setIdx, proofIdx⟩ in hypArgs do + let inputExpr ← instantiateMVars args[exprIdx]! + let inputSet ← instantiateMVars args[setIdx]! + let proofType ← instantiateMVars (← args[proofIdx]!.mvarId!.getType) + let some (_, _, inputToSetInst) := toSetMem? proofType | failure + let expected : IType := + ⟨← inferType inputExpr, ← inferType inputSet, inputToSetInst⟩ + let body ← mkHypInclusionBody inputExpr expected + unless ← isDefEq args[setIdx]! body.inclusionBody do failure + unless ← isDefEq args[proofIdx]! body.proofBody do failure + for h : i in [:args.size] do + let argId := args[i].mvarId! + unless ← argId.isAssigned do + if binderInfos[i]!.isInstImplicit then + argId.assign (← synthInstance (← argId.getType)) + else + throwError "Could not infer theorem argument '{(← argId.getDecl).userName}' in hypothesis \ + extension generated from '{theoremName}'" + let body := + { inclusionBody := ← instantiateMVars outputSet + proofBody := ← instantiateMVars (mkAppN theoremExpr args) } + addInclusionHyp iExpr body + +end HypothesisM + +end Inclusion diff --git a/MathlibTest/Tactic/Inclusion/Basic.lean b/MathlibTest/Tactic/Inclusion/Basic.lean new file mode 100644 index 00000000000000..d39c03ae1cd35d --- /dev/null +++ b/MathlibTest/Tactic/Inclusion/Basic.lean @@ -0,0 +1,48 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +import Mathlib.Tactic.Inclusion.Extension.Extensions + +open Inclusion + +namespace Inclusion.Tests + +def unitInterval : Interval Dyadic := ⟨1, 2⟩ + +example : (1 : ℝ) + 2 ≤ 3 := by + inclusion [core, real.dyadic] + +example : -(2 : ℝ) ≤ -1 := by + inclusion [core, real.dyadic] + +example : (3 : ℝ) - 1 ≤ 2 := by + inclusion [core, real.dyadic] + +example {x : ℝ} (hx : x ∈ unitInterval) : x + x ≤ 4 := by + inclusion [core, real.dyadic] + +example {x : ℝ} (hx : x ∈ Set.Icc 1 2) : x + 1 ≤ 3 := by + inclusion [core, real.dyadic] + +example {x : ℝ} (hx : x = 2) : x + x ≤ 4 := by + inclusion [core, real.dyadic] + +example : (1 : ℝ) ≤ 2 := by + inclusion +kernel [core, real.dyadic] + +/-- info: The inclusion check succeeded. -/ +#guard_msgs in +example : (1 : ℝ) ≤ 2 := by + inclusion? [core, real.dyadic] + inclusion [core, real.dyadic] + +/-- info: The inclusion check failed: +The proposition was not proven true or false. -/ +#guard_msgs in +example (h : False) : (2 : ℝ) ≤ 1 := by + inclusion? [core, real.dyadic] + exact h.elim + +end Inclusion.Tests diff --git a/MathlibTest/Tactic/Inclusion/Splitting.lean b/MathlibTest/Tactic/Inclusion/Splitting.lean new file mode 100644 index 00000000000000..c44f3de1bf960d --- /dev/null +++ b/MathlibTest/Tactic/Inclusion/Splitting.lean @@ -0,0 +1,32 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +import Mathlib.Tactic.Inclusion.Extension.Extensions + +open Inclusion + +namespace Inclusion.Tests + +def wideInterval : Interval Dyadic := ⟨0, 4⟩ + +example {x : ℝ} (_hx : x ∈ wideInterval) : True := by + fail_if_success + have : x - x ≤ 2 := by + inclusion [core, real.dyadic] + trivial + +example {x : ℝ} (hx : x ∈ wideInterval) : x - x ≤ 2 := by + inclusion [core, real.dyadic] (binSplit := 1) + +example {x : ℝ} (hx : x ∈ wideInterval) : x - x ≤ 1 := by + inclusion [core, real.dyadic] (binSplit := 2) + +/-- info: The inclusion check succeeded. -/ +#guard_msgs in +example {x : ℝ} (hx : x ∈ wideInterval) : x - x ≤ 2 := by + inclusion? [core, real.dyadic] (binSplit := 1) + inclusion [core, real.dyadic] (binSplit := 1) + +end Inclusion.Tests From e1f75c49699f20604d519daaec2a398e5fb48e0f Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Sat, 15 Aug 2026 14:52:23 -0400 Subject: [PATCH 02/38] progress --- Mathlib/Lean/Meta/DiscrTreeExt.lean | 4 +- Mathlib/Tactic/Inclusion/Core/Core.lean | 2 +- Mathlib/Tactic/Inclusion/Core/Elab.lean | 69 ++--- Mathlib/Tactic/Inclusion/Core/Extensions.lean | 15 +- Mathlib/Tactic/Inclusion/Core/Inclusion.lean | 23 +- Mathlib/Tactic/Inclusion/Core/ToSet.lean | 2 +- .../Inclusion/Extension/Core/Hypotheses.lean | 4 +- .../Extension/DyadicReal/Extensions.lean | 8 +- .../Tactic/Inclusion/ExtensionAPI/Attr.lean | 288 ++++++++++++------ .../Tactic/Inclusion/ExtensionAPI/Basic.lean | 181 ++--------- MathlibTest/Tactic/Inclusion/Basic.lean | 5 + MathlibTest/Tactic/Inclusion/Splitting.lean | 1 + 12 files changed, 285 insertions(+), 317 deletions(-) diff --git a/Mathlib/Lean/Meta/DiscrTreeExt.lean b/Mathlib/Lean/Meta/DiscrTreeExt.lean index db513e986b4cfe..75839fc052d450 100644 --- a/Mathlib/Lean/Meta/DiscrTreeExt.lean +++ b/Mathlib/Lean/Meta/DiscrTreeExt.lean @@ -32,7 +32,7 @@ abbrev Entry := Array (Array DiscrTree.Key) × Name /-- The state of a discrimination-tree environment extension. -/ structure State (α : Type) where - /-- The discrimintation-tree of the extension. -/ + /-- The discrimination tree of the extension. -/ tree : DiscrTree α := {} deriving Inhabited @@ -41,7 +41,7 @@ abbrev EnvExt (α : Type) := ScopedEnvExtension Entry (Entry × α) (State α) variable {α : Type} -/-- Return an the array of the declaration values whose `DiscrTree` key matches `e`. -/ +/-- Return the declaration values whose `DiscrTree` keys match `e`. -/ def State.getMatch (state : State α) (e : Expr) : MetaM (Array α) := state.tree.getMatch e /-- Create a scoped environment extension whose declarations have type `typeName`. By default, the diff --git a/Mathlib/Tactic/Inclusion/Core/Core.lean b/Mathlib/Tactic/Inclusion/Core/Core.lean index 046ede27c20fdb..04668dc467f6c2 100644 --- a/Mathlib/Tactic/Inclusion/Core/Core.lean +++ b/Mathlib/Tactic/Inclusion/Core/Core.lean @@ -24,7 +24,7 @@ namespace Inclusion structure InclusionConfig where /-- If `kernel == true` then skip the compiled check. -/ kernel : Bool := false - /-- If `native == true` then use compiled computation in the proof (warning: this adds the lean + /-- If `native == true` then use compiled computation in the proof (warning: this adds the Lean compiler to the trusted codebase). -/ native : Bool := false /-- A map from inclusion parameter names to their user-supplied values. -/ diff --git a/Mathlib/Tactic/Inclusion/Core/Elab.lean b/Mathlib/Tactic/Inclusion/Core/Elab.lean index 33229333d29f73..cba27ff1fb9602 100644 --- a/Mathlib/Tactic/Inclusion/Core/Elab.lean +++ b/Mathlib/Tactic/Inclusion/Core/Elab.lean @@ -7,7 +7,6 @@ module public meta import Mathlib.Tactic.Inclusion.Core.Core meta import Lean.Elab.ConfigEval -meta import Mathlib.Tactic.Linter.UnusedTacticExtension /-! # Elaboration of the `inclusion` tactic @@ -22,54 +21,51 @@ open Lean.Parser.Tactic namespace Inclusion -/-- Configuration elaborator for `inclusion`; families and parameter values use custom syntax. -/ +/-- Declare elaborator for the configuration `InclusionConfig` of the `inclusion` tactic. -/ declare_config_elab elabInclusionConfig InclusionConfig where omit paramSettings, families -/-- Syntax for assigning an inclusion parameter. -/ -declare_syntax_cat inclusionParam - -syntax ident " := " term : inclusionParam - -/-- Elaborate and validate the list of inclusion families enabled by a tactic invocation. -/ -def elabInclusionFamilies (config : InclusionConfig) (familyStxs : Array Syntax) : +/-- Collect the array of enabled inclusion families and pass them into `config`. -/ +def collectInclusionFamilies (config : InclusionConfig) (familyStxs : Array Syntax) : TacticM InclusionConfig := do if familyStxs.isEmpty then throwError "At least one inclusion family must be specified" let mut families := #[] for familyStx in familyStxs do let family := familyStx.getId - unless (← getInclusionFamily? family).isSome do - throwError "Unknown inclusion family '{family}'" - if families.contains family then - throwError "Inclusion family '{family}' was enabled more than once" - families := families.push family + unless families.contains family do + unless (← getInclusionFamily? family).isSome do + throwError "Unknown inclusion family '{family}'" + families := families.push family return { config with families } -/-- Elaborate an inclusion-parameter value against its registered type. -/ -private def elabParamTerm (stx : Syntax) (expectedType : Expr) : TacticM Expr := do - let value ← elabTerm stx expectedType - Term.synthesizeSyntheticMVarsNoPostponing - instantiateMVars value +/-- Declare syntax category for specifying inclusion parameters. -/ +declare_syntax_cat inclusionParam -/-- Elaborate inclusion parameters. -/ -def elabInclusionParams (config : InclusionConfig) (paramStxs : Array Syntax) : +/-- Syntax for specifying an inclusion parameter. -/ +syntax ident " := " term : inclusionParam + +/-- Collect the set of enabled inclusion parameters and their user set values and pass them into +`config`. -/ +def collectInclusionParams (config : InclusionConfig) (paramStxs : Array Syntax) : TacticM InclusionConfig := do let mut config := config - let registeredParams := inclusionParamExt.getState (← getEnv) + let params := inclusionParamExt.getState (← getEnv) for paramStx in paramStxs do let (name, valueStx) ← match paramStx with - | `(inclusionParam| $name:ident := $value:term) => - pure (name.getId, value) + | `(inclusionParam| $name:ident := $value:term) => pure (name.getId, value) | _ => throwUnsupportedSyntax - let some decl := registeredParams.find? name + let some decl := params.find? name | throwError "Unknown inclusion parameter '{name}'" - let value ← elabParamTerm valueStx decl.type if config.paramSettings.contains name then throwError "Inclusion parameter '{name}' was specified more than once" + let value ← elabTerm valueStx decl.type + Term.synthesizeSyntheticMVarsNoPostponing + let value ← instantiateMVars value config := { config with paramSettings := config.paramSettings.insert name value } return config +/-- Syntax for the `inclusion` tactic. -/ syntax (name := inclusionTacStx) "inclusion" optConfig " [" ident,* "]" (" (" inclusionParam,* ")")? : tactic @@ -78,33 +74,30 @@ syntax (name := inclusionTacStx) "inclusion" optConfig " [" ident,* "]" def inclusionTac : Tactic | `(tactic| inclusion $cfg:optConfig [$families,*] $[($paramStxs,*)]?) => do let config ← elabInclusionConfig cfg - let config ← elabInclusionFamilies config families.getElems + let config ← collectInclusionFamilies config families.getElems let params := paramStxs.map (·.getElems) |>.getD #[] - let config ← elabInclusionParams config params + let config ← collectInclusionParams config params closeMainGoalUsing `inclusion fun goal _ => inclusionCore goal config | _ => throwUnsupportedSyntax -syntax (name := inclusion?TacStx) "inclusion?" optConfig " [" ident,* "]" +/-- Syntax for the `inclusion?` tactic. -/ +syntax (name := inclusion?TacStx) "inclusion?" " [" ident,* "]" (" (" inclusionParam,* ")")? : tactic /-- Tactic for quickly checking if the `inclusion` tactic will succeed. -/ @[tactic inclusion?TacStx] def inclusion?Tac : Tactic - | `(tactic| inclusion? $cfg:optConfig [$families,*] $[($paramStxs,*)]?) => do - let config ← elabInclusionConfig cfg - let config ← elabInclusionFamilies config families.getElems + | `(tactic| inclusion? [$families,*] $[($paramStxs,*)]?) => do + let config : InclusionConfig := {} + let config ← collectInclusionFamilies config families.getElems let params := paramStxs.map (·.getElems) |>.getD #[] - let config ← elabInclusionParams config params + let config ← collectInclusionParams config params withoutModifyingStateWithInfoAndMessages <| withMainContext do try - discard <| inclusionCore (← getMainTarget) - { config with kernel := false, native := false } + discard <| inclusionCore (← getMainTarget) config logInfo "The inclusion check succeeded." catch err => logInfo m!"The inclusion check failed:\n{err.toMessageData}" | _ => throwUnsupportedSyntax -initialize - Mathlib.Linter.UnusedTactic.allowedRef.modify (·.insert `Inclusion.inclusion?TacStx) - end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Core/Extensions.lean b/Mathlib/Tactic/Inclusion/Core/Extensions.lean index c68f77f05245c6..19ffc4366fba29 100644 --- a/Mathlib/Tactic/Inclusion/Core/Extensions.lean +++ b/Mathlib/Tactic/Inclusion/Core/Extensions.lean @@ -47,18 +47,19 @@ structure HypothesisExt where structure InclusionFamily where /-- The name of an inclusion family. -/ name : Name - /-- The `DiscrTree` key indexed family of inclusion extensions. -/ + /-- The `DiscrTree`-indexed collection of inclusion extensions. -/ inclusionExt : EnvExt InclusionExt - /-- The `DiscrTree` key indexed family of hypothesis extensions. -/ + /-- The `DiscrTree`-indexed collection of hypothesis extensions. -/ hypothesisExt : EnvExt HypothesisExt deriving Nonempty /-- A map from family names to registered inclusion families. -/ abbrev InclusionFamilies := Std.HashMap Name InclusionFamily +/-- The registry of inclusion families. -/ initialize inclusionFamiliesRef : IO.Ref InclusionFamilies ← IO.mkRef {} -/-- Register an inclusion family containing a separate inclusion and hypothesis extension. -/ +/-- Register an inclusion family. -/ def registerInclusionFamily (name : Name) (ref : Name := by exact decl_name%) : IO InclusionFamily := do if (← inclusionFamiliesRef.get).contains name then @@ -109,14 +110,14 @@ def getHypothesisExtMatches (families : Array Name) (e : Expr) : section InclusionParam -/-- A registered, named parameter, that can be set by the user and used across inclusion and +/-- A registered named parameter that can be set by the user and used across inclusion and hypothesis extensions. -/ structure InclusionParamDecl where /-- The name of the parameter. -/ name : Name /-- The type of the parameter (as an expression). -/ type : Expr - /-- The default value of the parameter (if `some`). -/ + /-- The default value of the parameter, if present. -/ defaultValue? : Option Expr := none /-- The collection of registered inclusion parameters, indexed by name. -/ @@ -127,8 +128,7 @@ structure InclusionParams where /-- If `name` is the name of an `InclusionParamDecl` `param` then return `some param`, otherwise return `none`. -/ -def InclusionParams.find? (params : InclusionParams) (name : Name) : - Option InclusionParamDecl := +def InclusionParams.find? (params : InclusionParams) (name : Name) : Option InclusionParamDecl := params.decls.find? name /-- Evaluate the declaration `n` as an `InclusionParamDecl`. -/ @@ -136,6 +136,7 @@ def mkInclusionParamDecl (name : Name) : ImportM InclusionParamDecl := do let { env, opts, .. } ← read IO.ofExcept <| unsafe env.evalConstCheck InclusionParamDecl opts ``InclusionParamDecl name +/-- Initialize the `InclusionParamExt` environment extension. -/ initialize inclusionParamExt : ScopedEnvExtension Name (Name × InclusionParamDecl) InclusionParams ← registerScopedEnvExtension { diff --git a/Mathlib/Tactic/Inclusion/Core/Inclusion.lean b/Mathlib/Tactic/Inclusion/Core/Inclusion.lean index aaf04814e6a821..af3cb67a1921ea 100644 --- a/Mathlib/Tactic/Inclusion/Core/Inclusion.lean +++ b/Mathlib/Tactic/Inclusion/Core/Inclusion.lean @@ -12,18 +12,13 @@ public meta import Mathlib.Lean.Meta.Basic /-! # Constructing inclusions -This file defines the two main drivers of the `inclusion` tactic. +This file defines the main drivers of the `inclusion` tactic. -Given an expression `e` +Given an expression `e`, `mkExprInclusionBody` recursively applies `InclusionExt`s to construct an +`ExprInclusionBody` for `e`. Then `toExprInclusion` applies `HypothesisExt`s to the local context to +construct inclusion hypotheses for the body's inclusion variables and closes the body into an +`ExprInclusion`. -`mkExprInclusionBody` constructs an `ExprInclusionBody` for `e`, by -(often recursively) matching and applying `InclusionExt`s to `e`. - -then - -`mkExprInclusion (e : Expr)` constructs an `ExprInclusion` for `e` from the -body by constructing inclusion hypotheses for each of the `IVars` in the body -by applying `HypothesisExt`s to local declarations, and then "closing" the body. -/ public meta section @@ -37,10 +32,10 @@ initialize registerTraceClass `Tactic.inclusion /-- Construct an `ExprInclusionBody` for `e`. -/ def mkExprInclusionBody (e : Expr) : InclusionM ExprInclusionBody := do if let some iVar := (← get).iVars[e]? then - trace[Tactic.inclusion] "Reusing ivar for {e}" + trace[Tactic.inclusion] "Reusing inclusion variable for {e}" return iVar.toExprInclusionBody - let savedState ← saveState let matchedExts ← getInclusionExtMatches (← read).families e + let savedState ← saveState for (family, ext) in matchedExts do try let body ← ext.derive e @@ -49,7 +44,7 @@ def mkExprInclusionBody (e : Expr) : InclusionM ExprInclusionBody := do return body catch err => trace[Tactic.inclusion] - "Failed to apply [{family}] {ext.userName} to {e} : {err.toMessageData}" + "Failed to apply [{family}] {ext.userName} to {e}: {err.toMessageData}" restoreState savedState throwError "No inclusion extension applies to {e}" @@ -74,7 +69,7 @@ def runHypothesisExts (h : Expr) : HypothesisM Unit := do trace[Tactic.inclusion] "[{family}] {ext.userName} processed {type}" catch err => trace[Tactic.inclusion] - "Failed to apply [{family}] {ext.userName} to {type} : {err.toMessageData}" + "Failed to apply [{family}] {ext.userName} to {type}: {err.toMessageData}" restoreState saved /-- Run hypothesis extensions on all declarations in the local context. -/ diff --git a/Mathlib/Tactic/Inclusion/Core/ToSet.lean b/Mathlib/Tactic/Inclusion/Core/ToSet.lean index 660d22d9d30ecb..7f6a4a2a957bb6 100644 --- a/Mathlib/Tactic/Inclusion/Core/ToSet.lean +++ b/Mathlib/Tactic/Inclusion/Core/ToSet.lean @@ -117,7 +117,7 @@ theorem true_of_mem_intervalBool_eq_true {p : Prop} {b : IntervalBool} (hp : p (hb : b = IntervalBool.true) : p := true_of_mem_intervalBool_true (hb ▸ hp) -/-- Return `true` when `IntervalBool = true` otherwise return `false`. -/ +/-- Return `true` exactly when the input is `IntervalBool.true`. -/ def IntervalBool.isTrue : IntervalBool → Bool | .true => Bool.true | .false | .undetermined => Bool.false diff --git a/Mathlib/Tactic/Inclusion/Extension/Core/Hypotheses.lean b/Mathlib/Tactic/Inclusion/Extension/Core/Hypotheses.lean index 5dab232efd6c73..6d1010d5dfe9a4 100644 --- a/Mathlib/Tactic/Inclusion/Extension/Core/Hypotheses.lean +++ b/Mathlib/Tactic/Inclusion/Extension/Core/Hypotheses.lean @@ -32,7 +32,7 @@ meta def directMembershipHyp : HypothesisExt where if set.hasFVar || set.hasMVar then trace[Tactic.inclusion] "Ignoring non-closed direct hypothesis {type}" failure - let some iExpr ← requestedIVar? expr | return - addInclusionHyp iExpr ⟨set, h⟩ + let some iVar ← findIVar? expr | return + addInclusionHyp iVar.iExpr ⟨set, h⟩ end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Extensions.lean b/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Extensions.lean index 3a1a6c0b53dde8..e998aa1cfe0ee4 100644 --- a/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Extensions.lean +++ b/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Extensions.lean @@ -28,12 +28,16 @@ meta def binSplitParam : InclusionParamDecl where type := mkConst ``Nat private def mkRealCover (iExpr : IExpr) : InclusionM (Option Expr) := do - let some depth ← getParam? `binSplit | return none + let some depth ← InclusionM.getParam? `binSplit | return none return some (← mkAppOptM ``Splitter.cover #[iExpr.iType.setType, iExpr.iType.elemType, iExpr.iType.toSetInst, none, depth]) @[inclusionExt real.dyadic | (_ : ℝ)] meta def mkRealIVar : InclusionExt := - mkNDIVarExt (mkConst ``Real) (mkAppM ``Interval #[mkConst ``Dyadic]) mkRealCover + mkNDIVarExt + { elemType := mkConst ``Real + setType := mkApp (mkConst ``Interval [.zero]) (mkConst ``Dyadic) + toSetInst := mkConst ``instToSetIntervalDyadicReal } + mkRealCover end Inclusion diff --git a/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean b/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean index 736f35dc5440dd..65f733cd769002 100644 --- a/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean +++ b/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean @@ -10,8 +10,8 @@ public meta import Mathlib.Tactic.Inclusion.ExtensionAPI.Basic /-! # Attributes for `inclusion` extensions -This file defines the attributes used to register inclusion extensions, inclusion and hypothesis -operations, hypothesis extensions, and inclusion parameters. +This file defines the attributes used to register handwritten inclusion and hypothesis extensions, +inclusion parameters, and theorem-generated inclusion and hypothesis operations. -/ public meta section @@ -20,6 +20,186 @@ open Lean Meta Elab Term DiscrTreeExt namespace Inclusion +section Extension + +syntax (name := inclusionExtAttr) "inclusionExt " ident " | " term,+ : attr + +/-- Add the inclusion extension `declName` to `familyName` under `keys`. -/ +def addInclusionExt (familyName declName : Name) (keys : Array (Array DiscrTree.Key)) + (kind : AttributeKind) : AttrM Unit := do + let family ← getInclusionFamily familyName + let ext ← evalDecl InclusionExt ``InclusionExt declName + family.inclusionExt.add ((keys, declName), ext) kind + +/-- The `inclusionExt` attribute registers a handwritten inclusion extension. -/ +initialize registerBuiltinAttribute { + name := `inclusionExtAttr + descr := "adds an inclusion-function extension" + applicationTime := .afterCompilation + add := fun declName stx kind => do + let env ← getEnv + if (IR.getSorryDep env declName).isSome then return + match stx with + | `(attr| inclusionExt $familyName:ident | $es,*) => do + unless (env.getModuleIdxFor? declName).isNone do + throwError "invalid attribute `inclusionExt`, declaration is in an imported module" + ensureAttrDeclIsMeta `inclusionExt declName kind + let keys ← elabExtKeys (es.getElems.map (·.raw)) + addInclusionExt familyName.getId declName keys kind + | _ => throwUnsupportedSyntax + erase := fun _ => throwError "Inclusion extensions cannot be erased by declaration" +} + +syntax (name := hypothesisExtAttr) "hypothesisExt " ident " | " term,+ : attr + +/-- Add the hypothesis extension `declName` to `familyName` under `keys`. -/ +def addHypothesisExt (familyName declName : Name) (keys : Array (Array DiscrTree.Key)) + (kind : AttributeKind) : AttrM Unit := do + let family ← getInclusionFamily familyName + let ext ← evalDecl HypothesisExt ``HypothesisExt declName + family.hypothesisExt.add ((keys, declName), ext) kind + +/-- The `hypothesisExt` attribute registers a hypothesis extension. -/ +initialize registerBuiltinAttribute { + name := `hypothesisExtAttr + descr := "adds a hypothesis extension" + applicationTime := .afterCompilation + add := fun declName stx kind => do + let env ← getEnv + if (IR.getSorryDep env declName).isSome then return + match stx with + | `(attr| hypothesisExt $familyName:ident | $es,*) => do + unless (env.getModuleIdxFor? declName).isNone do + throwError "invalid attribute `hypothesisExt`, declaration is in an imported module" + ensureAttrDeclIsMeta `hypothesisExt declName kind + let keys ← elabExtKeys (es.getElems.map (·.raw)) + addHypothesisExt familyName.getId declName keys kind + | _ => throwUnsupportedSyntax + erase := fun _ => throwError "Hypothesis extensions cannot be erased by declaration" +} + +end Extension + +section Param + +syntax (name := inclusionParamAttr) "inclusionParam" : attr + +private def validateInclusionParamDecl (decl : InclusionParamDecl) : MetaM Unit := do + if decl.type.hasFVar || decl.type.hasMVar then + throwError "The type of inclusion parameter '{decl.name}' is not closed" + unless (← inferType decl.type).isSort do + throwError "The declared type {decl.type} of inclusion parameter '{decl.name}' is not a type" + if let some value := decl.defaultValue? then + if value.hasFVar || value.hasMVar then + throwError "The default value of inclusion parameter '{decl.name}' is not closed" + unless ← isDefEq (← inferType value) decl.type do + throwError "The default value {value} of inclusion parameter '{decl.name}' does not have \ + type {decl.type}" + +/-- Add the inclusion parameter declared by `declName`. -/ +def addInclusionParam (declName : Name) (kind : AttributeKind) : AttrM Unit := do + let env ← getEnv + ensureAttrDeclIsMeta `inclusionParam declName kind + unless (env.getModuleIdxFor? declName).isNone do + throwError "invalid attribute `inclusionParam`, declaration is in an imported module" + if (IR.getSorryDep env declName).isSome then return + let decl ← mkInclusionParamDecl declName + MetaM.run' <| validateInclusionParamDecl decl + let params := inclusionParamExt.getState env + if params.decls.contains decl.name then + throwError "Inclusion parameter '{decl.name}' is already registered" + inclusionParamExt.add (declName, decl) kind + +/-- The `inclusionParam` attribute registers a typed inclusion-tactic parameter. -/ +initialize registerBuiltinAttribute { + name := `inclusionParamAttr + descr := "registers an inclusion-tactic parameter" + applicationTime := .afterCompilation + add := fun declName _ kind => addInclusionParam declName kind +} + +end Param + +section Operation + +/-- The argument indices of an expression, its inclusion set, and its membership proof in an +inclusion theorem. -/ +structure InclusionHypothesisArg where + /-- The index of the expression argument. -/ + exprIdx : Nat + /-- The index of the inclusion-set argument. -/ + setIdx : Nat + /-- The index of the membership-proof argument. -/ + proofIdx : Nat + deriving Inhabited, ToExpr + +/-- A registered inclusion parameter and the index of its argument in an inclusion theorem. -/ +structure ParamArg where + /-- The name of the registered inclusion parameter. -/ + name : Name + /-- The index of the corresponding theorem argument. -/ + idx : Nat + deriving Inhabited, ToExpr + +/-- Apply the inclusion theorem `theoremName` to `e`, recursively constructing the inclusion bodies +specified by `hypArgs` and filling the registered parameter arguments specified by `paramArgs`. -/ +def deriveInclusionOp (theoremName : Name) (hypArgs : Array InclusionHypothesisArg) + (paramArgs : Array ParamArg) (e : Expr) : InclusionM ExprInclusionBody := do + let theoremExpr ← mkConstWithFreshMVarLevels theoremName + let (args, binderInfos, conclusion) ← forallMetaTelescopeReducing (← inferType theoremExpr) + let some (expr, inclusionBody, _) := toSetMem? conclusion | failure + unless ← isDefEq expr e do failure + for ⟨name, idx⟩ in paramArgs do + unless ← isDefEq args[idx]! (← InclusionM.getParam name) do failure + for ⟨exprIdx, setIdx, proofIdx⟩ in hypArgs do + let arg ← instantiateMVars args[exprIdx]! + let body ← mkExprInclusionBody arg + unless ← isDefEq args[setIdx]! body.inclusionBody do failure + unless ← isDefEq args[proofIdx]! body.proofBody do failure + for h : i in [:args.size] do + let argId := args[i].mvarId! + unless ← argId.isAssigned do + if binderInfos[i]!.isInstImplicit then + argId.assign (← synthInstance (← argId.getType)) + else + throwError "Could not infer theorem argument '{(← argId.getDecl).userName}' in inclusion \ + extension generated from '{theoremName}'" + return ⟨← instantiateMVars inclusionBody, ← instantiateMVars (mkAppN theoremExpr args)⟩ + +/-- Apply the hypothesis extension generated from `theoremName` to hypothesis `h`. -/ +def deriveHypothesisOp (theoremName : Name) (sourceIdx : Nat) + (hypArgs : Array InclusionHypothesisArg) (paramArgs : Array ParamArg) + (h : Expr) : HypothesisM Unit := do + let type ← instantiateMVars (← inferType h) + let theoremExpr ← mkConstWithFreshMVarLevels theoremName + let (args, binderInfos, conclusion) ← forallMetaTelescopeReducing (← inferType theoremExpr) + let sourceId := args[sourceIdx]!.mvarId! + unless ← isDefEq (← sourceId.getType) type do failure + sourceId.assign h + let some (outputExpr, outputSet, _) := toSetMem? conclusion | failure + let outputExpr ← instantiateMVars outputExpr + let some iVar ← findIVar? outputExpr | return + let iExpr := iVar.iExpr + for ⟨name, idx⟩ in paramArgs do + unless ← isDefEq args[idx]! (← HypothesisM.getParam name) do failure + for ⟨exprIdx, setIdx, proofIdx⟩ in hypArgs do + let inputExpr ← instantiateMVars args[exprIdx]! + let body ← mkHypExprInclusionBody inputExpr + unless ← isDefEq args[setIdx]! body.inclusionBody do failure + unless ← isDefEq args[proofIdx]! body.proofBody do failure + for h : i in [:args.size] do + let argId := args[i].mvarId! + unless ← argId.isAssigned do + if binderInfos[i]!.isInstImplicit then + argId.assign (← synthInstance (← argId.getType)) + else + throwError "Could not infer theorem argument '{(← argId.getDecl).userName}' in hypothesis \ + extension generated from '{theoremName}'" + let body := + { inclusionBody := ← instantiateMVars outputSet + proofBody := ← instantiateMVars (mkAppN theoremExpr args) } + addInclusionHyp iExpr body + private def analyzeTheoremArgs (declName : Name) (pattern outputSet : Expr) (args : Array Expr) (binderInfos : Array BinderInfo) (sourceIdx : Option Nat := none) : MetaM (Array InclusionHypothesisArg × Array ParamArg) := do @@ -83,19 +263,10 @@ private def analyzeTheoremArgs (declName : Name) (pattern outputSet : Expr) throwError "Unsupported premise '{argType}' in theorem '{declName}'" return (inputs, params) -section InclusionExt - -syntax (name := inclusionExtAttr) "inclusionExt " ident " | " term,+ : attr +section InclusionOp syntax (name := inclusionOpAttr) "inclusionOp " ident (prio)? : attr -/-- Add the inclusion extension `declName` to `familyName` under `keys`. -/ -def addInclusionExt (familyName declName : Name) (keys : Array (Array DiscrTree.Key)) - (kind : AttributeKind) : AttrM Unit := do - let family ← getInclusionFamily familyName - let ext ← evalDecl InclusionExt ``InclusionExt declName - family.inclusionExt.add ((keys, declName), ext) kind - private def analyzeInclusionTheorem (declName : Name) : MetaM (Array DiscrTree.Key × Array InclusionHypothesisArg × Array ParamArg) := do let theoremExpr ← mkConstWithFreshMVarLevels declName @@ -119,25 +290,6 @@ private def addInclusionOp (theoremName familyName : Name) (priority : Nat) addAndCompile (markMeta := true) (.defnDecl decl) addInclusionExt familyName declName #[path] kind -/-- The `inclusionExt` attribute registers a handwritten inclusion extension. -/ -initialize registerBuiltinAttribute { - name := `inclusionExtAttr - descr := "adds an inclusion-function extension" - applicationTime := .afterCompilation - add := fun declName stx kind => do - let env ← getEnv - if (IR.getSorryDep env declName).isSome then return - match stx with - | `(attr| inclusionExt $familyName:ident | $es,*) => do - unless (env.getModuleIdxFor? declName).isNone do - throwError "invalid attribute `inclusionExt`, declaration is in an imported module" - ensureAttrDeclIsMeta `inclusionExt declName kind - let keys ← elabExtKeys (es.getElems.map (·.raw)) - addInclusionExt familyName.getId declName keys kind - | _ => throwUnsupportedSyntax - erase := fun _ => throwError "Inclusion extensions cannot be erased by declaration" -} - /-- The `inclusionOp` attribute generates an inclusion extension from an inclusion theorem. -/ initialize registerBuiltinAttribute { name := `inclusionOpAttr @@ -152,21 +304,12 @@ initialize registerBuiltinAttribute { erase := fun _ => throwError "Inclusion operations cannot be erased by declaration" } -end InclusionExt +end InclusionOp -section HypothesisExt - -syntax (name := hypothesisExtAttr) "hypothesisExt " ident " | " term,+ : attr +section HypothesisOp syntax (name := hypothesisOpAttr) "hypothesisOp " ident (prio)? : attr -/-- Add the hypothesis extension `declName` to `familyName` under `keys`. -/ -def addHypothesisExt (familyName declName : Name) (keys : Array (Array DiscrTree.Key)) - (kind : AttributeKind) : AttrM Unit := do - let family ← getInclusionFamily familyName - let ext ← evalDecl HypothesisExt ``HypothesisExt declName - family.hypothesisExt.add ((keys, declName), ext) kind - private def analyzeHypothesisTheorem (declName : Name) : MetaM (Array DiscrTree.Key × Nat × Array InclusionHypothesisArg × Array ParamArg) := do let theoremExpr ← mkConstWithFreshMVarLevels declName @@ -209,25 +352,6 @@ private def addHypothesisOp (theoremName familyName : Name) (priority : Nat) addAndCompile (markMeta := true) (.defnDecl decl) addHypothesisExt familyName declName #[path] kind -/-- The `hypothesisExt` attribute registers a hypothesis extension. -/ -initialize registerBuiltinAttribute { - name := `hypothesisExtAttr - descr := "adds a hypothesis extension" - applicationTime := .afterCompilation - add := fun declName stx kind => do - let env ← getEnv - if (IR.getSorryDep env declName).isSome then return - match stx with - | `(attr| hypothesisExt $familyName:ident | $es,*) => do - unless (env.getModuleIdxFor? declName).isNone do - throwError "invalid attribute `hypothesisExt`, declaration is in an imported module" - ensureAttrDeclIsMeta `hypothesisExt declName kind - let keys ← elabExtKeys (es.getElems.map (·.raw)) - addHypothesisExt familyName.getId declName keys kind - | _ => throwUnsupportedSyntax - erase := fun _ => throwError "Hypothesis extensions cannot be erased by declaration" -} - /-- The `hypothesisOp` attribute generates a hypothesis extension from an inclusion theorem. -/ initialize registerBuiltinAttribute { name := `hypothesisOpAttr @@ -242,46 +366,8 @@ initialize registerBuiltinAttribute { erase := fun _ => throwError "Hypothesis operations cannot be erased by declaration" } -end HypothesisExt +end HypothesisOp -section Param - -syntax (name := inclusionParamAttr) "inclusionParam" : attr - -private def validateInclusionParamDecl (decl : InclusionParamDecl) : MetaM Unit := do - if decl.type.hasFVar || decl.type.hasMVar then - throwError "The type of inclusion parameter '{decl.name}' is not closed" - unless (← inferType decl.type).isSort do - throwError "The declared type {decl.type} of inclusion parameter '{decl.name}' is not a type" - if let some value := decl.defaultValue? then - if value.hasFVar || value.hasMVar then - throwError "The default value of inclusion parameter '{decl.name}' is not closed" - unless ← isDefEq (← inferType value) decl.type do - throwError "The default value {value} of inclusion parameter '{decl.name}' does not have \ - type {decl.type}" - -/-- Add the inclusion parameter declared by `declName`. -/ -def addInclusionParam (declName : Name) (kind : AttributeKind) : AttrM Unit := do - let env ← getEnv - ensureAttrDeclIsMeta `inclusionParam declName kind - unless (env.getModuleIdxFor? declName).isNone do - throwError "invalid attribute `inclusionParam`, declaration is in an imported module" - if (IR.getSorryDep env declName).isSome then return - let decl ← mkInclusionParamDecl declName - MetaM.run' <| validateInclusionParamDecl decl - let params := inclusionParamExt.getState env - if params.decls.contains decl.name then - throwError "Inclusion parameter '{decl.name}' is already registered" - inclusionParamExt.add (declName, decl) kind - -/-- The `inclusionParam` attribute registers a typed inclusion-tactic parameter. -/ -initialize registerBuiltinAttribute { - name := `inclusionParamAttr - descr := "registers an inclusion-tactic parameter" - applicationTime := .afterCompilation - add := fun declName _ kind => addInclusionParam declName kind -} - -end Param +end Operation end Inclusion diff --git a/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean b/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean index f63093e22d089e..32d65731cc1686 100644 --- a/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean +++ b/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean @@ -20,30 +20,35 @@ open Lean Meta namespace Inclusion -private def getParamDecl (name : Name) : MetaM InclusionParamDecl := do +/-- Return the value of parameter `name`, if it was supplied or has a default. -/ +def InclusionM.getParam? (name : Name) : InclusionM (Option Expr) := do let some decl := (inclusionParamExt.getState (← getEnv)).find? name | throwError "Unknown inclusion parameter '{name}'" - return decl - -private def InclusionM.Context.resolveParam? (context : InclusionM.Context) (name : Name) : - MetaM (Option Expr) := do - let decl ← getParamDecl name - if let some value := context.paramSettings.find? name then + if let some value := (← read).paramSettings.find? name then return some value return decl.defaultValue? -section InclusionM +/-- Return the value of parameter `name`. -/ +def InclusionM.getParam (name : Name) : InclusionM Expr := do + let some value ← InclusionM.getParam? name + | throwError "No value was supplied for inclusion parameter '{name}'" + return value /-- Return the value of parameter `name`, if it was supplied or has a default. -/ -def getParam? (name : Name) : InclusionM (Option Expr) := do - (← read).resolveParam? name +def HypothesisM.getParam? (name : Name) : HypothesisM (Option Expr) := do + let some decl := (inclusionParamExt.getState (← getEnv)).find? name + | throwError "Unknown inclusion parameter '{name}'" + if let some value := (← read).paramSettings.find? name then + return some value + return decl.defaultValue? /-- Return the value of parameter `name`, or report that it was not supplied. -/ -def getParam (name : Name) : InclusionM Expr := do - let some value ← getParam? name +def HypothesisM.getParam (name : Name) : HypothesisM Expr := do + let some value ← HypothesisM.getParam? name | throwError "No value was supplied for inclusion parameter '{name}'" return value +/-- Check that `iExpr` is well formed in `localContext`. -/ private def checkIVarWellFormed (localContext : LocalContext) (iExpr : IExpr) : MetaM Unit := do let ⟨iType, e⟩ := iExpr unless ← MetavarContext.isWellFormed localContext e do @@ -71,91 +76,22 @@ def mkIVar (iExpr : IExpr) (cover : Option Expr := none) : InclusionM IVar := do modify fun state => { state with iVars := state.iVars.insert iVar.expr iVar } return iVar -/-- Construct an inclusion extension that treats matching expressions as inclusion variables. -`mkSetType` chooses the computational set type from the inferred element type, and `mkCover` may -attach a cover to the resulting inclusion variable. -/ -def mkIVarExt (mkSetType : Expr → InclusionM Expr) +/-- Construct an inclusion extension for making non dependently typed inclusion variables. -/ +def mkNDIVarExt (iType : IType) (mkCover : IExpr → InclusionM (Option Expr) := fun _ ↦ pure none) (priority : Nat := eval_prio low) (name : Name := by exact decl_name%) : InclusionExt where declName := name userName := name priority := priority derive e := do - let elemType ← inferType e - let setType ← mkSetType elemType - let toSetInst ← synthInstance (← mkAppM ``ToSet #[setType, elemType]) - let iExpr : IExpr := ⟨⟨elemType, setType, toSetInst⟩, e⟩ + let eType ← inferType e + unless ← isDefEq eType iType.elemType do failure + let iExpr : IExpr := ⟨iType, e⟩ return (← mkIVar iExpr (← mkCover iExpr)).toExprInclusionBody -/-- Construct an inclusion extension for a nondependent inclusion variable with fixed element and -represented-set types. -/ -def mkNDIVarExt (elemType : Expr) (setType : MetaM Expr) - (mkCover : IExpr → InclusionM (Option Expr) := fun _ ↦ pure none) - (priority : Nat := eval_prio low) (name : Name := by exact decl_name%) : InclusionExt := - mkIVarExt - (fun actualType => do - unless ← isDefEq actualType elemType do failure - liftM setType) - mkCover (priority := priority) (name := name) - -structure InclusionHypothesisArg where - exprIdx : Nat - setIdx : Nat - proofIdx : Nat - deriving Inhabited, ToExpr - -structure ParamArg where - name : Name - idx : Nat - deriving Inhabited, ToExpr - -def deriveInclusionOp (theoremName : Name) (hypArgs : Array InclusionHypothesisArg) - (paramArgs : Array ParamArg) (e : Expr) : InclusionM ExprInclusionBody := do - let theoremExpr ← mkConstWithFreshMVarLevels theoremName - let (args, binderInfos, conclusion) ← forallMetaTelescopeReducing (← inferType theoremExpr) - let some (expr, inclusionBody, _) := toSetMem? conclusion | failure - unless ← isDefEq expr e do failure - for ⟨name, idx⟩ in paramArgs do - unless ← isDefEq args[idx]! (← getParam name) do failure - for ⟨exprIdx, setIdx, proofIdx⟩ in hypArgs do - let arg ← instantiateMVars args[exprIdx]! - let body ← mkExprInclusionBody arg - unless ← isDefEq args[setIdx]! body.inclusionBody do failure - unless ← isDefEq args[proofIdx]! body.proofBody do failure - for h : i in [:args.size] do - let argId := args[i].mvarId! - unless ← argId.isAssigned do - if binderInfos[i]!.isInstImplicit then - argId.assign (← synthInstance (← argId.getType)) - else - throwError "Could not infer theorem argument '{(← argId.getDecl).userName}' in inclusion \ - extension generated from '{theoremName}'" - return ⟨← instantiateMVars inclusionBody, ← instantiateMVars (mkAppN theoremExpr args)⟩ - -end InclusionM - -section HypothesisM - -/-- Return the value of parameter `name`, or report that it was not supplied. -/ -def HypothesisM.getParam (name : Name) : HypothesisM Expr := do - let some value ← (← read).toContext.resolveParam? name - | throwError "No value was supplied for inclusion parameter '{name}'" - return value - -/-- Find the canonical goal inclusion variable definitionally equal to `e`. Exact expression -matching is attempted first and does not invoke the elaborator. -/ -def requestedIVar? (e : Expr) : HypothesisM (Option IExpr) := do - -- Hypothesis processing uses the fixed collection of variables requested by the goal body. - let ctx ← read - -- Exact `ExprMap` lookup handles the overwhelmingly common case without unification. - if let some iVar := ctx.iVarsMap[e]? then - return some iVar.iExpr - -- Fall back to definitional equality when the hypothesis uses a reducibly different expression. - for iVar in ctx.iVars do - if ← pureIsDefEq e iVar.expr then - return some iVar.iExpr - -- Hypotheses about expressions not requested by the goal are irrelevant. - return none +/-- Return the inclusion variable registered for `e`, if there is one. -/ +def findIVar? (e : Expr) : HypothesisM (Option IVar) := do + return (← read).iVarsMap[e]? /-- Check that two inclusion types are definitionally equal, including their chosen `ToSet` instances. -/ @@ -174,30 +110,20 @@ def ensureOutputType (actual expected : IType) : MetaM Unit := do -- Require the same interpretation so that the two membership propositions agree. throwError "Inclusion uses an unexpected `ToSet` instance" -/-- Construct an inclusion body for a hypothesis endpoint using the same parameters as the goal -computation. -/ -def mkHypInclusionBody (e : Expr) (expected : IType) : HypothesisM ExprInclusionBody := do - -- Read the fixed hypothesis context inherited from the enclosing goal computation. +/-- Construct and validate an inclusion body for an expression argument of a hypothesis rule. -/ +def mkHypExprInclusionBody (e : Expr) : HypothesisM ExprInclusionBody := do let ctx ← read - -- Recursively construct the endpoint's inclusion body and retain the resulting inclusion state. - let (body, inclusionState) ← - (mkExprInclusionBody e).runWith ctx.toContext - -- Read the represented-set type and interpretation actually produced by the endpoint body. - let iType ← body.inferIType e - -- A hypothesis endpoint must close without introducing further unknown inclusion expressions. + let (body, inclusionState) ← (mkExprInclusionBody e).runWith ctx.toContext unless inclusionState.iVars.isEmpty do throwError "The inclusion for {e} depends on inclusion variables" - -- Ensure that its resulting set can be used as a hypothesis for the requested expression. - ensureOutputType iType expected - -- Any remaining free variable would escape from the eventual closed inclusion. if body.inclusionBody.hasFVar then - throwError "The computational inclusion for {e} contains a free variable" - -- Any remaining metavariable would leave the computational result under-specified. + throwError "The inclusion hypothesis generated from {e} contains a free variable" if body.inclusionBody.hasMVar then - throwError "The computational inclusion for {e} contains a metavariable" + throwError "The inclusion hypothesis generated from {e} contains a metavariable" + discard <| body.inferIType e return body -/-- Validate and add an inclusion hypothesis body for a requested inclusion expression. -/ +/-- Add the inclusion hypothesis `body` for `iExpr`. -/ def addInclusionHyp (iExpr : IExpr) (body : ExprInclusionBody) : HypothesisM Unit := do -- Reject candidates whose element type, represented-set type, or `ToSet` instance is unsuitable. ensureOutputType (← body.inferIType iExpr.expr) iExpr.iType @@ -208,47 +134,4 @@ def addInclusionHyp (iExpr : IExpr) (body : ExprInclusionBody) : HypothesisM Uni -- Create the candidate array when this is the first useful hypothesis for the expression. | none => #[body] } -/-- Apply the hypothesis extension generated from `theoremName` to hypothesis `h`. -/ -def deriveHypothesisOp (theoremName : Name) (sourceIdx : Nat) - (hypArgs : Array InclusionHypothesisArg) (paramArgs : Array ParamArg) - (h : Expr) : HypothesisM Unit := do - let type ← instantiateMVars (← inferType h) - let theoremExpr ← mkConstWithFreshMVarLevels theoremName - let (args, binderInfos, conclusion) ← forallMetaTelescopeReducing (← inferType theoremExpr) - let sourceId := args[sourceIdx]!.mvarId! - unless ← isDefEq (← sourceId.getType) type do failure - sourceId.assign h - let some (outputExpr, outputSet, outputToSetInst) := toSetMem? conclusion | failure - let outputExpr ← instantiateMVars outputExpr - let some iExpr ← requestedIVar? outputExpr | return - for ⟨name, idx⟩ in paramArgs do - unless ← isDefEq args[idx]! (← HypothesisM.getParam name) do failure - unless ← isDefEq (← inferType outputExpr) iExpr.iType.elemType do failure - unless ← isDefEq (← inferType outputSet) iExpr.iType.setType do failure - unless ← isDefEq outputToSetInst iExpr.iType.toSetInst do failure - for ⟨exprIdx, setIdx, proofIdx⟩ in hypArgs do - let inputExpr ← instantiateMVars args[exprIdx]! - let inputSet ← instantiateMVars args[setIdx]! - let proofType ← instantiateMVars (← args[proofIdx]!.mvarId!.getType) - let some (_, _, inputToSetInst) := toSetMem? proofType | failure - let expected : IType := - ⟨← inferType inputExpr, ← inferType inputSet, inputToSetInst⟩ - let body ← mkHypInclusionBody inputExpr expected - unless ← isDefEq args[setIdx]! body.inclusionBody do failure - unless ← isDefEq args[proofIdx]! body.proofBody do failure - for h : i in [:args.size] do - let argId := args[i].mvarId! - unless ← argId.isAssigned do - if binderInfos[i]!.isInstImplicit then - argId.assign (← synthInstance (← argId.getType)) - else - throwError "Could not infer theorem argument '{(← argId.getDecl).userName}' in hypothesis \ - extension generated from '{theoremName}'" - let body := - { inclusionBody := ← instantiateMVars outputSet - proofBody := ← instantiateMVars (mkAppN theoremExpr args) } - addInclusionHyp iExpr body - -end HypothesisM - end Inclusion diff --git a/MathlibTest/Tactic/Inclusion/Basic.lean b/MathlibTest/Tactic/Inclusion/Basic.lean index d39c03ae1cd35d..5e13bba4e2a003 100644 --- a/MathlibTest/Tactic/Inclusion/Basic.lean +++ b/MathlibTest/Tactic/Inclusion/Basic.lean @@ -14,6 +14,9 @@ def unitInterval : Interval Dyadic := ⟨1, 2⟩ example : (1 : ℝ) + 2 ≤ 3 := by inclusion [core, real.dyadic] +example : (1 : ℝ) + 2 ≤ 3 := by + inclusion [core, real.dyadic, core, real.dyadic] + example : -(2 : ℝ) ≤ -1 := by inclusion [core, real.dyadic] @@ -34,6 +37,7 @@ example : (1 : ℝ) ≤ 2 := by /-- info: The inclusion check succeeded. -/ #guard_msgs in +set_option linter.unusedTactic false in example : (1 : ℝ) ≤ 2 := by inclusion? [core, real.dyadic] inclusion [core, real.dyadic] @@ -41,6 +45,7 @@ example : (1 : ℝ) ≤ 2 := by /-- info: The inclusion check failed: The proposition was not proven true or false. -/ #guard_msgs in +set_option linter.unusedTactic false in example (h : False) : (2 : ℝ) ≤ 1 := by inclusion? [core, real.dyadic] exact h.elim diff --git a/MathlibTest/Tactic/Inclusion/Splitting.lean b/MathlibTest/Tactic/Inclusion/Splitting.lean index c44f3de1bf960d..02d84cd25845c3 100644 --- a/MathlibTest/Tactic/Inclusion/Splitting.lean +++ b/MathlibTest/Tactic/Inclusion/Splitting.lean @@ -25,6 +25,7 @@ example {x : ℝ} (hx : x ∈ wideInterval) : x - x ≤ 1 := by /-- info: The inclusion check succeeded. -/ #guard_msgs in +set_option linter.unusedTactic false in example {x : ℝ} (hx : x ∈ wideInterval) : x - x ≤ 2 := by inclusion? [core, real.dyadic] (binSplit := 1) inclusion [core, real.dyadic] (binSplit := 1) From 86a2bb51fb74efae186814893816d3383fface36 Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Sun, 16 Aug 2026 02:43:19 -0400 Subject: [PATCH 03/38] more progress --- Mathlib.lean | 16 +- Mathlib/Data/Dyadic.lean | 93 ++++ Mathlib/Tactic.lean | 15 +- Mathlib/Tactic/Inclusion/Core/ToSet.lean | 47 ++ Mathlib/Tactic/Inclusion/Core/Types.lean | 2 + .../Tactic/Inclusion/Extension/Core/Core.lean | 45 ++ .../Inclusion/Extension/Core/Family.lean | 5 +- .../Inclusion/Extension/Core/Hypotheses.lean | 38 -- .../Inclusion/Extension/DyadicReal/Basic.lean | 226 -------- .../Extension/DyadicReal/Dyadic.lean | 31 -- .../Extension/DyadicReal/Extensions.lean | 43 -- .../Extension/DyadicReal/Family.lean | 22 - .../Extension/DyadicReal/Hypotheses.lean | 90 ---- .../Inclusion/Extension/Extensions.lean | 16 - .../Tactic/Inclusion/Extension/Interval.lean | 483 +++++++++++++----- .../Extension/IntervalDyadicReal/Basic.lean | 123 +++++ .../IntervalDyadicReal/Extensions.lean | 45 ++ .../IntervalDyadicReal/Hypotheses.lean | 76 +++ .../Extension/IntervalDyadicReal/Init.lean | 47 ++ .../Splitting.lean | 47 +- .../Tactic/Inclusion/Extension/Splitter.lean | 16 - .../Tactic/Inclusion/ExtensionAPI/Attr.lean | 100 ++-- .../Tactic/Inclusion/ExtensionAPI/Basic.lean | 35 +- MathlibTest/Tactic/Inclusion/Basic.lean | 170 +++++- MathlibTest/Tactic/Inclusion/Splitting.lean | 19 +- 25 files changed, 1104 insertions(+), 746 deletions(-) create mode 100644 Mathlib/Data/Dyadic.lean create mode 100644 Mathlib/Tactic/Inclusion/Extension/Core/Core.lean delete mode 100644 Mathlib/Tactic/Inclusion/Extension/Core/Hypotheses.lean delete mode 100644 Mathlib/Tactic/Inclusion/Extension/DyadicReal/Basic.lean delete mode 100644 Mathlib/Tactic/Inclusion/Extension/DyadicReal/Dyadic.lean delete mode 100644 Mathlib/Tactic/Inclusion/Extension/DyadicReal/Extensions.lean delete mode 100644 Mathlib/Tactic/Inclusion/Extension/DyadicReal/Family.lean delete mode 100644 Mathlib/Tactic/Inclusion/Extension/DyadicReal/Hypotheses.lean delete mode 100644 Mathlib/Tactic/Inclusion/Extension/Extensions.lean create mode 100644 Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Basic.lean create mode 100644 Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Extensions.lean create mode 100644 Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Hypotheses.lean create mode 100644 Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Init.lean rename Mathlib/Tactic/Inclusion/Extension/{DyadicReal => IntervalDyadicReal}/Splitting.lean (53%) delete mode 100644 Mathlib/Tactic/Inclusion/Extension/Splitter.lean diff --git a/Mathlib.lean b/Mathlib.lean index 6064e4f3c00ff7..3a28bed35fdd02 100644 --- a/Mathlib.lean +++ b/Mathlib.lean @@ -3847,6 +3847,7 @@ public import Mathlib.Data.DFinsupp.Small public import Mathlib.Data.DFinsupp.Submonoid public import Mathlib.Data.DFinsupp.WellFounded public import Mathlib.Data.DList.Instances +public import Mathlib.Data.Dyadic public import Mathlib.Data.ENNReal.Action public import Mathlib.Data.ENNReal.Basic public import Mathlib.Data.ENNReal.BigOperators @@ -7371,17 +7372,14 @@ public import Mathlib.Tactic.Inclusion.Core.Extensions public import Mathlib.Tactic.Inclusion.Core.Inclusion public import Mathlib.Tactic.Inclusion.Core.ToSet public import Mathlib.Tactic.Inclusion.Core.Types +public import Mathlib.Tactic.Inclusion.Extension.Core.Core public import Mathlib.Tactic.Inclusion.Extension.Core.Family -public import Mathlib.Tactic.Inclusion.Extension.Core.Hypotheses -public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Basic -public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Dyadic -public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Extensions -public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Family -public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Hypotheses -public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Splitting -public import Mathlib.Tactic.Inclusion.Extension.Extensions public import Mathlib.Tactic.Inclusion.Extension.Interval -public import Mathlib.Tactic.Inclusion.Extension.Splitter +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Basic +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Extensions +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Hypotheses +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Init +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Splitting public import Mathlib.Tactic.Inclusion.ExtensionAPI.Attr public import Mathlib.Tactic.Inclusion.ExtensionAPI.Basic public import Mathlib.Tactic.InferParam diff --git a/Mathlib/Data/Dyadic.lean b/Mathlib/Data/Dyadic.lean new file mode 100644 index 00000000000000..21e1a2d5c5a453 --- /dev/null +++ b/Mathlib/Data/Dyadic.lean @@ -0,0 +1,93 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public import Mathlib.Data.Real.Basic +public import Mathlib.Data.Rat.Cast.Order + +/-! +# Dyadic rationals + +This file provides general API for Dyadic rationals that are used in Mathlib but not available in +core. +-/ + +@[expose] public section + +instance : LinearOrder Dyadic where + le_refl := Std.IsPreorder.le_refl + le_trans := Std.IsPreorder.le_trans + le_antisymm := Std.IsPartialOrder.le_antisymm + lt_iff_le_not_ge := Std.LawfulOrderLT.lt_iff + le_total := Std.IsLinearOrder.le_total + toDecidableLE := Dyadic.instDecidableLE + +instance : AddCommGroup Dyadic where + nsmul := nsmulRec + zsmul := zsmulRec + add_zero := Dyadic.add_zero + zero_add := Dyadic.zero_add + add_assoc := Dyadic.add_assoc + sub_eq_add_neg _ _ := rfl + neg_add_cancel := Dyadic.neg_add_cancel + add_comm := Dyadic.add_comm + +namespace Dyadic + +section toReal + +/-- Interpret a dyadic rational as a real number. -/ +def toReal (d : Dyadic) : ℝ := d.toRat + +@[simp] +lemma toReal_add (a b : Dyadic) : toReal (a + b) = toReal a + toReal b := by + simp [toReal] + +@[simp] +lemma toReal_neg (a : Dyadic) : toReal (-a) = -toReal a := by + simp [toReal] + +@[simp] +lemma toReal_sub (a b : Dyadic) : toReal (a - b) = toReal a - toReal b := by + simp [toReal] + +@[simp] +lemma toReal_natCast (n : ℕ) : toReal (n : Dyadic) = (n : ℝ) := by + simp [toReal] + +@[simp] +lemma toReal_intCast (z : ℤ) : toReal (z : Dyadic) = (z : ℝ) := by + simp [toReal] + +@[simp] +lemma toReal_le_toReal {a b : Dyadic} : toReal a ≤ toReal b ↔ a ≤ b := by + simp [toReal] + +@[simp] +lemma toReal_lt_toReal {a b : Dyadic} : toReal a < toReal b ↔ a < b := by + simp [toReal] + +/-- `Dyadic.toReal` as an additive monoid homomorphism. -/ +def toRealAddMonoidHom : Dyadic →+ ℝ where + toFun := toReal + map_zero' := by simp [toReal] + map_add' := toReal_add + +/-- `Dyadic.toReal` as an order embedding. -/ +def toRealOrderEmbedding : Dyadic ↪o ℝ := + OrderEmbedding.ofStrictMono toReal fun _ _ h ↦ toReal_lt_toReal.mpr h + +@[simp] +lemma toReal_min (a b : Dyadic) : toReal (min a b) = min (toReal a) (toReal b) := + toRealOrderEmbedding.monotone.map_min + +@[simp] +lemma toReal_max (a b : Dyadic) : toReal (max a b) = max (toReal a) (toReal b) := + toRealOrderEmbedding.monotone.map_max + +end toReal + +end Dyadic diff --git a/Mathlib/Tactic.lean b/Mathlib/Tactic.lean index a02fb203d9ec5a..9d3db1ba38a591 100644 --- a/Mathlib/Tactic.lean +++ b/Mathlib/Tactic.lean @@ -161,17 +161,14 @@ public import Mathlib.Tactic.Inclusion.Core.Extensions public import Mathlib.Tactic.Inclusion.Core.Inclusion public import Mathlib.Tactic.Inclusion.Core.ToSet public import Mathlib.Tactic.Inclusion.Core.Types +public import Mathlib.Tactic.Inclusion.Extension.Core.Core public import Mathlib.Tactic.Inclusion.Extension.Core.Family -public import Mathlib.Tactic.Inclusion.Extension.Core.Hypotheses -public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Basic -public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Dyadic -public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Extensions -public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Family -public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Hypotheses -public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Splitting -public import Mathlib.Tactic.Inclusion.Extension.Extensions public import Mathlib.Tactic.Inclusion.Extension.Interval -public import Mathlib.Tactic.Inclusion.Extension.Splitter +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Basic +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Extensions +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Hypotheses +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Init +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Splitting public import Mathlib.Tactic.Inclusion.ExtensionAPI.Attr public import Mathlib.Tactic.Inclusion.ExtensionAPI.Basic public import Mathlib.Tactic.InferParam diff --git a/Mathlib/Tactic/Inclusion/Core/ToSet.lean b/Mathlib/Tactic/Inclusion/Core/ToSet.lean index 7f6a4a2a957bb6..f225cb940305cf 100644 --- a/Mathlib/Tactic/Inclusion/Core/ToSet.lean +++ b/Mathlib/Tactic/Inclusion/Core/ToSet.lean @@ -71,6 +71,13 @@ class Coarsen (Iα α : Type*) [ToSet Iα α] where /-- If `x ∈ t` then `x ∈ coarsen s t`. -/ mem_coarsen_right {x : α} {s t : Iα} (hx : x ∈ t) : x ∈ coarsen s t +theorem Coarsen.mem_coarsen_of_or {Iα α : Type*} [ToSet Iα α] [Coarsen Iα α] + {p q : Prop} {x : α} {s t : Iα} (h : p ∨ q) + (hp : p → x ∈ s) (hq : q → x ∈ t) : + x ∈ Coarsen.coarsen (α := α) s t := + h.elim (fun hp' ↦ Coarsen.mem_coarsen_left (hp hp')) + (fun hq' ↦ Coarsen.mem_coarsen_right (hq hq')) + universe u /-- A `Cover Iα α` specifies a function `coverMap` to compute a "refined" inclusion of `F s` @@ -110,6 +117,46 @@ def IntervalBool.toPropSet : IntervalBool → Set Prop instance : ToSet IntervalBool Prop := ⟨IntervalBool.toPropSet⟩ +theorem IntervalBool.mem_true {p : Prop} (hp : p) : p ∈ IntervalBool.true := by + simpa [ToSet.toSet, IntervalBool.toPropSet] using hp + +theorem IntervalBool.mem_false {p : Prop} (hp : ¬p) : p ∈ IntervalBool.false := by + simpa [ToSet.toSet, IntervalBool.toPropSet] using hp + +theorem IntervalBool.mem_undetermined (p : Prop) : p ∈ IntervalBool.undetermined := by + by_cases hp : p <;> simp [ToSet.toSet, IntervalBool.toPropSet, hp] + +/-- Negation of an `IntervalBool` value. -/ +def IntervalBool.not : IntervalBool → IntervalBool + | .true => .false + | .false => .true + | .undetermined => .undetermined + +theorem IntervalBool.not_mem {p : Prop} {a : IntervalBool} + (hp : p ∈ a) : (¬p) ∈ a.not := by + cases a <;> by_cases hp' : p <;> simp_all [IntervalBool.not, ToSet.toSet, IntervalBool.toPropSet] + +/-- Conjunction of two `IntervalBool` values. -/ +def IntervalBool.and : IntervalBool → IntervalBool → IntervalBool + | .true, .true => .true + | .false, _ | _, .false => .false + | _, _ => .undetermined + +theorem IntervalBool.and_mem {p q : Prop} {a b : IntervalBool} + (hp : p ∈ a) (hq : q ∈ b) : (p ∧ q) ∈ a.and b := by + cases a <;> cases b <;> simp_all [IntervalBool.and, ToSet.toSet, IntervalBool.toPropSet] + +/-- Disjunction of two `IntervalBool` values. -/ +def IntervalBool.or : IntervalBool → IntervalBool → IntervalBool + | .true, _ | _, .true => .true + | .false, .false => .false + | _, _ => .undetermined + +theorem IntervalBool.or_mem {p q : Prop} {a b : IntervalBool} + (hp : p ∈ a) (hq : q ∈ b) : (p ∨ q) ∈ a.or b := by + cases a <;> cases b <;> by_cases hp' : p <;> by_cases hq' : q <;> + simp_all [IntervalBool.or, ToSet.toSet, IntervalBool.toPropSet] + theorem true_of_mem_intervalBool_true {p : Prop} (hp : p ∈ IntervalBool.true) : p := by simpa [mem_set_iff_mem_toSet, toSet, IntervalBool.toPropSet] using hp diff --git a/Mathlib/Tactic/Inclusion/Core/Types.lean b/Mathlib/Tactic/Inclusion/Core/Types.lean index 916052fa798058..a2be58ef9d11f6 100644 --- a/Mathlib/Tactic/Inclusion/Core/Types.lean +++ b/Mathlib/Tactic/Inclusion/Core/Types.lean @@ -100,6 +100,8 @@ structure InclusionM.Context where paramSettings : NameMap Expr /-- The names of the inclusion extension families to use. -/ families : Array Name + /-- If `noIVars` is `true` then inclusion extensions should not register `IVar`s. -/ + noIVars : Bool := false /-- The mutable state of the `InclusionM` monad. -/ structure InclusionM.State where diff --git a/Mathlib/Tactic/Inclusion/Extension/Core/Core.lean b/Mathlib/Tactic/Inclusion/Extension/Core/Core.lean new file mode 100644 index 00000000000000..716065893465ef --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Extension/Core/Core.lean @@ -0,0 +1,45 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public import Mathlib.Tactic.Inclusion.Core.ToSet +public meta import Mathlib.Tactic.Inclusion.Extension.Core.Family +public meta import Mathlib.Tactic.Inclusion.ExtensionAPI.Attr + +/-! +# Core extensions for the `inclusion` tactic + +This file defines inclusion and hypothesis extensions for the core inclusion family. +-/ + +public meta section + +open Lean Meta + +namespace Inclusion + +attribute [inclusionOp core] IntervalBool.not_mem IntervalBool.and_mem IntervalBool.or_mem +attribute [hypothesisOp core] ToSet.mem_of_eq_of_mem ToSet.mem_of_mem_of_eq + +/-- `HypothesisExt` for direct `ToSet` instance membership hypotheses. -/ +@[hypothesisExt core | _ ∈ _] +def instMembershipHyp : HypothesisExt where + derive h := do + let type ← instantiateMVars (← inferType h) + let some (expr, set, _) := toSetMem? type | failure + if set.hasFVar || set.hasMVar then failure + let some iVar ← findIVar? expr | return + addInclusionHyp iVar.iExpr ⟨set, h⟩ + +/-- `HypothesisExt` for conjunction hypotheses. -/ +@[hypothesisExt core | _ ∧ _] +def andHyp : HypothesisExt where + derive h := do + let (``And, #[_, _]) := (← instantiateMVars (← inferType h)).getAppFnArgs | failure + runHypothesisExts (← mkAppM ``And.left #[h]) + runHypothesisExts (← mkAppM ``And.right #[h]) + +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/Core/Family.lean b/Mathlib/Tactic/Inclusion/Extension/Core/Family.lean index a7390c602b38b3..386d60f3676aa9 100644 --- a/Mathlib/Tactic/Inclusion/Extension/Core/Family.lean +++ b/Mathlib/Tactic/Inclusion/Extension/Core/Family.lean @@ -10,14 +10,15 @@ public meta import Mathlib.Tactic.Inclusion.Core.Extensions /-! # Core extension family for the `inclusion` tactic -This file registers the extension family containing rules that are independent of the represented -set implementation. +This file initializes the `core` family of inclusion and hypothesis extensions which are generally +useful and type independent. -/ public meta section namespace Inclusion +/-- Initialize the `core` inclusion family. -/ initialize coreFamily : InclusionFamily ← registerInclusionFamily `core end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/Core/Hypotheses.lean b/Mathlib/Tactic/Inclusion/Extension/Core/Hypotheses.lean deleted file mode 100644 index 6d1010d5dfe9a4..00000000000000 --- a/Mathlib/Tactic/Inclusion/Extension/Core/Hypotheses.lean +++ /dev/null @@ -1,38 +0,0 @@ -/- -Copyright (c) 2026 David Ledvinka. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: David Ledvinka --/ -module - -public meta import Mathlib.Tactic.Inclusion.Extension.Core.Family -public meta import Mathlib.Tactic.Inclusion.ExtensionAPI.Attr - -/-! -# Generic hypothesis extensions for the `inclusion` tactic - -This file registers hypothesis rules that work for every represented-set implementation. --/ - -public meta section - -open Lean Meta - -namespace Inclusion - -attribute [hypothesisOp core] ToSet.mem_of_eq_of_mem ToSet.mem_of_mem_of_eq - -/-- The generic hypothesis extension that uses a closed `ToSet` membership hypothesis directly as -an inclusion hypothesis. -/ -@[hypothesisExt core | _ ∈ _] -meta def directMembershipHyp : HypothesisExt where - derive h := do - let type ← instantiateMVars (← inferType h) - let some (expr, set, _) := toSetMem? type | failure - if set.hasFVar || set.hasMVar then - trace[Tactic.inclusion] "Ignoring non-closed direct hypothesis {type}" - failure - let some iVar ← findIVar? expr | return - addInclusionHyp iVar.iExpr ⟨set, h⟩ - -end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Basic.lean b/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Basic.lean deleted file mode 100644 index 0664dee3f0270d..00000000000000 --- a/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Basic.lean +++ /dev/null @@ -1,226 +0,0 @@ -/- -Copyright (c) 2026 David Ledvinka. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: David Ledvinka --/ -module - -public import Mathlib.Tactic.Inclusion.Extension.Interval -public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Dyadic -public meta import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Family -public meta import Mathlib.Tactic.Inclusion.ExtensionAPI.Attr - -/-! -# Basic dyadic interval operations for real expressions - -This file defines the core operations for the `real.dyadic` inclusion family whose computational -implementations are suitable for general use. --/ - -set_option linter.style.header false - -@[expose] public section - -namespace Inclusion - -instance : ToSet (Interval Dyadic) ℝ where - toSet I := (I.map Dyadic.toReal).toSet - -def ofNat (n : ℕ) : Interval Dyadic := Interval.singleton Dyadic n - -def add (x y : Interval Dyadic) : Interval Dyadic where - lb := match x.lb, y.lb with - | some a, some b => some (a + b) - | _, _ => ⊥ - ub := match x.ub, y.ub with - | some a, some b => some (a + b) - | _, _ => ⊤ - -def neg (x : Interval Dyadic) : Interval Dyadic where - lb := match x.ub with - | some a => some (-a) - | ⊤ => ⊥ - ub := match x.lb with - | some a => some (-a) - | ⊥ => ⊤ - -def sub (x y : Interval Dyadic) : Interval Dyadic where - lb := match x.lb, y.ub with - | some a, some b => some (a - b) - | _, _ => ⊥ - ub := match x.ub, y.lb with - | some a, some b => some (a - b) - | _, _ => ⊤ - -def le (x y : Interval Dyadic) : IntervalBool := - match x.ub, y.lb with - | some xu, some yl => if xu ≤ yl then .true else .undetermined - | _, _ => .undetermined - -theorem mem_univ (r : ℝ) : r ∈ Interval.univ Dyadic := by - constructor <;> simp [Interval.univ, Interval.map] - -@[simp] -lemma toReal_add (a b : Dyadic) : - Dyadic.toReal (a + b) = Dyadic.toReal a + Dyadic.toReal b := by - simp [Dyadic.toReal, Dyadic.toRat_add] - -@[simp] -lemma toReal_neg (a : Dyadic) : Dyadic.toReal (-a) = -Dyadic.toReal a := by - simp [Dyadic.toReal, Dyadic.toRat_neg] - -@[simp] -lemma toReal_sub (a b : Dyadic) : - Dyadic.toReal (a - b) = Dyadic.toReal a - Dyadic.toReal b := by - simp [Dyadic.toReal, Dyadic.toRat_sub] - -lemma toReal_le_toReal {a b : Dyadic} : Dyadic.toReal a ≤ Dyadic.toReal b ↔ a ≤ b := by - simp [Dyadic.toReal] - -@[simp] -lemma toReal_min (a b : Dyadic) : - Dyadic.toReal (min a b) = min (Dyadic.toReal a) (Dyadic.toReal b) := by - rcases le_total a b with h | h - · rw [min_eq_left h, min_eq_left (toReal_le_toReal.mpr h)] - · rw [min_eq_right h, min_eq_right (toReal_le_toReal.mpr h)] - -@[simp] -lemma toReal_max (a b : Dyadic) : - Dyadic.toReal (max a b) = max (Dyadic.toReal a) (Dyadic.toReal b) := by - rcases le_total a b with h | h - · rw [max_eq_right h, max_eq_right (toReal_le_toReal.mpr h)] - · rw [max_eq_left h, max_eq_left (toReal_le_toReal.mpr h)] - -theorem map_inter (I J : Interval Dyadic) : - (I.inter J).map Dyadic.toReal = (I.map Dyadic.toReal).inter (J.map Dyadic.toReal) := by - rcases I with ⟨il, iu⟩ - rcases J with ⟨jl, ju⟩ - cases il <;> cases iu <;> cases jl <;> cases ju <;> - simp [Interval.inter, Interval.map, toReal_min, toReal_max] - -theorem inter_mem {r : ℝ} {I J : Interval Dyadic} (hI : r ∈ I) (hJ : r ∈ J) : - r ∈ I.inter J := by - change r ∈ (I.inter J).map Dyadic.toReal - rw [map_inter] - exact Refine.mem_refine (Iα := Interval ℝ) - (hI : r ∈ I.map Dyadic.toReal) (hJ : r ∈ J.map Dyadic.toReal) - -instance : Univ (Interval Dyadic) ℝ where - univ := Interval.univ Dyadic - mem_univ := mem_univ - -instance : Refine (Interval Dyadic) ℝ where - refine := Interval.inter - mem_refine := inter_mem - -theorem map_hull (I J : Interval Dyadic) : - (I.hull J).map Dyadic.toReal = (I.map Dyadic.toReal).hull (J.map Dyadic.toReal) := by - rcases I with ⟨il, iu⟩ - rcases J with ⟨jl, ju⟩ - cases il <;> cases iu <;> cases jl <;> cases ju <;> - simp [Interval.hull, Interval.map, toReal_min, toReal_max] - -theorem hull_mem_left {r : ℝ} {I J : Interval Dyadic} (hI : r ∈ I) : r ∈ I.hull J := by - change r ∈ (I.hull J).map Dyadic.toReal - rw [map_hull] - exact Coarsen.mem_coarsen_left (Iα := Interval ℝ) (hI : r ∈ I.map Dyadic.toReal) - -theorem hull_mem_right {r : ℝ} {I J : Interval Dyadic} (hJ : r ∈ J) : r ∈ I.hull J := by - change r ∈ (I.hull J).map Dyadic.toReal - rw [map_hull] - exact Coarsen.mem_coarsen_right (Iα := Interval ℝ) (hJ : r ∈ J.map Dyadic.toReal) - -instance : Coarsen (Interval Dyadic) ℝ where - coarsen := Interval.hull - mem_coarsen_left := hull_mem_left - mem_coarsen_right := hull_mem_right - -@[inclusionOp real.dyadic] -theorem ofNat_mem (n : ℕ) : (OfNat.ofNat n : ℝ) ∈ ofNat n := by - constructor - · exact WithBot.coe_le_coe.mpr <| by - simp [Dyadic.toReal, Dyadic.toRat_natCast, Semiring.toGrindSemiring_ofNat ℝ n] - · exact WithTop.coe_le_coe.mpr <| by - simp [Dyadic.toReal, Dyadic.toRat_natCast, Semiring.toGrindSemiring_ofNat ℝ n] - -@[inclusionOp real.dyadic] -theorem add_mem {r s : ℝ} {x y : Interval Dyadic} - (hrx : r ∈ x) (hsy : s ∈ y) : r + s ∈ add x y := by - match x, y with - | ⟨xl, xu⟩, ⟨yl, yu⟩ => - constructor - · match xl, yl with - | ⊥, _ => simp [add, Interval.map] - | xl, ⊥ => cases xl <;> simp [add, Interval.map] - | some a, some b => - exact WithBot.coe_le_coe.mpr <| by - rw [toReal_add] - exact add_le_add (WithBot.coe_le_coe.mp hrx.1) (WithBot.coe_le_coe.mp hsy.1) - · match xu, yu with - | ⊤, _ => simp [add, Interval.map] - | xu, ⊤ => cases xu <;> simp [add, Interval.map] - | some a, some b => - exact WithTop.coe_le_coe.mpr <| by - rw [toReal_add] - exact add_le_add (WithTop.coe_le_coe.mp hrx.2) (WithTop.coe_le_coe.mp hsy.2) - -@[inclusionOp real.dyadic] -theorem neg_mem {r : ℝ} {x : Interval Dyadic} (hrx : r ∈ x) : -r ∈ neg x := by - match x with - | ⟨xl, xu⟩ => - constructor - · match xu with - | ⊤ => simp [neg, Interval.map] - | some a => - exact WithBot.coe_le_coe.mpr <| by - rw [toReal_neg] - exact neg_le_neg (WithTop.coe_le_coe.mp hrx.2) - · match xl with - | ⊥ => simp [neg, Interval.map] - | some a => - exact WithTop.coe_le_coe.mpr <| by - rw [toReal_neg] - exact neg_le_neg (WithBot.coe_le_coe.mp hrx.1) - -@[inclusionOp real.dyadic] -theorem sub_mem {r s : ℝ} {x y : Interval Dyadic} - (hrx : r ∈ x) (hsy : s ∈ y) : r - s ∈ sub x y := by - match x, y with - | ⟨xl, xu⟩, ⟨yl, yu⟩ => - constructor - · match xl, yu with - | ⊥, _ => simp [sub, Interval.map] - | xl, ⊤ => cases xl <;> simp [sub, Interval.map] - | some a, some b => - exact WithBot.coe_le_coe.mpr <| by - rw [toReal_sub] - exact sub_le_sub (WithBot.coe_le_coe.mp hrx.1) (WithTop.coe_le_coe.mp hsy.2) - · match xu, yl with - | ⊤, _ => simp [sub, Interval.map] - | xu, ⊥ => cases xu <;> simp [sub, Interval.map] - | some a, some b => - exact WithTop.coe_le_coe.mpr <| by - rw [toReal_sub] - exact sub_le_sub (WithTop.coe_le_coe.mp hrx.2) (WithBot.coe_le_coe.mp hsy.1) - -theorem mem_intervalBool_true {p : Prop} (hp : p) : p ∈ IntervalBool.true := by - simpa [ToSet.toSet, IntervalBool.toPropSet] using hp - -theorem mem_intervalBool_undetermined (p : Prop) : p ∈ IntervalBool.undetermined := by - classical - by_cases hp : p <;> simp [ToSet.toSet, IntervalBool.toPropSet, hp] - -@[inclusionOp real.dyadic] -theorem le_mem {r s : ℝ} {x y : Interval Dyadic} - (hrx : r ∈ x) (hsy : s ∈ y) : (r ≤ s) ∈ le x y := by - match x, y with - | ⟨_, some xu⟩, ⟨some yl, _⟩ => - simp only [le] - split_ifs with h - · apply mem_intervalBool_true - exact (WithTop.coe_le_coe.mp hrx.2).trans - ((Monotone.dyadicToReal h).trans (WithBot.coe_le_coe.mp hsy.1)) - · exact mem_intervalBool_undetermined _ - | ⟨_, ⊤⟩, _ | ⟨_, some _⟩, ⟨⊥, _⟩ => exact mem_intervalBool_undetermined _ - -end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Dyadic.lean b/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Dyadic.lean deleted file mode 100644 index b0f84ad6c6f897..00000000000000 --- a/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Dyadic.lean +++ /dev/null @@ -1,31 +0,0 @@ -/- -Copyright (c) 2026 David Ledvinka. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: David Ledvinka --/ -module - -public import Mathlib.Data.Real.Basic -public import Mathlib.Data.Rat.Cast.Order - -set_option linter.style.header false - -@[expose] public section - -namespace Inclusion - -instance : LinearOrder Dyadic where - le_refl := Std.IsPreorder.le_refl - le_trans := Std.IsPreorder.le_trans - le_antisymm := Std.IsPartialOrder.le_antisymm - lt_iff_le_not_ge := Std.LawfulOrderLT.lt_iff - le_total := Std.IsLinearOrder.le_total - toDecidableLE := Dyadic.instDecidableLE - -def Dyadic.toReal (d : Dyadic) : ℝ := d.toRat - -theorem Monotone.dyadicToReal : Monotone Dyadic.toReal := by - intro _ _ h - simp [Dyadic.toReal, h] - -end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Extensions.lean b/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Extensions.lean deleted file mode 100644 index e998aa1cfe0ee4..00000000000000 --- a/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Extensions.lean +++ /dev/null @@ -1,43 +0,0 @@ -/- -Copyright (c) 2026 David Ledvinka. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: David Ledvinka --/ -module - -public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Basic -public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Splitting -public meta import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Basic -public meta import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Splitting -public meta import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Hypotheses -public meta import Mathlib.Tactic.Inclusion.ExtensionAPI.Basic - -/-! -# Inclusion extensions for dyadic real intervals --/ - -public meta section - -open Lean Meta - -namespace Inclusion - -@[inclusionParam] -meta def binSplitParam : InclusionParamDecl where - name := `binSplit - type := mkConst ``Nat - -private def mkRealCover (iExpr : IExpr) : InclusionM (Option Expr) := do - let some depth ← InclusionM.getParam? `binSplit | return none - return some (← mkAppOptM ``Splitter.cover - #[iExpr.iType.setType, iExpr.iType.elemType, iExpr.iType.toSetInst, none, depth]) - -@[inclusionExt real.dyadic | (_ : ℝ)] -meta def mkRealIVar : InclusionExt := - mkNDIVarExt - { elemType := mkConst ``Real - setType := mkApp (mkConst ``Interval [.zero]) (mkConst ``Dyadic) - toSetInst := mkConst ``instToSetIntervalDyadicReal } - mkRealCover - -end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Family.lean b/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Family.lean deleted file mode 100644 index 281f7d59d2d961..00000000000000 --- a/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Family.lean +++ /dev/null @@ -1,22 +0,0 @@ -/- -Copyright (c) 2026 David Ledvinka. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: David Ledvinka --/ -module - -public meta import Mathlib.Tactic.Inclusion.Core.Extensions - -/-! -# Dyadic real extension family for the `inclusion` tactic - -This file registers the family using dyadic intervals to enclose real expressions. --/ - -public meta section - -namespace Inclusion - -initialize realDyadicFamily : InclusionFamily ← registerInclusionFamily `real.dyadic - -end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Hypotheses.lean b/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Hypotheses.lean deleted file mode 100644 index 12648195fa39e3..00000000000000 --- a/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Hypotheses.lean +++ /dev/null @@ -1,90 +0,0 @@ -/- -Copyright (c) 2026 David Ledvinka. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: David Ledvinka --/ -module - -public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Basic - -/-! -# Hypothesis operations for dyadic real intervals --/ - -@[expose] public section - -namespace Inclusion - -private theorem mem_bounds {a b x : ℝ} {I J : Interval Dyadic} - (ha : a ∈ I) (hax : a ≤ x) (hxb : x ≤ b) (hb : b ∈ J) : - x ∈ (⟨I.lb, J.ub⟩ : Interval Dyadic) := - ⟨ha.1.trans (WithBot.coe_le_coe.mpr hax), - (WithTop.coe_le_coe.mpr hxb).trans hb.2⟩ - -@[hypothesisOp real.dyadic] -theorem downwardClosure_mem {x y : ℝ} {I : Interval Dyadic} - (hxy : x ≤ y) (hy : y ∈ I) : x ∈ I.downwardClosure := - ⟨by simp [Interval.downwardClosure, Interval.map], - (WithTop.coe_le_coe.mpr hxy).trans hy.2⟩ - -@[hypothesisOp real.dyadic] -theorem upwardClosure_mem {x y : ℝ} {I : Interval Dyadic} - (hxy : x ≤ y) (hx : x ∈ I) : y ∈ I.upwardClosure := - ⟨hx.1.trans (WithBot.coe_le_coe.mpr hxy), - by simp [Interval.upwardClosure, Interval.map]⟩ - -@[hypothesisOp real.dyadic] -theorem downwardClosure_mem_of_lt {x y : ℝ} {I : Interval Dyadic} - (hxy : x < y) (hy : y ∈ I) : x ∈ I.downwardClosure := - downwardClosure_mem hxy.le hy - -@[hypothesisOp real.dyadic] -theorem upwardClosure_mem_of_lt {x y : ℝ} {I : Interval Dyadic} - (hxy : x < y) (hx : x ∈ I) : y ∈ I.upwardClosure := - upwardClosure_mem hxy.le hx - -@[hypothesisOp real.dyadic] -theorem upwardClosure_mem_of_mem_Ici {a x : ℝ} {I : Interval Dyadic} - (hx : x ∈ Set.Ici a) (ha : a ∈ I) : x ∈ I.upwardClosure := - upwardClosure_mem (Set.mem_Ici.mp hx) ha - -@[hypothesisOp real.dyadic] -theorem upwardClosure_mem_of_mem_Ioi {a x : ℝ} {I : Interval Dyadic} - (hx : x ∈ Set.Ioi a) (ha : a ∈ I) : x ∈ I.upwardClosure := - upwardClosure_mem (Set.mem_Ioi.mp hx).le ha - -@[hypothesisOp real.dyadic] -theorem downwardClosure_mem_of_mem_Iic {b x : ℝ} {I : Interval Dyadic} - (hx : x ∈ Set.Iic b) (hb : b ∈ I) : x ∈ I.downwardClosure := - downwardClosure_mem (Set.mem_Iic.mp hx) hb - -@[hypothesisOp real.dyadic] -theorem downwardClosure_mem_of_mem_Iio {b x : ℝ} {I : Interval Dyadic} - (hx : x ∈ Set.Iio b) (hb : b ∈ I) : x ∈ I.downwardClosure := - downwardClosure_mem (Set.mem_Iio.mp hx).le hb - -@[hypothesisOp real.dyadic] -theorem bounds_mem_of_mem_Ico {a b x : ℝ} {I J : Interval Dyadic} - (hx : x ∈ Set.Ico a b) (ha : a ∈ I) (hb : b ∈ J) : - x ∈ (⟨I.lb, J.ub⟩ : Interval Dyadic) := - mem_bounds ha hx.1 hx.2.le hb - -@[hypothesisOp real.dyadic] -theorem bounds_mem_of_mem_Ioc {a b x : ℝ} {I J : Interval Dyadic} - (hx : x ∈ Set.Ioc a b) (ha : a ∈ I) (hb : b ∈ J) : - x ∈ (⟨I.lb, J.ub⟩ : Interval Dyadic) := - mem_bounds ha hx.1.le hx.2 hb - -@[hypothesisOp real.dyadic] -theorem bounds_mem_of_mem_Icc {a b x : ℝ} {I J : Interval Dyadic} - (hx : x ∈ Set.Icc a b) (ha : a ∈ I) (hb : b ∈ J) : - x ∈ (⟨I.lb, J.ub⟩ : Interval Dyadic) := - mem_bounds ha hx.1 hx.2 hb - -@[hypothesisOp real.dyadic] -theorem bounds_mem_of_mem_Ioo {a b x : ℝ} {I J : Interval Dyadic} - (hx : x ∈ Set.Ioo a b) (ha : a ∈ I) (hb : b ∈ J) : - x ∈ (⟨I.lb, J.ub⟩ : Interval Dyadic) := - mem_bounds ha hx.1.le hx.2.le hb - -end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/Extensions.lean b/Mathlib/Tactic/Inclusion/Extension/Extensions.lean deleted file mode 100644 index 70b043cef9a6a5..00000000000000 --- a/Mathlib/Tactic/Inclusion/Extension/Extensions.lean +++ /dev/null @@ -1,16 +0,0 @@ -/- -Copyright (c) 2026 David Ledvinka. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: David Ledvinka --/ -module - -public meta import Mathlib.Tactic.Inclusion.Extension.Core.Hypotheses -public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Extensions -public meta import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Extensions - -/-! -# Standard extensions for the `inclusion` tactic - -This file imports the polished extension families currently provided with the tactic. --/ diff --git a/Mathlib/Tactic/Inclusion/Extension/Interval.lean b/Mathlib/Tactic/Inclusion/Extension/Interval.lean index 72e75b67d5edb1..c0ac46736d07a0 100644 --- a/Mathlib/Tactic/Inclusion/Extension/Interval.lean +++ b/Mathlib/Tactic/Inclusion/Extension/Interval.lean @@ -1,154 +1,407 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ module -public import Mathlib.Tactic.Inclusion.Core.ToSet +public import Mathlib.Algebra.Group.Hom.Defs +public import Mathlib.Algebra.Order.Group.Defs +public import Mathlib.Order.Hom.Basic +public import Mathlib.Order.Interval.Set.Defs +public import Mathlib.Order.MinMax public import Mathlib.Order.WithBot +public import Mathlib.Tactic.Inclusion.Core.ToSet -set_option linter.style.header false +/-! +# (possibly unbounded) intervals -@[expose] public section +This file defines the `Interval` type for computation in the `inclusion` tactic. This +type represents a possibly unbounded interval with closed endpoints. +-/ -open Set +@[expose] public section namespace Inclusion variable {α β : Type*} +/-- An `Interval` represents a possibly unbounded interval with closed endpoints. -/ structure Interval (α : Type*) where + /-- The lower endpoint, or `⊥` if the interval is unbounded below. -/ lb : WithBot α + /-- The upper endpoint, or `⊤` if the interval is unbounded above. -/ ub : WithTop α deriving Inhabited +/-- Maps `I` to `{a | I.lb ≤ a ∧ a ≤ I.ub}` -/ +def Interval.toSet [Preorder α] (I : Interval α) : Set α := {a | I.lb ≤ a ∧ a ≤ I.ub} + +instance [Preorder α] : ToSet (Interval α) α := ⟨Interval.toSet⟩ + +/-- Apply a function to the finite endpoints of an interval. -/ def Interval.map (I : Interval α) (f : α → β) : Interval β := - let lb := match I.lb with - | some a => some (f a) - | ⊥ => ⊥ - let ub := match I.ub with - | some a => some (f a) - | ⊤ => ⊤ - ⟨lb, ub⟩ + ⟨WithBot.map f I.lb, WithTop.map f I.ub⟩ +/-- The interval unbounded on both sides. -/ def Interval.univ (α : Type*) : Interval α := ⟨⊥, ⊤⟩ +instance [Preorder α] : Univ (Interval α) α where + univ := Interval.univ α + mem_univ _ := ⟨bot_le, le_top⟩ + +theorem Interval.mem_map_univ [Preorder β] (f : α → β) (x : β) : + x ∈ (Interval.univ α).map f := ⟨bot_le, le_top⟩ + +/-- The interval whose two endpoints are `a`. -/ def Interval.singleton (α : Type*) (a : α) : Interval α := ⟨a, a⟩ -/-- The exact intersection of two intervals. -/ -def Interval.inter [LinearOrder α] (I J : Interval α) : Interval α := - let lb := match I.lb, J.lb with - | ⊥, lb | lb, ⊥ => lb - | some a, some b => some (max a b) - let ub := match I.ub, J.ub with - | ⊤, ub | ub, ⊤ => ub - | some a, some b => some (min a b) - ⟨lb, ub⟩ - -/-- The smallest interval whose endpoints contain both input intervals. -/ -def Interval.hull [LinearOrder α] (I J : Interval α) : Interval α := - let lb := match I.lb, J.lb with - | ⊥, _ | _, ⊥ => ⊥ - | some a, some b => some (min a b) - let ub := match I.ub, J.ub with - | ⊤, _ | _, ⊤ => ⊤ - | some a, some b => some (max a b) - ⟨lb, ub⟩ +theorem Interval.mem_map_singleton [Preorder β] (a : α) (f : α → β) : + f a ∈ (Interval.singleton α a).map f := + ⟨le_rfl, le_rfl⟩ -/-- Forget the lower endpoint of an interval. -/ -def Interval.downwardClosure (I : Interval α) : Interval α := ⟨⊥, I.ub⟩ +/-- The interval with lower endpoint `lb` and upper endpoint `ub`. -/ +def Interval.Icc (lb : WithBot α) (ub : WithTop α) : Interval α := ⟨lb, ub⟩ -/-- Forget the upper endpoint of an interval. -/ -def Interval.upwardClosure (I : Interval α) : Interval α := ⟨I.lb, ⊤⟩ +/-- The interval unbounded below with upper endpoint `ub`. -/ +def Interval.Iic (ub : WithTop α) : Interval α := ⟨⊥, ub⟩ -def Interval.toSet [Preorder α] (I : Interval α) : Set α := {a | I.lb ≤ a ∧ a ≤ I.ub} +/-- The interval unbounded above with lower endpoint `lb`. -/ +def Interval.Ici (lb : WithBot α) : Interval α := ⟨lb, ⊤⟩ -instance [Preorder α] : ToSet (Interval α) α := ⟨Interval.toSet⟩ +theorem Interval.mem_Iic_of_le [Preorder α] {x y : α} {I : Interval α} + (hxy : x ≤ y) (hy : y ∈ I) : x ∈ Interval.Iic I.ub := by + dsimp [Interval.Iic] + constructor + · exact bot_le + · grind [hy.2, WithTop.coe_le_coe.mpr hxy] -instance [Preorder α] : Univ (Interval α) α where - univ := Interval.univ α - mem_univ x := - ⟨show (⊥ : WithBot α) ≤ x from bot_le, show (x : WithTop α) ≤ ⊤ from le_top⟩ +theorem Interval.mem_Ici_of_le [Preorder α] {x y : α} {I : Interval α} + (hxy : x ≤ y) (hx : x ∈ I) : y ∈ Interval.Ici I.lb := by + dsimp [Interval.Ici] + constructor + · grind [hx.1, WithBot.coe_le_coe.mpr hxy] + · exact le_top + +theorem Interval.mem_Icc_of_le [Preorder α] {a b x : α} {I J : Interval α} + (ha : a ∈ I) (hax : a ≤ x) (hxb : x ≤ b) (hb : b ∈ J) : + x ∈ Interval.Icc I.lb J.ub := by + dsimp [Interval.Icc] + constructor <;> grind [ha.1, hb.2, WithBot.coe_le_coe.mpr hax, + WithTop.coe_le_coe.mpr hxb] + +theorem Interval.mem_Iic_of_lt [Preorder α] {x y : α} {I : Interval α} + (hxy : x < y) (hy : y ∈ I) : x ∈ Interval.Iic I.ub := + Interval.mem_Iic_of_le hxy.le hy + +theorem Interval.mem_Ici_of_lt [Preorder α] {x y : α} {I : Interval α} + (hxy : x < y) (hx : x ∈ I) : y ∈ Interval.Ici I.lb := + Interval.mem_Ici_of_le hxy.le hx + +theorem Interval.mem_Ici_of_mem_Ici [Preorder α] {a x : α} {I : Interval α} + (hx : x ∈ Set.Ici a) (ha : a ∈ I) : x ∈ Interval.Ici I.lb := + Interval.mem_Ici_of_le hx ha + +theorem Interval.mem_Ici_of_mem_Ioi [Preorder α] {a x : α} {I : Interval α} + (hx : x ∈ Set.Ioi a) (ha : a ∈ I) : x ∈ Interval.Ici I.lb := + Interval.mem_Ici_of_le hx.le ha + +theorem Interval.mem_Iic_of_mem_Iic [Preorder α] {b x : α} {I : Interval α} + (hx : x ∈ Set.Iic b) (hb : b ∈ I) : x ∈ Interval.Iic I.ub := + Interval.mem_Iic_of_le hx hb + +theorem Interval.mem_Iic_of_mem_Iio [Preorder α] {b x : α} {I : Interval α} + (hx : x ∈ Set.Iio b) (hb : b ∈ I) : x ∈ Interval.Iic I.ub := + Interval.mem_Iic_of_le hx.le hb + +theorem Interval.mem_Icc_of_mem_Ico [Preorder α] {a b x : α} {I J : Interval α} + (hx : x ∈ Set.Ico a b) (ha : a ∈ I) (hb : b ∈ J) : + x ∈ Interval.Icc I.lb J.ub := + Interval.mem_Icc_of_le ha hx.1 hx.2.le hb + +theorem Interval.mem_Icc_of_mem_Ioc [Preorder α] {a b x : α} {I J : Interval α} + (hx : x ∈ Set.Ioc a b) (ha : a ∈ I) (hb : b ∈ J) : + x ∈ Interval.Icc I.lb J.ub := + Interval.mem_Icc_of_le ha hx.1.le hx.2 hb + +theorem Interval.mem_Icc_of_mem_Icc [Preorder α] {a b x : α} {I J : Interval α} + (hx : x ∈ Set.Icc a b) (ha : a ∈ I) (hb : b ∈ J) : + x ∈ Interval.Icc I.lb J.ub := + Interval.mem_Icc_of_le ha hx.1 hx.2 hb + +theorem Interval.mem_Icc_of_mem_Ioo [Preorder α] {a b x : α} {I J : Interval α} + (hx : x ∈ Set.Ioo a b) (ha : a ∈ I) (hb : b ∈ J) : + x ∈ Interval.Icc I.lb J.ub := + Interval.mem_Icc_of_le ha hx.1.le hx.2.le hb + +/-- The intersection of two intervals. -/ +def Interval.inter [LinearOrder α] (I J : Interval α) : Interval α := + ⟨max I.lb J.lb, min I.ub J.ub⟩ instance [LinearOrder α] : Refine (Interval α) α where refine := Interval.inter - mem_refine := by - intro x s t hs ht - constructor - · rcases hI : s.lb with _ | sl - · simpa [Interval.inter, hI] using ht.1 - rcases hJ : t.lb with _ | tl - · simpa [Interval.inter, hI, hJ] using hs.1 - have hs' := hs.1 - have ht' := ht.1 - rw [hI] at hs' - rw [hJ] at ht' - simp only [Interval.inter, hI, hJ] - exact WithBot.coe_le_coe.mpr (max_le - (WithBot.coe_le_coe.mp hs') (WithBot.coe_le_coe.mp ht')) - · rcases hI : s.ub with _ | su - · simpa [Interval.inter, hI] using ht.2 - rcases hJ : t.ub with _ | tu - · simpa [Interval.inter, hI, hJ] using hs.2 - have hs' := hs.2 - have ht' := ht.2 - rw [hI] at hs' - rw [hJ] at ht' - simp only [Interval.inter, hI, hJ] - exact WithTop.coe_le_coe.mpr (le_min - (WithTop.coe_le_coe.mp hs') (WithTop.coe_le_coe.mp ht')) - -theorem Interval.mem_hull_left [LinearOrder α] {x : α} {s t : Interval α} (hx : x ∈ s) : - x ∈ s.hull t := by - constructor - · rcases hs : s.lb with _ | sl - · simp [Interval.hull, hs] - rcases ht : t.lb with _ | tl - · simp [Interval.hull, hs, ht] - have hx' := hx.1 - rw [hs] at hx' - simp only [Interval.hull, hs, ht] - exact WithBot.coe_le_coe.mpr <| - (min_le_left sl tl).trans (WithBot.coe_le_coe.mp hx') - · rcases hs : s.ub with _ | su - · simp [Interval.hull, hs] - rcases ht : t.ub with _ | tu - · simp [Interval.hull, hs, ht] - have hx' := hx.2 - rw [hs] at hx' - simp only [Interval.hull, hs, ht] - exact WithTop.coe_le_coe.mpr <| - (WithTop.coe_le_coe.mp hx').trans (le_max_left su tu) - -theorem Interval.mem_hull_right [LinearOrder α] {x : α} {s t : Interval α} (hx : x ∈ t) : - x ∈ s.hull t := by - constructor - · rcases hs : s.lb with _ | sl - · simp [Interval.hull, hs] - rcases ht : t.lb with _ | tl - · simp [Interval.hull, hs, ht] - have hx' := hx.1 - rw [ht] at hx' - simp only [Interval.hull, hs, ht] - exact WithBot.coe_le_coe.mpr <| - (min_le_right sl tl).trans (WithBot.coe_le_coe.mp hx') - · rcases hs : s.ub with _ | su - · simp [Interval.hull, hs] - rcases ht : t.ub with _ | tu - · simp [Interval.hull, hs, ht] - have hx' := hx.2 - rw [ht] at hx' - simp only [Interval.hull, hs, ht] - exact WithTop.coe_le_coe.mpr <| - (WithTop.coe_le_coe.mp hx').trans (le_max_right su tu) + mem_refine hs ht := ⟨max_le hs.1 ht.1, le_min hs.2 ht.2⟩ + +theorem Interval.map_inter [LinearOrder α] [LinearOrder β] (f : α ↪o β) (I J : Interval α) : + (I.inter J).map f = (I.map f).inter (J.map f) := by + simp [Interval.inter, Interval.map, f.monotone.withBot_map.map_max, + f.monotone.withTop_map.map_min] + +theorem Interval.inter_mem [LinearOrder α] [LinearOrder β] (f : α ↪o β) + {x : β} {I J : Interval α} (hxI : x ∈ I.map f) (hxJ : x ∈ J.map f) : + x ∈ (I.inter J).map f := by + rw [Interval.map_inter] + exact Refine.mem_refine hxI hxJ + +/-- The convex hull of two intervals. -/ +def Interval.hull [LinearOrder α] (I J : Interval α) : Interval α := + ⟨min I.lb J.lb, max I.ub J.ub⟩ + +theorem Interval.mem_hull_left [LinearOrder α] {x : α} {I J : Interval α} (hx : x ∈ I) : + x ∈ I.hull J := by + simp_all [Interval.hull, ToSet.toSet, Interval.toSet] + +theorem Interval.mem_hull_right [LinearOrder α] {x : α} {I J : Interval α} (hx : x ∈ J) : + x ∈ I.hull J := by + simp_all [Interval.hull, ToSet.toSet, Interval.toSet] instance [LinearOrder α] : Coarsen (Interval α) α where coarsen := Interval.hull mem_coarsen_left := Interval.mem_hull_left mem_coarsen_right := Interval.mem_hull_right -theorem Interval.mem_downwardClosure_of_le [Preorder α] {x y : α} {I : Interval α} - (hxy : x ≤ y) (hy : y ∈ I) : x ∈ I.downwardClosure := - ⟨by simp [Interval.downwardClosure], (WithTop.coe_le_coe.mpr hxy).trans hy.2⟩ +theorem Interval.map_hull [LinearOrder α] [LinearOrder β] (f : α ↪o β) (I J : Interval α) : + (I.hull J).map f = (I.map f).hull (J.map f) := by + simp [Interval.hull, Interval.map, f.monotone.withBot_map.map_min, + f.monotone.withTop_map.map_max] + +theorem Interval.hull_mem_left [LinearOrder α] [LinearOrder β] (f : α ↪o β) + {x : β} {I J : Interval α} (hx : x ∈ I.map f) : x ∈ (I.hull J).map f := by + rw [Interval.map_hull] + exact Interval.mem_hull_left hx + +theorem Interval.hull_mem_right [LinearOrder α] [LinearOrder β] (f : α ↪o β) + {x : β} {I J : Interval α} (hx : x ∈ J.map f) : x ∈ (I.hull J).map f := by + rw [Interval.map_hull] + exact Interval.mem_hull_right hx + +/-- Add two intervals. -/ +def Interval.add [Add α] (I J : Interval α) : Interval α where + lb := match I.lb, J.lb with + | some a, some b => some (a + b) + | _, _ => ⊥ + ub := match I.ub, J.ub with + | some a, some b => some (a + b) + | _, _ => ⊤ + +theorem Interval.add_mem [AddZero α] [AddCommMonoid β] [Preorder β] [IsOrderedAddMonoid β] + (f : α →+ β) {x y : β} {I J : Interval α} (hx : x ∈ I.map f) (hy : y ∈ J.map f) : + x + y ∈ (I.add J).map f := by + rcases I with ⟨il, iu⟩ + rcases J with ⟨jl, ju⟩ + constructor + · rcases il with _ | il + · simp [Interval.add, Interval.map] + rcases jl with _ | jl + · simp [Interval.add, Interval.map] + apply WithBot.coe_le_coe.mpr + grind [add_le_add, WithBot.coe_le_coe.mp hx.1, WithBot.coe_le_coe.mp hy.1] + · rcases iu with _ | iu + · simp [Interval.add, Interval.map] + rcases ju with _ | ju + · simp [Interval.add, Interval.map] + apply WithTop.coe_le_coe.mpr + grind [add_le_add, WithTop.coe_le_coe.mp hx.2, WithTop.coe_le_coe.mp hy.2] + +/-- Negate an interval. -/ +def Interval.neg [Neg α] (I : Interval α) : Interval α where + lb := match I.ub with + | some a => some (-a) + | ⊤ => ⊥ + ub := match I.lb with + | some a => some (-a) + | ⊥ => ⊤ + +theorem Interval.neg_mem [AddGroup α] [AddCommGroup β] [PartialOrder β] [IsOrderedAddMonoid β] + (f : α →+ β) {x : β} {I : Interval α} (hx : x ∈ I.map f) : -x ∈ I.neg.map f := by + rcases I with ⟨il, iu⟩ + constructor + · rcases iu with _ | iu + · simp [Interval.neg, Interval.map] + apply WithBot.coe_le_coe.mpr + grind [neg_le_neg, WithTop.coe_le_coe.mp hx.2] + · rcases il with _ | il + · simp [Interval.neg, Interval.map] + apply WithTop.coe_le_coe.mpr + grind [neg_le_neg, WithBot.coe_le_coe.mp hx.1] + +/-- Subtract one interval from another. -/ +def Interval.sub [Sub α] (I J : Interval α) : Interval α where + lb := match I.lb, J.ub with + | some a, some b => some (a - b) + | _, _ => ⊥ + ub := match I.ub, J.lb with + | some a, some b => some (a - b) + | _, _ => ⊤ + +theorem Interval.sub_mem [AddGroup α] [AddCommGroup β] [Preorder β] [IsOrderedAddMonoid β] + (f : α →+ β) {x y : β} {I J : Interval α} + (hx : x ∈ I.map f) (hy : y ∈ J.map f) : x - y ∈ (I.sub J).map f := by + rcases I with ⟨il, iu⟩ + rcases J with ⟨jl, ju⟩ + constructor + · rcases il with _ | il + · simp [Interval.sub, Interval.map] + rcases ju with _ | ju + · simp [Interval.sub, Interval.map] + apply WithBot.coe_le_coe.mpr + grind [sub_le_sub, WithBot.coe_le_coe.mp hx.1, WithTop.coe_le_coe.mp hy.2] + · rcases iu with _ | iu + · simp [Interval.sub, Interval.map] + rcases jl with _ | jl + · simp [Interval.sub, Interval.map] + apply WithTop.coe_le_coe.mpr + grind [sub_le_sub, WithTop.coe_le_coe.mp hx.2, WithBot.coe_le_coe.mp hy.1] + +/-- Check `x ≤ y` for `x ∈ I` and `y ∈ J`, returning `true` or `false` when the +endpoints decide it and `undetermined` otherwise. -/ +def Interval.le [LE α] [DecidableLE α] (I J : Interval α) : IntervalBool := + match I.ub, J.lb with + | some iu, some jl => + if iu ≤ jl then .true + else + match I.lb, J.ub with + | some il, some ju => if il ≤ ju then .undetermined else .false + | _, _ => .undetermined + | _, _ => + match I.lb, J.ub with + | some il, some ju => if il ≤ ju then .undetermined else .false + | _, _ => .undetermined + +theorem Interval.le_mem [Preorder α] [Preorder β] [DecidableLE α] (f : α ↪o β) + {x y : β} {I J : Interval α} (hx : x ∈ I.map f) (hy : y ∈ J.map f) : + (x ≤ y) ∈ I.le J := by + have hfallback : + (x ≤ y) ∈ + (match I.lb, J.ub with + | some il, some ju => + if il ≤ ju then IntervalBool.undetermined else IntervalBool.false + | _, _ => IntervalBool.undetermined) := by + rcases I with ⟨_ | il, iu⟩ + · exact IntervalBool.mem_undetermined _ + rcases J with ⟨jl, _ | ju⟩ + · exact IntervalBool.mem_undetermined _ + dsimp + split_ifs with h + · exact IntervalBool.mem_undetermined _ + · apply IntervalBool.mem_false + intro hxy + apply h + rw [← f.le_iff_le] + exact (WithBot.coe_le_coe.mp hx.1).trans (hxy.trans (WithTop.coe_le_coe.mp hy.2)) + rcases I with ⟨il, _ | iu⟩ + · exact hfallback + rcases J with ⟨_ | jl, ju⟩ + · exact hfallback + dsimp [Interval.le] + split_ifs with h + · apply IntervalBool.mem_true + grind [WithTop.coe_le_coe.mp hx.2, f.monotone h, WithBot.coe_le_coe.mp hy.1] + · exact hfallback + +/-- Check `x < y` for `x ∈ I` and `y ∈ J`, returning `true` or `false` when the endpoints +decide it and `undetermined` otherwise. -/ +def Interval.lt [LT α] [DecidableLT α] (I J : Interval α) : IntervalBool := + match I.ub, J.lb with + | some iu, some jl => + if iu < jl then .true + else + match I.lb, J.ub with + | some il, some ju => if il < ju then .undetermined else .false + | _, _ => .undetermined + | _, _ => + match I.lb, J.ub with + | some il, some ju => if il < ju then .undetermined else .false + | _, _ => .undetermined + +theorem Interval.lt_mem [Preorder α] [Preorder β] [DecidableLT α] (f : α ↪o β) + {x y : β} {I J : Interval α} (hx : x ∈ I.map f) (hy : y ∈ J.map f) : + (x < y) ∈ I.lt J := by + have hfallback : + (x < y) ∈ + (match I.lb, J.ub with + | some il, some ju => + if il < ju then IntervalBool.undetermined else IntervalBool.false + | _, _ => IntervalBool.undetermined) := by + rcases I with ⟨_ | il, iu⟩ + · exact IntervalBool.mem_undetermined _ + rcases J with ⟨jl, _ | ju⟩ + · exact IntervalBool.mem_undetermined _ + dsimp + split_ifs with h + · exact IntervalBool.mem_undetermined _ + · apply IntervalBool.mem_false + intro hxy + apply h + rw [← f.lt_iff_lt] + exact (WithBot.coe_le_coe.mp hx.1).trans_lt (hxy.trans_le (WithTop.coe_le_coe.mp hy.2)) + rcases I with ⟨il, _ | iu⟩ + · exact hfallback + rcases J with ⟨_ | jl, ju⟩ + · exact hfallback + dsimp [Interval.lt] + split_ifs with h + · apply IntervalBool.mem_true + grind [WithTop.coe_le_coe.mp hx.2, f.strictMono h, WithBot.coe_le_coe.mp hy.1] + · exact hfallback + +/-- Return the conjunction of the two interval comparisons needed to verify equality. -/ +def Interval.eq [LE α] [DecidableLE α] (I J : Interval α) : IntervalBool := + (I.le J).and (J.le I) + +theorem Interval.eq_mem [Preorder α] [PartialOrder β] [DecidableLE α] (f : α ↪o β) + {x y : β} {I J : Interval α} (hx : x ∈ I.map f) (hy : y ∈ J.map f) : + (x = y) ∈ I.eq J := by + apply ToSet.mem_of_eq_of_mem (propext le_antisymm_iff) + exact IntervalBool.and_mem (Interval.le_mem f hx hy) (Interval.le_mem f hy hx) + +theorem Interval.mem_Ici [Preorder α] [Preorder β] [DecidableLE α] (f : α ↪o β) + {a x : β} {I J : Interval α} (ha : a ∈ I.map f) (hx : x ∈ J.map f) : + (x ∈ Set.Ici a) ∈ I.le J := Interval.le_mem f ha hx + +theorem Interval.mem_Ioi [Preorder α] [Preorder β] [DecidableLT α] (f : α ↪o β) + {a x : β} {I J : Interval α} (ha : a ∈ I.map f) (hx : x ∈ J.map f) : + (x ∈ Set.Ioi a) ∈ I.lt J := Interval.lt_mem f ha hx + +theorem Interval.mem_Iic [Preorder α] [Preorder β] [DecidableLE α] (f : α ↪o β) + {b x : β} {I J : Interval α} (hx : x ∈ I.map f) (hb : b ∈ J.map f) : + (x ∈ Set.Iic b) ∈ I.le J := Interval.le_mem f hx hb + +theorem Interval.mem_Iio [Preorder α] [Preorder β] [DecidableLT α] (f : α ↪o β) + {b x : β} {I J : Interval α} (hx : x ∈ I.map f) (hb : b ∈ J.map f) : + (x ∈ Set.Iio b) ∈ I.lt J := Interval.lt_mem f hx hb + +theorem Interval.mem_Icc [Preorder α] [Preorder β] [DecidableLE α] (f : α ↪o β) + {a b x : β} {I J K : Interval α} + (ha : a ∈ I.map f) (hx : x ∈ J.map f) (hb : b ∈ K.map f) : + (x ∈ Set.Icc a b) ∈ (I.le J).and (J.le K) := + IntervalBool.and_mem (Interval.le_mem f ha hx) (Interval.le_mem f hx hb) + +theorem Interval.mem_Ico [Preorder α] [Preorder β] [DecidableLE α] [DecidableLT α] + (f : α ↪o β) {a b x : β} {I J K : Interval α} + (ha : a ∈ I.map f) (hx : x ∈ J.map f) (hb : b ∈ K.map f) : + (x ∈ Set.Ico a b) ∈ (I.le J).and (J.lt K) := + IntervalBool.and_mem (Interval.le_mem f ha hx) (Interval.lt_mem f hx hb) + +theorem Interval.mem_Ioc [Preorder α] [Preorder β] [DecidableLE α] [DecidableLT α] + (f : α ↪o β) {a b x : β} {I J K : Interval α} + (ha : a ∈ I.map f) (hx : x ∈ J.map f) (hb : b ∈ K.map f) : + (x ∈ Set.Ioc a b) ∈ (I.lt J).and (J.le K) := + IntervalBool.and_mem (Interval.lt_mem f ha hx) (Interval.le_mem f hx hb) -theorem Interval.mem_upwardClosure_of_le [Preorder α] {x y : α} {I : Interval α} - (hxy : x ≤ y) (hx : x ∈ I) : y ∈ I.upwardClosure := - ⟨hx.1.trans (WithBot.coe_le_coe.mpr hxy), by simp [Interval.upwardClosure]⟩ +theorem Interval.mem_Ioo [Preorder α] [Preorder β] [DecidableLT α] (f : α ↪o β) + {a b x : β} {I J K : Interval α} (ha : a ∈ I.map f) (hx : x ∈ J.map f) + (hb : b ∈ K.map f) : (x ∈ Set.Ioo a b) ∈ (I.lt J).and (J.lt K) := + IntervalBool.and_mem (Interval.lt_mem f ha hx) (Interval.lt_mem f hx hb) end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Basic.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Basic.lean new file mode 100644 index 00000000000000..abb8dc8cf2f017 --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Basic.lean @@ -0,0 +1,123 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Init +public meta import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Init +public meta import Mathlib.Tactic.Inclusion.ExtensionAPI.Attr + +/-! +# Basic inclusion extensions for interval_dyadic_real + +This file defines basic operations for the `interval_dyadic_real` inclusion family. +-/ + +@[expose] public section + +namespace Inclusion + +namespace IntervalDyadicReal + +theorem mem_iff_mem_map {x : ℝ} {I : Interval Dyadic} : + x ∈ I ↔ x ∈ I.map Dyadic.toReal := Iff.rfl + +section Constants + +@[inclusionOp interval_dyadic_real] +theorem natCast_mem (n : ℕ) : (n : ℝ) ∈ Interval.singleton Dyadic n := by + rw [mem_iff_mem_map] + simpa using Interval.mem_map_singleton (n : Dyadic) Dyadic.toReal + +@[inclusionOp interval_dyadic_real] +theorem ofNat_mem (n : ℕ) : (OfNat.ofNat n : ℝ) ∈ Interval.singleton Dyadic n := by + rw [Semiring.toGrindSemiring_ofNat] + exact natCast_mem n + +@[inclusionOp interval_dyadic_real] +theorem intCast_mem (z : ℤ) : (z : ℝ) ∈ Interval.singleton Dyadic z := by + rw [mem_iff_mem_map] + simpa using Interval.mem_map_singleton (z : Dyadic) Dyadic.toReal + +end Constants + +section Arithmetic + +@[inclusionOp interval_dyadic_real] +theorem add_mem {x y : ℝ} {I J : Interval Dyadic} (hx : x ∈ I) (hy : y ∈ J) : x + y ∈ I.add J := + Interval.add_mem Dyadic.toRealAddMonoidHom hx hy + +@[inclusionOp interval_dyadic_real] +theorem neg_mem {x : ℝ} {I : Interval Dyadic} (hx : x ∈ I) : -x ∈ I.neg := + Interval.neg_mem Dyadic.toRealAddMonoidHom hx + +@[inclusionOp interval_dyadic_real] +theorem sub_mem {x y : ℝ} {I J : Interval Dyadic} (hx : x ∈ I) (hy : y ∈ J) : x - y ∈ I.sub J := + Interval.sub_mem Dyadic.toRealAddMonoidHom hx hy + +end Arithmetic + +section Props + +@[inclusionOp interval_dyadic_real] +theorem le_mem {x y : ℝ} {I J : Interval Dyadic} (hx : x ∈ I) (hy : y ∈ J) : + (x ≤ y) ∈ I.le J := + Interval.le_mem Dyadic.toRealOrderEmbedding hx hy + +@[inclusionOp interval_dyadic_real] +theorem lt_mem {x y : ℝ} {I J : Interval Dyadic} (hx : x ∈ I) (hy : y ∈ J) : + (x < y) ∈ I.lt J := + Interval.lt_mem Dyadic.toRealOrderEmbedding hx hy + +@[inclusionOp interval_dyadic_real] +theorem eq_mem {x y : ℝ} {I J : Interval Dyadic} (hx : x ∈ I) (hy : y ∈ J) : + (x = y) ∈ I.eq J := + Interval.eq_mem Dyadic.toRealOrderEmbedding hx hy + +@[inclusionOp interval_dyadic_real] +theorem mem_Ici {a x : ℝ} {I J : Interval Dyadic} (ha : a ∈ I) (hx : x ∈ J) : + (x ∈ Set.Ici a) ∈ I.le J := + Interval.mem_Ici Dyadic.toRealOrderEmbedding ha hx + +@[inclusionOp interval_dyadic_real] +theorem mem_Ioi {a x : ℝ} {I J : Interval Dyadic} (ha : a ∈ I) (hx : x ∈ J) : + (x ∈ Set.Ioi a) ∈ I.lt J := + Interval.mem_Ioi Dyadic.toRealOrderEmbedding ha hx + +@[inclusionOp interval_dyadic_real] +theorem mem_Iic {b x : ℝ} {I J : Interval Dyadic} (hx : x ∈ I) (hb : b ∈ J) : + (x ∈ Set.Iic b) ∈ I.le J := + Interval.mem_Iic Dyadic.toRealOrderEmbedding hx hb + +@[inclusionOp interval_dyadic_real] +theorem mem_Iio {b x : ℝ} {I J : Interval Dyadic} (hx : x ∈ I) (hb : b ∈ J) : + (x ∈ Set.Iio b) ∈ I.lt J := + Interval.mem_Iio Dyadic.toRealOrderEmbedding hx hb + +@[inclusionOp interval_dyadic_real] +theorem mem_Icc {a b x : ℝ} {I J K : Interval Dyadic} (ha : a ∈ I) (hx : x ∈ J) (hb : b ∈ K) : + (x ∈ Set.Icc a b) ∈ (I.le J).and (J.le K) := + Interval.mem_Icc Dyadic.toRealOrderEmbedding ha hx hb + +@[inclusionOp interval_dyadic_real] +theorem mem_Ico {a b x : ℝ} {I J K : Interval Dyadic} (ha : a ∈ I) (hx : x ∈ J) (hb : b ∈ K) : + (x ∈ Set.Ico a b) ∈ (I.le J).and (J.lt K) := + Interval.mem_Ico Dyadic.toRealOrderEmbedding ha hx hb + +@[inclusionOp interval_dyadic_real] +theorem mem_Ioc {a b x : ℝ} {I J K : Interval Dyadic} (ha : a ∈ I) (hx : x ∈ J) (hb : b ∈ K) : + (x ∈ Set.Ioc a b) ∈ (I.lt J).and (J.le K) := + Interval.mem_Ioc Dyadic.toRealOrderEmbedding ha hx hb + +@[inclusionOp interval_dyadic_real] +theorem mem_Ioo {a b x : ℝ} {I J K : Interval Dyadic} (ha : a ∈ I) (hx : x ∈ J) (hb : b ∈ K) : + (x ∈ Set.Ioo a b) ∈ (I.lt J).and (J.lt K) := + Interval.mem_Ioo Dyadic.toRealOrderEmbedding ha hx hb + +end Props + +end IntervalDyadicReal + +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Extensions.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Extensions.lean new file mode 100644 index 00000000000000..a764ea36a3b6b4 --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Extensions.lean @@ -0,0 +1,45 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Basic +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Splitting +public meta import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Basic +public meta import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Splitting +public meta import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Hypotheses +public meta import Mathlib.Tactic.Inclusion.ExtensionAPI.Basic +public meta import Qq + +/-! +# Inclusion extensions for dyadic real intervals +-/ + +public meta section + +open Lean Meta Qq + +namespace Inclusion +namespace IntervalDyadicReal + +/-- The depth to which bounded dyadic intervals are repeatedly bisected. A depth of `n` produces +`2 ^ n` pieces; unbounded intervals are left unchanged. -/ +@[inclusionParam] +def binSplitParam : InclusionParamDecl where + name := `binSplit + type := q(ℕ) + +/-- Construct the binary-splitting cover with `2 ^ n` pieces. -/ +def mkBinSplitCover : InclusionM (Option Expr) := do + let some depth ← InclusionM.getParam? `binSplit | return none + return some (mkApp (mkConst ``BinarySplit.cover [.zero]) depth) + +/-- Construct an inclusion variable for a real expression using a dyadic interval. -/ +@[inclusionExt interval_dyadic_real | (_ : ℝ)] +def mkRealIVar : InclusionExt := + mkNDIVarExt ⟨q(ℝ), q(Interval Dyadic), q(instToSetIntervalDyadicReal)⟩ mkBinSplitCover + +end IntervalDyadicReal +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Hypotheses.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Hypotheses.lean new file mode 100644 index 00000000000000..b223711ce4750f --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Hypotheses.lean @@ -0,0 +1,76 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Basic + +/-! +# Hypothesis operations for dyadic real intervals + +This file defines the hypothesis extensions for the `interval_dyadic_real` inclusion family. +-/ + +@[expose] public section + +namespace Inclusion + +namespace IntervalDyadicReal + +@[hypothesisOp interval_dyadic_real] +theorem Iic_mem_of_le {x y : ℝ} {I : Interval Dyadic} (hxy : x ≤ y) (hy : y ∈ I) : + x ∈ Interval.Iic I.ub := Interval.mem_Iic_of_le hxy hy + +@[hypothesisOp interval_dyadic_real] +theorem Ici_mem_of_le {x y : ℝ} {I : Interval Dyadic} (hxy : x ≤ y) (hx : x ∈ I) : + y ∈ Interval.Ici I.lb := Interval.mem_Ici_of_le hxy hx + +@[hypothesisOp interval_dyadic_real] +theorem Iic_mem_of_lt {x y : ℝ} {I : Interval Dyadic} (hxy : x < y) (hy : y ∈ I) : + x ∈ Interval.Iic I.ub := Interval.mem_Iic_of_lt hxy hy + +@[hypothesisOp interval_dyadic_real] +theorem Ici_mem_of_lt {x y : ℝ} {I : Interval Dyadic} (hxy : x < y) (hx : x ∈ I) : + y ∈ Interval.Ici I.lb := Interval.mem_Ici_of_lt hxy hx + +@[hypothesisOp interval_dyadic_real] +theorem Ici_mem_of_mem_Ici {a x : ℝ} {I : Interval Dyadic} (hx : x ∈ Set.Ici a) (ha : a ∈ I) : + x ∈ Interval.Ici I.lb := Interval.mem_Ici_of_mem_Ici hx ha + +@[hypothesisOp interval_dyadic_real] +theorem Ici_mem_of_mem_Ioi {a x : ℝ} {I : Interval Dyadic} (hx : x ∈ Set.Ioi a) (ha : a ∈ I) : + x ∈ Interval.Ici I.lb := Interval.mem_Ici_of_mem_Ioi hx ha + +@[hypothesisOp interval_dyadic_real] +theorem Iic_mem_of_mem_Iic {b x : ℝ} {I : Interval Dyadic} (hx : x ∈ Set.Iic b) (hb : b ∈ I) : + x ∈ Interval.Iic I.ub := Interval.mem_Iic_of_mem_Iic hx hb + +@[hypothesisOp interval_dyadic_real] +theorem Iic_mem_of_mem_Iio {b x : ℝ} {I : Interval Dyadic} (hx : x ∈ Set.Iio b) (hb : b ∈ I) : + x ∈ Interval.Iic I.ub := Interval.mem_Iic_of_mem_Iio hx hb + +@[hypothesisOp interval_dyadic_real] +theorem Icc_mem_of_mem_Ico {a b x : ℝ} {I J : Interval Dyadic} (hx : x ∈ Set.Ico a b) + (ha : a ∈ I) (hb : b ∈ J) : x ∈ Interval.Icc I.lb J.ub := + Interval.mem_Icc_of_mem_Ico hx ha hb + +@[hypothesisOp interval_dyadic_real] +theorem Icc_mem_of_mem_Ioc {a b x : ℝ} {I J : Interval Dyadic} (hx : x ∈ Set.Ioc a b) + (ha : a ∈ I) (hb : b ∈ J) : x ∈ Interval.Icc I.lb J.ub := + Interval.mem_Icc_of_mem_Ioc hx ha hb + +@[hypothesisOp interval_dyadic_real] +theorem Icc_mem_of_mem_Icc {a b x : ℝ} {I J : Interval Dyadic} (hx : x ∈ Set.Icc a b) + (ha : a ∈ I) (hb : b ∈ J) : x ∈ Interval.Icc I.lb J.ub := + Interval.mem_Icc_of_mem_Icc hx ha hb + +@[hypothesisOp interval_dyadic_real] +theorem Icc_mem_of_mem_Ioo {a b x : ℝ} {I J : Interval Dyadic} (hx : x ∈ Set.Ioo a b) + (ha : a ∈ I) (hb : b ∈ J) : x ∈ Interval.Icc I.lb J.ub := + Interval.mem_Icc_of_mem_Ioo hx ha hb + +end IntervalDyadicReal + +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Init.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Init.lean new file mode 100644 index 00000000000000..8b069a7237dfb6 --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Init.lean @@ -0,0 +1,47 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public import Mathlib.Data.Dyadic +public meta import Mathlib.Tactic.Inclusion.Core.Extensions +public import Mathlib.Tactic.Inclusion.Extension.Interval + +/-! +# Initialization for the dyadic real interval extension family + +This file initializes the `interval_dyadic_real` inclusion family and defines the `ToSet`, +`Univ` and `Coarsen` instances it uses in the `inclusion` tactic. +-/ + +@[expose] public section + +namespace Inclusion + +namespace IntervalDyadicReal + +/-- Initializes the `interval_dyadic_real` inclusion family. -/ +meta initialize intervalDyadicRealFamily : InclusionFamily ← + registerInclusionFamily `interval_dyadic_real + +instance instToSetIntervalDyadicReal : ToSet (Interval Dyadic) ℝ where + toSet I := (I.map Dyadic.toReal).toSet + +instance : Univ (Interval Dyadic) ℝ where + univ := Interval.univ Dyadic + mem_univ := Interval.mem_map_univ Dyadic.toReal + +instance : Refine (Interval Dyadic) ℝ where + refine := Interval.inter + mem_refine := Interval.inter_mem Dyadic.toRealOrderEmbedding + +instance : Coarsen (Interval Dyadic) ℝ where + coarsen := Interval.hull + mem_coarsen_left := Interval.hull_mem_left Dyadic.toRealOrderEmbedding + mem_coarsen_right := Interval.hull_mem_right Dyadic.toRealOrderEmbedding + +end IntervalDyadicReal + +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Splitting.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Splitting.lean similarity index 53% rename from Mathlib/Tactic/Inclusion/Extension/DyadicReal/Splitting.lean rename to Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Splitting.lean index b9e2152d74238f..06f287cbee776e 100644 --- a/Mathlib/Tactic/Inclusion/Extension/DyadicReal/Splitting.lean +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Splitting.lean @@ -5,31 +5,26 @@ Authors: David Ledvinka -/ module -public import Mathlib.Tactic.Inclusion.Extension.DyadicReal.Basic -public import Mathlib.Tactic.Inclusion.Extension.Splitter +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Basic /-! # Binary splitting of dyadic real intervals -This file defines a `Splitter` instance that repeatedly bisects bounded dyadic intervals. +This file defines a cover that repeatedly bisects bounded dyadic intervals. -/ -set_option linter.style.header false - @[expose] public section namespace Inclusion +namespace IntervalDyadicReal namespace BinarySplit -/-- Divide a dyadic number by two. -/ -def half (x : Dyadic) : Dyadic := - match x with - | .zero => 0 - | .ofOdd n k _ => Dyadic.ofIntWithPrec n (k + 1) - /-- The dyadic midpoint of `a` and `b`. -/ -def midpoint (a b : Dyadic) : Dyadic := half (a + b) +def midpoint (a b : Dyadic) : Dyadic := + match a + b with + | .zero => .zero + | .ofOdd n k hn => .ofOdd n (k + 1) hn /-- Map `F` over the intervals produced by bisecting `I` to depth `n`, coarsening the results. -/ @[specialize] @@ -40,8 +35,8 @@ def coverMap {Iβ β : Type*} [ToSet Iβ β] [Coarsen Iβ β] : match I with | ⟨some l, some u⟩ => let m := midpoint l u - Coarsen.coarsen (Iα := Iβ) (α := β) (coverMap n ⟨l, m⟩ F) (coverMap n ⟨m, u⟩ F) - | _ => coverMap n I F + Coarsen.coarsen (α := β) (coverMap n ⟨l, m⟩ F) (coverMap n ⟨m, u⟩ F) + | _ => F I theorem mem_coverMap {Iβ β : Type*} [ToSet Iβ β] [Coarsen Iβ β] (n : ℕ) (I : Interval Dyadic) (F : Interval Dyadic → Iβ) {y : β} {r : ℝ} @@ -51,32 +46,26 @@ theorem mem_coverMap {Iβ β : Type*} [ToSet Iβ β] [Coarsen Iβ β] | succ n ih => rcases I with ⟨lb, ub⟩ cases lb with - | bot => exact ih ⟨⊥, ub⟩ hr + | bot => exact hy _ hr | coe l => cases ub with - | top => exact ih ⟨l, ⊤⟩ hr + | top => exact hy _ hr | coe u => let m := midpoint l u let left : Interval Dyadic := ⟨l, m⟩ let right : Interval Dyadic := ⟨m, u⟩ - change y ∈ Coarsen.coarsen (Iα := Iβ) (α := β) - (coverMap n left F) (coverMap n right F) by_cases hl : r ≤ Dyadic.toReal m - · apply Coarsen.mem_coarsen_left (Iα := Iβ) (α := β) - exact ih left ⟨hr.1, WithTop.coe_le_coe.mpr hl⟩ - · apply Coarsen.mem_coarsen_right (Iα := Iβ) (α := β) - exact ih right ⟨WithBot.coe_le_coe.mpr (le_of_not_ge hl), hr.2⟩ + · exact Coarsen.mem_coarsen_left + (ih left ⟨hr.1, WithTop.coe_le_coe.mpr hl⟩) + · exact Coarsen.mem_coarsen_right + (ih right ⟨WithBot.coe_le_coe.mpr (le_of_not_ge hl), hr.2⟩) /-- Cover a dyadic interval by repeatedly bisecting it to depth `n`. -/ def cover (n : ℕ) : Cover (Interval Dyadic) ℝ where - coverMap := fun I F ↦ coverMap n I F - mem_coverMap := by - intro Iβ β _ _ I F x y hx hy - exact mem_coverMap n I F hx hy + coverMap := coverMap n + mem_coverMap hx hy := mem_coverMap n _ _ hx hy end BinarySplit -instance : Splitter (Interval Dyadic) ℝ where - cover := BinarySplit.cover - +end IntervalDyadicReal end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/Splitter.lean b/Mathlib/Tactic/Inclusion/Extension/Splitter.lean deleted file mode 100644 index bfac4deef18f84..00000000000000 --- a/Mathlib/Tactic/Inclusion/Extension/Splitter.lean +++ /dev/null @@ -1,16 +0,0 @@ -module - -public import Mathlib.Tactic.Inclusion.Core.ToSet - -set_option linter.style.header false - -@[expose] public section - -namespace Inclusion - -/-- A procedure for covering a represented set by sufficiently many refinements. -/ -class Splitter (Iα α : Type*) [ToSet Iα α] where - /-- The cover obtained by refining a represented set to depth `n`. -/ - cover (n : ℕ) : Cover.{0} Iα α - -end Inclusion diff --git a/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean b/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean index 65f733cd769002..b6f34fd68ea39f 100644 --- a/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean +++ b/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean @@ -10,8 +10,8 @@ public meta import Mathlib.Tactic.Inclusion.ExtensionAPI.Basic /-! # Attributes for `inclusion` extensions -This file defines the attributes used to register handwritten inclusion and hypothesis extensions, -inclusion parameters, and theorem-generated inclusion and hypothesis operations. +This file defines the attributes used to register inclusion and hypothesis extensions. + -/ public meta section @@ -20,8 +20,43 @@ open Lean Meta Elab Term DiscrTreeExt namespace Inclusion -section Extension +/-- Syntax for registering an inclusion parameter using the `inclusionParam` attribute. -/ +syntax (name := inclusionParamAttr) "inclusionParam" : attr + +private def validateInclusionParamDecl (decl : InclusionParamDecl) : MetaM Unit := do + if decl.type.hasFVar || decl.type.hasMVar then + throwError "The type of inclusion parameter '{decl.name}' is not closed" + unless (← inferType decl.type).isSort do + throwError "The declared type {decl.type} of inclusion parameter '{decl.name}' is not a type" + if let some value := decl.defaultValue? then + if value.hasFVar || value.hasMVar then + throwError "The default value of inclusion parameter '{decl.name}' is not closed" + unless ← isDefEq (← inferType value) decl.type do + throwError "The default value {value} of inclusion parameter '{decl.name}' does not have \ + type {decl.type}" +/-- Add the inclusion parameter declared by `declName`. -/ +def addInclusionParam (declName : Name) (kind : AttributeKind) : AttrM Unit := do + let env ← getEnv + ensureAttrDeclIsMeta `inclusionParam declName kind + unless (env.getModuleIdxFor? declName).isNone do + throwError "invalid attribute `inclusionParam`, declaration is in an imported module" + if (IR.getSorryDep env declName).isSome then return + let decl ← mkInclusionParamDecl declName + MetaM.run' <| validateInclusionParamDecl decl + let params := inclusionParamExt.getState env + if params.decls.contains decl.name then + throwError "Inclusion parameter '{decl.name}' is already registered" + inclusionParamExt.add (declName, decl) kind + +/-- Register the `inclusionParam` attribute registers a typed inclusion-tactic parameter. -/ +initialize registerBuiltinAttribute { + name := `inclusionParamAttr + descr := "registers an inclusion-tactic parameter" + applicationTime := .afterCompilation + add := fun declName _ kind => addInclusionParam declName kind +} +/-- Syntax for declaring an inclusion extension using the `inclusionExt` attribute. -/ syntax (name := inclusionExtAttr) "inclusionExt " ident " | " term,+ : attr /-- Add the inclusion extension `declName` to `familyName` under `keys`. -/ @@ -31,7 +66,7 @@ def addInclusionExt (familyName declName : Name) (keys : Array (Array DiscrTree. let ext ← evalDecl InclusionExt ``InclusionExt declName family.inclusionExt.add ((keys, declName), ext) kind -/-- The `inclusionExt` attribute registers a handwritten inclusion extension. -/ +/-- Register the `inclusionExt` attribute. -/ initialize registerBuiltinAttribute { name := `inclusionExtAttr descr := "adds an inclusion-function extension" @@ -50,6 +85,7 @@ initialize registerBuiltinAttribute { erase := fun _ => throwError "Inclusion extensions cannot be erased by declaration" } +/-- Syntax for declaring an hypothesis extension using the `hypothesisExt` attribute. -/ syntax (name := hypothesisExtAttr) "hypothesisExt " ident " | " term,+ : attr /-- Add the hypothesis extension `declName` to `familyName` under `keys`. -/ @@ -59,7 +95,7 @@ def addHypothesisExt (familyName declName : Name) (keys : Array (Array DiscrTree let ext ← evalDecl HypothesisExt ``HypothesisExt declName family.hypothesisExt.add ((keys, declName), ext) kind -/-- The `hypothesisExt` attribute registers a hypothesis extension. -/ +/-- Register the `hypothesisExt` attribute. -/ initialize registerBuiltinAttribute { name := `hypothesisExtAttr descr := "adds a hypothesis extension" @@ -78,50 +114,6 @@ initialize registerBuiltinAttribute { erase := fun _ => throwError "Hypothesis extensions cannot be erased by declaration" } -end Extension - -section Param - -syntax (name := inclusionParamAttr) "inclusionParam" : attr - -private def validateInclusionParamDecl (decl : InclusionParamDecl) : MetaM Unit := do - if decl.type.hasFVar || decl.type.hasMVar then - throwError "The type of inclusion parameter '{decl.name}' is not closed" - unless (← inferType decl.type).isSort do - throwError "The declared type {decl.type} of inclusion parameter '{decl.name}' is not a type" - if let some value := decl.defaultValue? then - if value.hasFVar || value.hasMVar then - throwError "The default value of inclusion parameter '{decl.name}' is not closed" - unless ← isDefEq (← inferType value) decl.type do - throwError "The default value {value} of inclusion parameter '{decl.name}' does not have \ - type {decl.type}" - -/-- Add the inclusion parameter declared by `declName`. -/ -def addInclusionParam (declName : Name) (kind : AttributeKind) : AttrM Unit := do - let env ← getEnv - ensureAttrDeclIsMeta `inclusionParam declName kind - unless (env.getModuleIdxFor? declName).isNone do - throwError "invalid attribute `inclusionParam`, declaration is in an imported module" - if (IR.getSorryDep env declName).isSome then return - let decl ← mkInclusionParamDecl declName - MetaM.run' <| validateInclusionParamDecl decl - let params := inclusionParamExt.getState env - if params.decls.contains decl.name then - throwError "Inclusion parameter '{decl.name}' is already registered" - inclusionParamExt.add (declName, decl) kind - -/-- The `inclusionParam` attribute registers a typed inclusion-tactic parameter. -/ -initialize registerBuiltinAttribute { - name := `inclusionParamAttr - descr := "registers an inclusion-tactic parameter" - applicationTime := .afterCompilation - add := fun declName _ kind => addInclusionParam declName kind -} - -end Param - -section Operation - /-- The argument indices of an expression, its inclusion set, and its membership proof in an inclusion theorem. -/ structure InclusionHypothesisArg where @@ -263,8 +255,6 @@ private def analyzeTheoremArgs (declName : Name) (pattern outputSet : Expr) throwError "Unsupported premise '{argType}' in theorem '{declName}'" return (inputs, params) -section InclusionOp - syntax (name := inclusionOpAttr) "inclusionOp " ident (prio)? : attr private def analyzeInclusionTheorem (declName : Name) : @@ -304,10 +294,6 @@ initialize registerBuiltinAttribute { erase := fun _ => throwError "Inclusion operations cannot be erased by declaration" } -end InclusionOp - -section HypothesisOp - syntax (name := hypothesisOpAttr) "hypothesisOp " ident (prio)? : attr private def analyzeHypothesisTheorem (declName : Name) : @@ -366,8 +352,4 @@ initialize registerBuiltinAttribute { erase := fun _ => throwError "Hypothesis operations cannot be erased by declaration" } -end HypothesisOp - -end Operation - end Inclusion diff --git a/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean b/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean index 32d65731cc1686..b4c03bfe3d6358 100644 --- a/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean +++ b/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean @@ -67,6 +67,8 @@ private def checkIVarWellFormed (localContext : LocalContext) (iExpr : IExpr) : /-- Create and register an inclusion variable for `iExpr`. -/ def mkIVar (iExpr : IExpr) (cover : Option Expr := none) : InclusionM IVar := do let ctx ← read + if ctx.noIVars then + throwError "Cannot create an inclusion variable for {iExpr.expr} since `noIVars` is set to true" checkIVarWellFormed ctx.localContext iExpr let setVar ← mkFreshExprMVarAt ctx.localContext ctx.localInstances iExpr.iType.setType .syntheticOpaque @@ -78,7 +80,7 @@ def mkIVar (iExpr : IExpr) (cover : Option Expr := none) : InclusionM IVar := do /-- Construct an inclusion extension for making non dependently typed inclusion variables. -/ def mkNDIVarExt (iType : IType) - (mkCover : IExpr → InclusionM (Option Expr) := fun _ ↦ pure none) + (mkCover : InclusionM (Option Expr) := pure none) (priority : Nat := eval_prio low) (name : Name := by exact decl_name%) : InclusionExt where declName := name userName := name @@ -87,7 +89,7 @@ def mkNDIVarExt (iType : IType) let eType ← inferType e unless ← isDefEq eType iType.elemType do failure let iExpr : IExpr := ⟨iType, e⟩ - return (← mkIVar iExpr (← mkCover iExpr)).toExprInclusionBody + return (← mkIVar iExpr (← mkCover)).toExprInclusionBody /-- Return the inclusion variable registered for `e`, if there is one. -/ def findIVar? (e : Expr) : HypothesisM (Option IVar) := do @@ -95,43 +97,32 @@ def findIVar? (e : Expr) : HypothesisM (Option IVar) := do /-- Check that two inclusion types are definitionally equal, including their chosen `ToSet` instances. -/ -def ensureOutputType (actual expected : IType) : MetaM Unit := do - -- The represented element types must agree, for example both must be `Real`. - unless ← pureIsDefEq actual.elemType expected.elemType do - -- Report the component that differs instead of a generic type mismatch. - throwError "Inclusion has expression type {actual.elemType}, expected \ - {expected.elemType}" - -- The computational set types must agree, for example both must be `Interval Dyadic`. - unless ← pureIsDefEq actual.setType expected.setType do - -- A hypothesis using a different backend cannot be substituted into the main function. - throwError "Inclusion has set type {actual.setType}, expected {expected.setType}" - -- Even equal element and set types may be interpreted by definitionally different `ToSet`s. - unless ← pureIsDefEq actual.toSetInst expected.toSetInst do - -- Require the same interpretation so that the two membership propositions agree. +def ensureOutputType (type expectedType : IType) : MetaM Unit := do + unless ← pureIsDefEq type.elemType expectedType.elemType do + throwError "Inclusion has expression type {type.elemType}, expected {expectedType.elemType}" + unless ← pureIsDefEq type.setType expectedType.setType do + throwError "Inclusion has set type {type.setType}, expected {expectedType.setType}" + unless ← pureIsDefEq type.toSetInst expectedType.toSetInst do throwError "Inclusion uses an unexpected `ToSet` instance" -/-- Construct and validate an inclusion body for an expression argument of a hypothesis rule. -/ +/-- Construct a closed inclusion body for an expression argument of a hypothesis rule. -/ def mkHypExprInclusionBody (e : Expr) : HypothesisM ExprInclusionBody := do let ctx ← read - let (body, inclusionState) ← (mkExprInclusionBody e).runWith ctx.toContext + let inclusionContext := { ctx.toContext with noIVars := true } + let (body, inclusionState) ← (mkExprInclusionBody e).runWith inclusionContext unless inclusionState.iVars.isEmpty do throwError "The inclusion for {e} depends on inclusion variables" if body.inclusionBody.hasFVar then throwError "The inclusion hypothesis generated from {e} contains a free variable" if body.inclusionBody.hasMVar then throwError "The inclusion hypothesis generated from {e} contains a metavariable" - discard <| body.inferIType e return body /-- Add the inclusion hypothesis `body` for `iExpr`. -/ def addInclusionHyp (iExpr : IExpr) (body : ExprInclusionBody) : HypothesisM Unit := do - -- Reject candidates whose element type, represented-set type, or `ToSet` instance is unsuitable. ensureOutputType (← body.inferIType iExpr.expr) iExpr.iType - -- Append the candidate to the array associated with the canonical requested expression. modify fun state => { state with inclusions := state.inclusions.alter iExpr.expr fun - -- Preserve earlier candidates because they will later be combined with `Refine`. | some hyps => hyps.push body - -- Create the candidate array when this is the first useful hypothesis for the expression. | none => #[body] } end Inclusion diff --git a/MathlibTest/Tactic/Inclusion/Basic.lean b/MathlibTest/Tactic/Inclusion/Basic.lean index 5e13bba4e2a003..332e064130782e 100644 --- a/MathlibTest/Tactic/Inclusion/Basic.lean +++ b/MathlibTest/Tactic/Inclusion/Basic.lean @@ -3,7 +3,8 @@ Copyright (c) 2026 David Ledvinka. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: David Ledvinka -/ -import Mathlib.Tactic.Inclusion.Extension.Extensions +import Mathlib.Tactic.Inclusion.Extension.Core.Core +import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Extensions open Inclusion @@ -11,43 +12,186 @@ namespace Inclusion.Tests def unitInterval : Interval Dyadic := ⟨1, 2⟩ +section Constants + +example : (0 : ℝ) ≤ 0 := by + inclusion [core, interval_dyadic_real] + +example : (1 : ℝ) ≤ 1 := by + inclusion [core, interval_dyadic_real] + +example : (2 : ℝ) ≤ 2 := by + inclusion [core, interval_dyadic_real] + +example : ((3 : ℕ) : ℝ) ≤ 3 := by + inclusion [core, interval_dyadic_real] + +example : ((-3 : ℤ) : ℝ) = -3 := by + inclusion [core, interval_dyadic_real] + +end Constants + +section Arithmetic + example : (1 : ℝ) + 2 ≤ 3 := by - inclusion [core, real.dyadic] + inclusion [core, interval_dyadic_real] example : (1 : ℝ) + 2 ≤ 3 := by - inclusion [core, real.dyadic, core, real.dyadic] + inclusion [core, interval_dyadic_real, core, interval_dyadic_real] example : -(2 : ℝ) ≤ -1 := by - inclusion [core, real.dyadic] + inclusion [core, interval_dyadic_real] example : (3 : ℝ) - 1 ≤ 2 := by - inclusion [core, real.dyadic] + inclusion [core, interval_dyadic_real] + +end Arithmetic + +section Propositions + +example : (1 : ℝ) ≤ 2 := by + inclusion [core, interval_dyadic_real] + +example : (2 : ℝ) ≥ 1 := by + inclusion [core, interval_dyadic_real] + +example : (1 : ℝ) < 2 := by + inclusion [core, interval_dyadic_real] + +example : (2 : ℝ) > 1 := by + inclusion [core, interval_dyadic_real] + +example : (1 : ℝ) = 1 := by + inclusion [core, interval_dyadic_real] + +example : (1 : ℝ) ∈ Set.Ici 1 := by + inclusion [core, interval_dyadic_real] + +example : (1 : ℝ) ∈ Set.Iic 1 := by + inclusion [core, interval_dyadic_real] + +example : (1 : ℝ) ∈ Set.Ioi 0 := by + inclusion [core, interval_dyadic_real] + +example : (1 : ℝ) ∈ Set.Iio 2 := by + inclusion [core, interval_dyadic_real] + +example : (1 : ℝ) ∈ Set.Icc 1 1 := by + inclusion [core, interval_dyadic_real] + +example : (1 : ℝ) ∈ Set.Ico 1 2 := by + inclusion [core, interval_dyadic_real] + +example : (1 : ℝ) ∈ Set.Ioc 0 1 := by + inclusion [core, interval_dyadic_real] + +example : (1 : ℝ) ∈ Set.Ioo 0 2 := by + inclusion [core, interval_dyadic_real] + +example : ¬¬(1 : ℝ) ≤ 2 := by + inclusion [core, interval_dyadic_real] + +example : ((1 : ℝ) ≤ 2 ∧ (2 : ℝ) ≤ 3) := by + inclusion [core, interval_dyadic_real] + +example : ((2 : ℝ) ≤ 1 ∨ (2 : ℝ) ≤ 3) := by + inclusion [core, interval_dyadic_real] + +example : ¬(2 : ℝ) ≤ 1 := by + inclusion [core, interval_dyadic_real] + +example : ¬(2 : ℝ) < 1 := by + inclusion [core, interval_dyadic_real] + +example : (1 : ℝ) ≠ 2 := by + inclusion [core, interval_dyadic_real] + +example : ¬((2 : ℝ) ∈ Set.Icc 0 1) := by + inclusion [core, interval_dyadic_real] + +end Propositions + +section Hypotheses example {x : ℝ} (hx : x ∈ unitInterval) : x + x ≤ 4 := by - inclusion [core, real.dyadic] + inclusion [core, interval_dyadic_real] example {x : ℝ} (hx : x ∈ Set.Icc 1 2) : x + 1 ≤ 3 := by - inclusion [core, real.dyadic] + inclusion [core, interval_dyadic_real] example {x : ℝ} (hx : x = 2) : x + x ≤ 4 := by - inclusion [core, real.dyadic] + inclusion [core, interval_dyadic_real] + +example {x : ℝ} (hx : 1 ≤ x ∧ x ≤ 2) : x + x ≤ 4 := by + inclusion [core, interval_dyadic_real] + +example {x : ℝ} (hx : x ≤ 2) : x ≤ 2 := by + inclusion [core, interval_dyadic_real] + +example {x : ℝ} (hx : x ≥ 1) : x ≥ 1 := by + inclusion [core, interval_dyadic_real] + +example {x : ℝ} (hx : x < 2) : x < 3 := by + inclusion [core, interval_dyadic_real] + +example {x : ℝ} (hx : x > 1) : x > 0 := by + inclusion [core, interval_dyadic_real] + +example {x : ℝ} (hx : x ∈ Set.Ici 1) : x ∈ Set.Ici 1 := by + inclusion [core, interval_dyadic_real] + +example {x : ℝ} (hx : x ∈ Set.Iic 2) : x ∈ Set.Iic 2 := by + inclusion [core, interval_dyadic_real] + +example {x : ℝ} (hx : x ∈ Set.Ioi 1) : x ∈ Set.Ici 1 := by + inclusion [core, interval_dyadic_real] + +example {x : ℝ} (hx : x ∈ Set.Iio 2) : x ∈ Set.Iic 2 := by + inclusion [core, interval_dyadic_real] + +example {x : ℝ} (hx : x ∈ Set.Icc 1 2) : x ∈ Set.Icc 1 2 := by + inclusion [core, interval_dyadic_real] + +example {x : ℝ} (hx : x ∈ Set.Ico 1 2) : x ∈ Set.Icc 1 2 := by + inclusion [core, interval_dyadic_real] + +example {x : ℝ} (hx : x ∈ Set.Ioc 1 2) : x ∈ Set.Icc 1 2 := by + inclusion [core, interval_dyadic_real] + +example {x : ℝ} (hx : x ∈ Set.Ioo 1 2) : x ∈ Set.Icc 1 2 := by + inclusion [core, interval_dyadic_real] + +example {x y : ℝ} (hx : x ∈ Set.Icc 2 3) (hy : y ∈ Set.Icc 0 1) : ¬x ≤ y := by + inclusion [core, interval_dyadic_real] + +example {x y : ℝ} (hx : x ∈ Set.Icc 2 3) (hy : y ∈ Set.Icc 1 2) : ¬x < y := by + inclusion [core, interval_dyadic_real] + +end Hypotheses + +section Evaluation example : (1 : ℝ) ≤ 2 := by - inclusion +kernel [core, real.dyadic] + inclusion +kernel [core, interval_dyadic_real] + +example : ¬(2 : ℝ) ≤ 1 := by + inclusion +kernel [core, interval_dyadic_real] /-- info: The inclusion check succeeded. -/ #guard_msgs in set_option linter.unusedTactic false in example : (1 : ℝ) ≤ 2 := by - inclusion? [core, real.dyadic] - inclusion [core, real.dyadic] + inclusion? [core, interval_dyadic_real] + inclusion [core, interval_dyadic_real] /-- info: The inclusion check failed: -The proposition was not proven true or false. -/ +The proposition is provably false -/ #guard_msgs in set_option linter.unusedTactic false in example (h : False) : (2 : ℝ) ≤ 1 := by - inclusion? [core, real.dyadic] + inclusion? [core, interval_dyadic_real] exact h.elim +end Evaluation + end Inclusion.Tests diff --git a/MathlibTest/Tactic/Inclusion/Splitting.lean b/MathlibTest/Tactic/Inclusion/Splitting.lean index 02d84cd25845c3..89784dac837fa7 100644 --- a/MathlibTest/Tactic/Inclusion/Splitting.lean +++ b/MathlibTest/Tactic/Inclusion/Splitting.lean @@ -3,7 +3,8 @@ Copyright (c) 2026 David Ledvinka. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: David Ledvinka -/ -import Mathlib.Tactic.Inclusion.Extension.Extensions +import Mathlib.Tactic.Inclusion.Extension.Core.Core +import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Extensions open Inclusion @@ -14,20 +15,26 @@ def wideInterval : Interval Dyadic := ⟨0, 4⟩ example {x : ℝ} (_hx : x ∈ wideInterval) : True := by fail_if_success have : x - x ≤ 2 := by - inclusion [core, real.dyadic] + inclusion [core, interval_dyadic_real] trivial example {x : ℝ} (hx : x ∈ wideInterval) : x - x ≤ 2 := by - inclusion [core, real.dyadic] (binSplit := 1) + inclusion [core, interval_dyadic_real] (binSplit := 1) example {x : ℝ} (hx : x ∈ wideInterval) : x - x ≤ 1 := by - inclusion [core, real.dyadic] (binSplit := 2) + inclusion [core, interval_dyadic_real] (binSplit := 2) + +example {x : ℝ} (hx : x ≤ 2) : x ≤ 2 := by + inclusion [core, interval_dyadic_real] (binSplit := 100) + +example {x : ℝ} (hx : x ≥ 1) : x ≥ 1 := by + inclusion [core, interval_dyadic_real] (binSplit := 100) /-- info: The inclusion check succeeded. -/ #guard_msgs in set_option linter.unusedTactic false in example {x : ℝ} (hx : x ∈ wideInterval) : x - x ≤ 2 := by - inclusion? [core, real.dyadic] (binSplit := 1) - inclusion [core, real.dyadic] (binSplit := 1) + inclusion? [core, interval_dyadic_real] (binSplit := 1) + inclusion [core, interval_dyadic_real] (binSplit := 1) end Inclusion.Tests From 66ab98786bfdd0fd2e77f19538e6dc4a7f83a8bf Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Sun, 16 Aug 2026 19:28:09 -0400 Subject: [PATCH 04/38] linter --- Mathlib/Data/Dyadic.lean | 35 +- Mathlib/Tactic/Inclusion/Core/Elab.lean | 16 +- Mathlib/Tactic/Inclusion/Core/Extensions.lean | 4 +- .../Tactic/Inclusion/Extension/Core/Core.lean | 2 +- .../IntervalDyadicReal/Extensions.lean | 2 + .../IntervalDyadicReal/Rational.lean | 125 +++++++ .../Extension/IntervalDyadicReal/Tactic.lean | 40 +++ .../Tactic/Inclusion/ExtensionAPI/Attr.lean | 319 ++++++++---------- .../Tactic/Inclusion/ExtensionAPI/Basic.lean | 8 +- .../Tactic/Linter/DeprecatedSyntaxLinter.lean | 65 ++-- MathlibTest/Tactic/Inclusion/Basic.lean | 33 +- MathlibTest/Tactic/Inclusion/Linter.lean | 48 +++ MathlibTest/Tactic/Inclusion/Splitting.lean | 7 +- 13 files changed, 468 insertions(+), 236 deletions(-) create mode 100644 Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Rational.lean create mode 100644 Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean create mode 100644 MathlibTest/Tactic/Inclusion/Linter.lean diff --git a/Mathlib/Data/Dyadic.lean b/Mathlib/Data/Dyadic.lean index 21e1a2d5c5a453..5551ceee57ca2e 100644 --- a/Mathlib/Data/Dyadic.lean +++ b/Mathlib/Data/Dyadic.lean @@ -37,38 +37,45 @@ instance : AddCommGroup Dyadic where namespace Dyadic +section Division + +/-- Round a quotient of natural numbers downward to a dyadic number with precision `prec`. -/ +def divNatDown (prec m d : ℕ) : Dyadic := + Dyadic.ofIntWithPrec (((m <<< prec) / d : ℕ) : ℤ) prec + +/-- Round a quotient of natural numbers upward to a dyadic number with precision `prec`. -/ +def divNatUp (prec m d : ℕ) : Dyadic := + let scaled := m <<< prec + let quotient := scaled / d + Dyadic.ofIntWithPrec (if quotient * d = scaled then quotient else quotient + 1) prec + +end Division + section toReal /-- Interpret a dyadic rational as a real number. -/ def toReal (d : Dyadic) : ℝ := d.toRat @[simp] -lemma toReal_add (a b : Dyadic) : toReal (a + b) = toReal a + toReal b := by - simp [toReal] +lemma toReal_add (a b : Dyadic) : toReal (a + b) = toReal a + toReal b := by simp [toReal] @[simp] -lemma toReal_neg (a : Dyadic) : toReal (-a) = -toReal a := by - simp [toReal] +lemma toReal_neg (a : Dyadic) : toReal (-a) = -toReal a := by simp [toReal] @[simp] -lemma toReal_sub (a b : Dyadic) : toReal (a - b) = toReal a - toReal b := by - simp [toReal] +lemma toReal_sub (a b : Dyadic) : toReal (a - b) = toReal a - toReal b := by simp [toReal] @[simp] -lemma toReal_natCast (n : ℕ) : toReal (n : Dyadic) = (n : ℝ) := by - simp [toReal] +lemma toReal_natCast (n : ℕ) : toReal (n : Dyadic) = (n : ℝ) := by simp [toReal] @[simp] -lemma toReal_intCast (z : ℤ) : toReal (z : Dyadic) = (z : ℝ) := by - simp [toReal] +lemma toReal_intCast (z : ℤ) : toReal (z : Dyadic) = (z : ℝ) := by simp [toReal] @[simp] -lemma toReal_le_toReal {a b : Dyadic} : toReal a ≤ toReal b ↔ a ≤ b := by - simp [toReal] +lemma toReal_le_toReal {a b : Dyadic} : toReal a ≤ toReal b ↔ a ≤ b := by simp [toReal] @[simp] -lemma toReal_lt_toReal {a b : Dyadic} : toReal a < toReal b ↔ a < b := by - simp [toReal] +lemma toReal_lt_toReal {a b : Dyadic} : toReal a < toReal b ↔ a < b := by simp [toReal] /-- `Dyadic.toReal` as an additive monoid homomorphism. -/ def toRealAddMonoidHom : Dyadic →+ ℝ where diff --git a/Mathlib/Tactic/Inclusion/Core/Elab.lean b/Mathlib/Tactic/Inclusion/Core/Elab.lean index cba27ff1fb9602..c0252fb3b024b0 100644 --- a/Mathlib/Tactic/Inclusion/Core/Elab.lean +++ b/Mathlib/Tactic/Inclusion/Core/Elab.lean @@ -32,10 +32,10 @@ def collectInclusionFamilies (config : InclusionConfig) (familyStxs : Array Synt throwError "At least one inclusion family must be specified" let mut families := #[] for familyStx in familyStxs do - let family := familyStx.getId + let family := familyStx.getId.eraseMacroScopes unless families.contains family do unless (← getInclusionFamily? family).isSome do - throwError "Unknown inclusion family '{family}'" + throwError "Unknown inclusion family `{family}`" families := families.push family return { config with families } @@ -56,20 +56,20 @@ def collectInclusionParams (config : InclusionConfig) (paramStxs : Array Syntax) | `(inclusionParam| $name:ident := $value:term) => pure (name.getId, value) | _ => throwUnsupportedSyntax let some decl := params.find? name - | throwError "Unknown inclusion parameter '{name}'" + | throwError "Unknown inclusion parameter `{name}`" if config.paramSettings.contains name then - throwError "Inclusion parameter '{name}' was specified more than once" + throwError "Inclusion parameter `{name}` was specified more than once" let value ← elabTerm valueStx decl.type Term.synthesizeSyntheticMVarsNoPostponing let value ← instantiateMVars value config := { config with paramSettings := config.paramSettings.insert name value } return config -/-- Syntax for the `inclusion` tactic. -/ +/-- `inclusion` tactic for proving "inclusion" propositions. -/ syntax (name := inclusionTacStx) "inclusion" optConfig " [" ident,* "]" (" (" inclusionParam,* ")")? : tactic -/-- `inclusion` tactic for proving "inclusion" propositions. -/ +/-- Elaborator for the `inclusion` tactic. -/ @[tactic inclusionTacStx] def inclusionTac : Tactic | `(tactic| inclusion $cfg:optConfig [$families,*] $[($paramStxs,*)]?) => do @@ -80,11 +80,11 @@ def inclusionTac : Tactic closeMainGoalUsing `inclusion fun goal _ => inclusionCore goal config | _ => throwUnsupportedSyntax -/-- Syntax for the `inclusion?` tactic. -/ +/-- Tactic for quickly checking if the `inclusion` tactic will succeed. -/ syntax (name := inclusion?TacStx) "inclusion?" " [" ident,* "]" (" (" inclusionParam,* ")")? : tactic -/-- Tactic for quickly checking if the `inclusion` tactic will succeed. -/ +/-- Elaborator for the `inclusion?` tactic. -/ @[tactic inclusion?TacStx] def inclusion?Tac : Tactic | `(tactic| inclusion? [$families,*] $[($paramStxs,*)]?) => do diff --git a/Mathlib/Tactic/Inclusion/Core/Extensions.lean b/Mathlib/Tactic/Inclusion/Core/Extensions.lean index 19ffc4366fba29..7736db44caafba 100644 --- a/Mathlib/Tactic/Inclusion/Core/Extensions.lean +++ b/Mathlib/Tactic/Inclusion/Core/Extensions.lean @@ -63,7 +63,7 @@ initialize inclusionFamiliesRef : IO.Ref InclusionFamilies ← IO.mkRef {} def registerInclusionFamily (name : Name) (ref : Name := by exact decl_name%) : IO InclusionFamily := do if (← inclusionFamiliesRef.get).contains name then - throw <| IO.userError s!"Inclusion family '{name}' is already registered" + throw <| IO.userError s!"Inclusion family `{name}` is already registered" let inclusionExt ← initializeEnvExt ``InclusionExt (ref.str "inclusionExt") let hypothesisExt ← initializeEnvExt ``HypothesisExt (ref.str "hypothesisExt") let family := { name, inclusionExt, hypothesisExt } @@ -81,7 +81,7 @@ def getInclusionFamily? (name : Name) : CoreM (Option InclusionFamily) := do /-- Return the registered inclusion family named `name`, or fail if it is not registered. -/ def getInclusionFamily (name : Name) : CoreM InclusionFamily := do let some family ← getInclusionFamily? name - | throwError "Unknown inclusion family '{name}'" + | throwError "Unknown inclusion family `{name}`" return family /-- Return an array of the inclusion extensions in `families` whose `DiscrTree` key matches `e`, diff --git a/Mathlib/Tactic/Inclusion/Extension/Core/Core.lean b/Mathlib/Tactic/Inclusion/Extension/Core/Core.lean index 716065893465ef..b1a650f1e58b39 100644 --- a/Mathlib/Tactic/Inclusion/Extension/Core/Core.lean +++ b/Mathlib/Tactic/Inclusion/Extension/Core/Core.lean @@ -31,7 +31,7 @@ def instMembershipHyp : HypothesisExt where let type ← instantiateMVars (← inferType h) let some (expr, set, _) := toSetMem? type | failure if set.hasFVar || set.hasMVar then failure - let some iVar ← findIVar? expr | return + let some iVar ← findIVar? expr | failure addInclusionHyp iVar.iExpr ⟨set, h⟩ /-- `HypothesisExt` for conjunction hypotheses. -/ diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Extensions.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Extensions.lean index a764ea36a3b6b4..e5216d36183bc5 100644 --- a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Extensions.lean +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Extensions.lean @@ -6,8 +6,10 @@ Authors: David Ledvinka module public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Basic +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Rational public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Splitting public meta import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Basic +public meta import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Rational public meta import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Splitting public meta import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Hypotheses public meta import Mathlib.Tactic.Inclusion.ExtensionAPI.Basic diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Rational.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Rational.lean new file mode 100644 index 00000000000000..642b32ddbb7f4a --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Rational.lean @@ -0,0 +1,125 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Basic +public meta import Mathlib.Tactic.Inclusion.ExtensionAPI.Attr + +/-! +# Rational constants for interval_dyadic_real + +This file defines dyadic enclosures of rational casts and scientific literals for the +`interval_dyadic_real` inclusion family. +-/ + +public meta section + +open Lean Qq + +namespace Inclusion + +namespace IntervalDyadicReal + +/-- The precision of dyadic approximations. -/ +@[inclusionParam] +def precParam : InclusionParamDecl where + name := `prec + type := q(Nat) + +end IntervalDyadicReal + +end Inclusion + +end + +@[expose] public section + +namespace Inclusion + +namespace IntervalDyadicReal + +/-- Enclose a quotient of natural numbers in a dyadic interval with precision `prec`. -/ +def divNatBounds (prec m d : ℕ) : Interval Dyadic := + let scaled := m <<< prec + let quotient := scaled / d + let upper := if quotient * d = scaled then quotient else quotient + 1 + Interval.Icc (Dyadic.ofIntWithPrec quotient prec) (Dyadic.ofIntWithPrec upper prec) + +/-- Enclose a rational number in a dyadic interval with precision `prec`. -/ +def ratBounds (prec : ℕ) (q : ℚ) : Interval Dyadic := + let lower := q.toDyadic prec + let upper := if lower.toRat = q then lower else lower + Dyadic.ofIntWithPrec 1 prec + ⟨lower, upper⟩ + +/-- Enclose a scientific literal in a dyadic interval with precision `prec`. -/ +def scientific (prec m : ℕ) (s : Bool) (e : ℕ) : Interval Dyadic := + if s then + divNatBounds prec m (10 ^ e) + else + Interval.singleton Dyadic ((m * 10 ^ e : ℕ) : Dyadic) + +@[inclusionOp interval_dyadic_real] +theorem ratCast_mem (prec : ℕ) (q : ℚ) : (q : ℝ) ∈ ratBounds prec q := by + rw [mem_iff_mem_map] + constructor + · exact WithBot.coe_le_coe.mpr <| Rat.cast_le.mpr Rat.toRat_toDyadic_le + · apply WithTop.coe_le_coe.mpr + split_ifs with h + · rw [Dyadic.toReal, h] + · exact (Rat.cast_lt (K := ℝ)).mpr Rat.lt_toRat_toDyadic_add |>.le + +theorem divNatDown_le (prec m : ℕ) {d : ℕ} (hd : 0 < d) : + Dyadic.toReal (Dyadic.divNatDown prec m d) ≤ (m : ℝ) / d := by + rw [Dyadic.toReal, Dyadic.divNatDown, Dyadic.toRat_ofIntWithPrec_eq_mul_two_pow] + norm_num + rw [Int.shiftLeft_eq, ← div_eq_mul_inv] + apply (div_le_div_iff₀ + (by exact_mod_cast Nat.pow_pos (by decide : 0 < 2)) + (by exact_mod_cast hd)).2 + norm_cast + exact Nat.div_mul_le_self (m * 2 ^ prec) d + +theorem le_divNatUp (prec m : ℕ) {d : ℕ} (hd : 0 < d) : + (m : ℝ) / d ≤ Dyadic.toReal (Dyadic.divNatUp prec m d) := by + rw [Dyadic.toReal, ← Rat.cast_natCast (α := ℝ) m, ← Rat.cast_natCast (α := ℝ) d, + ← Rat.cast_div, Rat.cast_le] + rw [Dyadic.divNatUp, Dyadic.toRat_ofIntWithPrec_eq_mul_two_pow] + norm_num + split_ifs with h + all_goals + rw [← div_eq_mul_inv] + apply (div_le_div_iff₀ (by exact_mod_cast hd) (pow_pos (by norm_num) _)).2 + simp only [Nat.shiftLeft_eq, Int.shiftLeft_eq] at h ⊢ + norm_cast + · exact h.ge + · have hlt := Nat.lt_mul_div_self_add (x := m * 2 ^ prec) hd + rw [Nat.mul_comm d] at hlt + rw [Nat.add_mul, Nat.one_mul] + exact hlt.le + +theorem divNat_mem_bounds (prec m : ℕ) {d : ℕ} (hd : 0 < d) : + (m : ℝ) / d ∈ (divNatBounds prec m d).map Dyadic.toReal := by + constructor + · apply WithBot.coe_le_coe.mpr + simpa [divNatBounds, Dyadic.divNatDown] using divNatDown_le prec m hd + · apply WithTop.coe_le_coe.mpr + simpa [divNatBounds, Dyadic.divNatUp] using le_divNatUp prec m hd + +@[inclusionOp interval_dyadic_real] +theorem scientific_mem (prec m : ℕ) (s : Bool) (e : ℕ) : + (OfScientific.ofScientific (α := ℝ) m s e) ∈ scientific prec m s e := by + cases s + · rw [mem_iff_mem_map, NNRatCast.ofScientific_eq_ite] + simp only [Bool.false_eq_true, if_false, NNRat.cast_natCast] + simpa [scientific] using + Interval.mem_map_singleton ((m * 10 ^ e : ℕ) : Dyadic) Dyadic.toReal + · rw [mem_iff_mem_map] + simpa [scientific, NNRatCast.ofScientific_eq_ite] using + divNat_mem_bounds prec m (Nat.pow_pos (by decide : 0 < 10)) + +end IntervalDyadicReal + +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean new file mode 100644 index 00000000000000..eb1e592f35b440 --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean @@ -0,0 +1,40 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public meta import Mathlib.Tactic.Inclusion.Extension.Core.Core +public meta import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Extensions + +/-! +# The `dyadic_interval` tactic + +This file defines the `dyadic_interval` tactic which is a wrapper around the inclusion tactic +with a specific set of enabled inclusion families. +-/ + +public meta section + +open Lean.Parser.Tactic + +namespace Inclusion + +/-- `dyadic_interval` runs `inclusion` with the `core` and `interval_dyadic_real` extension +families. Inclusion parameters may be supplied using `dyadic_interval [name := value, ...]`. -/ +syntax (name := dyadicInterval) "dyadic_interval" optConfig + (" [" inclusionParam,* "]")? : tactic + +macro_rules + | `(tactic| dyadic_interval $cfg:optConfig $[[$params:inclusionParam,*]]?) => + `(tactic| inclusion $cfg [core, interval_dyadic_real] $[($params,*)]?) + +/-- `dyadic_interval?` checks whether `dyadic_interval` can prove the goal without closing it. -/ +syntax (name := dyadicInterval?) "dyadic_interval?" (" [" inclusionParam,* "]")? : tactic + +macro_rules + | `(tactic| dyadic_interval? $[[$params:inclusionParam,*]]?) => + `(tactic| inclusion? [core, interval_dyadic_real] $[($params,*)]?) + +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean b/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean index b6f34fd68ea39f..045d9b4f039461 100644 --- a/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean +++ b/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean @@ -11,7 +11,6 @@ public meta import Mathlib.Tactic.Inclusion.ExtensionAPI.Basic # Attributes for `inclusion` extensions This file defines the attributes used to register inclusion and hypothesis extensions. - -/ public meta section @@ -23,16 +22,13 @@ namespace Inclusion /-- Syntax for registering an inclusion parameter using the `inclusionParam` attribute. -/ syntax (name := inclusionParamAttr) "inclusionParam" : attr +/-- Validate an inclusion parameter declaration. -/ private def validateInclusionParamDecl (decl : InclusionParamDecl) : MetaM Unit := do - if decl.type.hasFVar || decl.type.hasMVar then - throwError "The type of inclusion parameter '{decl.name}' is not closed" - unless (← inferType decl.type).isSort do - throwError "The declared type {decl.type} of inclusion parameter '{decl.name}' is not a type" + unless ← isType decl.type do + throwError "The declared type {decl.type} of inclusion parameter `{decl.name}` is not a type" if let some value := decl.defaultValue? then - if value.hasFVar || value.hasMVar then - throwError "The default value of inclusion parameter '{decl.name}' is not closed" unless ← isDefEq (← inferType value) decl.type do - throwError "The default value {value} of inclusion parameter '{decl.name}' does not have \ + throwError "The default value {value} of inclusion parameter `{decl.name}` does not have \ type {decl.type}" /-- Add the inclusion parameter declared by `declName`. -/ @@ -40,22 +36,22 @@ def addInclusionParam (declName : Name) (kind : AttributeKind) : AttrM Unit := d let env ← getEnv ensureAttrDeclIsMeta `inclusionParam declName kind unless (env.getModuleIdxFor? declName).isNone do - throwError "invalid attribute `inclusionParam`, declaration is in an imported module" + throwAttrDeclInImportedModule `inclusionParam declName if (IR.getSorryDep env declName).isSome then return let decl ← mkInclusionParamDecl declName MetaM.run' <| validateInclusionParamDecl decl let params := inclusionParamExt.getState env if params.decls.contains decl.name then - throwError "Inclusion parameter '{decl.name}' is already registered" + throwError "Inclusion parameter `{decl.name}` is already registered" inclusionParamExt.add (declName, decl) kind -/-- Register the `inclusionParam` attribute registers a typed inclusion-tactic parameter. -/ initialize registerBuiltinAttribute { name := `inclusionParamAttr descr := "registers an inclusion-tactic parameter" applicationTime := .afterCompilation add := fun declName _ kind => addInclusionParam declName kind } + /-- Syntax for declaring an inclusion extension using the `inclusionExt` attribute. -/ syntax (name := inclusionExtAttr) "inclusionExt " ident " | " term,+ : attr @@ -66,26 +62,24 @@ def addInclusionExt (familyName declName : Name) (keys : Array (Array DiscrTree. let ext ← evalDecl InclusionExt ``InclusionExt declName family.inclusionExt.add ((keys, declName), ext) kind -/-- Register the `inclusionExt` attribute. -/ initialize registerBuiltinAttribute { name := `inclusionExtAttr - descr := "adds an inclusion-function extension" + descr := "adds an inclusion extension" applicationTime := .afterCompilation - add := fun declName stx kind => do - let env ← getEnv - if (IR.getSorryDep env declName).isSome then return - match stx with + add := fun declName stx kind => match stx with | `(attr| inclusionExt $familyName:ident | $es,*) => do - unless (env.getModuleIdxFor? declName).isNone do - throwError "invalid attribute `inclusionExt`, declaration is in an imported module" + let env ← getEnv ensureAttrDeclIsMeta `inclusionExt declName kind + unless (env.getModuleIdxFor? declName).isNone do + throwAttrDeclInImportedModule `inclusionExt declName + if (IR.getSorryDep env declName).isSome then return let keys ← elabExtKeys (es.getElems.map (·.raw)) addInclusionExt familyName.getId declName keys kind | _ => throwUnsupportedSyntax erase := fun _ => throwError "Inclusion extensions cannot be erased by declaration" } -/-- Syntax for declaring an hypothesis extension using the `hypothesisExt` attribute. -/ +/-- Syntax for declaring a hypothesis extension using the `hypothesisExt` attribute. -/ syntax (name := hypothesisExtAttr) "hypothesisExt " ident " | " term,+ : attr /-- Add the hypothesis extension `declName` to `familyName` under `keys`. -/ @@ -100,32 +94,31 @@ initialize registerBuiltinAttribute { name := `hypothesisExtAttr descr := "adds a hypothesis extension" applicationTime := .afterCompilation - add := fun declName stx kind => do - let env ← getEnv - if (IR.getSorryDep env declName).isSome then return - match stx with + add := fun declName stx kind => match stx with | `(attr| hypothesisExt $familyName:ident | $es,*) => do - unless (env.getModuleIdxFor? declName).isNone do - throwError "invalid attribute `hypothesisExt`, declaration is in an imported module" + let env ← getEnv ensureAttrDeclIsMeta `hypothesisExt declName kind + unless (env.getModuleIdxFor? declName).isNone do + throwAttrDeclInImportedModule `hypothesisExt declName + if (IR.getSorryDep env declName).isSome then return let keys ← elabExtKeys (es.getElems.map (·.raw)) addHypothesisExt familyName.getId declName keys kind | _ => throwUnsupportedSyntax erase := fun _ => throwError "Hypothesis extensions cannot be erased by declaration" } -/-- The argument indices of an expression, its inclusion set, and its membership proof in an -inclusion theorem. -/ -structure InclusionHypothesisArg where - /-- The index of the expression argument. -/ - exprIdx : Nat - /-- The index of the inclusion-set argument. -/ +/-- The argument indices of an element `x`, a set `s`, and an inclusion hypothesis `x ∈ s` +in some theorem statement. -/ +structure HypArg where + /-- The index of the element argument. -/ + elemIdx : Nat + /-- The index of the set argument. -/ setIdx : Nat - /-- The index of the membership-proof argument. -/ + /-- The index of the membership proof argument. -/ proofIdx : Nat deriving Inhabited, ToExpr -/-- A registered inclusion parameter and the index of its argument in an inclusion theorem. -/ +/-- The name of an inclusion parameter and its index in some theorem statement. -/ structure ParamArg where /-- The name of the registered inclusion parameter. -/ name : Name @@ -135,17 +128,17 @@ structure ParamArg where /-- Apply the inclusion theorem `theoremName` to `e`, recursively constructing the inclusion bodies specified by `hypArgs` and filling the registered parameter arguments specified by `paramArgs`. -/ -def deriveInclusionOp (theoremName : Name) (hypArgs : Array InclusionHypothesisArg) - (paramArgs : Array ParamArg) (e : Expr) : InclusionM ExprInclusionBody := do +def deriveInclusionOp (theoremName : Name) (hypArgs : Array HypArg) (paramArgs : Array ParamArg) + (e : Expr) : InclusionM ExprInclusionBody := do let theoremExpr ← mkConstWithFreshMVarLevels theoremName let (args, binderInfos, conclusion) ← forallMetaTelescopeReducing (← inferType theoremExpr) let some (expr, inclusionBody, _) := toSetMem? conclusion | failure unless ← isDefEq expr e do failure for ⟨name, idx⟩ in paramArgs do unless ← isDefEq args[idx]! (← InclusionM.getParam name) do failure - for ⟨exprIdx, setIdx, proofIdx⟩ in hypArgs do - let arg ← instantiateMVars args[exprIdx]! - let body ← mkExprInclusionBody arg + for ⟨elemIdx, setIdx, proofIdx⟩ in hypArgs do + let inputExpr ← instantiateMVars args[elemIdx]! + let body ← mkExprInclusionBody inputExpr unless ← isDefEq args[setIdx]! body.inclusionBody do failure unless ← isDefEq args[proofIdx]! body.proofBody do failure for h : i in [:args.size] do @@ -154,28 +147,30 @@ def deriveInclusionOp (theoremName : Name) (hypArgs : Array InclusionHypothesisA if binderInfos[i]!.isInstImplicit then argId.assign (← synthInstance (← argId.getType)) else - throwError "Could not infer theorem argument '{(← argId.getDecl).userName}' in inclusion \ - extension generated from '{theoremName}'" - return ⟨← instantiateMVars inclusionBody, ← instantiateMVars (mkAppN theoremExpr args)⟩ + throwError "Could not infer theorem argument `{(← argId.getDecl).userName}` in inclusion \ + extension generated from `{.ofConstName theoremName}`" + let inclusionBody ← instantiateMVars inclusionBody + let proofBody ← instantiateMVars (mkAppN theoremExpr args) + return { inclusionBody, proofBody } -/-- Apply the hypothesis extension generated from `theoremName` to hypothesis `h`. -/ -def deriveHypothesisOp (theoremName : Name) (sourceIdx : Nat) - (hypArgs : Array InclusionHypothesisArg) (paramArgs : Array ParamArg) - (h : Expr) : HypothesisM Unit := do - let type ← instantiateMVars (← inferType h) +/-- Apply the hypothesis theorem `theoremName` to `h` as its source hypothesis at `sourceIdx`, +recursively constructing the closed inclusion bodies specified by `hypArgs` and filling the +registered parameter arguments specified by `paramArgs`. -/ +def deriveHypothesisOp (theoremName : Name) (sourceIdx : Nat) (hypArgs : Array HypArg) + (paramArgs : Array ParamArg) (h : Expr) : HypothesisM Unit := do + let hypType ← instantiateMVars (← inferType h) let theoremExpr ← mkConstWithFreshMVarLevels theoremName let (args, binderInfos, conclusion) ← forallMetaTelescopeReducing (← inferType theoremExpr) let sourceId := args[sourceIdx]!.mvarId! - unless ← isDefEq (← sourceId.getType) type do failure + unless ← isDefEq (← sourceId.getType) hypType do failure sourceId.assign h - let some (outputExpr, outputSet, _) := toSetMem? conclusion | failure - let outputExpr ← instantiateMVars outputExpr - let some iVar ← findIVar? outputExpr | return - let iExpr := iVar.iExpr + let some (e, s, _) := toSetMem? conclusion | failure + let e ← instantiateMVars e + let some ⟨iExpr, _, _, _⟩ ← findIVar? e | failure for ⟨name, idx⟩ in paramArgs do unless ← isDefEq args[idx]! (← HypothesisM.getParam name) do failure - for ⟨exprIdx, setIdx, proofIdx⟩ in hypArgs do - let inputExpr ← instantiateMVars args[exprIdx]! + for ⟨elemIdx, setIdx, proofIdx⟩ in hypArgs do + let inputExpr ← instantiateMVars args[elemIdx]! let body ← mkHypExprInclusionBody inputExpr unless ← isDefEq args[setIdx]! body.inclusionBody do failure unless ← isDefEq args[proofIdx]! body.proofBody do failure @@ -185,168 +180,144 @@ def deriveHypothesisOp (theoremName : Name) (sourceIdx : Nat) if binderInfos[i]!.isInstImplicit then argId.assign (← synthInstance (← argId.getType)) else - throwError "Could not infer theorem argument '{(← argId.getDecl).userName}' in hypothesis \ - extension generated from '{theoremName}'" - let body := - { inclusionBody := ← instantiateMVars outputSet - proofBody := ← instantiateMVars (mkAppN theoremExpr args) } - addInclusionHyp iExpr body + throwError "Could not infer theorem argument `{(← argId.getDecl).userName}` in \ + hypothesis extension generated from `{.ofConstName theoremName}`" + let inclusionBody ← instantiateMVars s + let proofBody ← instantiateMVars (mkAppN theoremExpr args) + addInclusionHyp iExpr { inclusionBody, proofBody } -private def analyzeTheoremArgs (declName : Name) (pattern outputSet : Expr) - (args : Array Expr) (binderInfos : Array BinderInfo) (sourceIdx : Option Nat := none) : - MetaM (Array InclusionHypothesisArg × Array ParamArg) := do +/-- Extract the `HypArg` and `ParamArg` metadata from a theorem declaration. -/ +private def getOpArgInfo (declName : Name) (matchExpr : Expr) + (args : Array Expr) (sourceIdx? : Option Nat := none) : + MetaM (Array HypArg × Array ParamArg) := do let registeredParams := inclusionParamExt.getState (← getEnv) - let mut inputs := #[] + let mut hypArgs := #[] let mut params := #[] for h : i in [:args.size] do - let arg := args[i] - let argDecl ← arg.mvarId!.getDecl + let argId := args[i].mvarId! + let argDecl ← argId.getDecl let argType := argDecl.type - if let some (inputExpr, inputSet, _) := toSetMem? argType then - let some exprIndex := args.findIdx? (· == inputExpr) - | throwError "The member '{inputExpr}' in premise '{argType}' of theorem \ - '{declName}' is not a theorem variable" - let some setIndex := args.findIdx? (· == inputSet) - | throwError "The set '{inputSet}' in premise '{argType}' of theorem \ - '{declName}' is not a theorem variable" - let exprId := args[exprIndex]!.mvarId! - let setId := args[setIndex]!.mvarId! - let proofId := arg.mvarId! - unless (pattern.findMVar? (· == exprId)).isSome do - throwError "The recursive input '{inputExpr}' of theorem '{declName}' does not occur in \ - the matched expression" - if (outputSet.findMVar? (· == exprId)).isSome then - throwError "The recursive input '{inputExpr}' of theorem '{declName}' occurs in its \ - output inclusion" - if (pattern.findMVar? (· == setId)).isSome then - throwError "The input set '{inputSet}' of theorem '{declName}' occurs in the matched \ - expression" - unless (outputSet.findMVar? (· == setId)).isSome do - throwError "The input set '{inputSet}' of theorem '{declName}' does not occur in its \ - output inclusion" - if (pattern.findMVar? (· == proofId)).isSome then - throwError "The inclusion hypothesis '{argType}' of theorem '{declName}' occurs in the \ - matched expression" - if (outputSet.findMVar? (· == proofId)).isSome then - throwError "The inclusion hypothesis '{argType}' of theorem '{declName}' occurs in its \ - output inclusion" - if inputs.any fun input => input.exprIdx == exprIndex then - throwError "The recursive input '{inputExpr}' occurs in more than one premise of theorem \ - '{declName}'" - if inputs.any fun input => input.setIdx == setIndex then - throwError "The input set '{inputSet}' occurs in more than one premise of theorem \ - '{declName}'" - inputs := inputs.push { exprIdx := exprIndex, setIdx := setIndex, proofIdx := i } - else if sourceIdx != some i then + if let some (e, s, _) := toSetMem? argType then + let some elemIdx := args.findIdx? (· == e) + | throwError "The member `{e}` in premise `{argType}` of theorem `{.ofConstName declName}` \ + is not a theorem variable" + let some setIdx := args.findIdx? (· == s) + | throwError "The set `{s}` in premise `{argType}` of theorem `{.ofConstName declName}` \ + is not a theorem variable" + let elemId := args[elemIdx]!.mvarId! + unless (matchExpr.findMVar? (· == elemId)).isSome do + throwError "The recursive input `{e}` of theorem \ + `{.ofConstName declName}` does not occur in the matched expression" + hypArgs := hypArgs.push { elemIdx, setIdx, proofIdx := i } + else if sourceIdx? != some i then let userName := argDecl.userName if let some param := registeredParams.find? userName then unless ← isDefEq argType param.type do - throwError "Inclusion parameter '{userName}' in '{declName}' has \ + throwError "Inclusion parameter `{userName}` in `{.ofConstName declName}` has \ type {argType}, expected {param.type}" - if (pattern.findMVar? fun mvarId => mvarId == arg.mvarId!).isSome then - throwError "Inclusion parameter '{userName}' in '{declName}' occurs in the matched \ - expression" - if params.any (·.name == userName) then - throwError "Inclusion parameter '{userName}' occurs more than once in '{declName}'" + if (matchExpr.findMVar? (· == argId)).isSome then + throwError "Inclusion parameter `{userName}` in `{.ofConstName declName}` occurs in the \ + matched expression" params := params.push { name := userName, idx := i } - else if ← isProp argType then - unless binderInfos[i]!.isInstImplicit || - (pattern.findMVar? fun mvarId => mvarId == arg.mvarId!).isSome do - throwError "Unsupported premise '{argType}' in theorem '{declName}'" - return (inputs, params) - -syntax (name := inclusionOpAttr) "inclusionOp " ident (prio)? : attr + return (hypArgs, params) -private def analyzeInclusionTheorem (declName : Name) : - MetaM (Array DiscrTree.Key × Array InclusionHypothesisArg × Array ParamArg) := do +/-- Validate an inclusion theorem and return its discrimination-tree path and argument metadata. -/ +private def getInclusionOpInfo (declName : Name) : + MetaM (Array DiscrTree.Key × Array HypArg × Array ParamArg) := do let theoremExpr ← mkConstWithFreshMVarLevels declName - let (args, binderInfos, conclusion) ← - forallMetaTelescopeReducing (← inferType theoremExpr) - let some (pattern, outputSet, _) := toSetMem? conclusion - | throwError "The conclusion of '{declName}' is not an inclusion using a `ToSet` instance" - let (inputs, params) ← analyzeTheoremArgs declName pattern outputSet args binderInfos - return (← DiscrTree.mkPath pattern, inputs, params) + let (args, _, conclusion) ← forallMetaTelescopeReducing (← inferType theoremExpr) + let some (e, _, _) := toSetMem? conclusion + | throwError "The conclusion of `{.ofConstName declName}` is not an inclusion using a `ToSet` \ + instance" + let (hypArgs, params) ← getOpArgInfo declName e args + return (← DiscrTree.mkPath e, hypArgs, params) +/-- Generate and register an inclusion extension from `theoremName` in `familyName`. -/ private def addInclusionOp (theoremName familyName : Name) (priority : Nat) (kind : AttributeKind) : AttrM Unit := do - let (path, inputs, params) ← MetaM.run' <| analyzeInclusionTheorem theoremName - let declName := Name.str ((← getEnv).mainModule ++ theoremName) "_inclusionExt" - unless (← getEnv).contains declName do - let derive := mkAppN (mkConst ``deriveInclusionOp) - #[toExpr theoremName, toExpr inputs, toExpr params] - let value := mkAppN (mkConst ``InclusionExt.mk) - #[toExpr declName, toExpr theoremName, derive, toExpr priority] - let decl ← mkDefinitionValInferringUnsafe declName [] (mkConst ``InclusionExt) value .opaque - addAndCompile (markMeta := true) (.defnDecl decl) - addInclusionExt familyName declName #[path] kind + let (path, hypArgs, params) ← MetaM.run' <| getInclusionOpInfo theoremName + let extName ← withDeclNameForAuxNaming theoremName do mkAuxDeclName `_inclusionExt + let derive := mkAppN (mkConst ``deriveInclusionOp) + #[toExpr theoremName, toExpr hypArgs, toExpr params] + let value := mkAppN (mkConst ``InclusionExt.mk) + #[toExpr extName, toExpr theoremName, derive, toExpr priority] + let decl ← mkDefinitionValInferringUnsafe extName [] (mkConst ``InclusionExt) value .opaque + addAndCompile (.defnDecl decl) (markMeta := true) + addInclusionExt familyName extName #[path] kind + +/-- Syntax for registering an inclusion extension from a theorem using the `inclusionOp` +attribute. -/ +syntax (name := inclusionOpAttr) "inclusionOp " ident (prio)? : attr -/-- The `inclusionOp` attribute generates an inclusion extension from an inclusion theorem. -/ initialize registerBuiltinAttribute { name := `inclusionOpAttr descr := "adds an inclusion operation" applicationTime := .afterCompilation - add := fun declName stx kind => do - if (IR.getSorryDep (← getEnv) declName).isSome then return - match stx with - | `(attr| inclusionOp $familyName:ident $[$_prio:prio]?) => + add := fun declName stx kind => match stx with + | `(attr| inclusionOp $familyName:ident $[$_prio:prio]?) => do + if (IR.getSorryDep (← getEnv) declName).isSome then return addInclusionOp declName familyName.getId (← getAttrParamOptPrio stx[2]) kind | _ => throwUnsupportedSyntax erase := fun _ => throwError "Inclusion operations cannot be erased by declaration" } -syntax (name := hypothesisOpAttr) "hypothesisOp " ident (prio)? : attr - -private def analyzeHypothesisTheorem (declName : Name) : - MetaM (Array DiscrTree.Key × Nat × Array InclusionHypothesisArg × Array ParamArg) := do +/-- Validate a hypothesis theorem and return its discrimination-tree path and argument metadata. -/ +private def getHypothesisOpInfo (declName : Name) : + MetaM (Array DiscrTree.Key × Nat × Array HypArg × Array ParamArg) := do let theoremExpr ← mkConstWithFreshMVarLevels declName - let (args, binderInfos, conclusion) ← - forallMetaTelescopeReducing (← inferType theoremExpr) - let some (outputExpr, outputSet, _) := toSetMem? conclusion - | throwError "The conclusion of '{declName}' is not an inclusion using a `ToSet` instance" + let (args, binderInfos, conclusion) ← forallMetaTelescopeReducing (← inferType theoremExpr) + let some (e, s, _) := toSetMem? conclusion + | throwError "The conclusion of `{.ofConstName declName}` is not an inclusion using a `ToSet` \ + instance" let mut sourceIdx? := none for h : i in [:args.size] do let argType ← args[i].mvarId!.getType - if binderInfos[i]!.isExplicit && (← isProp argType) && (toSetMem? argType).isNone then + if binderInfos[i]!.isExplicit && (toSetMem? argType).isNone && (← isProp argType) then if sourceIdx?.isSome then - throwError "Hypothesis theorem '{declName}' has more than one non-inclusion premise" + throwError "Hypothesis theorem `{.ofConstName declName}` has more than one non-inclusion \ + premise" sourceIdx? := some i let some sourceIdx := sourceIdx? - | throwError "Hypothesis theorem '{declName}' has no non-inclusion premise" + | throwError "Hypothesis theorem `{.ofConstName declName}` has no non-inclusion premise" let sourceId := args[sourceIdx]!.mvarId! - if (outputExpr.findMVar? (· == sourceId)).isSome || - (outputSet.findMVar? (· == sourceId)).isSome then - throwError "The source hypothesis of '{declName}' occurs in its output inclusion" - let pattern ← args[sourceIdx]!.mvarId!.getType - let (inputs, params) ← - analyzeTheoremArgs declName pattern outputSet args binderInfos (some sourceIdx) + let sourceType ← sourceId.getType + if (e.findMVar? (· == sourceId)).isSome then + throwError "The source hypothesis of `{.ofConstName declName}` occurs in its represented \ + expression" + if (s.findMVar? (· == sourceId)).isSome then + throwError "The source hypothesis of `{.ofConstName declName}` occurs in its output inclusion" + let (hypArgs, params) ← getOpArgInfo declName sourceType args (some sourceIdx) for ⟨name, idx⟩ in params do - if (outputExpr.findMVar? (· == args[idx]!.mvarId!)).isSome then - throwError "Inclusion parameter '{name}' in '{declName}' occurs in the output expression" - return (← DiscrTree.mkPath pattern, sourceIdx, inputs, params) + let paramId := args[idx]!.mvarId! + if (e.findMVar? (· == paramId)).isSome then + throwError "Inclusion parameter `{name}` in `{.ofConstName declName}` occurs in the \ + represented expression" + return (← DiscrTree.mkPath sourceType, sourceIdx, hypArgs, params) +/-- Generate and register a hypothesis extension from `theoremName` in `familyName`. -/ private def addHypothesisOp (theoremName familyName : Name) (priority : Nat) (kind : AttributeKind) : AttrM Unit := do - let (path, sourceIdx, inputs, params) ← - MetaM.run' <| analyzeHypothesisTheorem theoremName - let declName := Name.str ((← getEnv).mainModule ++ theoremName) "_hypothesisExt" - unless (← getEnv).contains declName do - let derive := mkAppN (mkConst ``deriveHypothesisOp) - #[toExpr theoremName, toExpr sourceIdx, toExpr inputs, toExpr params] - let value := mkAppN (mkConst ``HypothesisExt.mk) - #[toExpr declName, toExpr theoremName, derive, toExpr priority] - let decl ← mkDefinitionValInferringUnsafe declName [] (mkConst ``HypothesisExt) value .opaque - addAndCompile (markMeta := true) (.defnDecl decl) - addHypothesisExt familyName declName #[path] kind + let (path, sourceIdx, hypArgs, params) ← MetaM.run' <| getHypothesisOpInfo theoremName + let extName ← withDeclNameForAuxNaming theoremName do mkAuxDeclName `_hypothesisExt + let derive := mkAppN (mkConst ``deriveHypothesisOp) + #[toExpr theoremName, toExpr sourceIdx, toExpr hypArgs, toExpr params] + let value := mkAppN (mkConst ``HypothesisExt.mk) + #[toExpr extName, toExpr theoremName, derive, toExpr priority] + let decl ← mkDefinitionValInferringUnsafe extName [] (mkConst ``HypothesisExt) value .opaque + addAndCompile (.defnDecl decl) (markMeta := true) + addHypothesisExt familyName extName #[path] kind + +/-- Syntax for registering a hypothesis extension from a theorem using the `hypothesisOp` +attribute. -/ +syntax (name := hypothesisOpAttr) "hypothesisOp " ident (prio)? : attr -/-- The `hypothesisOp` attribute generates a hypothesis extension from an inclusion theorem. -/ initialize registerBuiltinAttribute { name := `hypothesisOpAttr descr := "adds an inclusion-hypothesis operation" applicationTime := .afterCompilation - add := fun declName stx kind => do - if (IR.getSorryDep (← getEnv) declName).isSome then return - match stx with - | `(attr| hypothesisOp $familyName:ident $[$_prio:prio]?) => + add := fun declName stx kind => match stx with + | `(attr| hypothesisOp $familyName:ident $[$_prio:prio]?) => do + if (IR.getSorryDep (← getEnv) declName).isSome then return addHypothesisOp declName familyName.getId (← getAttrParamOptPrio stx[2]) kind | _ => throwUnsupportedSyntax erase := fun _ => throwError "Hypothesis operations cannot be erased by declaration" diff --git a/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean b/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean index b4c03bfe3d6358..57fa680b77b2c8 100644 --- a/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean +++ b/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean @@ -23,7 +23,7 @@ namespace Inclusion /-- Return the value of parameter `name`, if it was supplied or has a default. -/ def InclusionM.getParam? (name : Name) : InclusionM (Option Expr) := do let some decl := (inclusionParamExt.getState (← getEnv)).find? name - | throwError "Unknown inclusion parameter '{name}'" + | throwError "Unknown inclusion parameter `{name}`" if let some value := (← read).paramSettings.find? name then return some value return decl.defaultValue? @@ -31,13 +31,13 @@ def InclusionM.getParam? (name : Name) : InclusionM (Option Expr) := do /-- Return the value of parameter `name`. -/ def InclusionM.getParam (name : Name) : InclusionM Expr := do let some value ← InclusionM.getParam? name - | throwError "No value was supplied for inclusion parameter '{name}'" + | throwError "No value was supplied for inclusion parameter `{name}`" return value /-- Return the value of parameter `name`, if it was supplied or has a default. -/ def HypothesisM.getParam? (name : Name) : HypothesisM (Option Expr) := do let some decl := (inclusionParamExt.getState (← getEnv)).find? name - | throwError "Unknown inclusion parameter '{name}'" + | throwError "Unknown inclusion parameter `{name}`" if let some value := (← read).paramSettings.find? name then return some value return decl.defaultValue? @@ -45,7 +45,7 @@ def HypothesisM.getParam? (name : Name) : HypothesisM (Option Expr) := do /-- Return the value of parameter `name`, or report that it was not supplied. -/ def HypothesisM.getParam (name : Name) : HypothesisM Expr := do let some value ← HypothesisM.getParam? name - | throwError "No value was supplied for inclusion parameter '{name}'" + | throwError "No value was supplied for inclusion parameter `{name}`" return value /-- Check that `iExpr` is well formed in `localContext`. -/ diff --git a/Mathlib/Tactic/Linter/DeprecatedSyntaxLinter.lean b/Mathlib/Tactic/Linter/DeprecatedSyntaxLinter.lean index aad82013bae39a..9516e16e4bf4f1 100644 --- a/Mathlib/Tactic/Linter/DeprecatedSyntaxLinter.lean +++ b/Mathlib/Tactic/Linter/DeprecatedSyntaxLinter.lean @@ -82,8 +82,8 @@ public register_option linter.style.admit : Bool := { descr := "enable the admit linter" } -/-- The option `linter.style.nativeDecide` of the deprecated syntax linter flags usages of -the `native_decide` tactic, which is disallowed in mathlib. -/ +/-- The option `linter.style.nativeDecide` of the deprecated syntax linter flags proof tactics +that trust native evaluation, which are disallowed in mathlib. -/ -- Note: this linter is purely for user information. Running `lean4checker` in CI catches *any* -- additional axioms that are introduced (not just `ofReduceBool`): the point of this check is to -- alert the user quickly, not to be airtight. @@ -124,26 +124,26 @@ def getSetOptionMaxHeartbeatsComment : Syntax → Option (Name × Nat × Substri some default | _ => none -/-- Whether a given piece of syntax represents a `decide` tactic call with the `native` option -enabled. This may have false negatives for `decide (config := {})` syntax. -/ -def isDecideNative (stx : Syntax ) : Bool := - match stx with - | .node _ ``Lean.Parser.Tactic.decide args => - -- The configuration passed to the tactic call. - let config := args[1]![0] - -- Check all configuration arguments in order to determine the final - -- toggling of the native decide option. - if let (.node _ _ config_args) := config then - let natives := config_args.filterMap (match ·[0] with - | `(Parser.Tactic.posConfigItem| +native) => some true - | `(Parser.Tactic.negConfigItem| -native) => some false - | `(Parser.Tactic.valConfigItem| (config := {native := true})) => some true - | `(Parser.Tactic.valConfigItem| (config := {native := false})) => some false - | _ => none) - natives.back? == some true - else - false - | _ => false +/-- Whether the tactic syntax `stx` enables the `native` option. This may have false negatives for +`tac (config := {})` syntax. -/ +def usesNativeConfig (stx : Syntax) : Bool := + let config := stx[1]![0] + if let .node _ _ configArgs := config then + let natives := configArgs.filterMap (match ·[0] with + | `(Parser.Tactic.posConfigItem| +native) => some true + | `(Parser.Tactic.negConfigItem| -native) => some false + | `(Parser.Tactic.valConfigItem| (native := true)) => some true + | `(Parser.Tactic.valConfigItem| (native := false)) => some false + | `(Parser.Tactic.valConfigItem| (config := {native := true})) => some true + | `(Parser.Tactic.valConfigItem| (config := {native := false})) => some false + | _ => none) + natives.back? == some true + else + false + +/-- Whether `stx` is a `decide` tactic call with the `native` option enabled. -/ +def isDecideNative (stx : Syntax) : Bool := + stx.isOfKind ``Lean.Parser.Tactic.decide && usesNativeConfig stx /-- `getDeprecatedSyntax t` returns all usages of deprecated syntax in the input syntax `t`. -/ partial @@ -178,6 +178,20 @@ def getDeprecatedSyntax : Syntax → Array (SyntaxNodeKind × Syntax × MessageD rargs.push (kind, stx, "Using `native_decide` is not allowed in mathlib: \ because it trusts the entire Lean compiler (not just the Lean kernel), \ it could quite possibly be used to prove false.") + | `Inclusion.inclusionTacStx => + if usesNativeConfig stx then + rargs.push (kind, stx, "Using `inclusion +native` is not allowed in mathlib: \ + because it trusts the entire Lean compiler (not just the Lean kernel), \ + it could quite possibly be used to prove false.") + else + rargs + | `Inclusion.dyadicInterval => + if usesNativeConfig stx then + rargs.push (kind, stx, "Using `dyadic_interval +native` is not allowed in mathlib: \ + because it trusts the entire Lean compiler (not just the Lean kernel), \ + it could quite possibly be used to prove false.") + else + rargs | ``Lean.Parser.Command.in => match getSetOptionMaxHeartbeatsComment stx with | none => rargs @@ -202,6 +216,10 @@ replacement syntax. For each individual case, linting can be turned on or off se * `cases'`, superseded by `obtain`, `rcases` and `cases` (controlled by `linter.style.cases`) * `induction'`, superseded by `induction` (controlled by `linter.style.induction`) * `admit`, superseded by `sorry` (controlled by `linter.style.admit`) +* `native_decide` and `decide +native`, which trust the Lean compiler + (controlled by `linter.style.nativeDecide`) +* `inclusion +native` and `dyadic_interval +native`, which trust the Lean compiler + (controlled by `linter.style.nativeDecide`) * `set_option maxHeartbeats`, should contain an explanatory comment (controlled by `linter.style.maxHeartbeats`) -/ @@ -228,7 +246,8 @@ def deprecatedSyntaxLinter : Linter where run stx := do | `Mathlib.Tactic.cases' => Linter.logLintIf linter.style.cases stx' msg | `Mathlib.Tactic.induction' => Linter.logLintIf linter.style.induction stx' msg | ``Lean.Parser.Tactic.tacticAdmit => Linter.logLintIf linter.style.admit stx' msg - | ``Lean.Parser.Tactic.nativeDecide | ``Lean.Parser.Tactic.decide => + | ``Lean.Parser.Tactic.nativeDecide | ``Lean.Parser.Tactic.decide | + `Inclusion.inclusionTacStx | `Inclusion.dyadicInterval => Linter.logLintIf linter.style.nativeDecide stx' msg | `MaxHeartbeats => Linter.logLintIf linter.style.maxHeartbeats stx' msg | _ => continue) stx diff --git a/MathlibTest/Tactic/Inclusion/Basic.lean b/MathlibTest/Tactic/Inclusion/Basic.lean index 332e064130782e..db19a602cccde1 100644 --- a/MathlibTest/Tactic/Inclusion/Basic.lean +++ b/MathlibTest/Tactic/Inclusion/Basic.lean @@ -3,8 +3,7 @@ Copyright (c) 2026 David Ledvinka. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: David Ledvinka -/ -import Mathlib.Tactic.Inclusion.Extension.Core.Core -import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Extensions +import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Tactic open Inclusion @@ -15,7 +14,7 @@ def unitInterval : Interval Dyadic := ⟨1, 2⟩ section Constants example : (0 : ℝ) ≤ 0 := by - inclusion [core, interval_dyadic_real] + dyadic_interval example : (1 : ℝ) ≤ 1 := by inclusion [core, interval_dyadic_real] @@ -29,6 +28,25 @@ example : ((3 : ℕ) : ℝ) ≤ 3 := by example : ((-3 : ℤ) : ℝ) = -3 := by inclusion [core, interval_dyadic_real] +example : (((1 : ℚ) / 3 : ℚ) : ℝ) < (((334 : ℚ) / 1000 : ℚ) : ℝ) := by + dyadic_interval [prec := 12] + +example : (((1 : ℚ) / 2 : ℚ) : ℝ) = 0.5 := by + inclusion [core, interval_dyadic_real] (prec := 1) + +example : (((-1 : ℚ) / 3 : ℚ) : ℝ) < -0.3 := by + inclusion [core, interval_dyadic_real] (prec := 12) + +example : (0.1 : ℝ) < 0.2 := by + fail_if_success inclusion [core, interval_dyadic_real] (prec := 2) + inclusion [core, interval_dyadic_real] (prec := 4) + +example : (123e4 : ℝ) = 1230000 := by + inclusion [core, interval_dyadic_real] (prec := 0) + +example : (0.12345678901234567890123456789 : ℝ) < 0.1234567890123456789012345679 := by + inclusion [core, interval_dyadic_real] (prec := 100) + end Constants section Arithmetic @@ -172,16 +190,19 @@ end Hypotheses section Evaluation example : (1 : ℝ) ≤ 2 := by - inclusion +kernel [core, interval_dyadic_real] + dyadic_interval +kernel example : ¬(2 : ℝ) ≤ 1 := by inclusion +kernel [core, interval_dyadic_real] +example : (0.12345678901234567890123456789 : ℝ) < 0.1234567890123456789012345679 := by + dyadic_interval +kernel [prec := 100] + /-- info: The inclusion check succeeded. -/ #guard_msgs in set_option linter.unusedTactic false in example : (1 : ℝ) ≤ 2 := by - inclusion? [core, interval_dyadic_real] + dyadic_interval? inclusion [core, interval_dyadic_real] /-- info: The inclusion check failed: @@ -189,7 +210,7 @@ The proposition is provably false -/ #guard_msgs in set_option linter.unusedTactic false in example (h : False) : (2 : ℝ) ≤ 1 := by - inclusion? [core, interval_dyadic_real] + dyadic_interval? exact h.elim end Evaluation diff --git a/MathlibTest/Tactic/Inclusion/Linter.lean b/MathlibTest/Tactic/Inclusion/Linter.lean new file mode 100644 index 00000000000000..3fa943671f41bb --- /dev/null +++ b/MathlibTest/Tactic/Inclusion/Linter.lean @@ -0,0 +1,48 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Tactic + +open Inclusion + +set_option linter.style.nativeDecide true + +/-- +warning: Using `inclusion +native` is not allowed in mathlib: because it trusts the entire Lean +compiler (not just the Lean kernel), it could quite possibly be used to prove false. + +Note: This linter can be disabled with `set_option linter.style.nativeDecide false` +-/ +#guard_msgs in +example : (0 : ℝ) ≤ 1 := by + inclusion +native [core, interval_dyadic_real] + +/-- +warning: Using `inclusion +native` is not allowed in mathlib: because it trusts the entire Lean +compiler (not just the Lean kernel), it could quite possibly be used to prove false. + +Note: This linter can be disabled with `set_option linter.style.nativeDecide false` +-/ +#guard_msgs in +example : (0 : ℝ) ≤ 1 := by + inclusion (native := true) [core, interval_dyadic_real] + +/-- +warning: Using `dyadic_interval +native` is not allowed in mathlib: because it trusts the entire Lean +compiler (not just the Lean kernel), it could quite possibly be used to prove false. + +Note: This linter can be disabled with `set_option linter.style.nativeDecide false` +-/ +#guard_msgs in +example : (0 : ℝ) ≤ 1 := by + dyadic_interval +native + +#guard_msgs in +example : (0 : ℝ) ≤ 1 := by + dyadic_interval +native -native + +#guard_msgs in +example : (0 : ℝ) ≤ 1 := by + inclusion (native := false) [core, interval_dyadic_real] diff --git a/MathlibTest/Tactic/Inclusion/Splitting.lean b/MathlibTest/Tactic/Inclusion/Splitting.lean index 89784dac837fa7..76eea1b9ad1c87 100644 --- a/MathlibTest/Tactic/Inclusion/Splitting.lean +++ b/MathlibTest/Tactic/Inclusion/Splitting.lean @@ -3,8 +3,7 @@ Copyright (c) 2026 David Ledvinka. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: David Ledvinka -/ -import Mathlib.Tactic.Inclusion.Extension.Core.Core -import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Extensions +import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Tactic open Inclusion @@ -19,7 +18,7 @@ example {x : ℝ} (_hx : x ∈ wideInterval) : True := by trivial example {x : ℝ} (hx : x ∈ wideInterval) : x - x ≤ 2 := by - inclusion [core, interval_dyadic_real] (binSplit := 1) + dyadic_interval [binSplit := 1] example {x : ℝ} (hx : x ∈ wideInterval) : x - x ≤ 1 := by inclusion [core, interval_dyadic_real] (binSplit := 2) @@ -34,7 +33,7 @@ example {x : ℝ} (hx : x ≥ 1) : x ≥ 1 := by #guard_msgs in set_option linter.unusedTactic false in example {x : ℝ} (hx : x ∈ wideInterval) : x - x ≤ 2 := by - inclusion? [core, interval_dyadic_real] (binSplit := 1) + dyadic_interval? [binSplit := 1] inclusion [core, interval_dyadic_real] (binSplit := 1) end Inclusion.Tests From 72e17ffb4c8914666737844d41056d2c6b1f1062 Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Sun, 16 Aug 2026 21:02:19 -0400 Subject: [PATCH 05/38] more progress --- Mathlib/Data/Dyadic.lean | 14 ---- Mathlib/Init.lean | 2 +- Mathlib/Tactic.lean | 2 +- Mathlib/Tactic/Inclusion/Core/Elab.lean | 80 +++++++++---------- .../Tactic/Inclusion/Extension/Core/Core.lean | 2 +- .../Extension/Core/{Family.lean => Init.lean} | 0 .../IntervalDyadicReal/Rational.lean | 62 +++++++------- .../Extension/IntervalDyadicReal/Tactic.lean | 18 +++-- .../Tactic/Linter/DeprecatedSyntaxLinter.lean | 20 ++--- MathlibTest/Linter/DeprecatedSyntax.lean | 14 ++-- MathlibTest/Tactic/Inclusion/Basic.lean | 14 ++-- MathlibTest/Tactic/Inclusion/Linter.lean | 8 +- MathlibTest/Tactic/Inclusion/Splitting.lean | 8 +- 13 files changed, 109 insertions(+), 135 deletions(-) rename Mathlib/Tactic/Inclusion/Extension/Core/{Family.lean => Init.lean} (100%) diff --git a/Mathlib/Data/Dyadic.lean b/Mathlib/Data/Dyadic.lean index 5551ceee57ca2e..99ed04c8af206a 100644 --- a/Mathlib/Data/Dyadic.lean +++ b/Mathlib/Data/Dyadic.lean @@ -37,20 +37,6 @@ instance : AddCommGroup Dyadic where namespace Dyadic -section Division - -/-- Round a quotient of natural numbers downward to a dyadic number with precision `prec`. -/ -def divNatDown (prec m d : ℕ) : Dyadic := - Dyadic.ofIntWithPrec (((m <<< prec) / d : ℕ) : ℤ) prec - -/-- Round a quotient of natural numbers upward to a dyadic number with precision `prec`. -/ -def divNatUp (prec m d : ℕ) : Dyadic := - let scaled := m <<< prec - let quotient := scaled / d - Dyadic.ofIntWithPrec (if quotient * d = scaled then quotient else quotient + 1) prec - -end Division - section toReal /-- Interpret a dyadic rational as a real number. -/ diff --git a/Mathlib/Init.lean b/Mathlib/Init.lean index e49829ccc5766f..75b3b4ca64151d 100644 --- a/Mathlib/Init.lean +++ b/Mathlib/Init.lean @@ -98,7 +98,7 @@ register_linter_set linter.mathlibStandardSet := linter.style.longLine linter.style.longFile linter.style.multiGoal - linter.style.nativeDecide + linter.style.native linter.style.openClassical linter.style.maxHeartbeats linter.style.missingEnd diff --git a/Mathlib/Tactic.lean b/Mathlib/Tactic.lean index 9d3db1ba38a591..8bb9d896c06cc0 100644 --- a/Mathlib/Tactic.lean +++ b/Mathlib/Tactic.lean @@ -162,7 +162,7 @@ public import Mathlib.Tactic.Inclusion.Core.Inclusion public import Mathlib.Tactic.Inclusion.Core.ToSet public import Mathlib.Tactic.Inclusion.Core.Types public import Mathlib.Tactic.Inclusion.Extension.Core.Core -public import Mathlib.Tactic.Inclusion.Extension.Core.Family +public import Mathlib.Tactic.Inclusion.Extension.Core.Init public import Mathlib.Tactic.Inclusion.Extension.Interval public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Basic public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Extensions diff --git a/Mathlib/Tactic/Inclusion/Core/Elab.lean b/Mathlib/Tactic/Inclusion/Core/Elab.lean index c0252fb3b024b0..ce0980ac377ca6 100644 --- a/Mathlib/Tactic/Inclusion/Core/Elab.lean +++ b/Mathlib/Tactic/Inclusion/Core/Elab.lean @@ -25,73 +25,65 @@ namespace Inclusion declare_config_elab elabInclusionConfig InclusionConfig where omit paramSettings, families -/-- Collect the array of enabled inclusion families and pass them into `config`. -/ -def collectInclusionFamilies (config : InclusionConfig) (familyStxs : Array Syntax) : - TacticM InclusionConfig := do - if familyStxs.isEmpty then - throwError "At least one inclusion family must be specified" - let mut families := #[] - for familyStx in familyStxs do - let family := familyStx.getId.eraseMacroScopes - unless families.contains family do - unless (← getInclusionFamily? family).isSome do - throwError "Unknown inclusion family `{family}`" - families := families.push family - return { config with families } - -/-- Declare syntax category for specifying inclusion parameters. -/ -declare_syntax_cat inclusionParam - /-- Syntax for specifying an inclusion parameter. -/ -syntax ident " := " term : inclusionParam +syntax inclusionParam := ident " := " term -/-- Collect the set of enabled inclusion parameters and their user set values and pass them into +/-- Syntax for specifying an inclusion family. -/ +syntax inclusionFamily := ident + +/-- Parser for an inclusion family or parameter. -/ +def inclusionArg := (inclusionParam.unary `atomic).binary `orelse inclusionFamily + +/-- Collect the enabled inclusion families and user-set parameter values and pass them into `config`. -/ -def collectInclusionParams (config : InclusionConfig) (paramStxs : Array Syntax) : +def collectInclusionArgs (config : InclusionConfig) (argStxs : Array Syntax) : TacticM InclusionConfig := do let mut config := config let params := inclusionParamExt.getState (← getEnv) - for paramStx in paramStxs do - let (name, valueStx) ← match paramStx with - | `(inclusionParam| $name:ident := $value:term) => pure (name.getId, value) - | _ => throwUnsupportedSyntax - let some decl := params.find? name - | throwError "Unknown inclusion parameter `{name}`" - if config.paramSettings.contains name then - throwError "Inclusion parameter `{name}` was specified more than once" - let value ← elabTerm valueStx decl.type - Term.synthesizeSyntheticMVarsNoPostponing - let value ← instantiateMVars value - config := { config with paramSettings := config.paramSettings.insert name value } + for argStx in argStxs do + match argStx with + | `(inclusionFamily| $familyStx:ident) => + let family := familyStx.getId.eraseMacroScopes + unless config.families.contains family do + unless (← getInclusionFamily? family).isSome do + throwError "Unknown inclusion family `{family}`" + config := { config with families := config.families.push family } + | `(inclusionParam| $nameStx:ident := $valueStx:term) => + let name := nameStx.getId + let some decl := params.find? name + | throwError "Unknown inclusion parameter `{name}`" + if config.paramSettings.contains name then + throwError "Inclusion parameter `{name}` was specified more than once" + let value ← elabTerm valueStx decl.type + Term.synthesizeSyntheticMVarsNoPostponing + let value ← instantiateMVars value + config := { config with paramSettings := config.paramSettings.insert name value } + | _ => throwUnsupportedSyntax + if config.families.isEmpty then + throwError "At least one inclusion family must be specified" return config /-- `inclusion` tactic for proving "inclusion" propositions. -/ -syntax (name := inclusionTacStx) "inclusion" optConfig " [" ident,* "]" - (" (" inclusionParam,* ")")? : tactic +syntax (name := inclusionTacStx) "inclusion" optConfig " [" inclusionArg,* "]" : tactic /-- Elaborator for the `inclusion` tactic. -/ @[tactic inclusionTacStx] def inclusionTac : Tactic - | `(tactic| inclusion $cfg:optConfig [$families,*] $[($paramStxs,*)]?) => do + | `(tactic| inclusion $cfg:optConfig [$args,*]) => do let config ← elabInclusionConfig cfg - let config ← collectInclusionFamilies config families.getElems - let params := paramStxs.map (·.getElems) |>.getD #[] - let config ← collectInclusionParams config params + let config ← collectInclusionArgs config args.getElems closeMainGoalUsing `inclusion fun goal _ => inclusionCore goal config | _ => throwUnsupportedSyntax /-- Tactic for quickly checking if the `inclusion` tactic will succeed. -/ -syntax (name := inclusion?TacStx) "inclusion?" " [" ident,* "]" - (" (" inclusionParam,* ")")? : tactic +syntax (name := inclusion?TacStx) "inclusion?" " [" inclusionArg,* "]" : tactic /-- Elaborator for the `inclusion?` tactic. -/ @[tactic inclusion?TacStx] def inclusion?Tac : Tactic - | `(tactic| inclusion? [$families,*] $[($paramStxs,*)]?) => do + | `(tactic| inclusion? [$args,*]) => do let config : InclusionConfig := {} - let config ← collectInclusionFamilies config families.getElems - let params := paramStxs.map (·.getElems) |>.getD #[] - let config ← collectInclusionParams config params + let config ← collectInclusionArgs config args.getElems withoutModifyingStateWithInfoAndMessages <| withMainContext do try discard <| inclusionCore (← getMainTarget) config diff --git a/Mathlib/Tactic/Inclusion/Extension/Core/Core.lean b/Mathlib/Tactic/Inclusion/Extension/Core/Core.lean index b1a650f1e58b39..9760a847bd0f53 100644 --- a/Mathlib/Tactic/Inclusion/Extension/Core/Core.lean +++ b/Mathlib/Tactic/Inclusion/Extension/Core/Core.lean @@ -6,7 +6,7 @@ Authors: David Ledvinka module public import Mathlib.Tactic.Inclusion.Core.ToSet -public meta import Mathlib.Tactic.Inclusion.Extension.Core.Family +public meta import Mathlib.Tactic.Inclusion.Extension.Core.Init public meta import Mathlib.Tactic.Inclusion.ExtensionAPI.Attr /-! diff --git a/Mathlib/Tactic/Inclusion/Extension/Core/Family.lean b/Mathlib/Tactic/Inclusion/Extension/Core/Init.lean similarity index 100% rename from Mathlib/Tactic/Inclusion/Extension/Core/Family.lean rename to Mathlib/Tactic/Inclusion/Extension/Core/Init.lean diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Rational.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Rational.lean index 642b32ddbb7f4a..5c8972faa25d22 100644 --- a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Rational.lean +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Rational.lean @@ -41,28 +41,28 @@ namespace Inclusion namespace IntervalDyadicReal -/-- Enclose a quotient of natural numbers in a dyadic interval with precision `prec`. -/ -def divNatBounds (prec m d : ℕ) : Interval Dyadic := +/-- Efficiently enclose `m / d` in a dyadic interval with precision `prec`, for positive `d`. -/ +def divNatInterval (m d prec : ℕ) : Interval Dyadic := let scaled := m <<< prec let quotient := scaled / d let upper := if quotient * d = scaled then quotient else quotient + 1 Interval.Icc (Dyadic.ofIntWithPrec quotient prec) (Dyadic.ofIntWithPrec upper prec) /-- Enclose a rational number in a dyadic interval with precision `prec`. -/ -def ratBounds (prec : ℕ) (q : ℚ) : Interval Dyadic := - let lower := q.toDyadic prec - let upper := if lower.toRat = q then lower else lower + Dyadic.ofIntWithPrec 1 prec - ⟨lower, upper⟩ +def ratInterval (x : ℚ) (prec : ℕ) : Interval Dyadic := + let lower := x.toDyadic prec + let upper := if lower.toRat = x then lower else lower + Dyadic.ofIntWithPrec 1 prec + Interval.Icc lower upper /-- Enclose a scientific literal in a dyadic interval with precision `prec`. -/ -def scientific (prec m : ℕ) (s : Bool) (e : ℕ) : Interval Dyadic := +def scientificInterval (m : ℕ) (s : Bool) (e prec : ℕ) : Interval Dyadic := if s then - divNatBounds prec m (10 ^ e) + divNatInterval m (10 ^ e) prec else Interval.singleton Dyadic ((m * 10 ^ e : ℕ) : Dyadic) @[inclusionOp interval_dyadic_real] -theorem ratCast_mem (prec : ℕ) (q : ℚ) : (q : ℝ) ∈ ratBounds prec q := by +theorem ratCast_mem (q : ℚ) (prec : ℕ) : (q : ℝ) ∈ ratInterval q prec := by rw [mem_iff_mem_map] constructor · exact WithBot.coe_le_coe.mpr <| Rat.cast_le.mpr Rat.toRat_toDyadic_le @@ -71,23 +71,22 @@ theorem ratCast_mem (prec : ℕ) (q : ℚ) : (q : ℝ) ∈ ratBounds prec q := b · rw [Dyadic.toReal, h] · exact (Rat.cast_lt (K := ℝ)).mpr Rat.lt_toRat_toDyadic_add |>.le -theorem divNatDown_le (prec m : ℕ) {d : ℕ} (hd : 0 < d) : - Dyadic.toReal (Dyadic.divNatDown prec m d) ≤ (m : ℝ) / d := by - rw [Dyadic.toReal, Dyadic.divNatDown, Dyadic.toRat_ofIntWithPrec_eq_mul_two_pow] - norm_num - rw [Int.shiftLeft_eq, ← div_eq_mul_inv] +private theorem divNatInterval_lower_le (m : ℕ) {d : ℕ} (prec : ℕ) (hd : 0 < d) : + Dyadic.toReal (Dyadic.ofIntWithPrec ((m <<< prec) / d) prec) ≤ (m : ℝ) / d := by + norm_num [Dyadic.toReal, Dyadic.toRat_ofIntWithPrec_eq_mul_two_pow, Int.shiftLeft_eq] + rw [← div_eq_mul_inv] apply (div_le_div_iff₀ (by exact_mod_cast Nat.pow_pos (by decide : 0 < 2)) (by exact_mod_cast hd)).2 - norm_cast - exact Nat.div_mul_le_self (m * 2 ^ prec) d + exact_mod_cast Nat.div_mul_le_self (m * 2 ^ prec) d -theorem le_divNatUp (prec m : ℕ) {d : ℕ} (hd : 0 < d) : - (m : ℝ) / d ≤ Dyadic.toReal (Dyadic.divNatUp prec m d) := by +private theorem le_divNatInterval_upper (m : ℕ) {d : ℕ} (prec : ℕ) (hd : 0 < d) : + (m : ℝ) / d ≤ Dyadic.toReal (Dyadic.ofIntWithPrec + (if (m <<< prec) / d * d = m <<< prec then (m <<< prec) / d else (m <<< prec) / d + 1) + prec) := by rw [Dyadic.toReal, ← Rat.cast_natCast (α := ℝ) m, ← Rat.cast_natCast (α := ℝ) d, ← Rat.cast_div, Rat.cast_le] - rw [Dyadic.divNatUp, Dyadic.toRat_ofIntWithPrec_eq_mul_two_pow] - norm_num + norm_num [Dyadic.toRat_ofIntWithPrec_eq_mul_two_pow] split_ifs with h all_goals rw [← div_eq_mul_inv] @@ -95,30 +94,27 @@ theorem le_divNatUp (prec m : ℕ) {d : ℕ} (hd : 0 < d) : simp only [Nat.shiftLeft_eq, Int.shiftLeft_eq] at h ⊢ norm_cast · exact h.ge - · have hlt := Nat.lt_mul_div_self_add (x := m * 2 ^ prec) hd - rw [Nat.mul_comm d] at hlt - rw [Nat.add_mul, Nat.one_mul] - exact hlt.le + · simpa [Nat.add_mul] using (Nat.lt_div_mul_add (a := m * 2 ^ prec) hd).le -theorem divNat_mem_bounds (prec m : ℕ) {d : ℕ} (hd : 0 < d) : - (m : ℝ) / d ∈ (divNatBounds prec m d).map Dyadic.toReal := by +private theorem divNat_mem_interval (m : ℕ) {d : ℕ} (prec : ℕ) (hd : 0 < d) : + (m : ℝ) / d ∈ (divNatInterval m d prec).map Dyadic.toReal := by constructor · apply WithBot.coe_le_coe.mpr - simpa [divNatBounds, Dyadic.divNatDown] using divNatDown_le prec m hd + simpa [divNatInterval] using divNatInterval_lower_le m prec hd · apply WithTop.coe_le_coe.mpr - simpa [divNatBounds, Dyadic.divNatUp] using le_divNatUp prec m hd + simpa [divNatInterval] using le_divNatInterval_upper m prec hd @[inclusionOp interval_dyadic_real] -theorem scientific_mem (prec m : ℕ) (s : Bool) (e : ℕ) : - (OfScientific.ofScientific (α := ℝ) m s e) ∈ scientific prec m s e := by +theorem scientific_mem (m : ℕ) (s : Bool) (e prec : ℕ) : + (OfScientific.ofScientific (α := ℝ) m s e) ∈ scientificInterval m s e prec := by cases s · rw [mem_iff_mem_map, NNRatCast.ofScientific_eq_ite] simp only [Bool.false_eq_true, if_false, NNRat.cast_natCast] - simpa [scientific] using + simpa [scientificInterval] using Interval.mem_map_singleton ((m * 10 ^ e : ℕ) : Dyadic) Dyadic.toReal · rw [mem_iff_mem_map] - simpa [scientific, NNRatCast.ofScientific_eq_ite] using - divNat_mem_bounds prec m (Nat.pow_pos (by decide : 0 < 10)) + simpa [scientificInterval, NNRatCast.ofScientific_eq_ite] using + divNat_mem_interval m prec (Nat.pow_pos (by decide : 0 < 10)) end IntervalDyadicReal diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean index eb1e592f35b440..3c8fdcda1c8d0a 100644 --- a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean @@ -22,19 +22,23 @@ open Lean.Parser.Tactic namespace Inclusion /-- `dyadic_interval` runs `inclusion` with the `core` and `interval_dyadic_real` extension -families. Inclusion parameters may be supplied using `dyadic_interval [name := value, ...]`. -/ +families. Additional families and inclusion parameters may be supplied in brackets. -/ syntax (name := dyadicInterval) "dyadic_interval" optConfig - (" [" inclusionParam,* "]")? : tactic + (" [" inclusionArg,* "]")? : tactic macro_rules - | `(tactic| dyadic_interval $cfg:optConfig $[[$params:inclusionParam,*]]?) => - `(tactic| inclusion $cfg [core, interval_dyadic_real] $[($params,*)]?) + | `(tactic| dyadic_interval $cfg:optConfig) => + `(tactic| inclusion $cfg [core, interval_dyadic_real]) + | `(tactic| dyadic_interval $cfg:optConfig [$args,*]) => + `(tactic| inclusion $cfg [core, interval_dyadic_real, $args,*]) /-- `dyadic_interval?` checks whether `dyadic_interval` can prove the goal without closing it. -/ -syntax (name := dyadicInterval?) "dyadic_interval?" (" [" inclusionParam,* "]")? : tactic +syntax (name := dyadicInterval?) "dyadic_interval?" (" [" inclusionArg,* "]")? : tactic macro_rules - | `(tactic| dyadic_interval? $[[$params:inclusionParam,*]]?) => - `(tactic| inclusion? [core, interval_dyadic_real] $[($params,*)]?) + | `(tactic| dyadic_interval?) => + `(tactic| inclusion? [core, interval_dyadic_real]) + | `(tactic| dyadic_interval? [$args,*]) => + `(tactic| inclusion? [core, interval_dyadic_real, $args,*]) end Inclusion diff --git a/Mathlib/Tactic/Linter/DeprecatedSyntaxLinter.lean b/Mathlib/Tactic/Linter/DeprecatedSyntaxLinter.lean index 9516e16e4bf4f1..f4156c0b121b1a 100644 --- a/Mathlib/Tactic/Linter/DeprecatedSyntaxLinter.lean +++ b/Mathlib/Tactic/Linter/DeprecatedSyntaxLinter.lean @@ -82,14 +82,14 @@ public register_option linter.style.admit : Bool := { descr := "enable the admit linter" } -/-- The option `linter.style.nativeDecide` of the deprecated syntax linter flags proof tactics +/-- The option `linter.style.native` of the deprecated syntax linter flags proof tactics that trust native evaluation, which are disallowed in mathlib. -/ -- Note: this linter is purely for user information. Running `lean4checker` in CI catches *any* -- additional axioms that are introduced (not just `ofReduceBool`): the point of this check is to -- alert the user quickly, not to be airtight. -public register_option linter.style.nativeDecide : Bool := { +public register_option linter.style.native : Bool := { defValue := false - descr := "enable the nativeDecide linter" + descr := "enable the native-evaluation linter" } /-- The option `linter.style.maxHeartbeats` of the deprecated syntax linter flags usages of @@ -141,10 +141,6 @@ def usesNativeConfig (stx : Syntax) : Bool := else false -/-- Whether `stx` is a `decide` tactic call with the `native` option enabled. -/ -def isDecideNative (stx : Syntax) : Bool := - stx.isOfKind ``Lean.Parser.Tactic.decide && usesNativeConfig stx - /-- `getDeprecatedSyntax t` returns all usages of deprecated syntax in the input syntax `t`. -/ partial def getDeprecatedSyntax : Syntax → Array (SyntaxNodeKind × Syntax × MessageData) @@ -168,7 +164,7 @@ def getDeprecatedSyntax : Syntax → Array (SyntaxNodeKind × Syntax × MessageD "The `admit` tactic is discouraged: \ please strongly consider using the synonymous `sorry` instead.") | ``Lean.Parser.Tactic.decide => - if isDecideNative stx then + if usesNativeConfig stx then rargs.push (kind, stx, "Using `decide +native` is not allowed in mathlib: \ because it trusts the entire Lean compiler (not just the Lean kernel), \ it could quite possibly be used to prove false.") @@ -217,9 +213,9 @@ replacement syntax. For each individual case, linting can be turned on or off se * `induction'`, superseded by `induction` (controlled by `linter.style.induction`) * `admit`, superseded by `sorry` (controlled by `linter.style.admit`) * `native_decide` and `decide +native`, which trust the Lean compiler - (controlled by `linter.style.nativeDecide`) + (controlled by `linter.style.native`) * `inclusion +native` and `dyadic_interval +native`, which trust the Lean compiler - (controlled by `linter.style.nativeDecide`) + (controlled by `linter.style.native`) * `set_option maxHeartbeats`, should contain an explanatory comment (controlled by `linter.style.maxHeartbeats`) -/ @@ -229,7 +225,7 @@ def deprecatedSyntaxLinter : Linter where run stx := do getLinterValue linter.style.induction (← getLinterOptions) || getLinterValue linter.style.admit (← getLinterOptions) || getLinterValue linter.style.maxHeartbeats (← getLinterOptions) || - getLinterValue linter.style.nativeDecide (← getLinterOptions) do + getLinterValue linter.style.native (← getLinterOptions) do return if (← MonadState.get).messages.hasErrors then return @@ -248,7 +244,7 @@ def deprecatedSyntaxLinter : Linter where run stx := do | ``Lean.Parser.Tactic.tacticAdmit => Linter.logLintIf linter.style.admit stx' msg | ``Lean.Parser.Tactic.nativeDecide | ``Lean.Parser.Tactic.decide | `Inclusion.inclusionTacStx | `Inclusion.dyadicInterval => - Linter.logLintIf linter.style.nativeDecide stx' msg + Linter.logLintIf linter.style.native stx' msg | `MaxHeartbeats => Linter.logLintIf linter.style.maxHeartbeats stx' msg | _ => continue) stx diff --git a/MathlibTest/Linter/DeprecatedSyntax.lean b/MathlibTest/Linter/DeprecatedSyntax.lean index af7adf08df2aa9..8d02632d730acb 100644 --- a/MathlibTest/Linter/DeprecatedSyntax.lean +++ b/MathlibTest/Linter/DeprecatedSyntax.lean @@ -111,13 +111,13 @@ warning: declaration uses `sorry` #guard_msgs in example : False := by admit -set_option linter.style.nativeDecide true +set_option linter.style.native true /-- warning: Using `native_decide` is not allowed in mathlib: because it trusts the entire Lean compiler (not just the Lean kernel), it could quite possibly be used to prove false. -Note: This linter can be disabled with `set_option linter.style.nativeDecide false` +Note: This linter can be disabled with `set_option linter.style.native false` -/ #guard_msgs in example : 1 + 1 = 2 := by native_decide @@ -127,7 +127,7 @@ warning: Using `decide +native` is not allowed in mathlib: because it trusts the entire Lean compiler (not just the Lean kernel), it could quite possibly be used to prove false. -Note: This linter can be disabled with `set_option linter.style.nativeDecide false` +Note: This linter can be disabled with `set_option linter.style.native false` -/ #guard_msgs in example : 1 + 1 = 2 := by decide +native @@ -139,7 +139,7 @@ warning: Using `decide +native` is not allowed in mathlib: because it trusts the entire Lean compiler (not just the Lean kernel), it could quite possibly be used to prove false. -Note: This linter can be disabled with `set_option linter.style.nativeDecide false` +Note: This linter can be disabled with `set_option linter.style.native false` -/ #guard_msgs in theorem foo : 1 + 1 = 2 := by decide -native +native @@ -151,7 +151,7 @@ warning: Using `decide +native` is not allowed in mathlib: because it trusts the entire Lean compiler (not just the Lean kernel), it could quite possibly be used to prove false. -Note: This linter can be disabled with `set_option linter.style.nativeDecide false` +Note: This linter can be disabled with `set_option linter.style.native false` -/ #guard_msgs in example : 1 + 1 = 2 := by decide +native -native +native +native @@ -163,7 +163,7 @@ warning: Using `decide +native` is not allowed in mathlib: because it trusts the entire Lean compiler (not just the Lean kernel), it could quite possibly be used to prove false. -Note: This linter can be disabled with `set_option linter.style.nativeDecide false` +Note: This linter can be disabled with `set_option linter.style.native false` -/ #guard_msgs in example : 1 + 1 = 2 := by decide (config := { native := true }) @@ -172,7 +172,7 @@ example : 1 + 1 = 2 := by decide (config := { native := false }) -- for user information (and not hard guarantees), we deem this acceptable. example : 1 + 1 = 2 := by decide (config := { native := true, kernel := false }) -set_option linter.style.nativeDecide false +set_option linter.style.native false set_option linter.style.maxHeartbeats true /-- diff --git a/MathlibTest/Tactic/Inclusion/Basic.lean b/MathlibTest/Tactic/Inclusion/Basic.lean index db19a602cccde1..d710a1f8ab9670 100644 --- a/MathlibTest/Tactic/Inclusion/Basic.lean +++ b/MathlibTest/Tactic/Inclusion/Basic.lean @@ -29,23 +29,23 @@ example : ((-3 : ℤ) : ℝ) = -3 := by inclusion [core, interval_dyadic_real] example : (((1 : ℚ) / 3 : ℚ) : ℝ) < (((334 : ℚ) / 1000 : ℚ) : ℝ) := by - dyadic_interval [prec := 12] + dyadic_interval [core, prec := 12] example : (((1 : ℚ) / 2 : ℚ) : ℝ) = 0.5 := by - inclusion [core, interval_dyadic_real] (prec := 1) + inclusion [core, interval_dyadic_real, prec := 1] example : (((-1 : ℚ) / 3 : ℚ) : ℝ) < -0.3 := by - inclusion [core, interval_dyadic_real] (prec := 12) + inclusion [core, interval_dyadic_real, prec := 12] example : (0.1 : ℝ) < 0.2 := by - fail_if_success inclusion [core, interval_dyadic_real] (prec := 2) - inclusion [core, interval_dyadic_real] (prec := 4) + fail_if_success inclusion [core, interval_dyadic_real, prec := 2] + inclusion [core, interval_dyadic_real, prec := 4] example : (123e4 : ℝ) = 1230000 := by - inclusion [core, interval_dyadic_real] (prec := 0) + inclusion [core, interval_dyadic_real, prec := 0] example : (0.12345678901234567890123456789 : ℝ) < 0.1234567890123456789012345679 := by - inclusion [core, interval_dyadic_real] (prec := 100) + inclusion [core, interval_dyadic_real, prec := 100] end Constants diff --git a/MathlibTest/Tactic/Inclusion/Linter.lean b/MathlibTest/Tactic/Inclusion/Linter.lean index 3fa943671f41bb..67baa2d4c9c586 100644 --- a/MathlibTest/Tactic/Inclusion/Linter.lean +++ b/MathlibTest/Tactic/Inclusion/Linter.lean @@ -7,13 +7,13 @@ import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Tactic open Inclusion -set_option linter.style.nativeDecide true +set_option linter.style.native true /-- warning: Using `inclusion +native` is not allowed in mathlib: because it trusts the entire Lean compiler (not just the Lean kernel), it could quite possibly be used to prove false. -Note: This linter can be disabled with `set_option linter.style.nativeDecide false` +Note: This linter can be disabled with `set_option linter.style.native false` -/ #guard_msgs in example : (0 : ℝ) ≤ 1 := by @@ -23,7 +23,7 @@ example : (0 : ℝ) ≤ 1 := by warning: Using `inclusion +native` is not allowed in mathlib: because it trusts the entire Lean compiler (not just the Lean kernel), it could quite possibly be used to prove false. -Note: This linter can be disabled with `set_option linter.style.nativeDecide false` +Note: This linter can be disabled with `set_option linter.style.native false` -/ #guard_msgs in example : (0 : ℝ) ≤ 1 := by @@ -33,7 +33,7 @@ example : (0 : ℝ) ≤ 1 := by warning: Using `dyadic_interval +native` is not allowed in mathlib: because it trusts the entire Lean compiler (not just the Lean kernel), it could quite possibly be used to prove false. -Note: This linter can be disabled with `set_option linter.style.nativeDecide false` +Note: This linter can be disabled with `set_option linter.style.native false` -/ #guard_msgs in example : (0 : ℝ) ≤ 1 := by diff --git a/MathlibTest/Tactic/Inclusion/Splitting.lean b/MathlibTest/Tactic/Inclusion/Splitting.lean index 76eea1b9ad1c87..9b1a9948b998c5 100644 --- a/MathlibTest/Tactic/Inclusion/Splitting.lean +++ b/MathlibTest/Tactic/Inclusion/Splitting.lean @@ -21,19 +21,19 @@ example {x : ℝ} (hx : x ∈ wideInterval) : x - x ≤ 2 := by dyadic_interval [binSplit := 1] example {x : ℝ} (hx : x ∈ wideInterval) : x - x ≤ 1 := by - inclusion [core, interval_dyadic_real] (binSplit := 2) + inclusion [core, interval_dyadic_real, binSplit := 2] example {x : ℝ} (hx : x ≤ 2) : x ≤ 2 := by - inclusion [core, interval_dyadic_real] (binSplit := 100) + inclusion [core, interval_dyadic_real, binSplit := 100] example {x : ℝ} (hx : x ≥ 1) : x ≥ 1 := by - inclusion [core, interval_dyadic_real] (binSplit := 100) + inclusion [core, interval_dyadic_real, binSplit := 100] /-- info: The inclusion check succeeded. -/ #guard_msgs in set_option linter.unusedTactic false in example {x : ℝ} (hx : x ∈ wideInterval) : x - x ≤ 2 := by dyadic_interval? [binSplit := 1] - inclusion [core, interval_dyadic_real] (binSplit := 1) + inclusion [core, interval_dyadic_real, binSplit := 1] end Inclusion.Tests From c2ed4e0810da3788ff8048e79a57d8f8746c1159 Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Sun, 16 Aug 2026 21:13:34 -0400 Subject: [PATCH 06/38] mk_all --- Mathlib.lean | 4 +++- Mathlib/Tactic.lean | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Mathlib.lean b/Mathlib.lean index 3a28bed35fdd02..6a6e97bec8746f 100644 --- a/Mathlib.lean +++ b/Mathlib.lean @@ -7373,13 +7373,15 @@ public import Mathlib.Tactic.Inclusion.Core.Inclusion public import Mathlib.Tactic.Inclusion.Core.ToSet public import Mathlib.Tactic.Inclusion.Core.Types public import Mathlib.Tactic.Inclusion.Extension.Core.Core -public import Mathlib.Tactic.Inclusion.Extension.Core.Family +public import Mathlib.Tactic.Inclusion.Extension.Core.Init public import Mathlib.Tactic.Inclusion.Extension.Interval public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Basic public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Extensions public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Hypotheses public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Init +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Rational public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Splitting +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Tactic public import Mathlib.Tactic.Inclusion.ExtensionAPI.Attr public import Mathlib.Tactic.Inclusion.ExtensionAPI.Basic public import Mathlib.Tactic.InferParam diff --git a/Mathlib/Tactic.lean b/Mathlib/Tactic.lean index 8bb9d896c06cc0..83fce3c5c90632 100644 --- a/Mathlib/Tactic.lean +++ b/Mathlib/Tactic.lean @@ -168,7 +168,9 @@ public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Basic public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Extensions public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Hypotheses public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Init +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Rational public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Splitting +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Tactic public import Mathlib.Tactic.Inclusion.ExtensionAPI.Attr public import Mathlib.Tactic.Inclusion.ExtensionAPI.Basic public import Mathlib.Tactic.InferParam From 5cd47facf3ff041877b50649d80a6127c6dbc2e2 Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Wed, 19 Aug 2026 21:28:57 -0400 Subject: [PATCH 07/38] minor updates --- Mathlib/Tactic/Inclusion/Core/Elab.lean | 14 ++++---------- Mathlib/Tactic/Inclusion/Core/Extensions.lean | 19 ++++--------------- Mathlib/Tactic/Inclusion/Core/Inclusion.lean | 2 +- Mathlib/Tactic/Inclusion/Core/ToSet.lean | 7 ------- Mathlib/Tactic/Inclusion/Core/Types.lean | 4 +--- .../Tactic/Inclusion/ExtensionAPI/Attr.lean | 2 +- 6 files changed, 11 insertions(+), 37 deletions(-) diff --git a/Mathlib/Tactic/Inclusion/Core/Elab.lean b/Mathlib/Tactic/Inclusion/Core/Elab.lean index ce0980ac377ca6..5903582c8522cf 100644 --- a/Mathlib/Tactic/Inclusion/Core/Elab.lean +++ b/Mathlib/Tactic/Inclusion/Core/Elab.lean @@ -25,14 +25,8 @@ namespace Inclusion declare_config_elab elabInclusionConfig InclusionConfig where omit paramSettings, families -/-- Syntax for specifying an inclusion parameter. -/ -syntax inclusionParam := ident " := " term - -/-- Syntax for specifying an inclusion family. -/ -syntax inclusionFamily := ident - -/-- Parser for an inclusion family or parameter. -/ -def inclusionArg := (inclusionParam.unary `atomic).binary `orelse inclusionFamily +/-- Syntax for specifying an inclusion family or parameter. -/ +syntax inclusionArg := ident (" := " term)? /-- Collect the enabled inclusion families and user-set parameter values and pass them into `config`. -/ @@ -42,13 +36,13 @@ def collectInclusionArgs (config : InclusionConfig) (argStxs : Array Syntax) : let params := inclusionParamExt.getState (← getEnv) for argStx in argStxs do match argStx with - | `(inclusionFamily| $familyStx:ident) => + | `(inclusionArg| $familyStx:ident) => let family := familyStx.getId.eraseMacroScopes unless config.families.contains family do unless (← getInclusionFamily? family).isSome do throwError "Unknown inclusion family `{family}`" config := { config with families := config.families.push family } - | `(inclusionParam| $nameStx:ident := $valueStx:term) => + | `(inclusionArg| $nameStx:ident := $valueStx:term) => let name := nameStx.getId let some decl := params.find? name | throwError "Unknown inclusion parameter `{name}`" diff --git a/Mathlib/Tactic/Inclusion/Core/Extensions.lean b/Mathlib/Tactic/Inclusion/Core/Extensions.lean index 7736db44caafba..c231fbc9972107 100644 --- a/Mathlib/Tactic/Inclusion/Core/Extensions.lean +++ b/Mathlib/Tactic/Inclusion/Core/Extensions.lean @@ -43,10 +43,8 @@ structure HypothesisExt where /-- The priority of the extension. Extensions with higher priority are tried first. -/ priority : Nat := eval_prio default -/-- A named family of inclusion and hypothesis extensions. -/ +/-- A family of inclusion and hypothesis extensions. -/ structure InclusionFamily where - /-- The name of an inclusion family. -/ - name : Name /-- The `DiscrTree`-indexed collection of inclusion extensions. -/ inclusionExt : EnvExt InclusionExt /-- The `DiscrTree`-indexed collection of hypothesis extensions. -/ @@ -66,7 +64,7 @@ def registerInclusionFamily (name : Name) (ref : Name := by exact decl_name%) : throw <| IO.userError s!"Inclusion family `{name}` is already registered" let inclusionExt ← initializeEnvExt ``InclusionExt (ref.str "inclusionExt") let hypothesisExt ← initializeEnvExt ``HypothesisExt (ref.str "hypothesisExt") - let family := { name, inclusionExt, hypothesisExt } + let family := { inclusionExt, hypothesisExt } inclusionFamiliesRef.modify (·.insert name family) return family @@ -121,15 +119,7 @@ structure InclusionParamDecl where defaultValue? : Option Expr := none /-- The collection of registered inclusion parameters, indexed by name. -/ -structure InclusionParams where - /-- Map from names to their inclusion parameter declaration. -/ - decls : NameMap InclusionParamDecl := {} - deriving Inhabited - -/-- If `name` is the name of an `InclusionParamDecl` `param` then return `some param`, -otherwise return `none`. -/ -def InclusionParams.find? (params : InclusionParams) (name : Name) : Option InclusionParamDecl := - params.decls.find? name +abbrev InclusionParams := NameMap InclusionParamDecl /-- Evaluate the declaration `n` as an `InclusionParamDecl`. -/ def mkInclusionParamDecl (name : Name) : ImportM InclusionParamDecl := do @@ -143,8 +133,7 @@ initialize inclusionParamExt : mkInitial := pure {} ofOLeanEntry := fun _ name => return (name, ← mkInclusionParamDecl name) toOLeanEntry := (·.1) - addEntry := fun state (_, decl) => - { state with decls := state.decls.insert decl.name decl } + addEntry := fun state (_, decl) => state.insert decl.name decl } end InclusionParam diff --git a/Mathlib/Tactic/Inclusion/Core/Inclusion.lean b/Mathlib/Tactic/Inclusion/Core/Inclusion.lean index af3cb67a1921ea..a1cd1574cd18f5 100644 --- a/Mathlib/Tactic/Inclusion/Core/Inclusion.lean +++ b/Mathlib/Tactic/Inclusion/Core/Inclusion.lean @@ -129,7 +129,7 @@ def mkExprInclusion (output : IExpr) (body : ExprInclusionBody) : HypothesisM Ex let inclusionBody ← iVar.mkCoverMap output.iType hypBody.inclusionBody cover coarsen inclusion let proofBody ← iVar.mkCoverMapProof output hypBody cover coarsen inclusion proof return { inclusionBody, proofBody } - return ⟨output.iType, body.inclusionBody, body.proofBody⟩ + return ⟨body.inclusionBody, body.proofBody⟩ /-- Construct an `ExprInclusion` for `e`. -/ def toExprInclusion (e : Expr) : InclusionM ExprInclusion := do diff --git a/Mathlib/Tactic/Inclusion/Core/ToSet.lean b/Mathlib/Tactic/Inclusion/Core/ToSet.lean index f225cb940305cf..6dd1adfe9b57e7 100644 --- a/Mathlib/Tactic/Inclusion/Core/ToSet.lean +++ b/Mathlib/Tactic/Inclusion/Core/ToSet.lean @@ -71,13 +71,6 @@ class Coarsen (Iα α : Type*) [ToSet Iα α] where /-- If `x ∈ t` then `x ∈ coarsen s t`. -/ mem_coarsen_right {x : α} {s t : Iα} (hx : x ∈ t) : x ∈ coarsen s t -theorem Coarsen.mem_coarsen_of_or {Iα α : Type*} [ToSet Iα α] [Coarsen Iα α] - {p q : Prop} {x : α} {s t : Iα} (h : p ∨ q) - (hp : p → x ∈ s) (hq : q → x ∈ t) : - x ∈ Coarsen.coarsen (α := α) s t := - h.elim (fun hp' ↦ Coarsen.mem_coarsen_left (hp hp')) - (fun hq' ↦ Coarsen.mem_coarsen_right (hq hq')) - universe u /-- A `Cover Iα α` specifies a function `coverMap` to compute a "refined" inclusion of `F s` diff --git a/Mathlib/Tactic/Inclusion/Core/Types.lean b/Mathlib/Tactic/Inclusion/Core/Types.lean index a2be58ef9d11f6..d5c3b7200e0cbb 100644 --- a/Mathlib/Tactic/Inclusion/Core/Types.lean +++ b/Mathlib/Tactic/Inclusion/Core/Types.lean @@ -66,8 +66,6 @@ def IVar.expr (iVar : IVar) : Expr := iVar.iExpr.expr /-- An `ExprInclusion` is a structure associated with an expression `e`, containing a computed inclusion set for `e` and a proof that this inclusion is correct. -/ structure ExprInclusion where - /-- The types of the inclusion result. -/ - iType : IType /-- The expression computing an inclusion set for `e`. -/ inclusion : Expr /-- A proof of `e ∈ inclusion`, where `e` is the represented expression. -/ @@ -105,7 +103,7 @@ structure InclusionM.Context where /-- The mutable state of the `InclusionM` monad. -/ structure InclusionM.State where - /-- The inclusion variables registered for expressions encountered during construction. -/ + /-- A map from expressions to their inclusion variables. -/ iVars : ExprMap IVar := {} /-- The monad used by the `inclusion` tactic during the construction of `ExprInclusion`s. -/ diff --git a/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean b/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean index 045d9b4f039461..65fb54e09afc5f 100644 --- a/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean +++ b/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean @@ -41,7 +41,7 @@ def addInclusionParam (declName : Name) (kind : AttributeKind) : AttrM Unit := d let decl ← mkInclusionParamDecl declName MetaM.run' <| validateInclusionParamDecl decl let params := inclusionParamExt.getState env - if params.decls.contains decl.name then + if params.contains decl.name then throwError "Inclusion parameter `{decl.name}` is already registered" inclusionParamExt.add (declName, decl) kind From b2381e564fb8643ef8caf22a88e346fa2becd303 Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Mon, 24 Aug 2026 01:58:13 -0400 Subject: [PATCH 08/38] almost ready --- Mathlib.lean | 3 +- Mathlib/Data/Dyadic.lean | 43 ++-- Mathlib/Tactic.lean | 3 +- Mathlib/Tactic/Inclusion/Core/Elab.lean | 29 ++- Mathlib/Tactic/Inclusion/Core/Extensions.lean | 24 +- Mathlib/Tactic/Inclusion/Core/Inclusion.lean | 58 +++-- Mathlib/Tactic/Inclusion/Core/ToSet.lean | 37 +-- Mathlib/Tactic/Inclusion/Core/Types.lean | 2 + .../Tactic/Inclusion/Extension/Core/Core.lean | 6 +- .../Tactic/Inclusion/Extension/Interval.lean | 222 ++++++++--------- .../Extension/IntervalDyadicReal/Basic.lean | 31 ++- .../IntervalDyadicReal/BinarySplit.lean | 74 ++++++ .../IntervalDyadicReal/Extensions.lean | 47 ---- .../IntervalDyadicReal/Rational.lean | 113 ++++----- .../IntervalDyadicReal/Splitting.lean | 71 ------ .../Extension/IntervalDyadicReal/Tactic.lean | 6 +- .../Tactic/Inclusion/ExtensionAPI/Attr.lean | 32 +-- .../Tactic/Inclusion/ExtensionAPI/Basic.lean | 27 ++- MathlibTest/Tactic/Inclusion/Basic.lean | 227 ++++++++---------- .../{Linter.lean => NativeLinter.lean} | 20 +- MathlibTest/Tactic/Inclusion/Splitting.lean | 39 --- 21 files changed, 526 insertions(+), 588 deletions(-) create mode 100644 Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/BinarySplit.lean delete mode 100644 Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Extensions.lean delete mode 100644 Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Splitting.lean rename MathlibTest/Tactic/Inclusion/{Linter.lean => NativeLinter.lean} (67%) delete mode 100644 MathlibTest/Tactic/Inclusion/Splitting.lean diff --git a/Mathlib.lean b/Mathlib.lean index 6a6e97bec8746f..1dedf28a3a5132 100644 --- a/Mathlib.lean +++ b/Mathlib.lean @@ -7376,11 +7376,10 @@ public import Mathlib.Tactic.Inclusion.Extension.Core.Core public import Mathlib.Tactic.Inclusion.Extension.Core.Init public import Mathlib.Tactic.Inclusion.Extension.Interval public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Basic -public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Extensions +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.BinarySplit public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Hypotheses public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Init public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Rational -public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Splitting public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Tactic public import Mathlib.Tactic.Inclusion.ExtensionAPI.Attr public import Mathlib.Tactic.Inclusion.ExtensionAPI.Basic diff --git a/Mathlib/Data/Dyadic.lean b/Mathlib/Data/Dyadic.lean index 99ed04c8af206a..d41fbab7d8fc60 100644 --- a/Mathlib/Data/Dyadic.lean +++ b/Mathlib/Data/Dyadic.lean @@ -37,31 +37,38 @@ instance : AddCommGroup Dyadic where namespace Dyadic -section toReal +/-- One unit on the dyadic grid with precision `prec`. -/ +def step (prec : Int) : Dyadic := .ofOdd 1 prec (by decide) + +theorem ofIntWithPrec_one (prec : Int) : ofIntWithPrec 1 prec = step prec := by + simp [step, ofIntWithPrec, Int.trailingZeros_eq_zero_of_mod_eq (show 1 % 2 = 1 by decide)] + +section Real /-- Interpret a dyadic rational as a real number. -/ def toReal (d : Dyadic) : ℝ := d.toRat @[simp] -lemma toReal_add (a b : Dyadic) : toReal (a + b) = toReal a + toReal b := by simp [toReal] - -@[simp] -lemma toReal_neg (a : Dyadic) : toReal (-a) = -toReal a := by simp [toReal] +lemma toReal_natCast (n : ℕ) : toReal (n : Dyadic) = (n : ℝ) := by simp [toReal] @[simp] -lemma toReal_sub (a b : Dyadic) : toReal (a - b) = toReal a - toReal b := by simp [toReal] +lemma toReal_ofNat (n : ℕ) [n.AtLeastTwo] : + toReal (ofNat(n) : Dyadic) = (ofNat(n) : ℝ) := by + rw [← Nat.cast_ofNat (R := Dyadic), ← Nat.cast_ofNat (R := ℝ)] + exact toReal_natCast n @[simp] -lemma toReal_natCast (n : ℕ) : toReal (n : Dyadic) = (n : ℝ) := by simp [toReal] +lemma toReal_intCast (z : ℤ) : toReal (z : Dyadic) = (z : ℝ) := by simp [toReal] @[simp] -lemma toReal_intCast (z : ℤ) : toReal (z : Dyadic) = (z : ℝ) := by simp [toReal] +lemma toReal_add (a b : Dyadic) : toReal (a + b) = toReal a + toReal b := by simp [toReal] @[simp] -lemma toReal_le_toReal {a b : Dyadic} : toReal a ≤ toReal b ↔ a ≤ b := by simp [toReal] +lemma toReal_mul (a b : Dyadic) : toReal (a * b) = toReal a * toReal b := by simp [toReal] @[simp] -lemma toReal_lt_toReal {a b : Dyadic} : toReal a < toReal b ↔ a < b := by simp [toReal] +lemma toReal_pow (a : Dyadic) (n : ℕ) : toReal (a ^ n) = toReal a ^ n := by + simpa [toReal] using map_pow (Rat.castHom ℝ) a.toRat n /-- `Dyadic.toReal` as an additive monoid homomorphism. -/ def toRealAddMonoidHom : Dyadic →+ ℝ where @@ -69,18 +76,16 @@ def toRealAddMonoidHom : Dyadic →+ ℝ where map_zero' := by simp [toReal] map_add' := toReal_add -/-- `Dyadic.toReal` as an order embedding. -/ -def toRealOrderEmbedding : Dyadic ↪o ℝ := - OrderEmbedding.ofStrictMono toReal fun _ _ h ↦ toReal_lt_toReal.mpr h - @[simp] -lemma toReal_min (a b : Dyadic) : toReal (min a b) = min (toReal a) (toReal b) := - toRealOrderEmbedding.monotone.map_min +lemma toReal_le_toReal {a b : Dyadic} : toReal a ≤ toReal b ↔ a ≤ b := by simp [toReal] @[simp] -lemma toReal_max (a b : Dyadic) : toReal (max a b) = max (toReal a) (toReal b) := - toRealOrderEmbedding.monotone.map_max +lemma toReal_lt_toReal {a b : Dyadic} : toReal a < toReal b ↔ a < b := by simp [toReal] + +/-- `Dyadic.toReal` as an order embedding. -/ +def toRealOrderEmbedding : Dyadic ↪o ℝ := + OrderEmbedding.ofStrictMono toReal fun _ _ h ↦ toReal_lt_toReal.mpr h -end toReal +end Real end Dyadic diff --git a/Mathlib/Tactic.lean b/Mathlib/Tactic.lean index 83fce3c5c90632..ea5ced8d577158 100644 --- a/Mathlib/Tactic.lean +++ b/Mathlib/Tactic.lean @@ -165,11 +165,10 @@ public import Mathlib.Tactic.Inclusion.Extension.Core.Core public import Mathlib.Tactic.Inclusion.Extension.Core.Init public import Mathlib.Tactic.Inclusion.Extension.Interval public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Basic -public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Extensions +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.BinarySplit public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Hypotheses public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Init public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Rational -public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Splitting public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Tactic public import Mathlib.Tactic.Inclusion.ExtensionAPI.Attr public import Mathlib.Tactic.Inclusion.ExtensionAPI.Basic diff --git a/Mathlib/Tactic/Inclusion/Core/Elab.lean b/Mathlib/Tactic/Inclusion/Core/Elab.lean index 5903582c8522cf..c9d96e6bbb4852 100644 --- a/Mathlib/Tactic/Inclusion/Core/Elab.lean +++ b/Mathlib/Tactic/Inclusion/Core/Elab.lean @@ -28,34 +28,33 @@ declare_config_elab elabInclusionConfig InclusionConfig where /-- Syntax for specifying an inclusion family or parameter. -/ syntax inclusionArg := ident (" := " term)? -/-- Collect the enabled inclusion families and user-set parameter values and pass them into -`config`. -/ -def collectInclusionArgs (config : InclusionConfig) (argStxs : Array Syntax) : - TacticM InclusionConfig := do - let mut config := config +/-- Collect the enabled inclusion families and user-set parameter values. -/ +def collectInclusionArgs (argStxs : Array Syntax) : TacticM InclusionConfig := do + let mut paramSettings : NameMap Expr := {} + let mut families := #[] let params := inclusionParamExt.getState (← getEnv) for argStx in argStxs do match argStx with | `(inclusionArg| $familyStx:ident) => let family := familyStx.getId.eraseMacroScopes - unless config.families.contains family do + unless families.contains family do unless (← getInclusionFamily? family).isSome do throwError "Unknown inclusion family `{family}`" - config := { config with families := config.families.push family } + families := families.push family | `(inclusionArg| $nameStx:ident := $valueStx:term) => let name := nameStx.getId let some decl := params.find? name | throwError "Unknown inclusion parameter `{name}`" - if config.paramSettings.contains name then + if paramSettings.contains name then throwError "Inclusion parameter `{name}` was specified more than once" let value ← elabTerm valueStx decl.type Term.synthesizeSyntheticMVarsNoPostponing let value ← instantiateMVars value - config := { config with paramSettings := config.paramSettings.insert name value } + paramSettings := paramSettings.insert name value | _ => throwUnsupportedSyntax - if config.families.isEmpty then + if families.isEmpty then throwError "At least one inclusion family must be specified" - return config + return { paramSettings, families } /-- `inclusion` tactic for proving "inclusion" propositions. -/ syntax (name := inclusionTacStx) "inclusion" optConfig " [" inclusionArg,* "]" : tactic @@ -64,8 +63,9 @@ syntax (name := inclusionTacStx) "inclusion" optConfig " [" inclusionArg,* "]" : @[tactic inclusionTacStx] def inclusionTac : Tactic | `(tactic| inclusion $cfg:optConfig [$args,*]) => do - let config ← elabInclusionConfig cfg - let config ← collectInclusionArgs config args.getElems + let options ← elabInclusionConfig cfg + let config ← collectInclusionArgs args.getElems + let config := { config with kernel := options.kernel, native := options.native } closeMainGoalUsing `inclusion fun goal _ => inclusionCore goal config | _ => throwUnsupportedSyntax @@ -76,8 +76,7 @@ syntax (name := inclusion?TacStx) "inclusion?" " [" inclusionArg,* "]" : tactic @[tactic inclusion?TacStx] def inclusion?Tac : Tactic | `(tactic| inclusion? [$args,*]) => do - let config : InclusionConfig := {} - let config ← collectInclusionArgs config args.getElems + let config ← collectInclusionArgs args.getElems withoutModifyingStateWithInfoAndMessages <| withMainContext do try discard <| inclusionCore (← getMainTarget) config diff --git a/Mathlib/Tactic/Inclusion/Core/Extensions.lean b/Mathlib/Tactic/Inclusion/Core/Extensions.lean index c231fbc9972107..e71319fb4ff1e1 100644 --- a/Mathlib/Tactic/Inclusion/Core/Extensions.lean +++ b/Mathlib/Tactic/Inclusion/Core/Extensions.lean @@ -24,8 +24,10 @@ namespace Inclusion structure InclusionExt where /-- Name of the declaration of the extension. -/ declName : Name := by exact decl_name% - /-- User-facing name of the extension. -/ - userName : Name := by exact decl_name% + /-- The family in which the extension is registered. -/ + family : Name + /-- The user-facing name of the extension. -/ + userName : Name := declName /-- Attempt to construct an `ExprInclusionBody` for `e`. -/ derive (e : Expr) : InclusionM ExprInclusionBody /-- The priority of the extension. Extensions with higher priority are tried first. -/ @@ -36,8 +38,10 @@ declarations. -/ structure HypothesisExt where /-- Name of the declaration of the extension. -/ declName : Name := by exact decl_name% - /-- User-facing name of the extension. -/ - userName : Name := by exact decl_name% + /-- The family in which the extension is registered. -/ + family : Name + /-- The user-facing name of the extension. -/ + userName : Name := declName /-- Attempt to construct inclusion hypotheses from `h`. -/ derive (h : Expr) : HypothesisM Unit /-- The priority of the extension. Extensions with higher priority are tried first. -/ @@ -85,26 +89,26 @@ def getInclusionFamily (name : Name) : CoreM InclusionFamily := do /-- Return an array of the inclusion extensions in `families` whose `DiscrTree` key matches `e`, sorted in order of highest to lowest priority. -/ def getInclusionExtMatches (families : Array Name) (e : Expr) : - MetaM (Array (Name × InclusionExt)) := do + MetaM (Array InclusionExt) := do let env ← getEnv let mut matched := #[] for familyName in families do let family ← getInclusionFamily familyName for ext in ← family.inclusionExt.getState env |>.getMatch e do - matched := matched.push (familyName, ext) - return matched.qsort fun (_, a) (_, b) => a.priority > b.priority + matched := matched.push ext + return matched.qsort fun a b => a.priority > b.priority /-- Return an array of the hypothesis extensions in `families` whose `DiscrTree` key matches `e`, sorted in order of highest to lowest priority. -/ def getHypothesisExtMatches (families : Array Name) (e : Expr) : - MetaM (Array (Name × HypothesisExt)) := do + MetaM (Array HypothesisExt) := do let env ← getEnv let mut matched := #[] for familyName in families do let family ← getInclusionFamily familyName for ext in ← family.hypothesisExt.getState env |>.getMatch e do - matched := matched.push (familyName, ext) - return matched.qsort fun (_, a) (_, b) => a.priority > b.priority + matched := matched.push ext + return matched.qsort fun a b => a.priority > b.priority section InclusionParam diff --git a/Mathlib/Tactic/Inclusion/Core/Inclusion.lean b/Mathlib/Tactic/Inclusion/Core/Inclusion.lean index a1cd1574cd18f5..e485fd7d841123 100644 --- a/Mathlib/Tactic/Inclusion/Core/Inclusion.lean +++ b/Mathlib/Tactic/Inclusion/Core/Inclusion.lean @@ -21,31 +21,55 @@ construct inclusion hypotheses for the body's inclusion variables and closes the -/ -public meta section +namespace Inclusion -open Lean Meta Elab Term +public meta section -namespace Inclusion +open Lean Meta initialize registerTraceClass `Tactic.inclusion +/-- Given the `userName` of an `InclusionExt` and the name of the inclusion family it belongs to, +print `[family] userName`. -/ +private def ppExtensionName (family userName : Name) : MessageData := + m!"[{family}] {.ofConstName userName}" + +/-- Given `exts : Array InclusionExt`, print each extension in a numbered list. -/ +private def ppMatchedExts (exts : Array InclusionExt) : MessageData := + m!"\n".joinSep (exts.toList.mapIdx fun i ext => + m!"{i + 1}. {ppExtensionName ext.family ext.userName}") + +private def ppInclusionExpr (e : Expr) : InclusionM MessageData := do + let iVarDisplays := (← get).iVarDisplays + return m!"{e.replace fun e => iVarDisplays[e]?}" + /-- Construct an `ExprInclusionBody` for `e`. -/ def mkExprInclusionBody (e : Expr) : InclusionM ExprInclusionBody := do + withTraceNode `Tactic.inclusion + (fun _ => return m!"Making `ExprInclusionBody` for:\n {e}") do if let some iVar := (← get).iVars[e]? then trace[Tactic.inclusion] "Reusing inclusion variable for {e}" return iVar.toExprInclusionBody let matchedExts ← getInclusionExtMatches (← read).families e + trace[Tactic.inclusion] + "Matched inclusion extensions (in order of priority):\n{ppMatchedExts matchedExts}" let savedState ← saveState - for (family, ext) in matchedExts do - try - let body ← ext.derive e - recordExtraModUseFromDecl (isMeta := true) ext.declName - trace[Tactic.inclusion] "[{family}] {ext.userName} applied to {e}" + for ext in matchedExts do + let body? : Option ExprInclusionBody ← withTraceNode `Tactic.inclusion + (fun _ => do return m!"Trying {ppExtensionName ext.family ext.userName}") do + try + let body ← ext.derive e + recordExtraModUseFromDecl (isMeta := true) ext.declName + return some body + catch err => + trace[Tactic.inclusion] + "Failed to apply {ppExtensionName ext.family ext.userName} to {e}: \ + {err.toMessageData}" + restoreState savedState + return none + if let some body := body? then + trace[Tactic.inclusion] "Inclusion body:\n {← ppInclusionExpr body.inclusionBody}" return body - catch err => - trace[Tactic.inclusion] - "Failed to apply [{family}] {ext.userName} to {e}: {err.toMessageData}" - restoreState savedState throwError "No inclusion extension applies to {e}" /-- Check that `body.proofBody` is a proof of `e ∈ body.inclusionBody` and infer its `IType`. -/ @@ -61,15 +85,17 @@ def ExprInclusionBody.inferIType (body : ExprInclusionBody) (e : Expr) : MetaM I def runHypothesisExts (h : Expr) : HypothesisM Unit := do let type ← instantiateMVars (← inferType h) let matchedExts ← getHypothesisExtMatches (← read).families type - for (family, ext) in matchedExts do + for ext in matchedExts do let saved ← saveState try ext.derive h recordExtraModUseFromDecl (isMeta := true) ext.declName - trace[Tactic.inclusion] "[{family}] {ext.userName} processed {type}" + trace[Tactic.inclusion] + "{ppExtensionName ext.family ext.userName} processed {type}" catch err => trace[Tactic.inclusion] - "Failed to apply [{family}] {ext.userName} to {type}: {err.toMessageData}" + "Failed to apply {ppExtensionName ext.family ext.userName} to {type}: \ + {err.toMessageData}" restoreState saved /-- Run hypothesis extensions on all declarations in the local context. -/ @@ -137,4 +163,6 @@ def toExprInclusion (e : Expr) : InclusionM ExprInclusion := do let iType ← body.inferIType e HypothesisM.run <| mkExprInclusion ⟨iType, e⟩ body +end + end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Core/ToSet.lean b/Mathlib/Tactic/Inclusion/Core/ToSet.lean index 6dd1adfe9b57e7..8f8deb6542ab09 100644 --- a/Mathlib/Tactic/Inclusion/Core/ToSet.lean +++ b/Mathlib/Tactic/Inclusion/Core/ToSet.lean @@ -33,8 +33,8 @@ instance {Iα α : Type*} [ToSet Iα α] : Membership α Iα where mem s a := ToSet.toSet s a @[simp] -lemma mem_set_iff_mem_toSet {Iα α : Type*} [ToSet Iα α] (a : α) (s : Iα) : - a ∈ s ↔ a ∈ (s : Set α) := Iff.rfl +lemma mem_toSet_iff_mem {Iα α : Type*} [ToSet Iα α] (a : α) (s : Iα) : + a ∈ (s : Set α) ↔ a ∈ s := Iff.rfl lemma ToSet.mem_of_eq_of_mem {Iα α : Type*} [ToSet Iα α] {x y : α} {s : Iα} (hxy : x = y) (hy : y ∈ s) : x ∈ s := hxy ▸ hy @@ -110,14 +110,23 @@ def IntervalBool.toPropSet : IntervalBool → Set Prop instance : ToSet IntervalBool Prop := ⟨IntervalBool.toPropSet⟩ -theorem IntervalBool.mem_true {p : Prop} (hp : p) : p ∈ IntervalBool.true := by - simpa [ToSet.toSet, IntervalBool.toPropSet] using hp +@[simp] +theorem IntervalBool.mem_true_iff {p : Prop} : p ∈ IntervalBool.true ↔ p := by + simp [← mem_toSet_iff_mem, ToSet.toSet, IntervalBool.toPropSet] + +@[simp] +theorem IntervalBool.mem_false_iff {p : Prop} : p ∈ IntervalBool.false ↔ ¬p := by + simp [← mem_toSet_iff_mem, ToSet.toSet, IntervalBool.toPropSet] -theorem IntervalBool.mem_false {p : Prop} (hp : ¬p) : p ∈ IntervalBool.false := by - simpa [ToSet.toSet, IntervalBool.toPropSet] using hp +theorem IntervalBool.mem_true {p : Prop} (hp : p) : p ∈ IntervalBool.true := + IntervalBool.mem_true_iff.mpr hp +theorem IntervalBool.mem_false {p : Prop} (hp : ¬p) : p ∈ IntervalBool.false := + IntervalBool.mem_false_iff.mpr hp + +@[simp] theorem IntervalBool.mem_undetermined (p : Prop) : p ∈ IntervalBool.undetermined := by - by_cases hp : p <;> simp [ToSet.toSet, IntervalBool.toPropSet, hp] + by_cases hp : p <;> simp [← mem_toSet_iff_mem, ToSet.toSet, IntervalBool.toPropSet, hp] /-- Negation of an `IntervalBool` value. -/ def IntervalBool.not : IntervalBool → IntervalBool @@ -127,7 +136,7 @@ def IntervalBool.not : IntervalBool → IntervalBool theorem IntervalBool.not_mem {p : Prop} {a : IntervalBool} (hp : p ∈ a) : (¬p) ∈ a.not := by - cases a <;> by_cases hp' : p <;> simp_all [IntervalBool.not, ToSet.toSet, IntervalBool.toPropSet] + cases a <;> by_cases hp' : p <;> simp_all [IntervalBool.not] /-- Conjunction of two `IntervalBool` values. -/ def IntervalBool.and : IntervalBool → IntervalBool → IntervalBool @@ -137,7 +146,7 @@ def IntervalBool.and : IntervalBool → IntervalBool → IntervalBool theorem IntervalBool.and_mem {p q : Prop} {a b : IntervalBool} (hp : p ∈ a) (hq : q ∈ b) : (p ∧ q) ∈ a.and b := by - cases a <;> cases b <;> simp_all [IntervalBool.and, ToSet.toSet, IntervalBool.toPropSet] + cases a <;> cases b <;> simp_all [IntervalBool.and] /-- Disjunction of two `IntervalBool` values. -/ def IntervalBool.or : IntervalBool → IntervalBool → IntervalBool @@ -148,10 +157,10 @@ def IntervalBool.or : IntervalBool → IntervalBool → IntervalBool theorem IntervalBool.or_mem {p q : Prop} {a b : IntervalBool} (hp : p ∈ a) (hq : q ∈ b) : (p ∨ q) ∈ a.or b := by cases a <;> cases b <;> by_cases hp' : p <;> by_cases hq' : q <;> - simp_all [IntervalBool.or, ToSet.toSet, IntervalBool.toPropSet] + simp_all [IntervalBool.or] -theorem true_of_mem_intervalBool_true {p : Prop} (hp : p ∈ IntervalBool.true) : p := by - simpa [mem_set_iff_mem_toSet, toSet, IntervalBool.toPropSet] using hp +theorem true_of_mem_intervalBool_true {p : Prop} (hp : p ∈ IntervalBool.true) : p := + IntervalBool.mem_true_iff.mp hp theorem true_of_mem_intervalBool_eq_true {p : Prop} {b : IntervalBool} (hp : p ∈ b) (hb : b = IntervalBool.true) : p := @@ -176,10 +185,10 @@ instance : Coarsen IntervalBool Prop where coarsen := IntervalBool.union mem_coarsen_left := by intro p s t hp - cases s <;> cases t <;> simp_all [IntervalBool.union, ToSet.toSet, IntervalBool.toPropSet] + cases s <;> cases t <;> simp_all [IntervalBool.union] mem_coarsen_right := by intro p s t hp - cases s <;> cases t <;> simp_all [IntervalBool.union, ToSet.toSet, IntervalBool.toPropSet] + cases s <;> cases t <;> simp_all [IntervalBool.union] end IntervalBool diff --git a/Mathlib/Tactic/Inclusion/Core/Types.lean b/Mathlib/Tactic/Inclusion/Core/Types.lean index d5c3b7200e0cbb..41a15e848a18bf 100644 --- a/Mathlib/Tactic/Inclusion/Core/Types.lean +++ b/Mathlib/Tactic/Inclusion/Core/Types.lean @@ -105,6 +105,8 @@ structure InclusionM.Context where structure InclusionM.State where /-- A map from expressions to their inclusion variables. -/ iVars : ExprMap IVar := {} + /-- A map from inclusion set variables to the expressions used to display them in traces. -/ + iVarDisplays : ExprMap Expr := {} /-- The monad used by the `inclusion` tactic during the construction of `ExprInclusion`s. -/ abbrev InclusionM := ReaderT InclusionM.Context <| StateT InclusionM.State MetaM diff --git a/Mathlib/Tactic/Inclusion/Extension/Core/Core.lean b/Mathlib/Tactic/Inclusion/Extension/Core/Core.lean index 9760a847bd0f53..36cc5dd8d1d279 100644 --- a/Mathlib/Tactic/Inclusion/Extension/Core/Core.lean +++ b/Mathlib/Tactic/Inclusion/Extension/Core/Core.lean @@ -25,8 +25,9 @@ attribute [inclusionOp core] IntervalBool.not_mem IntervalBool.and_mem IntervalB attribute [hypothesisOp core] ToSet.mem_of_eq_of_mem ToSet.mem_of_mem_of_eq /-- `HypothesisExt` for direct `ToSet` instance membership hypotheses. -/ -@[hypothesisExt core | _ ∈ _] +@[hypothesisExt _ ∈ _] def instMembershipHyp : HypothesisExt where + family := `core derive h := do let type ← instantiateMVars (← inferType h) let some (expr, set, _) := toSetMem? type | failure @@ -35,8 +36,9 @@ def instMembershipHyp : HypothesisExt where addInclusionHyp iVar.iExpr ⟨set, h⟩ /-- `HypothesisExt` for conjunction hypotheses. -/ -@[hypothesisExt core | _ ∧ _] +@[hypothesisExt _ ∧ _] def andHyp : HypothesisExt where + family := `core derive h := do let (``And, #[_, _]) := (← instantiateMVars (← inferType h)).getAppFnArgs | failure runHypothesisExts (← mkAppM ``And.left #[h]) diff --git a/Mathlib/Tactic/Inclusion/Extension/Interval.lean b/Mathlib/Tactic/Inclusion/Extension/Interval.lean index c0ac46736d07a0..5d0b7d8a9e589a 100644 --- a/Mathlib/Tactic/Inclusion/Extension/Interval.lean +++ b/Mathlib/Tactic/Inclusion/Extension/Interval.lean @@ -5,12 +5,11 @@ Authors: David Ledvinka -/ module -public import Mathlib.Algebra.Group.Hom.Defs public import Mathlib.Algebra.Order.Group.Defs +public import Mathlib.Algebra.Order.Monoid.Unbundled.WithTop public import Mathlib.Order.Hom.Basic public import Mathlib.Order.Interval.Set.Defs public import Mathlib.Order.MinMax -public import Mathlib.Order.WithBot public import Mathlib.Tactic.Inclusion.Core.ToSet /-! @@ -39,6 +38,10 @@ def Interval.toSet [Preorder α] (I : Interval α) : Set α := {a | I.lb ≤ a instance [Preorder α] : ToSet (Interval α) α := ⟨Interval.toSet⟩ +@[simp, grind =] +theorem Interval.mem_def [Preorder α] {x : α} {I : Interval α} : + x ∈ I ↔ I.lb ≤ x ∧ x ≤ I.ub := Iff.rfl + /-- Apply a function to the finite endpoints of an interval. -/ def Interval.map (I : Interval α) (f : α → β) : Interval β := ⟨WithBot.map f I.lb, WithTop.map f I.ub⟩ @@ -54,15 +57,26 @@ theorem Interval.mem_map_univ [Preorder β] (f : α → β) (x : β) : x ∈ (Interval.univ α).map f := ⟨bot_le, le_top⟩ /-- The interval whose two endpoints are `a`. -/ -def Interval.singleton (α : Type*) (a : α) : Interval α := ⟨a, a⟩ +def Interval.singleton (a : α) : Interval α := ⟨a, a⟩ theorem Interval.mem_map_singleton [Preorder β] (a : α) (f : α → β) : - f a ∈ (Interval.singleton α a).map f := - ⟨le_rfl, le_rfl⟩ + f a ∈ (Interval.singleton a).map f := ⟨le_rfl, le_rfl⟩ /-- The interval with lower endpoint `lb` and upper endpoint `ub`. -/ def Interval.Icc (lb : WithBot α) (ub : WithTop α) : Interval α := ⟨lb, ub⟩ +theorem Interval.mem_map_Icc [Preorder β] (f : α → β) {lb ub : α} {x : β} + (hl : f lb ≤ x) (hu : x ≤ f ub) : x ∈ (Interval.Icc lb ub).map f := + ⟨WithBot.coe_le_coe.mpr hl, WithTop.coe_le_coe.mpr hu⟩ + +theorem Interval.map_lb_le [Preorder β] (f : α → β) {lb : α} {ub : WithTop α} {x : β} + (hx : x ∈ (Interval.Icc lb ub).map f) : f lb ≤ x := + WithBot.coe_le_coe.mp hx.1 + +theorem Interval.le_map_ub [Preorder β] (f : α → β) {lb : WithBot α} {ub : α} {x : β} + (hx : x ∈ (Interval.Icc lb ub).map f) : x ≤ f ub := + WithTop.coe_le_coe.mp hx.2 + /-- The interval unbounded below with upper endpoint `ub`. -/ def Interval.Iic (ub : WithTop α) : Interval α := ⟨⊥, ub⟩ @@ -71,24 +85,16 @@ def Interval.Ici (lb : WithBot α) : Interval α := ⟨lb, ⊤⟩ theorem Interval.mem_Iic_of_le [Preorder α] {x y : α} {I : Interval α} (hxy : x ≤ y) (hy : y ∈ I) : x ∈ Interval.Iic I.ub := by - dsimp [Interval.Iic] - constructor - · exact bot_le - · grind [hy.2, WithTop.coe_le_coe.mpr hxy] + grind [Iic, bot_le, WithTop.coe_le_coe.mpr hxy] theorem Interval.mem_Ici_of_le [Preorder α] {x y : α} {I : Interval α} (hxy : x ≤ y) (hx : x ∈ I) : y ∈ Interval.Ici I.lb := by - dsimp [Interval.Ici] - constructor - · grind [hx.1, WithBot.coe_le_coe.mpr hxy] - · exact le_top + grind [Ici, le_top, WithBot.coe_le_coe.mpr hxy] theorem Interval.mem_Icc_of_le [Preorder α] {a b x : α} {I J : Interval α} (ha : a ∈ I) (hax : a ≤ x) (hxb : x ≤ b) (hb : b ∈ J) : x ∈ Interval.Icc I.lb J.ub := by - dsimp [Interval.Icc] - constructor <;> grind [ha.1, hb.2, WithBot.coe_le_coe.mpr hax, - WithTop.coe_le_coe.mpr hxb] + grind [Icc, WithBot.coe_le_coe.mpr hax, WithTop.coe_le_coe.mpr hxb] theorem Interval.mem_Iic_of_lt [Preorder α] {x y : α} {I : Interval α} (hxy : x < y) (hy : y ∈ I) : x ∈ Interval.Iic I.ub := @@ -144,8 +150,8 @@ instance [LinearOrder α] : Refine (Interval α) α where theorem Interval.map_inter [LinearOrder α] [LinearOrder β] (f : α ↪o β) (I J : Interval α) : (I.inter J).map f = (I.map f).inter (J.map f) := by - simp [Interval.inter, Interval.map, f.monotone.withBot_map.map_max, - f.monotone.withTop_map.map_min] + simp [f.monotone.withBot_map.map_max, f.monotone.withTop_map.map_min, + Interval.inter, Interval.map] theorem Interval.inter_mem [LinearOrder α] [LinearOrder β] (f : α ↪o β) {x : β} {I J : Interval α} (hxI : x ∈ I.map f) (hxJ : x ∈ J.map f) : @@ -159,11 +165,11 @@ def Interval.hull [LinearOrder α] (I J : Interval α) : Interval α := theorem Interval.mem_hull_left [LinearOrder α] {x : α} {I J : Interval α} (hx : x ∈ I) : x ∈ I.hull J := by - simp_all [Interval.hull, ToSet.toSet, Interval.toSet] + grind [Interval.hull] theorem Interval.mem_hull_right [LinearOrder α] {x : α} {I J : Interval α} (hx : x ∈ J) : x ∈ I.hull J := by - simp_all [Interval.hull, ToSet.toSet, Interval.toSet] + grind [Interval.hull] instance [LinearOrder α] : Coarsen (Interval α) α where coarsen := Interval.hull @@ -172,8 +178,8 @@ instance [LinearOrder α] : Coarsen (Interval α) α where theorem Interval.map_hull [LinearOrder α] [LinearOrder β] (f : α ↪o β) (I J : Interval α) : (I.hull J).map f = (I.map f).hull (J.map f) := by - simp [Interval.hull, Interval.map, f.monotone.withBot_map.map_min, - f.monotone.withTop_map.map_max] + simp [f.monotone.withBot_map.map_min, f.monotone.withTop_map.map_max, + Interval.hull, Interval.map] theorem Interval.hull_mem_left [LinearOrder α] [LinearOrder β] (f : α ↪o β) {x : β} {I J : Interval α} (hx : x ∈ I.map f) : x ∈ (I.hull J).map f := by @@ -194,24 +200,20 @@ def Interval.add [Add α] (I J : Interval α) : Interval α where | some a, some b => some (a + b) | _, _ => ⊤ +@[simp] +theorem Interval.add_lb [AddZero α] (I J : Interval α) : (I.add J).lb = I.lb + J.lb := by + rcases I with ⟨_ | il, iu⟩ <;> rcases J with ⟨_ | jl, ju⟩ <;> rfl + +@[simp] +theorem Interval.add_ub [AddZero α] (I J : Interval α) : (I.add J).ub = I.ub + J.ub := by + rcases I with ⟨il, _ | iu⟩ <;> rcases J with ⟨jl, _ | ju⟩ <;> rfl + theorem Interval.add_mem [AddZero α] [AddCommMonoid β] [Preorder β] [IsOrderedAddMonoid β] (f : α →+ β) {x y : β} {I J : Interval α} (hx : x ∈ I.map f) (hy : y ∈ J.map f) : x + y ∈ (I.add J).map f := by - rcases I with ⟨il, iu⟩ - rcases J with ⟨jl, ju⟩ constructor - · rcases il with _ | il - · simp [Interval.add, Interval.map] - rcases jl with _ | jl - · simp [Interval.add, Interval.map] - apply WithBot.coe_le_coe.mpr - grind [add_le_add, WithBot.coe_le_coe.mp hx.1, WithBot.coe_le_coe.mp hy.1] - · rcases iu with _ | iu - · simp [Interval.add, Interval.map] - rcases ju with _ | ju - · simp [Interval.add, Interval.map] - apply WithTop.coe_le_coe.mpr - grind [add_le_add, WithTop.coe_le_coe.mp hx.2, WithTop.coe_le_coe.mp hy.2] + · simpa [Interval.map] using add_le_add hx.1 hy.1 + · simpa [Interval.map] using add_le_add hx.2 hy.2 /-- Negate an interval. -/ def Interval.neg [Neg α] (I : Interval α) : Interval α where @@ -222,18 +224,17 @@ def Interval.neg [Neg α] (I : Interval α) : Interval α where | some a => some (-a) | ⊥ => ⊤ -theorem Interval.neg_mem [AddGroup α] [AddCommGroup β] [PartialOrder β] [IsOrderedAddMonoid β] +theorem Interval.neg_mem [AddGroup α] [AddCommGroup β] [Preorder β] [IsOrderedAddMonoid β] (f : α →+ β) {x : β} {I : Interval α} (hx : x ∈ I.map f) : -x ∈ I.neg.map f := by - rcases I with ⟨il, iu⟩ constructor - · rcases iu with _ | iu + · rcases I with ⟨il, _ | iu⟩ · simp [Interval.neg, Interval.map] apply WithBot.coe_le_coe.mpr - grind [neg_le_neg, WithTop.coe_le_coe.mp hx.2] - · rcases il with _ | il + simpa using neg_le_neg_iff.mpr (WithTop.coe_le_coe.mp hx.2) + · rcases I with ⟨_ | il, iu⟩ · simp [Interval.neg, Interval.map] apply WithTop.coe_le_coe.mpr - grind [neg_le_neg, WithBot.coe_le_coe.mp hx.1] + simpa using neg_le_neg_iff.mpr (WithBot.coe_le_coe.mp hx.1) /-- Subtract one interval from another. -/ def Interval.sub [Sub α] (I J : Interval α) : Interval α where @@ -244,27 +245,65 @@ def Interval.sub [Sub α] (I J : Interval α) : Interval α where | some a, some b => some (a - b) | _, _ => ⊤ +theorem Interval.sub_eq_add_neg [AddGroup α] (I J : Interval α) : I.sub J = I.add J.neg := by + rcases I with ⟨_ | il, _ | iu⟩ <;> + rcases J with ⟨_ | jl, _ | ju⟩ <;> + simp [Interval.sub, Interval.add, Interval.neg, _root_.sub_eq_add_neg] + theorem Interval.sub_mem [AddGroup α] [AddCommGroup β] [Preorder β] [IsOrderedAddMonoid β] (f : α →+ β) {x y : β} {I J : Interval α} (hx : x ∈ I.map f) (hy : y ∈ J.map f) : x - y ∈ (I.sub J).map f := by - rcases I with ⟨il, iu⟩ - rcases J with ⟨jl, ju⟩ - constructor - · rcases il with _ | il - · simp [Interval.sub, Interval.map] - rcases ju with _ | ju - · simp [Interval.sub, Interval.map] - apply WithBot.coe_le_coe.mpr - grind [sub_le_sub, WithBot.coe_le_coe.mp hx.1, WithTop.coe_le_coe.mp hy.2] - · rcases iu with _ | iu - · simp [Interval.sub, Interval.map] - rcases jl with _ | jl - · simp [Interval.sub, Interval.map] - apply WithTop.coe_le_coe.mpr - grind [sub_le_sub, WithTop.coe_le_coe.mp hx.2, WithBot.coe_le_coe.mp hy.1] + rw [_root_.sub_eq_add_neg, Interval.sub_eq_add_neg] + exact Interval.add_mem f hx (Interval.neg_mem f hy) + +/-- Check if `r x y` is false is implied by `x ∈ I` and `y ∈ J` -/ +def Interval.orderRelFalse (r : α → α → Prop) [DecidableRel r] + (I J : Interval α) : IntervalBool := + match I.lb, J.ub with + | some il, some ju => if r il ju then .undetermined else .false + | _, _ => .undetermined + +theorem Interval.orderRelFalse_mem [Preorder β] {r : α → α → Prop} {s : β → β → Prop} + [DecidableRel r] [Trans (· ≤ ·) s s] [Trans s (· ≤ ·) s] (f : r ↪r s) + {x y : β} {I J : Interval α} (hx : x ∈ I.map f) (hy : y ∈ J.map f) : + s x y ∈ Interval.orderRelFalse r I J := by + rcases I with ⟨_ | il, iu⟩ + · simp [Interval.orderRelFalse] + rcases J with ⟨jl, _ | ju⟩ + · simp [Interval.orderRelFalse] + dsimp [Interval.orderRelFalse] + split_ifs with h + · simp + · exact IntervalBool.mem_false fun hxy ↦ h <| f.map_rel_iff.mp <| + trans (Interval.map_lb_le f hx) (trans hxy (Interval.le_map_ub f hy)) + +/-- Check if `r x y` is implied (true or false) by `x ∈ I` and `y ∈ J`. -/ +def Interval.orderRel (r : α → α → Prop) [DecidableRel r] + (I J : Interval α) : IntervalBool := + match I.ub, J.lb with + | some iu, some jl => + if r iu jl then + .true + else + Interval.orderRelFalse r I J + | _, _ => Interval.orderRelFalse r I J + +theorem Interval.orderRel_mem [Preorder β] {r : α → α → Prop} {s : β → β → Prop} + [DecidableRel r] [Trans (· ≤ ·) s s] [Trans s (· ≤ ·) s] (f : r ↪r s) + {x y : β} {I J : Interval α} (hx : x ∈ I.map f) (hy : y ∈ J.map f) : + s x y ∈ Interval.orderRel r I J := by + have hFalse := Interval.orderRelFalse_mem f hx hy + rcases I with ⟨il, _ | iu⟩ + · exact hFalse + rcases J with ⟨_ | jl, ju⟩ + · exact hFalse + dsimp [Interval.orderRel] + split_ifs with h + · apply IntervalBool.mem_true + exact trans (Interval.le_map_ub f hx) <| trans (f.map_rel_iff.mpr h) (Interval.map_lb_le f hy) + · exact hFalse -/-- Check `x ≤ y` for `x ∈ I` and `y ∈ J`, returning `true` or `false` when the -endpoints decide it and `undetermined` otherwise. -/ +/-- Check if `x ≤ y` is implied (true or false) by `x ∈ I` and `y ∈ J` -/ def Interval.le [LE α] [DecidableLE α] (I J : Interval α) : IntervalBool := match I.ub, J.lb with | some iu, some jl => @@ -280,37 +319,10 @@ def Interval.le [LE α] [DecidableLE α] (I J : Interval α) : IntervalBool := theorem Interval.le_mem [Preorder α] [Preorder β] [DecidableLE α] (f : α ↪o β) {x y : β} {I J : Interval α} (hx : x ∈ I.map f) (hy : y ∈ J.map f) : - (x ≤ y) ∈ I.le J := by - have hfallback : - (x ≤ y) ∈ - (match I.lb, J.ub with - | some il, some ju => - if il ≤ ju then IntervalBool.undetermined else IntervalBool.false - | _, _ => IntervalBool.undetermined) := by - rcases I with ⟨_ | il, iu⟩ - · exact IntervalBool.mem_undetermined _ - rcases J with ⟨jl, _ | ju⟩ - · exact IntervalBool.mem_undetermined _ - dsimp - split_ifs with h - · exact IntervalBool.mem_undetermined _ - · apply IntervalBool.mem_false - intro hxy - apply h - rw [← f.le_iff_le] - exact (WithBot.coe_le_coe.mp hx.1).trans (hxy.trans (WithTop.coe_le_coe.mp hy.2)) - rcases I with ⟨il, _ | iu⟩ - · exact hfallback - rcases J with ⟨_ | jl, ju⟩ - · exact hfallback - dsimp [Interval.le] - split_ifs with h - · apply IntervalBool.mem_true - grind [WithTop.coe_le_coe.mp hx.2, f.monotone h, WithBot.coe_le_coe.mp hy.1] - · exact hfallback + (x ≤ y) ∈ I.le J := + Interval.orderRel_mem f hx hy -/-- Check `x < y` for `x ∈ I` and `y ∈ J`, returning `true` or `false` when the endpoints -decide it and `undetermined` otherwise. -/ +/-- Check if `x < y` is implied (true or false) by `x ∈ I` and `y ∈ J`. -/ def Interval.lt [LT α] [DecidableLT α] (I J : Interval α) : IntervalBool := match I.ub, J.lb with | some iu, some jl => @@ -326,36 +338,10 @@ def Interval.lt [LT α] [DecidableLT α] (I J : Interval α) : IntervalBool := theorem Interval.lt_mem [Preorder α] [Preorder β] [DecidableLT α] (f : α ↪o β) {x y : β} {I J : Interval α} (hx : x ∈ I.map f) (hy : y ∈ J.map f) : - (x < y) ∈ I.lt J := by - have hfallback : - (x < y) ∈ - (match I.lb, J.ub with - | some il, some ju => - if il < ju then IntervalBool.undetermined else IntervalBool.false - | _, _ => IntervalBool.undetermined) := by - rcases I with ⟨_ | il, iu⟩ - · exact IntervalBool.mem_undetermined _ - rcases J with ⟨jl, _ | ju⟩ - · exact IntervalBool.mem_undetermined _ - dsimp - split_ifs with h - · exact IntervalBool.mem_undetermined _ - · apply IntervalBool.mem_false - intro hxy - apply h - rw [← f.lt_iff_lt] - exact (WithBot.coe_le_coe.mp hx.1).trans_lt (hxy.trans_le (WithTop.coe_le_coe.mp hy.2)) - rcases I with ⟨il, _ | iu⟩ - · exact hfallback - rcases J with ⟨_ | jl, ju⟩ - · exact hfallback - dsimp [Interval.lt] - split_ifs with h - · apply IntervalBool.mem_true - grind [WithTop.coe_le_coe.mp hx.2, f.strictMono h, WithBot.coe_le_coe.mp hy.1] - · exact hfallback + (x < y) ∈ I.lt J := + Interval.orderRel_mem f.ltEmbedding hx hy -/-- Return the conjunction of the two interval comparisons needed to verify equality. -/ +/-- Check if `x = y` is implied (true or false) by `x ∈ I` and `y ∈ J`. -/ def Interval.eq [LE α] [DecidableLE α] (I J : Interval α) : IntervalBool := (I.le J).and (J.le I) diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Basic.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Basic.lean index abb8dc8cf2f017..84095bf292a9d2 100644 --- a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Basic.lean +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Basic.lean @@ -5,9 +5,8 @@ Authors: David Ledvinka -/ module -public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Init -public meta import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Init -public meta import Mathlib.Tactic.Inclusion.ExtensionAPI.Attr +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.BinarySplit +public meta import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.BinarySplit /-! # Basic inclusion extensions for interval_dyadic_real @@ -17,29 +16,35 @@ This file defines basic operations for the `interval_dyadic_real` inclusion fami @[expose] public section -namespace Inclusion +open Lean Qq +namespace Inclusion namespace IntervalDyadicReal -theorem mem_iff_mem_map {x : ℝ} {I : Interval Dyadic} : - x ∈ I ↔ x ∈ I.map Dyadic.toReal := Iff.rfl +/-- Construct an inclusion variable for a real expression using a dyadic interval. -/ +@[inclusionExt(_ : ℝ)] +meta def mkRealIVar : InclusionExt := + mkNDIVarExt `interval_dyadic_real + ⟨q(ℝ), q(Interval Dyadic), q(instToSetIntervalDyadicReal)⟩ mkBinarySplitCover + +@[grind =] +theorem mem_iff_mem_map {x : ℝ} {I : Interval Dyadic} : x ∈ I ↔ x ∈ I.map Dyadic.toReal := + Iff.rfl section Constants @[inclusionOp interval_dyadic_real] -theorem natCast_mem (n : ℕ) : (n : ℝ) ∈ Interval.singleton Dyadic n := by - rw [mem_iff_mem_map] - simpa using Interval.mem_map_singleton (n : Dyadic) Dyadic.toReal +theorem natCast_mem (n : ℕ) : (n : ℝ) ∈ Interval.singleton (n : Dyadic) := by + simpa [mem_iff_mem_map] using Interval.mem_map_singleton (n : Dyadic) Dyadic.toReal @[inclusionOp interval_dyadic_real] -theorem ofNat_mem (n : ℕ) : (OfNat.ofNat n : ℝ) ∈ Interval.singleton Dyadic n := by +theorem ofNat_mem (n : ℕ) : (OfNat.ofNat n : ℝ) ∈ Interval.singleton (n : Dyadic) := by rw [Semiring.toGrindSemiring_ofNat] exact natCast_mem n @[inclusionOp interval_dyadic_real] -theorem intCast_mem (z : ℤ) : (z : ℝ) ∈ Interval.singleton Dyadic z := by - rw [mem_iff_mem_map] - simpa using Interval.mem_map_singleton (z : Dyadic) Dyadic.toReal +theorem intCast_mem (z : ℤ) : (z : ℝ) ∈ Interval.singleton (z : Dyadic) := by + simpa [mem_iff_mem_map] using Interval.mem_map_singleton (z : Dyadic) Dyadic.toReal end Constants diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/BinarySplit.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/BinarySplit.lean new file mode 100644 index 00000000000000..ef9224279f4e5d --- /dev/null +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/BinarySplit.lean @@ -0,0 +1,74 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +module + +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Init +public meta import Mathlib.Tactic.Inclusion.ExtensionAPI.Attr +public meta import Qq + +/-! +# Binary splitting of dyadic real intervals + +This file defines the `binarySplit` cover for the `interval_dyadic_real` inclusion family. +-/ + +@[expose] public section + +open Lean Qq + +namespace Inclusion +namespace IntervalDyadicReal + +/-- The midpoint of `a` and `b`. -/ +def midpoint (a b : Dyadic) : Dyadic := + match a + b with + | .zero => .zero + | .ofOdd n k hn => .ofOdd n (k + 1) hn + +/-- Map `F` over the intervals produced by bisecting `I` to depth `n`, coarsening the results. -/ +@[specialize] +def binarySplitMap {Iβ β : Type*} [ToSet Iβ β] [Coarsen Iβ β] : + ℕ → Interval Dyadic → (Interval Dyadic → Iβ) → Iβ + | 0, I, F => F I + | n + 1, I, F => + match I with + | ⟨some l, some u⟩ => + let m := midpoint l u + Coarsen.coarsen β (binarySplitMap n ⟨l, m⟩ F) (binarySplitMap n ⟨m, u⟩ F) + | _ => F I + +theorem mem_binarySplitMap {Iβ β : Type*} [ToSet Iβ β] [Coarsen Iβ β] + (n : ℕ) {I : Interval Dyadic} {F : Interval Dyadic → Iβ} {y : β} {r : ℝ} + (hr : r ∈ I) (hy : ∀ J, r ∈ J → y ∈ F J) : y ∈ binarySplitMap n I F := by + induction n generalizing I with + | zero => exact hy I hr + | succ n ih => match I with + | ⟨some l, some u⟩ => + let m := midpoint l u + by_cases hl : r ≤ Dyadic.toReal m + · exact Coarsen.mem_coarsen_left (ih ⟨hr.1, WithTop.coe_le_coe.mpr hl⟩) + · exact Coarsen.mem_coarsen_right (ih ⟨WithBot.coe_le_coe.mpr (le_of_not_ge hl), hr.2⟩) + | ⟨⊥, ⊤⟩ | ⟨⊥, some u⟩ | ⟨some l, ⊤⟩ => exact hy _ hr + +/-- Cover a dyadic interval by repeatedly bisecting it to depth `n`. -/ +def binarySplit (n : ℕ) : Cover (Interval Dyadic) ℝ where + coverMap := binarySplitMap n + mem_coverMap := mem_binarySplitMap n + +/-- The depth to which bounded dyadic intervals are repeatedly bisected. A depth of +`n` produces `2 ^ n` pieces. -/ +@[inclusionParam] +meta def binarySplitParam : InclusionParamDecl where + name := `binSplit + type := q(ℕ) + +/-- Construct the binary-splitting cover with `2 ^ n` pieces. -/ +meta def mkBinarySplitCover : InclusionM (Option Expr) := do + let some depth ← InclusionM.getParam? `binSplit | return none + return some (mkApp (mkConst ``binarySplit [.zero]) depth) + +end IntervalDyadicReal +end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Extensions.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Extensions.lean deleted file mode 100644 index e5216d36183bc5..00000000000000 --- a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Extensions.lean +++ /dev/null @@ -1,47 +0,0 @@ -/- -Copyright (c) 2026 David Ledvinka. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: David Ledvinka --/ -module - -public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Basic -public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Rational -public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Splitting -public meta import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Basic -public meta import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Rational -public meta import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Splitting -public meta import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Hypotheses -public meta import Mathlib.Tactic.Inclusion.ExtensionAPI.Basic -public meta import Qq - -/-! -# Inclusion extensions for dyadic real intervals --/ - -public meta section - -open Lean Meta Qq - -namespace Inclusion -namespace IntervalDyadicReal - -/-- The depth to which bounded dyadic intervals are repeatedly bisected. A depth of `n` produces -`2 ^ n` pieces; unbounded intervals are left unchanged. -/ -@[inclusionParam] -def binSplitParam : InclusionParamDecl where - name := `binSplit - type := q(ℕ) - -/-- Construct the binary-splitting cover with `2 ^ n` pieces. -/ -def mkBinSplitCover : InclusionM (Option Expr) := do - let some depth ← InclusionM.getParam? `binSplit | return none - return some (mkApp (mkConst ``BinarySplit.cover [.zero]) depth) - -/-- Construct an inclusion variable for a real expression using a dyadic interval. -/ -@[inclusionExt interval_dyadic_real | (_ : ℝ)] -def mkRealIVar : InclusionExt := - mkNDIVarExt ⟨q(ℝ), q(Interval Dyadic), q(instToSetIntervalDyadicReal)⟩ mkBinSplitCover - -end IntervalDyadicReal -end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Rational.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Rational.lean index 5c8972faa25d22..3866721dfcd8cd 100644 --- a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Rational.lean +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Rational.lean @@ -7,12 +7,13 @@ module public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Basic public meta import Mathlib.Tactic.Inclusion.ExtensionAPI.Attr +import Mathlib.Data.Rat.Cast.Lemmas /-! -# Rational constants for interval_dyadic_real +# Rational enclosures for interval_dyadic_real -This file defines dyadic enclosures of rational casts and scientific literals for the -`interval_dyadic_real` inclusion family. +This file defines inclusion operations for the `interval_dyadic_real` inclusion family which +define dyadic interval enclosures for rational numbers. -/ public meta section @@ -23,11 +24,12 @@ namespace Inclusion namespace IntervalDyadicReal -/-- The precision of dyadic approximations. -/ +/-- The precision of dyadic approximations, defaulting to zero. -/ @[inclusionParam] def precParam : InclusionParamDecl where name := `prec type := q(Nat) + defaultValue? := some q(0) end IntervalDyadicReal @@ -41,80 +43,57 @@ namespace Inclusion namespace IntervalDyadicReal -/-- Efficiently enclose `m / d` in a dyadic interval with precision `prec`, for positive `d`. -/ -def divNatInterval (m d prec : ℕ) : Interval Dyadic := - let scaled := m <<< prec - let quotient := scaled / d - let upper := if quotient * d = scaled then quotient else quotient + 1 - Interval.Icc (Dyadic.ofIntWithPrec quotient prec) (Dyadic.ofIntWithPrec upper prec) - /-- Enclose a rational number in a dyadic interval with precision `prec`. -/ -def ratInterval (x : ℚ) (prec : ℕ) : Interval Dyadic := +def rat (x : ℚ) (prec : ℕ) : Interval Dyadic := let lower := x.toDyadic prec - let upper := if lower.toRat = x then lower else lower + Dyadic.ofIntWithPrec 1 prec + let upper := if lower.toRat = x then lower else lower + Dyadic.step prec Interval.Icc lower upper +@[inclusionOp interval_dyadic_real] +theorem ratCast_mem (q : ℚ) (prec : ℕ) : (q : ℝ) ∈ rat q prec := by + apply Interval.mem_map_Icc Dyadic.toReal + · exact Rat.cast_le.mpr Rat.toRat_toDyadic_le + · split_ifs with h + · rw [Dyadic.toReal, h] + · rw [← Dyadic.ofIntWithPrec_one] + exact Rat.cast_lt.mpr Rat.lt_toRat_toDyadic_add |>.le + +/-- Efficiently enclose `m / d` in a dyadic interval with precision `prec`. -/ +def natDiv (m d prec : ℕ) : Interval Dyadic := + let scaled := m <<< prec + let quotient := scaled / d + let lower := Dyadic.ofIntWithPrec quotient prec + let upper := if quotient * d = scaled then lower else lower + Dyadic.step prec + Interval.Icc lower upper + +theorem natDiv_eq_rat (m : ℕ) {d : ℕ} (prec : ℕ) (hd : 0 < d) : + natDiv m d prec = rat (mkRat m d) prec := by + rw [natDiv, rat] + congr 2 <;> + simp [Rat.toDyadic_mkRat, Dyadic.toRat_ofIntWithPrec_eq_mkRat, Rat.mkRat_eq_iff, + Int.shiftLeft_eq, Nat.shiftLeft_eq, hd.ne'] + norm_cast + +theorem natDiv_mem (m : ℕ) {d : ℕ} (prec : ℕ) (hd : 0 < d) : + (m : ℝ) / d ∈ natDiv m d prec := by + rw [natDiv_eq_rat m prec hd] + simpa [Rat.cast_mkRat_of_ne_zero, hd.ne'] using ratCast_mem (mkRat m d) prec + /-- Enclose a scientific literal in a dyadic interval with precision `prec`. -/ -def scientificInterval (m : ℕ) (s : Bool) (e prec : ℕ) : Interval Dyadic := +def scientific (m : ℕ) (s : Bool) (e prec : ℕ) : Interval Dyadic := if s then - divNatInterval m (10 ^ e) prec + natDiv m (10 ^ e) prec else - Interval.singleton Dyadic ((m * 10 ^ e : ℕ) : Dyadic) - -@[inclusionOp interval_dyadic_real] -theorem ratCast_mem (q : ℚ) (prec : ℕ) : (q : ℝ) ∈ ratInterval q prec := by - rw [mem_iff_mem_map] - constructor - · exact WithBot.coe_le_coe.mpr <| Rat.cast_le.mpr Rat.toRat_toDyadic_le - · apply WithTop.coe_le_coe.mpr - split_ifs with h - · rw [Dyadic.toReal, h] - · exact (Rat.cast_lt (K := ℝ)).mpr Rat.lt_toRat_toDyadic_add |>.le - -private theorem divNatInterval_lower_le (m : ℕ) {d : ℕ} (prec : ℕ) (hd : 0 < d) : - Dyadic.toReal (Dyadic.ofIntWithPrec ((m <<< prec) / d) prec) ≤ (m : ℝ) / d := by - norm_num [Dyadic.toReal, Dyadic.toRat_ofIntWithPrec_eq_mul_two_pow, Int.shiftLeft_eq] - rw [← div_eq_mul_inv] - apply (div_le_div_iff₀ - (by exact_mod_cast Nat.pow_pos (by decide : 0 < 2)) - (by exact_mod_cast hd)).2 - exact_mod_cast Nat.div_mul_le_self (m * 2 ^ prec) d - -private theorem le_divNatInterval_upper (m : ℕ) {d : ℕ} (prec : ℕ) (hd : 0 < d) : - (m : ℝ) / d ≤ Dyadic.toReal (Dyadic.ofIntWithPrec - (if (m <<< prec) / d * d = m <<< prec then (m <<< prec) / d else (m <<< prec) / d + 1) - prec) := by - rw [Dyadic.toReal, ← Rat.cast_natCast (α := ℝ) m, ← Rat.cast_natCast (α := ℝ) d, - ← Rat.cast_div, Rat.cast_le] - norm_num [Dyadic.toRat_ofIntWithPrec_eq_mul_two_pow] - split_ifs with h - all_goals - rw [← div_eq_mul_inv] - apply (div_le_div_iff₀ (by exact_mod_cast hd) (pow_pos (by norm_num) _)).2 - simp only [Nat.shiftLeft_eq, Int.shiftLeft_eq] at h ⊢ - norm_cast - · exact h.ge - · simpa [Nat.add_mul] using (Nat.lt_div_mul_add (a := m * 2 ^ prec) hd).le - -private theorem divNat_mem_interval (m : ℕ) {d : ℕ} (prec : ℕ) (hd : 0 < d) : - (m : ℝ) / d ∈ (divNatInterval m d prec).map Dyadic.toReal := by - constructor - · apply WithBot.coe_le_coe.mpr - simpa [divNatInterval] using divNatInterval_lower_le m prec hd - · apply WithTop.coe_le_coe.mpr - simpa [divNatInterval] using le_divNatInterval_upper m prec hd + Interval.singleton (m * (10 : Dyadic) ^ e) @[inclusionOp interval_dyadic_real] theorem scientific_mem (m : ℕ) (s : Bool) (e prec : ℕ) : - (OfScientific.ofScientific (α := ℝ) m s e) ∈ scientificInterval m s e prec := by + (OfScientific.ofScientific (α := ℝ) m s e) ∈ scientific m s e prec := by cases s - · rw [mem_iff_mem_map, NNRatCast.ofScientific_eq_ite] - simp only [Bool.false_eq_true, if_false, NNRat.cast_natCast] - simpa [scientificInterval] using - Interval.mem_map_singleton ((m * 10 ^ e : ℕ) : Dyadic) Dyadic.toReal - · rw [mem_iff_mem_map] - simpa [scientificInterval, NNRatCast.ofScientific_eq_ite] using - divNat_mem_interval m prec (Nat.pow_pos (by decide : 0 < 10)) + · simpa [scientific, NNRatCast.ofScientific_eq_ite, mem_iff_mem_map] using + Interval.mem_map_singleton (m * (10 : Dyadic) ^ e) Dyadic.toReal + · simpa [scientific, NNRatCast.ofScientific_eq_ite] using + natDiv_mem m prec (Nat.pow_pos (by decide : 0 < 10)) end IntervalDyadicReal diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Splitting.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Splitting.lean deleted file mode 100644 index 06f287cbee776e..00000000000000 --- a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Splitting.lean +++ /dev/null @@ -1,71 +0,0 @@ -/- -Copyright (c) 2026 David Ledvinka. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: David Ledvinka --/ -module - -public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Basic - -/-! -# Binary splitting of dyadic real intervals - -This file defines a cover that repeatedly bisects bounded dyadic intervals. --/ - -@[expose] public section - -namespace Inclusion -namespace IntervalDyadicReal - -namespace BinarySplit - -/-- The dyadic midpoint of `a` and `b`. -/ -def midpoint (a b : Dyadic) : Dyadic := - match a + b with - | .zero => .zero - | .ofOdd n k hn => .ofOdd n (k + 1) hn - -/-- Map `F` over the intervals produced by bisecting `I` to depth `n`, coarsening the results. -/ -@[specialize] -def coverMap {Iβ β : Type*} [ToSet Iβ β] [Coarsen Iβ β] : - ℕ → Interval Dyadic → (Interval Dyadic → Iβ) → Iβ - | 0, I, F => F I - | n + 1, I, F => - match I with - | ⟨some l, some u⟩ => - let m := midpoint l u - Coarsen.coarsen (α := β) (coverMap n ⟨l, m⟩ F) (coverMap n ⟨m, u⟩ F) - | _ => F I - -theorem mem_coverMap {Iβ β : Type*} [ToSet Iβ β] [Coarsen Iβ β] - (n : ℕ) (I : Interval Dyadic) (F : Interval Dyadic → Iβ) {y : β} {r : ℝ} - (hr : r ∈ I) (hy : ∀ J, r ∈ J → y ∈ F J) : y ∈ coverMap n I F := by - induction n generalizing I with - | zero => exact hy I hr - | succ n ih => - rcases I with ⟨lb, ub⟩ - cases lb with - | bot => exact hy _ hr - | coe l => - cases ub with - | top => exact hy _ hr - | coe u => - let m := midpoint l u - let left : Interval Dyadic := ⟨l, m⟩ - let right : Interval Dyadic := ⟨m, u⟩ - by_cases hl : r ≤ Dyadic.toReal m - · exact Coarsen.mem_coarsen_left - (ih left ⟨hr.1, WithTop.coe_le_coe.mpr hl⟩) - · exact Coarsen.mem_coarsen_right - (ih right ⟨WithBot.coe_le_coe.mpr (le_of_not_ge hl), hr.2⟩) - -/-- Cover a dyadic interval by repeatedly bisecting it to depth `n`. -/ -def cover (n : ℕ) : Cover (Interval Dyadic) ℝ where - coverMap := coverMap n - mem_coverMap hx hy := mem_coverMap n _ _ hx hy - -end BinarySplit - -end IntervalDyadicReal -end Inclusion diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean index 3c8fdcda1c8d0a..e3a7bce459ed47 100644 --- a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean @@ -6,7 +6,11 @@ Authors: David Ledvinka module public meta import Mathlib.Tactic.Inclusion.Extension.Core.Core -public meta import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Extensions +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Basic +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Rational +public meta import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Basic +public meta import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Hypotheses +public meta import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Rational /-! # The `dyadic_interval` tactic diff --git a/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean b/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean index 65fb54e09afc5f..295a76120a68c5 100644 --- a/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean +++ b/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean @@ -53,13 +53,13 @@ initialize registerBuiltinAttribute { } /-- Syntax for declaring an inclusion extension using the `inclusionExt` attribute. -/ -syntax (name := inclusionExtAttr) "inclusionExt " ident " | " term,+ : attr +syntax (name := inclusionExtAttr) "inclusionExt" term,+ : attr -/-- Add the inclusion extension `declName` to `familyName` under `keys`. -/ -def addInclusionExt (familyName declName : Name) (keys : Array (Array DiscrTree.Key)) +/-- Add the inclusion extension `declName` under `keys`. -/ +def addInclusionExt (declName : Name) (keys : Array (Array DiscrTree.Key)) (kind : AttributeKind) : AttrM Unit := do - let family ← getInclusionFamily familyName let ext ← evalDecl InclusionExt ``InclusionExt declName + let family ← getInclusionFamily ext.family family.inclusionExt.add ((keys, declName), ext) kind initialize registerBuiltinAttribute { @@ -67,26 +67,26 @@ initialize registerBuiltinAttribute { descr := "adds an inclusion extension" applicationTime := .afterCompilation add := fun declName stx kind => match stx with - | `(attr| inclusionExt $familyName:ident | $es,*) => do + | `(attr| inclusionExt $es,*) => do let env ← getEnv ensureAttrDeclIsMeta `inclusionExt declName kind unless (env.getModuleIdxFor? declName).isNone do throwAttrDeclInImportedModule `inclusionExt declName if (IR.getSorryDep env declName).isSome then return let keys ← elabExtKeys (es.getElems.map (·.raw)) - addInclusionExt familyName.getId declName keys kind + addInclusionExt declName keys kind | _ => throwUnsupportedSyntax erase := fun _ => throwError "Inclusion extensions cannot be erased by declaration" } /-- Syntax for declaring a hypothesis extension using the `hypothesisExt` attribute. -/ -syntax (name := hypothesisExtAttr) "hypothesisExt " ident " | " term,+ : attr +syntax (name := hypothesisExtAttr) "hypothesisExt" term,+ : attr -/-- Add the hypothesis extension `declName` to `familyName` under `keys`. -/ -def addHypothesisExt (familyName declName : Name) (keys : Array (Array DiscrTree.Key)) +/-- Add the hypothesis extension `declName` under `keys`. -/ +def addHypothesisExt (declName : Name) (keys : Array (Array DiscrTree.Key)) (kind : AttributeKind) : AttrM Unit := do - let family ← getInclusionFamily familyName let ext ← evalDecl HypothesisExt ``HypothesisExt declName + let family ← getInclusionFamily ext.family family.hypothesisExt.add ((keys, declName), ext) kind /-- Register the `hypothesisExt` attribute. -/ @@ -95,14 +95,14 @@ initialize registerBuiltinAttribute { descr := "adds a hypothesis extension" applicationTime := .afterCompilation add := fun declName stx kind => match stx with - | `(attr| hypothesisExt $familyName:ident | $es,*) => do + | `(attr| hypothesisExt $es,*) => do let env ← getEnv ensureAttrDeclIsMeta `hypothesisExt declName kind unless (env.getModuleIdxFor? declName).isNone do throwAttrDeclInImportedModule `hypothesisExt declName if (IR.getSorryDep env declName).isSome then return let keys ← elabExtKeys (es.getElems.map (·.raw)) - addHypothesisExt familyName.getId declName keys kind + addHypothesisExt declName keys kind | _ => throwUnsupportedSyntax erase := fun _ => throwError "Hypothesis extensions cannot be erased by declaration" } @@ -240,10 +240,10 @@ private def addInclusionOp (theoremName familyName : Name) (priority : Nat) let derive := mkAppN (mkConst ``deriveInclusionOp) #[toExpr theoremName, toExpr hypArgs, toExpr params] let value := mkAppN (mkConst ``InclusionExt.mk) - #[toExpr extName, toExpr theoremName, derive, toExpr priority] + #[toExpr extName, toExpr familyName, toExpr theoremName, derive, toExpr priority] let decl ← mkDefinitionValInferringUnsafe extName [] (mkConst ``InclusionExt) value .opaque addAndCompile (.defnDecl decl) (markMeta := true) - addInclusionExt familyName extName #[path] kind + addInclusionExt extName #[path] kind /-- Syntax for registering an inclusion extension from a theorem using the `inclusionOp` attribute. -/ @@ -302,10 +302,10 @@ private def addHypothesisOp (theoremName familyName : Name) (priority : Nat) let derive := mkAppN (mkConst ``deriveHypothesisOp) #[toExpr theoremName, toExpr sourceIdx, toExpr hypArgs, toExpr params] let value := mkAppN (mkConst ``HypothesisExt.mk) - #[toExpr extName, toExpr theoremName, derive, toExpr priority] + #[toExpr extName, toExpr familyName, toExpr theoremName, derive, toExpr priority] let decl ← mkDefinitionValInferringUnsafe extName [] (mkConst ``HypothesisExt) value .opaque addAndCompile (.defnDecl decl) (markMeta := true) - addHypothesisExt familyName extName #[path] kind + addHypothesisExt extName #[path] kind /-- Syntax for registering a hypothesis extension from a theorem using the `hypothesisOp` attribute. -/ diff --git a/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean b/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean index 57fa680b77b2c8..f83aef401ecf92 100644 --- a/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean +++ b/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean @@ -64,6 +64,25 @@ private def checkIVarWellFormed (localContext : LocalContext) (iExpr : IExpr) : throwError "Cannot use the `ToSet` instance for {e} because it depends on variables \ introduced while constructing the inclusion" +open PrettyPrinter Delaborator SubExpr in +/-- Delaborate an inclusion set variable as `I[e]`. -/ +@[delab mdata.Inclusion.Internal.iVarDisplay] +def delabIVarDisplay : Delab := do + let iVarDisplayExpr ← getExpr + let some (.letE _ _ _ _ _) := annotation? `Inclusion.Internal.iVarDisplay iVarDisplayExpr + | failure + let exprSyntax ← withMDataExpr (withLetValue delab) + let stx ← `($(mkIdent `I)[$exprSyntax]) + let stx ← annotateCurPos ⟨stx.raw.rewriteBottomUp (·.setInfo .none)⟩ + let infoStx : Term := ⟨stx.raw.setKind `Inclusion.Internal.iVarDisplay⟩ + addDelabTermInfo (← getPos) infoStx iVarDisplayExpr (explicit := false) + return stx + +/-- Construct the expression used to display `setVar` as `I[iExpr.expr]`. -/ +private def mkIVarDisplay (iExpr : IExpr) (setVar : Expr) : Expr := + mkAnnotation `Inclusion.Internal.iVarDisplay <| + mkLet .anonymous iExpr.iType.elemType iExpr.expr setVar (nondep := true) + /-- Create and register an inclusion variable for `iExpr`. -/ def mkIVar (iExpr : IExpr) (cover : Option Expr := none) : InclusionM IVar := do let ctx ← read @@ -76,14 +95,18 @@ def mkIVar (iExpr : IExpr) (cover : Option Expr := none) : InclusionM IVar := do let hypVar ← mkFreshExprMVarAt ctx.localContext ctx.localInstances hypType .syntheticOpaque let iVar := { iExpr, setVar, hypVar, cover } modify fun state => { state with iVars := state.iVars.insert iVar.expr iVar } + if ← isTracingEnabledFor `Tactic.inclusion then + let iVarDisplayExpr := mkIVarDisplay iExpr setVar + modify fun state => { + state with iVarDisplays := state.iVarDisplays.insert setVar iVarDisplayExpr } return iVar /-- Construct an inclusion extension for making non dependently typed inclusion variables. -/ -def mkNDIVarExt (iType : IType) +def mkNDIVarExt (family : Name) (iType : IType) (mkCover : InclusionM (Option Expr) := pure none) (priority : Nat := eval_prio low) (name : Name := by exact decl_name%) : InclusionExt where declName := name - userName := name + family := family priority := priority derive e := do let eType ← inferType e diff --git a/MathlibTest/Tactic/Inclusion/Basic.lean b/MathlibTest/Tactic/Inclusion/Basic.lean index d710a1f8ab9670..66d55832cbf21b 100644 --- a/MathlibTest/Tactic/Inclusion/Basic.lean +++ b/MathlibTest/Tactic/Inclusion/Basic.lean @@ -9,201 +9,172 @@ open Inclusion namespace Inclusion.Tests -def unitInterval : Interval Dyadic := ⟨1, 2⟩ - section Constants -example : (0 : ℝ) ≤ 0 := by - dyadic_interval +example : (2 : ℝ) ≤ 2 := by dyadic_interval -example : (1 : ℝ) ≤ 1 := by - inclusion [core, interval_dyadic_real] +example : ((3 : ℕ) : ℝ) ≤ 3 := by dyadic_interval -example : (2 : ℝ) ≤ 2 := by - inclusion [core, interval_dyadic_real] +example : ((-3 : ℤ) : ℝ) = -3 := by dyadic_interval -example : ((3 : ℕ) : ℝ) ≤ 3 := by - inclusion [core, interval_dyadic_real] +end Constants -example : ((-3 : ℤ) : ℝ) = -3 := by - inclusion [core, interval_dyadic_real] +section Rationals -example : (((1 : ℚ) / 3 : ℚ) : ℝ) < (((334 : ℚ) / 1000 : ℚ) : ℝ) := by - dyadic_interval [core, prec := 12] +example : ((1 / 2 : ℚ) : ℝ) = 0.5 := by dyadic_interval [prec := 1] -example : (((1 : ℚ) / 2 : ℚ) : ℝ) = 0.5 := by - inclusion [core, interval_dyadic_real, prec := 1] +example : ((-1 / 3 : ℚ) : ℝ) < -0.3 := by dyadic_interval [prec := 12] -example : (((-1 : ℚ) / 3 : ℚ) : ℝ) < -0.3 := by - inclusion [core, interval_dyadic_real, prec := 12] +example : ((22 / 7 : ℚ) : ℝ) ∈ Set.Ioo 3.14 3.15 := by dyadic_interval [prec := 12] -example : (0.1 : ℝ) < 0.2 := by - fail_if_success inclusion [core, interval_dyadic_real, prec := 2] - inclusion [core, interval_dyadic_real, prec := 4] +example : ((3 / 8 : ℚ) : ℝ) + ((5 / 8 : ℚ) : ℝ) = 1 := by dyadic_interval [prec := 3] -example : (123e4 : ℝ) = 1230000 := by - inclusion [core, interval_dyadic_real, prec := 0] +end Rationals -example : (0.12345678901234567890123456789 : ℝ) < 0.1234567890123456789012345679 := by - inclusion [core, interval_dyadic_real, prec := 100] +section Scientific -end Constants +example : (12.5 : ℝ) + 0.25 = 12.75 := by dyadic_interval [prec := 2] -section Arithmetic +example : (3.1415 : ℝ) ∈ Set.Ioo 3.14 3.15 := by dyadic_interval [prec := 14] -example : (1 : ℝ) + 2 ≤ 3 := by - inclusion [core, interval_dyadic_real] +example : (1e12 : ℝ) - 999999999999 = 1 := by dyadic_interval -example : (1 : ℝ) + 2 ≤ 3 := by - inclusion [core, interval_dyadic_real, core, interval_dyadic_real] +end Scientific -example : -(2 : ℝ) ≤ -1 := by - inclusion [core, interval_dyadic_real] +section Sets -example : (3 : ℝ) - 1 ≤ 2 := by - inclusion [core, interval_dyadic_real] +example {x : ℝ} (hx : x ∈ Set.Ici 1) : x ∈ Set.Ici 1 := by dyadic_interval -end Arithmetic +example {x : ℝ} (hx : x ∈ Set.Iic 2) : x ∈ Set.Iic 2 := by dyadic_interval -section Propositions +example {x : ℝ} (hx : x ∈ Set.Ioi 1) : x ∈ Set.Ioi 0 := by dyadic_interval -example : (1 : ℝ) ≤ 2 := by - inclusion [core, interval_dyadic_real] +example {x : ℝ} (hx : x ∈ Set.Iio 2) : x ∈ Set.Iio 3 := by dyadic_interval -example : (2 : ℝ) ≥ 1 := by - inclusion [core, interval_dyadic_real] +example {x : ℝ} (hx : x ∈ Set.Icc 1 2) : x ∈ Set.Icc 1 2 := by dyadic_interval -example : (1 : ℝ) < 2 := by - inclusion [core, interval_dyadic_real] +example {x : ℝ} (hx : x ∈ Set.Ico 1 2) : x ∈ Set.Ico 0 3 := by dyadic_interval -example : (2 : ℝ) > 1 := by - inclusion [core, interval_dyadic_real] +example {x : ℝ} (hx : x ∈ Set.Ioc 1 2) : x ∈ Set.Ioc 0 3 := by dyadic_interval -example : (1 : ℝ) = 1 := by - inclusion [core, interval_dyadic_real] +example {x : ℝ} (hx : x ∈ Set.Ioo 1 2) : x ∈ Set.Ioo 0 3 := by dyadic_interval -example : (1 : ℝ) ∈ Set.Ici 1 := by - inclusion [core, interval_dyadic_real] +end Sets -example : (1 : ℝ) ∈ Set.Iic 1 := by - inclusion [core, interval_dyadic_real] +section Logic -example : (1 : ℝ) ∈ Set.Ioi 0 := by - inclusion [core, interval_dyadic_real] +example {x y : ℝ} (hx : x ∈ Set.Icc 1 2) (hy : y ∈ Set.Icc 2 3) : + x ≤ 2 ∧ y ≤ 3 := by dyadic_interval -example : (1 : ℝ) ∈ Set.Iio 2 := by - inclusion [core, interval_dyadic_real] +example {x y : ℝ} (hx : x ∈ Set.Icc 2 3) (hy : y ∈ Set.Icc 0 1) : + x ≤ 1 ∨ y ≤ 1 := by dyadic_interval -example : (1 : ℝ) ∈ Set.Icc 1 1 := by - inclusion [core, interval_dyadic_real] +example {x y : ℝ} (hx : x ∈ Set.Icc 2 3) (hy : y ∈ Set.Icc 0 1) : + ¬x < y := by dyadic_interval -example : (1 : ℝ) ∈ Set.Ico 1 2 := by - inclusion [core, interval_dyadic_real] +example {x : ℝ} (hx : x = 2) : x + x = 4 := by dyadic_interval -example : (1 : ℝ) ∈ Set.Ioc 0 1 := by - inclusion [core, interval_dyadic_real] +example {x : ℝ} (hx : 1 ≤ x ∧ x ≤ 2) : x ≤ 2 := by dyadic_interval -example : (1 : ℝ) ∈ Set.Ioo 0 2 := by - inclusion [core, interval_dyadic_real] +end Logic -example : ¬¬(1 : ℝ) ≤ 2 := by - inclusion [core, interval_dyadic_real] +section Hypotheses -example : ((1 : ℝ) ≤ 2 ∧ (2 : ℝ) ≤ 3) := by - inclusion [core, interval_dyadic_real] +example {x : ℝ} (hx : x ≤ 2) : x ≤ 2 := by dyadic_interval -example : ((2 : ℝ) ≤ 1 ∨ (2 : ℝ) ≤ 3) := by - inclusion [core, interval_dyadic_real] +example {x : ℝ} (hx : x ≥ 1) : x ≥ 1 := by dyadic_interval -example : ¬(2 : ℝ) ≤ 1 := by - inclusion [core, interval_dyadic_real] +example {x : ℝ} (hx₀ : 1 < x) (hx₁ : x < 2) : x ∈ Set.Ioo 0 3 := by dyadic_interval -example : ¬(2 : ℝ) < 1 := by - inclusion [core, interval_dyadic_real] +example {x : ℝ} (hx₀ : 1 ≤ x) (hx₁ : x ≤ 2) : x ∈ Set.Icc 1 2 := by dyadic_interval -example : (1 : ℝ) ≠ 2 := by - inclusion [core, interval_dyadic_real] +example {x : ℝ} (hx₀ : x ≤ 3) (hx₁ : x ≤ 2) (hx₂ : 0 ≤ x) (hx₃ : 1 ≤ x) : + x ∈ Set.Icc 1 2 := by dyadic_interval -example : ¬((2 : ℝ) ∈ Set.Icc 0 1) := by - inclusion [core, interval_dyadic_real] +end Hypotheses -end Propositions +section Arithmetic -section Hypotheses +example {x : ℝ} (hx : x ∈ (⟨1, 2⟩ : Interval Dyadic)) : x + x ≤ 4 := by dyadic_interval -example {x : ℝ} (hx : x ∈ unitInterval) : x + x ≤ 4 := by - inclusion [core, interval_dyadic_real] +example {x y : ℝ} (hx : x ∈ Set.Icc 1 2) (hy : y ∈ Set.Icc 3 4) : + x + y ∈ Set.Icc 4 6 := by dyadic_interval -example {x : ℝ} (hx : x ∈ Set.Icc 1 2) : x + 1 ≤ 3 := by - inclusion [core, interval_dyadic_real] +example {x : ℝ} (hx : x ∈ Set.Icc (-2) 1) : -x ∈ Set.Icc (-1) 2 := by dyadic_interval -example {x : ℝ} (hx : x = 2) : x + x ≤ 4 := by - inclusion [core, interval_dyadic_real] +example {x y : ℝ} (hx₀ : 2 ≤ x) (hx₁ : x ≤ 3) (hy₀ : 0 ≤ y) (hy₁ : y ≤ 1) : + x - y ∈ Set.Icc 1 3 := by dyadic_interval -example {x : ℝ} (hx : 1 ≤ x ∧ x ≤ 2) : x + x ≤ 4 := by - inclusion [core, interval_dyadic_real] +example {x y : ℝ} (hx : x ≤ 3) (hy : 2 ≤ y) : x - y ≤ 1 := by dyadic_interval -example {x : ℝ} (hx : x ≤ 2) : x ≤ 2 := by - inclusion [core, interval_dyadic_real] +example {x y : ℝ} (hx : 1 ≤ x) (hy : y ≤ 2) : -1 ≤ x - y := by dyadic_interval -example {x : ℝ} (hx : x ≥ 1) : x ≥ 1 := by - inclusion [core, interval_dyadic_real] +example {w x y z : ℝ} (hw : w ∈ Set.Ici 1) (hx : x ∈ Set.Iic 2) (hy : y ∈ Set.Ioi 3) + (hz : z ∈ Set.Iio 4) : -2 ≤ w - x + y - z := by dyadic_interval -example {x : ℝ} (hx : x < 2) : x < 3 := by - inclusion [core, interval_dyadic_real] +example {x y z : ℝ} (hx : x ∈ Set.Ico 0 1) (hy : y ∈ Set.Ioc 1 2) + (hz : z ∈ Set.Ioo 2 3) : x + y + z ∈ Set.Icc 3 6 := by dyadic_interval -example {x : ℝ} (hx : x > 1) : x > 0 := by - inclusion [core, interval_dyadic_real] +example {x y z : ℝ} (hx₀ : -2 ≤ x) (hx₁ : x ≤ 1) (hy₀ : 1 ≤ y) (hy₁ : y ≤ 3) + (hz₀ : 4 ≤ z) (hz₁ : z ≤ 5) : x + y - z ∈ Set.Icc (-6) 0 := by dyadic_interval -example {x : ℝ} (hx : x ∈ Set.Ici 1) : x ∈ Set.Ici 1 := by - inclusion [core, interval_dyadic_real] +example {x y : ℝ} (hx₀ : x ≤ 3) (hx₁ : x ≤ 2) (hy₀ : 0 ≤ y) (hy₁ : 1 ≤ y) : + x - y ≤ 1 := by dyadic_interval -example {x : ℝ} (hx : x ∈ Set.Iic 2) : x ∈ Set.Iic 2 := by - inclusion [core, interval_dyadic_real] +example {x : ℝ} (hx₀ : 1 ≤ x) (hx₁ : x ≤ 2) : + x + ((1 / 3 : ℚ) : ℝ) ∈ Set.Icc 1.3 2.4 := by dyadic_interval [prec := 12] -example {x : ℝ} (hx : x ∈ Set.Ioi 1) : x ∈ Set.Ici 1 := by - inclusion [core, interval_dyadic_real] +example {x y : ℝ} (hx : x ≤ 1.25) (hy : 0.5 ≤ y) : + x - y + 2.5 ≤ 3.25 := by dyadic_interval [prec := 2] -example {x : ℝ} (hx : x ∈ Set.Iio 2) : x ∈ Set.Iic 2 := by - inclusion [core, interval_dyadic_real] +end Arithmetic -example {x : ℝ} (hx : x ∈ Set.Icc 1 2) : x ∈ Set.Icc 1 2 := by - inclusion [core, interval_dyadic_real] +section Splitting -example {x : ℝ} (hx : x ∈ Set.Ico 1 2) : x ∈ Set.Icc 1 2 := by - inclusion [core, interval_dyadic_real] +example {x : ℝ} (hx : x ∈ Set.Icc (-4) 4) : + x + 1.25 - (x - 0.5) ∈ Set.Icc 0.75 2.75 := by + dyadic_interval [binSplit := 3, prec := 2] -example {x : ℝ} (hx : x ∈ Set.Ioc 1 2) : x ∈ Set.Icc 1 2 := by - inclusion [core, interval_dyadic_real] +example {x : ℝ} (hx : x ∈ Set.Icc 0 4) : + x + ((1 / 3 : ℚ) : ℝ) - x ∈ Set.Icc (-0.2) 0.9 := by + dyadic_interval [binSplit := 3, prec := 12] -example {x : ℝ} (hx : x ∈ Set.Ioo 1 2) : x ∈ Set.Icc 1 2 := by - inclusion [core, interval_dyadic_real] +example {x : ℝ} (hx₀ : -2 ≤ x) (hx₁ : x ≤ 2) : + x - x + 3.125 ∈ Set.Icc 2.625 3.625 := by + dyadic_interval [binSplit := 3, prec := 3] -example {x y : ℝ} (hx : x ∈ Set.Icc 2 3) (hy : y ∈ Set.Icc 0 1) : ¬x ≤ y := by - inclusion [core, interval_dyadic_real] +end Splitting -example {x y : ℝ} (hx : x ∈ Set.Icc 2 3) (hy : y ∈ Set.Icc 1 2) : ¬x < y := by - inclusion [core, interval_dyadic_real] +section Kernel -end Hypotheses +example {x y : ℝ} (hx : x ∈ Set.Icc (-2) 3) (hy : y ∈ Set.Icc 1 2) : + x + y - x ∈ Set.Icc (-4) 7 := by dyadic_interval +kernel -section Evaluation +example : (0.12345678901234567890123456789 : ℝ) < 0.1234567890123456789012345679 := by + dyadic_interval +kernel [prec := 100] -example : (1 : ℝ) ≤ 2 := by - dyadic_interval +kernel +end Kernel + +section Native + +set_option linter.style.native false -example : ¬(2 : ℝ) ≤ 1 := by - inclusion +kernel [core, interval_dyadic_real] +example {x y : ℝ} (hx : x ∈ Set.Icc 2 3) (hy : y ∈ Set.Icc 0 1) : + x - y + 1 ∈ Set.Icc 2 4 := by dyadic_interval +native example : (0.12345678901234567890123456789 : ℝ) < 0.1234567890123456789012345679 := by - dyadic_interval +kernel [prec := 100] + dyadic_interval +native [prec := 100] + +end Native + +section Check /-- info: The inclusion check succeeded. -/ #guard_msgs in set_option linter.unusedTactic false in example : (1 : ℝ) ≤ 2 := by dyadic_interval? - inclusion [core, interval_dyadic_real] + dyadic_interval /-- info: The inclusion check failed: The proposition is provably false -/ @@ -213,6 +184,14 @@ example (h : False) : (2 : ℝ) ≤ 1 := by dyadic_interval? exact h.elim -end Evaluation +/-- info: The inclusion check failed: +The proposition was not proven true or false. -/ +#guard_msgs in +set_option linter.unusedTactic false in +example (x : ℝ) (h : False) : x ≤ 1 := by + dyadic_interval? + exact h.elim + +end Check end Inclusion.Tests diff --git a/MathlibTest/Tactic/Inclusion/Linter.lean b/MathlibTest/Tactic/Inclusion/NativeLinter.lean similarity index 67% rename from MathlibTest/Tactic/Inclusion/Linter.lean rename to MathlibTest/Tactic/Inclusion/NativeLinter.lean index 67baa2d4c9c586..e7e9504ea4ce82 100644 --- a/MathlibTest/Tactic/Inclusion/Linter.lean +++ b/MathlibTest/Tactic/Inclusion/NativeLinter.lean @@ -16,8 +16,7 @@ compiler (not just the Lean kernel), it could quite possibly be used to prove fa Note: This linter can be disabled with `set_option linter.style.native false` -/ #guard_msgs in -example : (0 : ℝ) ≤ 1 := by - inclusion +native [core, interval_dyadic_real] +example : (0 : ℝ) ≤ 1 := by inclusion +native [core, interval_dyadic_real] /-- warning: Using `inclusion +native` is not allowed in mathlib: because it trusts the entire Lean @@ -26,8 +25,7 @@ compiler (not just the Lean kernel), it could quite possibly be used to prove fa Note: This linter can be disabled with `set_option linter.style.native false` -/ #guard_msgs in -example : (0 : ℝ) ≤ 1 := by - inclusion (native := true) [core, interval_dyadic_real] +example : (0 : ℝ) ≤ 1 := by inclusion (native := true) [core, interval_dyadic_real] /-- warning: Using `dyadic_interval +native` is not allowed in mathlib: because it trusts the entire Lean @@ -36,13 +34,13 @@ compiler (not just the Lean kernel), it could quite possibly be used to prove fa Note: This linter can be disabled with `set_option linter.style.native false` -/ #guard_msgs in -example : (0 : ℝ) ≤ 1 := by - dyadic_interval +native +example : (0 : ℝ) ≤ 1 := by dyadic_interval +native -#guard_msgs in -example : (0 : ℝ) ≤ 1 := by - dyadic_interval +native -native +/-- +warning: Using `dyadic_interval +native` is not allowed in mathlib: because it trusts the entire Lean +compiler (not just the Lean kernel), it could quite possibly be used to prove false. +Note: This linter can be disabled with `set_option linter.style.native false` +-/ #guard_msgs in -example : (0 : ℝ) ≤ 1 := by - inclusion (native := false) [core, interval_dyadic_real] +example : (0 : ℝ) ≤ 1 := by dyadic_interval (native := true) diff --git a/MathlibTest/Tactic/Inclusion/Splitting.lean b/MathlibTest/Tactic/Inclusion/Splitting.lean deleted file mode 100644 index 9b1a9948b998c5..00000000000000 --- a/MathlibTest/Tactic/Inclusion/Splitting.lean +++ /dev/null @@ -1,39 +0,0 @@ -/- -Copyright (c) 2026 David Ledvinka. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: David Ledvinka --/ -import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Tactic - -open Inclusion - -namespace Inclusion.Tests - -def wideInterval : Interval Dyadic := ⟨0, 4⟩ - -example {x : ℝ} (_hx : x ∈ wideInterval) : True := by - fail_if_success - have : x - x ≤ 2 := by - inclusion [core, interval_dyadic_real] - trivial - -example {x : ℝ} (hx : x ∈ wideInterval) : x - x ≤ 2 := by - dyadic_interval [binSplit := 1] - -example {x : ℝ} (hx : x ∈ wideInterval) : x - x ≤ 1 := by - inclusion [core, interval_dyadic_real, binSplit := 2] - -example {x : ℝ} (hx : x ≤ 2) : x ≤ 2 := by - inclusion [core, interval_dyadic_real, binSplit := 100] - -example {x : ℝ} (hx : x ≥ 1) : x ≥ 1 := by - inclusion [core, interval_dyadic_real, binSplit := 100] - -/-- info: The inclusion check succeeded. -/ -#guard_msgs in -set_option linter.unusedTactic false in -example {x : ℝ} (hx : x ∈ wideInterval) : x - x ≤ 2 := by - dyadic_interval? [binSplit := 1] - inclusion [core, interval_dyadic_real, binSplit := 1] - -end Inclusion.Tests From b6c9661db55712989a311c97a39a428aca8ecab0 Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Mon, 24 Aug 2026 17:27:20 -0400 Subject: [PATCH 09/38] linter --- Mathlib/Tactic/Linter/DeprecatedSyntaxLinter.lean | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Mathlib/Tactic/Linter/DeprecatedSyntaxLinter.lean b/Mathlib/Tactic/Linter/DeprecatedSyntaxLinter.lean index f4156c0b121b1a..7986528f003834 100644 --- a/Mathlib/Tactic/Linter/DeprecatedSyntaxLinter.lean +++ b/Mathlib/Tactic/Linter/DeprecatedSyntaxLinter.lean @@ -92,6 +92,16 @@ public register_option linter.style.native : Bool := { descr := "enable the native-evaluation linter" } +/-- Deprecated in favor of `linter.style.native`. -/ +public register_option linter.style.nativeDecide : Bool := { + defValue := false + descr := "deprecated: use the `linter.style.native` option instead" + deprecation? := some { + since := "2026-08-24" + text? := "use the `linter.style.native` option instead" + } +} + /-- The option `linter.style.maxHeartbeats` of the deprecated syntax linter flags usages of `set_option n in cmd` that do not add a comment explaining the reason for the modification of the `maxHeartbeats`. From df9c3db7c3a4f9a1a60fcd62e981da8d42f08500 Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Mon, 24 Aug 2026 20:33:33 -0400 Subject: [PATCH 10/38] fix(Inclusion): adapt option deprecation to Lean 4.34 --- Mathlib/Tactic/Linter/DeprecatedSyntaxLinter.lean | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Mathlib/Tactic/Linter/DeprecatedSyntaxLinter.lean b/Mathlib/Tactic/Linter/DeprecatedSyntaxLinter.lean index 7986528f003834..6f83d69a251cde 100644 --- a/Mathlib/Tactic/Linter/DeprecatedSyntaxLinter.lean +++ b/Mathlib/Tactic/Linter/DeprecatedSyntaxLinter.lean @@ -93,13 +93,10 @@ public register_option linter.style.native : Bool := { } /-- Deprecated in favor of `linter.style.native`. -/ +@[deprecated linter.style.native (since := "2026-08-24")] public register_option linter.style.nativeDecide : Bool := { defValue := false descr := "deprecated: use the `linter.style.native` option instead" - deprecation? := some { - since := "2026-08-24" - text? := "use the `linter.style.native` option instead" - } } /-- The option `linter.style.maxHeartbeats` of the deprecated syntax linter flags usages of From fc93189a60797a6a1655594834e569d1336702fb Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Tue, 25 Aug 2026 00:22:45 -0400 Subject: [PATCH 11/38] try fix --- Mathlib/Lean/Meta/DiscrTreeExt.lean | 1 - Mathlib/Tactic/Inclusion/Core/Elab.lean | 1 - Mathlib/Tactic/Inclusion/Core/Inclusion.lean | 1 - Mathlib/Tactic/Inclusion/Core/Types.lean | 1 - Mathlib/Tactic/Inclusion/Extension/Core/Core.lean | 1 - Mathlib/Tactic/Inclusion/Extension/Interval.lean | 4 ++-- .../Tactic/Inclusion/Extension/IntervalDyadicReal/Basic.lean | 1 - .../Inclusion/Extension/IntervalDyadicReal/BinarySplit.lean | 1 - .../Inclusion/Extension/IntervalDyadicReal/Hypotheses.lean | 3 ++- .../Inclusion/Extension/IntervalDyadicReal/Rational.lean | 1 - .../Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean | 4 +--- Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean | 3 ++- 12 files changed, 7 insertions(+), 15 deletions(-) diff --git a/Mathlib/Lean/Meta/DiscrTreeExt.lean b/Mathlib/Lean/Meta/DiscrTreeExt.lean index 75839fc052d450..44a72ada299f51 100644 --- a/Mathlib/Lean/Meta/DiscrTreeExt.lean +++ b/Mathlib/Lean/Meta/DiscrTreeExt.lean @@ -6,7 +6,6 @@ Authors: David Ledvinka module public import Mathlib.Init -public meta import Lean.Elab.Term.TermElabM public meta import Lean.Meta.DiscrTree /-! diff --git a/Mathlib/Tactic/Inclusion/Core/Elab.lean b/Mathlib/Tactic/Inclusion/Core/Elab.lean index c9d96e6bbb4852..45637d1b7ba4f4 100644 --- a/Mathlib/Tactic/Inclusion/Core/Elab.lean +++ b/Mathlib/Tactic/Inclusion/Core/Elab.lean @@ -6,7 +6,6 @@ Authors: David Ledvinka module public meta import Mathlib.Tactic.Inclusion.Core.Core -meta import Lean.Elab.ConfigEval /-! # Elaboration of the `inclusion` tactic diff --git a/Mathlib/Tactic/Inclusion/Core/Inclusion.lean b/Mathlib/Tactic/Inclusion/Core/Inclusion.lean index e485fd7d841123..3b6f318051227a 100644 --- a/Mathlib/Tactic/Inclusion/Core/Inclusion.lean +++ b/Mathlib/Tactic/Inclusion/Core/Inclusion.lean @@ -7,7 +7,6 @@ module public meta import Mathlib.Tactic.Inclusion.Core.Expr public meta import Mathlib.Tactic.Inclusion.Core.Extensions -public meta import Mathlib.Lean.Meta.Basic /-! # Constructing inclusions diff --git a/Mathlib/Tactic/Inclusion/Core/Types.lean b/Mathlib/Tactic/Inclusion/Core/Types.lean index 41a15e848a18bf..db52a96d091648 100644 --- a/Mathlib/Tactic/Inclusion/Core/Types.lean +++ b/Mathlib/Tactic/Inclusion/Core/Types.lean @@ -6,7 +6,6 @@ Authors: David Ledvinka module public import Mathlib.Init -public meta import Lean.Meta.Basic /-! # Datatypes for the `inclusion` tactic diff --git a/Mathlib/Tactic/Inclusion/Extension/Core/Core.lean b/Mathlib/Tactic/Inclusion/Extension/Core/Core.lean index 36cc5dd8d1d279..993e1ae034500f 100644 --- a/Mathlib/Tactic/Inclusion/Extension/Core/Core.lean +++ b/Mathlib/Tactic/Inclusion/Extension/Core/Core.lean @@ -5,7 +5,6 @@ Authors: David Ledvinka -/ module -public import Mathlib.Tactic.Inclusion.Core.ToSet public meta import Mathlib.Tactic.Inclusion.Extension.Core.Init public meta import Mathlib.Tactic.Inclusion.ExtensionAPI.Attr diff --git a/Mathlib/Tactic/Inclusion/Extension/Interval.lean b/Mathlib/Tactic/Inclusion/Extension/Interval.lean index 5d0b7d8a9e589a..889688a3b4c875 100644 --- a/Mathlib/Tactic/Inclusion/Extension/Interval.lean +++ b/Mathlib/Tactic/Inclusion/Extension/Interval.lean @@ -5,11 +5,11 @@ Authors: David Ledvinka -/ module -public import Mathlib.Algebra.Order.Group.Defs +public import Mathlib.Algebra.Order.Group.Unbundled.Basic +public import Mathlib.Algebra.Order.Monoid.Defs public import Mathlib.Algebra.Order.Monoid.Unbundled.WithTop public import Mathlib.Order.Hom.Basic public import Mathlib.Order.Interval.Set.Defs -public import Mathlib.Order.MinMax public import Mathlib.Tactic.Inclusion.Core.ToSet /-! diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Basic.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Basic.lean index 84095bf292a9d2..551f0d2a13cbc2 100644 --- a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Basic.lean +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Basic.lean @@ -6,7 +6,6 @@ Authors: David Ledvinka module public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.BinarySplit -public meta import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.BinarySplit /-! # Basic inclusion extensions for interval_dyadic_real diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/BinarySplit.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/BinarySplit.lean index ef9224279f4e5d..528a274d02bb08 100644 --- a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/BinarySplit.lean +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/BinarySplit.lean @@ -7,7 +7,6 @@ module public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Init public meta import Mathlib.Tactic.Inclusion.ExtensionAPI.Attr -public meta import Qq /-! # Binary splitting of dyadic real intervals diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Hypotheses.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Hypotheses.lean index b223711ce4750f..bce0235fcc723d 100644 --- a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Hypotheses.lean +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Hypotheses.lean @@ -5,7 +5,8 @@ Authors: David Ledvinka -/ module -public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Basic +public meta import Mathlib.Tactic.Inclusion.ExtensionAPI.Attr +public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Init /-! # Hypothesis operations for dyadic real intervals diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Rational.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Rational.lean index 3866721dfcd8cd..d53b73234900db 100644 --- a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Rational.lean +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Rational.lean @@ -6,7 +6,6 @@ Authors: David Ledvinka module public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Basic -public meta import Mathlib.Tactic.Inclusion.ExtensionAPI.Attr import Mathlib.Data.Rat.Cast.Lemmas /-! diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean index e3a7bce459ed47..b0f0fe66a3c83f 100644 --- a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean @@ -5,12 +5,10 @@ Authors: David Ledvinka -/ module +public meta import Mathlib.Tactic.Inclusion.Core.Elab public meta import Mathlib.Tactic.Inclusion.Extension.Core.Core -public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Basic public import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Rational -public meta import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Basic public meta import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Hypotheses -public meta import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Rational /-! # The `dyadic_interval` tactic diff --git a/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean b/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean index f83aef401ecf92..814a8826c2a5c4 100644 --- a/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean +++ b/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean @@ -5,7 +5,8 @@ Authors: David Ledvinka -/ module -public meta import Mathlib.Tactic.Inclusion.Core.Elab +public meta import Mathlib.Lean.Meta.Basic +public meta import Mathlib.Tactic.Inclusion.Core.Inclusion /-! # Basic API for `inclusion` extensions From c5c31efba0ccfd5ecb93f67c41f795077d141095 Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Tue, 25 Aug 2026 00:52:12 -0400 Subject: [PATCH 12/38] actual fix --- Mathlib/Data/Dyadic.lean | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Mathlib/Data/Dyadic.lean b/Mathlib/Data/Dyadic.lean index d41fbab7d8fc60..65bca9473fa225 100644 --- a/Mathlib/Data/Dyadic.lean +++ b/Mathlib/Data/Dyadic.lean @@ -26,14 +26,19 @@ instance : LinearOrder Dyadic where toDecidableLE := Dyadic.instDecidableLE instance : AddCommGroup Dyadic where - nsmul := nsmulRec - zsmul := zsmulRec + nsmul := (· * ·) + zsmul := (· * ·) add_zero := Dyadic.add_zero zero_add := Dyadic.zero_add add_assoc := Dyadic.add_assoc sub_eq_add_neg _ _ := rfl neg_add_cancel := Dyadic.neg_add_cancel add_comm := Dyadic.add_comm + nsmul_zero := by grind + nsmul_succ := by grind + zsmul_zero' := by grind + zsmul_succ' := by grind + zsmul_neg' := by grind namespace Dyadic From f2f52b9c4e9bfd2e54abdb337158417f69381465 Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Tue, 25 Aug 2026 21:54:21 -0400 Subject: [PATCH 13/38] minor cleanup --- Mathlib/Tactic/Inclusion/Core/Inclusion.lean | 4 ++-- Mathlib/Tactic/Inclusion/Core/Types.lean | 2 +- Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean | 4 ++-- Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean | 15 ++++++--------- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/Mathlib/Tactic/Inclusion/Core/Inclusion.lean b/Mathlib/Tactic/Inclusion/Core/Inclusion.lean index 3b6f318051227a..f07f0f62f5e1c0 100644 --- a/Mathlib/Tactic/Inclusion/Core/Inclusion.lean +++ b/Mathlib/Tactic/Inclusion/Core/Inclusion.lean @@ -135,7 +135,7 @@ def mkExprInclusion (output : IExpr) (body : ExprInclusionBody) : HypothesisM Ex collectHyps let context ← read let state ← get - let coarsen? ← match context.iVars.any (·.cover.isSome) with + let coarsen? ← match context.iVars.any (·.cover?.isSome) with | true => some <$> output.iType.synthCoarsen | false => pure none let body ← context.iVars.foldrM (init := body) fun iVar body => do @@ -144,7 +144,7 @@ def mkExprInclusion (output : IExpr) (body : ExprInclusionBody) : HypothesisM Ex (binderInfoForMVars := .default) let proof ← mkLambdaFVars #[iVar.setVar, iVar.hypVar] body.proofBody (binderInfoForMVars := .default) - match iVar.cover with + match iVar.cover? with | none => let inclusionBody := mkApp inclusion hypBody.inclusionBody let proofBody := mkAppN proof #[hypBody.inclusionBody, hypBody.proofBody] diff --git a/Mathlib/Tactic/Inclusion/Core/Types.lean b/Mathlib/Tactic/Inclusion/Core/Types.lean index db52a96d091648..1645ea9e54b6c0 100644 --- a/Mathlib/Tactic/Inclusion/Core/Types.lean +++ b/Mathlib/Tactic/Inclusion/Core/Types.lean @@ -54,7 +54,7 @@ structure IVar where hypVar : Expr /-- An optional expression of type `Cover iVar.type.setType iVar.type.elemType`. When present, the inclusion computation is mapped over this cover to reduce the "dependency effect". -/ - cover : Option Expr + cover? : Option Expr /-- The `IType` of an `IVar`. -/ def IVar.type (iVar : IVar) : IType := iVar.iExpr.iType diff --git a/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean b/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean index 295a76120a68c5..e0abfd8642e261 100644 --- a/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean +++ b/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean @@ -166,7 +166,7 @@ def deriveHypothesisOp (theoremName : Name) (sourceIdx : Nat) (hypArgs : Array H sourceId.assign h let some (e, s, _) := toSetMem? conclusion | failure let e ← instantiateMVars e - let some ⟨iExpr, _, _, _⟩ ← findIVar? e | failure + let some iVar ← findIVar? e | failure for ⟨name, idx⟩ in paramArgs do unless ← isDefEq args[idx]! (← HypothesisM.getParam name) do failure for ⟨elemIdx, setIdx, proofIdx⟩ in hypArgs do @@ -184,7 +184,7 @@ def deriveHypothesisOp (theoremName : Name) (sourceIdx : Nat) (hypArgs : Array H hypothesis extension generated from `{.ofConstName theoremName}`" let inclusionBody ← instantiateMVars s let proofBody ← instantiateMVars (mkAppN theoremExpr args) - addInclusionHyp iExpr { inclusionBody, proofBody } + addInclusionHyp iVar.iExpr { inclusionBody, proofBody } /-- Extract the `HypArg` and `ParamArg` metadata from a theorem declaration. -/ private def getOpArgInfo (declName : Name) (matchExpr : Expr) diff --git a/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean b/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean index 814a8826c2a5c4..ab27c7bc76c79d 100644 --- a/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean +++ b/Mathlib/Tactic/Inclusion/ExtensionAPI/Basic.lean @@ -61,9 +61,6 @@ private def checkIVarWellFormed (localContext : LocalContext) (iExpr : IExpr) : unless ← MetavarContext.isWellFormed localContext iType.setType do throwError "Cannot use set type {iType.setType} for {e} because it depends on \ variables introduced while constructing the inclusion" - unless ← MetavarContext.isWellFormed localContext iType.toSetInst do - throwError "Cannot use the `ToSet` instance for {e} because it depends on variables \ - introduced while constructing the inclusion" open PrettyPrinter Delaborator SubExpr in /-- Delaborate an inclusion set variable as `I[e]`. -/ @@ -85,16 +82,16 @@ private def mkIVarDisplay (iExpr : IExpr) (setVar : Expr) : Expr := mkLet .anonymous iExpr.iType.elemType iExpr.expr setVar (nondep := true) /-- Create and register an inclusion variable for `iExpr`. -/ -def mkIVar (iExpr : IExpr) (cover : Option Expr := none) : InclusionM IVar := do +def mkIVar (iExpr : IExpr) (cover? : Option Expr := none) : InclusionM IVar := do let ctx ← read if ctx.noIVars then throwError "Cannot create an inclusion variable for {iExpr.expr} since `noIVars` is set to true" checkIVarWellFormed ctx.localContext iExpr let setVar ← mkFreshExprMVarAt ctx.localContext ctx.localInstances iExpr.iType.setType .syntheticOpaque - let hypType ← iExpr.mkMem setVar - let hypVar ← mkFreshExprMVarAt ctx.localContext ctx.localInstances hypType .syntheticOpaque - let iVar := { iExpr, setVar, hypVar, cover } + let hypVarType ← iExpr.mkMem setVar + let hypVar ← mkFreshExprMVarAt ctx.localContext ctx.localInstances hypVarType .syntheticOpaque + let iVar := { iExpr, setVar, hypVar, cover? } modify fun state => { state with iVars := state.iVars.insert iVar.expr iVar } if ← isTracingEnabledFor `Tactic.inclusion then let iVarDisplayExpr := mkIVarDisplay iExpr setVar @@ -104,7 +101,7 @@ def mkIVar (iExpr : IExpr) (cover : Option Expr := none) : InclusionM IVar := do /-- Construct an inclusion extension for making non dependently typed inclusion variables. -/ def mkNDIVarExt (family : Name) (iType : IType) - (mkCover : InclusionM (Option Expr) := pure none) + (mkCover? : InclusionM (Option Expr) := pure none) (priority : Nat := eval_prio low) (name : Name := by exact decl_name%) : InclusionExt where declName := name family := family @@ -113,7 +110,7 @@ def mkNDIVarExt (family : Name) (iType : IType) let eType ← inferType e unless ← isDefEq eType iType.elemType do failure let iExpr : IExpr := ⟨iType, e⟩ - return (← mkIVar iExpr (← mkCover)).toExprInclusionBody + return (← mkIVar iExpr (← mkCover?)).toExprInclusionBody /-- Return the inclusion variable registered for `e`, if there is one. -/ def findIVar? (e : Expr) : HypothesisM (Option IVar) := do From 53556b9f7875288057f09d215f3dea3ace4e3364 Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Wed, 26 Aug 2026 12:46:02 -0400 Subject: [PATCH 14/38] rename --- Mathlib/Tactic/Inclusion/Core/Elab.lean | 8 ++++---- Mathlib/Tactic/Linter/DeprecatedSyntaxLinter.lean | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Mathlib/Tactic/Inclusion/Core/Elab.lean b/Mathlib/Tactic/Inclusion/Core/Elab.lean index 45637d1b7ba4f4..0fcb29e0e8e036 100644 --- a/Mathlib/Tactic/Inclusion/Core/Elab.lean +++ b/Mathlib/Tactic/Inclusion/Core/Elab.lean @@ -56,10 +56,10 @@ def collectInclusionArgs (argStxs : Array Syntax) : TacticM InclusionConfig := d return { paramSettings, families } /-- `inclusion` tactic for proving "inclusion" propositions. -/ -syntax (name := inclusionTacStx) "inclusion" optConfig " [" inclusionArg,* "]" : tactic +syntax (name := inclusion) "inclusion" optConfig " [" inclusionArg,* "]" : tactic /-- Elaborator for the `inclusion` tactic. -/ -@[tactic inclusionTacStx] +@[tactic inclusion] def inclusionTac : Tactic | `(tactic| inclusion $cfg:optConfig [$args,*]) => do let options ← elabInclusionConfig cfg @@ -69,10 +69,10 @@ def inclusionTac : Tactic | _ => throwUnsupportedSyntax /-- Tactic for quickly checking if the `inclusion` tactic will succeed. -/ -syntax (name := inclusion?TacStx) "inclusion?" " [" inclusionArg,* "]" : tactic +syntax (name := inclusion?) "inclusion?" " [" inclusionArg,* "]" : tactic /-- Elaborator for the `inclusion?` tactic. -/ -@[tactic inclusion?TacStx] +@[tactic inclusion?] def inclusion?Tac : Tactic | `(tactic| inclusion? [$args,*]) => do let config ← collectInclusionArgs args.getElems diff --git a/Mathlib/Tactic/Linter/DeprecatedSyntaxLinter.lean b/Mathlib/Tactic/Linter/DeprecatedSyntaxLinter.lean index 6f83d69a251cde..b6793c0a92258d 100644 --- a/Mathlib/Tactic/Linter/DeprecatedSyntaxLinter.lean +++ b/Mathlib/Tactic/Linter/DeprecatedSyntaxLinter.lean @@ -181,7 +181,7 @@ def getDeprecatedSyntax : Syntax → Array (SyntaxNodeKind × Syntax × MessageD rargs.push (kind, stx, "Using `native_decide` is not allowed in mathlib: \ because it trusts the entire Lean compiler (not just the Lean kernel), \ it could quite possibly be used to prove false.") - | `Inclusion.inclusionTacStx => + | `Inclusion.inclusion => if usesNativeConfig stx then rargs.push (kind, stx, "Using `inclusion +native` is not allowed in mathlib: \ because it trusts the entire Lean compiler (not just the Lean kernel), \ @@ -250,7 +250,7 @@ def deprecatedSyntaxLinter : Linter where run stx := do | `Mathlib.Tactic.induction' => Linter.logLintIf linter.style.induction stx' msg | ``Lean.Parser.Tactic.tacticAdmit => Linter.logLintIf linter.style.admit stx' msg | ``Lean.Parser.Tactic.nativeDecide | ``Lean.Parser.Tactic.decide | - `Inclusion.inclusionTacStx | `Inclusion.dyadicInterval => + `Inclusion.inclusion | `Inclusion.dyadicInterval => Linter.logLintIf linter.style.native stx' msg | `MaxHeartbeats => Linter.logLintIf linter.style.maxHeartbeats stx' msg | _ => continue) stx From fbe6dd8624718c349afddb8d92aa221a5cee627c Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Fri, 28 Aug 2026 01:49:42 -0400 Subject: [PATCH 15/38] remove coe --- Mathlib/Tactic/Inclusion/Core/ToSet.lean | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/Mathlib/Tactic/Inclusion/Core/ToSet.lean b/Mathlib/Tactic/Inclusion/Core/ToSet.lean index 8f8deb6542ab09..317be2620975e0 100644 --- a/Mathlib/Tactic/Inclusion/Core/ToSet.lean +++ b/Mathlib/Tactic/Inclusion/Core/ToSet.lean @@ -26,15 +26,11 @@ class ToSet (Iα : Type*) (α : outParam Type*) where /-- The mapping of elements of `Iα` to sets in `α`. -/ toSet : Iα → Set α -instance {Iα α : Type*} [ToSet Iα α] : CoeTC Iα (Set α) where - coe := ToSet.toSet - instance {Iα α : Type*} [ToSet Iα α] : Membership α Iα where mem s a := ToSet.toSet s a -@[simp] -lemma mem_toSet_iff_mem {Iα α : Type*} [ToSet Iα α] (a : α) (s : Iα) : - a ∈ (s : Set α) ↔ a ∈ s := Iff.rfl +lemma ToSet.mem_def {Iα α : Type*} [ToSet Iα α] (a : α) (s : Iα) : + a ∈ s ↔ a ∈ ToSet.toSet s := Iff.rfl lemma ToSet.mem_of_eq_of_mem {Iα α : Type*} [ToSet Iα α] {x y : α} {s : Iα} (hxy : x = y) (hy : y ∈ s) : x ∈ s := hxy ▸ hy @@ -88,8 +84,6 @@ structure Cover (Iα α : Type*) [ToSet Iα α] where mem_coverMap {Iβ β : Type u} [ToSet Iβ β] [Coarsen Iβ β] {s : Iα} {F : Iα → Iβ} {x : α} {y : β} (hx : x ∈ s) (hy : ∀ t, x ∈ t → y ∈ F t) : y ∈ coverMap s F -open ToSet - section IntervalBool /-- An `IntervalBool` represents the result of a `Prop` inclusion and is either @@ -112,11 +106,11 @@ instance : ToSet IntervalBool Prop := ⟨IntervalBool.toPropSet⟩ @[simp] theorem IntervalBool.mem_true_iff {p : Prop} : p ∈ IntervalBool.true ↔ p := by - simp [← mem_toSet_iff_mem, ToSet.toSet, IntervalBool.toPropSet] + simp [ToSet.mem_def, ToSet.toSet, IntervalBool.toPropSet] @[simp] theorem IntervalBool.mem_false_iff {p : Prop} : p ∈ IntervalBool.false ↔ ¬p := by - simp [← mem_toSet_iff_mem, ToSet.toSet, IntervalBool.toPropSet] + simp [ToSet.mem_def, ToSet.toSet, IntervalBool.toPropSet] theorem IntervalBool.mem_true {p : Prop} (hp : p) : p ∈ IntervalBool.true := IntervalBool.mem_true_iff.mpr hp @@ -126,7 +120,7 @@ theorem IntervalBool.mem_false {p : Prop} (hp : ¬p) : p ∈ IntervalBool.false @[simp] theorem IntervalBool.mem_undetermined (p : Prop) : p ∈ IntervalBool.undetermined := by - by_cases hp : p <;> simp [← mem_toSet_iff_mem, ToSet.toSet, IntervalBool.toPropSet, hp] + simpa [ToSet.mem_def, ToSet.toSet, IntervalBool.toPropSet] using Classical.em p /-- Negation of an `IntervalBool` value. -/ def IntervalBool.not : IntervalBool → IntervalBool From 80c2c74314a7eeaa4ac5049cb2bc6742dd082db5 Mon Sep 17 00:00:00 2001 From: DavidLedvinka <50644608+DavidLedvinka@users.noreply.github.com> Date: Sat, 29 Aug 2026 04:11:22 +0100 Subject: [PATCH 16/38] Update Mathlib/Tactic/Inclusion/Core/Elab.lean Co-authored-by: Thomas R. Murrills <68410468+thorimur@users.noreply.github.com> --- Mathlib/Tactic/Inclusion/Core/Elab.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mathlib/Tactic/Inclusion/Core/Elab.lean b/Mathlib/Tactic/Inclusion/Core/Elab.lean index 0fcb29e0e8e036..0c8317d1bdec83 100644 --- a/Mathlib/Tactic/Inclusion/Core/Elab.lean +++ b/Mathlib/Tactic/Inclusion/Core/Elab.lean @@ -81,7 +81,7 @@ def inclusion?Tac : Tactic discard <| inclusionCore (← getMainTarget) config logInfo "The inclusion check succeeded." catch err => - logInfo m!"The inclusion check failed:\n{err.toMessageData}" + logError m!"The inclusion check failed:\n{err.toMessageData}" | _ => throwUnsupportedSyntax end Inclusion From 32d6348a3faf793a05a0662c8d052ae0171d5330 Mon Sep 17 00:00:00 2001 From: DavidLedvinka <50644608+DavidLedvinka@users.noreply.github.com> Date: Sat, 29 Aug 2026 04:18:14 +0100 Subject: [PATCH 17/38] Update Mathlib/Tactic/Inclusion/Core/Expr.lean Co-authored-by: Thomas R. Murrills <68410468+thorimur@users.noreply.github.com> --- Mathlib/Tactic/Inclusion/Core/Expr.lean | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mathlib/Tactic/Inclusion/Core/Expr.lean b/Mathlib/Tactic/Inclusion/Core/Expr.lean index b2d72ef5f79691..016c942f92f7ef 100644 --- a/Mathlib/Tactic/Inclusion/Core/Expr.lean +++ b/Mathlib/Tactic/Inclusion/Core/Expr.lean @@ -24,8 +24,8 @@ namespace Inclusion /-- If `e` is an `Expr` of the form `x ∈ s` using a `ToSet` instance, return `some (x, s, toSetInst)`. -/ def toSetMem? (e : Expr) : Option (Expr × Expr × Expr) := do - let (``Membership.mem, #[_, _, membershipInst, s, x]) := e.getAppFnArgs | none - let (``instMembershipOfToSet, #[_, _, toSetInst]) := membershipInst.getAppFnArgs | none + let_expr Membership.mem _ _ membershipInst s x := e | none + let_expr instMembershipOfToSet _ _ toSetInst := membershipInst | none return (x, s, toSetInst) /-- Given expressions `x : xType`, `s : setType`, and `toSetInst : ToSet setType xType`, create From faf14a5e5a3f6708d75f0c7053f2b6e9dc0159f6 Mon Sep 17 00:00:00 2001 From: DavidLedvinka <50644608+DavidLedvinka@users.noreply.github.com> Date: Sat, 29 Aug 2026 05:11:33 +0100 Subject: [PATCH 18/38] Update Mathlib/Tactic/Inclusion/Core/Inclusion.lean Co-authored-by: Thomas R. Murrills <68410468+thorimur@users.noreply.github.com> --- Mathlib/Tactic/Inclusion/Core/Inclusion.lean | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Mathlib/Tactic/Inclusion/Core/Inclusion.lean b/Mathlib/Tactic/Inclusion/Core/Inclusion.lean index f07f0f62f5e1c0..cb1fc0135b9905 100644 --- a/Mathlib/Tactic/Inclusion/Core/Inclusion.lean +++ b/Mathlib/Tactic/Inclusion/Core/Inclusion.lean @@ -54,21 +54,18 @@ def mkExprInclusionBody (e : Expr) : InclusionM ExprInclusionBody := do "Matched inclusion extensions (in order of priority):\n{ppMatchedExts matchedExts}" let savedState ← saveState for ext in matchedExts do - let body? : Option ExprInclusionBody ← withTraceNode `Tactic.inclusion - (fun _ => do return m!"Trying {ppExtensionName ext.family ext.userName}") do + withTraceNode `Tactic.inclusion + (fun _ => do return m!"Trying {ppExtensionName ext.family ext.userName}") do← try let body ← ext.derive e recordExtraModUseFromDecl (isMeta := true) ext.declName - return some body + trace[Tactic.inclusion] "Inclusion body:\n {← ppInclusionExpr body.inclusionBody}" + return body catch err => trace[Tactic.inclusion] "Failed to apply {ppExtensionName ext.family ext.userName} to {e}: \ {err.toMessageData}" restoreState savedState - return none - if let some body := body? then - trace[Tactic.inclusion] "Inclusion body:\n {← ppInclusionExpr body.inclusionBody}" - return body throwError "No inclusion extension applies to {e}" /-- Check that `body.proofBody` is a proof of `e ∈ body.inclusionBody` and infer its `IType`. -/ From 01c0481f78914d24661f33ffdef4eee87c4bbcce Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Sat, 29 Aug 2026 00:51:39 -0400 Subject: [PATCH 19/38] suggestions --- Mathlib/Tactic/Inclusion/Core/ToSet.lean | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Mathlib/Tactic/Inclusion/Core/ToSet.lean b/Mathlib/Tactic/Inclusion/Core/ToSet.lean index 317be2620975e0..12e0e4d799406b 100644 --- a/Mathlib/Tactic/Inclusion/Core/ToSet.lean +++ b/Mathlib/Tactic/Inclusion/Core/ToSet.lean @@ -133,6 +133,7 @@ theorem IntervalBool.not_mem {p : Prop} {a : IntervalBool} cases a <;> by_cases hp' : p <;> simp_all [IntervalBool.not] /-- Conjunction of two `IntervalBool` values. -/ +@[macro_inline] def IntervalBool.and : IntervalBool → IntervalBool → IntervalBool | .true, .true => .true | .false, _ | _, .false => .false @@ -143,6 +144,7 @@ theorem IntervalBool.and_mem {p q : Prop} {a b : IntervalBool} cases a <;> cases b <;> simp_all [IntervalBool.and] /-- Disjunction of two `IntervalBool` values. -/ +@[macro_inline] def IntervalBool.or : IntervalBool → IntervalBool → IntervalBool | .true, _ | _, .true => .true | .false, .false => .false @@ -170,6 +172,7 @@ theorem IntervalBool.eq_true_of_isTrue_eq_true {b : IntervalBool} cases b <;> simp_all [IntervalBool.isTrue] /-- Union of two `IntervalBool`s. -/ +@[macro_inline] def IntervalBool.union : IntervalBool → IntervalBool → IntervalBool | .true, .true => .true | .false, .false => .false From cf0ce72f705f94b94482d5e6d9fd543f4be7ef1c Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Sat, 29 Aug 2026 13:53:46 -0400 Subject: [PATCH 20/38] lower_snake --- .../Tactic/Inclusion/Extension/Core/Core.lean | 8 ++-- .../Extension/IntervalDyadicReal/Basic.lean | 36 ++++++++-------- .../IntervalDyadicReal/BinarySplit.lean | 2 +- .../IntervalDyadicReal/Hypotheses.lean | 24 +++++------ .../IntervalDyadicReal/Rational.lean | 6 +-- .../Tactic/Inclusion/ExtensionAPI/Attr.lean | 42 +++++++++---------- 6 files changed, 59 insertions(+), 59 deletions(-) diff --git a/Mathlib/Tactic/Inclusion/Extension/Core/Core.lean b/Mathlib/Tactic/Inclusion/Extension/Core/Core.lean index 993e1ae034500f..3d77935764feb3 100644 --- a/Mathlib/Tactic/Inclusion/Extension/Core/Core.lean +++ b/Mathlib/Tactic/Inclusion/Extension/Core/Core.lean @@ -20,11 +20,11 @@ open Lean Meta namespace Inclusion -attribute [inclusionOp core] IntervalBool.not_mem IntervalBool.and_mem IntervalBool.or_mem -attribute [hypothesisOp core] ToSet.mem_of_eq_of_mem ToSet.mem_of_mem_of_eq +attribute [inclusion_op core] IntervalBool.not_mem IntervalBool.and_mem IntervalBool.or_mem +attribute [hypothesis_op core] ToSet.mem_of_eq_of_mem ToSet.mem_of_mem_of_eq /-- `HypothesisExt` for direct `ToSet` instance membership hypotheses. -/ -@[hypothesisExt _ ∈ _] +@[hypothesis_ext _ ∈ _] def instMembershipHyp : HypothesisExt where family := `core derive h := do @@ -35,7 +35,7 @@ def instMembershipHyp : HypothesisExt where addInclusionHyp iVar.iExpr ⟨set, h⟩ /-- `HypothesisExt` for conjunction hypotheses. -/ -@[hypothesisExt _ ∧ _] +@[hypothesis_ext _ ∧ _] def andHyp : HypothesisExt where family := `core derive h := do diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Basic.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Basic.lean index 551f0d2a13cbc2..fbf7193b550f1c 100644 --- a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Basic.lean +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Basic.lean @@ -21,7 +21,7 @@ namespace Inclusion namespace IntervalDyadicReal /-- Construct an inclusion variable for a real expression using a dyadic interval. -/ -@[inclusionExt(_ : ℝ)] +@[inclusion_ext(_ : ℝ)] meta def mkRealIVar : InclusionExt := mkNDIVarExt `interval_dyadic_real ⟨q(ℝ), q(Interval Dyadic), q(instToSetIntervalDyadicReal)⟩ mkBinarySplitCover @@ -32,16 +32,16 @@ theorem mem_iff_mem_map {x : ℝ} {I : Interval Dyadic} : x ∈ I ↔ x ∈ I.ma section Constants -@[inclusionOp interval_dyadic_real] +@[inclusion_op interval_dyadic_real] theorem natCast_mem (n : ℕ) : (n : ℝ) ∈ Interval.singleton (n : Dyadic) := by simpa [mem_iff_mem_map] using Interval.mem_map_singleton (n : Dyadic) Dyadic.toReal -@[inclusionOp interval_dyadic_real] +@[inclusion_op interval_dyadic_real] theorem ofNat_mem (n : ℕ) : (OfNat.ofNat n : ℝ) ∈ Interval.singleton (n : Dyadic) := by rw [Semiring.toGrindSemiring_ofNat] exact natCast_mem n -@[inclusionOp interval_dyadic_real] +@[inclusion_op interval_dyadic_real] theorem intCast_mem (z : ℤ) : (z : ℝ) ∈ Interval.singleton (z : Dyadic) := by simpa [mem_iff_mem_map] using Interval.mem_map_singleton (z : Dyadic) Dyadic.toReal @@ -49,15 +49,15 @@ end Constants section Arithmetic -@[inclusionOp interval_dyadic_real] +@[inclusion_op interval_dyadic_real] theorem add_mem {x y : ℝ} {I J : Interval Dyadic} (hx : x ∈ I) (hy : y ∈ J) : x + y ∈ I.add J := Interval.add_mem Dyadic.toRealAddMonoidHom hx hy -@[inclusionOp interval_dyadic_real] +@[inclusion_op interval_dyadic_real] theorem neg_mem {x : ℝ} {I : Interval Dyadic} (hx : x ∈ I) : -x ∈ I.neg := Interval.neg_mem Dyadic.toRealAddMonoidHom hx -@[inclusionOp interval_dyadic_real] +@[inclusion_op interval_dyadic_real] theorem sub_mem {x y : ℝ} {I J : Interval Dyadic} (hx : x ∈ I) (hy : y ∈ J) : x - y ∈ I.sub J := Interval.sub_mem Dyadic.toRealAddMonoidHom hx hy @@ -65,57 +65,57 @@ end Arithmetic section Props -@[inclusionOp interval_dyadic_real] +@[inclusion_op interval_dyadic_real] theorem le_mem {x y : ℝ} {I J : Interval Dyadic} (hx : x ∈ I) (hy : y ∈ J) : (x ≤ y) ∈ I.le J := Interval.le_mem Dyadic.toRealOrderEmbedding hx hy -@[inclusionOp interval_dyadic_real] +@[inclusion_op interval_dyadic_real] theorem lt_mem {x y : ℝ} {I J : Interval Dyadic} (hx : x ∈ I) (hy : y ∈ J) : (x < y) ∈ I.lt J := Interval.lt_mem Dyadic.toRealOrderEmbedding hx hy -@[inclusionOp interval_dyadic_real] +@[inclusion_op interval_dyadic_real] theorem eq_mem {x y : ℝ} {I J : Interval Dyadic} (hx : x ∈ I) (hy : y ∈ J) : (x = y) ∈ I.eq J := Interval.eq_mem Dyadic.toRealOrderEmbedding hx hy -@[inclusionOp interval_dyadic_real] +@[inclusion_op interval_dyadic_real] theorem mem_Ici {a x : ℝ} {I J : Interval Dyadic} (ha : a ∈ I) (hx : x ∈ J) : (x ∈ Set.Ici a) ∈ I.le J := Interval.mem_Ici Dyadic.toRealOrderEmbedding ha hx -@[inclusionOp interval_dyadic_real] +@[inclusion_op interval_dyadic_real] theorem mem_Ioi {a x : ℝ} {I J : Interval Dyadic} (ha : a ∈ I) (hx : x ∈ J) : (x ∈ Set.Ioi a) ∈ I.lt J := Interval.mem_Ioi Dyadic.toRealOrderEmbedding ha hx -@[inclusionOp interval_dyadic_real] +@[inclusion_op interval_dyadic_real] theorem mem_Iic {b x : ℝ} {I J : Interval Dyadic} (hx : x ∈ I) (hb : b ∈ J) : (x ∈ Set.Iic b) ∈ I.le J := Interval.mem_Iic Dyadic.toRealOrderEmbedding hx hb -@[inclusionOp interval_dyadic_real] +@[inclusion_op interval_dyadic_real] theorem mem_Iio {b x : ℝ} {I J : Interval Dyadic} (hx : x ∈ I) (hb : b ∈ J) : (x ∈ Set.Iio b) ∈ I.lt J := Interval.mem_Iio Dyadic.toRealOrderEmbedding hx hb -@[inclusionOp interval_dyadic_real] +@[inclusion_op interval_dyadic_real] theorem mem_Icc {a b x : ℝ} {I J K : Interval Dyadic} (ha : a ∈ I) (hx : x ∈ J) (hb : b ∈ K) : (x ∈ Set.Icc a b) ∈ (I.le J).and (J.le K) := Interval.mem_Icc Dyadic.toRealOrderEmbedding ha hx hb -@[inclusionOp interval_dyadic_real] +@[inclusion_op interval_dyadic_real] theorem mem_Ico {a b x : ℝ} {I J K : Interval Dyadic} (ha : a ∈ I) (hx : x ∈ J) (hb : b ∈ K) : (x ∈ Set.Ico a b) ∈ (I.le J).and (J.lt K) := Interval.mem_Ico Dyadic.toRealOrderEmbedding ha hx hb -@[inclusionOp interval_dyadic_real] +@[inclusion_op interval_dyadic_real] theorem mem_Ioc {a b x : ℝ} {I J K : Interval Dyadic} (ha : a ∈ I) (hx : x ∈ J) (hb : b ∈ K) : (x ∈ Set.Ioc a b) ∈ (I.lt J).and (J.le K) := Interval.mem_Ioc Dyadic.toRealOrderEmbedding ha hx hb -@[inclusionOp interval_dyadic_real] +@[inclusion_op interval_dyadic_real] theorem mem_Ioo {a b x : ℝ} {I J K : Interval Dyadic} (ha : a ∈ I) (hx : x ∈ J) (hb : b ∈ K) : (x ∈ Set.Ioo a b) ∈ (I.lt J).and (J.lt K) := Interval.mem_Ioo Dyadic.toRealOrderEmbedding ha hx hb diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/BinarySplit.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/BinarySplit.lean index 528a274d02bb08..23e53162d808c7 100644 --- a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/BinarySplit.lean +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/BinarySplit.lean @@ -59,7 +59,7 @@ def binarySplit (n : ℕ) : Cover (Interval Dyadic) ℝ where /-- The depth to which bounded dyadic intervals are repeatedly bisected. A depth of `n` produces `2 ^ n` pieces. -/ -@[inclusionParam] +@[inclusion_param] meta def binarySplitParam : InclusionParamDecl where name := `binSplit type := q(ℕ) diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Hypotheses.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Hypotheses.lean index bce0235fcc723d..e204ee15ea99aa 100644 --- a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Hypotheses.lean +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Hypotheses.lean @@ -20,54 +20,54 @@ namespace Inclusion namespace IntervalDyadicReal -@[hypothesisOp interval_dyadic_real] +@[hypothesis_op interval_dyadic_real] theorem Iic_mem_of_le {x y : ℝ} {I : Interval Dyadic} (hxy : x ≤ y) (hy : y ∈ I) : x ∈ Interval.Iic I.ub := Interval.mem_Iic_of_le hxy hy -@[hypothesisOp interval_dyadic_real] +@[hypothesis_op interval_dyadic_real] theorem Ici_mem_of_le {x y : ℝ} {I : Interval Dyadic} (hxy : x ≤ y) (hx : x ∈ I) : y ∈ Interval.Ici I.lb := Interval.mem_Ici_of_le hxy hx -@[hypothesisOp interval_dyadic_real] +@[hypothesis_op interval_dyadic_real] theorem Iic_mem_of_lt {x y : ℝ} {I : Interval Dyadic} (hxy : x < y) (hy : y ∈ I) : x ∈ Interval.Iic I.ub := Interval.mem_Iic_of_lt hxy hy -@[hypothesisOp interval_dyadic_real] +@[hypothesis_op interval_dyadic_real] theorem Ici_mem_of_lt {x y : ℝ} {I : Interval Dyadic} (hxy : x < y) (hx : x ∈ I) : y ∈ Interval.Ici I.lb := Interval.mem_Ici_of_lt hxy hx -@[hypothesisOp interval_dyadic_real] +@[hypothesis_op interval_dyadic_real] theorem Ici_mem_of_mem_Ici {a x : ℝ} {I : Interval Dyadic} (hx : x ∈ Set.Ici a) (ha : a ∈ I) : x ∈ Interval.Ici I.lb := Interval.mem_Ici_of_mem_Ici hx ha -@[hypothesisOp interval_dyadic_real] +@[hypothesis_op interval_dyadic_real] theorem Ici_mem_of_mem_Ioi {a x : ℝ} {I : Interval Dyadic} (hx : x ∈ Set.Ioi a) (ha : a ∈ I) : x ∈ Interval.Ici I.lb := Interval.mem_Ici_of_mem_Ioi hx ha -@[hypothesisOp interval_dyadic_real] +@[hypothesis_op interval_dyadic_real] theorem Iic_mem_of_mem_Iic {b x : ℝ} {I : Interval Dyadic} (hx : x ∈ Set.Iic b) (hb : b ∈ I) : x ∈ Interval.Iic I.ub := Interval.mem_Iic_of_mem_Iic hx hb -@[hypothesisOp interval_dyadic_real] +@[hypothesis_op interval_dyadic_real] theorem Iic_mem_of_mem_Iio {b x : ℝ} {I : Interval Dyadic} (hx : x ∈ Set.Iio b) (hb : b ∈ I) : x ∈ Interval.Iic I.ub := Interval.mem_Iic_of_mem_Iio hx hb -@[hypothesisOp interval_dyadic_real] +@[hypothesis_op interval_dyadic_real] theorem Icc_mem_of_mem_Ico {a b x : ℝ} {I J : Interval Dyadic} (hx : x ∈ Set.Ico a b) (ha : a ∈ I) (hb : b ∈ J) : x ∈ Interval.Icc I.lb J.ub := Interval.mem_Icc_of_mem_Ico hx ha hb -@[hypothesisOp interval_dyadic_real] +@[hypothesis_op interval_dyadic_real] theorem Icc_mem_of_mem_Ioc {a b x : ℝ} {I J : Interval Dyadic} (hx : x ∈ Set.Ioc a b) (ha : a ∈ I) (hb : b ∈ J) : x ∈ Interval.Icc I.lb J.ub := Interval.mem_Icc_of_mem_Ioc hx ha hb -@[hypothesisOp interval_dyadic_real] +@[hypothesis_op interval_dyadic_real] theorem Icc_mem_of_mem_Icc {a b x : ℝ} {I J : Interval Dyadic} (hx : x ∈ Set.Icc a b) (ha : a ∈ I) (hb : b ∈ J) : x ∈ Interval.Icc I.lb J.ub := Interval.mem_Icc_of_mem_Icc hx ha hb -@[hypothesisOp interval_dyadic_real] +@[hypothesis_op interval_dyadic_real] theorem Icc_mem_of_mem_Ioo {a b x : ℝ} {I J : Interval Dyadic} (hx : x ∈ Set.Ioo a b) (ha : a ∈ I) (hb : b ∈ J) : x ∈ Interval.Icc I.lb J.ub := Interval.mem_Icc_of_mem_Ioo hx ha hb diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Rational.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Rational.lean index d53b73234900db..64c7cced5d5bc6 100644 --- a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Rational.lean +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Rational.lean @@ -24,7 +24,7 @@ namespace Inclusion namespace IntervalDyadicReal /-- The precision of dyadic approximations, defaulting to zero. -/ -@[inclusionParam] +@[inclusion_param] def precParam : InclusionParamDecl where name := `prec type := q(Nat) @@ -48,7 +48,7 @@ def rat (x : ℚ) (prec : ℕ) : Interval Dyadic := let upper := if lower.toRat = x then lower else lower + Dyadic.step prec Interval.Icc lower upper -@[inclusionOp interval_dyadic_real] +@[inclusion_op interval_dyadic_real] theorem ratCast_mem (q : ℚ) (prec : ℕ) : (q : ℝ) ∈ rat q prec := by apply Interval.mem_map_Icc Dyadic.toReal · exact Rat.cast_le.mpr Rat.toRat_toDyadic_le @@ -85,7 +85,7 @@ def scientific (m : ℕ) (s : Bool) (e prec : ℕ) : Interval Dyadic := else Interval.singleton (m * (10 : Dyadic) ^ e) -@[inclusionOp interval_dyadic_real] +@[inclusion_op interval_dyadic_real] theorem scientific_mem (m : ℕ) (s : Bool) (e prec : ℕ) : (OfScientific.ofScientific (α := ℝ) m s e) ∈ scientific m s e prec := by cases s diff --git a/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean b/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean index e0abfd8642e261..cc33b7cd9a758e 100644 --- a/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean +++ b/Mathlib/Tactic/Inclusion/ExtensionAPI/Attr.lean @@ -19,8 +19,8 @@ open Lean Meta Elab Term DiscrTreeExt namespace Inclusion -/-- Syntax for registering an inclusion parameter using the `inclusionParam` attribute. -/ -syntax (name := inclusionParamAttr) "inclusionParam" : attr +/-- Syntax for registering an inclusion parameter using the `inclusion_param` attribute. -/ +syntax (name := inclusionParamAttr) "inclusion_param" : attr /-- Validate an inclusion parameter declaration. -/ private def validateInclusionParamDecl (decl : InclusionParamDecl) : MetaM Unit := do @@ -34,9 +34,9 @@ private def validateInclusionParamDecl (decl : InclusionParamDecl) : MetaM Unit /-- Add the inclusion parameter declared by `declName`. -/ def addInclusionParam (declName : Name) (kind : AttributeKind) : AttrM Unit := do let env ← getEnv - ensureAttrDeclIsMeta `inclusionParam declName kind + ensureAttrDeclIsMeta `inclusion_param declName kind unless (env.getModuleIdxFor? declName).isNone do - throwAttrDeclInImportedModule `inclusionParam declName + throwAttrDeclInImportedModule `inclusion_param declName if (IR.getSorryDep env declName).isSome then return let decl ← mkInclusionParamDecl declName MetaM.run' <| validateInclusionParamDecl decl @@ -52,8 +52,8 @@ initialize registerBuiltinAttribute { add := fun declName _ kind => addInclusionParam declName kind } -/-- Syntax for declaring an inclusion extension using the `inclusionExt` attribute. -/ -syntax (name := inclusionExtAttr) "inclusionExt" term,+ : attr +/-- Syntax for declaring an inclusion extension using the `inclusion_ext` attribute. -/ +syntax (name := inclusionExtAttr) "inclusion_ext" term,+ : attr /-- Add the inclusion extension `declName` under `keys`. -/ def addInclusionExt (declName : Name) (keys : Array (Array DiscrTree.Key)) @@ -67,11 +67,11 @@ initialize registerBuiltinAttribute { descr := "adds an inclusion extension" applicationTime := .afterCompilation add := fun declName stx kind => match stx with - | `(attr| inclusionExt $es,*) => do + | `(attr| inclusion_ext $es,*) => do let env ← getEnv - ensureAttrDeclIsMeta `inclusionExt declName kind + ensureAttrDeclIsMeta `inclusion_ext declName kind unless (env.getModuleIdxFor? declName).isNone do - throwAttrDeclInImportedModule `inclusionExt declName + throwAttrDeclInImportedModule `inclusion_ext declName if (IR.getSorryDep env declName).isSome then return let keys ← elabExtKeys (es.getElems.map (·.raw)) addInclusionExt declName keys kind @@ -79,8 +79,8 @@ initialize registerBuiltinAttribute { erase := fun _ => throwError "Inclusion extensions cannot be erased by declaration" } -/-- Syntax for declaring a hypothesis extension using the `hypothesisExt` attribute. -/ -syntax (name := hypothesisExtAttr) "hypothesisExt" term,+ : attr +/-- Syntax for declaring a hypothesis extension using the `hypothesis_ext` attribute. -/ +syntax (name := hypothesisExtAttr) "hypothesis_ext" term,+ : attr /-- Add the hypothesis extension `declName` under `keys`. -/ def addHypothesisExt (declName : Name) (keys : Array (Array DiscrTree.Key)) @@ -89,17 +89,17 @@ def addHypothesisExt (declName : Name) (keys : Array (Array DiscrTree.Key)) let family ← getInclusionFamily ext.family family.hypothesisExt.add ((keys, declName), ext) kind -/-- Register the `hypothesisExt` attribute. -/ +/-- Register the `hypothesis_ext` attribute. -/ initialize registerBuiltinAttribute { name := `hypothesisExtAttr descr := "adds a hypothesis extension" applicationTime := .afterCompilation add := fun declName stx kind => match stx with - | `(attr| hypothesisExt $es,*) => do + | `(attr| hypothesis_ext $es,*) => do let env ← getEnv - ensureAttrDeclIsMeta `hypothesisExt declName kind + ensureAttrDeclIsMeta `hypothesis_ext declName kind unless (env.getModuleIdxFor? declName).isNone do - throwAttrDeclInImportedModule `hypothesisExt declName + throwAttrDeclInImportedModule `hypothesis_ext declName if (IR.getSorryDep env declName).isSome then return let keys ← elabExtKeys (es.getElems.map (·.raw)) addHypothesisExt declName keys kind @@ -245,16 +245,16 @@ private def addInclusionOp (theoremName familyName : Name) (priority : Nat) addAndCompile (.defnDecl decl) (markMeta := true) addInclusionExt extName #[path] kind -/-- Syntax for registering an inclusion extension from a theorem using the `inclusionOp` +/-- Syntax for registering an inclusion extension from a theorem using the `inclusion_op` attribute. -/ -syntax (name := inclusionOpAttr) "inclusionOp " ident (prio)? : attr +syntax (name := inclusionOpAttr) "inclusion_op " ident (prio)? : attr initialize registerBuiltinAttribute { name := `inclusionOpAttr descr := "adds an inclusion operation" applicationTime := .afterCompilation add := fun declName stx kind => match stx with - | `(attr| inclusionOp $familyName:ident $[$_prio:prio]?) => do + | `(attr| inclusion_op $familyName:ident $[$_prio:prio]?) => do if (IR.getSorryDep (← getEnv) declName).isSome then return addInclusionOp declName familyName.getId (← getAttrParamOptPrio stx[2]) kind | _ => throwUnsupportedSyntax @@ -307,16 +307,16 @@ private def addHypothesisOp (theoremName familyName : Name) (priority : Nat) addAndCompile (.defnDecl decl) (markMeta := true) addHypothesisExt extName #[path] kind -/-- Syntax for registering a hypothesis extension from a theorem using the `hypothesisOp` +/-- Syntax for registering a hypothesis extension from a theorem using the `hypothesis_op` attribute. -/ -syntax (name := hypothesisOpAttr) "hypothesisOp " ident (prio)? : attr +syntax (name := hypothesisOpAttr) "hypothesis_op " ident (prio)? : attr initialize registerBuiltinAttribute { name := `hypothesisOpAttr descr := "adds an inclusion-hypothesis operation" applicationTime := .afterCompilation add := fun declName stx kind => match stx with - | `(attr| hypothesisOp $familyName:ident $[$_prio:prio]?) => do + | `(attr| hypothesis_op $familyName:ident $[$_prio:prio]?) => do if (IR.getSorryDep (← getEnv) declName).isSome then return addHypothesisOp declName familyName.getId (← getAttrParamOptPrio stx[2]) kind | _ => throwUnsupportedSyntax From 9facecaa41843e14832552afd3efc01677ce43e4 Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Mon, 31 Aug 2026 23:45:20 +0100 Subject: [PATCH 21/38] move discr_tree_ext --- Mathlib.lean | 2 +- Mathlib/{Lean/Meta => Tactic/Inclusion/Core}/DiscrTreeExt.lean | 0 Mathlib/Tactic/Inclusion/Core/Extensions.lean | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename Mathlib/{Lean/Meta => Tactic/Inclusion/Core}/DiscrTreeExt.lean (100%) diff --git a/Mathlib.lean b/Mathlib.lean index 40738b9da0fa38..5d052157d48557 100644 --- a/Mathlib.lean +++ b/Mathlib.lean @@ -5037,7 +5037,6 @@ public import Mathlib.Lean.Meta public import Mathlib.Lean.Meta.Basic public import Mathlib.Lean.Meta.CongrTheorems public import Mathlib.Lean.Meta.DiscrTree -public import Mathlib.Lean.Meta.DiscrTreeExt public import Mathlib.Lean.Meta.KAbstractPositions public import Mathlib.Lean.Meta.RefinedDiscrTree public import Mathlib.Lean.Meta.RefinedDiscrTree.Basic @@ -7527,6 +7526,7 @@ public import Mathlib.Tactic.HigherOrder public import Mathlib.Tactic.Hint public import Mathlib.Tactic.ITauto public import Mathlib.Tactic.Inclusion.Core.Core +public import Mathlib.Tactic.Inclusion.Core.DiscrTreeExt public import Mathlib.Tactic.Inclusion.Core.Elab public import Mathlib.Tactic.Inclusion.Core.Expr public import Mathlib.Tactic.Inclusion.Core.Extensions diff --git a/Mathlib/Lean/Meta/DiscrTreeExt.lean b/Mathlib/Tactic/Inclusion/Core/DiscrTreeExt.lean similarity index 100% rename from Mathlib/Lean/Meta/DiscrTreeExt.lean rename to Mathlib/Tactic/Inclusion/Core/DiscrTreeExt.lean diff --git a/Mathlib/Tactic/Inclusion/Core/Extensions.lean b/Mathlib/Tactic/Inclusion/Core/Extensions.lean index e71319fb4ff1e1..c623cb15ab9044 100644 --- a/Mathlib/Tactic/Inclusion/Core/Extensions.lean +++ b/Mathlib/Tactic/Inclusion/Core/Extensions.lean @@ -5,8 +5,8 @@ Authors: David Ledvinka -/ module +public meta import Mathlib.Tactic.Inclusion.Core.DiscrTreeExt public meta import Mathlib.Tactic.Inclusion.Core.Types -public meta import Mathlib.Lean.Meta.DiscrTreeExt /-! # Environment extensions for the `inclusion` tactic From 25f0cdf8b4d8d69e052da1303c6b9021c4f6466f Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Mon, 31 Aug 2026 23:58:17 +0100 Subject: [PATCH 22/38] mk_all --- Mathlib/Tactic.lean | 1 + 1 file changed, 1 insertion(+) diff --git a/Mathlib/Tactic.lean b/Mathlib/Tactic.lean index b26d63f1bcc5d5..fd4e64cd59c761 100644 --- a/Mathlib/Tactic.lean +++ b/Mathlib/Tactic.lean @@ -161,6 +161,7 @@ public import Mathlib.Tactic.HigherOrder public import Mathlib.Tactic.Hint public import Mathlib.Tactic.ITauto public import Mathlib.Tactic.Inclusion.Core.Core +public import Mathlib.Tactic.Inclusion.Core.DiscrTreeExt public import Mathlib.Tactic.Inclusion.Core.Elab public import Mathlib.Tactic.Inclusion.Core.Expr public import Mathlib.Tactic.Inclusion.Core.Extensions From 146d9949498c513a42a3279d6ad67c878c2a245e Mon Sep 17 00:00:00 2001 From: DavidLedvinka <50644608+DavidLedvinka@users.noreply.github.com> Date: Wed, 2 Sep 2026 17:50:31 +0100 Subject: [PATCH 23/38] Update Mathlib/Tactic/Inclusion/Core/Elab.lean Co-authored-by: Anne Baanen --- Mathlib/Tactic/Inclusion/Core/Elab.lean | 1 - 1 file changed, 1 deletion(-) diff --git a/Mathlib/Tactic/Inclusion/Core/Elab.lean b/Mathlib/Tactic/Inclusion/Core/Elab.lean index 0c8317d1bdec83..ad252853db4d17 100644 --- a/Mathlib/Tactic/Inclusion/Core/Elab.lean +++ b/Mathlib/Tactic/Inclusion/Core/Elab.lean @@ -20,7 +20,6 @@ open Lean.Parser.Tactic namespace Inclusion -/-- Declare elaborator for the configuration `InclusionConfig` of the `inclusion` tactic. -/ declare_config_elab elabInclusionConfig InclusionConfig where omit paramSettings, families From 81f8eaa1a70699ed7ee582dc3ff74dcf80fbece3 Mon Sep 17 00:00:00 2001 From: DavidLedvinka <50644608+DavidLedvinka@users.noreply.github.com> Date: Wed, 2 Sep 2026 17:50:54 +0100 Subject: [PATCH 24/38] Update Mathlib/Tactic/Inclusion/Core/Elab.lean Co-authored-by: Anne Baanen --- Mathlib/Tactic/Inclusion/Core/Elab.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mathlib/Tactic/Inclusion/Core/Elab.lean b/Mathlib/Tactic/Inclusion/Core/Elab.lean index ad252853db4d17..d736cc772fe852 100644 --- a/Mathlib/Tactic/Inclusion/Core/Elab.lean +++ b/Mathlib/Tactic/Inclusion/Core/Elab.lean @@ -23,7 +23,7 @@ namespace Inclusion declare_config_elab elabInclusionConfig InclusionConfig where omit paramSettings, families -/-- Syntax for specifying an inclusion family or parameter. -/ +/-- Families and parameters for the `inclusion` tactic -/ syntax inclusionArg := ident (" := " term)? /-- Collect the enabled inclusion families and user-set parameter values. -/ From 0d60740d68f17e978772a1864ac036c4cd6f70df Mon Sep 17 00:00:00 2001 From: DavidLedvinka <50644608+DavidLedvinka@users.noreply.github.com> Date: Wed, 2 Sep 2026 17:51:34 +0100 Subject: [PATCH 25/38] Update Mathlib/Tactic/Inclusion/Core/Elab.lean Co-authored-by: Anne Baanen --- Mathlib/Tactic/Inclusion/Core/Elab.lean | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/Mathlib/Tactic/Inclusion/Core/Elab.lean b/Mathlib/Tactic/Inclusion/Core/Elab.lean index d736cc772fe852..a79ccab1eb5ef9 100644 --- a/Mathlib/Tactic/Inclusion/Core/Elab.lean +++ b/Mathlib/Tactic/Inclusion/Core/Elab.lean @@ -55,17 +55,11 @@ def collectInclusionArgs (argStxs : Array Syntax) : TacticM InclusionConfig := d return { paramSettings, families } /-- `inclusion` tactic for proving "inclusion" propositions. -/ -syntax (name := inclusion) "inclusion" optConfig " [" inclusionArg,* "]" : tactic - -/-- Elaborator for the `inclusion` tactic. -/ -@[tactic inclusion] -def inclusionTac : Tactic - | `(tactic| inclusion $cfg:optConfig [$args,*]) => do - let options ← elabInclusionConfig cfg - let config ← collectInclusionArgs args.getElems - let config := { config with kernel := options.kernel, native := options.native } - closeMainGoalUsing `inclusion fun goal _ => inclusionCore goal config - | _ => throwUnsupportedSyntax +elab (name := inclusion) "inclusion" cfg:optConfig " [" args:inclusionArg,* "]" : tactic => do + let options ← elabInclusionConfig cfg + let config ← collectInclusionArgs args.getElems + let config := { config with kernel := options.kernel, native := options.native } + closeMainGoalUsing `inclusion fun goal _ => inclusionCore goal config /-- Tactic for quickly checking if the `inclusion` tactic will succeed. -/ syntax (name := inclusion?) "inclusion?" " [" inclusionArg,* "]" : tactic From 482b61915f7095a1c81099060ee6e1b26f86b475 Mon Sep 17 00:00:00 2001 From: DavidLedvinka <50644608+DavidLedvinka@users.noreply.github.com> Date: Wed, 2 Sep 2026 17:51:52 +0100 Subject: [PATCH 26/38] Update Mathlib/Tactic/Inclusion/Core/Elab.lean Co-authored-by: Anne Baanen --- Mathlib/Tactic/Inclusion/Core/Elab.lean | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/Mathlib/Tactic/Inclusion/Core/Elab.lean b/Mathlib/Tactic/Inclusion/Core/Elab.lean index a79ccab1eb5ef9..862f30bead3e0e 100644 --- a/Mathlib/Tactic/Inclusion/Core/Elab.lean +++ b/Mathlib/Tactic/Inclusion/Core/Elab.lean @@ -62,19 +62,13 @@ elab (name := inclusion) "inclusion" cfg:optConfig " [" args:inclusionArg,* "]" closeMainGoalUsing `inclusion fun goal _ => inclusionCore goal config /-- Tactic for quickly checking if the `inclusion` tactic will succeed. -/ -syntax (name := inclusion?) "inclusion?" " [" inclusionArg,* "]" : tactic - -/-- Elaborator for the `inclusion?` tactic. -/ -@[tactic inclusion?] -def inclusion?Tac : Tactic - | `(tactic| inclusion? [$args,*]) => do - let config ← collectInclusionArgs args.getElems - withoutModifyingStateWithInfoAndMessages <| withMainContext do - try - discard <| inclusionCore (← getMainTarget) config - logInfo "The inclusion check succeeded." - catch err => - logError m!"The inclusion check failed:\n{err.toMessageData}" - | _ => throwUnsupportedSyntax +elab (name := inclusion?) "inclusion?" " [" args:inclusionArg,* "]" : tactic => do + let config ← collectInclusionArgs args.getElems + withoutModifyingStateWithInfoAndMessages <| withMainContext do + try + discard <| inclusionCore (← getMainTarget) config + logInfo "The inclusion check succeeded." + catch err => + logError m!"The inclusion check failed:\n{err.toMessageData}" end Inclusion From e9069380b42ad5f9e92f8b9f2ad51ce01a2d009b Mon Sep 17 00:00:00 2001 From: DavidLedvinka <50644608+DavidLedvinka@users.noreply.github.com> Date: Wed, 2 Sep 2026 17:53:12 +0100 Subject: [PATCH 27/38] Update Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean Co-authored-by: Anne Baanen --- .../Extension/IntervalDyadicReal/Tactic.lean | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean index b0f0fe66a3c83f..cf714ed245eeae 100644 --- a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean @@ -24,7 +24,21 @@ open Lean.Parser.Tactic namespace Inclusion /-- `dyadic_interval` runs `inclusion` with the `core` and `interval_dyadic_real` extension -families. Additional families and inclusion parameters may be supplied in brackets. -/ +/-- `dyadic_interval` proves real number equalities, inequalities and interval memberships by +approximating as an interval of dyadic rational numbers. + +This tactic is implemented as a family for the `inclusion` tactic: `dyadic_interval` is the same as +`inclusion [core, interval_dyadic_real]`. + +* `dyadic_interval [binSplit := n]` splits each interval `n` times, into `2^n` pieces. Higher values + of `n` make the tactic slower but able to prove more. Default: no splitting. +* `dyadic_interval [prec := n]` uses a precision of `2^-n` when constructing the approximation. + Higher values of `n` make the tactic slower but able to prove more. Default value: 0. +* `dyadic_interval [fam₁, ... famₙ]` uses the inclusion families `fam₁`, ..., `famₙ` for additional + reasoning capabilities. +* `dyadic_interval (config := cfg)` uses `cfg` as a configuration for the `inclusion` tactic. + (See there for further details.) +-/ syntax (name := dyadicInterval) "dyadic_interval" optConfig (" [" inclusionArg,* "]")? : tactic From 55a7f50bf92c5a3f1421c57a406c21099e0d50eb Mon Sep 17 00:00:00 2001 From: DavidLedvinka <50644608+DavidLedvinka@users.noreply.github.com> Date: Wed, 2 Sep 2026 17:53:56 +0100 Subject: [PATCH 28/38] Update Mathlib/Tactic/Inclusion/Core/Elab.lean Co-authored-by: Anne Baanen --- Mathlib/Tactic/Inclusion/Core/Elab.lean | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Mathlib/Tactic/Inclusion/Core/Elab.lean b/Mathlib/Tactic/Inclusion/Core/Elab.lean index 862f30bead3e0e..347b1d5b31f95e 100644 --- a/Mathlib/Tactic/Inclusion/Core/Elab.lean +++ b/Mathlib/Tactic/Inclusion/Core/Elab.lean @@ -61,7 +61,9 @@ elab (name := inclusion) "inclusion" cfg:optConfig " [" args:inclusionArg,* "]" let config := { config with kernel := options.kernel, native := options.native } closeMainGoalUsing `inclusion fun goal _ => inclusionCore goal config -/-- Tactic for quickly checking if the `inclusion` tactic will succeed. -/ +/-- `inclusion? [fam₁, ...]` is a proof writing aid that quickly checks if `inclusion [fam₁, ...]` +would close the goal, without doing the expensive kernel computation that actually closes the goal. +-/ elab (name := inclusion?) "inclusion?" " [" args:inclusionArg,* "]" : tactic => do let config ← collectInclusionArgs args.getElems withoutModifyingStateWithInfoAndMessages <| withMainContext do From e7be09f3461eff0087d3e3a26c1b5d91861918b0 Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Wed, 2 Sep 2026 19:49:33 +0100 Subject: [PATCH 29/38] fix --- .../Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean | 1 - 1 file changed, 1 deletion(-) diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean index cf714ed245eeae..0dfb2387156638 100644 --- a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Tactic.lean @@ -23,7 +23,6 @@ open Lean.Parser.Tactic namespace Inclusion -/-- `dyadic_interval` runs `inclusion` with the `core` and `interval_dyadic_real` extension /-- `dyadic_interval` proves real number equalities, inequalities and interval memberships by approximating as an interval of dyadic rational numbers. From 7508dcb8dcd6d41e04ec9a6c8a335567d107782b Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Wed, 2 Sep 2026 20:09:26 +0100 Subject: [PATCH 30/38] fix --- Mathlib/Tactic/Inclusion/Core/Elab.lean | 1 + 1 file changed, 1 insertion(+) diff --git a/Mathlib/Tactic/Inclusion/Core/Elab.lean b/Mathlib/Tactic/Inclusion/Core/Elab.lean index 347b1d5b31f95e..ba59ba583b843c 100644 --- a/Mathlib/Tactic/Inclusion/Core/Elab.lean +++ b/Mathlib/Tactic/Inclusion/Core/Elab.lean @@ -20,6 +20,7 @@ open Lean.Parser.Tactic namespace Inclusion +/-- Configuration elaborator for the `inclusion` tactic. -/ declare_config_elab elabInclusionConfig InclusionConfig where omit paramSettings, families From 754ae626d1bbb3cd27d1cd88e54216ae049a3f84 Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Wed, 2 Sep 2026 21:24:15 +0100 Subject: [PATCH 31/38] docs --- Mathlib/Tactic/Inclusion/Core/Core.lean | 6 + .../Tactic/Inclusion/Core/DiscrTreeExt.lean | 16 + Mathlib/Tactic/Inclusion/Core/Elab.lean | 273 +++++++++++++++++- Mathlib/Tactic/Inclusion/Core/ToSet.lean | 5 + 4 files changed, 295 insertions(+), 5 deletions(-) diff --git a/Mathlib/Tactic/Inclusion/Core/Core.lean b/Mathlib/Tactic/Inclusion/Core/Core.lean index 04668dc467f6c2..2f2ec01861aa60 100644 --- a/Mathlib/Tactic/Inclusion/Core/Core.lean +++ b/Mathlib/Tactic/Inclusion/Core/Core.lean @@ -12,6 +12,12 @@ public meta import Lean.Meta.Native # Core implementation of the `inclusion` tactic This file defines the `TacticM` core of the `inclusion` tactic. + +## Implimentation Notes + +The approach to the `kernel == true` and `native == true` options mirrors the approach used by +the `decide` tactic (and reuses the code where possible). + -/ @[expose] public meta section diff --git a/Mathlib/Tactic/Inclusion/Core/DiscrTreeExt.lean b/Mathlib/Tactic/Inclusion/Core/DiscrTreeExt.lean index 44a72ada299f51..1d2be370f733d7 100644 --- a/Mathlib/Tactic/Inclusion/Core/DiscrTreeExt.lean +++ b/Mathlib/Tactic/Inclusion/Core/DiscrTreeExt.lean @@ -13,6 +13,22 @@ public meta import Lean.Meta.DiscrTree This file provides an API for scoped environment extensions whose declarations are indexed by elaborated expression patterns in a `DiscrTree`. + +## Implimentation Notes + +The inclusion tactic uses two seperate types of `DiscrTree` indexed environment extensions. These +have nearly identical APIs except the stored values are different types (one is `InclusionExt`s +and the other is `HypothesisExt`s). This file essentially generalizes the `DiscrTree` valued +environment extension API from the implimentation for the `norm_num` tactic so that it can take +arbitrary values. + +## TODO + +Investigate the possibility of using this API for other tactics in Mathlib with `DiscrTree` +indexed environment extensions such as `norm_num` and `positivity`. This should perhaps be +part of a wider investigation into whether more API from the environment extensions of various +Mathlib tactics could be unified. + -/ public meta section diff --git a/Mathlib/Tactic/Inclusion/Core/Elab.lean b/Mathlib/Tactic/Inclusion/Core/Elab.lean index ba59ba583b843c..37426420487608 100644 --- a/Mathlib/Tactic/Inclusion/Core/Elab.lean +++ b/Mathlib/Tactic/Inclusion/Core/Elab.lean @@ -8,9 +8,272 @@ module public meta import Mathlib.Tactic.Inclusion.Core.Core /-! -# Elaboration of the `inclusion` tactic +# The `inclusion` tactic + +The primary function of the inclusion tactic is as follows: given an expression `e` (for example +the type of a goal), compute an inclusion expression for `e`: + +(In `Inclusion/Core/Types`) + +```lean +structure ExprInclusion where + inclusion : Expr + proof : Expr +``` + +where `inclusion` is some expression that is built up of some kernel-computation-friendly +expressions in some kernel-computation-friendly type, and `proof` is a proof of `e ∈ inclusion`. + +Since `inclusion` itself is meant to live in some kernel-computation-friendly type, we need a way +to interpret `inclusion` as a set in the type of `e`. That is the idea behind the following class: + +(In `Inclusion/Core/ToSet`) + +```lean +class ToSet (Iα : Type*) (α : outParam Type*) where + toSet : Iα → Set α +``` + +Examples can be things like "intervals with dyadic endpoints" to sets of `ℝ`, "vectors of +intervals of dyadic endpoints" to sets of `ℝⁿ`, "balls with a dyadic complex center and dyadic +radius" to sets of `ℂ`, etc. + +The most important example, however, is `IntervalBool` to `Prop`: + +```lean +def IntervalBool.toPropSet : IntervalBool → Set Prop + | true => {True} + | false => {False} + | undetermined => {True, False} + +instance : ToSet IntervalBool Prop := ⟨IntervalBool.toPropSet⟩ +``` + +Using this, the tactic can generate an inclusion expression for a goal like `x ^ 2 + 1 < 5`, and +then the proof of the goal is `proof` that `(x ^ 2 + 1 < 5) ∈ inclusion` along with a proof by +reflection that `inclusion = IntervalBool.true`. + +## Constructing `ExprInclusion`s + +`ExprInclusion`s are constructed in two phases which take place inside two different monads. The +first phase takes place in the `InclusionM` monad and is to construct an inclusion body: + +(In `Inclusion/Core/Types`) + +```lean +structure ExprInclusionBody where + inclusionBody : Expr + proofBody : Expr +``` + +which is the same as `ExprInclusion` except that `inclusionBody` is allowed to have "free `IVar`s +(inclusion variables)" (see the structure `IVar` in `Inclusion/Core/Types`) which represent "atomic" +variables whose initial value will be determined by hypotheses in the local context (in the next +phase). As an example, if we are applying the `inclusion` tactic to the goal +`(x : ℝ) + 1 ≤ 5` then (depending on which extensions we have enabled) we might have that `x` is +made into an `IVar`, which contains the expression of a placeholder variable `I` (which could be of +type `Interval Dyadic` for example) and a placeholder hypothesis `x ∈ I`. For technical reasons +these variables are synthetic opaque metavariables rather than free variables. + +Remark: The technical reason that `IVar`s use synthetic opaque metavariables is that metavariables +are stored in the state of `MetaM` and are mutable. Free variables are stored in the local context +and are not mutable. Since the tactic constructs `IVar`s during the `InclusionM` phase and doesn't +know how many `IVars` there are or what they will be, it makes it difficult to use free variables. + +The `InclusionM` phase uses inclusion extensions: + +(In `Inclusion/Core/Extensions`) + +```lean +structure InclusionExt where + declName : Name := by exact decl_name% + family : Name + userName : Name := declName + derive (e : Expr) : InclusionM ExprInclusionBody + priority : Nat := eval_prio default +``` + +which belong to families (for example `interval_dyadic_real` containing extensions involving +computations as intervals of dyadics as inclusions for operations on the reals) and are registered +under discrimination tree keys which determine which expressions they match on (and thus can +possibly apply to). + +The main driver of this phase is the function `mkExprInclusionBody` +(in `Inclusion/Core/Inclusion`) which collects all the extensions (from enabled families) that match +the current expression `e`, sorts them in order of priority, and then tries applying their `derive` +to `e` until one succeeds in producing an `ExprInclusionBody`. The expectation is that if +`derive e` succeeds it should produce a valid `ExprInclusionBody` for `e` and the metadata in the +`InclusionM` monad should be up to date. Many `derive`s will recursively call +`mkExprInclusionBody`; for example, you would expect that the extension which matches +`e := e1 ≤ e2` will call `mkExprInclusionBody` on `e1` and `e2` and then combine the results to +produce the `ExprInclusionBody` for `e`. + +The second phase takes place in the `HypothesisM` monad. In this phase, initial inclusion +expressions for each `IVar` appearing in the `ExprInclusionBody` (constructed in the previous phase) +are derived from hypotheses in the local context. Then these hypotheses are used to "close" the +body and construct the final `ExprInclusion`. If an `IVar` has an enabled `cover` it is used to +"divide" the inclusion computation into checks on each of the smaller pieces, effectively creating +a refined inclusion function. + +This phase uses hypothesis extensions: + +(In `Inclusion/Core/Extensions`) + +```lean +structure HypothesisExt where + declName : Name := by exact decl_name% + family : Name + userName : Name := declName + derive (h : Expr) : HypothesisM Unit + priority : Nat := eval_prio default +``` + +which also belong to families and are registered under discrimination tree keys just like +`InclusionExt`s. Here, though, `derive h` generates inclusion hypotheses from a local hypothesis `h` +and puts them into the state of `HypothesisM`. The main driver for this phase is `collectHyps` +(in `Inclusion/Core/Inclusion`) which loops over all local declarations `h`, finds all hypothesis +extensions matching the type of `h`, and then tries each of their `derive` functions. The changes +made by a failed extension are rolled back, while every successful extension is allowed to add one +or more inclusion hypotheses. + +## Params + +It is convenient to allow `InclusionExt`s and `HypothesisExt`s to depend on shared parameters +which can be set by the user: + +(In `Inclusion/Core/Extensions`) + +```lean +structure InclusionParamDecl where + name : Name + type : Expr + defaultValue? : Option Expr := none +``` + +The two examples in the current PR are: + +* `prec`: which sets the dyadic precision (in bits) that each extension should use. +* `binSplit`: which sets the depth of binary interval splitting for each `IVar`. + +These can be set by the user when calling the tactic like: + +`inclusion [core, interval_dyadic_real, prec := 20, binSplit := 3]` + +Two additional features which are not present in the current PR (and will require a bit of +refactoring) which will be added later are: + +1. The ability to set "local" params. An important example being you may only want to set + binary splitting on one specific variable (since doing it on each grows the number of cases + exponentially). + +2. The ability for `inclusion?` to "search" for optimal parameters using a compiled `ExprInclusion` + function. To be maximally efficient these will have to be restricted to specific types (maybe + just Nats) so that the function can be compiled once and used repeatedly. + +## Writing Extensions + +One can directly write inclusion and hypothesis extensions like: + +```lean +@[inclusion_ext (_ : ℝ)] +meta def mkRealIVar : InclusionExt := + mkNDIVarExt `interval_dyadic_real + ⟨q(ℝ), q(Interval Dyadic), q(instToSetIntervalDyadicReal)⟩ mkBinarySplitCover +``` + +```lean +@[hypothesis_ext _ ∧ _] +meta def andHyp : HypothesisExt where + family := `core + derive h := do + let (``And, #[_, _]) := (← instantiateMVars (← inferType h)).getAppFnArgs | failure + runHypothesisExts (← mkAppM ``And.left #[h]) + runHypothesisExts (← mkAppM ``And.right #[h]) +``` + +where each expression supplied to `inclusion_ext` or `hypothesis_ext` is elaborated and then +converted into a `DiscrTree` key that the extension matches on. + +However it is up to the extender to make sure both that the extension is deriving the right +inclusion body or hypotheses and is correctly maintaining the state of the current monad. This +approach to writing extensions gives significant flexibility but also could be highly error-prone. +Just like `MetaM` has both lots of low-level functions that are capable of breaking things and +should usually be avoided, as well as higher-level functions that are meant to be safer for tactic +writers to use, `Inclusion/ExtensionAPI` is meant to provide functions that extenders can use which +maintain the invariants expected by the `InclusionM` or `HypothesisM` monad. Most of these are +currently in `Inclusion/ExtensionAPI/Basic`. I expect many more to be added as the tactic develops. + +The rules for `InclusionM` are: + +* When an `InclusionExt` succeeds on an expression `e`, it must return an `ExprInclusionBody` whose + `proofBody` proves `e ∈ inclusionBody` using a `ToSet` instance. +* Every inclusion-variable placeholder in the returned body must belong to an `IVar` registered in + `InclusionM.State`. Its `setVar` must have the registered set type, and its `hypVar` must prove + that its associated expression belongs to `setVar` using the registered `ToSet` instance. +* Every registered `IVar` must be well-formed in the initial local context. If it has a cover, the + cover expression must have the corresponding `Cover` type. +* The returned body must not depend on untracked metavariables or temporary free variables + introduced while running the extension. + +The rules for `HypothesisM` are: + +* Every inclusion hypothesis added by a `HypothesisExt` must be associated with an existing `IVar`. +* Each added `ExprInclusionBody` must have the same element type, set type, and `ToSet` instance as + that `IVar`. Its `proofBody` must prove that the associated expression belongs to its + `inclusionBody`. +* An added `inclusionBody` must not contain unresolved inclusion-variable placeholders. + +But for certain `InclusionExt`s or `HypothesisExt`s that fit a (very specific) mold, there is an +API for defining extensions that doesn't even require metaprogramming. Instead you add an attribute +to a theorem which must be formatted in a specific way. We give these a special name +`InclusionOp`s and `HypothesisOp`s. Here are some examples: + +```lean +@[inclusion_op interval_dyadic_real] +theorem add_mem {x y : ℝ} {I J : Interval Dyadic} (hx : x ∈ I) (hy : y ∈ J) : + x + y ∈ I.add J := + Interval.add_mem Dyadic.toRealAddMonoidHom hx hy +``` + +```lean +@[hypothesis_op interval_dyadic_real] +theorem Iic_mem_of_le {x y : ℝ} {I : Interval Dyadic} (hxy : x ≤ y) (hy : y ∈ I) : + x ∈ Interval.Iic I.ub := + Interval.mem_Iic_of_le hxy hy +``` + +The rules for `InclusionOp`s are: + +* The theorem must conclude with an inclusion `e ∈ I` using a `ToSet` instance. The expression + `e` becomes the discrimination-tree pattern, while `I` becomes the resulting inclusion body. +* Every hypothesis of the form `x ∈ X` using a `ToSet` instance is treated as a recursive input. + Both `x` and `X` must be theorem variables, and `x` must occur in `e`. The generated extension + recursively constructs an inclusion body for `x`, then substitutes its inclusion and proof for + `X` and the hypothesis. +* To use a registered inclusion parameter, the theorem must have an argument with the same name and + type as that parameter. The generated extension supplies its current value. The parameter may + occur in `I`, but it may not occur in `e`. +* All remaining theorem arguments must be determined by matching the theorem or by typeclass + synthesis. + +The rules for `HypothesisOp`s are: + +* The theorem must conclude with an inclusion `e ∈ I` using a `ToSet` instance. +* The theorem must have exactly one explicit proposition hypothesis which is not itself an + inclusion. This hypothesis is the source hypothesis, and its type becomes the discrimination-tree + pattern. Its proof may not occur in `e` or `I`. +* Every hypothesis of the form `x ∈ X` using a `ToSet` instance is treated as a recursive input. + Both `x` and `X` must be theorem variables, and `x` must occur in the source hypothesis. The + generated extension recursively constructs a closed inclusion body for `x`, then substitutes its + inclusion and proof for `X` and the hypothesis. +* When the generated extension runs, `e` must be the expression associated with an existing `IVar`. + The extension adds `I` as an inclusion hypothesis for that `IVar`. +* To use a registered inclusion parameter, the theorem must have an argument with the same name and + type as that parameter. The generated extension supplies its current value. The parameter may + occur in `I`, but it may not occur in the source hypothesis or `e`. +* All remaining theorem arguments must be determined by matching the theorem or by typeclass + synthesis. -This file defines the syntax and elaborator for the `inclusion` tactic. -/ public meta section @@ -62,9 +325,9 @@ elab (name := inclusion) "inclusion" cfg:optConfig " [" args:inclusionArg,* "]" let config := { config with kernel := options.kernel, native := options.native } closeMainGoalUsing `inclusion fun goal _ => inclusionCore goal config -/-- `inclusion? [fam₁, ...]` is a proof writing aid that quickly checks if `inclusion [fam₁, ...]` -would close the goal, without doing the expensive kernel computation that actually closes the goal. --/ +/-- `inclusion? [fam₁, ...]` is a proof writing aid that quickly checks if +`inclusion [fam₁, ...]` would close the goal, without doing the expensive kernel computation that +actually closes the goal. -/ elab (name := inclusion?) "inclusion?" " [" args:inclusionArg,* "]" : tactic => do let config ← collectInclusionArgs args.getElems withoutModifyingStateWithInfoAndMessages <| withMainContext do diff --git a/Mathlib/Tactic/Inclusion/Core/ToSet.lean b/Mathlib/Tactic/Inclusion/Core/ToSet.lean index 12e0e4d799406b..98e2921256a7ce 100644 --- a/Mathlib/Tactic/Inclusion/Core/ToSet.lean +++ b/Mathlib/Tactic/Inclusion/Core/ToSet.lean @@ -12,6 +12,11 @@ public import Mathlib.Data.Set.Insert This file defines the `ToSet` class and its API needed for the `inclusion` tactic. +## Implimentation Notes + +* `Inclusion.IntervalBool` is nearly identical to `Lean.LBool` but with a seperate API and +documentation which is catered to the needs of the inclusion tactic. + -/ @[expose] public section From 23ce40b03143f54e68bf864739957245d6525bdb Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Wed, 2 Sep 2026 21:26:07 +0100 Subject: [PATCH 32/38] minor reword --- Mathlib/Tactic/Inclusion/Core/Core.lean | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mathlib/Tactic/Inclusion/Core/Core.lean b/Mathlib/Tactic/Inclusion/Core/Core.lean index 2f2ec01861aa60..2788a5bdab7abb 100644 --- a/Mathlib/Tactic/Inclusion/Core/Core.lean +++ b/Mathlib/Tactic/Inclusion/Core/Core.lean @@ -15,8 +15,8 @@ This file defines the `TacticM` core of the `inclusion` tactic. ## Implimentation Notes -The approach to the `kernel == true` and `native == true` options mirrors the approach used by -the `decide` tactic (and reuses the code where possible). +The approach to the implimentation of the `kernel == true` and `native == true` options mirrors +the approach used by the `decide` tactic (and reuses the code where possible). -/ From ffaf176ec7ed425d4969f2ea4aa3d385afb27930 Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Sun, 6 Sep 2026 20:50:25 +0100 Subject: [PATCH 33/38] Add interval multiplication support to inclusion tactic --- .gitignore | 4 + .worktrees/inclusion-primegaps | 1 + .../Tactic/Inclusion/Extension/Interval.lean | 263 ++++++++++++++++++ .../Extension/IntervalDyadicReal/Basic.lean | 5 + .../Tactic/Inclusion/Multiplication.lean | 94 +++++++ PrimeGaps186-work | 1 + 6 files changed, 368 insertions(+) create mode 160000 .worktrees/inclusion-primegaps create mode 100644 MathlibTest/Tactic/Inclusion/Multiplication.lean create mode 160000 PrimeGaps186-work diff --git a/.gitignore b/.gitignore index 6973f6af86cc29..8bb408051ab6cc 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,7 @@ scripts/.rm_set_option_progress.jsonl # Output of scripts/export_crossrefs.lean /crossrefs.json + +# Personal files for developing the inclusion tactic +/Mathlib/Tactic/Inclusion/notes.md +/MathlibTest/Tactic/Inclusion/Scratch.lean diff --git a/.worktrees/inclusion-primegaps b/.worktrees/inclusion-primegaps new file mode 160000 index 00000000000000..7f20fbce9f0486 --- /dev/null +++ b/.worktrees/inclusion-primegaps @@ -0,0 +1 @@ +Subproject commit 7f20fbce9f0486e87b2e0ea4043e21e679bfbbf1 diff --git a/Mathlib/Tactic/Inclusion/Extension/Interval.lean b/Mathlib/Tactic/Inclusion/Extension/Interval.lean index 889688a3b4c875..51f9007abe90d8 100644 --- a/Mathlib/Tactic/Inclusion/Extension/Interval.lean +++ b/Mathlib/Tactic/Inclusion/Extension/Interval.lean @@ -8,6 +8,7 @@ module public import Mathlib.Algebra.Order.Group.Unbundled.Basic public import Mathlib.Algebra.Order.Monoid.Defs public import Mathlib.Algebra.Order.Monoid.Unbundled.WithTop +public import Mathlib.Algebra.Order.Ring.Defs public import Mathlib.Order.Hom.Basic public import Mathlib.Order.Interval.Set.Defs public import Mathlib.Tactic.Inclusion.Core.ToSet @@ -256,6 +257,268 @@ theorem Interval.sub_mem [AddGroup α] [AddCommGroup β] [Preorder β] [IsOrdere rw [_root_.sub_eq_add_neg, Interval.sub_eq_add_neg] exact Interval.add_mem f hx (Interval.neg_mem f hy) +/-- Multiply two finite or infinite interval bounds. -/ +def Interval.mulBound [Mul α] [Zero α] [DecidableEq α] : + Option α → Option α → Option α + | some a, some b => some (a * b) + | some a, none => if a = 0 then some 0 else none + | none, some b => if b = 0 then some 0 else none + | none, none => none + +-- `WithBot α` and `WithTop α` are definitionally `Option α`, so `mulBound` handles both. +/-- Multiply two intervals. -/ +def Interval.mul [Mul α] [Zero α] [LinearOrder α] (I J : Interval α) : Interval α := + if 0 ≤ I.lb then + if 0 ≤ J.lb then + ⟨Interval.mulBound I.lb J.lb, Interval.mulBound I.ub J.ub⟩ + else if J.ub ≤ 0 then + ⟨Interval.mulBound I.ub J.lb, Interval.mulBound I.lb J.ub⟩ + else + ⟨Interval.mulBound I.ub J.lb, Interval.mulBound I.ub J.ub⟩ + else if I.ub ≤ 0 then + if 0 ≤ J.lb then + ⟨Interval.mulBound I.lb J.ub, Interval.mulBound I.ub J.lb⟩ + else if J.ub ≤ 0 then + ⟨Interval.mulBound I.ub J.ub, Interval.mulBound I.lb J.lb⟩ + else + ⟨Interval.mulBound I.lb J.ub, Interval.mulBound I.lb J.lb⟩ + else + if 0 ≤ J.lb then + ⟨Interval.mulBound I.lb J.ub, Interval.mulBound I.ub J.ub⟩ + else if J.ub ≤ 0 then + ⟨Interval.mulBound I.ub J.lb, Interval.mulBound I.lb J.lb⟩ + else + ⟨min (Interval.mulBound I.lb J.ub) (Interval.mulBound I.ub J.lb), + max (Interval.mulBound I.lb J.lb) (Interval.mulBound I.ub J.ub)⟩ + +private theorem map_mulBound_le [Mul α] [Zero α] [DecidableEq α] [Preorder β] + (f : α → β) (a b : Option α) {z : β} + (hmul : ∀ x y, a = some x → b = some y → f (x * y) ≤ z) + (hzero : (a = none ∧ b = some 0 ∨ a = some 0 ∧ b = none) → f 0 ≤ z) : + WithBot.map f (Interval.mulBound a b : WithBot α) ≤ z := by + rcases a with _ | a <;> rcases b with _ | b <;> + simp only [Interval.mulBound] <;> try split_ifs + all_goals first | exact bot_le | apply WithBot.coe_le_coe.mpr + all_goals first + | exact hzero (by simp_all [WithBot.none_eq_bot]; rfl) + | exact hmul _ _ rfl rfl + +private theorem le_map_mulBound [Mul α] [Zero α] [DecidableEq α] [Preorder β] + (f : α → β) (a b : Option α) {z : β} + (hmul : ∀ x y, a = some x → b = some y → z ≤ f (x * y)) + (hzero : (a = none ∧ b = some 0 ∨ a = some 0 ∧ b = none) → z ≤ f 0) : + z ≤ WithTop.map f (Interval.mulBound a b : WithTop α) := by + rcases a with _ | a <;> rcases b with _ | b <;> + simp only [Interval.mulBound] <;> try split_ifs + all_goals first | exact le_top | apply WithTop.coe_le_coe.mpr + all_goals first + | exact hzero (by simp_all [WithTop.none_eq_top]; rfl) + | exact hmul _ _ rfl rfl + +private theorem nonneg_of_mem_map [Preorder α] [Preorder β] [Zero α] [Zero β] (f : α ↪o β) + (map_zero : f 0 = 0) {x : β} {I : Interval α} (hI : 0 ≤ I.lb) + (hx : x ∈ I.map f) : 0 ≤ x := by + simpa [map_zero] using (f.monotone.withBot_map hI).trans hx.1 + +private theorem nonpos_of_mem_map [Preorder α] [Preorder β] [Zero α] [Zero β] (f : α ↪o β) + (map_zero : f 0 = 0) {x : β} {I : Interval α} (hI : I.ub ≤ 0) + (hx : x ∈ I.map f) : x ≤ 0 := by + simpa [map_zero] using hx.2.trans (f.monotone.withTop_map hI) + +private theorem map_lb_le_of_eq [Preorder β] (f : α → β) {a : α} {x : β} + {I : Interval α} (hx : x ∈ I.map f) (h : I.lb = some a) : f a ≤ x := by + rw [Interval.map, h] at hx + exact WithBot.coe_le_coe.mp hx.1 + +private theorem le_map_ub_of_eq [Preorder β] (f : α → β) {a : α} {x : β} + {I : Interval α} (hx : x ∈ I.map f) (h : I.ub = some a) : x ≤ f a := by + rw [Interval.map, h] at hx + exact WithTop.coe_le_coe.mp hx.2 + +private theorem zero_le_map_lb_iff [LinearOrder α] [LinearOrder β] [Zero α] [Zero β] + (f : α ↪o β) (map_zero : f 0 = 0) {a : α} {I : Interval α} + (h : I.lb = some a) : 0 ≤ f a ↔ 0 ≤ I.lb := by + rw [h, ← map_zero, f.le_iff_le] + exact (WithBot.coe_le_coe (a := (0 : α)) (b := a)).symm + +private theorem map_ub_le_zero_iff [LinearOrder α] [LinearOrder β] [Zero α] [Zero β] + (f : α ↪o β) (map_zero : f 0 = 0) {a : α} {I : Interval α} + (h : I.ub = some a) : f a ≤ 0 ↔ I.ub ≤ 0 := by + rw [h, ← map_zero, f.le_iff_le] + exact (WithTop.coe_le_coe (a := (0 : α)) (b := a)).symm + +private theorem eq_zero_of_nonneg_of_ub_eq [PartialOrder β] [Zero α] [Zero β] + (f : α → β) (map_zero : f 0 = 0) {x : β} {I : Interval α} (hx0 : 0 ≤ x) + (hx : x ∈ I.map f) (h : I.ub = some 0) : x = 0 := + le_antisymm (by simpa [map_zero] using le_map_ub_of_eq f hx h) hx0 + +private theorem eq_zero_of_lb_eq_of_nonpos [PartialOrder β] [Zero α] [Zero β] + (f : α → β) (map_zero : f 0 = 0) {x : β} {I : Interval α} (hx0 : x ≤ 0) + (hx : x ∈ I.map f) (h : I.lb = some 0) : x = 0 := + le_antisymm hx0 (by simpa [map_zero] using map_lb_le_of_eq f hx h) + +theorem Interval.mul_mem [Mul α] [Zero α] [LinearOrder α] [Ring β] [LinearOrder β] + [IsStrictOrderedRing β] (f : α ↪o β) (map_zero : f 0 = 0) + (map_mul : ∀ a b, f (a * b) = f a * f b) {x y : β} {I J : Interval α} + (hx : x ∈ I.map f) (hy : y ∈ J.map f) : x * y ∈ (I.mul J).map f := by + rw [Interval.mem_def] + have hxl {a : α} (h : I.lb = some a) : f a ≤ x := map_lb_le_of_eq f hx h + have hxu {a : α} (h : I.ub = some a) : x ≤ f a := le_map_ub_of_eq f hx h + have hyl {a : α} (h : J.lb = some a) : f a ≤ y := map_lb_le_of_eq f hy h + have hyu {a : α} (h : J.ub = some a) : y ≤ f a := le_map_ub_of_eq f hy h + by_cases hIl : 0 ≤ I.lb + · have hx0 := nonneg_of_mem_map f map_zero hIl hx + by_cases hJl : 0 ≤ J.lb + · have hy0 := nonneg_of_mem_map f map_zero hJl hy + simp only [Interval.mul, hIl, hJl, ite_true] + constructor + · apply map_mulBound_le + · grind [mul_le_mul, zero_le_map_lb_iff] + · rintro (⟨ha, -⟩ | ⟨-, hb⟩) + all_goals simp_all [WithBot.none_eq_bot] + · apply le_map_mulBound <;> + grind [mul_le_mul, eq_zero_of_nonneg_of_ub_eq] + · by_cases hJu : J.ub ≤ 0 + · have hy0 := nonpos_of_mem_map f map_zero hJu hy + simp only [Interval.mul, hIl, hJl, hJu, ite_true, ite_false] + constructor + · apply map_mulBound_le + · grind [mul_le_mul_of_nonneg_of_nonpos] + · rintro (⟨-, hb⟩ | ⟨ha, -⟩) <;> + grind [eq_zero_of_lb_eq_of_nonpos, eq_zero_of_nonneg_of_ub_eq] + · apply le_map_mulBound + · grind [mul_le_mul_of_nonneg_of_nonpos, zero_le_map_lb_iff] + · rintro (⟨ha, -⟩ | ⟨-, hb⟩) + · simp [ha, WithBot.none_eq_bot] at hIl + · simp [hb, WithTop.none_eq_top] at hJu + · simp only [Interval.mul, hIl, hJl, hJu, ite_false] + constructor + · apply map_mulBound_le + · grind [mul_le_mul_of_nonneg_of_nonpos, zero_le_map_lb_iff] + · rintro (⟨-, hb⟩ | ⟨ha, -⟩) + · simp [hb, WithBot.some_eq_coe] at hJl + · rw [map_zero, eq_zero_of_nonneg_of_ub_eq f map_zero hx0 hx ha, zero_mul] + · apply le_map_mulBound + · intro a b ha hb + rw [map_mul] + exact (mul_le_mul_of_nonneg_left (hyu hb) hx0).trans + (mul_le_mul_of_nonneg_right (hxu ha) + (le_of_not_ge <| (map_ub_le_zero_iff f map_zero hb).not.mpr hJu)) + · rintro (⟨-, hb⟩ | ⟨ha, -⟩) + · simp [hb, WithTop.some_eq_coe] at hJu + · rw [map_zero, eq_zero_of_nonneg_of_ub_eq f map_zero hx0 hx ha, zero_mul] + · by_cases hIu : I.ub ≤ 0 + · have hx0 := nonpos_of_mem_map f map_zero hIu hx + by_cases hJl : 0 ≤ J.lb + · have hy0 := nonneg_of_mem_map f map_zero hJl hy + simp only [Interval.mul, hIl, hIu, hJl, ite_true, ite_false] + constructor + · apply map_mulBound_le + · grind [mul_le_mul_of_nonpos_of_nonneg] + · rintro (⟨-, hb⟩ | ⟨ha, -⟩) <;> + grind [eq_zero_of_nonneg_of_ub_eq, eq_zero_of_lb_eq_of_nonpos] + · apply le_map_mulBound + · grind [mul_le_mul_of_nonpos_of_nonneg, map_ub_le_zero_iff] + · rintro (⟨ha, -⟩ | ⟨-, hb⟩) + · simp [ha, WithTop.none_eq_top] at hIu + · simp [hb, WithBot.none_eq_bot] at hJl + · by_cases hJu : J.ub ≤ 0 + · have hy0 := nonpos_of_mem_map f map_zero hJu hy + simp only [Interval.mul, hIl, hIu, hJl, hJu, ite_true, ite_false] + constructor + · apply map_mulBound_le + · grind [mul_le_mul_of_nonpos_of_nonpos', map_ub_le_zero_iff] + · rintro (⟨ha, -⟩ | ⟨-, hb⟩) + all_goals simp_all [WithTop.none_eq_top] + · apply le_map_mulBound + · grind [mul_le_mul_of_nonpos_of_nonpos] + · rintro (⟨-, hb⟩ | ⟨ha, -⟩) <;> + grind [eq_zero_of_lb_eq_of_nonpos] + · simp only [Interval.mul, hIl, hIu, hJl, hJu, ite_false] + constructor + · apply map_mulBound_le + · grind [mul_le_mul_of_nonpos_of_nonneg, map_ub_le_zero_iff] + · rintro (⟨-, hb⟩ | ⟨ha, -⟩) + · exact (hJu (hb.symm ▸ WithTop.coe_le_coe.mpr le_rfl)).elim + · rw [map_zero, eq_zero_of_lb_eq_of_nonpos f map_zero hx0 hx ha, zero_mul] + · apply le_map_mulBound + · grind [mul_le_mul_of_nonpos_of_nonpos', zero_le_map_lb_iff] + · rintro (⟨-, hb⟩ | ⟨ha, -⟩) + · exact (hJl (hb.symm ▸ WithBot.coe_le_coe.mpr le_rfl)).elim + · rw [map_zero, eq_zero_of_lb_eq_of_nonpos f map_zero hx0 hx ha, zero_mul] + · by_cases hJl : 0 ≤ J.lb + · have hy0 := nonneg_of_mem_map f map_zero hJl hy + simp only [Interval.mul, hIl, hIu, hJl, ite_true, ite_false] + constructor + · apply map_mulBound_le + · intro a b ha hb + rw [map_mul] + exact (mul_le_mul_of_nonpos_left (hyu hb) + (le_of_not_ge <| (zero_le_map_lb_iff f map_zero ha).not.mpr hIl)).trans + (mul_le_mul_of_nonneg_right (hxl ha) hy0) + · rintro (⟨-, hb⟩ | ⟨ha, -⟩) + · rw [map_zero, eq_zero_of_nonneg_of_ub_eq f map_zero hy0 hy hb, mul_zero] + · exact (hIl (ha.symm ▸ WithBot.coe_le_coe.mpr le_rfl)).elim + · apply le_map_mulBound + · grind [mul_le_mul, map_ub_le_zero_iff] + · rintro (⟨-, hb⟩ | ⟨ha, -⟩) + · rw [map_zero, eq_zero_of_nonneg_of_ub_eq f map_zero hy0 hy hb, mul_zero] + · exact (hIu (ha.symm ▸ WithTop.coe_le_coe.mpr le_rfl)).elim + · by_cases hJu : J.ub ≤ 0 + · have hy0 := nonpos_of_mem_map f map_zero hJu hy + simp only [Interval.mul, hIl, hIu, hJl, hJu, ite_true, ite_false] + constructor + · apply map_mulBound_le + · grind [mul_le_mul_of_nonneg_of_nonpos', map_ub_le_zero_iff] + · rintro (⟨-, hb⟩ | ⟨ha, -⟩) + · rw [map_zero, eq_zero_of_lb_eq_of_nonpos f map_zero hy0 hy hb, mul_zero] + · simp [ha, WithTop.some_eq_coe] at hIu + · apply le_map_mulBound + · grind [mul_le_mul_of_nonpos_of_nonpos, zero_le_map_lb_iff] + · rintro (⟨-, hb⟩ | ⟨ha, -⟩) + · rw [map_zero, eq_zero_of_lb_eq_of_nonpos f map_zero hy0 hy hb, mul_zero] + · simp [ha, WithBot.some_eq_coe] at hIl + · simp only [Interval.mul, hIl, hIu, hJl, hJu, ite_false] + constructor + · simp only [Interval.map] + by_cases hy0 : 0 ≤ y + · refine (f.monotone.withBot_map (min_le_left _ _)).trans + (map_mulBound_le f I.lb J.ub ?_ ?_) + · intro a b ha hb + rw [map_mul] + exact (mul_le_mul_of_nonpos_left (hyu hb) + (le_of_not_ge <| (zero_le_map_lb_iff f map_zero ha).not.mpr hIl)).trans + (mul_le_mul_of_nonneg_right (hxl ha) hy0) + · rintro (⟨-, hb⟩ | ⟨ha, -⟩) + · simp [hb, WithTop.some_eq_coe] at hJu + · simp [ha, WithBot.some_eq_coe] at hIl + · refine (f.monotone.withBot_map (min_le_right _ _)).trans + (map_mulBound_le f I.ub J.lb ?_ ?_) + · intro a b ha hb + rw [map_mul] + by_cases hx0 : 0 ≤ x + · exact mul_le_mul_of_nonneg_of_nonpos (hxu ha) (hyl hb) hx0 + (le_of_not_ge <| (zero_le_map_lb_iff f map_zero hb).not.mpr hJl) + · exact (mul_nonpos_of_nonneg_of_nonpos + (le_of_not_ge <| (map_ub_le_zero_iff f map_zero ha).not.mpr hIu) + (le_of_not_ge <| (zero_le_map_lb_iff f map_zero hb).not.mpr hJl)).trans + (mul_nonneg_of_nonpos_of_nonpos (le_of_not_ge hx0) (le_of_not_ge hy0)) + · rintro (⟨-, hb⟩ | ⟨ha, -⟩) + · simp [hb, WithBot.some_eq_coe] at hJl + · simp [ha, WithTop.some_eq_coe] at hIu + · simp only [Interval.map] + by_cases hy0 : 0 ≤ y + · refine (le_map_mulBound f I.ub J.ub ?_ ?_).trans + (f.monotone.withTop_map (le_max_right _ _)) + · grind [mul_le_mul, map_ub_le_zero_iff] + · rintro (⟨-, hb⟩ | ⟨ha, -⟩) + all_goals simp_all [WithTop.some_eq_coe] + · refine (le_map_mulBound f I.lb J.lb ?_ ?_).trans + (f.monotone.withTop_map (le_max_left _ _)) + · grind [mul_le_mul_of_nonpos_of_nonpos, zero_le_map_lb_iff] + · rintro (⟨-, hb⟩ | ⟨ha, -⟩) + all_goals simp_all [WithBot.some_eq_coe] + /-- Check if `r x y` is false is implied by `x ∈ I` and `y ∈ J` -/ def Interval.orderRelFalse (r : α → α → Prop) [DecidableRel r] (I J : Interval α) : IntervalBool := diff --git a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Basic.lean b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Basic.lean index fbf7193b550f1c..8c1ee207171c01 100644 --- a/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Basic.lean +++ b/Mathlib/Tactic/Inclusion/Extension/IntervalDyadicReal/Basic.lean @@ -61,6 +61,11 @@ theorem neg_mem {x : ℝ} {I : Interval Dyadic} (hx : x ∈ I) : -x ∈ I.neg := theorem sub_mem {x y : ℝ} {I J : Interval Dyadic} (hx : x ∈ I) (hy : y ∈ J) : x - y ∈ I.sub J := Interval.sub_mem Dyadic.toRealAddMonoidHom hx hy +@[inclusion_op interval_dyadic_real] +theorem mul_mem {x y : ℝ} {I J : Interval Dyadic} (hx : x ∈ I) (hy : y ∈ J) : x * y ∈ I.mul J := + Interval.mul_mem Dyadic.toRealOrderEmbedding (map_zero Dyadic.toRealAddMonoidHom) + Dyadic.toReal_mul hx hy + end Arithmetic section Props diff --git a/MathlibTest/Tactic/Inclusion/Multiplication.lean b/MathlibTest/Tactic/Inclusion/Multiplication.lean new file mode 100644 index 00000000000000..d003daa4f0e432 --- /dev/null +++ b/MathlibTest/Tactic/Inclusion/Multiplication.lean @@ -0,0 +1,94 @@ +/- +Copyright (c) 2026 David Ledvinka. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: David Ledvinka +-/ +import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Tactic + +open Inclusion + +namespace Inclusion.Tests + +section Interval + +private def closed (lb ub : Dyadic) : Interval Dyadic := Interval.Icc lb ub + +private def atLeast (lb : Dyadic) : Interval Dyadic := Interval.Ici lb + +private def atMost (ub : Dyadic) : Interval Dyadic := Interval.Iic ub + +-- The nine possible pairs of interval signs use their sharp endpoint formulas. +example : (closed 1 2).mul (closed 3 4) = closed 3 8 := by rfl + +example : (closed 1 2).mul (closed (-3) 4) = closed (-6) 8 := by rfl + +example : (closed 1 2).mul (closed (-4) (-3)) = closed (-8) (-3) := by rfl + +example : (closed (-2) 3).mul (closed 4 5) = closed (-10) 15 := by rfl + +example : (closed (-2) 3).mul (closed (-5) 7) = closed (-15) 21 := by rfl + +example : (closed (-2) 3).mul (closed (-5) (-4)) = closed (-15) 10 := by rfl + +example : (closed (-3) (-2)).mul (closed 4 5) = closed (-15) (-8) := by rfl + +example : (closed (-3) (-2)).mul (closed (-4) 5) = closed (-15) 12 := by rfl + +example : (closed (-3) (-2)).mul (closed (-5) (-4)) = closed 8 15 := by rfl + +-- Infinite endpoints propagate according to sign, while zero times an unbounded interval is zero. +example : (atLeast 3).mul (atLeast 2) = atLeast 6 := by rfl + +example : (atMost (-3)).mul (atMost (-2)) = atLeast 6 := by rfl + +example : (Interval.singleton (0 : Dyadic)).mul (Interval.univ Dyadic) = + Interval.singleton 0 := by rfl + +example : (atLeast 0).mul (atMost 0) = atMost 0 := by rfl + +example : (atLeast 2).mul (closed (-3) 4) = + Interval.univ Dyadic := by rfl + +end Interval + +section Tactic + +example {x y : ℝ} (hx : x ∈ Set.Icc 1 2) (hy : y ∈ Set.Icc 3 4) : + x * y ∈ Set.Icc 3 8 := by dyadic_interval + +example {x y : ℝ} (hx : x ∈ Set.Icc (-2) 3) (hy : y ∈ Set.Icc (-5) 7) : + x * y ∈ Set.Icc (-15) 21 := by dyadic_interval + +example {x y : ℝ} (hx : 3 ≤ x) (hy : 2 ≤ y) : 6 ≤ x * y := by dyadic_interval + +example {x y : ℝ} (hx : x ≤ -3) (hy : y ≤ -2) : 6 ≤ x * y := by dyadic_interval + +example {x y : ℝ} (hx : 0 ≤ x) (hy : y ≤ 0) : x * y ≤ 0 := by dyadic_interval + +example {x y : ℝ} (hx : x = 0) : x * y = 0 := by dyadic_interval + +example {x y : ℝ} (hx : x ∈ Set.Icc (-1.25) 2.5) (hy : y ∈ Set.Icc 3 4) : + x * y ∈ Set.Icc (-5) 10 := by dyadic_interval [prec := 2] + +example {x y z : ℝ} (hx : x ∈ Set.Icc (-2) 3) (hy : y ∈ Set.Icc 1 4) + (hz : z ∈ Set.Icc (-1) 2) : x * y + z ∈ Set.Icc (-9) 14 := by dyadic_interval + +example {x y : ℝ} (hx : x ∈ Set.Icc ((1 / 3 : ℚ) : ℝ) ((2 / 3 : ℚ) : ℝ)) + (hy : y ∈ Set.Icc 3 6) : x * y ∈ Set.Icc 0.9 4.1 := by + dyadic_interval [prec := 12] + +example {x : ℝ} (hx : x ∈ Set.Icc (-2) 2) : 0 ≤ x * x := by + dyadic_interval [binSplit := 1] + +end Tactic + +section Kernel + +example {a b c d e f : ℝ} (ha : a ∈ Set.Icc (-2) 3) (hb : b ∈ Set.Icc (-4) 5) + (hc : c ∈ Set.Icc 1 2) (hd : d ∈ Set.Icc (-3) (-1)) (he : e ∈ Set.Icc 2 4) + (hf : f ∈ Set.Icc (-1) 1) : a * b + c * d + e * f ∈ Set.Icc (-27) 25 := by + dyadic_interval +kernel + +end Kernel + +end Inclusion.Tests diff --git a/PrimeGaps186-work b/PrimeGaps186-work new file mode 160000 index 00000000000000..61340d0b741630 --- /dev/null +++ b/PrimeGaps186-work @@ -0,0 +1 @@ +Subproject commit 61340d0b74163003b32756bb16e91d9209a5e330 From 1f20a6afe4cafd65ab721a12aef8e04c81f2919d Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Mon, 7 Sep 2026 19:09:07 +0100 Subject: [PATCH 34/38] draft --- .../Algebra/Order/Ring/Unbundled/Basic.lean | 6 + Mathlib/Order/WithBot.lean | 21 ++ .../Tactic/Inclusion/Extension/Interval.lean | 292 +++++------------- 3 files changed, 96 insertions(+), 223 deletions(-) diff --git a/Mathlib/Algebra/Order/Ring/Unbundled/Basic.lean b/Mathlib/Algebra/Order/Ring/Unbundled/Basic.lean index 40148991eda37e..c8be34393a064d 100644 --- a/Mathlib/Algebra/Order/Ring/Unbundled/Basic.lean +++ b/Mathlib/Algebra/Order/Ring/Unbundled/Basic.lean @@ -107,6 +107,12 @@ theorem mul_le_mul_of_nonpos_of_nonneg' [ExistsAddOfLE R] [PosMulMono R] [MulPos (hca : c ≤ a) (hbd : b ≤ d) (ha : 0 ≤ a) (hd : d ≤ 0) : a * b ≤ c * d := (mul_le_mul_of_nonneg_left hbd ha).trans <| mul_le_mul_of_nonpos_right hca hd +/-- Variant of `mul_le_mul_of_nonpos_of_nonneg` with sign assumptions on `a` and `d`. -/ +theorem mul_le_mul_of_nonpos_of_nonneg'' [ExistsAddOfLE R] [PosMulMono R] [MulPosMono R] + [AddRightMono R] [AddRightReflectLE R] + (hac : a ≤ c) (hdb : d ≤ b) (ha : a ≤ 0) (hd : 0 ≤ d) : a * b ≤ c * d := + (mul_le_mul_of_nonpos_left hdb ha).trans <| mul_le_mul_of_nonneg_right hac hd + theorem mul_le_mul_of_nonpos_of_nonpos [ExistsAddOfLE R] [MulPosMono R] [PosMulMono R] [AddRightMono R] [AddRightReflectLE R] (hca : c ≤ a) (hdb : d ≤ b) (hc : c ≤ 0) (hb : b ≤ 0) : a * b ≤ c * d := diff --git a/Mathlib/Order/WithBot.lean b/Mathlib/Order/WithBot.lean index 3578c84775f4ff..5ba61552473512 100644 --- a/Mathlib/Order/WithBot.lean +++ b/Mathlib/Order/WithBot.lean @@ -431,6 +431,27 @@ theorem coe_le_iff : a ≤ x ↔ ∃ b : α, x = b ∧ a ≤ b := by simp [le_if @[to_dual coe_le_iff] theorem le_coe_iff : x ≤ b ↔ ∀ a : α, x = ↑a → a ≤ b := by simp [le_iff_forall] +omit [LE α] in +/-- Compare a mapped value with a finite upper bound. -/ +@[to_dual coe_le_map_iff /-- Compare a finite lower bound with a mapped value. -/] +theorem map_le_coe_iff [LE β] (f : α → β) (a : WithBot α) (x : β) : + WithBot.map f a ≤ x ↔ ∀ b, Option.some b = a → f b ≤ x := by + cases a with + | bot => simp + | coe a => exact WithBot.coe_le_coe.trans (by simp [WithBot.some]) + +/-- Express comparison with a finite lower bound using an order-preserving and order-reflecting +map. -/ +@[to_dual le_coe_iff_map +/-- Express comparison with a finite upper bound using an order-preserving and order-reflecting +map. -/] +theorem coe_le_iff_map [LE β] (f : α → β) + (mono_iff : ∀ {a b}, f a ≤ f b ↔ a ≤ b) (c : α) (a : WithBot α) : + (c : WithBot α) ≤ a ↔ ∃ b, Option.some b = a ∧ f c ≤ f b := by + refine WithBot.coe_le_iff.trans <| exists_congr fun b => and_congr ?_ ?_ + · exact eq_comm + · rw [mono_iff] + @[to_dual (attr := simp)] theorem isMax_coe_iff : IsMax (a : WithBot α) ↔ IsMax a := by simp [IsMax, WithBot.forall] diff --git a/Mathlib/Tactic/Inclusion/Extension/Interval.lean b/Mathlib/Tactic/Inclusion/Extension/Interval.lean index 51f9007abe90d8..1964945a1c6d3e 100644 --- a/Mathlib/Tactic/Inclusion/Extension/Interval.lean +++ b/Mathlib/Tactic/Inclusion/Extension/Interval.lean @@ -265,6 +265,24 @@ def Interval.mulBound [Mul α] [Zero α] [DecidableEq α] : | none, some b => if b = 0 then some 0 else none | none, none => none +/-- Show that `mulBound a b`, interpreted as a lower bound and mapped by `f`, is at most `z`. -/ +theorem Interval.map_mulBound_le [Mul α] [Zero α] [DecidableEq α] [LE β] + (f : α → β) (a b : Option α) {z : β} + (hmul : ∀ x y, a = some x → b = some y → f (x * y) ≤ z) + (hzero : (a = none ∧ b = some 0 ∨ a = some 0 ∧ b = none) → f 0 ≤ z) : + WithBot.map f (Interval.mulBound a b : WithBot α) ≤ z := by + cases a <;> cases b <;> simp only [Interval.mulBound] <;> try split_ifs + all_goals first | exact bot_le | apply WithBot.coe_le_coe.mpr + all_goals grind + +/-- Show that `mulBound a b`, interpreted as an upper bound and mapped by `f`, is at least `z`. -/ +theorem Interval.le_map_mulBound [Mul α] [Zero α] [DecidableEq α] [LE β] + (f : α → β) (a b : Option α) {z : β} + (hmul : ∀ x y, a = some x → b = some y → z ≤ f (x * y)) + (hzero : (a = none ∧ b = some 0 ∨ a = some 0 ∧ b = none) → z ≤ f 0) : + z ≤ WithTop.map f (Interval.mulBound a b : WithTop α) := + Interval.map_mulBound_le (β := βᵒᵈ) f a b hmul hzero + -- `WithBot α` and `WithTop α` are definitionally `Option α`, so `mulBound` handles both. /-- Multiply two intervals. -/ def Interval.mul [Mul α] [Zero α] [LinearOrder α] (I J : Interval α) : Interval α := @@ -291,233 +309,61 @@ def Interval.mul [Mul α] [Zero α] [LinearOrder α] (I J : Interval α) : Inter ⟨min (Interval.mulBound I.lb J.ub) (Interval.mulBound I.ub J.lb), max (Interval.mulBound I.lb J.lb) (Interval.mulBound I.ub J.ub)⟩ -private theorem map_mulBound_le [Mul α] [Zero α] [DecidableEq α] [Preorder β] - (f : α → β) (a b : Option α) {z : β} - (hmul : ∀ x y, a = some x → b = some y → f (x * y) ≤ z) - (hzero : (a = none ∧ b = some 0 ∨ a = some 0 ∧ b = none) → f 0 ≤ z) : - WithBot.map f (Interval.mulBound a b : WithBot α) ≤ z := by - rcases a with _ | a <;> rcases b with _ | b <;> - simp only [Interval.mulBound] <;> try split_ifs - all_goals first | exact bot_le | apply WithBot.coe_le_coe.mpr - all_goals first - | exact hzero (by simp_all [WithBot.none_eq_bot]; rfl) - | exact hmul _ _ rfl rfl - -private theorem le_map_mulBound [Mul α] [Zero α] [DecidableEq α] [Preorder β] - (f : α → β) (a b : Option α) {z : β} - (hmul : ∀ x y, a = some x → b = some y → z ≤ f (x * y)) - (hzero : (a = none ∧ b = some 0 ∨ a = some 0 ∧ b = none) → z ≤ f 0) : - z ≤ WithTop.map f (Interval.mulBound a b : WithTop α) := by - rcases a with _ | a <;> rcases b with _ | b <;> - simp only [Interval.mulBound] <;> try split_ifs - all_goals first | exact le_top | apply WithTop.coe_le_coe.mpr - all_goals first - | exact hzero (by simp_all [WithTop.none_eq_top]; rfl) - | exact hmul _ _ rfl rfl - -private theorem nonneg_of_mem_map [Preorder α] [Preorder β] [Zero α] [Zero β] (f : α ↪o β) - (map_zero : f 0 = 0) {x : β} {I : Interval α} (hI : 0 ≤ I.lb) - (hx : x ∈ I.map f) : 0 ≤ x := by - simpa [map_zero] using (f.monotone.withBot_map hI).trans hx.1 - -private theorem nonpos_of_mem_map [Preorder α] [Preorder β] [Zero α] [Zero β] (f : α ↪o β) - (map_zero : f 0 = 0) {x : β} {I : Interval α} (hI : I.ub ≤ 0) - (hx : x ∈ I.map f) : x ≤ 0 := by - simpa [map_zero] using hx.2.trans (f.monotone.withTop_map hI) - -private theorem map_lb_le_of_eq [Preorder β] (f : α → β) {a : α} {x : β} - {I : Interval α} (hx : x ∈ I.map f) (h : I.lb = some a) : f a ≤ x := by - rw [Interval.map, h] at hx - exact WithBot.coe_le_coe.mp hx.1 - -private theorem le_map_ub_of_eq [Preorder β] (f : α → β) {a : α} {x : β} - {I : Interval α} (hx : x ∈ I.map f) (h : I.ub = some a) : x ≤ f a := by - rw [Interval.map, h] at hx - exact WithTop.coe_le_coe.mp hx.2 - -private theorem zero_le_map_lb_iff [LinearOrder α] [LinearOrder β] [Zero α] [Zero β] - (f : α ↪o β) (map_zero : f 0 = 0) {a : α} {I : Interval α} - (h : I.lb = some a) : 0 ≤ f a ↔ 0 ≤ I.lb := by - rw [h, ← map_zero, f.le_iff_le] - exact (WithBot.coe_le_coe (a := (0 : α)) (b := a)).symm - -private theorem map_ub_le_zero_iff [LinearOrder α] [LinearOrder β] [Zero α] [Zero β] - (f : α ↪o β) (map_zero : f 0 = 0) {a : α} {I : Interval α} - (h : I.ub = some a) : f a ≤ 0 ↔ I.ub ≤ 0 := by - rw [h, ← map_zero, f.le_iff_le] - exact (WithTop.coe_le_coe (a := (0 : α)) (b := a)).symm - -private theorem eq_zero_of_nonneg_of_ub_eq [PartialOrder β] [Zero α] [Zero β] - (f : α → β) (map_zero : f 0 = 0) {x : β} {I : Interval α} (hx0 : 0 ≤ x) - (hx : x ∈ I.map f) (h : I.ub = some 0) : x = 0 := - le_antisymm (by simpa [map_zero] using le_map_ub_of_eq f hx h) hx0 - -private theorem eq_zero_of_lb_eq_of_nonpos [PartialOrder β] [Zero α] [Zero β] - (f : α → β) (map_zero : f 0 = 0) {x : β} {I : Interval α} (hx0 : x ≤ 0) - (hx : x ∈ I.map f) (h : I.lb = some 0) : x = 0 := - le_antisymm hx0 (by simpa [map_zero] using map_lb_le_of_eq f hx h) - theorem Interval.mul_mem [Mul α] [Zero α] [LinearOrder α] [Ring β] [LinearOrder β] [IsStrictOrderedRing β] (f : α ↪o β) (map_zero : f 0 = 0) (map_mul : ∀ a b, f (a * b) = f a * f b) {x y : β} {I J : Interval α} (hx : x ∈ I.map f) (hy : y ∈ J.map f) : x * y ∈ (I.mul J).map f := by - rw [Interval.mem_def] - have hxl {a : α} (h : I.lb = some a) : f a ≤ x := map_lb_le_of_eq f hx h - have hxu {a : α} (h : I.ub = some a) : x ≤ f a := le_map_ub_of_eq f hx h - have hyl {a : α} (h : J.lb = some a) : f a ≤ y := map_lb_le_of_eq f hy h - have hyu {a : α} (h : J.ub = some a) : y ≤ f a := le_map_ub_of_eq f hy h - by_cases hIl : 0 ≤ I.lb - · have hx0 := nonneg_of_mem_map f map_zero hIl hx - by_cases hJl : 0 ≤ J.lb - · have hy0 := nonneg_of_mem_map f map_zero hJl hy - simp only [Interval.mul, hIl, hJl, ite_true] - constructor - · apply map_mulBound_le - · grind [mul_le_mul, zero_le_map_lb_iff] - · rintro (⟨ha, -⟩ | ⟨-, hb⟩) - all_goals simp_all [WithBot.none_eq_bot] - · apply le_map_mulBound <;> - grind [mul_le_mul, eq_zero_of_nonneg_of_ub_eq] - · by_cases hJu : J.ub ≤ 0 - · have hy0 := nonpos_of_mem_map f map_zero hJu hy - simp only [Interval.mul, hIl, hJl, hJu, ite_true, ite_false] - constructor - · apply map_mulBound_le - · grind [mul_le_mul_of_nonneg_of_nonpos] - · rintro (⟨-, hb⟩ | ⟨ha, -⟩) <;> - grind [eq_zero_of_lb_eq_of_nonpos, eq_zero_of_nonneg_of_ub_eq] - · apply le_map_mulBound - · grind [mul_le_mul_of_nonneg_of_nonpos, zero_le_map_lb_iff] - · rintro (⟨ha, -⟩ | ⟨-, hb⟩) - · simp [ha, WithBot.none_eq_bot] at hIl - · simp [hb, WithTop.none_eq_top] at hJu - · simp only [Interval.mul, hIl, hJl, hJu, ite_false] - constructor - · apply map_mulBound_le - · grind [mul_le_mul_of_nonneg_of_nonpos, zero_le_map_lb_iff] - · rintro (⟨-, hb⟩ | ⟨ha, -⟩) - · simp [hb, WithBot.some_eq_coe] at hJl - · rw [map_zero, eq_zero_of_nonneg_of_ub_eq f map_zero hx0 hx ha, zero_mul] - · apply le_map_mulBound - · intro a b ha hb - rw [map_mul] - exact (mul_le_mul_of_nonneg_left (hyu hb) hx0).trans - (mul_le_mul_of_nonneg_right (hxu ha) - (le_of_not_ge <| (map_ub_le_zero_iff f map_zero hb).not.mpr hJu)) - · rintro (⟨-, hb⟩ | ⟨ha, -⟩) - · simp [hb, WithTop.some_eq_coe] at hJu - · rw [map_zero, eq_zero_of_nonneg_of_ub_eq f map_zero hx0 hx ha, zero_mul] - · by_cases hIu : I.ub ≤ 0 - · have hx0 := nonpos_of_mem_map f map_zero hIu hx - by_cases hJl : 0 ≤ J.lb - · have hy0 := nonneg_of_mem_map f map_zero hJl hy - simp only [Interval.mul, hIl, hIu, hJl, ite_true, ite_false] - constructor - · apply map_mulBound_le - · grind [mul_le_mul_of_nonpos_of_nonneg] - · rintro (⟨-, hb⟩ | ⟨ha, -⟩) <;> - grind [eq_zero_of_nonneg_of_ub_eq, eq_zero_of_lb_eq_of_nonpos] - · apply le_map_mulBound - · grind [mul_le_mul_of_nonpos_of_nonneg, map_ub_le_zero_iff] - · rintro (⟨ha, -⟩ | ⟨-, hb⟩) - · simp [ha, WithTop.none_eq_top] at hIu - · simp [hb, WithBot.none_eq_bot] at hJl - · by_cases hJu : J.ub ≤ 0 - · have hy0 := nonpos_of_mem_map f map_zero hJu hy - simp only [Interval.mul, hIl, hIu, hJl, hJu, ite_true, ite_false] - constructor - · apply map_mulBound_le - · grind [mul_le_mul_of_nonpos_of_nonpos', map_ub_le_zero_iff] - · rintro (⟨ha, -⟩ | ⟨-, hb⟩) - all_goals simp_all [WithTop.none_eq_top] - · apply le_map_mulBound - · grind [mul_le_mul_of_nonpos_of_nonpos] - · rintro (⟨-, hb⟩ | ⟨ha, -⟩) <;> - grind [eq_zero_of_lb_eq_of_nonpos] - · simp only [Interval.mul, hIl, hIu, hJl, hJu, ite_false] - constructor - · apply map_mulBound_le - · grind [mul_le_mul_of_nonpos_of_nonneg, map_ub_le_zero_iff] - · rintro (⟨-, hb⟩ | ⟨ha, -⟩) - · exact (hJu (hb.symm ▸ WithTop.coe_le_coe.mpr le_rfl)).elim - · rw [map_zero, eq_zero_of_lb_eq_of_nonpos f map_zero hx0 hx ha, zero_mul] - · apply le_map_mulBound - · grind [mul_le_mul_of_nonpos_of_nonpos', zero_le_map_lb_iff] - · rintro (⟨-, hb⟩ | ⟨ha, -⟩) - · exact (hJl (hb.symm ▸ WithBot.coe_le_coe.mpr le_rfl)).elim - · rw [map_zero, eq_zero_of_lb_eq_of_nonpos f map_zero hx0 hx ha, zero_mul] - · by_cases hJl : 0 ≤ J.lb - · have hy0 := nonneg_of_mem_map f map_zero hJl hy - simp only [Interval.mul, hIl, hIu, hJl, ite_true, ite_false] - constructor - · apply map_mulBound_le - · intro a b ha hb - rw [map_mul] - exact (mul_le_mul_of_nonpos_left (hyu hb) - (le_of_not_ge <| (zero_le_map_lb_iff f map_zero ha).not.mpr hIl)).trans - (mul_le_mul_of_nonneg_right (hxl ha) hy0) - · rintro (⟨-, hb⟩ | ⟨ha, -⟩) - · rw [map_zero, eq_zero_of_nonneg_of_ub_eq f map_zero hy0 hy hb, mul_zero] - · exact (hIl (ha.symm ▸ WithBot.coe_le_coe.mpr le_rfl)).elim - · apply le_map_mulBound - · grind [mul_le_mul, map_ub_le_zero_iff] - · rintro (⟨-, hb⟩ | ⟨ha, -⟩) - · rw [map_zero, eq_zero_of_nonneg_of_ub_eq f map_zero hy0 hy hb, mul_zero] - · exact (hIu (ha.symm ▸ WithTop.coe_le_coe.mpr le_rfl)).elim - · by_cases hJu : J.ub ≤ 0 - · have hy0 := nonpos_of_mem_map f map_zero hJu hy - simp only [Interval.mul, hIl, hIu, hJl, hJu, ite_true, ite_false] - constructor - · apply map_mulBound_le - · grind [mul_le_mul_of_nonneg_of_nonpos', map_ub_le_zero_iff] - · rintro (⟨-, hb⟩ | ⟨ha, -⟩) - · rw [map_zero, eq_zero_of_lb_eq_of_nonpos f map_zero hy0 hy hb, mul_zero] - · simp [ha, WithTop.some_eq_coe] at hIu - · apply le_map_mulBound - · grind [mul_le_mul_of_nonpos_of_nonpos, zero_le_map_lb_iff] - · rintro (⟨-, hb⟩ | ⟨ha, -⟩) - · rw [map_zero, eq_zero_of_lb_eq_of_nonpos f map_zero hy0 hy hb, mul_zero] - · simp [ha, WithBot.some_eq_coe] at hIl - · simp only [Interval.mul, hIl, hIu, hJl, hJu, ite_false] - constructor - · simp only [Interval.map] - by_cases hy0 : 0 ≤ y - · refine (f.monotone.withBot_map (min_le_left _ _)).trans - (map_mulBound_le f I.lb J.ub ?_ ?_) - · intro a b ha hb - rw [map_mul] - exact (mul_le_mul_of_nonpos_left (hyu hb) - (le_of_not_ge <| (zero_le_map_lb_iff f map_zero ha).not.mpr hIl)).trans - (mul_le_mul_of_nonneg_right (hxl ha) hy0) - · rintro (⟨-, hb⟩ | ⟨ha, -⟩) - · simp [hb, WithTop.some_eq_coe] at hJu - · simp [ha, WithBot.some_eq_coe] at hIl - · refine (f.monotone.withBot_map (min_le_right _ _)).trans - (map_mulBound_le f I.ub J.lb ?_ ?_) - · intro a b ha hb - rw [map_mul] - by_cases hx0 : 0 ≤ x - · exact mul_le_mul_of_nonneg_of_nonpos (hxu ha) (hyl hb) hx0 - (le_of_not_ge <| (zero_le_map_lb_iff f map_zero hb).not.mpr hJl) - · exact (mul_nonpos_of_nonneg_of_nonpos - (le_of_not_ge <| (map_ub_le_zero_iff f map_zero ha).not.mpr hIu) - (le_of_not_ge <| (zero_le_map_lb_iff f map_zero hb).not.mpr hJl)).trans - (mul_nonneg_of_nonpos_of_nonpos (le_of_not_ge hx0) (le_of_not_ge hy0)) - · rintro (⟨-, hb⟩ | ⟨ha, -⟩) - · simp [hb, WithBot.some_eq_coe] at hJl - · simp [ha, WithTop.some_eq_coe] at hIu - · simp only [Interval.map] - by_cases hy0 : 0 ≤ y - · refine (le_map_mulBound f I.ub J.ub ?_ ?_).trans - (f.monotone.withTop_map (le_max_right _ _)) - · grind [mul_le_mul, map_ub_le_zero_iff] - · rintro (⟨-, hb⟩ | ⟨ha, -⟩) - all_goals simp_all [WithTop.some_eq_coe] - · refine (le_map_mulBound f I.lb J.lb ?_ ?_).trans - (f.monotone.withTop_map (le_max_left _ _)) - · grind [mul_le_mul_of_nonpos_of_nonpos, zero_le_map_lb_iff] - · rintro (⟨-, hb⟩ | ⟨ha, -⟩) - all_goals simp_all [WithBot.some_eq_coe] + -- Record the finite-endpoint bounds and the meaning of each interval's sign tests. + simp only [Interval.mem_def, Interval.map, WithBot.map_le_coe_iff, + WithTop.coe_le_map_iff] at hx hy + have hnonneg := WithBot.coe_le_iff_map f f.map_rel_iff 0 + have hnonpos := WithTop.le_coe_iff_map f f.map_rel_iff 0 + simp only [WithBot.coe_zero, WithTop.coe_zero, map_zero] at hnonneg hnonpos + unfold Interval.mul + split_ifs <;> constructor + -- Nonnegative × nonnegative. + · apply Interval.map_mulBound_le <;> grind [mul_le_mul] + · apply Interval.le_map_mulBound <;> grind [mul_le_mul] + -- Nonnegative × nonpositive. + · apply Interval.map_mulBound_le <;> grind [mul_le_mul_of_nonneg_of_nonpos] + · apply Interval.le_map_mulBound <;> grind [mul_le_mul_of_nonneg_of_nonpos] + -- Nonnegative × mixed. + · apply Interval.map_mulBound_le <;> grind [mul_le_mul_of_nonneg_of_nonpos] + · apply Interval.le_map_mulBound <;> grind [mul_le_mul_of_nonneg] + -- Nonpositive × nonnegative. + · apply Interval.map_mulBound_le <;> grind [mul_le_mul_of_nonpos_of_nonneg] + · apply Interval.le_map_mulBound <;> grind [mul_le_mul_of_nonpos_of_nonneg] + -- Nonpositive × nonpositive. + · apply Interval.map_mulBound_le <;> grind [mul_le_mul_of_nonpos_of_nonpos'] + · apply Interval.le_map_mulBound <;> grind [mul_le_mul_of_nonpos_of_nonpos] + -- Nonpositive × mixed. + · apply Interval.map_mulBound_le <;> grind [mul_le_mul_of_nonpos_of_nonneg] + · apply Interval.le_map_mulBound <;> grind [mul_le_mul_of_nonpos_of_nonpos'] + -- Mixed × nonnegative. + · apply Interval.map_mulBound_le <;> grind [mul_le_mul_of_nonpos_of_nonneg''] + · apply Interval.le_map_mulBound <;> grind [mul_le_mul] + -- Mixed × nonpositive. + · apply Interval.map_mulBound_le <;> grind [mul_le_mul_of_nonneg_of_nonpos'] + · apply Interval.le_map_mulBound <;> grind [mul_le_mul_of_nonpos_of_nonpos] + -- Mixed × mixed: choose one of the two bounds according to the sign of `y`. + · by_cases hy0 : 0 ≤ y + · refine (f.monotone.withBot_map (min_le_left _ _)).trans + (Interval.map_mulBound_le f I.lb J.ub ?_ ?_) + · grind [mul_le_mul_of_nonpos_of_nonneg''] + · grind + · refine (f.monotone.withBot_map (min_le_right _ _)).trans + (Interval.map_mulBound_le f I.ub J.lb ?_ ?_) + · grind [mul_le_mul_of_nonneg_of_nonpos'] + · grind + · by_cases hy0 : 0 ≤ y + · refine (Interval.le_map_mulBound f I.ub J.ub ?_ ?_).trans + (f.monotone.withTop_map (le_max_right _ _)) + · grind [mul_le_mul_of_nonneg'] + · grind + · refine (Interval.le_map_mulBound f I.lb J.lb ?_ ?_).trans + (f.monotone.withTop_map (le_max_left _ _)) + · grind [mul_le_mul_of_nonpos_of_nonpos] + · grind /-- Check if `r x y` is false is implied by `x ∈ I` and `y ∈ J` -/ def Interval.orderRelFalse (r : α → α → Prop) [DecidableRel r] From 8c84c492346ff518cd111c00ec7197cba8e10453 Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Mon, 7 Sep 2026 19:20:10 +0100 Subject: [PATCH 35/38] chore: keep local inclusion files out of the PR --- .gitignore | 4 ---- .worktrees/inclusion-primegaps | 1 - PrimeGaps186-work | 1 - 3 files changed, 6 deletions(-) delete mode 160000 .worktrees/inclusion-primegaps delete mode 160000 PrimeGaps186-work diff --git a/.gitignore b/.gitignore index 8bb408051ab6cc..6973f6af86cc29 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,3 @@ scripts/.rm_set_option_progress.jsonl # Output of scripts/export_crossrefs.lean /crossrefs.json - -# Personal files for developing the inclusion tactic -/Mathlib/Tactic/Inclusion/notes.md -/MathlibTest/Tactic/Inclusion/Scratch.lean diff --git a/.worktrees/inclusion-primegaps b/.worktrees/inclusion-primegaps deleted file mode 160000 index 7f20fbce9f0486..00000000000000 --- a/.worktrees/inclusion-primegaps +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7f20fbce9f0486e87b2e0ea4043e21e679bfbbf1 diff --git a/PrimeGaps186-work b/PrimeGaps186-work deleted file mode 160000 index 61340d0b741630..00000000000000 --- a/PrimeGaps186-work +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 61340d0b74163003b32756bb16e91d9209a5e330 From 5d3c99563b7217b8bb1481cf772cb908ea48adb2 Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Tue, 8 Sep 2026 00:53:19 +0100 Subject: [PATCH 36/38] improve --- .../Algebra/Order/Ring/Unbundled/Basic.lean | 1 - Mathlib/Order/WithBot.lean | 21 ---- .../Tactic/Inclusion/Extension/Interval.lean | 109 ++++++++---------- 3 files changed, 46 insertions(+), 85 deletions(-) diff --git a/Mathlib/Algebra/Order/Ring/Unbundled/Basic.lean b/Mathlib/Algebra/Order/Ring/Unbundled/Basic.lean index 3d8f8cc03e2240..10000be595a02f 100644 --- a/Mathlib/Algebra/Order/Ring/Unbundled/Basic.lean +++ b/Mathlib/Algebra/Order/Ring/Unbundled/Basic.lean @@ -107,7 +107,6 @@ theorem mul_le_mul_of_nonpos_of_nonneg' [ExistsAddOfLE R] [PosMulMono R] [MulPos (hca : c ≤ a) (hbd : b ≤ d) (ha : 0 ≤ a) (hd : d ≤ 0) : a * b ≤ c * d := (mul_le_mul_of_nonneg_left hbd ha).trans <| mul_le_mul_of_nonpos_right hca hd -/-- Variant of `mul_le_mul_of_nonpos_of_nonneg` with sign assumptions on `a` and `d`. -/ theorem mul_le_mul_of_nonpos_of_nonneg'' [ExistsAddOfLE R] [PosMulMono R] [MulPosMono R] [AddRightMono R] [AddRightReflectLE R] (hac : a ≤ c) (hdb : d ≤ b) (ha : a ≤ 0) (hd : 0 ≤ d) : a * b ≤ c * d := diff --git a/Mathlib/Order/WithBot.lean b/Mathlib/Order/WithBot.lean index 6ba47e7695b5dd..ea24709e219858 100644 --- a/Mathlib/Order/WithBot.lean +++ b/Mathlib/Order/WithBot.lean @@ -434,27 +434,6 @@ theorem coe_le_iff : a ≤ x ↔ ∃ b : α, x = b ∧ a ≤ b := by simp [le_if @[to_dual coe_le_iff] theorem le_coe_iff : x ≤ b ↔ ∀ a : α, x = ↑a → a ≤ b := by simp [le_iff_forall] -omit [LE α] in -/-- Compare a mapped value with a finite upper bound. -/ -@[to_dual coe_le_map_iff /-- Compare a finite lower bound with a mapped value. -/] -theorem map_le_coe_iff [LE β] (f : α → β) (a : WithBot α) (x : β) : - WithBot.map f a ≤ x ↔ ∀ b, Option.some b = a → f b ≤ x := by - cases a with - | bot => simp - | coe a => exact WithBot.coe_le_coe.trans (by simp [WithBot.some]) - -/-- Express comparison with a finite lower bound using an order-preserving and order-reflecting -map. -/ -@[to_dual le_coe_iff_map -/-- Express comparison with a finite upper bound using an order-preserving and order-reflecting -map. -/] -theorem coe_le_iff_map [LE β] (f : α → β) - (mono_iff : ∀ {a b}, f a ≤ f b ↔ a ≤ b) (c : α) (a : WithBot α) : - (c : WithBot α) ≤ a ↔ ∃ b, Option.some b = a ∧ f c ≤ f b := by - refine WithBot.coe_le_iff.trans <| exists_congr fun b => and_congr ?_ ?_ - · exact eq_comm - · rw [mono_iff] - @[to_dual (attr := simp)] theorem isMax_coe_iff : IsMax (a : WithBot α) ↔ IsMax a := by simp [IsMax, WithBot.forall] diff --git a/Mathlib/Tactic/Inclusion/Extension/Interval.lean b/Mathlib/Tactic/Inclusion/Extension/Interval.lean index 6a6c17eb2511d3..7d64ef75c91914 100644 --- a/Mathlib/Tactic/Inclusion/Extension/Interval.lean +++ b/Mathlib/Tactic/Inclusion/Extension/Interval.lean @@ -24,6 +24,15 @@ type represents a possibly unbounded interval with closed endpoints. namespace Inclusion +-- local `grind` rules +attribute [local grind unfold] WithBot.some WithTop.some +attribute [local grind norm ←] WithBot.coe_zero WithTop.coe_zero + WithBot.none_eq_bot WithTop.none_eq_top +attribute [local grind norm] WithBot.coe_le_coe WithTop.coe_le_coe +attribute [local grind =] WithBot.coe_le_iff WithTop.le_coe_iff + +local grind_pattern OrderEmbedding.le_iff_le => a ≤ b, f a, f b + variable {α β : Type*} /-- An `Interval` represents a possibly unbounded interval with closed endpoints. -/ @@ -47,6 +56,13 @@ theorem Interval.mem_def [Preorder α] {x : α} {I : Interval α} : def Interval.map (I : Interval α) (f : α → β) : Interval β := ⟨WithBot.map f I.lb, WithTop.map f I.ub⟩ +@[grind =] +theorem Interval.mem_map_iff [Preorder β] (f : α → β) {x : β} {I : Interval α} : + x ∈ I.map f ↔ (∀ a : α, I.lb = ↑a → f a ≤ x) ∧ + (∀ a : α, I.ub = ↑a → x ≤ f a) := by + simp [Interval.map, WithBot.le_coe_iff, WithTop.coe_le_iff, + WithBot.map_eq_some_iff, WithTop.map_eq_some_iff] + /-- The interval unbounded on both sides. -/ def Interval.univ (α : Type*) : Interval α := ⟨⊥, ⊤⟩ @@ -86,11 +102,11 @@ def Interval.Ici (lb : WithBot α) : Interval α := ⟨lb, ⊤⟩ theorem Interval.mem_Iic_of_le [Preorder α] {x y : α} {I : Interval α} (hxy : x ≤ y) (hy : y ∈ I) : x ∈ Interval.Iic I.ub := by - grind [Iic, bot_le, WithTop.coe_le_coe.mpr hxy] + grind [Interval.Iic, WithBot.le_coe_iff, WithTop.coe_le_iff] theorem Interval.mem_Ici_of_le [Preorder α] {x y : α} {I : Interval α} (hxy : x ≤ y) (hx : x ∈ I) : y ∈ Interval.Ici I.lb := by - grind [Ici, le_top, WithBot.coe_le_coe.mpr hxy] + grind [Interval.Ici, WithBot.le_coe_iff, WithTop.coe_le_iff] theorem Interval.mem_Icc_of_le [Preorder α] {a b x : α} {I J : Interval α} (ha : a ∈ I) (hax : a ≤ x) (hxb : x ≤ b) (hb : b ∈ J) : @@ -149,6 +165,7 @@ instance [LinearOrder α] : Refine (Interval α) α where refine := Interval.inter mem_refine hs ht := ⟨max_le hs.1 ht.1, le_min hs.2 ht.2⟩ +@[simp, grind =] theorem Interval.map_inter [LinearOrder α] [LinearOrder β] (f : α ↪o β) (I J : Interval α) : (I.inter J).map f = (I.map f).inter (J.map f) := by simp [f.monotone.withBot_map.map_max, f.monotone.withTop_map.map_min, @@ -157,8 +174,7 @@ theorem Interval.map_inter [LinearOrder α] [LinearOrder β] (f : α ↪o β) (I theorem Interval.inter_mem [LinearOrder α] [LinearOrder β] (f : α ↪o β) {x : β} {I J : Interval α} (hxI : x ∈ I.map f) (hxJ : x ∈ J.map f) : x ∈ (I.inter J).map f := by - rw [Interval.map_inter] - exact Refine.mem_refine hxI hxJ + grind [Interval.inter, max_le, le_min] /-- The convex hull of two intervals. -/ def Interval.hull [LinearOrder α] (I J : Interval α) : Interval α := @@ -166,17 +182,18 @@ def Interval.hull [LinearOrder α] (I J : Interval α) : Interval α := theorem Interval.mem_hull_left [LinearOrder α] {x : α} {I J : Interval α} (hx : x ∈ I) : x ∈ I.hull J := by - grind [Interval.hull] + grind [Interval.hull, min_le_left, le_max_left] theorem Interval.mem_hull_right [LinearOrder α] {x : α} {I J : Interval α} (hx : x ∈ J) : x ∈ I.hull J := by - grind [Interval.hull] + grind [Interval.hull, min_le_right, le_max_right] instance [LinearOrder α] : Coarsen (Interval α) α where coarsen := Interval.hull mem_coarsen_left := Interval.mem_hull_left mem_coarsen_right := Interval.mem_hull_right +@[simp, grind =] theorem Interval.map_hull [LinearOrder α] [LinearOrder β] (f : α ↪o β) (I J : Interval α) : (I.hull J).map f = (I.map f).hull (J.map f) := by simp [f.monotone.withBot_map.map_min, f.monotone.withTop_map.map_max, @@ -184,13 +201,11 @@ theorem Interval.map_hull [LinearOrder α] [LinearOrder β] (f : α ↪o β) (I theorem Interval.hull_mem_left [LinearOrder α] [LinearOrder β] (f : α ↪o β) {x : β} {I J : Interval α} (hx : x ∈ I.map f) : x ∈ (I.hull J).map f := by - rw [Interval.map_hull] - exact Interval.mem_hull_left hx + simpa using Interval.mem_hull_left hx theorem Interval.hull_mem_right [LinearOrder α] [LinearOrder β] (f : α ↪o β) {x : β} {I J : Interval α} (hx : x ∈ J.map f) : x ∈ (I.hull J).map f := by - rw [Interval.map_hull] - exact Interval.mem_hull_right hx + simpa using Interval.mem_hull_right hx /-- Add two intervals. -/ def Interval.add [Add α] (I J : Interval α) : Interval α where @@ -212,9 +227,7 @@ theorem Interval.add_ub [AddZero α] (I J : Interval α) : (I.add J).ub = I.ub + theorem Interval.add_mem [AddZero α] [AddCommMonoid β] [Preorder β] [IsOrderedAddMonoid β] (f : α →+ β) {x y : β} {I J : Interval α} (hx : x ∈ I.map f) (hy : y ∈ J.map f) : x + y ∈ (I.add J).map f := by - constructor - · simpa [Interval.map] using add_le_add hx.1 hy.1 - · simpa [Interval.map] using add_le_add hx.2 hy.2 + grind [Interval.add, add_le_add] /-- Negate an interval. -/ def Interval.neg [Neg α] (I : Interval α) : Interval α where @@ -227,15 +240,7 @@ def Interval.neg [Neg α] (I : Interval α) : Interval α where theorem Interval.neg_mem [AddGroup α] [AddCommGroup β] [Preorder β] [IsOrderedAddMonoid β] (f : α →+ β) {x : β} {I : Interval α} (hx : x ∈ I.map f) : -x ∈ I.neg.map f := by - constructor - · rcases I with ⟨il, _ | iu⟩ - · simp [Interval.neg, Interval.map] - apply WithBot.coe_le_coe.mpr - simpa using neg_le_neg_iff.mpr (WithTop.coe_le_coe.mp hx.2) - · rcases I with ⟨_ | il, iu⟩ - · simp [Interval.neg, Interval.map] - apply WithTop.coe_le_coe.mpr - simpa using neg_le_neg_iff.mpr (WithBot.coe_le_coe.mp hx.1) + grind [Interval.neg, neg_le_neg_iff] /-- Subtract one interval from another. -/ def Interval.sub [Sub α] (I J : Interval α) : Interval α where @@ -247,9 +252,7 @@ def Interval.sub [Sub α] (I J : Interval α) : Interval α where | _, _ => ⊤ theorem Interval.sub_eq_add_neg [AddGroup α] (I J : Interval α) : I.sub J = I.add J.neg := by - rcases I with ⟨_ | il, _ | iu⟩ <;> - rcases J with ⟨_ | jl, _ | ju⟩ <;> - simp [Interval.sub, Interval.add, Interval.neg, _root_.sub_eq_add_neg] + grind [Interval.sub, Interval.add, Interval.neg, _root_.sub_eq_add_neg] theorem Interval.sub_mem [AddGroup α] [AddCommGroup β] [Preorder β] [IsOrderedAddMonoid β] (f : α →+ β) {x y : β} {I J : Interval α} @@ -265,19 +268,22 @@ def Interval.mulBound [Mul α] [Zero α] [DecidableEq α] : | none, some b => if b = 0 then some 0 else none | none, none => none -/-- Show that `mulBound a b`, interpreted as a lower bound and mapped by `f`, is at most `z`. -/ -@[to_dual le_map_mulBound -/-- Show that `mulBound a b`, interpreted as an upper bound and mapped by `f`, is at least `z`. -/] +@[to_dual le_map_mulBound] theorem Interval.map_mulBound_le [Mul α] [Zero α] [DecidableEq α] [LE β] (f : α → β) (a b : Option α) {z : β} (hmul : ∀ x y, a = some x → b = some y → f (x * y) ≤ z) (hzero : (a = none ∧ b = some 0 ∨ a = some 0 ∧ b = none) → f 0 ≤ z) : WithBot.map f (Interval.mulBound a b : WithBot α) ≤ z := by - cases a <;> cases b <;> simp only [Interval.mulBound] <;> try split_ifs - all_goals first | exact bot_le | apply WithBot.coe_le_coe.mpr - all_goals grind + rcases a with _ | a <;> rcases b with _ | b + · exact bot_le + · by_cases hb : b = 0 + · simpa [Interval.mulBound, hb, WithBot.some_eq_coe] using hzero (by simp [hb]) + · simp [Interval.mulBound, hb, WithBot.none_eq_bot] + · by_cases ha : a = 0 + · simpa [Interval.mulBound, ha, WithBot.some_eq_coe] using hzero (by simp [ha]) + · simp [Interval.mulBound, ha, WithBot.none_eq_bot] + · exact WithBot.coe_le_coe.mpr (hmul a b rfl rfl) --- `WithBot α` and `WithTop α` are definitionally `Option α`, so `mulBound` handles both. /-- Multiply two intervals. -/ def Interval.mul [Mul α] [Zero α] [LinearOrder α] (I J : Interval α) : Interval α := if 0 ≤ I.lb then @@ -307,57 +313,34 @@ theorem Interval.mul_mem [Mul α] [Zero α] [LinearOrder α] [Ring β] [LinearOr [IsStrictOrderedRing β] (f : α ↪o β) (map_zero : f 0 = 0) (map_mul : ∀ a b, f (a * b) = f a * f b) {x y : β} {I J : Interval α} (hx : x ∈ I.map f) (hy : y ∈ J.map f) : x * y ∈ (I.mul J).map f := by - -- Record the finite-endpoint bounds and the meaning of each interval's sign tests. - simp only [Interval.mem_def, Interval.map, WithBot.map_le_coe_iff, - WithTop.coe_le_map_iff] at hx hy - have hnonneg := WithBot.coe_le_iff_map f f.map_rel_iff 0 - have hnonpos := WithTop.le_coe_iff_map f f.map_rel_iff 0 - simp only [WithBot.coe_zero, WithTop.coe_zero, map_zero] at hnonneg hnonpos unfold Interval.mul split_ifs <;> constructor - -- Nonnegative × nonnegative. · apply Interval.map_mulBound_le <;> grind [mul_le_mul] · apply Interval.le_map_mulBound <;> grind [mul_le_mul] - -- Nonnegative × nonpositive. · apply Interval.map_mulBound_le <;> grind [mul_le_mul_of_nonneg_of_nonpos] · apply Interval.le_map_mulBound <;> grind [mul_le_mul_of_nonneg_of_nonpos] - -- Nonnegative × mixed. · apply Interval.map_mulBound_le <;> grind [mul_le_mul_of_nonneg_of_nonpos] · apply Interval.le_map_mulBound <;> grind [mul_le_mul_of_nonneg] - -- Nonpositive × nonnegative. · apply Interval.map_mulBound_le <;> grind [mul_le_mul_of_nonpos_of_nonneg] · apply Interval.le_map_mulBound <;> grind [mul_le_mul_of_nonpos_of_nonneg] - -- Nonpositive × nonpositive. · apply Interval.map_mulBound_le <;> grind [mul_le_mul_of_nonpos_of_nonpos'] · apply Interval.le_map_mulBound <;> grind [mul_le_mul_of_nonpos_of_nonpos] - -- Nonpositive × mixed. · apply Interval.map_mulBound_le <;> grind [mul_le_mul_of_nonpos_of_nonneg] · apply Interval.le_map_mulBound <;> grind [mul_le_mul_of_nonpos_of_nonpos'] - -- Mixed × nonnegative. · apply Interval.map_mulBound_le <;> grind [mul_le_mul_of_nonpos_of_nonneg''] · apply Interval.le_map_mulBound <;> grind [mul_le_mul] - -- Mixed × nonpositive. · apply Interval.map_mulBound_le <;> grind [mul_le_mul_of_nonneg_of_nonpos'] · apply Interval.le_map_mulBound <;> grind [mul_le_mul_of_nonpos_of_nonpos] - -- Mixed × mixed: choose one of the two bounds according to the sign of `y`. · by_cases hy0 : 0 ≤ y - · refine (f.monotone.withBot_map (min_le_left _ _)).trans - (Interval.map_mulBound_le f I.lb J.ub ?_ ?_) - · grind [mul_le_mul_of_nonpos_of_nonneg''] - · grind - · refine (f.monotone.withBot_map (min_le_right _ _)).trans - (Interval.map_mulBound_le f I.ub J.lb ?_ ?_) - · grind [mul_le_mul_of_nonneg_of_nonpos'] - · grind + · apply (f.monotone.withBot_map (min_le_left _ _)).trans + apply Interval.map_mulBound_le <;> grind [mul_le_mul_of_nonpos_of_nonneg''] + · apply (f.monotone.withBot_map (min_le_right _ _)).trans + apply Interval.map_mulBound_le <;> grind [mul_le_mul_of_nonneg_of_nonpos'] · by_cases hy0 : 0 ≤ y - · refine (Interval.le_map_mulBound f I.ub J.ub ?_ ?_).trans - (f.monotone.withTop_map (le_max_right _ _)) - · grind [mul_le_mul_of_nonneg'] - · grind - · refine (Interval.le_map_mulBound f I.lb J.lb ?_ ?_).trans - (f.monotone.withTop_map (le_max_left _ _)) - · grind [mul_le_mul_of_nonpos_of_nonpos] - · grind + · apply (f.monotone.withTop_map (le_max_right _ _)).trans' + apply Interval.le_map_mulBound <;> grind [mul_le_mul_of_nonneg'] + · apply (f.monotone.withTop_map (le_max_left _ _)).trans' + apply Interval.le_map_mulBound <;> grind [mul_le_mul_of_nonpos_of_nonpos] /-- Check if `r x y` is false is implied by `x ∈ I` and `y ∈ J` -/ def Interval.orderRelFalse (r : α → α → Prop) [DecidableRel r] From c8db13ca768c017ee97235ed74e9c57083379ecd Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Tue, 8 Sep 2026 01:04:07 +0100 Subject: [PATCH 37/38] fix tests --- MathlibTest/Tactic/Inclusion/Basic.lean | 14 +++ .../Tactic/Inclusion/Multiplication.lean | 94 ------------------- 2 files changed, 14 insertions(+), 94 deletions(-) delete mode 100644 MathlibTest/Tactic/Inclusion/Multiplication.lean diff --git a/MathlibTest/Tactic/Inclusion/Basic.lean b/MathlibTest/Tactic/Inclusion/Basic.lean index 4e9b3d297ff8f8..5fda808661801f 100644 --- a/MathlibTest/Tactic/Inclusion/Basic.lean +++ b/MathlibTest/Tactic/Inclusion/Basic.lean @@ -100,6 +100,14 @@ example {x : ℝ} (hx : x ∈ (⟨1, 2⟩ : Interval Dyadic)) : x + x ≤ 4 := b example {x y : ℝ} (hx : x ∈ Set.Icc 1 2) (hy : y ∈ Set.Icc 3 4) : x + y ∈ Set.Icc 4 6 := by dyadic_interval +example {x y : ℝ} (hx : x ∈ Set.Icc 1 2) (hy : y ∈ Set.Icc 3 4) : + x * y ∈ Set.Icc 3 8 := by dyadic_interval + +example {x y : ℝ} (hx : x ∈ Set.Icc (-2) 3) (hy : y ∈ Set.Icc (-5) 7) : + x * y ∈ Set.Icc (-15) 21 := by dyadic_interval + +example {x y : ℝ} (hx : 3 ≤ x) (hy : 2 ≤ y) : 6 ≤ x * y := by dyadic_interval + example {x : ℝ} (hx : x ∈ Set.Icc (-2) 1) : -x ∈ Set.Icc (-1) 2 := by dyadic_interval example {x y : ℝ} (hx₀ : 2 ≤ x) (hx₁ : x ≤ 3) (hy₀ : 0 ≤ y) (hy₁ : y ≤ 1) : @@ -118,6 +126,9 @@ example {x y z : ℝ} (hx : x ∈ Set.Ico 0 1) (hy : y ∈ Set.Ioc 1 2) example {x y z : ℝ} (hx₀ : -2 ≤ x) (hx₁ : x ≤ 1) (hy₀ : 1 ≤ y) (hy₁ : y ≤ 3) (hz₀ : 4 ≤ z) (hz₁ : z ≤ 5) : x + y - z ∈ Set.Icc (-6) 0 := by dyadic_interval +example {x y z : ℝ} (hx₀ : -2 ≤ x) (hx₁ : x ≤ 3) (hy : y ∈ Set.Icc 1 4) + (hz : z ∈ Set.Icc (-1) 2) : x * y + z ∈ Set.Icc (-9) 14 := by dyadic_interval + example {x y : ℝ} (hx₀ : x ≤ 3) (hx₁ : x ≤ 2) (hy₀ : 0 ≤ y) (hy₁ : 1 ≤ y) : x - y ≤ 1 := by dyadic_interval @@ -127,6 +138,9 @@ example {x : ℝ} (hx₀ : 1 ≤ x) (hx₁ : x ≤ 2) : example {x y : ℝ} (hx : x ≤ 1.25) (hy : 0.5 ≤ y) : x - y + 2.5 ≤ 3.25 := by dyadic_interval [prec := 2] +example {x y : ℝ} (hx₀ : 1 ≤ x) (hx₁ : x ≤ 2) (hy : y ∈ Set.Icc 0.5 1.25) : + x * y + ((1 / 3 : ℚ) : ℝ) ∈ Set.Icc 0.8 2.9 := by dyadic_interval [prec := 12] + end Arithmetic section Splitting diff --git a/MathlibTest/Tactic/Inclusion/Multiplication.lean b/MathlibTest/Tactic/Inclusion/Multiplication.lean deleted file mode 100644 index d003daa4f0e432..00000000000000 --- a/MathlibTest/Tactic/Inclusion/Multiplication.lean +++ /dev/null @@ -1,94 +0,0 @@ -/- -Copyright (c) 2026 David Ledvinka. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: David Ledvinka --/ -import Mathlib.Tactic.Inclusion.Extension.IntervalDyadicReal.Tactic - -open Inclusion - -namespace Inclusion.Tests - -section Interval - -private def closed (lb ub : Dyadic) : Interval Dyadic := Interval.Icc lb ub - -private def atLeast (lb : Dyadic) : Interval Dyadic := Interval.Ici lb - -private def atMost (ub : Dyadic) : Interval Dyadic := Interval.Iic ub - --- The nine possible pairs of interval signs use their sharp endpoint formulas. -example : (closed 1 2).mul (closed 3 4) = closed 3 8 := by rfl - -example : (closed 1 2).mul (closed (-3) 4) = closed (-6) 8 := by rfl - -example : (closed 1 2).mul (closed (-4) (-3)) = closed (-8) (-3) := by rfl - -example : (closed (-2) 3).mul (closed 4 5) = closed (-10) 15 := by rfl - -example : (closed (-2) 3).mul (closed (-5) 7) = closed (-15) 21 := by rfl - -example : (closed (-2) 3).mul (closed (-5) (-4)) = closed (-15) 10 := by rfl - -example : (closed (-3) (-2)).mul (closed 4 5) = closed (-15) (-8) := by rfl - -example : (closed (-3) (-2)).mul (closed (-4) 5) = closed (-15) 12 := by rfl - -example : (closed (-3) (-2)).mul (closed (-5) (-4)) = closed 8 15 := by rfl - --- Infinite endpoints propagate according to sign, while zero times an unbounded interval is zero. -example : (atLeast 3).mul (atLeast 2) = atLeast 6 := by rfl - -example : (atMost (-3)).mul (atMost (-2)) = atLeast 6 := by rfl - -example : (Interval.singleton (0 : Dyadic)).mul (Interval.univ Dyadic) = - Interval.singleton 0 := by rfl - -example : (atLeast 0).mul (atMost 0) = atMost 0 := by rfl - -example : (atLeast 2).mul (closed (-3) 4) = - Interval.univ Dyadic := by rfl - -end Interval - -section Tactic - -example {x y : ℝ} (hx : x ∈ Set.Icc 1 2) (hy : y ∈ Set.Icc 3 4) : - x * y ∈ Set.Icc 3 8 := by dyadic_interval - -example {x y : ℝ} (hx : x ∈ Set.Icc (-2) 3) (hy : y ∈ Set.Icc (-5) 7) : - x * y ∈ Set.Icc (-15) 21 := by dyadic_interval - -example {x y : ℝ} (hx : 3 ≤ x) (hy : 2 ≤ y) : 6 ≤ x * y := by dyadic_interval - -example {x y : ℝ} (hx : x ≤ -3) (hy : y ≤ -2) : 6 ≤ x * y := by dyadic_interval - -example {x y : ℝ} (hx : 0 ≤ x) (hy : y ≤ 0) : x * y ≤ 0 := by dyadic_interval - -example {x y : ℝ} (hx : x = 0) : x * y = 0 := by dyadic_interval - -example {x y : ℝ} (hx : x ∈ Set.Icc (-1.25) 2.5) (hy : y ∈ Set.Icc 3 4) : - x * y ∈ Set.Icc (-5) 10 := by dyadic_interval [prec := 2] - -example {x y z : ℝ} (hx : x ∈ Set.Icc (-2) 3) (hy : y ∈ Set.Icc 1 4) - (hz : z ∈ Set.Icc (-1) 2) : x * y + z ∈ Set.Icc (-9) 14 := by dyadic_interval - -example {x y : ℝ} (hx : x ∈ Set.Icc ((1 / 3 : ℚ) : ℝ) ((2 / 3 : ℚ) : ℝ)) - (hy : y ∈ Set.Icc 3 6) : x * y ∈ Set.Icc 0.9 4.1 := by - dyadic_interval [prec := 12] - -example {x : ℝ} (hx : x ∈ Set.Icc (-2) 2) : 0 ≤ x * x := by - dyadic_interval [binSplit := 1] - -end Tactic - -section Kernel - -example {a b c d e f : ℝ} (ha : a ∈ Set.Icc (-2) 3) (hb : b ∈ Set.Icc (-4) 5) - (hc : c ∈ Set.Icc 1 2) (hd : d ∈ Set.Icc (-3) (-1)) (he : e ∈ Set.Icc 2 4) - (hf : f ∈ Set.Icc (-1) 1) : a * b + c * d + e * f ∈ Set.Icc (-27) 25 := by - dyadic_interval +kernel - -end Kernel - -end Inclusion.Tests From 3dab67ecd3c257e37d0220bf5850ae543ea6e942 Mon Sep 17 00:00:00 2001 From: David Ledvinka Date: Tue, 8 Sep 2026 01:28:21 +0100 Subject: [PATCH 38/38] test --- Mathlib/Tactic/Inclusion/Core/ToSet.lean | 6 +++--- Mathlib/Tactic/Inclusion/Extension/Interval.lean | 13 +++---------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/Mathlib/Tactic/Inclusion/Core/ToSet.lean b/Mathlib/Tactic/Inclusion/Core/ToSet.lean index 98e2921256a7ce..52bd3b0ce99b5e 100644 --- a/Mathlib/Tactic/Inclusion/Core/ToSet.lean +++ b/Mathlib/Tactic/Inclusion/Core/ToSet.lean @@ -109,11 +109,11 @@ def IntervalBool.toPropSet : IntervalBool → Set Prop instance : ToSet IntervalBool Prop := ⟨IntervalBool.toPropSet⟩ -@[simp] +@[simp, grind =] theorem IntervalBool.mem_true_iff {p : Prop} : p ∈ IntervalBool.true ↔ p := by simp [ToSet.mem_def, ToSet.toSet, IntervalBool.toPropSet] -@[simp] +@[simp, grind =] theorem IntervalBool.mem_false_iff {p : Prop} : p ∈ IntervalBool.false ↔ ¬p := by simp [ToSet.mem_def, ToSet.toSet, IntervalBool.toPropSet] @@ -123,7 +123,7 @@ theorem IntervalBool.mem_true {p : Prop} (hp : p) : p ∈ IntervalBool.true := theorem IntervalBool.mem_false {p : Prop} (hp : ¬p) : p ∈ IntervalBool.false := IntervalBool.mem_false_iff.mpr hp -@[simp] +@[simp, grind ←] theorem IntervalBool.mem_undetermined (p : Prop) : p ∈ IntervalBool.undetermined := by simpa [ToSet.mem_def, ToSet.toSet, IntervalBool.toPropSet] using Classical.em p diff --git a/Mathlib/Tactic/Inclusion/Extension/Interval.lean b/Mathlib/Tactic/Inclusion/Extension/Interval.lean index 7d64ef75c91914..bce9f7c6c02c47 100644 --- a/Mathlib/Tactic/Inclusion/Extension/Interval.lean +++ b/Mathlib/Tactic/Inclusion/Extension/Interval.lean @@ -111,7 +111,7 @@ theorem Interval.mem_Ici_of_le [Preorder α] {x y : α} {I : Interval α} theorem Interval.mem_Icc_of_le [Preorder α] {a b x : α} {I J : Interval α} (ha : a ∈ I) (hax : a ≤ x) (hxb : x ≤ b) (hb : b ∈ J) : x ∈ Interval.Icc I.lb J.ub := by - grind [Icc, WithBot.coe_le_coe.mpr hax, WithTop.coe_le_coe.mpr hxb] + grind [Interval.Icc, WithBot.le_coe_iff, WithTop.coe_le_iff] theorem Interval.mem_Iic_of_lt [Preorder α] {x y : α} {I : Interval α} (hxy : x < y) (hy : y ∈ I) : x ∈ Interval.Iic I.ub := @@ -274,15 +274,8 @@ theorem Interval.map_mulBound_le [Mul α] [Zero α] [DecidableEq α] [LE β] (hmul : ∀ x y, a = some x → b = some y → f (x * y) ≤ z) (hzero : (a = none ∧ b = some 0 ∨ a = some 0 ∧ b = none) → f 0 ≤ z) : WithBot.map f (Interval.mulBound a b : WithBot α) ≤ z := by - rcases a with _ | a <;> rcases b with _ | b - · exact bot_le - · by_cases hb : b = 0 - · simpa [Interval.mulBound, hb, WithBot.some_eq_coe] using hzero (by simp [hb]) - · simp [Interval.mulBound, hb, WithBot.none_eq_bot] - · by_cases ha : a = 0 - · simpa [Interval.mulBound, ha, WithBot.some_eq_coe] using hzero (by simp [ha]) - · simp [Interval.mulBound, ha, WithBot.none_eq_bot] - · exact WithBot.coe_le_coe.mpr (hmul a b rfl rfl) + cases a <;> cases b <;> + grind [Interval.mulBound, WithBot.map_eq_some_iff, WithBot.le_coe_iff] /-- Multiply two intervals. -/ def Interval.mul [Mul α] [Zero α] [LinearOrder α] (I J : Interval α) : Interval α :=