# Datatypes for `linarith`

#

Some of the data structures here are used in multiple parts of the tactic. We split them into their own file.

This file also contains a few convenient auxiliary functions.

A shorthand for tracing the types of a list of proof terms
when the `trace.linarith`

option is set to true.

## Equations

- One or more equations did not get rendered due to their size.

### Linear expressions #

A linear expression is a list of pairs of variable indices and coefficients, representing the sum of the products of each coefficient with its corresponding variable.

Some functions on `Linexp`

assume that `n : Nat`

occurs at most once as the first element of a pair,
and that the list is sorted in decreasing order of the first argument.
This is not enforced by the type but the operations here preserve it.

Add two `Linexp`

s together componentwise.
Preserves sorting and uniqueness of the first argument.

`l.scale c`

scales the values in `l`

by `c`

without modifying the order or keys.

`l.get n`

returns the value in `l`

associated with key `n`

, if it exists, and `none`

otherwise.
This function assumes that `l`

is sorted in decreasing order of the first argument,
that is, it will return `none`

as soon as it finds a key smaller than `n`

.

## Equations

- Linarith.Linexp.get n [] = none
- Linarith.Linexp.get n ((a, b) :: t) = if a < n then none else if a = n then some b else Linarith.Linexp.get n t

`l.contains n`

is true iff `n`

is the first element of a pair in `l`

.

## Equations

- Linarith.Linexp.contains n = Option.isSome ∘ Linarith.Linexp.get n

`l.zfind n`

returns the value associated with key `n`

if there is one, and 0 otherwise.

## Equations

- Linarith.Linexp.zfind n l = match Linarith.Linexp.get n l with | none => 0 | some v => v

`l.vars`

returns the list of variables that occur in `l`

.

## Equations

- Linarith.Linexp.vars l = List.map Prod.fst l

Defines a lex ordering on `Linexp`

. This function is performance critical.

## Equations

- One or more equations did not get rendered due to their size.
- Linarith.Linexp.cmp [] [] = Ordering.eq
- Linarith.Linexp.cmp [] x = Ordering.lt
- Linarith.Linexp.cmp x [] = Ordering.gt

### Inequalities #

- eq: Linarith.Ineq
- le: Linarith.Ineq
- lt: Linarith.Ineq

The three-element type `Ineq`

is used to represent the strength of a comparison between
terms.

## Instances For

## Equations

- Linarith.instDecidableEqIneq x y = if h : Linarith.Ineq.toCtorIdx x = Linarith.Ineq.toCtorIdx y then isTrue (_ : x = y) else isFalse (_ : x = y → False)

## Equations

- Linarith.instInhabitedIneq = { default := Linarith.Ineq.eq }

## Equations

- Linarith.instReprIneq = { reprPrec := Linarith.reprIneq✝ }

`max R1 R2`

computes the strength of the sum of two inequalities. If `t1 R1 0`

and `t2 R2 0`

,
then `t1 + t2 (max R1 R2) 0`

.

## Equations

- One or more equations did not get rendered due to their size.

`Ineq`

is ordered `eq < le < lt`

.

## Equations

- One or more equations did not get rendered due to their size.

Prints an `Ineq`

as the corresponding infix symbol.

## Equations

- Linarith.Ineq.toString x = match x with | Linarith.Ineq.eq => "=" | Linarith.Ineq.le => "≤" | Linarith.Ineq.lt => "<"

Finds the name of a multiplicative lemma corresponding to an inequality strength.

## Equations

