Zulip Chat Archive
Stream: new members
Topic: Optimal solutions exist for discrete problems
Asei Inoue (Nov 04 2023 at 16:54):
import Mathlib.Tactic
namespace Knapsack
open List
/-!
## The definition of Knapsack problem
-/
structure Item where
/-- the weight of item -/
weight : ℕ
/-- the price of item -/
price : ℕ
deriving Repr
structure Problem where
/-- the list of items -/
items : List Item
/-- the capacity of the knapsack -/
capacity : ℕ
deriving Repr
/-!
## The Basic Functions
-/
abbrev ItemList := List Item
def ItemList.price (items : ItemList) : ℕ :=
(items.map Item.price).sum
def ItemList.weight (items : ItemList) : ℕ :=
(items.map Item.weight).sum
def ItemList.feasible (items : ItemList) (P : Problem) : Bool :=
items.weight <= P.capacity
def ItemList.optimal (items : ItemList) (P : Problem) : Prop :=
items.feasible P ∧
∀ opp : ItemList, opp.feasible P → opp.price ≤ items.price
theorem exist_optimal (P : Problem) : ∃ items : ItemList, items.optimal P := by
sorry
Asei Inoue (Nov 04 2023 at 16:55):
I am always grateful to those who answer my questions on Zulip. :pray:
I have defined the Knapsack problem as described above. However, I am having trouble proving an optimal solution exists
. How can I fill in the above sorry
?
Asei Inoue (Nov 04 2023 at 16:59):
The existence of an optimal solution is obviously not a fact specific to the Knapsack problem. Would it be easier to define a more general 'discrete problem' and show for it...?
Adomas Baliuka (Nov 04 2023 at 17:18):
Isn't the theorem false? You didn't specify what Item
s are available so I can always have an item of weight 0 with arbitrary price. Put that in an ItemList
and you get arbitrarily high-price knapsacks.
Adomas Baliuka (Nov 04 2023 at 17:25):
In fact,
open ItemList in
theorem not_exist_optimal (P : Problem) : ¬∃ items : ItemList, items.optimal P := by
simp
intro itl
simp [optimal, feasible]
intro _incap
use [{weight:=0, price:=itl.price + 1 : Item}]
simp [weight, price]
Asei Inoue (Nov 04 2023 at 18:03):
@Adomas Baliuka Thanks. I was careless ...
Asei Inoue (Nov 04 2023 at 18:03):
It seems that I have to edit the definition of feasible
.
But if I add items ⊆ P.items
to the definition of ItemList.feasible
, fun items : ItemList ↦ items.feasible P
would not work.
It is not immediately clear how to fix it.
Adomas Baliuka (Nov 04 2023 at 18:20):
I don't understand what "would not work". What do you want this hypothetical fun items
to do?
Asei Inoue (Nov 04 2023 at 18:22):
@Adomas Baliuka
Oh sorry... My whole code is here:
import Mathlib.Tactic
namespace Knapsack
open List
/-!
## The definition of Knapsack problem
-/
structure Item where
/-- the weight of item -/
weight : ℕ
/-- the price of item -/
price : ℕ
deriving Repr
structure Problem where
/-- the list of items -/
items : List Item
/-- the capacity of the knapsack -/
capacity : ℕ
deriving Repr
/-!
## The Basic Functions
-/
abbrev ItemList := List Item
def ItemList.price (items : ItemList) : ℕ :=
(items.map Item.price).sum
def ItemList.weight (items : ItemList) : ℕ :=
(items.map Item.weight).sum
def ItemList.feasible (items : ItemList) (P : Problem) : Prop :=
items ⊆ P.items ∧ items.weight <= P.capacity
def ItemList.optimal (items : ItemList) (P : Problem) : Prop :=
items.feasible P ∧
∀ opp : ItemList, opp.feasible P → opp.price ≤ items.price
theorem exist_optimal (P : Problem) : ∃ items : ItemList, items.optimal P := by
sorry
/-!
## Examples for Test
--------------------
-/
def problem1 : Problem := {
items := [Item.mk 1 2, Item.mk 2 1, Item.mk 3 4]
capacity := 5
}
#eval problem1.items
def sol1 : ItemList := [Item.mk 1 2]
#eval sol1.price
/-!
## Algorithm to Solve Knapsack Problem
--------------------------------------
-/
variable (α β : Type) [LinearOrder β]
def brute (P : Problem) : ItemList :=
let sublists := P.items.sublists
let feasible_solutions := sublists.filter (fun items : ItemList ↦ items.feasible P) -- error
let max_list := argmax ItemList.price feasible_solutions
match max_list with
| none => []
| some opt => opt
#eval brute problem1
Asei Inoue (Nov 04 2023 at 18:26):
What do you want this hypothetical fun items to do?
I want to use feasible
to filter items, but the function must return a Bool type instead of a Prop type.
Asei Inoue (Nov 04 2023 at 18:27):
However, if items ⊆ P.items
is included in the definition, it will be of type Prop.
Adomas Baliuka (Nov 04 2023 at 18:31):
You cannot compute with Prop
anyway. Use Bool
instead. I would separate the conditions "within capacity" (Bool
so you can use it inside brute
) and "optimal" still has to be Prop
because you use a quantifier.
Yaël Dillies (Nov 04 2023 at 18:45):
Certainly you can compute with Prop. You just need to provide a Decidable
instance.
Adomas Baliuka (Nov 04 2023 at 21:08):
I wrote a Decidable
instance, seems to work:
import Mathlib.Tactic
namespace Knapsack
open List
/-!
## The definition of Knapsack problem
-/
structure Item where
/-- the weight of item -/
weight : ℕ
/-- the price of item -/
price : ℕ
deriving Repr, DecidableEq -- added this to make instance work !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
structure Problem where
/-- the list of items -/
items : List Item
/-- the capacity of the knapsack -/
capacity : ℕ
deriving Repr, DecidableEq -- added this too (probably won't ever get used) !!!!!!!!!!!!!!!!
/-!
## The Basic Functions
-/
abbrev ItemList := List Item
def ItemList.price (items : ItemList) : ℕ :=
(items.map Item.price).sum
def ItemList.weight (items : ItemList) : ℕ :=
(items.map Item.weight).sum
def ItemList.feasible (items : ItemList) (P : Problem) : Prop :=
items ⊆ P.items ∧ items.weight <= P.capacity
open ItemList in
instance instDecidableFeasible (items : ItemList) (P : Problem)
: Decidable (ItemList.feasible items P) := by
simp [feasible, weight]
infer_instance
def ItemList.optimal (items : ItemList) (P : Problem) : Prop :=
items.feasible P ∧
∀ opp : ItemList, opp.feasible P → opp.price ≤ items.price
open ItemList in
theorem exist_optimal (P : Problem) (nonem : P.items ≠ []) : ∃ items : ItemList, items.optimal P := by
sorry
-- something like this might help, although I also haven't proved it yet...
#check Finset.exists_maximal
/-!
## Examples for Test
--------------------
-/
def problem1 : Problem := {
items := [⟨1, 2⟩, ⟨2, 1⟩, ⟨3, 4⟩]
capacity := 5
}
#eval problem1.items
def sol1 : ItemList := [⟨1, 2⟩]
#eval sol1.price
/-!
## Algorithm to Solve Knapsack Problem
--------------------------------------
-/
variable (α β : Type) [LinearOrder β]
def brute (P : Problem) : ItemList :=
let sublists := P.items.sublists
let feasible_solutions := sublists.filter (fun items : ItemList ↦ decide (items.feasible P))
let max_list := argmax ItemList.price feasible_solutions
match max_list with
| none => []
| some opt => opt
#eval brute problem1
Z. Wu (Nov 05 2023 at 02:36):
Not sure if it's appropriate here, but do you mind posting the final proof when you are done?
I'm interested to see the final version of this problem
Asei Inoue (Nov 05 2023 at 02:49):
@Adomas Baliuka @Yaël Dillies Thank you very much. I'm glad to know how to use `Decidable.
Asei Inoue (Nov 05 2023 at 02:52):
@Z. Wu Thanks for your interest. I'll share the code here when it's completed.
Asei Inoue (Nov 05 2023 at 05:27):
There is an optimal solution even if the item set is empty
@Adomas Baliuka Why did you add hypothesis (nonem : P.items ≠ [])
? If P.items = []
, then []
is an optimal solution.
Asei Inoue (Nov 05 2023 at 05:55):
I can prove optimal_of_empty
as followings.
import Mathlib.Tactic
namespace Knapsack
open List
/-!
## The definition of Knapsack problem
-/
structure Item where
/-- the weight of item -/
weight : ℕ
/-- the price of item -/
price : ℕ
deriving Repr, DecidableEq
structure Problem where
/-- the list of items -/
items : List Item
/-- the capacity of the knapsack -/
capacity : ℕ
deriving Repr, DecidableEq
/-!
## The Basic Functions
-/
abbrev ItemList := List Item
def ItemList.price (items : ItemList) : ℕ :=
(items.map Item.price).sum
def ItemList.weight (items : ItemList) : ℕ :=
(items.map Item.weight).sum
def ItemList.feasible (items : ItemList) (P : Problem) : Prop :=
items ⊆ P.items ∧ items.weight <= P.capacity
open ItemList
instance instDecidableFeasible (items : ItemList) (P : Problem) :
Decidable (ItemList.feasible items P) := by
simp [feasible, weight]
infer_instance
def ItemList.optimal (items : ItemList) (P : Problem) : Prop :=
items.feasible P ∧
∀ opp : ItemList, opp.feasible P → opp.price ≤ items.price
@[simp]
lemma optimal_of_empty (P : Problem) (emp : P.items = []) : optimal [] P := by
simp [optimal, feasible, weight, emp]
intro opp hopp
replace hopp : opp = [] := by
exact? says
exact subset_nil.mp hopp
simp? [hopp] says
simp only [
hopp,
map_nil,
sum_nil,
zero_le,
le_refl,
forall_true_left
]
theorem exist_optimal (P : Problem) : ∃ items : ItemList, items.optimal P := by
let sublists := P.items.sublists
let feasible_solutions := sublists.filter (fun items : ItemList ↦ decide (items.feasible P))
sorry
/- Finset.exists_maximal.{u_4} {α : Type u_4} [inst✝ : Preorder α] (s : Finset α) (h : Finset.Nonempty s) :
∃ m ∈ s, ∀ x ∈ s, ¬m < x -/
#check Finset.exists_maximal
/-!
## Examples for Test
--------------------
-/
def problem1 : Problem := {
items := [⟨1, 2⟩, ⟨2, 1⟩, ⟨3, 4⟩]
capacity := 5
}
#eval problem1.items
def sol1 : ItemList := [⟨1, 2⟩]
#eval sol1.price
/-!
## Algorithm to Solve Knapsack Problem
--------------------------------------
-/
variable (α β : Type) [LinearOrder β]
def brute (P : Problem) : ItemList :=
let sublists := P.items.sublists
let feasible_solutions := sublists.filter (fun items : ItemList ↦ decide (items.feasible P))
let max_list := argmax ItemList.price feasible_solutions
match max_list with
| none => []
| some opt => opt
#eval brute problem1
Asei Inoue (Nov 05 2023 at 06:15):
finite set has an element that gives the maximum value of the function
-- something like this might help, although I also haven't proved it yet...
#check Finset.exists_maximal
Thank you very much! I am sure your comments will be useful. But I want a proposition that shows that there exists an element in a finite set that maximises the value of a given function.
Asei Inoue (Nov 05 2023 at 06:16):
Unfortunately I don't find such a theorem.
import Mathlib.Tactic
#find Finset ?a → (?a → ?b) → ?a
I would like to ask if there is a similar theorem in mathlib.
Yakov Pechersky (Nov 05 2023 at 06:43):
(Finset.image f s).max' (hs.image _), where s is your finset and hs is your proof that it's not empty. This is the direct construction of the max. Then, Finset.max'_mem is the proof that that max is a member of the image. Use Finset.mem_image to rewrite that proof into the existential of the element in the original finset such that it maps to the max.
Yakov Pechersky (Nov 05 2023 at 06:43):
(deleted)
Asei Inoue (Nov 05 2023 at 07:12):
@Yakov Pechersky Thanks! I will try it.
Asei Inoue (Nov 05 2023 at 07:31):
How can I create a Finset
term from a list of lists?
import Mathlib.Tactic
namespace Knapsack
open List
/-!
## The definition of Knapsack problem
-/
structure Item where
/-- the weight of item -/
weight : ℕ
/-- the price of item -/
price : ℕ
deriving Repr, DecidableEq
structure Problem where
/-- the list of items -/
items : List Item
/-- the capacity of the knapsack -/
capacity : ℕ
deriving Repr, DecidableEq
/-!
## The Basic Functions
-/
abbrev ItemList := List Item
def ItemList.price (items : ItemList) : ℕ :=
(items.map Item.price).sum
def ItemList.weight (items : ItemList) : ℕ :=
(items.map Item.weight).sum
def ItemList.feasible (items : ItemList) (P : Problem) : Prop :=
items ⊆ P.items ∧ items.weight <= P.capacity
open ItemList
instance instDecidableFeasible (items : ItemList) (P : Problem) :
Decidable (ItemList.feasible items P) := by
simp [feasible, weight]
infer_instance
def ItemList.optimal (items : ItemList) (P : Problem) : Prop :=
items.feasible P ∧
∀ opp : ItemList, opp.feasible P → opp.price ≤ items.price
@[simp]
lemma optimal_of_empty (P : Problem) (emp : P.items = []) : optimal [] P := by
simp [optimal, feasible, weight, emp]
intro opp hopp
replace hopp : opp = [] := by
exact subset_nil.mp hopp
simp [hopp]
theorem exist_optimal (P : Problem) : ∃ items : ItemList, items.optimal P := by
let sublists := P.items.sublists
let feasible_solutions := sublists.filter (fun items : ItemList ↦ decide (items.feasible P))
let solutions : Finset (P.items : Type) := by sorry -- error!!!!!
sorry
Asei Inoue (Nov 05 2023 at 07:34):
oh I have found a List.toFinset
(https://loogle.lean-lang.org/?q=List+%3Fa+-%3E+Finset+%3Fa)
Asei Inoue (Nov 05 2023 at 08:11):
@Yakov Pechersky Thanks for the advice. But I think it could be shown using the argmax
of the List directly, without going through Finset
.
Asei Inoue (Nov 05 2023 at 08:12):
my attempt:
import Mathlib.Tactic
namespace Knapsack
open List
/-!
## The definition of Knapsack problem
-/
structure Item where
/-- the weight of item -/
weight : ℕ
/-- the price of item -/
price : ℕ
deriving Repr, DecidableEq
structure Problem where
/-- the list of items -/
items : List Item
/-- the capacity of the knapsack -/
capacity : ℕ
deriving Repr, DecidableEq
/-!
## The Basic Functions
-/
abbrev ItemList := List Item
def ItemList.price (items : ItemList) : ℕ :=
(items.map Item.price).sum
def ItemList.weight (items : ItemList) : ℕ :=
(items.map Item.weight).sum
def ItemList.feasible (items : ItemList) (P : Problem) : Prop :=
items ⊆ P.items ∧ items.weight <= P.capacity
open ItemList
instance instDecidableFeasible (items : ItemList) (P : Problem) :
Decidable (ItemList.feasible items P) := by
simp [feasible, weight]
infer_instance
/-!
### Optimality of a feasible solution
-/
def ItemList.optimal (items : ItemList) (P : Problem) : Prop :=
items.feasible P ∧
∀ opp : ItemList, opp.feasible P → opp.price ≤ items.price
@[simp]
lemma optimal_of_empty (P : Problem) (emp : P.items = []) : optimal [] P := by
simp [optimal, feasible, weight, emp]
intro opp hopp
replace hopp : opp = [] := by
exact subset_nil.mp hopp
simp [hopp]
#check argmax
#eval argmax (fun x ↦ - x) [1, 2, -5, 3]
theorem exist_optimal (P : Problem) : ∃ items : ItemList, items.optimal P := by
let sublists := P.items.sublists
let feasible_solution_list := sublists.filter (fun items : ItemList ↦ decide (items.feasible P))
have nonemp : feasible_solution_list ≠ [] := by
simp [feasible_solution_list]
intro h
guard_hyp h : filter (fun items => decide (feasible items P)) (List.sublists P.items) = []
-- how to get `[] : ItemList is not feasible solution of P` from `h`...?
sorry
let optimal_solution := argmax ItemList.price feasible_solution_list
match optimal_solution with
| none => sorry
| some opt =>
exists opt
unfold optimal
refine ⟨?feas, ?opt⟩
case feas =>
sorry
case opt =>
sorry
Yakov Pechersky (Nov 05 2023 at 08:12):
You asked about a Finset function, so I gave you a finset function.
Asei Inoue (Nov 05 2023 at 08:14):
@Yakov Pechersky Oh you're right. I didn't check enough ... Sorry ...
Yakov Pechersky (Nov 05 2023 at 08:14):
The same strategy of max'
, max'_mem
, and mem_image
should work for the equivalent statements for lists.
Yakov Pechersky (Nov 05 2023 at 08:21):
Suggestion: prove that Itemlist.feasible [] P
for any P
. Then you know for sure that feasible_solution_list \ne []
since []
is in any sublists
.
Yakov Pechersky (Nov 05 2023 at 08:26):
And use refine
with argmax
, thus leaving you with solely the \forall opp ...
goal. That goal should be dealt with using docs#List.le_of_mem_argmin. Since you have that opp \subset P.items
, so opp \mem P.items.sublists
so opp.price \le (argmax ...).price
.
Yaël Dillies (Nov 05 2023 at 09:14):
Asei Inoue said:
But I want a proposition that shows that there exists an element in a finite set that maximises the value of a given function.
Asei Inoue (Nov 05 2023 at 13:28):
theorem exist_optimal (P : Problem) : ∃ items : ItemList, items.optimal P := by
let sublists := P.items.sublists
let feasible_solution_list := sublists.filter (fun items : ItemList ↦ decide (items.feasible P))
have nonemp : feasible_solution_list ≠ [] := by
simp [feasible_solution_list]
intro h
set f := fun items => decide (feasible items P) with fh
have femp : f [] := by simp [f]
have emp_sub : [] ∈ P.items.sublists := by
simp [sublists]
have emp_mem : [] ∈ filter f (List.sublists P.items) := by exact mem_filter_of_mem emp_sub femp
rw [h] at emp_mem
contradiction
let feasible_solution_finset := feasible_solution_list.toFinset
let optimal_solution := argmax ItemList.price feasible_solution_list
match optimal_solution with
| none => sorry
| some opt =>
exists opt
unfold optimal
refine ⟨?feas, ?opt⟩
case feas =>
sorry
case opt =>
sorry
Asei Inoue (Nov 05 2023 at 13:29):
I am finally able to show nonemp . Thanks.
Adomas Baliuka (Nov 05 2023 at 17:07):
I tried doing this proof yesterday and got stuck on the fact that sublists
does not give you "every feasible subset" due to order. So using Finset.exists_max_image
requires you to prove that "for any feasible subset of available items, there is a sublist inside feasible_solution_list
that has the same price (i.e., a reordering that is inside feasible_solution_list
)". Of course, this can be proved but it might take some work that I had not anticipated.
Alternatively, one might use some function instead of sublists
which gives you a list of "all permutations of sublists". I didn't find such a function in the library. If you want to prove eventually that brute
gives an optimal solution, this approach might not be the way to go.
Adomas Baliuka (Nov 05 2023 at 17:12):
Here's what I did, just in case it helps you... I think I won't have time to play with this for a while
lemma ItemList.emptyFeasible (P : Problem) : ItemList.feasible [] P := by simp [ItemList.feasible, weight]
lemma mem_toFinset_pred [DecidableEq α] (l : List α) (P : α → Prop)
: (∃ x ∈ l, P x) ↔ (∃ x ∈ l.toFinset, P x) := by
simp_all only [mem_toFinset]
#check Finset.exists_max_image
lemma List.exists_max_image [DecidableEq α] [DecidableEq β] [LinearOrder α]
{l : List β} (f : β → α) (h : l ≠ [])
: ∃ x ∈ l, ∀ x' ∈ l, f x' ≤ f x := by
apply (mem_toFinset_pred l (fun m ↦ ∀ x ∈ l, f x ≤ f m)).mpr
simp_rw [←mem_toFinset]
apply Finset.exists_max_image
exact (toFinset_nonempty_iff l).mpr h
-- solution ∈ filter (fun items => decide (feasible items P)) (sublists P.items)
lemma prop_of_filter_prop [DecidableEq α] {l : List α} {P : α → Prop} [DecidablePred P] {a : α}
(h : a ∈ filter (fun a' => decide (P a')) l)
: P a := by
have := List.filter_subset (p:=(fun a' => decide (P a'))) l
have := (List.mem_filter.mp h).2
simp_all only [decide_eq_true_eq]
open ItemList in
theorem exist_optimal (P : Problem)
: ∃ items : ItemList, items.optimal P := by
let feasibles := (P.items).sublists.filter (fun items : ItemList ↦ decide (items.feasible P))
have sublnonempty : feasibles ≠ [] := by
simp [feasibles]
have nil_mem_sublists : [] ∈ P.items.sublists := by simp only [mem_sublists, nil_sublist]
by_contra h
have := List.filter_eq_nil.mp h [] (by simp)
have emptyFeas := emptyFeasible P
simp at this
contradiction
obtain ⟨solution, ⟨isfeasible, ismax⟩⟩ := List.exists_max_image price sublnonempty
use solution
simp [optimal]
constructor
· simp [feasibles] at isfeasible
apply prop_of_filter_prop isfeasible
· rintro opp ⟨opp_within, opp_capac⟩
sorry -- remains to prove that any feasible *subset* of P.items
-- is a reordering of some *sublist* inside `feasibles`, and that
-- `ItemList.price` is permutation-invariant.
Adomas Baliuka (Nov 05 2023 at 17:35):
(Also, of course, I was dumb and didn't use List.argmax
, so I had to prove a version of List.le_of_mem_argmax
, which you probably shouldn't copy...)
Yakov Pechersky (Nov 05 2023 at 18:42):
My argmax idea was wrong. Because what feasible
is doesn't limit the number of times an Item can be duplicated. For example, I can have a problem of { items := [(1, 1)], capacity: 100 }
. Then the optimal items are that item x 100. That's never going to be in sublists.
Yakov Pechersky (Nov 05 2023 at 18:44):
This is my way of building out the API:
import Mathlib.Tactic
namespace Knapsack
open List
/-!
## The definition of Knapsack problem
-/
structure Item where
/-- the weight of item -/
weight : ℕ
/-- the price of item -/
price : ℕ
deriving Repr, DecidableEq
structure Problem where
/-- the list of items -/
items : List Item
/-- the capacity of the knapsack -/
capacity : ℕ
deriving Repr, DecidableEq
/-!
## The Basic Functions
-/
abbrev ItemList := List Item
def ItemList.price (items : ItemList) : ℕ :=
(items.map Item.price).sum
def ItemList.weight (items : ItemList) : ℕ :=
(items.map Item.weight).sum
def ItemList.feasible (items : ItemList) (P : Problem) : Prop :=
items ⊆ P.items ∧ items.weight <= P.capacity
theorem ItemList.feasible_iff {items: ItemList} {P : Problem} :
items.feasible P ↔ items ⊆ P.items ∧ items.weight <= P.capacity := Iff.rfl
@[simp]
theorem ItemList.feasible_nil (P : Problem) : ItemList.feasible [] P :=
⟨nil_subset _, Nat.zero_le _⟩
abbrev Problem.empty (P : Problem) : Prop := P.items = []
theorem ItemList.feasible_empty_iff {items: ItemList} {P : Problem} (h : P.empty) :
items.feasible P ↔ items = [] := by
simp (config := { contextual := true }) [feasible_iff, h, List.subset_nil, weight]
theorem ItemList.perm_price_eq {items items' : ItemList} (h : items ~ items') :
items.price = items'.price := by
simp [price, (h.symm.map _).sum_eq]
theorem ItemList.perm_weight_eq {items items' : ItemList} (h : items ~ items') :
items.weight = items'.weight := by
simp [weight, (h.symm.map _).sum_eq]
theorem ItemList.feasible.perm {items items' : ItemList} {P : Problem} (h : items.feasible P)
(hp : items ~ items') : items'.feasible P :=
⟨hp.symm.subset.trans h.left, h.right.trans' (perm_weight_eq hp).ge⟩
open ItemList
instance instDecidableFeasible (items : ItemList) (P : Problem) :
Decidable (ItemList.feasible items P) :=
decidable_of_iff' _ (ItemList.feasible_iff)
/-!
### Optimality of a feasible solution
-/
def ItemList.optimal (items : ItemList) (P : Problem) : Prop :=
items.feasible P ∧
∀ opp : ItemList, opp.feasible P → opp.price ≤ items.price
theorem ItemList.optimal_iff {items : ItemList} {P : Problem} :
items.optimal P ↔
items.feasible P ∧ ∀ opp : ItemList, opp.feasible P → opp.price ≤ items.price := Iff.rfl
lemma optimal_of_empty (P : Problem) (emp : P.empty) : optimal [] P := by
simp [optimal_iff, feasible_empty_iff emp]
theorem ItemList.optimal.perm {items items' : ItemList} {P : Problem} (h : items.optimal P)
(hp : items ~ items') : items'.optimal P := by
rw [optimal_iff, items.perm_price_eq hp] at *
exact h.imp (ItemList.feasible.perm · hp) (by simp)
@[simp]
theorem _root_.List.sublists_ne_nil {α : Type*} (l : List α) : l.sublists ≠ [] := by
induction l
case nil => simp
case cons =>
rw [sublists_cons]
simp only [bind_eq_bind, ne_eq, bind_eq_nil, mem_sublists, not_forall, exists_prop, and_true]
exact ⟨[], by simp⟩
theorem _root_.List.argmax_isSome_of_ne_nil {α β : Type*} [Preorder β]
[DecidableRel (fun x y : β ↦ x < y)] (l : List α) (f : α → β) (hl : l ≠ []) :
(l.argmax f).isSome := by
simp [←Option.ne_none_iff_isSome, hl]
theorem _root_.List.argmin_isSome_of_ne_nil {α β : Type*} [Preorder β]
[DecidableRel (fun x y : β ↦ x < y)] (l : List α) (f : α → β) (hl : l ≠ []) :
(l.argmin f).isSome := by
simp [←Option.ne_none_iff_isSome, hl]
lemma ItemList.feasible.exists_perm_mem_sublists {items : ItemList} {P : Problem}
(h : items.feasible P) : ∃ l, items ~ l ∧ l ∈ P.items.sublists := by
rw [feasible_iff] at h
contrapose! h
intro H
sorry -- fails here
theorem exist_optimal (P : Problem) : ∃ items : ItemList, items.optimal P := by
have ha := (P.items.sublists.filter (ItemList.feasible · P)).argmax_isSome_of_ne_nil
ItemList.price $ List.ne_nil_of_mem
(List.mem_filter_of_mem (by simp) (decide_eq_true (ItemList.feasible_nil _)))
refine ⟨Option.get _ ha, ?_⟩
obtain ⟨l', hl⟩ := Option.isSome_iff_exists.mp ha
obtain ⟨ha'', hl'⟩ := Option.eq_some_iff_get_eq.mp hl
rw [List.argmax_eq_some_iff] at hl
simp_rw [hl']
rw [optimal_iff]
refine ⟨?_, ?_⟩
· simpa using List.of_mem_filter hl.left
intros opp hf
obtain ⟨opp', hp, hmem⟩ := hf.exists_perm_mem_sublists
rw [opp.perm_price_eq hp]
apply hl.right.left
exact List.mem_filter_of_mem hmem (by simpa using hf.perm hp)
Adomas Baliuka (Nov 05 2023 at 18:55):
Yakov Pechersky said:
My argmax idea was wrong. Because what
feasible
is doesn't limit the number of times an Item can be duplicated. For example, I can have a problem of{ items := [(1, 1)], capacity: 100 }
. Then the optimal items are that item x 100. That's never going to be in sublists.
I think that means the definition of feasible
should be changed? Doesn't sound like the normal Knapsack problem. (Edit: never mind, it's called "unbounded knapsack problem)
Yakov Pechersky (Nov 05 2023 at 19:17):
For the unbounded one, one brute force witness would be the argmax of sublists of Problem.items repeated capacity times (because we are over N). Of course, that's too loose, one could do capacity / min weight over P.items
but the proof will probably be easier if you do with a plain repeated capacity times.
Yakov Pechersky (Nov 05 2023 at 19:17):
But even that doesn't work here, because there is nothing constraining an Item to have greater than 0 weight.
Yakov Pechersky (Nov 05 2023 at 19:17):
If I had such an item, then feasible items are unbounded.
Adomas Baliuka (Nov 05 2023 at 21:04):
Maybe we should check first if this is really the problem @Asei Inoue wants to work on. Did you mean to define an "unbounded knapsack"? The brute
algorithm you wrote would suggest otherwise.
Asei Inoue (Nov 06 2023 at 15:40):
@Adomas Baliuka
Did you mean to define an "unbounded knapsack"?
No. I thought I had defined the knapsack problem of non-reusable items.
Asei Inoue (Nov 06 2023 at 15:42):
My brute
algorithm was designed with this assumption in mind, as you said
Yakov Pechersky (Nov 06 2023 at 16:12):
Then List.subset isn't the correct way to express the constraint. I think it would be fine if you used Multiset everywhere in place of List.
Asei Inoue (Nov 08 2023 at 13:06):
It certainly doesn't seem like a good idea to have items as a list...
import Mathlib.Tactic
#guard [1, 1, 2] ⊆ [1, 2]
Asei Inoue (Nov 08 2023 at 13:09):
Is this better...?
import Mathlib.Tactic
namespace Knapsack
open List
structure Item where
/-- the weight of item -/
weight : ℕ
/-- the price of item -/
price : ℕ
deriving Repr, DecidableEq
structure Problem where
/-- the group of items -/
items : Multiset Item
/-- the capacity of the knapsack -/
capacity : ℕ
deriving DecidableEq
Yakov Pechersky (Nov 08 2023 at 14:14):
You also likely want to make items weigh a positive amount
Adomas Baliuka (Nov 09 2023 at 00:15):
I guess the ways for doing that would be to either change the type of weight
of include another field for the hypothesis. Which would you choose here @Yakov Pechersky? I think in some cases such choices can turn out to make quite a difference for how easy it is to deal with later...
Yakov Pechersky (Nov 09 2023 at 00:18):
You can do make weight a pnat, you can add a property to Item, you can make a restriction in feasible
to restrict items to ones with positive weight.
Asei Inoue (Nov 12 2023 at 02:27):
Thanks to all who have helped me so far.
I won't have time for this problem for a while ...
If anyone is interested in this problem, please feel free to work on it.
Last updated: Dec 20 2023 at 11:08 UTC