In my optimization problem I have to allocate as much wagons as possible to its optimal routes for minimum cost with defined constraints (multi objective problem). At first step I have to distribute as much wagons as possible (maximum amount) and the second step of optimization is to allocate its number to the cheapest routes. I have the number of wagons from each station to distribute and I have the capacity at each station of arrival. For simplicity, I have only two stations of departure where rem_type column means the type of wagon at this station. Table df_vrp contains information about the capacity and table df_unload contains information about the number of wagons at each departure station which we have to allocate to routes.It is clear for me how to the maximum possible number of wagons from station to station in regards with constraints and then find the cheapest routes. But, in my case, I have to take in account the proportion of distributes wagons such that the sum of distriubuted wagons of Type 2 must be equal to 1/3 of the sum of distributed wagons of Type 1. So I cannot understand how to create such constraint because 1/3 can be done in many ways (1 wagon of Type 2 and 3 wagons of Type 1, 3 wagons of Type 2 and 9 wagons of Type 1 and so on).
import pandas as pd
import numpy as np
from pyomo.environ import *
data = {'rem_type' : ['Type1', 'Type1', 'Type2', 'Type2', 'Type1', 'Type1', 'Type2', 'Type2'],
'station_depart': ['A', 'A', 'A', 'A', 'F', 'F', 'F', 'F'],
'station_arrive': ['B', 'C', 'B', 'C', 'B', 'C', 'B', 'C'],
'cost': [100, 103, 111, 101, 105, 114, 95, 99]}
df_route = pd.DataFrame(data)
# unload
df_unload = pd.DataFrame({'rem_type' : ['Type1', 'Type2', 'Type1', 'Type2'],
'station_depart' : ['A', 'A', 'F', 'F'],
'volume' : [5, 6, 4, 7]})
# repair
df_vrp = pd.DataFrame({'rem_type' : ['Type1', 'Type2'],
'station_arrive' : ['B', 'C'],
'capacity' : [15, 30]})
# create routes
routes_unload = dict()
routes_vrp = dict()
# create model
model = ConcreteModel("OP")
# create multi objective function
model.Models = ObjectiveList()
# decision vars
model.x = Var([idx for idx in df_route.index], domain=NonNegativeIntegers)
# obj function to maximize the number of allocated wags to routes
model.Size = Objective(expr=sum([model.x[i] for i in model.x]), sense=maximize)
# obj function to minimize cost of those wags from the first solution
model.Cost = Objective(
expr=sum([df_route.loc[idx, "cost"] * model.x[idx] for idx in df_route.index]),
sense=minimize,
)
model.Size.activate()
model.Cost.deactivate()
# routes with indexes for creating constraints
for idx in tqdm(df_route.index):
vrp = (df_route.loc[idx, "rem_type"], df_route.loc[idx, "station_arrive"])
if vrp not in routes_vrp:
routes_vrp[vrp] = list()
routes_vrp[vrp].append(idx)
t_unload = (df_route.loc[idx, "rem_type"], df_route.loc[idx, "station_depart"])
if t_unload not in routes_unload:
routes_unload[t_unload] = list()
routes_unload[t_unload].append(idx)
# constraints on the arrive/repair station
model.vrp = ConstraintList()
for v in df_vrp.index:
t = (df_vrp.loc[v, "rem_type"], df_vrp.loc[v, "station_arrive"])
if t in routes_vrp:
model.vrp.add(
sum([model.x[idx] for idx in routes_vrp[t]]) <= df_vrp.loc[v, "capacity"]
)
# constraints on the depart station (how many wagons we have for allocation)
model.unload = ConstraintList()
for u in df_unload.index:
t = (df_unload.loc[u, "rem_type"], df_unload.loc[u, "station_depart"])
if t in routes_unload:
model.unload.add(
sum([model.x[idx] for idx in routes_unload[t]]) <= df_unload.loc[u, "volume"]
)
results = SolverFactory("glpk").solve(model)
results.write()
# the result from the first objective function as the constraint to the second one
model.fix_count = Constraint(expr=sum([model.x[i] for i in model.x]) == model.Size())
model.Size.deactivate()
model.Cost.activate()
results = SolverFactory("glpk").solve(model)
results.write()
# create df with results of allocation to the routes
vals = []
for i in tqdm(model.x):
vals.append(model.x[i].value)
df_results = df_route.dropna(
subset=["rem_type", "station_depart", "station_arrive", "cost"],
how="all",
)
df_results["total"] = vals