- Linarith.Ineq.toConstMulName x = match x with | Linarith.Ineq.lt => `Linarith.mul_neg | Linarith.Ineq.le => `Linarith.mul_nonpos | Linarith.Ineq.eq => `Linarith.mul_eq

## Equations

- Linarith.Ineq.instToStringIneq = { toString := Linarith.Ineq.toString }

## Equations

- Linarith.Ineq.instToFormatIneq = { format := fun i => Std.Format.text (Linarith.Ineq.toString i) }

### Comparisons with 0 #

The strength of the comparison,

`<`

,`≤≤`

, or`=`

.str : Linarith.IneqThe coefficients of the comparison, stored as list of pairs

`(i, a)`

, where`i`

is the index of a recorded atom, and`a`

is the coefficient.coeffs : Linarith.Linexp

The main datatype for FM elimination.
Variables are represented by natural numbers, each of which has an integer coefficient.
Index 0 is reserved for constants, i.e. `coeffs.find 0`

is the coefficient of 1.
The represented term is `coeffs.sum (λ ⟨k, v⟩, v * Var[k])`

.
str determines the strength of the comparison -- is it < 0, ≤ 0, or = 0?

## Instances For

## Equations

- Linarith.instInhabitedComp = { default := { str := default, coeffs := default } }

## Equations

- Linarith.instReprComp = { reprPrec := Linarith.reprComp✝ }

`c.vars`

returns the list of variables that appear in the linear expression contained in `c`

.

`c.coeffOf a`

projects the coefficient of variable `a`

out of `c`

.

## Equations

- Linarith.Comp.coeffOf c a = Linarith.Linexp.zfind a c.coeffs

`c.scale n`

scales the coefficients of `c`

by `n`

.

## Equations

- Linarith.Comp.scale c n = { str := c.str, coeffs := Linarith.Linexp.scale (↑n) c.coeffs }

`Comp.add c1 c2`

adds the expressions represented by `c1`

and `c2`

.
The coefficient of variable `a`

in `c1.add c2`

is the sum of the coefficients of `a`

in `c1`

and `c2`

.

## Equations

- Linarith.Comp.add c1 c2 = { str := Linarith.Ineq.max c1.str c2.str, coeffs := Linarith.Linexp.add c1.coeffs c2.coeffs }

`Comp`

has a lex order. First the `ineq`

s are compared, then the `coeff`

s.

## Equations

- One or more equations did not get rendered due to their size.

A `Comp`

represents a contradiction if its expression has no coefficients and its strength is <,
that is, it represents the fact `0 < 0`

.

## Equations

- Linarith.Comp.isContr c = (List.isEmpty c.coeffs && decide (c.str = Linarith.Ineq.lt))

## Equations

- Linarith.Comp.ToFormat = { format := fun p => Std.format p.coeffs ++ Std.Format.text (toString p.str) ++ Std.Format.text "0" }

### Parsing into linear form #

### Control #

The name of the preprocessor, used in trace output.

name : StringReplace a hypothesis by a list of hypotheses. These expressions are the proof terms.

transform : Lean.Expr → Lean.MetaM (List Lean.Expr)

A preprocessor transforms a proof of a proposition into a proof of a different propositon.
The return type is `List Expr`

, since some preprocessing steps may create multiple new hypotheses,
and some may remove a hypothesis from the list.
A "no-op" preprocessor should return its input as a singleton list.

## Instances For

The name of the global preprocessor, used in trace output.

name : StringReplace the collection of all hypotheses with new hypotheses. These expressions are proof terms.

transform : List Lean.Expr → Lean.MetaM (List Lean.Expr)

Some preprocessors need to examine the full list of hypotheses instead of working item by item.
As with `Preprocessor`

, the input to a `GlobalPreprocessor`

is replaced by, not added to, its
output.

## Instances For

Some preprocessors perform branching case splits. A `Branch`

is used to track one of these case
splits. The first component, an `MVarId`

, is the goal corresponding to this branch of the split,
given as a metavariable. The `List Expr`

component is the list of hypotheses for `linarith`

in this branch.

## Equations

The name of the global branching preprocessor, used in trace output.

name : StringGiven a goal, and a list of hypotheses, produce a list of pairs (consisting of a goal and list of hypotheses).

transform : Lean.MVarId → List Lean.Expr → Lean.MetaM (List Linarith.Branch)

Some preprocessors perform branching case splits.
A `GlobalBranchingPreprocessor`

produces a list of branches to run.
Each branch is independent, so hypotheses that appear in multiple branches should be duplicated.
The preprocessor is responsible for making sure that each branch contains the correct goal
metavariable.

## Instances For

A `Preprocessor`

lifts to a `GlobalPreprocessor`

by folding it over the input list.

## Equations

- One or more equations did not get rendered due to their size.

A `GlobalPreprocessor`

lifts to a `GlobalBranchingPreprocessor`

by producing only one branch.

## Equations

- Linarith.GlobalPreprocessor.branching pp = { name := pp.name, transform := fun g l => do let __do_lift ← Linarith.GlobalPreprocessor.transform pp l pure [(g, __do_lift)] }

`process pp l`

runs `pp.transform`

on `l`

and returns the result,
tracing the result if `trace.linarith`

is on.

## Equations

- One or more equations did not get rendered due to their size.

A `CertificateOracle`

is a function
`produceCertificate : List Comp → Nat → MetaM (HashMap Nat Nat)→ Nat → MetaM (HashMap Nat Nat)→ MetaM (HashMap Nat Nat)`

.
`produceCertificate hyps max_var`

tries to derive a contradiction from the comparisons in `hyps`

by eliminating all variables ≤ `max_var`

.
If successful, it returns a map `coeff : Nat → Nat→ Nat`

as a certificate.
This map represents that we can find a contradiction by taking the sum `∑ (coeff i) * hyps[i]∑ (coeff i) * hyps[i]`

.

The default `CertificateOracle`

used by `linarith`

is
`Linarith.FourierMotzkin.produceCertificate`

.

## Equations

Discharger to prove that a candidate linear combination of hypothesis is zero.

discharger : Lean.Elab.Tactic.TacticM UnitProve goals which are not linear comparisons by first calling

`exfalso`

.exfalso : BoolTransparency mode for identifying atomic expressions in comparisons.

transparency : Lean.Meta.TransparencyModeSplit conjunctions in hypotheses.

split_hypotheses : BoolSplit

`≠≠`

in hypotheses, by branching in cases`<`

and`>`

.split_ne : BoolOverride the list of preprocessors.

preprocessors : Option (List Linarith.GlobalBranchingPreprocessor)Specify an oracle for identifying candidate contradictions. The only implementation here is Fourier-Motzkin elimination.

oracle : Option Linarith.CertificateOracle

A configuration object for `linarith`

.

## Instances For

`cfg.updateReducibility reduce_default`

will change the transparency setting of `cfg`

to
`default`

if `reduce_default`

is true. In this case, it also sets the discharger to `ring!`

,
since this is typically needed when using stronger unification.

## Equations

- One or more equations did not get rendered due to their size.

### Auxiliary functions #

These functions are used by multiple modules, so we put them here for accessibility.

`getRelSides e`

returns the left and right hand sides of `e`

if `e`

is a comparison,
and fails otherwise.
This function is more naturally in the `Option`

monad, but it is convenient to put in `MetaM`

for compositionality.

## Equations

- One or more equations did not get rendered due to their size.

`parseCompAndExpr e`

checks if `e`

is of the form `t < 0`

, `t ≤ 0≤ 0`

, or `t = 0`

.
If it is, it returns the comparison along with `t`

.

## Equations

- One or more equations did not get rendered due to their size.

helper function for error message

## Equations

- Linarith.parseCompAndExpr.throwNotZero z = Lean.throwError (Lean.toMessageData "invalid comparison, rhs not zero: " ++ Lean.toMessageData z ++ Lean.toMessageData "")

`mkSingleCompZeroOf c h`

assumes that `h`

is a proof of `t R 0`

.
It produces a pair `(R', h')`

, where `h'`

is a proof of `c*t R' 0`

.
Typically `R`

and `R'`

will be the same, except when `c = 0`

, in which case `R'`

is `=`

.
If `c = 1`

, `h'`

is the same as `h`

-- specifically, it does *not* change the type to `1*t R 0`

.

## Equations

- One or more equations did not get rendered due to their size.