Quadratic Assignment Problem: Optimization with Gurobi Solver + Python + Rstudio
This is the simple case study of optimal resource re-distribution with two products of ‘ATMCashMachines’ and ‘BranchOffices’ to Finland, Sweden, Norway, Denmark and Germany for a multinational Commerical Bank.
- The main task is to estimate the optimal numbers of ATM Cash Machines and Branch Offices operating in Finland, Sweden, Norway, Denmark and Germany.
- The objective function is to minimize the sum of the operating costs (‘cost[i,j]’) based on the capacity constraints (‘capacity[i,j]’) among 5 countries.
- The inflow[h,i] represents the demand inflow from country h to coutnry j.
1 Base data
nodes = ['Finland', 'Sweden', 'Norway', 'Denmark', 'Germany']
commodities = ['ATMCashMachines', 'BranchOffices']
arcs, capacity = gp.multidict({
('Finland', 'Norway'): 500,
('Finland', 'Denmark'): 580,
('Finland', 'Germany'): 620,
('Sweden', 'Norway'): 720,
('Sweden', 'Denmark'): 820,
('Sweden', 'Germany'): 920})
2 Operation cost for triplets commodity-source-destination
cost = {
('ATMCashMachines', 'Finland', 'Norway'): 50,
('ATMCashMachines', 'Finland', 'Denmark'): 20,
('ATMCashMachines', 'Finland', 'Germany'): 10,
('ATMCashMachines', 'Sweden', 'Norway'): 40,
('ATMCashMachines', 'Sweden', 'Denmark'): 40,
('ATMCashMachines', 'Sweden', 'Germany'): 30,
('BranchOffices', 'Finland', 'Norway'): 20,
('BranchOffices', 'Finland', 'Denmark'): 40,
('BranchOffices', 'Finland', 'Germany'): 80,
('BranchOffices', 'Sweden', 'Norway'): 60,
('BranchOffices', 'Sweden', 'Denmark'): 30,
('BranchOffices', 'Sweden', 'Germany'): 30}
3 Demand pairs of commodity-country
inflow = {
('ATMCashMachines', 'Finland'): 50,
('ATMCashMachines', 'Sweden'): 60,
('ATMCashMachines', 'Norway'): -50,
('ATMCashMachines', 'Denmark'): -50,
('ATMCashMachines', 'Germany'): -10,
('BranchOffices', 'Finland'): 60,
('BranchOffices', 'Sweden'): 40,
('BranchOffices', 'Norway'): -40,
('BranchOffices', 'Denmark'): -30,
('BranchOffices', 'Germany'): -30}
4 Create optimization model
Using license file C:\Users\Heatfront\gurobi.lic
Academic license - for non-commercial use only
5 Create variables
6 Arc-capacity constraints
{('Finland', 'Norway'): <gurobi.Constr *Awaiting Model Update*>, ('Finland', 'Denmark'): <gurobi.Constr *Awaiting Model Update*>, ('Finland', 'Germany'): <gurobi.Constr *Awaiting Model Update*>, ('Sweden', 'Norway'): <gurobi.Constr *Awaiting Model Update*>, ('Sweden', 'Denmark'): <gurobi.Constr *Awaiting Model Update*>, ('Sweden', 'Germany'): <gurobi.Constr *Awaiting Model Update*>}
7 Model optimization
m.addConstrs(
(flow.sum(h, '*', j) + inflow[h, j] == flow.sum(h, j, '*')
for h in commodities for j in nodes), "node")
{('ATMCashMachines', 'Finland'): <gurobi.Constr *Awaiting Model Update*>, ('ATMCashMachines', 'Sweden'): <gurobi.Constr *Awaiting Model Update*>, ('ATMCashMachines', 'Norway'): <gurobi.Constr *Awaiting Model Update*>, ('ATMCashMachines', 'Denmark'): <gurobi.Constr *Awaiting Model Update*>, ('ATMCashMachines', 'Germany'): <gurobi.Constr *Awaiting Model Update*>, ('BranchOffices', 'Finland'): <gurobi.Constr *Awaiting Model Update*>, ('BranchOffices', 'Sweden'): <gurobi.Constr *Awaiting Model Update*>, ('BranchOffices', 'Norway'): <gurobi.Constr *Awaiting Model Update*>, ('BranchOffices', 'Denmark'): <gurobi.Constr *Awaiting Model Update*>, ('BranchOffices', 'Germany'): <gurobi.Constr *Awaiting Model Update*>}
Gurobi Optimizer version 9.0.0 build v9.0.0rc2 (win64)
Optimize a model with 16 rows, 12 columns and 36 nonzeros
Model fingerprint: 0xf3ddfcd8
Coefficient statistics:
Matrix range [1e+00, 1e+00]
Objective range [1e+01, 8e+01]
Bounds range [0e+00, 0e+00]
RHS range [1e+01, 9e+02]
Presolve removed 15 rows and 9 columns
Presolve time: 0.01s
Presolved: 1 rows, 3 columns, 3 nonzeros
Iteration Objective Primal Inf. Dual Inf. Time
0 5.7000000e+03 2.500000e+00 0.000000e+00 0s
1 6.1000000e+03 0.000000e+00 0.000000e+00 0s
Solved in 1 iterations and 0.02 seconds
Optimal objective 6.100000000e+03
8 Print solution
if m.status == GRB.OPTIMAL:
solution = m.getAttr('x', flow)
for h in commodities:
print('\nOptimal flows for %s:' % h)
for i, j in arcs:
if solution[h, i, j] > 0:
print('%s -> %s: %g' % (i, j, solution[h, i, j]))
Optimal flows for ATMCashMachines:
Finland -> Denmark: 50
Sweden -> Norway: 50
Sweden -> Germany: 10
Optimal flows for BranchOffices:
Finland -> Norway: 40
Finland -> Denmark: 20
Sweden -> Denmark: 10
Sweden -> Germany: 30