Built with
doc-gen4 , running Lean4.
Bubbles (
) indicate interactive fragments: hover for details, tap to reveal contents.
Use
Ctrl+↑ Ctrl+↓ to navigate,
Ctrl+🖱️ to focus.
On Mac, use
Cmd instead of
Ctrl .
/-
Copyright (c) 2020 Robert Y. Lewis. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Robert Y. Lewis
Ported by: Scott Morrison
-/
import Mathlib.Tactic.Linarith.Datatypes
import Mathlib.Tactic.Zify
import Mathlib.Tactic.CancelDenoms
import Mathlib.Lean.Exception
import Std.Data.RBMap.Basic
import Mathlib.Data.HashMap
/-!
# Linarith preprocessing
This file contains methods used to preprocess inputs to `linarith`.
In particular, `linarith` works over comparisons of the form `t R 0`, where `R ∈ {<,≤,=}`.
It assumes that expressions in `t` have integer coefficients and that the type of `t` has
well-behaved subtraction.
## Implementation details
A `GlobalPreprocessor` is a function `List Expr → TacticM (List Expr)`. Users can add custom
preprocessing steps by adding them to the `LinarithConfig` object. `Linarith.defaultPreprocessors`
is the main list, and generally none of these should be skipped unless you know what you're doing.
-/
namespace Linarith
/-! ### Preprocessing -/
open Lean Elab Tactic Meta
open Qq
/-- Processor that recursively replaces `P ∧ Q` hypotheses with the pair `P` and `Q`. -/
partial def splitConjunctions : Preprocessor where
name := "split conjunctions"
transform := aux
where
/-- Implementation of the `splitConjunctions` preprocessor. -/
aux ( proof : Expr ) : MetaM ( List Expr ) := do
match (← instantiateMVars (← inferType proof)) : ?m.190
(← instantiateMVars (← instantiateMVars (← inferType proof)) : ?m.190
(← inferType proof) : ?m.104
(← inferType (← inferType proof) : ?m.104
proof (← inferType proof) : ?m.104
) (← instantiateMVars (← inferType proof)) : ?m.190
) . getAppFnArgs with
| ( `` And , #[_, _]) =>
pure : {f : Type ?u.478 → Type ?u.477 } → [self : Pure f ] → {α : Type ?u.478} → α → f α pure ( (← aux (← mkAppM ``And.left #[proof])) ++
(← aux (← mkAppM ``And.right #[proof])) : ?m.526
(← aux (← aux (← mkAppM ``And.left #[proof])) ++
(← aux (← mkAppM ``And.right #[proof])) : ?m.526
(← mkAppM ``And.left #[proof]) : ?m.304
(← mkAppM (← mkAppM ``And.left #[proof]) : ?m.304
`` And.left : ∀ {a b : Prop }, a ∧ b → a And.left (← mkAppM ``And.left #[proof]) : ?m.304
#[ proof (← mkAppM ``And.left #[proof]) : ?m.304
]) (← aux (← mkAppM ``And.left #[proof])) ++
(← aux (← mkAppM ``And.right #[proof])) : ?m.526
) ++
(← aux (← aux (← mkAppM ``And.left #[proof])) ++
(← aux (← mkAppM ``And.right #[proof])) : ?m.526
(← mkAppM ``And.right #[proof]) : ?m.420
(← mkAppM (← mkAppM ``And.right #[proof]) : ?m.420
`` And.right : ∀ {a b : Prop }, a ∧ b → b And.right (← mkAppM ``And.right #[proof]) : ?m.420
#[ proof (← mkAppM ``And.right #[proof]) : ?m.420
]) (← aux (← mkAppM ``And.left #[proof])) ++
(← aux (← mkAppM ``And.right #[proof])) : ?m.526
))
| _ => pure : {f : Type ?u.623 → Type ?u.622 } → [self : Pure f ] → {α : Type ?u.623} → α → f α pure [ proof ]
/--
Removes any expressions that are not proofs of inequalities, equalities, or negations thereof.
-/
partial def filterComparisons : Preprocessor where
name := "filter terms that are not proofs of comparisons" : String "filter terms that are not proofs of comparisons"
transform h := do
let tp ← whnfR (← instantiateMVars (← inferType h)) : ?m.4180
(← instantiateMVars (← instantiateMVars (← inferType h)) : ?m.4180
(← inferType h ) (← instantiateMVars (← inferType h)) : ?m.4180
)
if (← isProp tp ) && (← aux tp ) then pure : {f : Type ?u.4440 → Type ?u.4439 } → [self : Pure f ] → {α : Type ?u.4440} → α → f α pure [ h ]
else pure : {f : Type ?u.4514 → Type ?u.4513 } → [self : Pure f ] → {α : Type ?u.4514} → α → f α pure []
where
/-- Implementation of the `filterComparisons` preprocessor. -/
aux ( e : Expr ) : MetaM Bool := do
match e . getAppFnArgs with
| ( `` Eq , _) | ( `` LE.le : {α : Type u} → [self : LE α ] → α → α → Prop LE.le, _) | ( `` LT.lt : {α : Type u} → [self : LT α ] → α → α → Prop LT.lt, _) => pure : {f : Type ?u.2548 → Type ?u.2547 } → [self : Pure f ] → {α : Type ?u.2548} → α → f α pure true
| ( `` Not , #[ e ]) => match (← whnfR e ) . getAppFnArgs with
| ( `` LE.le : {α : Type u} → [self : LE α ] → α → α → Prop LE.le, _) | ( `` LT.lt : {α : Type u} → [self : LT α ] → α → α → Prop LT.lt, _) => pure : {f : Type ?u.2746 → Type ?u.2745 } → [self : Pure f ] → {α : Type ?u.2746} → α → f α pure true
| _ => pure : {f : Type ?u.2834 → Type ?u.2833 } → [self : Pure f ] → {α : Type ?u.2834} → α → f α pure false
| _ => pure : {f : Type ?u.3276 → Type ?u.3275 } → [self : Pure f ] → {α : Type ?u.3276} → α → f α pure false
section removeNegations
/--
If `prf` is a proof of `¬ e`, where `e` is a comparison,
`flipNegatedComparison prf e` flips the comparison in `e` and returns a proof.
For example, if `prf : ¬ a < b`, ``flipNegatedComparison prf q(a < b)`` returns a proof of `a ≥ b`.
-/
def flipNegatedComparison ( prf : Expr ) ( e : Expr ) : MetaM Expr :=
match e . getAppFnArgs with
| ( `` LE.le : {α : Type u} → [self : LE α ] → α → α → Prop LE.le, #[_, _, _, _]) => mkAppM `` lt_of_not_ge #[ prf ]
| ( `` LT.lt : {α : Type u} → [self : LT α ] → α → α → Prop LT.lt, #[_, _, _, _]) => mkAppM `` le_of_not_gt #[ prf ]
| _ => throwError "Not a comparison (flipNegatedComparison): { e }"
/--
Replaces proofs of negations of comparisons with proofs of the reversed comparisons.
For example, a proof of `¬ a < b` will become a proof of `a ≥ b`.
-/
def removeNegations : Preprocessor where
name := "replace negations of comparisons" : String "replace negations of comparisons"
transform h := do
let t : Q( Prop ) ← whnfR (← inferType h )
match t with
| ~q(¬ $p) =>
trace[ linarith] "removing negation in { h }"
return [← flipNegatedComparison h (← whnfR p)] : List ?m.11212 [← flipNegatedComparison [← flipNegatedComparison h (← whnfR p)] : List ?m.11212 h [← flipNegatedComparison h (← whnfR p)] : List ?m.11212 (← whnfR p ) [← flipNegatedComparison h (← whnfR p)] : List ?m.11212 ]
| _ => return [ h ]
end removeNegations
section natToInt
open Mathlib.Tactic.Zify
/--
`isNatProp tp` is true iff `tp` is an inequality or equality between natural numbers
or the negation thereof.
-/
partial def isNatProp ( e : Expr ) : Bool :=
match e . getAppFnArgs with
| ( `` Eq , #[ .const `` Nat [], _, _]) => true
| ( `` LE.le : {α : Type u} → [self : LE α ] → α → α → Prop LE.le, #[ .const `` Nat [], _, _, _]) => true
| ( `` LT.lt : {α : Type u} → [self : LT α ] → α → α → Prop LT.lt, #[ .const `` Nat [], _, _, _]) => true
| ( `` GE.ge : {α : Type u} → [inst : LE α ] → α → α → Prop GE.ge, #[ .const `` Nat [], _, _, _]) => true
| ( `` GT.gt : {α : Type u} → [inst : LT α ] → α → α → Prop GT.gt, #[ .const `` Nat [], _, _, _]) => true
| ( `` Not , #[ e ]) => isNatProp e
| _ => false
/-- If `e` is of the form `((n : ℕ) : ℤ)`, `isNatIntCoe e` returns `n : ℕ`. -/
def isNatIntCoe ( e : Expr ) : Option Expr :=
match e . getAppFnArgs with
| ( `` Nat.cast , #[ .const `` Int [], _, n ]) => some n
| _ => none
/--
`getNatComparisons e` returns a list of all subexpressions of `e` of the form `((t : ℕ) : ℤ)`.
-/
partial def getNatComparisons ( e : Expr ) : List Expr :=
match isNatIntCoe e with
| some n => [ n ]
| none => match e . getAppFnArgs with
| ( `` HAdd.hAdd , #[_, _, _, _, a , b ]) => getNatComparisons a ++ getNatComparisons b
| ( `` HMul.hMul , #[_, _, _, _, a , b ]) => getNatComparisons a ++ getNatComparisons b
| _ => []
/-- If `e : ℕ`, returns a proof of `0 ≤ (e : ℤ)`. -/
def mk_coe_nat_nonneg_prf ( e : Expr ) : MetaM Expr :=
mkAppM `` Int.coe_nat_nonneg : ∀ (n : ℕ ), 0 ≤ ↑n Int.coe_nat_nonneg #[ e ]
open Std
/-- Ordering on `Expr`. -/
-- We only define this so we can use `RBSet Expr`. Perhaps `HashSet` would be more appropriate?
def Expr.compare ( a b : Expr ) : Ordering :=
if Expr.lt a b then .lt else if a . equal b then .eq else .gt
/--
If `h` is an equality or inequality between natural numbers,
`natToInt` lifts this inequality to the integers.
It also adds the facts that the integers involved are nonnegative.
To avoid adding the same nonnegativity facts many times, it is a global preprocessor.
-/
def natToInt : GlobalBranchingPreprocessor : Type GlobalBranchingPreprocessor where
name := "move nats to ints"
transform g l := do
let l ← l . mapM $ fun h => do
let t ← whnfR (← instantiateMVars (← inferType h)) : ?m.29036
(← instantiateMVars (← instantiateMVars (← inferType h)) : ?m.29036
(← inferType h) : ?m.28950
(← inferType (← inferType h) : ?m.28950
h (← inferType h) : ?m.28950
) (← instantiateMVars (← inferType h)) : ?m.29036
)
if isNatProp t then
let ( some ( h' , t' ), _) ← Term.TermElabM.run' : {α : Type } →
TermElabM α →
optParam Term.Context
{ declName? := none , auxDeclToFullName := ∅ , macroStack := [] , mayPostpone := true , errToSorry := true ,
autoBoundImplicit := false ,
autoBoundImplicits :=
{ root := PersistentArrayNode.node (Array.mkEmpty (USize.toNat PersistentArray.branching ) ) ,
tail := Array.mkEmpty (USize.toNat PersistentArray.branching ) , size := 0 ,
shift := PersistentArray.initShift , tailOff := 0 } ,
autoBoundImplicitForbidden := fun x => false , sectionVars := ∅ , sectionFVars := ∅ , implicitLambda := true ,
isNoncomputableSection := false , ignoreTCFailures := false , inPattern := false , tacticCache? := none ,
saveRecAppSyntax := true , holesAsSyntheticOpaque := false } →
optParam Term.State
{ levelNames := [] , syntheticMVars := ∅ , pendingMVars := ∅ , mvarErrorInfos := ∅ , letRecsToLift := [] } →
MetaM α Term.TermElabM.run' ( run_for g ( zifyProof none h t ))
| throwError "zifyProof failed on { h }"
if ← filterComparisons.aux t' then
pure : {f : Type ?u.29451 → Type ?u.29450 } → [self : Pure f ] → {α : Type ?u.29451} → α → f α pure h'
else
-- `zifyProof` turned our comparison into something that wasn't a comparison
-- probably replacing `n = n` with `True`, because of
-- https://github.com/leanprover-community/mathlib4/issues/741
-- so we just keep the original hypothesis.
pure : {f : Type ?u.29520 → Type ?u.29519 } → [self : Pure f ] → {α : Type ?u.29520} → α → f α pure h
else
pure : {f : Type ?u.30005 → Type ?u.30004 } → [self : Pure f ] → {α : Type ?u.30005} → α → f α pure h
let nonnegs ← l . foldlM : {m : Type ?u.30158 → Type ?u.30157 } →
[inst : Monad m ] → {s : Type ?u.30158} → {α : Type ?u.30156} → (s → α → m s ) → s → List α → m s foldlM (init := ∅ ) fun ( es : RBSet Expr Expr.compare ) h => do
try
let ( a , b ) ← getRelSides (← inferType h) : ?m.30288
(← inferType (← inferType h) : ?m.30288
h (← inferType h) : ?m.30288
)
pure : {f : Type ?u.30372 → Type ?u.30371 } → [self : Pure f ] → {α : Type ?u.30372} → α → f α pure <| ( es . insertList ( getNatComparisons a )). insertList ( getNatComparisons b )
catch _ => pure : {f : Type ?u.30515 → Type ?u.30514 } → [self : Pure f ] → {α : Type ?u.30515} → α → f α pure es
pure : {f : Type ?u.30954 → Type ?u.30953 } → [self : Pure f ] → {α : Type ?u.30954} → α → f α pure [( g , ((← nonnegs . toList . mapM mk_coe_nat_nonneg_prf ) ++ l : List Expr ))]
end natToInt
section strengthenStrictInt
/--
`isStrictIntComparison tp` is true iff `tp` is a strict inequality between integers
or the negation of a weak inequality between integers.
-/
def isStrictIntComparison ( e : Expr ) : Bool :=
match e . getAppFnArgs with
| ( `` LT.lt : {α : Type u} → [self : LT α ] → α → α → Prop LT.lt, #[ .const `` Int [], _, _, _]) => true
| ( `` GT.gt : {α : Type u} → [inst : LT α ] → α → α → Prop GT.gt, #[ .const `` Int [], _, _, _]) => true
| ( `` Not , #[ e ]) => match e . getAppFnArgs with
| ( `` LE.le : {α : Type u} → [self : LE α ] → α → α → Prop LE.le, #[ .const `` Int [], _, _, _]) => true
| ( `` GE.ge : {α : Type u} → [inst : LE α ] → α → α → Prop GE.ge, #[ .const `` Int [], _, _, _]) => true
| _ => false
| _ => false
/--
If `pf` is a proof of a strict inequality `(a : ℤ) < b`,
`mkNonstrictIntProof pf` returns a proof of `a + 1 ≤ b`,
and similarly if `pf` proves a negated weak inequality.
-/
def mkNonstrictIntProof ( pf : Expr ) : MetaM Expr := do
match (← inferType pf) : ?m.44282
(← inferType (← inferType pf) : ?m.44282
pf (← inferType pf) : ?m.44282
) . getAppFnArgs with
| ( `` LT.lt : {α : Type u} → [self : LT α ] → α → α → Prop LT.lt, #[_, _, a , b ]) =>
return mkApp (← mkAppM ``Iff.mpr #[← mkAppOptM ``Int.add_one_le_iff #[a, b]]) : ?m.44621
(← mkAppM (← mkAppM ``Iff.mpr #[← mkAppOptM ``Int.add_one_le_iff #[a, b]]) : ?m.44621
`` Iff.mpr : ∀ {a b : Prop }, (a ↔ b ) → b → a Iff.mpr (← mkAppM ``Iff.mpr #[← mkAppOptM ``Int.add_one_le_iff #[a, b]]) : ?m.44621
#[← mkAppOptM ``Int.add_one_le_iff #[a, b]] : Array ?m.44610 #[← mkAppOptM #[← mkAppOptM ``Int.add_one_le_iff #[a, b]] : Array ?m.44610 `` Int.add_one_le_iff : ∀ {a b : ℤ }, a + 1 ≤ b ↔ a < b Int.add_one_le_iff #[← mkAppOptM ``Int.add_one_le_iff #[a, b]] : Array ?m.44610 #[ a #[← mkAppOptM ``Int.add_one_le_iff #[a, b]] : Array ?m.44610 , b #[← mkAppOptM ``Int.add_one_le_iff #[a, b]] : Array ?m.44610 ]] (← mkAppM ``Iff.mpr #[← mkAppOptM ``Int.add_one_le_iff #[a, b]]) : ?m.44621
) pf
| ( `` GT.gt : {α : Type u} → [inst : LT α ] → α → α → Prop GT.gt, #[_, _, a , b ]) =>
return mkApp (← mkAppM ``Iff.mpr #[← mkAppOptM ``Int.add_one_le_iff #[b, a]]) : ?m.45014
(← mkAppM (← mkAppM ``Iff.mpr #[← mkAppOptM ``Int.add_one_le_iff #[b, a]]) : ?m.45014
`` Iff.mpr : ∀ {a b : Prop }, (a ↔ b ) → b → a Iff.mpr (← mkAppM ``Iff.mpr #[← mkAppOptM ``Int.add_one_le_iff #[b, a]]) : ?m.45014
#[← mkAppOptM ``Int.add_one_le_iff #[b, a]] : Array ?m.45003 #[← mkAppOptM #[← mkAppOptM ``Int.add_one_le_iff #[b, a]] : Array ?m.45003 `` Int.add_one_le_iff : ∀ {a b : ℤ }, a + 1 ≤ b ↔ a < b Int.add_one_le_iff #[← mkAppOptM ``Int.add_one_le_iff #[b, a]] : Array ?m.45003 #[ b #[← mkAppOptM ``Int.add_one_le_iff #[b, a]] : Array ?m.45003 , a #[← mkAppOptM ``Int.add_one_le_iff #[b, a]] : Array ?m.45003 ]] (← mkAppM ``Iff.mpr #[← mkAppOptM ``Int.add_one_le_iff #[b, a]]) : ?m.45014
) pf
| ( `` Not , #[ P ]) => match P . getAppFnArgs with
| ( `` LE.le : {α : Type u} → [self : LE α ] → α → α → Prop LE.le, #[_, _, a , b ]) =>
return mkApp (← mkAppM ``Iff.mpr #[← mkAppOptM ``Int.add_one_le_iff #[b, a]]) : ?m.45403
(← mkAppM (← mkAppM ``Iff.mpr #[← mkAppOptM ``Int.add_one_le_iff #[b, a]]) : ?m.45403
`` Iff.mpr : ∀ {a b : Prop }, (a ↔ b ) → b → a Iff.mpr (← mkAppM ``Iff.mpr #[← mkAppOptM ``Int.add_one_le_iff #[b, a]]) : ?m.45403
#[← mkAppOptM ``Int.add_one_le_iff #[b, a]] : Array ?m.45392 #[← mkAppOptM #[← mkAppOptM ``Int.add_one_le_iff #[b, a]] : Array ?m.45392 `` Int.add_one_le_iff : ∀ {a b : ℤ }, a + 1 ≤ b ↔ a < b Int.add_one_le_iff #[← mkAppOptM ``Int.add_one_le_iff #[b, a]] : Array ?m.45392 #[ b #[← mkAppOptM ``Int.add_one_le_iff #[b, a]] : Array ?m.45392 , a #[← mkAppOptM ``Int.add_one_le_iff #[b, a]] : Array ?m.45392 ]] (← mkAppM ``Iff.mpr #[← mkAppOptM ``Int.add_one_le_iff #[b, a]]) : ?m.45403
)
(← mkAppM ``lt_of_not_ge #[pf]) : ?m.45464
(← mkAppM (← mkAppM ``lt_of_not_ge #[pf]) : ?m.45464
`` lt_of_not_ge (← mkAppM ``lt_of_not_ge #[pf]) : ?m.45464
#[ pf (← mkAppM ``lt_of_not_ge #[pf]) : ?m.45464
])
| ( `` GE.ge : {α : Type u} → [inst : LE α ] → α → α → Prop GE.ge, #[_, _, a , b ]) =>
return mkApp (← mkAppM ``Iff.mpr #[← mkAppOptM ``Int.add_one_le_iff #[a, b]]) : ?m.45823
(← mkAppM (← mkAppM ``Iff.mpr #[← mkAppOptM ``Int.add_one_le_iff #[a, b]]) : ?m.45823
`` Iff.mpr : ∀ {a b : Prop }, (a ↔ b ) → b → a Iff.mpr (← mkAppM ``Iff.mpr #[← mkAppOptM ``Int.add_one_le_iff #[a, b]]) : ?m.45823
#[← mkAppOptM ``Int.add_one_le_iff #[a, b]] : Array ?m.45812 #[← mkAppOptM #[← mkAppOptM ``Int.add_one_le_iff #[a, b]] : Array ?m.45812 `` Int.add_one_le_iff : ∀ {a b : ℤ }, a + 1 ≤ b ↔ a < b Int.add_one_le_iff #[← mkAppOptM ``Int.add_one_le_iff #[a, b]] : Array ?m.45812 #[ a #[← mkAppOptM ``Int.add_one_le_iff #[a, b]] : Array ?m.45812 , b #[← mkAppOptM ``Int.add_one_le_iff #[a, b]] : Array ?m.45812 ]] (← mkAppM ``Iff.mpr #[← mkAppOptM ``Int.add_one_le_iff #[a, b]]) : ?m.45823
)
(← mkAppM ``lt_of_not_ge #[pf]) : ?m.45884
(← mkAppM (← mkAppM ``lt_of_not_ge #[pf]) : ?m.45884
`` lt_of_not_ge (← mkAppM ``lt_of_not_ge #[pf]) : ?m.45884
#[ pf (← mkAppM ``lt_of_not_ge #[pf]) : ?m.45884
])
| _ => throwError "mkNonstrictIntProof failed: proof is not an inequality" : ?m.45944 ?m.45945
throwError throwError "mkNonstrictIntProof failed: proof is not an inequality" : ?m.45944 ?m.45945
"mkNonstrictIntProof failed: proof is not an inequality"
| _ => throwError "mkNonstrictIntProof failed: proof is not an inequality" : ?m.47051 ?m.47052
throwError throwError "mkNonstrictIntProof failed: proof is not an inequality" : ?m.47051 ?m.47052
"mkNonstrictIntProof failed: proof is not an inequality"
/-- `strengthenStrictInt h` turns a proof `h` of a strict integer inequality `t1 < t2`
into a proof of `t1 ≤ t2 + 1`. -/
def strengthenStrictInt : Preprocessor where
name := "strengthen strict inequalities over int" : String "strengthen strict inequalities over int"
transform h := do
if isStrictIntComparison (← inferType h) : ?m.51581
(← inferType (← inferType h) : ?m.51581
h (← inferType h) : ?m.51581
) then
return [← mkNonstrictIntProof h] : List ?m.51756 [← mkNonstrictIntProof [← mkNonstrictIntProof h] : List ?m.51756 h [← mkNonstrictIntProof h] : List ?m.51756 ]
else
return [ h ]
end strengthenStrictInt
section compWithZero
/--
`rearrangeComparison e` takes a proof `e` of an equality, inequality, or negation thereof,
and turns it into a proof of a comparison `_ R 0`, where `R ∈ {=, ≤, <}`.
-/
partial def rearrangeComparison ( e : Expr ) : MetaM Expr := do
aux e (← instantiateMVars (← inferType e)) : ?m.138506
(← instantiateMVars (← instantiateMVars (← inferType e)) : ?m.138506
(← inferType e) : ?m.138420
(← inferType (← inferType e) : ?m.138420
e (← inferType e) : ?m.138420
) (← instantiateMVars (← inferType e)) : ?m.138506
)
where
/-- Implementation of `rearrangeComparison`, after type inference. -/
aux ( proof e : Expr ) : MetaM Expr :=
match e . getAppFnArgs with
| ( `` LE.le : {α : Type u} → [self : LE α ] → α → α → Prop LE.le, #[_, _, a , b ]) => match a . getAppFnArgs , b . getAppFnArgs with
| _, ( `` OfNat.ofNat : {α : Type u} → (x : ℕ ) → [self : OfNat α x ] → α OfNat.ofNat, #[_, .lit ( .natVal 0 ), _]) => return proof
| ( `` OfNat.ofNat : {α : Type u} → (x : ℕ ) → [self : OfNat α x ] → α OfNat.ofNat, #[_, .lit ( .natVal 0 ), _]), _ => mkAppM `` neg_nonpos_of_nonneg #[ proof ]
| _, _ => mkAppM `` sub_nonpos_of_le #[ proof ]
| ( `` LT.lt : {α : Type u} → [self : LT α ] → α → α → Prop LT.lt, #[_, _, a , b ]) => match a . getAppFnArgs , b . getAppFnArgs with
| _, ( `` OfNat.ofNat : {α : Type u} → (x : ℕ ) → [self : OfNat α x ] → α OfNat.ofNat, #[_, .lit ( .natVal 0 ), _]) => return proof
| ( `` OfNat.ofNat : {α : Type u} → (x : ℕ ) → [self : OfNat α x ] → α OfNat.ofNat, #[_, .lit ( .natVal 0 ), _]), _ => mkAppM `` neg_neg_of_pos #[ proof ]
| _, _ => mkAppM `` sub_neg_of_lt #[ proof ]
| ( `` Eq , #[_, a , b ]) => match a . getAppFnArgs , b . getAppFnArgs with
| _, ( `` OfNat.ofNat : {α : Type u} → (x : ℕ ) → [self : OfNat α x ] → α OfNat.ofNat, #[_, .lit ( .natVal 0 ), _]) => return proof
| ( `` OfNat.ofNat : {α : Type u} → (x : ℕ ) → [self : OfNat α x ] → α OfNat.ofNat, #[_, .lit ( .natVal 0 ), _]), _ => mkAppM `` Eq.symm : ∀ {α : Sort u} {a b : α }, a = b → b = a Eq.symm #[ proof ]
| _, _ => mkAppM `` sub_eq_zero_of_eq : ∀ {G : Type u_1} [inst : AddGroup G ] {a b : G }, a = b → a - b = 0 sub_eq_zero_of_eq #[ proof ]
| ( `` GT.gt : {α : Type u} → [inst : LT α ] → α → α → Prop GT.gt, #[_, _, a , b ]) => match a . getAppFnArgs , b . getAppFnArgs with
| _, ( `` OfNat.ofNat : {α : Type u} → (x : ℕ ) → [self : OfNat α x ] → α OfNat.ofNat, #[_, .lit ( .natVal 0 ), _]) => mkAppM `` neg_neg_of_pos #[ proof ]
| ( `` OfNat.ofNat : {α : Type u} → (x : ℕ ) → [self : OfNat α x ] → α OfNat.ofNat, #[_, .lit ( .natVal 0 ), _]), _ => mkAppM `` lt_zero_of_zero_gt : ∀ {α : Type u_1} [inst : Zero α ] [inst_1 : LT α ] {a : α }, 0 > a → a < 0 lt_zero_of_zero_gt #[ proof ]
| _, _ => mkAppM `` sub_neg_of_lt #[ proof ]
| ( `` GE.ge : {α : Type u} → [inst : LE α ] → α → α → Prop GE.ge, #[_, _, a , b ]) => match a . getAppFnArgs , b . getAppFnArgs with
| _, ( `` OfNat.ofNat : {α : Type u} → (x : ℕ ) → [self : OfNat α x ] → α OfNat.ofNat, #[_, .lit ( .natVal 0 ), _]) => mkAppM `` neg_nonpos_of_nonneg #[ proof ]
| ( `` OfNat.ofNat : {α : Type u} → (x : ℕ ) → [self : OfNat α x ] → α OfNat.ofNat, #[_, .lit ( .natVal 0 ), _]), _ => mkAppM `` le_zero_of_zero_ge : ∀ {α : Type u_1} [inst : Zero α ] [inst_1 : LE α ] {a : α }, 0 ≥ a → a ≤ 0 le_zero_of_zero_ge #[ proof ]
| _, _ => mkAppM `` sub_nonpos_of_le #[ proof ]
| ( `` Not , #[ a ]) => do
let nproof ← flipNegatedComparison proof a
aux nproof (← inferType nproof) : ?m.136153
(← inferType (← inferType nproof) : ?m.136153
nproof (← inferType nproof) : ?m.136153
)
| a => throwError "couldn't rearrange comparison { a }"
/--
`compWithZero h` takes a proof `h` of an equality, inequality, or negation thereof,
and turns it into a proof of a comparison `_ R 0`, where `R ∈ {=, ≤, <}`.
-/
def compWithZero : Preprocessor where
name := "make comparisons with zero" : String "make comparisons with zero"
transform e := try
pure : {f : Type ?u.337134 → Type ?u.337133 } → [self : Pure f ] → {α : Type ?u.337134} → α → f α pure [← rearrangeComparison e] : List ?m.337159 [← rearrangeComparison [← rearrangeComparison e] : List ?m.337159 e [← rearrangeComparison e] : List ?m.337159 ]
catch e =>
if ← e . isFailedToSynthesize then
pure : {f : Type ?u.337715 → Type ?u.337714 } → [self : Pure f ] → {α : Type ?u.337715} → α → f α pure []
else
throw e
end compWithZero
section cancelDenoms
theorem without_one_mul [ MulOneClass : Type ?u.339407 → Type ?u.339407 MulOneClass M ] { a b : M } ( h : 1 * a = b ) : a = b := by rwa [ one_mul ] at h
/--
`normalizeDenominatorsLHS h lhs` assumes that `h` is a proof of `lhs R 0`.
It creates a proof of `lhs' R 0`, where all numeric division in `lhs` has been cancelled.
-/
def normalizeDenominatorsLHS ( h lhs : Expr ) : MetaM Expr := do
let mut ( v , lhs' ) ← CancelDenoms.derive lhs
if v = 1 then
-- `lhs'` has a `1 *` out front, but `mkSingleCompZeroOf` has a special case
-- where it does not produce `1 *`. We strip it off here:
lhs' ← mkAppM `` without_one_mul #[ lhs' ]
let (_, h'' ) ← mkSingleCompZeroOf v h
try
h'' . rewriteType lhs'
catch e =>
dbg_trace
s!"Error in Linarith.normalizeDenominatorsLHS: {← e.toMessageData.toString}" : ?m.341474
s! s!"Error in Linarith.normalizeDenominatorsLHS: {← e.toMessageData.toString}" : ?m.341474
"Error in Linarith.normalizeDenominatorsLHS: {← e s!"Error in Linarith.normalizeDenominatorsLHS: {← e.toMessageData.toString}" : ?m.341474
.toMessageData s!"Error in Linarith.normalizeDenominatorsLHS: {← e.toMessageData.toString}" : ?m.341474
.toString s!"Error in Linarith.normalizeDenominatorsLHS: {← e.toMessageData.toString}" : ?m.341474
}"
throw e
/--
`cancelDenoms pf` assumes `pf` is a proof of `t R 0`. If `t` contains the division symbol `/`,
it tries to scale `t` to cancel out division by numerals.
-/
def cancelDenoms : Preprocessor where
name := "cancel denominators"
transform := fun pf => ( do
let (_, lhs ) ← parseCompAndExpr (← inferType pf) : ?m.344827
(← inferType (← inferType pf) : ?m.344827
pf (← inferType pf) : ?m.344827
)
guard $ lhs . containsConst ( fun n => n = `` HDiv.hDiv || n = `` Div.div : {α : Type u} → [self : Div α ] → α → α → α Div.div)
pure : {f : Type ?u.345186 → Type ?u.345185 } → [self : Pure f ] → {α : Type ?u.345186} → α → f α pure [← normalizeDenominatorsLHS pf lhs] : List ?m.345211 [← normalizeDenominatorsLHS [← normalizeDenominatorsLHS pf lhs] : List ?m.345211 pf [← normalizeDenominatorsLHS pf lhs] : List ?m.345211 lhs [← normalizeDenominatorsLHS pf lhs] : List ?m.345211 ])
<|> return [ pf ]
end cancelDenoms
section nlinarith
/--
`findSquares s e` collects all terms of the form `a ^ 2` and `a * a` that appear in `e`
and adds them to the set `s`.
A pair `(a, true)` is added to `s` when `a^2` appears in `e`,
and `(a, false)` is added to `s` when `a*a` appears in `e`. -/
partial def findSquares ( s : HashSet ( Expr × Bool )) ( e : Expr ) : MetaM ( HashSet ( Expr × Bool )) :=
match e . getAppFnArgs with
| ( `` HPow.hPow , #[_, _, _, _, a , b ]) => match b . numeral? with
| some 2 => do
let s ← findSquares s a
return ( s . insert ( a , true ))
| _ => e . foldlM findSquares s
| ( `` HMul.hMul , #[_, _, _, _, a , b ]) => if a . equal b then do
let s ← findSquares s a
return ( s . insert ( a , false ))
else
e . foldlM findSquares s
| _ => e . foldlM findSquares s
/--
`nlinarithExtras` is the preprocessor corresponding to the `nlinarith` tactic.
* For every term `t` such that `t^2` or `t*t` appears in the input, adds a proof of `t^2 ≥ 0`
or `t*t ≥ 0`.
* For every pair of comparisons `t1 R1 0` and `t2 R2 0`, adds a proof of `t1*t2 R 0`.
This preprocessor is typically run last, after all inputs have been canonized.
-/
def nlinarithExtras : GlobalPreprocessor where
name := "nonlinear arithmetic extras" : String "nonlinear arithmetic extras"
transform ls := do
let s ← ls . foldrM : {m : Type ?u.351943 → Type ?u.351942 } →
[inst : Monad m ] → {s : Type ?u.351943} → {α : Type ?u.351941} → (α → s → m s ) → s → List α → m s foldrM ( fun h s' => do findSquares s' (← instantiateMVars (← inferType h)) : ?m.352133
(← instantiateMVars (← instantiateMVars (← inferType h)) : ?m.352133
(← inferType h) : ?m.352047
(← inferType (← inferType h) : ?m.352047
h (← inferType h) : ?m.352047
) (← instantiateMVars (← inferType h)) : ?m.352133
) )
HashSet.empty
let new_es ← s . foldM ( fun new_es (⟨ e , is_sq ⟩ : Expr × Bool) =>
(( do
let p ← mkAppM ( if is_sq then `` sq_nonneg else `` mul_self_nonneg ) #[ e ]
pure : {f : Type ?u.353893 → Type ?u.353892 } → [self : Pure f ] → {α : Type ?u.353893} → α → f α pure $ p :: new_es ) <|> pure : {f : Type ?u.353673 → Type ?u.353672 } → [self : Pure f ] → {α : Type ?u.353673} → α → f α pure new_es )) ( [] : List Expr )
let new_es ← compWithZero . globalize . transform new_es
trace[ linarith] "nlinarith preprocessing found squares"
trace[ linarith] "{ s . toList }"
linarithTraceProofs "so we added proofs" new_es
let with_comps ← ( new_es ++ ls ). mapM ( fun e => do
let tp ← inferType e
try
let ⟨ ine , _⟩ ← parseCompAndExpr tp
pure : {f : Type ?u.354909 → Type ?u.354908 } → [self : Pure f ] → {α : Type ?u.354909} → α → f α pure ( ine , e )
catch _ => pure : {f : Type ?u.355004 → Type ?u.355003 } → [self : Pure f ] → {α : Type ?u.355004} → α → f α pure ( Ineq.lt , e ))
let products ← with_comps . mapDiagM : {m : Type ?u.355238 → Type ?u.355237 } →
{α : Type ?u.355236} → {β : Type ?u.355238} → [inst : Monad m ] → (α → α → m β ) → List α → m (List β ) mapDiagM $ fun (⟨ posa , a ⟩ : Ineq × Expr) ⟨ posb , b ⟩ =>
try
( some <$> match posa , posb with
| Ineq.eq , _ => mkAppM `` zero_mul_eq : ∀ {α : Type u_1} {R : α → α → Prop } [inst : Semiring α ] {a b : α }, a = 0 → R b 0 → a * b = 0 zero_mul_eq #[ a , b ]
| _, Ineq.eq => mkAppM `` mul_zero_eq : ∀ {α : Type u_1} {R : α → α → Prop } [inst : Semiring α ] {a b : α }, R a 0 → b = 0 → a * b = 0 mul_zero_eq #[ a , b ]
| Ineq.lt , Ineq.lt => mkAppM `` mul_pos_of_neg_of_neg #[ a , b ]
| Ineq.lt , Ineq.le => do
let a ← mkAppM `` le_of_lt #[ a ]
mkAppM `` mul_nonneg_of_nonpos_of_nonpos : ∀ {α : Type u} [inst : OrderedRing α ] {a b : α }, a ≤ 0 → b ≤ 0 → 0 ≤ a * b mul_nonneg_of_nonpos_of_nonpos #[ a , b ]
| Ineq.le , Ineq.lt => do
let b ← mkAppM `` le_of_lt #[ b ]
mkAppM `` mul_nonneg_of_nonpos_of_nonpos : ∀ {α : Type u} [inst : OrderedRing α ] {a b : α }, a ≤ 0 → b ≤ 0 → 0 ≤ a * b mul_nonneg_of_nonpos_of_nonpos #[ a , b ]
| Ineq.le , Ineq.le => mkAppM `` mul_nonneg_of_nonpos_of_nonpos : ∀ {α : Type u} [inst : OrderedRing α ] {a b : α }, a ≤ 0 → b ≤ 0 → 0 ≤ a * b mul_nonneg_of_nonpos_of_nonpos #[ a , b ])
catch _ => pure : {f : Type ?u.355988 → Type ?u.355987 } → [self : Pure f ] → {α : Type ?u.355988} → α → f α pure none
let products ← compWithZero . globalize . transform products . reduceOption
return ( new_es ++ ls ++ products )
end nlinarith
-- TODO the `removeNe` preprocesor
section removeNe
-- /--
-- `remove_ne_aux` case splits on any proof `h : a ≠ b` in the input,
-- turning it into `a < b ∨ a > b`.
-- This produces `2^n` branches when there are `n` such hypotheses in the input.
-- -/
-- meta def remove_ne_aux : list expr → tactic (list branch) :=
-- λ hs,
-- (do e ← hs.mfind (λ e : expr, do e ← infer_type e, guard $ e.is_ne.is_some),
-- [(_, ng1), (_, ng2)] ← to_expr ``(or.elim (lt_or_gt_of_ne %%e)) >>= apply,
-- let do_goal : expr → tactic (list branch) := λ g,
-- do set_goals [g],
-- h ← intro1,
-- ls ← remove_ne_aux $ hs.remove_all [e],
-- return $ ls.map (λ b : branch, (b.1, h::b.2)) in
-- (++) <$> do_goal ng1 <*> do_goal ng2)
-- <|> do g ← get_goal, return [(g, hs)]
-- /--
-- `remove_ne` case splits on any proof `h : a ≠ b` in the input, turning it into `a < b ∨ a > b`,
-- by calling `linarith.remove_ne_aux`.
-- This produces `2^n` branches when there are `n` such hypotheses in the input.
-- -/
-- meta def remove_ne : global_branching_preprocessor :=
-- { name := "remove_ne",
-- transform := remove_ne_aux }
end removeNe
/--
The default list of preprocessors, in the order they should typically run.
-/
def defaultPreprocessors : List GlobalBranchingPreprocessor : Type GlobalBranchingPreprocessor :=
[ filterComparisons , removeNegations , natToInt , strengthenStrictInt ,
compWithZero , cancelDenoms ]
/--
`preprocess pps l` takes a list `l` of proofs of propositions.
It maps each preprocessor `pp ∈ pps` over this list.
The preprocessors are run sequentially: each receives the output of the previous one.
Note that a preprocessor may produce multiple or no expressions from each input expression,
so the size of the list may change.
-/
def preprocess ( pps : List GlobalBranchingPreprocessor : Type GlobalBranchingPreprocessor) ( g : MVarId ) ( l : List Expr ) :
MetaM ( List Branch ) :=
pps . foldlM : {m : Type ?u.366801 → Type ?u.366800 } →
[inst : Monad m ] → {s : Type ?u.366801} → {α : Type ?u.366799} → (s → α → m s ) → s → List α → m s foldlM ( fun ls pp => return (← ls.mapM fun (g, l) => do pp.process g l) : ?m.366965
(← ls (← ls.mapM fun (g, l) => do pp.process g l) : ?m.366965
. mapM (← ls.mapM fun (g, l) => do pp.process g l) : ?m.366965
(← ls.mapM fun (g, l) => do pp.process g l) : ?m.366965
fun (← ls.mapM fun (g, l) => do pp.process g l) : ?m.366965
( g (← ls.mapM fun (g, l) => do pp.process g l) : ?m.366965
, l (← ls.mapM fun (g, l) => do pp.process g l) : ?m.366965
) => (← ls.mapM fun (g, l) => do pp.process g l) : ?m.366965
do (← ls.mapM fun (g, l) => do pp.process g l) : ?m.366965
pp (← ls.mapM fun (g, l) => do pp.process g l) : ?m.366965
. process (← ls.mapM fun (g, l) => do pp.process g l) : ?m.366965
g (← ls.mapM fun (g, l) => do pp.process g l) : ?m.366965
l (← ls.mapM fun (g, l) => do pp.process g l) : ?m.366965
) . join ) [( g , l )]
end Linarith