How to: Bind Data Tables to Parameters and Output

You can use linear programming to minimize or maximize functions. In this data binding 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. The following table shows the costs and capabilities of the two different crude oils.

Source

Saudi Arabia refining

Venezuela refining

Cost

$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 demonstrates how to use Solver Foundation to create and solve the refining model by using the Solver Foundation Services layer.

To bind data tables to input parameters and output variables

  1. Create a console application named PetroChem.

  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
    
    using Microsoft.SolverFoundation.Common;
    using Microsoft.SolverFoundation.Services;
    
  4. Create a class that defines the information used to describe a country or region, a class that defines the production capabilities, and a class that defines the petroleum yield output.

    Class CountryDef
        Public Property Country() As String
            Get
                Return m_Country
            End Get
            Set(ByVal value As String)
                m_Country = value
            End Set
        End Property
        Private m_Country As String
        Public Property MaxProduction() As Double
            Get
                Return m_MaxProduction
            End Get
            Set(ByVal value As Double)
                m_MaxProduction = value
            End Set
        End Property
        Private m_MaxProduction As Double
        Public Property Price() As Double
            Get
                Return m_Price
            End Get
            Set(ByVal value As Double)
                m_Price = value
            End Set
        End Property
        Private m_Price As Double
        Public Property Yield() As Double
            Get
                Return m_Yield
            End Get
            Set(ByVal value As Double)
                m_Yield = value
            End Set
        End Property
        Private m_Yield As Double
        Public Property Production() As Double
            Get
                Return m_Production
            End Get
            Set(ByVal value As Double)
                m_Production = value
            End Set
        End Property
        Private m_Production As Double
    
        Public Sub New(ByVal country As String, ByVal maxProduction As Double, ByVal price As Double, ByVal yield As Double)
            m_Country = country
            m_MaxProduction = maxProduction
            m_Price = price
            m_Yield = yield
            m_Production = -42
        End Sub
    End Class
    
    Class ProductionDef
        Public Property Product() As String
            Get
                Return m_Product
            End Get
            Set(ByVal value As String)
                m_Product = value
            End Set
        End Property
        Private m_Product As String
        Public Property MinBuy() As Double
            Get
                Return m_MinBuy
            End Get
            Set(ByVal value As Double)
                m_MinBuy = value
            End Set
        End Property
        Private m_MinBuy As Double
    
        Public Sub New(ByVal product As String, ByVal minBuy As Double)
            product = product
            minBuy = minBuy
        End Sub
    End Class
    
    Class YieldDef
        Public Property Country() As String
            Get
                Return m_Country
            End Get
            Set(ByVal value As String)
                m_Country = value
            End Set
        End Property
        Private m_Country As String
        Public Property Product() As String
            Get
                Return m_Product
            End Get
            Set(ByVal value As String)
                m_Product = value
            End Set
        End Property
        Private m_Product As String
        Public Property Yield() As Double
            Get
                Return m_Yield
            End Get
            Set(ByVal value As Double)
                m_Yield = value
            End Set
        End Property
        Private m_Yield As Double
    
        Public Sub New(ByVal country As String, ByVal product As String, ByVal yield As Double)
            m_Country = country
            m_Product = product
            m_Yield = yield
        End Sub
    End Class
    
    class CountryDef {
      public string Country { get; set; }
      public double MaxProduction { get; set; }
      public double Price { get; set; }
      public double Yield { get; set; }
      public double Production { get; set; }
    
      public CountryDef(string country, double maxProduction, double price, double yield) {
        Country = country;
        MaxProduction = maxProduction;
        Price = price;
        Yield = yield;
        Production = -42;
      }
    }
    
    class ProductionDef {
      public string Product { get; set; }
      public double MinBuy { get; set; }
    
      public ProductionDef(string product, double minBuy) {
        Product = product;
        MinBuy = minBuy;
      }
    }
    
    class YieldDef {
      public string Country { get; set; }
      public string Product { get; set; }
      public double Yield { get; set; }
    
      public YieldDef(string country, string product, double yield) {
        Country = country;
        Product = product;
        Yield = yield;
      }
    }
    
  5. In the Main method, add the following code to instantiate the classes and define the data that describes the country or region, production capabilities, and petroleum yield output.

    Dim ProductionCapacity As CountryDef() = {
        New CountryDef("Venezuela", 9000, 15, 0.4),
        New CountryDef("Saudi Arabia", 6000, 20, 0.3)
    }
    
    Dim ProductionYield As YieldDef() = {
        New YieldDef("Venezuela", "Gasoline", 0.4),
        New YieldDef("Venezuela", "JetFuel", 0.2),
        New YieldDef("Venezuela", "Lubricant", 0.3),
        New YieldDef("Saudi Arabia", "Gasoline", 0.3),
        New YieldDef("Saudi Arabia", "JetFuel", 0.4),
        New YieldDef("Saudi Arabia", "Lubricant", 0.2)
    }
    
    Dim ProductionRequirements As ProductionDef() = {
        New ProductionDef("Gasoline", 2000),
        New ProductionDef("JetFuel", 1500),
        New ProductionDef("Lubricant", 500)
    }
    
    CountryDef[] ProductionCapacity = new CountryDef[] { 
              new CountryDef("Venezuela", 9000, 15, 0.4),
              new CountryDef("Saudi Arabia", 6000, 20, 0.3)
          };
    
    YieldDef[] ProductionYield = new YieldDef[] {
              new YieldDef("Venezuela", "Gasoline", 0.4),
              new YieldDef("Venezuela", "JetFuel", 0.2),
              new YieldDef("Venezuela", "Lubricant", 0.3),
              new YieldDef("Saudi Arabia", "Gasoline", 0.3),
              new YieldDef("Saudi Arabia", "JetFuel", 0.4),
              new YieldDef("Saudi Arabia", "Lubricant", 0.2)
          };
    
    ProductionDef[] ProductionRequirments = new ProductionDef[] {
              new ProductionDef("Gasoline", 2000),
              new ProductionDef("JetFuel", 1500),
              new ProductionDef("Lubricant", 500)
          };
    
  6. Add the following code to get the context environment for a solver and create a new model.

    Dim context As SolverContext = SolverContext.GetContext()
    context.ClearModel()
    Dim model As Model = context.CreateModel()
    
    SolverContext context = SolverContext.GetContext();
    context.ClearModel();
    Model model = context.CreateModel();
    
  7. Create sets to hold the data for the country/region and products.

    Dim countries As New [Set](Domain.Any, "countries")
    Dim products As New [Set](Domain.Any, "products")
    
    Set countries = new Set(Domain.Any, "countries");
    Set products = new Set(Domain.Any, "products");
    
  8. Create a decision that determines how much oil to buy from each country or region, and then bind the production capabilities data table to the decision. Finally, add the decision to the model.

    Dim buy As New Decision(Domain.RealNonnegative, "barrels", countries)
    buy.SetBinding(ProductionCapacity, "Production", "Country")
    model.AddDecisions(buy)
    
    Decision buy = new Decision(Domain.RealNonnegative, "barrels", countries);
    buy.SetBinding(ProductionCapacity, "Production", "Country");
    model.AddDecisions(buy);
    
  9. Create parameters that represent columns of input data, and then bind data to the parameters. Then add the parameters to the model.

    Dim max As New Parameter(Domain.RealNonnegative, "max", countries)
    Dim price As New Parameter(Domain.RealNonnegative, "price", countries)
    Dim yield As New Parameter(Domain.RealNonnegative, "yield", countries, products)
    Dim min As New Parameter(Domain.RealNonnegative, "min", products)
    max.SetBinding(ProductionCapacity, "MaxProduction", "Country")
    price.SetBinding(ProductionCapacity, "Price", "Country")
    yield.SetBinding(ProductionYield, "Yield", "Country", "Product")
    min.SetBinding(ProductionRequirements, "MinBuy", "Product")
    model.AddParameters(max, price, yield, min)
    
    Parameter max = new Parameter(Domain.RealNonnegative, "max", countries);
    Parameter price = new Parameter(Domain.RealNonnegative, "price", countries);
    Parameter yield = new Parameter(Domain.RealNonnegative, "yield", countries, products);
    Parameter min = new Parameter(Domain.RealNonnegative, "min", products);
    max.SetBinding(ProductionCapacity, "MaxProduction", "Country");
    price.SetBinding(ProductionCapacity, "Price", "Country");
    yield.SetBinding(ProductionYield, "Yield", "Country", "Product");
    min.SetBinding(ProductionRequirments, "MinBuy", "Product");
    model.AddParameters(max, price, yield, min);
    
  10. Create and add a constraint for the production capabilities. In the following code, the data is specified algebraically in a way that ensures that the amount of oil purchased is less than or equal to the country or region's maximum production capabilities.

    model.AddConstraint("maxproduction",
        model.ForEach(countries, Function(c) 0 <= buy(c) <= max(c)))
    
    model.AddConstraint("maxproduction",
      Model.ForEach(countries, c => 0 <= buy[c] <= max[c]));
    
  11. Create and add a constraint for the petroleum yield output, which varies depending on the refining capabilities of each crude oil.

    model.AddConstraint("productionyield",
        model.ForEach(
            products,
          Function(p) model.Sum(
              model.ForEach(countries, Function(c) yield(c, p) * buy(c))) >= min(p)))
    
    model.AddConstraint("productionyield",
      Model.ForEach(
      products, 
      p =>Model.Sum(Model.ForEach(countries, c => yield[c, p] * buy[c])) >= min[p]));
    
  12. Add a goal that minimizes the cost of the products that are purchased.

    model.AddGoal("cost", GoalKind.Minimize,
                  model.Sum(model.ForEach(countries, Function(c) price(c) * buy(c))))
    
    model.AddGoal("cost", GoalKind.Minimize,
      Model.Sum(Model.ForEach(countries, c => price[c] * buy[c])));
    
  13. Solve the model, call the PropagateDecisions method to save the results, and get the report.

    Dim solution As Solution = context.Solve(New SimplexDirective())
    
    context.PropagateDecisions()
    
    Dim report As Report = solution.GetReport()
    Console.Write("{0}", report)
    
    Solution solution = context.Solve(new SimplexDirective());
    
    context.PropagateDecisions();
    
    Report report = solution.GetReport();
    Console.Write("{0}", report);
    
  14. 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): 152

    Total Time (ms): 288

    Solve Completion Status: Optimal

    Solver Selected: Microsoft.SolverFoundation.Solvers.SimplexSolver

    Directives:

    Simplex(TimeLimit = -1, MaximumGoalCount = -1, Arithmetic = Default, Pricing = Default, IterationLimit = -1, Algorithm = Default, Basis = Default, GetSensitivity = False)

    Algorithm: Primal

    Arithmetic: Double

    Variables: 2 -> 2 + 4

    Rows: 6 -> 4

    Nonzeros: 10

    Eliminated Slack Variables: 0

    Pricing (double): SteepestEdge

    Basis: Slack

    Pivot Count: 3

    Phase 1 Pivots: 3 + 0

    Phase 2 Pivots: 0 + 0

    Factorings: 4 + 0

    Degenerate Pivots: 0 (0.00 %)

    Branches: 0

    ===Solution Details===

    Goals:

    cost: 92500

    Decisions:

    barrels_venezuela: 3500

    barrels_saudiarabia: 2000

See Also

Concepts

Developing with Solver Foundation Services (SFS)