How to: Use Stochastic Programming to Solve Two-Stage Linear Models
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
Cost of crude oil
$20 per barrel
$15 per barrel
Maximum daily production
40% jet fuel
20% jet fuel
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
Create a console application named PetroChemStoch.
Add a reference to Microsoft Solver Foundation on the .NET tab.
Add the following Imports or using statements to the top of the Program code file.
In the Main method, add the following code to get the context environment for a solver and create a new model.
Create decision variables that represent the two sources of crude oil: Saudi Arabia and Venezuela. Then add the decisions to the model.
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.
RecourseDecision gasBuy = new RecourseDecision(Domain.RealNonnegative, "GasBuy"); RecourseDecision jetFuelBuy = new RecourseDecision(Domain.RealNonnegative, "JetFuelBuy"); RecourseDecision lubricantBuy = new RecourseDecision(Domain.RealNonnegative, "LubricantBuy"); model.AddDecisions(gasBuy, jetFuelBuy, lubricantBuy);
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.
Create and add random parameters that correspond to a normal bell-shaped probability distribution.
RandomParameter gasDemand = new NormalDistributionParameter("GasDemand", 1900, 50); RandomParameter jetFuelDemand = new NormalDistributionParameter("JetFuelDemand", 1500, 25); RandomParameter lubricantDemand = new NormalDistributionParameter("LubricantDemand", 500, 5); model.AddParameters(gasDemand, jetFuelDemand, lubricantDemand);
Add three second-stage constraints that evaluate the expected value of the recourse decision over all possible scenarios.
Solve the model and get the report.
Press F5 to build and run the code.
The Command window shows the following results.
===Solver Foundation Service Report===
Model Name: Default
Capabilities Applied: LP
Solve Time (ms): 235
Total Time (ms): 548
Solve Completion Status: Optimal
Solver Selected: Microsoft.SolverFoundation.Solvers.SimplexSolver
Variables: 302 -> 302 + 301
Rows: 301 -> 301
Eliminated Slack Variables: 0
Pricing (exact): SteepestEdge
Pricing (double): SteepestEdge
Pivot Count: 435
Phase 1 Pivots: 300 + 0
Phase 2 Pivots: 135 + 0
Factorings: 12 + 1
Degenerate Pivots: 0 (0.00 %)
Stochastic Solution Type: Sampled
Sampling Method: LatinHypercube
Sample Count: 100
Random Seed: 123456
Solving Method: Deterministic Equivalent
Second stage decisions (Average [Min, Max]):
GasBuy: 21.8666417357027 [0, 140.348907941673]
JetFuelBuy: 61.42678017752 [0, 125.109005887119]
LubricantBuy: 0 [0, 0]