Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Define linear programs #7386

Open
wants to merge 42 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
8352c37
LP defined
madvorak Sep 26, 2023
09eb773
for the linter
madvorak Sep 26, 2023
0832139
minimum solution
madvorak Sep 26, 2023
cc01f1a
revert change in manifest
madvorak Sep 26, 2023
f23e30e
generalizations
madvorak Sep 26, 2023
bfffc93
bugfix
madvorak Sep 26, 2023
e927639
linear function instead of a matrix
madvorak Sep 26, 2023
8ad85e1
more definitions of LP
madvorak Sep 28, 2023
3734234
visibility fixed
madvorak Sep 28, 2023
02ee2bd
docstrings on the correct place
madvorak Sep 28, 2023
04fb906
removed empty spaces
madvorak Sep 28, 2023
311abd9
CanonicalLP back to matrix form
madvorak Sep 28, 2023
ed5ca5e
generalization
madvorak Sep 28, 2023
85c2a02
partial fix
madvorak Sep 28, 2023
f6b53ac
more partial fix
madvorak Sep 28, 2023
e5944c5
the type arguments can be inferred from P
madvorak Sep 28, 2023
155d63c
bugfix
madvorak Sep 28, 2023
c3a4529
docstring
madvorak Sep 28, 2023
af45efb
by Eric Wieser
madvorak Sep 28, 2023
f7c80bc
major cleanup
madvorak Sep 28, 2023
fc1e7c5
LP definition finished
madvorak Oct 5, 2023
00da03e
actually it was not the standard form
madvorak Oct 5, 2023
25d9446
Update Mathlib/LinearAlgebra/Matrix/LinearProgramming.lean
madvorak Oct 9, 2023
247b7d4
as per PR
madvorak Oct 10, 2023
7e18396
move LP out of Matrix
madvorak Oct 10, 2023
b3b95a0
path changed
madvorak Oct 10, 2023
d6b731b
proof fix
madvorak Oct 10, 2023
5a04ae0
two trivial results
madvorak Oct 15, 2023
a6bb9fe
names fix
madvorak Oct 16, 2023
432c597
trivial lemma
madvorak Nov 10, 2023
5d2eba8
Update Mathlib/LinearAlgebra/LinearProgramming.lean
madvorak Nov 10, 2023
da8146f
Update Mathlib/LinearAlgebra/LinearProgramming.lean
madvorak Nov 10, 2023
0500287
fix
madvorak Nov 10, 2023
72a8028
refactoring a ∈ equalities → a x = 0
madvorak Nov 10, 2023
acb1869
Update Mathlib/LinearAlgebra/LinearProgramming.lean
madvorak Nov 13, 2023
8d9feeb
Update Mathlib/LinearAlgebra/LinearProgramming.lean
madvorak Nov 13, 2023
ca6899c
fix and refactor
madvorak Nov 13, 2023
3f08fde
refactor the proof of LinearProgram.feasibles_mkOfEqs
madvorak Nov 13, 2023
5560272
golf a long proof
eric-wieser Nov 13, 2023
0f8071d
LinearOrderedRing is enough
madvorak Nov 18, 2023
0b5163b
LP refactoring
madvorak Nov 24, 2023
45bbb4b
LP chore
madvorak Nov 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Mathlib.lean
Expand Up @@ -2268,6 +2268,7 @@ import Mathlib.LinearAlgebra.Isomorphisms
import Mathlib.LinearAlgebra.Lagrange
import Mathlib.LinearAlgebra.LinearIndependent
import Mathlib.LinearAlgebra.LinearPMap
import Mathlib.LinearAlgebra.LinearProgramming
import Mathlib.LinearAlgebra.Matrix.AbsoluteValue
import Mathlib.LinearAlgebra.Matrix.Adjugate
import Mathlib.LinearAlgebra.Matrix.Basis
Expand Down
104 changes: 104 additions & 0 deletions Mathlib/LinearAlgebra/LinearProgramming.lean
@@ -0,0 +1,104 @@
/-
Copyright (c) 2023 Martin Dvorak. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Martin Dvorak
-/
import Mathlib.LinearAlgebra.Matrix.ToLin
import Mathlib.LinearAlgebra.AffineSpace.AffineMap

