function facility()
% Copyright 2024, Gurobi Optimization, LLC
%
% Facility location: a company currently ships its product from 5 plants
% to 4 warehouses. It is considering closing some plants to reduce
% costs. What plant(s) should the company close, in order to minimize
% transportation and fixed costs?
%
% Note that this example uses lists instead of dictionaries. Since
% it does not work with sparse data, lists are a reasonable option.
%
% Based on an example from Frontline Systems:
% http://www.solver.com/disfacility.htm
% Used with permission.
% define primitive data
nPlants = 5;
nWarehouses = 4;
% Warehouse demand in thousands of units
Demand = [15; 18; 14; 20];
% Plant capacity in thousands of units
Capacity = [20; 22; 17; 19; 18];
% Fixed costs for each plant
FixedCosts = [12000; 15000; 17000; 13000; 16000];
% Transportation costs per thousand units
TransCosts = [
4000; 2000; 3000; 2500; 4500;
2500; 2600; 3400; 3000; 4000;
1200; 1800; 2600; 4100; 3000;
2200; 2600; 3100; 3700; 3200];
% Index helper function
flowidx = @(w, p) nPlants * w + p;
% Build model
model.modelname = 'facility';
model.modelsense = 'min';
% Set data for variables
ncol = nPlants + nPlants * nWarehouses;
model.lb = zeros(ncol, 1);
model.ub = [ones(nPlants, 1); inf(nPlants * nWarehouses, 1)];
model.obj = [FixedCosts; TransCosts];
model.vtype = [repmat('B', nPlants, 1); repmat('C', nPlants * nWarehouses, 1)];
for p = 1:nPlants
model.varnames{p} = sprintf('Open%d', p);
end
for w = 1:nWarehouses
for p = 1:nPlants
v = flowidx(w, p);
model.varnames{v} = sprintf('Trans%d,%d', w, p);
end
end
% Set data for constraints and matrix
nrow = nPlants + nWarehouses;
model.A = sparse(nrow, ncol);
model.rhs = [zeros(nPlants, 1); Demand];
model.sense = [repmat('<', nPlants, 1); repmat('=', nWarehouses, 1)];
% Production constraints
for p = 1:nPlants
for w = 1:nWarehouses
model.A(p, p) = -Capacity(p);
model.A(p, flowidx(w, p)) = 1.0;
end
model.constrnames{p} = sprintf('Capacity%d', p);
end
% Demand constraints
for w = 1:nWarehouses
for p = 1:nPlants
model.A(nPlants+w, flowidx(w, p)) = 1.0;
end
model.constrnames{nPlants+w} = sprintf('Demand%d', w);
end
% Save model
gurobi_write(model,'facility_m.lp');
% Guess at the starting point: close the plant with the highest fixed
% costs; open all others first open all plants
model.start = [ones(nPlants, 1); inf(nPlants * nWarehouses, 1)];
[~, idx] = max(FixedCosts);
model.start(idx) = 0;
% Set parameters
params.method = 2;
% Optimize
res = gurobi(model, params);
% Print solution
if strcmp(res.status, 'OPTIMAL')
fprintf('\nTotal Costs: %g\n', res.objval);
fprintf('solution:\n');
for p = 1:nPlants
if res.x(p) > 0.99
fprintf('Plant %d open:\n', p);
end
for w = 1:nWarehouses
if res.x(flowidx(w, p)) > 0.0001
fprintf(' Transport %g units to warehouse %d\n', res.x(flowidx(w, p)), w);
end
end
end
else
fprintf('\n No solution\n');
end
end