Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# Or-Tools Sat Solver

Tags:

f#

or-tools

I am experimenting with F# and want to do some constraint programming where I use Or-Tools. I have previously used the package together with Python, but I can’t get it to work with F#.

I follow the C# example: https://developers.google.com/optimization/cp/cp_solver#c_5

But get an error when try to add a constraint: enter image description here

like image 805
SCOUT Avatar asked Mar 25 '26 21:03

SCOUT


1 Answers

So this is kind of annoying, but it's how consuming operators that were overloaded in C# from F# works.

The reason why you couldn't use != like this is because:

  1. The operator != is (and this is unusual) overloaded in C# as a static operator on the LinearExpr class.
  2. The operator != compiles down to op_Inequality, but op_Inequality in F# is <>, not !=
  3. F# already defines <> as a generic operator that accepts any member that satisfies the equality constraint, which LinearExpr does
  4. The defined operator <> resolves correctly, and produces a bool, which is incompatible with model.Add because it doesn't expect a bool

The solution is to explicitly qualify your access to the operator like so:

LinearExpr.(<>) (x, y)

Note that because it takes tupled arguments in its definition, you must also tuple your arguments and you can't use it like a "normal" operator.

Here is the full F# solution, with a few small tweaks to make it idiomatic:

#r "nuget: Google.OrTools"

open Google.OrTools.Sat

let model = CpModel()

// Creates the variables.
let num_vals = 3L;

let x = model.NewIntVar(0L, num_vals - 1L, "x")
let y = model.NewIntVar(0L, num_vals - 1L, "y")
let z = model.NewIntVar(0L, num_vals - 1L, "z")

// Creates the constraints.
model.Add(LinearExpr.(<>) (x, y))

// Creates a solver and solves the model.
let solver = CpSolver();
let status = solver.Solve(model)

if status = CpSolverStatus.Optimal then
    printfn $"x = {solver.Value(x)}"
    printfn $"y = {solver.Value(y)}"
    printfn $"z = {solver.Value(z)}"

A way to make this a bit nicer from F# is to define a module of operators that map to the LinearExpr operators like so:

module LinearExprOperators =
    let ( ^<> ) (x: LinearExpr) (y: LinearExpr) = LinearExpr.(<>) (x, y)
    let ( ^= ) (x: LinearExpr) (y: LinearExpr) = LinearExpr.(=) (x, y)

Then you can use those operators instead. Another annoyance is that it appears that + and - and * work just fine, because the F# type doesn't produce a different type like bool.

So in short, this particular API is a little annoying to use from F#.

like image 189
Phillip Carter Avatar answered Mar 31 '26 12:03

Phillip Carter



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!