/-! # Linear programming

Minimizing a linear function on a region defined by linear inequalities.
madvorak marked this conversation as resolved.
Show resolved Hide resolved

## Main definitions

* `LinearProgram` defines a linear program in a form that generalizes "A x ≥ b".
* `LinearProgram.feasibles` is the set of all admissible solutions to given linear program.
* `LinearProgram.MinAt` defines an optimum solution of given linear program.

-/

/-- Linear program in the form of inequalities with general variables. -/
structure LinearProgram (K : Type*) {V : Type*} (P : Type*)
[LinearOrderedField K] [AddCommGroup V] [Module K V] [AddTorsor V P] where
madvorak marked this conversation as resolved.
Show resolved Hide resolved
/-- Inequality constraints (given in the form "aᵀx - b ≥ 0") -/
constraints : List (P →ᵃ[K] K)
madvorak marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am skeptical about making this a List rather than a Set. One is not actually going to do computation with this type: any meaningful algorithm will require a much more complicated data structure.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I remember Martin expressing some objections to situations with infinitely-many constraints, as those tend not to appear in linear programming. That would rule out Set, and prohibiting duplicates or permutations isn't particularly interesting since the constraints can still be redundant.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to hear Martin's take on this, or for someone to click "resolve" as is.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If finiteness is really the thing of interest though, you could argue that what we actually want is the type of convex sets built from finitely many half planes; either non-constructively via an existential, or constructively via a Finset and a proof that removing any one element increases the size of the feasible set.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you induct on Finsets?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right. Just one comment (not contradicting to your suggestion); all point on the sphere would be vertices.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About the second part of your comment, redundant constraints are often a thing that we have to deal with (because they come from the ambient problem in the LP). So we probably shouldn't have the proof obligation that says "removing a constraint strictly increases the feasible set"

I don't think this claim necessarily holds merit; finsets are not allowed to hold duplicates, and have a proof obligation to this end; but that doesn't stop you from writing {1, 1, 2, 2}. You only have to deal with the proof obligation if you call Finset.mk directly.

Arguably this "Finset of non-redundant affine constraints" object deserves it's own ConvexPolytope object.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though another reasonable approach might be to discard the proof obligation and work with the quotient of the type by equality of feasible spaces

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A redundant LP need not have identical constraints. It just needs to be a linear combination of other constraints. I am not sure I see the value in doing, let's say Gaussian elimination on constraints, to extract independent ones makes sense every time an LP is defined. In computer science and also possibly OR, redundant constraints are a fact of life that have to be dealt with algorithmically, and so one often constructs what Mathlib devs might call API lemmas to deal with these cases.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I think about it, having infinitely many conditions is less weird than having infinitely many variables. Given that Mathlib maintainers want the variable space to be general, we should also make the condition space general. Defining conditions as a list (or a multiset, or a finite set) of any type will be bad for LP duality, unless we change the type of P along with it.

/-- The objective function (affine map) -/
objective : P →ᵃ[K] K
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't particularly like the design, with constraints and objective bundled together. The feasibility of constraints makes no mention of the objective, and the design should allow talking about this without an objective. Please separate these.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you be more happy with the following definitions?

def LinearProgramFeas (R : Type*) {V : Type*} (P : Type*)
    [LinearOrderedRing R] [AddCommGroup V] [Module R V] [AddTorsor V P] :=
  List (P →ᵃ[R] R)

structure LinearProgramOpt (R : Type*) {V : Type*} (P : Type*)
    [LinearOrderedRing R] [AddCommGroup V] [Module R V] [AddTorsor V P] where
  constraints : LinearProgramFeas R P
  objective : P →ᵃ[R] R

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not two structures, one extending the other?

Presumably for the "just the constraints" version, you then want definitions satisfiable_at and satisfiable, with lots of lemmas about how these interact with list operations (e.g. adding/erasing etc constraints).


variable {K V P : Type*} [LinearOrderedField K] [AddCommGroup V] [Module K V] [AddTorsor V P]

