Typical example

open System
open Operations.Research.Types
open Operations.Research.Models
open Operations.Research.Solvers.Google.Linear

let x = Variable.real "x" 0.0 Double.PositiveInfinity |> toExpression
let y = Variable.real "y" 0.0 Double.PositiveInfinity |> toExpression


let mdl =
  Model.Default
  |> DecisionVars [x; y]
  |> Goal Maximize
  |> Objective  (6*x + 2*y + 77)
  |> Constraints [
    3*x + 1*y <== 48
    3*x + 4*y <== 120
    3*x + 1*y >== 36
  ]

let result = SolveWithCustomOptions mdl SolverOptions.Default

match result with
| Solution sol ->
    printfn "Objective: %i" (sol.Objective.toInt)
    printfn "%s: %f" (x.var().Name) (sol.Variables.[x.var().Name])
    printfn "%s: %f" (y.var().Name) (sol.Variables.[y.var().Name])
| Error e ->
    printfn "%A" e

Non-Standard Forms

The following is a problem with a set of constraints in various forms. The first methods solves this in the usual fashion.

Minimize \$-3x_0 + x_1 + x_2\$

subject to \$x_0 - 2x_1 + x_2 <= 11\$ \$-4x_0 + x_1 + 2x_2 >= 3\$ \$-2x_0 - x_2 = -1\$

where \$x_i >= AA i\$

let x0 = Variable.real "x0" 0.0 Double.PositiveInfinity |> toExpression
let x1 = Variable.real "x1" 0.0 Double.PositiveInfinity |> toExpression
let x2 = Variable.real "x2" 0.0 Double.PositiveInfinity |> toExpression

let mdl =
  Model.Default
  |> DecisionVars [x0; x1; x2]
  |> Goal Minimize
  |> Objective  (-3*x0 + x1 + x2)
  |> Constraints [
    x0 + (-2*x1) + x2 <== 11
    (-4*x0) + x1 + (2*x2) >== 3
    (-2*x0) +  (-1*x2) === -1
  ]

let result = SolveWithCustomOptions mdl SolverOptions.Default

match result with
| Solution sol ->
    printfn "Objective: %i" (sol.Objective.toInt)
    printfn "%s: %f" (x0.var().Name) (sol.Variables.[x0.var().Name])
    printfn "%s: %f" (x1.var().Name) (sol.Variables.[x1.var().Name])
    printfn "%s: %f" (x2.var().Name) (sol.Variables.[x2.var().Name])
| Error e ->
    printfn "%A" e

The issue with this form is that we don’t have access to the slack/surplus variables for inspection. To do this, we explicitly create them and add to decision variables so that they can be included in final result. This time we enter the problem in matrix form.

let x0 = Variable.real "x0" 0.0 Double.PositiveInfinity |> toExpression
let x1 = Variable.real "x1" 0.0 Double.PositiveInfinity |> toExpression
let x2 = Variable.real "x2" 0.0 Double.PositiveInfinity |> toExpression

let s0 = Variable.real "s0" 0.0 Double.PositiveInfinity |> toExpression
let s1 = Variable.real "s1" 0.0 Double.PositiveInfinity |> toExpression


let m = [
  [1.0 ; -2.0 ; 1.0 ; 1.0 ; 0.0];
  [-4.0 ; 1.0 ; 2.0 ; 0.0 ; -1.0];
  [-2.0 ; 0.0 ; -1.0 ; 0.0 ; 0.0];
]

let eq = [11.0; 3.0; -1.0]

let mdl =
      Model.Default
      |> DecisionVars [x0; x1; x2; s0; s1]
      |> Goal Minimize
      |> Objective  (-3*x0 + x1 + x2)
      |> MatrixEq m eq

let result = Solve mdl

match result with
| Solution sol ->
    printfn "Objective: %i" (sol.Objective.toInt)
    printfn "%s: %f" (x0.var().Name) (sol.Variables.[x0.var().Name])
    printfn "%s: %f" (x1.var().Name) (sol.Variables.[x1.var().Name])
    printfn "%s: %f" (x2.var().Name) (sol.Variables.[x2.var().Name])

    printfn "%s: %f" (s0.var().Name) (sol.Variables.[s0.var().Name])
    printfn "%s: %f" (s1.var().Name) (sol.Variables.[s1.var().Name])
| Error e ->
    printfn "%A" e

The Diet Problem

The following is a variation of Stigler’s diet problem written by Hakan Kjellerstrand. It was originally a mathematical exercise where nutritional needs can be met by a set of foods/products.

Each product will have a set of characteristics for salt, sugar, fat, and number of calories per product. The model defines variables for the amount of each product that needs to be consumed to meet daily nutritional requirements.

// products
let A = Variable.integer  "A" 0 100 |> toExpression
let B = Variable.integer  "B" 0 100 |> toExpression
let C = Variable.integer  "C" 0 100 |> toExpression
let D = Variable.integer  "D" 0 100 |> toExpression


let mdl =
  Model.Default
  |> DecisionVars [A; B; C; D]
  |> Goal Minimize
  |> Objective (50*A + 20*B + 30*C + 80*D)
  |> Constraints [
        400*A + 200*B + 150*C + 500*D >== 500;  // calories with minimum required intake
        3*A + 2*B  >== 6; // salt with minimum required intake
        2*A + 2*B + 4*C + 4*D >== 10; // sugar with minimum required intake
        2*A + 4*B + C + 5*D >== 8; // fat with minimum required intake
      ]

let result = Solve mdl

match result with
| Solution sol ->
    printfn "%s: %f" (A.var().Name) (sol.Variables.[A.var().Name])
    printfn "%s: %f" (B.var().Name) (sol.Variables.[B.var().Name])
    printfn "%s: %f" (C.var().Name) (sol.Variables.[C.var().Name])
    printfn "%s: %f" (D.var().Name) (sol.Variables.[D.var().Name])

| Error e ->
    printfn "%A" e

Note that there is also a cost associated with each product and that is set as the objective. We want to stay healthy as cheaply as possible.