Welcome to MSDN Blogs Sign in | Join | Help

Using Solver Foundation and plug-in solvers in IronPython

Solver Foundation provides an easy-to-use and flexible plug-in infrastructure as an addition to its already rich set of solvers. Users can choose their favorite plugin solvers to solve the model, and yet leverage all features provided in Solver Foundation Services (SFS).

The key process is to register the plug-in solvers via an app.config file. A typical config file looks as follows:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <configSections>

    <section name="MsfConfig"

             type="Microsoft.SolverFoundation.Services.MsfConfigSection, Microsoft.Solver.Foundation, Version=2.0.2.8632, Culture=neutral, PublicKeyToken=31bf3856ad364e35"

             allowLocation="true"

             allowDefinition="Everywhere"

             allowExeDefinition="MachineToApplication"

             restartOnExternalChanges="true"

             requirePermission="true" />

  </configSections>

 

  <MsfConfig>

    <MsfPluginSolvers>

      <MsfPluginSolver

              capability="<plug-in solver capability>"

              assembly="<path\to\your\plug-in\solver\dll"

              solverclass="<plug-in solver class name>"

              directiveclass="<plug-in solver directive class name>"

              parameterclass="<plug-in solver parameter class name>"/>

    </MsfPluginSolvers>

  </MsfConfig>

</configuration>

 

The information such as assembly name and solver class name can usually be found from the plug-in solver provider.

The config file then will co-exist with the compiled exe file of the user app. When, inside the app code, a Solve call is issued, SFS will pick a solver based on the model kind, directive instances passed in, and the solver registration.

The app code in this case can be written in any .NET compliant language such as C# or VB.NET.

IronPython is “an implementation of the Python programming language running under .NET …” So it is possible to use Solver Foundation, and with plug-in solvers, in IronPython code. However, one difficulty is that Python is an interpreted language. Therefore we cannot compile Python code to an exe file. So how do we register our plug-in solvers?

The trick is to register the plug-in solvers inside ipy.exe.config (or ipy64.exe.config). These two files must be put under the same folder with ipy.exe and ipy64.exe. We assume IronPython 2.6 RC2 in the following discussion.

There is IronPython sample code under <your document folder>\Microsoft Solver Foundation\Samples\SolverFoundationServices\IronPython if we install Solver Foundation v2.0. We will use the petrochem-sfs.py as the example. Let us again use Gurobi solver as our plug-in solver. Gurobi solver dlls are installed by default under C:\Program Files\Microsoft Solver Foundation\2.0.2.8632\Plugins (and C:\Program Files\Microsoft Solver Foundation\2.0.2.8632\Plugins(x86) if the target platform is 64bit). So the plug-in solver registration looks as follows:

ipy.exe.config

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <configSections>

    <section name="MsfConfig"

             type="Microsoft.SolverFoundation.Services.MsfConfigSection, Microsoft.Solver.Foundation, Version=2.0.2.8632, Culture=neutral, PublicKeyToken=31bf3856ad364e35"

             allowLocation="true"

             allowDefinition="Everywhere"

             allowExeDefinition="MachineToApplication"

             restartOnExternalChanges="true"

             requirePermission="true" />

  </configSections>

 

  <MsfConfig>

    <MsfPluginSolvers>

      <MsfPluginSolver

              capability="LP"

              assembly="C:\Program Files\Microsoft Solver Foundation\2.0.2.8632\Plugins(x86)\GurobiPlugin.dll"

              solverclass="SolverFoundation.Plugin.Gurobi.GurobiSolver"

              directiveclass="SolverFoundation.Plugin.Gurobi.GurobiDirective"

              parameterclass="SolverFoundation.Plugin.Gurobi.GurobiParams"/>

    </MsfPluginSolvers>

  </MsfConfig>

</configuration>

 

ipy64.exe.config

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <configSections>

    <section name="MsfConfig"

             type="Microsoft.SolverFoundation.Services.MsfConfigSection, Microsoft.Solver.Foundation, Version=2.0.2.8632, Culture=neutral, PublicKeyToken=31bf3856ad364e35"

             allowLocation="true"

             allowDefinition="Everywhere"

             allowExeDefinition="MachineToApplication"

             restartOnExternalChanges="true"

             requirePermission="true" />

  </configSections>

 

  <MsfConfig>

    <MsfPluginSolvers>

      <MsfPluginSolver

              capability="LP"

              assembly="C:\Program Files\Microsoft Solver Foundation\2.0.2.8632\Plugins\GurobiPlugin.dll"

              solverclass="SolverFoundation.Plugin.Gurobi.GurobiSolver"

              directiveclass="SolverFoundation.Plugin.Gurobi.GurobiDirective"

              parameterclass="SolverFoundation.Plugin.Gurobi.GurobiParams"/>

    </MsfPluginSolvers>

  </MsfConfig>

</configuration>

 

Now if we run the petrochem-sfs.py sample (ipy -O petrochem-sfs.py), we will see that Gurobi solver is used:

 

 

Lengning

Sparse input data and data binding

Solver Foundation supports flexible data binding so that the algebraic model can be separated from the data source. In Solver Foundation, both parameters and decisions can be bound to data source.  It makes writing and maintaining models really easy.

 To bind a parameter to a data source, we usually need to specify the data source, the value field, and indexes. For example, the following parameter WarehouseCap is bound to the table that has two columns.

The table looks as follows.

warehouse

capacity

1

10

2

29

3

13

 

Column warehouse is the index column/field, and column capacity is the value column/field.

In C#, the binding can be set via the following line.

  _model.WarehouseCap.SetBinding(warehouseCaps, "Capacity", "Warehouse");

 

assuming warehouseCaps is an IEnumerable that contains the data.

The sparse data case is where the table contains multiple index columns and not all the combinations of the index values exist in the table. For example, there could be a table that records the cost of shipping goods from warehouses to stores. However, it is usually not the case that every warehouse can ship goods and every store. For example,

warehouse

store

cost

1

2

100

