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.