' 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?
'
' Since the plant fixed costs and the warehouse demands are uncertain, a
' scenario approach is chosen.
'
' Note that this example is similar to the facility_vb.vb example. Here we
' added scenarios in order to illustrate the multi-scenario feature.
'
' Based on an example from Frontline Systems:
' http://www.solver.com/disfacility.htm
' Used with permission.
Imports System
Imports Gurobi
Class multiscenario_vb
Shared Sub Main()
Try
' Warehouse demand in thousands of units
Dim Demand As Double() = New Double() {15, 18, 14, 20}
' Plant capacity in thousands of units
Dim Capacity As Double() = New Double() {20, 22, 17, 19, 18}
' Fixed costs for each plant
Dim FixedCosts As Double() = New Double() {12000, 15000, 17000, 13000, 16000}
' Transportation costs per thousand units
Dim TransCosts As Double(,) = New Double(,) { {4000, 2000, 3000, 2500, 4500}, _
{2500, 2600, 3400, 3000, 4000}, _
{1200, 1800, 2600, 4100, 3000}, _
{2200, 2600, 3100, 3700, 3200}}
' Number of plants and warehouses
Dim nPlants As Integer = Capacity.Length
Dim nWarehouses As Integer = Demand.Length
Dim maxFixed As Double = -GRB.INFINITY
Dim minFixed As Double = GRB.INFINITY
For p As Integer = 0 To nPlants - 1
If FixedCosts(p) > maxFixed Then maxFixed = FixedCosts(p)
If FixedCosts(p) < minFixed Then minFixed = FixedCosts(p)
Next
' Model
Dim env As GRBEnv = New GRBEnv()
Dim model As GRBModel = New GRBModel(env)
model.ModelName = "multiscenario"
' Plant open decision variables: open(p) == 1 if plant p is open.
Dim open As GRBVar() = New GRBVar(nPlants - 1) {}
For p As Integer = 0 To nPlants - 1
open(p) = model.AddVar(0, 1, FixedCosts(p), GRB.BINARY, "Open" & p)
Next
' Transportation decision variables: how much to transport from a plant
' p to a warehouse w
Dim transport As GRBVar(,) = New GRBVar(nWarehouses - 1, nPlants - 1) {}
For w As Integer = 0 To nWarehouses - 1
For p As Integer = 0 To nPlants - 1
transport(w, p) = model.AddVar(0, GRB.INFINITY, TransCosts(w, p), _
GRB.CONTINUOUS, "Trans" & p & "." & w)
Next
Next
' The objective is to minimize the total fixed and variable costs
model.ModelSense = GRB.MINIMIZE
' Production constraints
' Note that the right-hand limit sets the production to zero if
' the plant is closed
For p As Integer = 0 To nPlants - 1
Dim ptot As GRBLinExpr = 0.0
For w As Integer = 0 To nWarehouses - 1
ptot.AddTerm(1.0, transport(w, p))
Next
model.AddConstr(ptot <= Capacity(p) * open(p), "Capacity" & p)
Next
' Demand constraints
Dim demandConstr As GRBConstr() = New GRBConstr(nWarehouses - 1) {}
For w As Integer = 0 To nWarehouses - 1
Dim dtot As GRBLinExpr = 0.0
For p As Integer = 0 To nPlants - 1
dtot.AddTerm(1.0, transport(w, p))
Next
demandConstr(w) = model.AddConstr(dtot = Demand(w), "Demand" & w)
Next
' We constructed the base model, now we add 7 scenarios
'
' Scenario 0: Represents the base model, hence, no manipulations.
' Scenario 1: Manipulate the warehouses demands slightly (constraint right
' hand sides).
' Scenario 2: Double the warehouses demands (constraint right hand sides).
' Scenario 3: Manipulate the plant fixed costs (objective coefficients).
' Scenario 4: Manipulate the warehouses demands and fixed costs.
' Scenario 5: Force the plant with the largest fixed cost to stay open
' (variable bounds).
' Scenario 6: Force the plant with the smallest fixed cost to be closed
' (variable bounds).
model.NumScenarios = 7
' Scenario 0: Base model, hence, nothing to do except giving the
' scenario a name
model.Parameters.ScenarioNumber = 0
model.ScenNName = "Base model"
' Scenario 1: Increase the warehouse demands by 10%
model.Parameters.ScenarioNumber = 1
model.ScenNName = "Increased warehouse demands"
For w As Integer = 0 To nWarehouses - 1
demandConstr(w).ScenNRHS = Demand(w) * 1.1
Next
' Scenario 2: Double the warehouse demands
model.Parameters.ScenarioNumber = 2
model.ScenNName = "Double the warehouse demands"
For w As Integer = 0 To nWarehouses - 1
demandConstr(w).ScenNRHS = Demand(w) * 2.0
Next
' Scenario 3: Decrease the plant fixed costs by 5%
model.Parameters.ScenarioNumber = 3
model.ScenNName = "Decreased plant fixed costs"
For p As Integer = 0 To nPlants - 1
open(p).ScenNObj = FixedCosts(p) * 0.95
Next
' Scenario 4: Combine scenario 1 and scenario 3 */
model.Parameters.ScenarioNumber = 4
model.ScenNName = "Increased warehouse demands and decreased plant fixed costs"
For w As Integer = 0 To nWarehouses - 1
demandConstr(w).ScenNRHS = Demand(w) * 1.1
Next
For p As Integer = 0 To nPlants - 1
open(p).ScenNObj = FixedCosts(p) * 0.95
Next
' Scenario 5: Force the plant with the largest fixed cost to stay
' open
model.Parameters.ScenarioNumber = 5
model.ScenNName = "Force plant with largest fixed cost to stay open"
For p As Integer = 0 To nPlants - 1
If FixedCosts(p) = maxFixed Then
open(p).ScenNLB = 1.0
Exit For
End If
Next
' Scenario 6: Force the plant with the smallest fixed cost to be
' closed
model.Parameters.ScenarioNumber = 6
model.ScenNName = "Force plant with smallest fixed cost to be closed"
For p As Integer = 0 To nPlants - 1
If FixedCosts(p) = minFixed Then
open(p).ScenNUB = 0.0
Exit For
End If
Next
' Guess at the starting point: close the plant with the highest fixed
' costs; open all others
' First, open all plants
For p As Integer = 0 To nPlants - 1
open(p).Start = 1.0
Next
' Now close the plant with the highest fixed cost
Console.WriteLine("Initial guess:")
For p As Integer = 0 To nPlants - 1
If FixedCosts(p) = maxFixed Then
open(p).Start = 0.0
Console.WriteLine("Closing plant " & p & vbLf)
Exit For
End If
Next
' Use barrier to solve root relaxation
model.Parameters.Method = GRB.METHOD_BARRIER
' Solve multi-scenario model
model.Optimize()
Dim nScenarios As Integer = model.NumScenarios
For s As Integer = 0 To nScenarios - 1
Dim modelSense As Integer = GRB.MINIMIZE
' Set the scenario number to query the information for this scenario
model.Parameters.ScenarioNumber = s
' collect result for the scenario
Dim scenNObjBound As Double = model.ScenNObjBound
Dim scenNObjVal As Double = model.ScenNObjVal
Console.WriteLine(vbLf & vbLf & "------ Scenario " & s & " (" & model.ScenNName & ")")
' Check if we found a feasible solution for this scenario
If modelSense * scenNObjVal >= GRB.INFINITY Then
If modelSense * scenNObjBound >= GRB.INFINITY Then
' Scenario was proven to be infeasible
Console.WriteLine(vbLf & "INFEASIBLE")
Else
' We did not find any feasible solution - should not happen in
' this case, because we did not set any limit (like a time
' limit) on the optimization process
Console.WriteLine(vbLf & "NO SOLUTION")
End If
Else
Console.WriteLine(vbLf & "TOTAL COSTS: " & scenNObjVal)
Console.WriteLine("SOLUTION:")
For p As Integer = 0 To nPlants - 1
Dim scenNX As Double = open(p).ScenNX
If scenNX > 0.5 Then
Console.WriteLine("Plant " & p & " open")
For w As Integer = 0 To nWarehouses - 1
scenNX = transport(w, p).ScenNX
If scenNX > 0.0001 Then Console.WriteLine(" Transport " & scenNX & " units to warehouse " & w)
Next
Else
Console.WriteLine("Plant " & p & " closed!")
End If
Next
End If
Next
' Print a summary table: for each scenario we add a single summary line
Console.WriteLine(vbLf & vbLf & "Summary: Closed plants depending on scenario" & vbLf)
Console.WriteLine("{0,8} | {1,17} {2,13}", "", "Plant", "|")
Console.Write("{0,8} |", "Scenario")
For p As Integer = 0 To nPlants - 1
Console.Write("{0,6}", p)
Next
Console.WriteLine(" | {0,6} Name", "Costs")
For s As Integer = 0 To nScenarios - 1
Dim modelSense As Integer = GRB.MINIMIZE
' Set the scenario number to query the information for this scenario
model.Parameters.ScenarioNumber = s
' Collect result for the scenario
Dim scenNObjBound As Double = model.ScenNObjBound
Dim scenNObjVal As Double = model.ScenNObjVal
Console.Write("{0,-8} |", s)
' Check if we found a feasible solution for this scenario
If modelSense * scenNObjVal >= GRB.INFINITY Then
If modelSense * scenNObjBound >= GRB.INFINITY Then
' Scenario was proven to be infeasible
Console.WriteLine(" {0,-30}| {1,6} " & model.ScenNName, "infeasible", "-")
Else
' We did not find any feasible solution - should not happen in
' this case, because we did not set any limit (like a Time
' limit) on the optimization process
Console.WriteLine(" {0,-30}| {1,6} " & model.ScenNName, "no solution found", "-")
End If
Else
For p As Integer = 0 To nPlants - 1
Dim scenNX As Double = open(p).ScenNX
If scenNX > 0.5 Then
Console.Write("{0,6}", " ")
Else
Console.Write("{0,6}", "x")
End If
Next
Console.WriteLine(" | {0,6} " & model.ScenNName, scenNObjVal)
End If
Next
model.Dispose()
env.Dispose()
Catch e As GRBException
Console.WriteLine("Error code: " & e.ErrorCode & ". " + e.Message)
End Try
End Sub
End Class