|
| 1 | +/- |
| 2 | +Copyright (c) 2026 David Ledvinka. All rights reserved. |
| 3 | +Released under Apache 2.0 license as described in the file LICENSE. |
| 4 | +Authors: David Ledvinka |
| 5 | +-/ |
| 6 | +module |
| 7 | + |
| 8 | +public meta import Mathlib.Tactic.Linarith |
| 9 | +public meta import Mathlib.Tactic.Rify |
| 10 | +public import Mathlib.Data.NNReal.Basic |
| 11 | + |
| 12 | +/-! |
| 13 | +# NNReal linarith preprocessing |
| 14 | +
|
| 15 | +This file contains a `linarith` preprocessor for converting (in)equalities in `ℝ≥0` to `ℝ`. |
| 16 | +
|
| 17 | +By overriding the behaviour of the placeholder preprocessor `nnrealToReal` (which does nothing |
| 18 | +unless this file is imported) `linarith` can still be used without importing `NNReal`. |
| 19 | +-/ |
| 20 | + |
| 21 | +public meta section |
| 22 | + |
| 23 | +namespace Mathlib.Tactic.Linarith |
| 24 | + |
| 25 | +open Lean Meta |
| 26 | + |
| 27 | +/-- |
| 28 | +`isNNRealProp tp` is true iff `tp` is an inequality or equality between nonnegative real numbers |
| 29 | +or the negation thereof. |
| 30 | +-/ |
| 31 | +partial def isNNRealProp (e : Expr) : MetaM Bool := succeeds do |
| 32 | + let (_, _, .const ``NNReal _, _, _) ← e.ineqOrNotIneq? | failure |
| 33 | + |
| 34 | +/-- If `e` is of the form `((x : ℝ≥0) : ℝ)`, `NNReal.toReal e` returns `x`. -/ |
| 35 | +def isNNRealtoReal (e : Expr) : Option Expr := |
| 36 | + match e with |
| 37 | + | .app (.const ``NNReal.toReal _) n => some n |
| 38 | + | _ => none |
| 39 | + |
| 40 | +/-- |
| 41 | +`getNNRealComparisons e` returns a list of all subexpressions of `e` of the form `(x : ℝ)`. |
| 42 | +-/ |
| 43 | +partial def getNNRealCoes (e : Expr) : List Expr := |
| 44 | + match isNNRealtoReal e with |
| 45 | + | some x => [x] |
| 46 | + | none => match e.getAppFnArgs with |
| 47 | + | (``HAdd.hAdd, #[_, _, _, _, a, b]) => getNNRealCoes a ++ getNNRealCoes b |
| 48 | + | (``HMul.hMul, #[_, _, _, _, a, b]) => getNNRealCoes a ++ getNNRealCoes b |
| 49 | + | (``HSub.hSub, #[_, _, _, _, a, b]) => getNNRealCoes a ++ getNNRealCoes b |
| 50 | + | (``HDiv.hDiv, #[_, _, _, _, a, _]) => getNNRealCoes a |
| 51 | + | (``Neg.neg, #[_, _, a]) => getNNRealCoes a |
| 52 | + | _ => [] |
| 53 | + |
| 54 | +/-- If `e : ℝ≥0`, returns a proof of `0 ≤ (e : ℝ)`. -/ |
| 55 | +def mk_toReal_nonneg_prf (e : Expr) : MetaM (Option Expr) := |
| 56 | + try commitIfNoEx (mkAppM ``NNReal.coe_nonneg #[e]) |
| 57 | + catch e => do |
| 58 | + trace[linarith] "Got exception when using `coe_nonneg` {e.toMessageData}" |
| 59 | + return none |
| 60 | + |
| 61 | +initialize nnrealToRealTransform.set fun l => do |
| 62 | + let l ← l.mapM fun e => do |
| 63 | + let t ← whnfR (← instantiateMVars (← inferType e)) |
| 64 | + if ← isNNRealProp t then |
| 65 | + return (← Rify.rifyProof e t).1 |
| 66 | + else |
| 67 | + return e |
| 68 | + let atoms : List Expr ← withNewMCtxDepth <| AtomM.run .reducible do |
| 69 | + for e in l do |
| 70 | + let (_, _, a, b) ← (← inferType e).ineq? |
| 71 | + discard <| (getNNRealCoes a).mapM AtomM.addAtom |
| 72 | + discard <| (getNNRealCoes b).mapM AtomM.addAtom |
| 73 | + return (← get).atoms.toList |
| 74 | + let nonneg_pfs : List Expr ← atoms.filterMapM mk_toReal_nonneg_prf |
| 75 | + return nonneg_pfs ++ l |
| 76 | + |
| 77 | +end Mathlib.Tactic.Linarith |
0 commit comments