2

3

200

3

1

150

 

Such table represents input data that are sparse. By default, the missing rows all have the value column being zero.

Currently in Solver Foundation v2.0, we cannot bind to such sparse data directly. However, we observe that the values appearing in an index column of the sparse table form a subset of the values allowed for that index column. Furthermore, Solver Foundation allows parameters to appear as the indexes to other parameters or decisions. We can use the following approach to bind to this sparse table.

Let us introduce a helper column called costid into the table.

costid

warehouse

store

cost

0

1

2

100

1

2

3

200

2

3

1

150

 

Now we create two extra parameters called CostWarehouse and CostStore, which will be bound to the warehouse column and store column respectively. The needed parameter Cost will be bound to the cost column as we expected. All three parameters now use costid as the index.

Here are the actual definitions for the three parameters.

  Parameters[

    Sets[Any],

    costid

  ],

  Parameters[

    Integers[0, Infinity],

    CostWarehouse[costid],

    CostStore[costid]

  ],

  Parameters[

    Reals[-Infinity, Infinity],

    Cost[costid]

  ],

 

Assume we have a decision called Ship defined as follows.

  Parameters[

    Sets[Any],

    warehouse,

    store

  ],

  Decisions[

    Integers[0, 1],

    Ship[warehouse, store]

  ],

Ship[w, s] being 1 if and only if we want to ship goods from warehouse w to store s.

Now suppose we want to construct as constraint that says “the total cost of shipping is between 100 and 400. The constraint can be written as follows.

100 <= Sum[{id, costid}, Cost[id] * Ship[CostWarehouse[id], CostStore[id]]] <= 400

 

Notice that we iterate through all rows in the cost table and use parameter CostWarehouse[id] and CostStore[id] as indexes on decision Ship.

To ensure that we do not ship any goods from incompatible warehouse and store pairs, we add the following constraint using the fact that default values are zero:

