multiscenario_vb.vb#

' 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