How to: Use Stochastic Programming to Solve Two-Stage Linear Models

Solver Foundation 3.0

You can use linear programming to minimize or maximize functions, and also account for fluctuating demand by using random parameters and recourse decisions. In this example, an oil refinery must procure crude oil from two sources. The objective is to minimize the purchase cost of crude oils of varying quality and to meet minimum production levels of 2,000 barrels of gasoline, 1,500 barrels of jet fuel, and 500 barrels of machine lubricant. Meanwhile, the suppliers cannot exceed their maximum daily production of crude oil. If demand cannot be met, the refinery must purchase pre-refined products at these prices: $38.40 for gas, $35.20 for jet fuel, and $28.80 for lubricant. The following table shows the costs and capabilities of the two different crude oils.


Saudi Arabia refining

Venezuela refining

Cost of crude oil

$20 per barrel

$15 per barrel

Maximum daily production

9,000 barrels

6,000 barrels

Refining percentages

30% gasoline

40% jet fuel

20% lubricant

10% waste

40% gasoline

20% jet fuel

30% lubricant

10% waste

The following example uses Solver Foundation to create, solve, and adjust the refining model to account for demand. This example demonstrates how to use the Solver Foundation Services layer.

To use stochastic programming and Solver Foundation Services to account for demand

  1. Create a console application named PetroChemStoch.

  2. Add a reference to Microsoft Solver Foundation on the .NET tab.

  3. Add the following Imports or using statements to the top of the Program code file.

    Imports Microsoft.SolverFoundation.Common
    Imports Microsoft.SolverFoundation.Services
  4. In the Main method, add the following code to get the context environment for a solver and create a new model.

    Dim context As SolverContext = SolverContext.GetContext()
    Dim model As Model = context.CreateModel()
  5. Create decision variables that represent the two sources of crude oil: Saudi Arabia and Venezuela. Then add the decisions to the model.

    Dim sa As New Decision(Domain.RealRange(0, 9000), "SA")
    Dim vz As New Decision(Domain.RealRange(0, 6000), "VZ")
    model.AddDecisions(sa, vz)
  6. Create recourse decision variables that represent the demand for the three products: gas, jet fuel, and lubricant. If the refining process does not meet demand, pre-refined product must be purchased. Add the decisions to the model.

    Dim gasBuy As New RecourseDecision(Domain.RealNonnegative, "GasBuy")
    Dim jetFuelBuy As New RecourseDecision(Domain.RealNonnegative, "JetFuelBuy")
    Dim lubricantBuy As New RecourseDecision(Domain.RealNonnegative, "LubricantBuy")
    model.AddDecisions(gasBuy, jetFuelBuy, lubricantBuy)
  7. Add the costs of the crude oils and the demand for the three products to the model. Then specify that the solver should minimize the goal by setting the second parameter to GoalKind.Minimize.

    model.AddGoal("goal", GoalKind.Minimize, 20 * sa + 15 * vz + (38.4 * gasBuy + 35.2 * jetFuelBuy + 28.8 * lubricantBuy))
  8. Create and add random parameters that correspond to a normal bell-shaped probability distribution.

    Dim gasDemand As RandomParameter = New NormalDistributionParameter("GasDemand", 1900, 50)
    Dim jetFuelDemand As RandomParameter = New NormalDistributionParameter("JetFuelDemand", 1500, 25)
    Dim lubricantDemand As RandomParameter = New NormalDistributionParameter("LubricantDemand", 500, 5)
    model.AddParameters(gasDemand, jetFuelDemand, lubricantDemand)
  9. Add three second-stage constraints that evaluate the expected value of the recourse decision over all possible scenarios.

    model.AddConstraint("demand1", 0.3 * sa + 0.4 * vz + gasBuy >= gasDemand)
    model.AddConstraint("demand2", 0.4 * sa + 0.2 * vz + jetFuelBuy >= jetFuelDemand)
    model.AddConstraint("demand3", 0.2 * sa + 0.3 * vz + lubricantBuy >= lubricantDemand)
  10. Solve the model and get the report.

    Dim solution As Solution = context.Solve()
    Dim report As Report = solution.GetReport()
  11. Press F5 to build and run the code.

    The Command window shows the following results.

    ===Solver Foundation Service Report===

    Date: Date

    Version: Version

    Model Name: Default

    Capabilities Applied: LP

    Solve Time (ms): 235

    Total Time (ms): 548

    Solve Completion Status: Optimal

    Solver Selected: Microsoft.SolverFoundation.Solvers.SimplexSolver



    Algorithm: Primal

    Arithmetic: Hybrid

    Variables: 302 -> 302 + 301

    Rows: 301 -> 301

    Nonzeros: 1202

    Eliminated Slack Variables: 0

    Pricing (exact): SteepestEdge

    Pricing (double): SteepestEdge

    Basis: Slack

    Pivot Count: 435

    Phase 1 Pivots: 300 + 0

    Phase 2 Pivots: 135 + 0

    Factorings: 12 + 1

    Degenerate Pivots: 0 (0.00 %)

    Branches: 0

    Stochastic Measures:

    Stochastic Solution Type: Sampled

    Sampling Method: LatinHypercube

    Sample Count: 100

    Random Seed: 123456

    Solving Method: Deterministic Equivalent

    EV: 90500

    VSS: 338.107860596763

    EVPI: 779.794594168023

    ===Solution Details===


    goal: 91279.5357087704


    SA: 1961.5464827093

    VZ: 3269.78028997898

    Second stage decisions (Average [Min, Max]):

    GasBuy: 21.8666417357027 [0, 140.348907941673]

    JetFuelBuy: 61.42678017752 [0, 125.109005887119]

    LubricantBuy: 0 [0, 0]