Foreach[{w, warehouse}, {s, store}, Ship[w, s] <= Sum[{id, costid}, AsInt[CostWarehouse[id] == w & CostStore[id] == s]]

 

We leave it to the reader to figure out how this constraint works. Notice that, in the last constraint, the complicated right-hand side of the inequality actually is reduced to a constant value. So the model is still a MILP model.

Here is the complete model.

Model[

  Parameters[

    Sets[Integers[0, Infinity]],

    costid,

    warehouse,

    store

  ],

  Parameters[

    Integers[0, Infinity],

    CostWarehouse[costid],

    CostStore[costid]

  ],

  Parameters[

    Reals[-Infinity, Infinity],

    Cost[costid],

    WarehouseCap[warehouse],

    StoreCap[store]

  ],

  Decisions[

    Integers[0, 1],

    Ship[warehouse, store]

  ],

  Constraints[

    Constraint1 -> 100 <= Sum[{id, costid}, Cost[id] * Ship[CostWarehouse[id], CostStore[id]]] <= 400,

    Constraint2 -> Foreach[{w, warehouse}, {s, store}, Ship[w, s] <= Sum[{id, costid}, AsInt[CostWarehouse[id] == w & CostStore[id] == s]]]

  ],

  Goals[

    Maximize[

      Goal1 -> Annotation[Sum[{w, warehouse}, {s, store}, Ship[w, s]], "order", 0]

    ]

  ]

]

 

Here is a sample run.

[11/2/2009 10:54:59 PM] Solve started...

[11/2/2009 10:55:00 PM] ===Solver Foundation Service Report===

Datetime: 11/2/2009 10:55:00 PM

Model Name: Default

Capabilities Requested: MILP

Solve Time (ms): 34

Total Time (ms): 187

Solve Completion Status: Optimal

Solver Selected: SolverFoundation.Plugin.Gurobi.GurobiSolver

Algorithm: Dual

Arithmetic: Double

Variables: 9 -> 20 + 11

Rows: 11 -> 11

Nonzeros: 21

Eliminated Slack Variables: 0

Pricing (double): Automatic

Pivot Count: 0

Phase 1 Pivots: -1 + 0

Phase 2 Pivots: -1 + 0

Factorings: -1 + 0

Degenerate Pivots: -1 (0.00 %)

Branches: 0

===Solution Details===

Goals:

Goal1: 2

 

Decisions:

Ship(1, 2): 1

Ship(2, 3): 1

Ship(3, 1): 0

Ship(1, 1): 0

Ship(1, 3): 0

Ship(2, 1): 0

Ship(2, 2): 0

Ship(3, 2): 0

Ship(3, 3): 0

 

[11/2/2009 10:55:00 PM] Solve Complete

The actual workbook is attached too.

Lengning

Deploy OML model from Excel to Visual Studio

Deploy-to-C# is a new feature we shipped in Solver Foundation v2.0 Excel Add-in. The idea is to simplify the process of deploying an OML model written in the Add-in into a Visual Studio project that builds the same model using Solver Foundation Services APIs. With this new feature, users who are comfortable in writing and debugging their models in Excel/OML environment now can produce an equivalent C# model by a click of a button. Once the C# model is generated, users can further embed the model code into their applications. It is also possible now to solve the model using multiple directives or use plug-in solvers.

In this post, we will show how to use the Deploy functionality to convert an OML model into a Visual Studio project. To repeat the following steps yourself, please make sure you have Microsoft Office 2007, Visual Studio 2008, and Solver Foundation v2.0 installed. Furthermore, to try out the plug-in solver you have, please update the solver registration section in the config file accordingly.

In Solver Foundation v2.0 release, we include a sample on the supply chain planning. It can be found under <your documents folder>\Microsoft Solver Foundation\Samples\Excel\LP. Let us start with this sample.

Once we load this Excel file, we can see the data are stored in the spreadsheet and the OML model is presented in our new modeling pane.

We can solve the model in Excel by clicking the Solve button. Here is the result.

As we can see from the log, Solver Foundation simplex solver is used with the default setting to solve this model.

Now let us start deploying the model into C#. We notice the new Deploy button in the Ribbon.

Let us click on it. The following window pops up, where we can specify the C# file name for the model to be deployed. Let's call it SupplyChainPlanning.cs.

Now we have two files created under the target folder: SupplyChainPlanning.cs and SupplyChainPlanning.xml. The first file contains the equivalent model written in C# and the second xml file contains the data from the spreadsheet. The model now can be embedded into any Visual Studio C# project.

Let us now open Visual Studio 2008 and create a new C# console project. Notice that Solver Foundation v2.0 installation creates two project templates in Visual Studio 2008. Let us pick the Solver Foundation Console Application template.

After the project is created, let us add the SupplyChainPlanning.cs and SupplyChainPlanning.xml into the project. We can delete the sample model file created automatically by the template because we are not going to use it. We make sure that the xml file will be always copied to the output directory during compilation since it contains the data we need.

Next, we need to write some glue code to bind the data from XML file to the model. This part is not automatically done at this moment. However, the data binding code is pretty straightforward. We use LINQ to XML in this case.

  class Program {

    private ExportedModel _model;

 

    public Program() {

      _model = new ExportedModel();

    }

 

    public void Run() {

      BindData();

      var solution = _model.Context.Solve();

      Console.WriteLine(solution.GetReport().ToString());

    }

 

    private void BindData() {

      var data = XElement.Load("SupplyChainPlanning.xml");

      var manufactureLoads =

          from load

          in data.Descendants("manufactureLoads")

          select new

          {

            Product = load.Element("Product").Value,

            Load = Convert.ToDouble(load.Element("ManufactureLoads").Value)

          };

      var factoryCapacities =

          from cap

          in data.Descendants("factoryCapacity")

          select new

          {

            Factory = cap.Element("Factory").Value,

            Capacity = Convert.ToDouble(cap.Element("FactoryCapacity").Value)

          };

      var unitManufactureCosts =

          from cost

          in data.Descendants("unitManufactureCost")

          select new

          {

            Product = cost.Element("Product").Value,

            Cost = Convert.ToDouble(cost.Element("UnitManufactureCost").Value)

          };

      var transports =

          from tran

          in data.Descendants("transport")

          select new

          {

            Product = tran.Element("Product").Value,

            Area = tran.Element("Geography").Value,

            Factory = tran.Element("Factory").Value,

            Transport = Convert.ToDouble(tran.Element("Transport").Value)

          };

      var demandForecastPrices =

          from demand

          in data.Descendants("demandForecastPrice")

          where demand.Elements().Count() == 4

          select new

          {

            Product = demand.Element("Product").Value,

            Area = demand.Element("Geography").Value,

            Promotion = demand.Element("Promotion").Value,

            Price = Convert.ToDouble(demand.Element("demandForecastPrice").Value)

          };

      var demandForecastUnits =

          from demand

          in data.Descendants("demandForecastUnits")

          where demand.Elements().Count() == 4

          select new

          {

            Product = demand.Element("Product").Value,

            Area = demand.Element("Geography").Value,

            Promotion = demand.Element("Promotion").Value,

            Units = Convert.ToDouble(demand.Element("demandForecastUnits").Value)

          };

      _model.manufactureLoads.SetBinding(manufactureLoads, "Load", "Product");

      _model.factoryCapacity.SetBinding(factoryCapacities, "Capacity", "Factory");

      _model.unitManufactureCost.SetBinding(unitManufactureCosts, "Cost", "Product");

      _model.transport.SetBinding(transports,

                                  "Transport", "Product", "Area", "Factory");

      _model.demandForecastPrice.SetBinding(demandForecastPrices,

                                            "Price", "Product", "Area", "Promotion");

      _model.demandForecastUnits.SetBinding(demandForecastUnits,

                                            "Units", "Product", "Area", "Promotion");

    }

 

    static void Main(string[] args) {

      var program = new Program();

      program.Run();

    }

  }

Note that we have a where clause in building demandForecastPrices and demandForecastUnits. We leave to the readers to experiment why the where clause is needed.

Now let's run the console app. Here is the output. For simplicity, we skip the decision values. We can verify that the objective function value is the same as we have seen in Excel.

===Solver Foundation Service Report===

Datetime: 10/30/2009 12:03:57

Model Name: Default

Capabilities Requested: LP

Solve Time (ms): 515

Total Time (ms): 911

Solve Completion Status: Optimal

Solver Selected: Microsoft.SolverFoundation.Solvers.SimplexSolver

Directives:

Microsoft.SolverFoundation.Services.Directive

Algorithm: Primal

Arithmetic: Hybrid

Variables: 154 -> 154 + 77

Rows: 104 -> 77

Nonzeros: 416

Eliminated Slack Variables: 0

Pricing (exact): SteepestEdge

Pricing (double): SteepestEdge

Basis: Slack

Pivot Count: 114

Phase 1 Pivots: 0 + 0

Phase 2 Pivots: 114 + 0

Factorings: 6 + 1

Degenerate Pivots: 82 (71.93 %)

Branches: 0

===Solution Details===

Goals:

Profit: 9158191.66666667

 

Now to illustrate a more advance usage of Solver Foundation in this C# project, let's include a solver registration and use a plug-in solver to solve it.

To use a plug-in solver with Solver Foundation, please make sure you have all the needed DLLs from the plug-in solver vendor. In this example, we will use Gurobi solver to solve this LP model.

First we need to add a config file to the project so that we can register the plug-in solvers. Here is what the app.config file looks like

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <configSections>

    <section name="MsfConfig"

             type="Microsoft.SolverFoundation.Services.MsfConfigSection, Microsoft.Solver.Foundation, Version=2.0.2.8632, Culture=neutral, PublicKeyToken=31bf3856ad364e35"

             allowLocation="true"

             allowDefinition="Everywhere"

             allowExeDefinition="MachineToApplication"

             restartOnExternalChanges="true"

             requirePermission="true" />

  </configSections>

 

  <MsfConfig>

    <MsfPluginSolvers>

      <MsfPluginSolver

              capability="LP"

              assembly="GurobiPlugin.dll"

              solverclass="SolverFoundation.Plugin.Gurobi.GurobiSolver"

              directiveclass="SolverFoundation.Plugin.Gurobi.GurobiDirective"

              parameterclass="SolverFoundation.Plugin.Gurobi.GurobiParams"/>

    </MsfPluginSolvers>

  </MsfConfig>

</configuration>

Notice that we register Gurobi solver as an LP solver here, with Gurobi solver's class, directive class, and parameter class names.

Next, we need to include necessary DLLs into the project so that Solver Foundation can find them. Here is what the project looks like now.

Here we reference GurobiPlugin.dll (plug-in solver wrapper) and add the gurobi20.dll (unmanaged code DLL for the actual solver) to the project. Notice that we set the "Copy to Output Directory" property of gurobi20.dll to "Copy Always" too. These two DLLs are shipped with Solver Foundation v2.0 and can be found under <your program files folder>\ Microsoft Solver Foundation\2.0.2.8632\Plugins.

Now let's run the application again. This time, we will see from the report that Gurobi solver is used and the same objective function value is returned.

===Solver Foundation Service Report===

Datetime: 10/30/2009 13:48:28

Model Name: Default

Capabilities Requested: LP

Solve Time (ms): 110

Total Time (ms): 522

Solve Completion Status: Optimal

Solver Selected: SolverFoundation.Plugin.Gurobi.GurobiSolver

Directives:

Microsoft.SolverFoundation.Services.Directive

Algorithm: Dual

Arithmetic: Double

Variables: 154 -> 258 + 104

Rows: 104 -> 104

Nonzeros: 416

Eliminated Slack Variables: 0

Pricing (double): Automatic

Pivot Count: 36

Phase 1 Pivots: -1 + 0

Phase 2 Pivots: -1 + 0

Factorings: -1 + 0

Degenerate Pivots: -1 (-2.78 %)

Branches: 0

===Solution Details===

Goals:

Profit: 9158191.66666666

To tweak the settings of Gurobi solver, we can further create a Gurobi solver directive and change the settings there. Then we pass the directive instance into Solve call. I will leave this to the readers to experiment.

This sample goes through all steps in deploying an OML model from Excel to Visual Studio. I hope it helps in your application development. Thank you for your time.

Lengning

Try out our F# ODSL and SFS wrapper sample with the newly released F# October CTP (1.9.7.8)

http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/default.aspx

The F# ODSL and SFS wrapper included in Solver Foundation v2.0 utilize the new feature, unit of measure, in F# to facilitate more robust modeling experience. In order to try out the sample, you will need to download the most recent update of F#, v1.9.7.8, from the link above.

Have fun!

Solver Foundation V2.0 is shipped today!

We are pleased to announce the release of Solver Foundation v2.0. This is the fourth on-time commercial release for the product. The Express edition is available now at http://code.msdn.microsoft.com/solverfoundation. The Standard Edition of Solver Foundation v2.0 has released-to-manufacturing, and will be available online via MSDN subscriber downloads the week of 11oct09, and offline world-wide via MSDN in approximately two months later. Our Enterprise edition is also available.

 

In version 2, we focused on growing our collection of solvers, vastly improving our tooling experience, and introduced the beginnings of what will be our simulation framework. At a high level, Solver Foundation v2.0 has the following new features:

 

Top level features we are introducing in Version 2.0:

-          Design Tools. We have improved the integration, interchange, and workflow of our design surfaces for Excel, SharePoint, and Visual Studio – modelers will now be able to take models algorithmically designed in Excel and inject this directly into a full-fledged Visual Studio/C# solutions with the press of a “deploy” button. Additionally, a model can be sent directly to SharePoint using our script-safe OMLX format. This is a long-term focus area to make modeling as easy as possible without losing any capabilities for the advanced user.

-          Solver Foundation Services

·         Stochastic Programming and Optimization with Monte Carlo simulation capabilities. The SFS supports random parameters and recourse decisions, allowing for the definition and solution of two-stage stochastic models

·         Submodels.  Our master model can now define and reference submodels, allowing for better encapsulation and reuse

·         Special Ordered Sets.  Introduced SOS1 and increased flexibility in the definition of SOS2 constraints

·         Solver Plug-in SDK improvements to include Special Ordered Sets, more detailed reporting (e.g., IIS), and expanded our solver partner ecosystem to include LINDO Systems

-          Solvers

·         Gurobi version 2.0 Solver is included in our release, and is the default Solver Foundation MIP solver

·         Second Order Conic Programming (SOCP) Solver Extensions to our IPM Solver to support rich constraint modeling (we can now have nonlinear goals and constraints)

·         Extensions to the Simplex Solver to support SOS1 and SOS2 modeling and solving

·         Improved our in-house Mixed Integer Programming Solver

·         Metaheuristics and Global Constraints for our Constraint Solver (developed in conjunction with our friends at MSR Cambridge) to ease and simplify declarative modeling with more powerful constructs

-          Created a new F# Optimization DSL (ODSL) that incorporates units-of-measure work from MSR Cambridge

-          Increased our solver vendor and modeling partner ecosystem to include LKINDO Systems and Princeton Consultants – more information can be found at www.solverfoundation.com

-          Our core DLL is now localized in German. This is the start of our longer term focus to localize all of Solver Foundation in the top languages world-wide

-          A host of other improvements

 

Posted by lengningliu | 0 Comments
Filed under: ,

Optimization domain specific language in F# with units of measure

In Solver Foundation CTP release, we shipped an optimization domain specific language (ODSL) in F#, which embedded Solver Foundation simplex solver for solving linear programming models. In the upcoming version 2 release, we will ship an enhanced version of this ODSL. Here are some of the highlights:

·         Units of measure: in F# numerical quantities can be annotated by a unit such as kilogram, dollar per day. These annotations not only provide additional and explicit documentation on the nature of the quantities involved, but also allow F# to statically check that no mistake is made in the use of these quantities: adding Watt and Newton is incorrect for example.

Units of measures in F# are declared as follows:

[<Measure>] type Dollar

[<Measure>] type Barrel

[<Measure>] type Day

[<Measure>] type ProductionRate = Barrel/Day

 

The syntax for annotating a numerical value with its measure is shown below:

let dailyOutput = 9000.0<Barrel/Day>

let price = 140.0<Dollar/Barrel>

let dailyCost = dailyOutput * price

 

In our new ODSL, units of measures can be used in modeling a problem, which help modelers to catch errors in the model where incompatible quantities are used in the constraints or goal functions. Here is a simple example of the Petrochem problem.

let a = 20.0<Dollar/Barrel>

let b = 15.0<Dollar/Barrel>

let sa = var<Barrel/Day>()

let vz = var<_>()

                

minimise (a * sa + b * vz)

           

where

[

      0.3 * sa + 0.4 * vz >= 2000.<_>;

      0.4 * sa + 0.2 * vz >= 1500.<_>;

      0.2 * sa + 0.3 * vz >= 500.<_>;

      sa <= 9000.<_>;

      vz <= 6000.<_>;

      sa >= 0.<_>;

      vz >= 0.<_> 

]

 

Note that if quantities such as a were defined to have a measure other than Dollar/Barrel, F# would have caught the error in the goal function where b*vz had the measure Dollar/Day while a*sa did not.

·         Solver Foundation wrapper in F#: this wrapper directly wraps Solver Foundation APIs and does not use F# quotations. With this wrapper, we also enabled units of measure in models that are written against Solver Foundation APIs. Here is the model of the same Petrochem problem written in this wrapper:

    [<Measure>] type Dollar

    [<Measure>] type Barrel

    [<Measure>] type Day

    [<Measure>] type ProductionRate = Barrel/Day

 

    // Initialization of the SFS model from the context

    // We use the SfsModel class that wraps the usual SFS Model class

    // and additionally keeps track of the F#-specific type information

    let context = SolverContext.GetContext()

    let petrochem = new SfsModel(context)

 

    /// Optimization variable: Production in Saudi-Arabia

    let buyfromSA = petrochem.CreateRealVariable<ProductionRate>()

    /// Optimization variable: Production in Venezuela

    let buyfromVZ = petrochem.CreateRealVariable<ProductionRate>()

 

    // Add the production constraints:

    // How much of gasoline, jet fuel and machine lubricants can

    // we refine from the oil provided by the two supplier countries?

    let c1 = petrochem.AddConstraint (

0.3*buyfromSA + 0.4*buyfromVZ >>== 2000.0<Barrel/Day>)

    let c2 = petrochem.AddConstraint (

0.4*buyfromSA + 0.2*buyfromVZ >>== 1500.0<Barrel/Day>)

    let c3 = petrochem.AddConstraint (

0.2*buyfromSA + 0.3*buyfromVZ >>== 500.0<Barrel/Day>)

   

 

    /// Maximum production in Saudi-Arabia

    let maxProductionSA = 9000.0<_>

    /// Maximum production in Venezuela

    let maxProductionVZ = 6000.0<_>

 

    // Add the maximum production constraints to the model

    petrochem.AddConstraints [|

        0.0<_> <<== buyfromVZ;  buyfromVZ <<== maxProductionVZ;

        0.0<_> <<== buyfromSA ; buyfromSA <<== maxProductionSA

    |]

 

    /// Business goal: minimize the running costs

    let runningCost = petrochem.AddGoal (GoalKind.Minimize,

20.0<Dollar/Barrel>*buyfromSA +

15.0<Dollar/Barrel>*buyfromVZ)

   

    /// Solution to the petrochem example

    let petrochemSolution = petrochem.Solve()

 

The ODSL and the wrapper source code will be shipped in Solver Foundation version 2 as a sample on how to enable F# to solve optimization problems. More details will be available in the release doc. The essence here is that, being a complete .NET solution and CLS compliant, Solver Foundation can be easily embedded in other .NET environments to help our customers to solve their problems in the environment that they are most familiar with.

Version 2 of Solver Foundation contains a number of new features and enhancements. Please also check out Nathan’s blog on simulation and stochastic programming. For more information, please stay tuned for the actual release.

Lengning

Solver Foundation v1.2 is released!

Please check out www.solverfoundation.com for an immediate access to our Express edition. Standard edition will be ready through MSDN subscription in the following days.

It's really exciting to see three releases in five months.

- Lengning

Posted by lengningliu | 1 Comments
Filed under:

Solver Foundation in Action!

Here is a post that describes a recent use case of Microsoft Solver Foundation in real world applications.

http://blogs.msdn.com/pascal/archive/2009/01/13/l-agenda-des-techdays-2009-a-t-enti-rement-construit-avec-microsoft-solver-foundation.aspx

Thank you Pascal for contacting us with the problem and collaborating with us to get the work done in time. It was great fun for us working with you.

Links to other blogs that talk about Microsoft Solver Foundation

  1. Alexander Stojanovic is the founder of Microsoft Solver Foundation. Please check out his blog at http://conceptualorigami.blogspot.com/ 
  2. Min Wei is the principle engineer of Microsoft Solver Foundation. His blog is available at http://blogs.msdn.com/minwei/
  3. Nathan Brixius has put up a nice blog post that aggregates links to other blogs and resources about Microsoft Solver Foundation: http://blogs.msdn.com/natbr/archive/2008/11/15/microsoft-solver-foundation-blogs-and-resources.aspx

- Lengning Liu

Using Solver Foundation to generate Sudoku puzzles, Part III (final)

This is the final post on the Sudoku puzzle generator. We have seen how to generate a Sudoku puzzle by using Microsoft Solver Foundation to repeatedly solve the Sudoku model with different board configurations. The data binding feature in Solver Foundation Services makes the process elegant in the sense that only the data source needs to be updated each time and model does not need to be changed.

In this post, we will integrate the puzzle generation into Excel (we assume Excel 2007 and Visual Studio 2008 are installed) via a new add-in. This new add-in creates a new Ribbon tab in Excel. We will not going into the details of the code this time as it is purely mechanical work of building an Excel add-in.

The main procedure is the event handler that handles the button click event on Generate button.

Private Sub GenerateSudokuButton_Click( _

  ByVal sender As System.Object, _

  ByVal e As _

   Microsoft.Office.Tools.Ribbon.RibbonControlEventArgs) _

  Handles GenerateSudokuButton.Click

 

  Dim sheet As Excel.Worksheet = GetSudokuSheet()

 

  With sheet

.Cells(1, 1) = _

 "Sudoku For Excel, Powered by Microsoft Solver Foundation"

    .Cells(_offset + 10, _offset).Clear()

    .Cells(_offset + 20, _offset).Clear()

 

.Range(.Cells(_offset, _offset), _

       .Cells(_offset + 8, _offset + 8)).Clear()

    For i = 0 To 2

      For j = 0 To 2

        .Range(.Cells(_offset + 3 * i, _offset + 3 * j), _

               .Cells(_offset + 3 * i + 2, _

                      _offset + 3 * j + _

        2)).BorderAround(Excel.XlLineStyle.xlContinuous, _

          Excel.XlBorderWeight.xlMedium, _

          Excel.XlColorIndex.xlColorIndexAutomatic)

      Next

    Next

.Range(.Cells(_offset, _offset), _

       .Cells(_offset + 8, _offset + _

        8)).BorderAround(Excel.XlLineStyle.xlDouble, _

       Excel.XlBorderWeight.xlThick,  _

       Excel.XlColorIndex.xlColorIndexAutomatic)

.Range(.Cells(_offset, _offset), _

       .Cells(_offset + 8, _offset + 8)).ColumnWidth = 5

 

 

    Dim board(,) As Sudoku.SudokuBoardCell = _

      Globals.Sudoku.GeneratePuzzle()

    If (board Is Nothing) Then

      MsgBox("Cannot generate a puzzle. Please try again.")

      Return

    End If

 

    For i = 0 To 8

      For j = 0 To 8

        If board(i, j).PresetValue <> 0 Then

          .Cells(_offset + i, _offset + j) = _

            board(i, j).PresetValue

          .Range(.Cells(_offset + i, _offset + j), _

                 .Cells(_offset + i, _offset + _

                        j)).Interior.Color = _

            Excel.XlRgbColor.rgbCornflowerBlue

        End If

      Next

    Next

  End With

 

  Me.FillInZerosToggleButton.Checked = False

End Sub

 

The highlighted places call the puzzle generator we have created to get a new puzzle and fill in the cells in a specific sheet called “Sudoku” with preset values in the puzzle.

This is the end of this sample. I hope you had fun reading through the posts. The complete sample solution can be downloaded from the download page of Microsoft Solver Foundation website. Please feel free to contact me regarding any questions or feedback on this sample. Thank you in advance.

In the next series of posts, we will take a look at the camera depot sample shipped in Microsoft Solver Foundation V1. That sample embeds Solver Foundation in an ASP.NET web service to recommend an optimal camera configuration based on user’s choices and preferences. On the client side, user can specify his/her choices and preferences using a Silverlight UI. This next sample is written in C#.

- Lengning Liu

Using Solver Foundation to generate Sudoku puzzles, Part II

In our last post, we saw the model for completing a Sudoku board given a particular board configuration. In this post, we will show how to repeatedly use this model to generate a Sudoku puzzle.

Let us first take a look at the main procedure of the puzzle generation.

Public Function GeneratePuzzle() As SudokuBoardCell(,)

  Dim context As SolverContext = SolverContext.GetContext()

  Dim model As Model

  Dim solution As Solution

 

  _board = GenerateRandomStartingPoint(3)

  model = CreateModel(context)

  solution = context.Solve()

 

  If solution.Quality <> SolverQuality.Feasible Then

    Return Nothing

  End If

 

  PopulateSolution(solution, context)

 

  Dim candidates As List(Of Integer) = New List(Of Integer)

  For i = 0 To 80

    candidates.Add(i)

  Next

 

  Dim rand As New Random()

 

  While candidates.Count > 0

    Dim pos As Integer = rand.Next(0, candidates.Count)

    Dim coordX As Integer = candidates(pos) \ 9

    Dim coordY As Integer = candidates(pos) Mod 9

    candidates.RemoveAt(pos)

    If IsRemovable(context, coordX, coordY) Then

      _board(coordX, coordY) = New _

        SudokuBoardCell(coordX, coordY, 0, _

                        _board(coordX, coordY).SolutionValue)

    End If

  End While

 

  Return _board

End Function

 

We start by randomly generating a board configuration that contains only four preset values and computing a solution from that initial board configuration.

  _board = GenerateRandomStartingPoint(3)

  model = CreateModel(context)

  solution = context.Solve()

 

Note that in CreateModel, we will use the preset numbers stored in _board via data binding. We skip the error handling case. In reality, given that we only preset three numbers, it is highly unlikely that we get an infeasible initial configuration (meaning that the initial configuration cannot be extended to a complete configuration while satisfying all Sudoku constraints).

Once the model is solved and we get a feasible solution, we can populate the values of aggregated decision variables back to _board (using Property SolutionValueDouble defined in SudokuBoardCell). This is done via the output data binding we specified in the model when we created decision _boardDec. We then use the computed solution as the starting point to generate the actual puzzle. To this end, we create a candidate list of integers that initially contains all the board cells.

  PopulateSolution(solution, context)

 

  Dim candidates As List(Of Integer) = New List(Of Integer)

  For i = 0 To 80

    candidates.Add(i)

  Next

 

Next, we will start removing numbers from the complete board configuration one by one. Remember that we will actually remove a number if and only if, after it is removed, the board configuration has exactly one way to be completed (a.k.a. exactly one feasible solution can be found after the number is removed).

In the while loop, we randomly pick a cell position from the candidate list. Then we use method IsRemovable to test if the number in that position can be removed according to the condition we just mentioned above. If so, we will put 0 in that cell as the PresetValue. Note that we still keep its original value from the solution for the checking purpose later (verifying if user fills in a correct number in this position).

  While candidates.Count > 0

    Dim pos As Integer = rand.Next(0, candidates.Count)

    Dim coordX As Integer = candidates(pos) \ 9

    Dim coordY As Integer = candidates(pos) Mod 9

    candidates.RemoveAt(pos)

    If IsRemovable(context, coordX, coordY) Then

      _board(coordX, coordY) = New _

        SudokuBoardCell(coordX, coordY, 0, _

                        _board(coordX, coordY).SolutionValue)

    End If

  End While

 

The three major sub-routines involved are GenerateRandomStartingPoint, PopulateSolution, and IsRemovable. We will now show them one by one.

Private Function GenerateRandomStartingPoint(ByVal numPrefill _

  As Integer) As SudokuBoardCell(,)

  Dim rand As New Random()

  Dim coordX As Integer

  Dim coordY As Integer

 

  For i = 0 To 8

    For j = 0 To 8

      _board(i, j) = New SudokuBoardCell(i, j)

    Next

  Next

 

  Dim values(9) As Integer

 

  If numPrefill > 8 Then

    numPrefill = 8

  End If

 

  For i = 0 To numPrefill

    For repeat = 0 To 5

      coordX = rand.Next(0, 9)

      coordY = rand.Next(0, 9)

      If board(coordX, coordY).PresetValue = 0 Then

        Dim k = rand.Next(1, 10)

        Do While values(k) <> 0

          k = k + 1

          If k = 10 Then

            k = 1

          End If

        Loop

        values(k) = 1

        _board(coordX, coordY) = New _

          SudokuBoardCell(coordX, coordY, k)

        Exit For

      End If

    Next

  Next

 

  Return _board

End Function

 

This procedure is straight-forward. We note that we do not use repeated values to avoid obvious conflict in generating the initial board configuration. This means the largest possible value of the input parameter is 8. Another place to note is that, even if the initial starting points are different, we may still end up with the same complete board configuration after the model is solved. We will not worry about this case in this sample though.

PopulateSolution procedure is defined as follows.

Private Sub PopulateSolution(ByRef sol As Solution, _

                             ByRef context As SolverContext)

  If sol.Quality = SolverQuality.Feasible Then

    context.PropagateDecisions()

 

    For i = 0 To 8

      For j = 0 To 8

        Board(i, j).PresetValue = Board(i, j).SolutionValue

      Next

    Next

  End If

End Sub

 

We only perform this operation if the solution found is feasible. As we mentioned earlier, we set all cells’ PresetValue to their SolutionValue (rounded version of SolutionValueDouble).

Finally, IsRemovable method is defined as follows.

Private Function IsRemovable(ByRef context _

  As SolverContext, ByVal coordX As Integer, _

  ByVal coordY As Integer) As Boolean

  Dim value As Integer = _board(coordX, coordY).PresetValue

  _board(coordX, coordY) = New SudokuBoardCell( _

    coordX, coordY, 0, _

    _board(coordX, coordY).SolutionValue)

 

  Dim solution As Solution = context.Solve()

  solution.GetNext()

 

  Dim removable As Boolean = (solution.Quality = _

    SolverQuality.Infeasible)

 

  If Not removable Then

Board(coordX, coordY) = New _

  SudokuBoardCell(coordX, coordY, value, value)

  End If

 

  Return removable

End Function

 

We first record the original PresetValue in the cell (coordX, coordY). Then we change its PresetValue to 0. Next we call context.Solve() and solution.GetNext() . The call to context.Solve() should always return a feasible solution as we started from a valid complete board configuration. The call to solution.GetNext() tries to compute a second feasible solution. If there is no other feasible solution, SFS will set the Quality of the Solution instance to Infeasible. So we will decide if the PresetValue of the cell should be set to 0 based on solution.Quality.

That’s all we need to generate a random Sudoku puzzle. Pretty easy, isn’t it? It’s worth pointing out that, IsRemovable method is called repeatedly in the main loop. Each time it is called, we need to solve the Sudoku model with a different preset board configuration. By using Solver Foundation Services and its data binding feature, we actually have separated the model from its input data, in this case the preset board configuration. So every time we want to solve the Sudoku model with a different preset board configuration, we do not need to reconstruct the model. The only thing we need to do is to change the data source to which the Parameter instances are bound to in the model. In this example, we only need to change PresetValue (the property to which the Parameter instance is bound to) of the given cell. In other words, the model is generic to all preset board configurations. The separation of model from data is really convenient in such scenarios where the model remains the same while the input data change from time to time.

In the next and the final post, we will briefly show how to build a new Ribbon tab in Excel for this add-in and how to trigger the Sudoku puzzle generation.

- Lengning Liu

Arithmetics on indexes in OML

Here is the link to the original post (thank Shahar for posting the trick). The trick to make arithmetics valid on Decision/Parameter indexes in OML is to declare the Sets on which the indexes are defined as Integers. Here is the OML model that illustrates how arithmetics is done.

Model[

  //define the set as an Integer

  Parameters[Sets[Integers], Month],

  Parameters[Reals, param[Month]],

 

  Decisions[Reals, decision[Month]],

 

  // You can use FilteredForeach if you want to make sure you don't get non existing month

  Constraints[

    FilteredForeach[{i,Month}, i > 1, decision[i] * param[i] >= decision[i - 1] ]

  ]

]

Parameter "param" is bound to the spreadsheet that has the following table.

Month

param

1

20

2

21

3

22

4

23

5

24

6

25

7

26

8

27

9

28

10

29

11

30

12

31

The Excel workbook of this sample is available as the attachment of this post.

- Lengning Liu

Use Scalar Parameters for SFS Data Binding in F#

Parameters in Solver Foundation Services (SFS) provide a convenient way of separating models from their input data. In the MSF-SFSProgrammingPrimer doc included in the release, we have the following example:

Sample 4: Using Parameters for LINQ Data Binding

Often we will want to iteratively run the same model with different data parameters.  Instead of recreating the same model again using different data, you can use a Parameter object.  The Parameter is bound to a database by LINQ, or to any other object which implements IEnumerable<T>.  The current values are pulled from the data source each time the model is run.

SolverContext context = SolverContext.GetContext();

Model model = context.CreateModel();

 

Decision vz = new Decision(Domain.RealNonnegative, "barrels_venezuela");

Decision sa = new Decision(Domain.RealNonnegative, "barrels_saudiarabia");

model.AddDecisions(vz, sa);

// A parameter represents an input to the model

Parameter maxvz = new Parameter(Domain.RealNonnegative, "maxproduction_venezuela");

Parameter maxsa = new Parameter(Domain.RealNonnegative, "maxproduction_saudiarabia");

 

// A parameter is bound to a data source using LINQ

maxvz.SetBinding(from row in ProductionCapacity where row.Country == "Venezuela" select row, "MaxProduction");

maxsa.SetBinding(from row in ProductionCapacity where row.Country == "Saudi Arabia" select row, "MaxProduction");

model.AddParameters(maxvz, maxsa);

 

// We use parameters instead of literal numbers to represent the maximum

// production

model.AddConstraints("limits",

  0 <= vz <= maxvz,

  0 <= sa <= maxsa);

model.AddConstraints("production",

  0.3 * sa + 0.4 * vz >= 2000,

  0.4 * sa + 0.2 * vz >= 1500,

  0.2 * sa + 0.3 * vz >= 500);

model.AddGoal("cost", GoalKind.Minimize,

  20 * sa + 15 * vz);

 

 

Solution solution = context.Solve(new SimplexDirective());

Report report = solution.GetReport();

Console.WriteLine("vz: {0}, sa: {1}", vz, sa);

Console.Write("{0}", report);

Here maxvz and maxsa are two parameters whose values come externally. The model does not need to change when it accepts different values for the two parameters.

In this example, the two parameters are scalar parameters as they do not involve any index sets. Note that the signature of Parameters.SetBinding is as follows:

// Binds the parameter to data. Each parameter must be bound

// before solving.

// The data must be specified as a sequence of objects, where

// each object has properties for the value and index(es) of

// the data element. The data is read each time Context.Solve()

// is called.

public void SetBinding<T>(IEnumerable<T> binding,

                          string valueField,

                          params string[] indexFields);

When there is no indexFields specified, the parameter instance is scalar.

Now the problem in F# is that, F# does not recognize the params attribute in this method and requires any call to Parameter.SetBinding to have three parameters being passed in. Here is a trick to define scalar parameters in F#. The sample code is shown below.

#light

open System

open System.Runtime.InteropServices

open Microsoft.SolverFoundation.Common

open Microsoft.SolverFoundation.Services

 

/// Need a simple class to hold the value

type RowBound = { Value:double }

 

do

    let context = SolverContext.GetContext()

    let model = context.CreateModel()

   

    let rational x = Rational.op_Implicit(x:float)

    let constant x = Term.op_Implicit(x:float)

    let maxbound(t,x) = Model.LessEqual([|t; x|])

    let minbound(t,x) = Model.GreaterEqual([|t; x|])

   

    /// Data source for Parameters

    let constant1 = [| { Value = 430.0 } |]

    let constant2 = [| { Value = 460.0 } |]

    let constant3 = [| { Value = 420.0 } |]

   

let boundC1 = Parameter(Domain.RealNonnegative,

                        "boundC1")

/// The trick is to use an empty array [| |]

/// as the third parameter.

    boundC1.SetBinding(constant1, "Value", [| |])

    model.AddParameter(boundC1)

   

let boundC2 = Parameter(Domain.RealNonnegative,

                      "boundC2")

    boundC2.SetBinding(constant2, "Value", [| |])

    model.AddParameter(boundC2)

 

let boundC3 = Parameter(Domain.RealNonnegative,

                        "boundC3")

    boundC3.SetBinding(constant3, "Value", [| |])

    model.AddParameter(boundC3)

 

    let x = Decision(Domain.Real, "x")

    let y = Decision(Domain.Real, "y")

    let z = Decision(Domain.Real, "z")

   

    model.AddDecision(x)

    model.AddDecision(y)

    model.AddDecision(z)   

let AddConstraint = model.AddConstraint("x>=0",

                    minbound(x, constant 0.0))

let AddConstraint = model.AddConstraint("y>=0",

                    minbound(y, constant 0.0))

let AddConstraint = model.AddConstraint("z>=0",

                    minbound(z, constant 0.0))

 

let AddGoal = model.AddGoal("goal", GoalKind.Maximize,

  constant 3.0 * x + constant 2.0 * y + constant 5.0 * z)

 

let AddConstraint = model.AddConstraint("c1",

  maxbound(constant 1.0 * x + constant 2.0 * y +

           constant 1.0 * z, boundC1))

let AddConstraint = model.AddConstraint("c2",

  maxbound(constant 3.0 * x + constant 2.0 * z, boundC2))

let AddConstraint = model.AddConstraint("c3",

  maxbound(constant 1.0 * x + constant 4.0 * y, boundC3))

 

    let sd = SimplexDirective()

    sd.Algorithm <- SimplexAlgorithm.Dual

    sd.GetSensitivity <- true

    let sol = context.Solve([|(sd :> Directive)|])

    let report = sol.GetReport()

   

    Console.WriteLine(report)

Note that the use of Parameter instances in this example is especially useful because, without the use of Parameters, we will not get shadow pricing in the sensitivity analysis from SFS because in general there is no one-to-one correspondence between a row in the linear model and a constraint in SFS (a constraint in SFS could be aggregated).

 - Lengning Liu

More Posts Next page »
 
Page view tracker