irt-service/app/services/solver_sandbox.py
2022-03-11 15:26:42 +00:00

221 lines
7.9 KiB
Python

# Local Dev Sandbox for the solver
# Useful for testing some concepts and functionality
# and offers a much faster feedback loop than the usual end-to-end process in Local Dev
#
# How to use:
# 1. run `compose exec meazure-solver bash`
# 2. run `python`
# 3. import this file in the python repl by `from services.solver_sandbox import SolverSandbox`
# 4. run any of the methds below e.g. `SolverSandbox.yas_elastic()`
import logging
from pulp import LpProblem, LpVariable, LpInteger, LpMinimize, LpMaximize, LpAffineExpression, LpConstraint, LpStatus, lpSum
from services.loft_service import LoftService
class SolverSandbox:
def loft_service(body):
LoftService(body).process()
def yosh_loop():
Items = [1,2,3,4,5]
tif = {
1: 0.2,
2: 0.5,
3: 0.3,
4: 0.8,
5: 0.1
}
iif = {
1: 0.09,
2: 0.2,
3: 0.113,
4: 0.3,
5: 0.1
}
drift = 0.0
drift_limit = 0.2
iif_target = 0.5
tif_target = 0.9
item_vars = LpVariable.dicts("Item", Items, cat="Binary")
while drift <= drift_limit:
prob = LpProblem("tif_tcc_test", LpMinimize)
prob += lpSum([(tif[i] + iif[i]) * item_vars[i] for i in Items]), "TifTccSum"
prob += lpSum([item_vars[i] for i in Items]) == 3, "TotalItems"
prob += lpSum([tif[i] * item_vars[i] for i in Items]) >= tif_target - (tif_target * drift), 'TifMin'
prob += lpSum([tif[i] * item_vars[i] for i in Items]) <= tif_target + (tif_target * drift), 'TifMax'
prob += lpSum([iif[i] * item_vars[i] for i in Items]) >= iif_target - (iif_target * drift), 'TccMin'
prob += lpSum([iif[i] * item_vars[i] for i in Items]) <= iif_target + (iif_target * drift), 'TccMax'
prob.solve()
print(prob)
if LpStatus[prob.status] == "Infeasible":
print('attempt infeasible')
for v in prob.variables():
print(v.name, "=", v.varValue)
drift += 0.02
else:
print(f"solution found with drift of {drift}!")
for v in prob.variables():
print(v.name, "=", v.varValue)
break
def yas_elastic(tif_target = 140.0): # 140 is the optimal
Items = [1,2,3,4,5]
# For TIF target
tif = {
1: 10,
2: 20,
3: 40,
4: 60,
5: 80
}
iif = {
1: 10,
2: 20,
3: 30,
4: 50,
5: 70
}
# ---
items = LpVariable.dicts('Item', Items, cat='Binary')
drift = 0
max_drift = 10 # 10% elasticity
while drift <= max_drift:
drift_percent = drift / 100
problem = LpProblem('TIF_TCC', LpMinimize)
# objective function
problem += lpSum([(tif[i] + iif[i]) * items[i] for i in Items])
# Constraint 1
problem += lpSum([items[i] for i in Items]) == 3, 'TotalItems'
print(f"Calculating TIF target of {tif_target} with drift of {drift}%")
# Our own "Elastic Constraints"
problem += lpSum(
[(tif[i] + iif[i]) * items[i] for i in Items]
) >= tif_target - (tif_target * drift_percent), 'TifIifMin'
problem += lpSum(
[(tif[i] + iif[i]) * items[i] for i in Items]
) <= tif_target + (tif_target * drift_percent), 'TifIifMax'
problem.solve()
if LpStatus[problem.status] == 'Infeasible':
print(f"attempt infeasible for drift of {drift}")
for v in problem.variables():
print(v.name, "=", v.varValue)
print(problem.objective.value())
print(problem.constraints)
print(problem.objective)
drift += 1
else:
print(f"solution found with drift of {drift}!")
for v in problem.variables():
print(v.name, "=", v.varValue)
print(problem.constraints)
print(problem.objective)
break
# Implementation of the Whiskas Cat problem, with elastic constraints
# https://www.coin-or.org/PuLP/CaseStudies/a_blending_problem.html
# https://stackoverflow.com/questions/27278691/how-can-an-elastic-subproblem-in-pulp-be-used-as-a-constraint?noredirect=1&lq=1
def whiskas():
# Creates a list of the Ingredients
Ingredients = ['CHICKEN', 'BEEF', 'MUTTON', 'RICE', 'WHEAT', 'GEL']
# A dictionary of the costs of each of the Ingredients is created
costs = {'CHICKEN': 0.013,
'BEEF': 0.008,
'MUTTON': 0.010,
'RICE': 0.002,
'WHEAT': 0.005,
'GEL': 0.001}
# A dictionary of the protein percent in each of the Ingredients is created
proteinPercent = {'CHICKEN': 0.100,
'BEEF': 0.200,
'MUTTON': 0.150,
'RICE': 0.000,
'WHEAT': 0.040,
'GEL': 0.000}
# A dictionary of the fat percent in each of the Ingredients is created
fatPercent = {'CHICKEN': 0.080,
'BEEF': 0.100,
'MUTTON': 0.110,
'RICE': 0.010,
'WHEAT': 0.010,
'GEL': 0.000}
# A dictionary of the fibre percent in each of the Ingredients is created
fibrePercent = {'CHICKEN': 0.001,
'BEEF': 0.005,
'MUTTON': 0.003,
'RICE': 0.100,
'WHEAT': 0.150,
'GEL': 0.000}
# A dictionary of the salt percent in each of the Ingredients is created
saltPercent = {'CHICKEN': 0.002,
'BEEF': 0.005,
'MUTTON': 0.007,
'RICE': 0.002,
'WHEAT': 0.008,
'GEL': 0.000}
logging.info('Running Test...')
# create problem
problem = LpProblem("The Whiskas Problem", LpMinimize)
# A dictionary called 'ingredient_vars' is created to contain the referenced Variables
ingredient_vars = LpVariable.dicts("Ingr", Ingredients, 0)
# set objective
problem += lpSum([costs[i]*ingredient_vars[i] for i in Ingredients]), "Total Cost of Ingredients per can"
# The five constraints are added to 'prob'
problem += lpSum([ingredient_vars[i] for i in Ingredients]) == 100, "PercentagesSum"
problem += lpSum([proteinPercent[i] * ingredient_vars[i] for i in Ingredients]) >= 8.0, "ProteinRequirement"
problem += lpSum([fatPercent[i] * ingredient_vars[i] for i in Ingredients]) >= 6.0, "FatRequirement"
problem += lpSum([fibrePercent[i] * ingredient_vars[i] for i in Ingredients]) <= 2.0, "FibreRequirement"
problem += lpSum([saltPercent[i] * ingredient_vars[i] for i in Ingredients]) <= 0.4, "SaltRequirement"
# ELASTICIZE
# c6_LHS_A = LpAffineExpression([ingredient_vars])
c6_LHS = LpAffineExpression([(ingredient_vars['GEL'],1), (ingredient_vars['BEEF'],1)])
c6= LpConstraint(e=c6_LHS, sense=-1, name='GelBeefTotal', rhs=30)
c6_elastic = c6.makeElasticSubProblem(penalty = 100, proportionFreeBound = .10)
problem.extend(c6_elastic)
print(problem)
# solve problem
problem.solve()
# The status of the solution is printed to the screen
print("Status:", LpStatus[problem.status])
# Each of the variables is printed with it's resolved optimum value
for v in problem.variables():
print(v.name, "=", v.varValue)
# The optimised objective function value is printed to the screen
print("Total Cost of Ingredients per can = ", problem.objective.value())