/-- Constructs a linear program given a list of equalities, a list of inequalities,
and an objective function. -/
def LinearProgram.mkOfEqs
(equalities inequalities : List (P →ᵃ[K] K)) (objective : P →ᵃ[K] K) :
LinearProgram K P :=
{ constraints := inequalities ++ equalities ++ equalities.map Neg.neg, objective }

/-- The set of all admissible solutions to given linear program. -/
def LinearProgram.feasibles (lp : LinearProgram K P) : Set P :=
{x | ∀ ⦃a⦄, a ∈ lp.constraints → 0 ≤ a x}

@[simp] lemma LinearProgram.mem_feasibles {lp : LinearProgram K P} {x : P} :
x ∈ lp.feasibles ↔ ∀ ⦃a⦄, a ∈ lp.constraints → 0 ≤ a x :=
Iff.rfl

/-- Given linear program is minimized at given point. -/
def LinearProgram.MinAt (lp : LinearProgram K P) (x : P) : Prop :=
IsLeast (lp.objective '' lp.feasibles) (lp.objective x)

lemma LinearProgram.feasibles_mkOfEqs
(equalities inequalities : List (P →ᵃ[K] K)) (objective : P →ᵃ[K] K) :
(mkOfEqs equalities inequalities objective).feasibles =
{ x : P | (∀ a ∈ equalities, a x = 0) ∧ (∀ a ∈ inequalities, 0 ≤ a x) } := by
ext x
constructor
· intro hyp
rw [Set.mem_setOf_eq]
simp only [LinearProgram.feasibles, LinearProgram.mkOfEqs,
List.append_assoc, List.mem_append, List.mem_map, Set.mem_setOf_eq,
Function.Involutive.exists_mem_and_apply_eq_iff, neg_involutive] at hyp
constructor
· intro constr_eq mem_equalities
refine le_antisymm ?neg (hyp (by simp [mem_equalities]))
rw [← Left.nonneg_neg_iff, ← Pi.neg_apply, ← AffineMap.coe_neg]
apply hyp
simp [mem_equalities]
· intro constr_le mem_inequalities
exact hyp (by simp [mem_inequalities])
· intro hyp
simp only [Set.mem_setOf_eq, LinearProgram.feasibles] at hyp
madvorak marked this conversation as resolved.
Show resolved Hide resolved
simp only [LinearProgram.feasibles, LinearProgram.mkOfEqs]
intro constraint constraint_mem
rw [List.mem_append, List.mem_append] at constraint_mem
cases constraint_mem with
| inl normal =>
cases normal with
| inl mem_les => exact hyp.2 constraint mem_les
| inr mem_eqs => exact Eq.ge (hyp.1 constraint mem_eqs)
| inr negated =>
rw [List.mem_map] at negated
rcases negated with ⟨orig, orig_mem, neg_orig⟩
rw [← neg_orig]
simp [hyp.1 orig orig_mem]

/-- Adding more constraints cannot enlarge the set of feasible solutions. -/
lemma feasiblesLinearProgram_superset_of_constraints_subset {lp₁ lp₂ : LinearProgram K P}
madvorak marked this conversation as resolved.
Show resolved Hide resolved
(constrss : lp₁.constraints ⊆ lp₂.constraints) :
lp₂.feasibles ⊆ lp₁.feasibles := by
intro x hx
simp only [LinearProgram.feasibles, Set.mem_setOf_eq] at hx ⊢
intro a ha
apply hx
exact constrss ha

/-- Adding more constraints cannot decrease the minimum. -/
lemma minLinearProgram_le_of_constraints_subset {lp₁ lp₂ : LinearProgram K P} {x₁ x₂ : P}
(constrss : lp₁.constraints ⊆ lp₂.constraints)
(hobj : lp₁.objective = lp₂.objective) (opt₁ : lp₁.MinAt x₁) (opt₂ : lp₂.MinAt x₂) :
lp₁.objective x₁ ≤ lp₂.objective x₂ := by
unfold LinearProgram.MinAt at opt₁ opt₂
apply IsLeast.mono opt₂ opt₁
rw [hobj]
apply Set.image_subset
exact feasiblesLinearProgram_superset_of_constraints_subset constrss