Add a solver sandbox for local dev purposes
This commit is contained in:
parent
0b41c341b7
commit
7bfafe6b67
@ -108,9 +108,11 @@ class LoftService(Base):
|
||||
] + [
|
||||
item.iif(self.solver_run, target.theta) * items[item.id]
|
||||
for item in self.solver_run.items
|
||||
])
|
||||
]) <= target.value, f'min tif theta {target.theta} target for form {form_number}'
|
||||
|
||||
problem += tif
|
||||
problem_objective_functions.append(tif)
|
||||
|
||||
e = LpAffineExpression(
|
||||
[(bundles[bundle.id],
|
||||
bundle.tif(self.solver_run.irt_model, target.theta))
|
||||
@ -133,7 +135,9 @@ class LoftService(Base):
|
||||
] + [
|
||||
item.irf(self.solver_run, target.theta) * items[item.id]
|
||||
for item in self.solver_run.items
|
||||
])
|
||||
]) <= target.value, f'min tcc theta {target.theta} target for form {form_number}'
|
||||
|
||||
problem += tcc
|
||||
problem_objective_functions.append(tcc)
|
||||
|
||||
e = LpAffineExpression(
|
||||
|
293
app/services/solver_sandbox.py
Normal file
293
app/services/solver_sandbox.py
Normal file
@ -0,0 +1,293 @@
|
||||
# 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 = {'Records': [{'eventVersion': '2.1', 'eventSource': 'aws:s3', 'awsRegion': 'us-east-1', 'eventTime': '2022-03-09T14:40:04.115Z', 'eventName': 'ObjectCreated:Put', 'userIdentity': {'principalId': 'AIDAJDPLRKLG7UEXAMPLE'}, 'requestParameters': {'sourceIPAddress': '127.0.0.1'}, 'responseElements': {'x-amz-request-id': '4629a38d', 'x-amz-id-2': 'eftixk72aD6Ap51TnqcoF8eFidJG9Z/2'}, 's3': {'s3SchemaVersion': '1.0', 'configurationId': 'testConfigRule', 'bucket': {'name': 'measure-local-solver-ingest', 'ownerIdentity': {'principalId': 'A3NL1KOZZKExample'}, 'arn': 'arn:aws:s3:::measure-local-solver-ingest'}, 'object': {'key': 'baf511b0-81e4-013a-6e98-0242ac120010_solver_run.tar.gz', 'size': 509, 'eTag': '"4c0911a335c6feca5493d63b58654e3a"', 'versionId': None, 'sequencer': '0055AED6DCD90281E5'}}}]}
|
||||
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_targets = [50.0], tcc_targets = [50.0]):
|
||||
Bundles = [1,2,3,4,5]
|
||||
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
|
||||
}
|
||||
# ---
|
||||
|
||||
# For TCC target
|
||||
trf = {
|
||||
1: 100,
|
||||
2: 200,
|
||||
3: 400,
|
||||
4: 600,
|
||||
5: 800
|
||||
}
|
||||
|
||||
irf = {
|
||||
1: 100,
|
||||
2: 200,
|
||||
3: 300,
|
||||
4: 500,
|
||||
5: 700
|
||||
}
|
||||
# ---
|
||||
|
||||
total_forms = 2
|
||||
|
||||
items = LpVariable.dicts('Item', Items, cat='Binary')
|
||||
bundles = LpVariable.dicts('Bundle', Bundles, cat='Binary')
|
||||
|
||||
for form in range(total_forms):
|
||||
drift = 0
|
||||
max_drift = 10 # 10% elasticity
|
||||
|
||||
while drift <= max_drift:
|
||||
drift_percent = drift / 100
|
||||
problem = LpProblem('TIF_TCC', LpMinimize)
|
||||
|
||||
# objective function
|
||||
problem += lpSum(
|
||||
[
|
||||
(iif[i] + irf[i]) * items[i] for i in Items
|
||||
] +
|
||||
[
|
||||
(tif[b] + trf[b]) * bundles[b] for b in Bundles
|
||||
]
|
||||
), 'TIF_TCC_Sum'
|
||||
|
||||
# Constraint 1
|
||||
problem += lpSum(
|
||||
[
|
||||
items[i] for i in Items
|
||||
] +
|
||||
[
|
||||
bundles[b] for b in Bundles
|
||||
]
|
||||
) == 3, 'TotalItems'
|
||||
|
||||
for tif_target in tif_targets:
|
||||
print(f"Calculating TIF target of {tif_target} with drift of {drift} for Form {form + 1}")
|
||||
|
||||
# Our own "Elastic Constraints"
|
||||
problem += lpSum(
|
||||
[
|
||||
tif[b] * bundles[b] for b in Bundles
|
||||
] +
|
||||
[
|
||||
iif[i] * items[i] for i in Items
|
||||
]
|
||||
) >= tif_target - (tif_target * drift_percent), 'TifIifMin'
|
||||
|
||||
problem += lpSum(
|
||||
[
|
||||
tif[b] * bundles[b] for b in Bundles
|
||||
] +
|
||||
[
|
||||
iif[i] * items[i] for i in Items
|
||||
]
|
||||
) <= tif_target + (tif_target * drift_percent), 'TifIifMax'
|
||||
|
||||
for tcc_target in tcc_targets:
|
||||
print(f"Calculating TCC target of {tcc_target} with drift of {drift} for Form {form + 1}")
|
||||
|
||||
# Our own "Elastic Constraints"
|
||||
problem += lpSum(
|
||||
[
|
||||
trf[b] * bundles[b] for b in Bundles
|
||||
] +
|
||||
[
|
||||
irf[i] * items[i] for i in Items
|
||||
]
|
||||
) >= tcc_target - (tcc_target * drift_percent), 'TrfIrffMin'
|
||||
|
||||
problem += lpSum(
|
||||
[
|
||||
trf[b] * bundles[b] for b in Bundles
|
||||
] +
|
||||
[
|
||||
irf[i] * items[i] for i in Items
|
||||
]
|
||||
) <= tcc_target + (tcc_target * drift_percent), 'TrfIrfMax'
|
||||
|
||||
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.constraints)
|
||||
print(problem.objective)
|
||||
|
||||
drift += 1
|
||||
|
||||
# breakpoint() if drift == 10:
|
||||
|
||||
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())
|
Loading…
x
Reference in New Issue
Block a user