mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
98 lines
3.3 KiB
Python
98 lines
3.3 KiB
Python
from mip import Model, xsum, maximize, BINARY
|
|
|
|
# Example parts
|
|
wall = [
|
|
{"id": 1, "cost": 2000, "gain": 5, "type": "wall"},
|
|
{"id": 2, "cost": 2300, "gain": 6, "type": "wall"}
|
|
]
|
|
|
|
floor = [
|
|
{"id": 1, "cost": 1500, "gain": 3, "type": "floor"},
|
|
{"id": 2, "cost": 1600, "gain": 3.1, "type": "floor"}
|
|
]
|
|
|
|
roof = [
|
|
{"id": 1, "cost": 1000, "gain": 2, "type": "roof"},
|
|
{"id": 2, "cost": 1100, "gain": 2.3, "type": "roof"}
|
|
]
|
|
|
|
# To solve this, we are solving a constrained Knapsack problem
|
|
# Maximize sum(gain_g . x_g) for g in groups
|
|
# subject to sum(cost_g . x_g) <= C
|
|
# subject to sum(x_g) <= 1 for g in groups
|
|
# x_g in {0, 1} for g in groups
|
|
#
|
|
# The first sum, which is the objective of the optimisation provlem, ensures that we are maximising the gain
|
|
# for the selected parts
|
|
# The second sum (and the first constraint) ensures that the cost of the selected parts is less than or equal to C
|
|
# The third sum (and the second constraint) ensures that at most one part from each group is selected
|
|
# The last constraint ensures that the decision variables are binary
|
|
|
|
C = 4000
|
|
|
|
# group all the parts
|
|
groups = [wall, floor, roof]
|
|
|
|
|
|
class GainOptimiser:
|
|
"""
|
|
This class is used maximise gain, given a constrained cost
|
|
"""
|
|
|
|
def __init__(self, components):
|
|
self.components = components
|
|
self.m = None
|
|
self.variables = []
|
|
self.solution = []
|
|
|
|
self.solution_gain = None
|
|
self.solution_cost = None
|
|
|
|
def setup(self):
|
|
# Initialize Model
|
|
self.m = Model("knapsack")
|
|
|
|
# Create variables
|
|
self.variables = [
|
|
[self.m.add_var(var_type=BINARY, name=str(component["id"])) for component in group] for group in groups
|
|
]
|
|
|
|
# Set objective
|
|
# This objective is the sum
|
|
# gain_ig * x_ig, where gain_ig represents the gain for ith part in group g
|
|
# and x_ig is the binary decision variable for the ith part in group g
|
|
self.m.objective = maximize(
|
|
xsum(
|
|
component['gain'] * var for group, group_vars in zip(groups, self.variables) for component, var in
|
|
zip(group, group_vars)
|
|
)
|
|
)
|
|
|
|
# Add constraints
|
|
# This constrain ensures that sum of cost_ig * x_ig <= C, where cost_ig represents the cost for the ith
|
|
# component
|
|
# in group g, and x_ig is the binary decision variable for the ith component in group g
|
|
self.m += xsum(
|
|
item['cost'] * var for group, group_vars in zip(groups, self.variables) for item, var in
|
|
zip(group, group_vars)
|
|
) <= C
|
|
|
|
# At most one item from each group
|
|
# This constraint ensures that at most one item from each group is selected
|
|
# This is expressed by summing up the decision variables for each group and ensuring that the sum is <= 1
|
|
for group_vars in self.variables:
|
|
self.m += xsum(var for var in group_vars) <= 1
|
|
|
|
def solve(self):
|
|
# Solve the problem
|
|
self.m.optimize()
|
|
|
|
self.solution = [
|
|
item for group, group_vars in zip(groups, self.variables) for item, var in zip(group, group_vars) if
|
|
var.x >= 0.99
|
|
]
|
|
|
|
# Get the selected items
|
|
|
|
solution_gain = self.m.objective.x
|
|
solution_cost = sum([component['cost'] for component in self.solution])
|