lean4-htt/src/Init/Omega/LinearCombo.lean
Scott Morrison 94d6286e5a
chore: reorganising to reduce imports (#3790)
[Before](https://github.com/leanprover/lean4/files/14772220/oi.pdf) and
[after](https://github.com/leanprover/lean4/files/14772226/oi2.pdf).

This gets `ByteArray`, `String.Extra`, `ToString.Macro` and `RCases` out
of the imports of `omega`. I'd hoped to get `Array.Subarray` too, but
it's tangled up in the list literal syntax. Further progress could come
from make `split` use available `Decidable` instances, so we could pull
out `Classical` (and possibly some of `PropLemmas`).
2024-03-27 11:15:01 +00:00

179 lines
6.7 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/-
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
prelude
import Init.Omega.Coeffs
import Init.Data.ToString.Macro
/-!
# Linear combinations
We use this data structure while processing hypotheses.
-/
namespace Lean.Omega
/-- Internal representation of a linear combination of atoms, and a constant term. -/
structure LinearCombo where
/-- Constant term. -/
const : Int := 0
/-- Coefficients of the atoms. -/
coeffs : Coeffs := []
deriving DecidableEq, Repr
namespace LinearCombo
instance : ToString LinearCombo where
toString lc :=
s!"{lc.const}{String.join <| lc.coeffs.toList.enum.map fun ⟨i, c⟩ => s!" + {c} * x{i+1}"}"
instance : Inhabited LinearCombo := ⟨{const := 1}⟩
theorem ext {a b : LinearCombo} (w₁ : a.const = b.const) (w₂ : a.coeffs = b.coeffs) :
a = b := by
cases a; cases b
subst w₁; subst w₂
congr
/--
Evaluate a linear combination `⟨r, [c_1, …, c_k]⟩` at values `[v_1, …, v_k]` to obtain
`r + (c_1 * x_1 + (c_2 * x_2 + ... (c_k * x_k + 0))))`.
-/
def eval (lc : LinearCombo) (values : Coeffs) : Int :=
lc.const + lc.coeffs.dot values
@[simp] theorem eval_nil : (lc : LinearCombo).eval .nil = lc.const := by
simp [eval]
/-- The `i`-th coordinate function. -/
def coordinate (i : Nat) : LinearCombo where
const := 0
coeffs := Coeffs.set .nil i 1
@[simp] theorem coordinate_eval (i : Nat) (v : Coeffs) :
(coordinate i).eval v = v.get i := by
simp [eval, coordinate]
theorem coordinate_eval_0 : (coordinate 0).eval (.ofList (a0 :: t)) = a0 := by simp
theorem coordinate_eval_1 : (coordinate 1).eval (.ofList (a0 :: a1 :: t)) = a1 := by simp
theorem coordinate_eval_2 : (coordinate 2).eval (.ofList (a0 :: a1 :: a2 :: t)) = a2 := by simp
theorem coordinate_eval_3 :
(coordinate 3).eval (.ofList (a0 :: a1 :: a2 :: a3 :: t)) = a3 := by simp
theorem coordinate_eval_4 :
(coordinate 4).eval (.ofList (a0 :: a1 :: a2 :: a3 :: a4 :: t)) = a4 := by simp
theorem coordinate_eval_5 :
(coordinate 5).eval (.ofList (a0 :: a1 :: a2 :: a3 :: a4 :: a5 :: t)) = a5 := by simp
theorem coordinate_eval_6 :
(coordinate 6).eval (.ofList (a0 :: a1 :: a2 :: a3 :: a4 :: a5 :: a6 :: t)) = a6 := by simp
theorem coordinate_eval_7 :
(coordinate 7).eval
(.ofList (a0 :: a1 :: a2 :: a3 :: a4 :: a5 :: a6 :: a7 :: t)) = a7 := by simp
theorem coordinate_eval_8 :
(coordinate 8).eval
(.ofList (a0 :: a1 :: a2 :: a3 :: a4 :: a5 :: a6 :: a7 :: a8 :: t)) = a8 := by simp
theorem coordinate_eval_9 :
(coordinate 9).eval
(.ofList (a0 :: a1 :: a2 :: a3 :: a4 :: a5 :: a6 :: a7 :: a8 :: a9 :: t)) = a9 := by simp
/-- Implementation of addition on `LinearCombo`. -/
def add (l₁ l₂ : LinearCombo) : LinearCombo where
const := l₁.const + l₂.const
coeffs := l₁.coeffs + l₂.coeffs
instance : Add LinearCombo := ⟨add⟩
@[simp] theorem add_const {l₁ l₂ : LinearCombo} : (l₁ + l₂).const = l₁.const + l₂.const := rfl
@[simp] theorem add_coeffs {l₁ l₂ : LinearCombo} : (l₁ + l₂).coeffs = l₁.coeffs + l₂.coeffs := rfl
/-- Implementation of subtraction on `LinearCombo`. -/
def sub (l₁ l₂ : LinearCombo) : LinearCombo where
const := l₁.const - l₂.const
coeffs := l₁.coeffs - l₂.coeffs
instance : Sub LinearCombo := ⟨sub⟩
@[simp] theorem sub_const {l₁ l₂ : LinearCombo} : (l₁ - l₂).const = l₁.const - l₂.const := rfl
@[simp] theorem sub_coeffs {l₁ l₂ : LinearCombo} : (l₁ - l₂).coeffs = l₁.coeffs - l₂.coeffs := rfl
/-- Implementation of negation on `LinearCombo`. -/
def neg (lc : LinearCombo) : LinearCombo where
const := -lc.const
coeffs := -lc.coeffs
instance : Neg LinearCombo := ⟨neg⟩
@[simp] theorem neg_const {l : LinearCombo} : (-l).const = -l.const := rfl
@[simp] theorem neg_coeffs {l : LinearCombo} : (-l).coeffs = -l.coeffs := rfl
theorem sub_eq_add_neg (l₁ l₂ : LinearCombo) : l₁ - l₂ = l₁ + -l₂ := by
rcases l₁ with ⟨a₁, c₁⟩; rcases l₂ with ⟨a₂, c₂⟩
apply ext
· simp [Int.sub_eq_add_neg]
· simp [Coeffs.sub_eq_add_neg]
/-- Implementation of scalar multiplication of a `LinearCombo` by an `Int`. -/
def smul (lc : LinearCombo) (i : Int) : LinearCombo where
const := i * lc.const
coeffs := lc.coeffs.smul i
instance : HMul Int LinearCombo LinearCombo := ⟨fun i lc => lc.smul i⟩
@[simp] theorem smul_const {lc : LinearCombo} {i : Int} : (i * lc).const = i * lc.const := rfl
@[simp] theorem smul_coeffs {lc : LinearCombo} {i : Int} : (i * lc).coeffs = i * lc.coeffs := rfl
@[simp] theorem add_eval (l₁ l₂ : LinearCombo) (v : Coeffs) :
(l₁ + l₂).eval v = l₁.eval v + l₂.eval v := by
rcases l₁ with ⟨r₁, c₁⟩; rcases l₂ with ⟨r₂, c₂⟩
simp only [eval, add_const, add_coeffs, Int.add_assoc, Int.add_left_comm]
congr
exact Coeffs.dot_distrib_left c₁ c₂ v
@[simp] theorem neg_eval (lc : LinearCombo) (v : Coeffs) : (-lc).eval v = - lc.eval v := by
rcases lc with ⟨a, coeffs⟩
simp [eval, Int.neg_add]
@[simp] theorem sub_eval (l₁ l₂ : LinearCombo) (v : Coeffs) :
(l₁ - l₂).eval v = l₁.eval v - l₂.eval v := by
simp [sub_eq_add_neg, Int.sub_eq_add_neg]
@[simp] theorem smul_eval (lc : LinearCombo) (i : Int) (v : Coeffs) :
(i * lc).eval v = i * lc.eval v := by
rcases lc with ⟨a, coeffs⟩
simp [eval, Int.mul_add]
theorem smul_eval_comm (lc : LinearCombo) (i : Int) (v : Coeffs) :
(i * lc).eval v = lc.eval v * i := by
simp [Int.mul_comm]
/--
Multiplication of two linear combinations.
This is useful only if at least one of the linear combinations is constant,
and otherwise should be considered as a junk value.
-/
def mul (l₁ l₂ : LinearCombo) : LinearCombo :=
l₂.const * l₁ + l₁.const * l₂ - { const := l₁.const * l₂.const }
theorem mul_eval_of_const_left (l₁ l₂ : LinearCombo) (v : Coeffs) (w : l₁.coeffs.isZero) :
(mul l₁ l₂).eval v = l₁.eval v * l₂.eval v := by
have : Coeffs.dot l₁.coeffs v = 0 := IntList.dot_of_left_zero w
simp [mul, eval, this, Coeffs.sub_eq_add_neg, Coeffs.dot_distrib_left, Int.add_mul, Int.mul_add,
Int.mul_comm]
theorem mul_eval_of_const_right (l₁ l₂ : LinearCombo) (v : Coeffs) (w : l₂.coeffs.isZero) :
(mul l₁ l₂).eval v = l₁.eval v * l₂.eval v := by
have : Coeffs.dot l₂.coeffs v = 0 := IntList.dot_of_left_zero w
simp [mul, eval, this, Coeffs.sub_eq_add_neg, Coeffs.dot_distrib_left, Int.add_mul, Int.mul_add,
Int.mul_comm]
theorem mul_eval (l₁ l₂ : LinearCombo) (v : Coeffs) (w : l₁.coeffs.isZero l₂.coeffs.isZero) :
(mul l₁ l₂).eval v = l₁.eval v * l₂.eval v := by
rcases w with w | w
· rw [mul_eval_of_const_left _ _ _ w]
· rw [mul_eval_of_const_right _ _ _ w]
end LinearCombo
end Lean.Omega