I am trying to do a basic example of optimisation using loco.
I have a vector of costs the index of which corresponds to the integer value of a number of slots and would like to minimize the sum of the costs for a distinct subset of the slots.
Please see my attempt below, which fails to work because there is no "link" between the picked slots and the costs.
(def costs [10 10 20 20 30 30 40 40 10 10])
(let [slot-vars (for [i (range 5)] ($in [:slot i] 1 10))
cost-vars (for [i (range 10)] ($in [:cost i] 10 40))]
(solution
(concat
slot-vars
cost-vars
[($distinct (for [i (range 5)] [:slot i]))]
(for [i (range 5)]
($= [:cost i] (get costs i))))
:minimize (apply $+ (for [i (range 5)] [:slot i]))))
First, I'm not entirely sure I understand the point of this problem, because clearly the solution is to just take the 5 smallest values in the list. But if you want to make loco do it, I agree that the knapsack constraint is a handy tool for this. Contrary to what Mike said in his comment, I don't see any obstacle to using knapsack for minimization. Let the weights all be 1, and enforce that the weights add up to 5 in order to select 5 out of the 10 slots. I've used the variable [:include i]
to indicate whether slot i should be included in the subset (1 for true and 0 for false). We want to minimize the dot product of the vector of :include variables and the cost vector.
(def costs [10 10 20 20 30 30 40 40 10 10])
(def weights (repeat 10 1))
(def include-vars (for [i (range 10)] [:include i]))
(def include-constraints (for [i (range 10)] ($in [:include i] 0 1)))
(def model
(concat
include-constraints
[($knapsack weights costs include-vars 5 :total)
($in :total 0 (apply + costs))]))
(solution model :minimize :total)
The result is:
{[:include 4] 0, [:include 6] 0, [:include 9] 1, [:include 1] 1, [:include 3] 0, [:include 8] 1, :total 60, [:include 0] 1, [:include 7] 0, [:include 2] 1, [:include 5] 0}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With