问题
I am trying to solve this minimization problem with Pulp.
We have packets of mangoes each having a category, age, and count of mangoes in it.
# Packet Names and the count, category and age of mangoes in each packet.
mango_packs = {
"pack_1": {
"count": 5,
"category": "pack",
"age": 10
},
"pack_2": {
"count": 9,
"category": "pack",
"age": 10
},
"bag_2": {
"count": 5,
"category": "bag",
"age": 20
},
"sack_1": {
"count": 5,
"category": "sack",
"age": 5
},
}
We will have demand for a number of mangoes. (Which will be a whole number). Our objective is to serve the packets in a way that there are fewer remnants of mangoes. For example, if the demand is 10, we serve the packet with 10 mangoes. If not, serve 2 packets with 5 mangoes each. If we have only 3 packets with 5, 7 and 6 mangoes each, we serve packets with 5 and 6 so we have only 1 remnant. The below code is working exactly what we need here.
For demand 15, it will give the following output.
Status: Optimal
OpenPack_bag_2? yes
OpenPack_pack_1? yes
OpenPack_pack_2? no
OpenPack_sack_1? yes
import pulp
def optimise(mango_packs, mango_count):
pack_names = list(mango_packs.keys())
prob = pulp.LpProblem("MangoPacks", pulp.LpMinimize)
# variables: names of the mango packs. We can either open them or not (0/1)
lp_pack_vars = pulp.LpVariable.dicts("OpenPack", pack_names, 0, 1, "Integer")
# objective: minimise total count of mangoes in the selected packs (so to
# minimise remnants). In case of a tie, minimise the number of opened packs.
prob += (
pulp.lpSum([mango_packs[name]["count"] * lp_pack_vars[name]
for name in pack_names]) * len(mango_packs) + pulp.lpSum(
[lp_pack_vars[name] for name in pack_names]))
# constraint 1: the opened packs need to amount to a minimum number of mangos
prob += pulp.lpSum(
[mango_packs[name]["count"] * lp_pack_vars[name]
for name in pack_names]) >= mango_count
# Packets should not be from more than 2 category.
# prob += len(set([mango_packs[name]["category"] for name in pack_names if lp_pack_vars[name] == 1 ])) <= 1
prob.solve()
print("Status:", pulp.LpStatus[prob.status])
# Each of the variables is printed with it's resolved optimum value
for i, v in enumerate(prob.variables()):
print("{}? {}".format(v.name, ("no", "yes")[int(v.varValue)]))
# Packet Names and the count of mangoes in each packet.
mango_packs = {
"pack_1": {
"count": 5,
"category": "pack",
"age": 10
},
"pack_2": {
"count": 9,
"category": "pack",
"age": 10
},
"bag_2": {
"count": 5,
"category": "bag",
"age": 20
},
"sack_1": {
"count": 5,
"category": "sack",
"age": 5
},
}
optimise(mango_packs, 15)
But we have 2 more constrains now:
- Packets picked should not be from more than 2 categories.
- The age difference between any 2 packets picked should not be greater than 25
I added below constraint to restrict the number of categories. Which is basically counting the unique categories and making sure it should be less than or equal to 2.
prob += len(set([mango_packs[name]["category"] for name in pack_names if lp_pack_vars[name] == 1 ])) <= 2
But this is not working, while doing this it's giving a stage output as open all mangoes and then an exception.
Status: Optimal
OpenPack_bag_2? yes
OpenPack_pack_1? yes
OpenPack_pack_2? yes
OpenPack_sack_1? yes Traceback (most recent call last):
File "main.py", line 61, in
optimise(mango_packs, 15)
File "main.py", line 34, in optimise
print("{}? {}".format(v.name, ("no", "yes")[int(v.varValue)]))
TypeError: int() argument must be a string, a bytes-like object or a number, not 'NoneType'
Looks like the if condition inside the generator expression is always returning True for some reason. Not very sure.
How to fix this problem? We need to add our new 2 constrains.
The working code without the 2 new constrains can be run from here: https://repl.it/@VinodM1/OptimumMangoes
Edited: Same Data represented differently here if it helps someone trying for a solution, as I believe we need to restructure the model to find a solution.
mango_packs_cat = {
"cat_pack": {
"packets": {
"pack_1": {
"name": "pack_1",
"count": 5
},
"pack_2": {
"name": "pack_2",
"count": 9
}
},
"age": 10
},
"cat_bag": {
"packets":{
"bag_1":{
"name": "bag_1",
"count": 5
}
},
"age": 20
},
"cat_sack": {
"packets":{
"sack_1":{
"name": "sack_1",
"count": 5
}
},
"age": 5
}
}
回答1:
I don't think you can sum the size of a set and present that to a solver as a constraint...so that is probably the issue with that statement.
You need to reformulate your model a bit and add another indexed variable for a switching constraint. The switching constraint should be a binary value, indexed by the different categories.
You should enable the variable with something like: (pseudocode)..
x[pack, cat] <= u[pack, cat]*y[cat]
Where x[pack, cat] is the amount of mangoes selected of a category in a pack, u[cat] is the max available in that pack of that category (or just the max of that category overall) and y[cat] is binary if that category is chosen.
Then limit the number of categories (pseudocode)..
sum(y[cat]) <= 2
For the age... you should be thinking about a duplicate index into your packs so that you can compare all differences....
来源:https://stackoverflow.com/questions/62975878/generator-expressions-not-working-as-expected-in-linear-programming-constraints