Constraint Solver: How to use JuMP for your own solver?

First of all: Happy new year to everyone!

This is kind of part 15 of the series about: How to build a Constraint Programming solver?
But it is also interesting for you if you don’t care about constraint programming.
You can learn how to use JuMP.jl as the modelling layer for your own solver.

Anyway these are the other posts so far in the ongoing series:

All of that can only happen because of the generous support of some people.
I’m very happy that I can cover my server expenses now because of my Patrons.
To keep this going please consider supporting this blog on Patreon. If you do I will create a better/faster blog experience and you will get posts like this earlier! Everything will be free forever but if you have some cash to spare -> This might be an option 😉

Special limited offer… I either just lost your attention or I have it now 😀
I decided to offer a 1:1 (binary) talk where you can ask me about my work and just chat with me if you’re willing to join the Patron 4$ club. 😉 This offer is until the 10th of February.

If you want to just keep reading -> Here you go 😉

Maybe I should have done that from the ground up but wasn’t sure how to and probably it was easier for the blog as well to start without a big code base. Therefore here it is now: Using JuMP.jl as the modelling layer for the constraint solver I’m creating for the past couple of months.

UserInterface change

Before I explain how it’s done I want to show how the user interface changes first:

How it was before

com = CS.init() # creating a constraint solver model
com_grid = Array{CS.Variable, 2}(undef, 9, 9)
for (ind,val) in enumerate(grid)
    if val == 0
        com_grid[ind] = add_var!(com, 1, 9)
    else
        com_grid[ind] = add_var!(com, 1, 9; fix=val)
    end
end

for rc=1:9
    #row
    variables = com_grid[CartesianIndices((rc:rc,1:9))]
    add_constraint!(com, CS.all_different([variables...]))
    #col
    variables = com_grid[CartesianIndices((1:9,rc:rc))]
    add_constraint!(com, CS.all_different([variables...]))
end

which is a bit of the sudoku part. Now it looks like this:

m = Model(with_optimizer(CS.Optimizer))
@variable(m, 1 <= x[1:9,1:9] <= 9, Int)
# set variables
for r=1:9, c=1:9
    if grid[r,c] != 0
        @constraint(m, x[r,c] == grid[r,c])
    end
end
for rc = 1:9
    @constraint(m, x[rc,:] in CS.AllDifferentSet(9))
    @constraint(m, x[:,rc] in CS.AllDifferentSet(9))
end

I think it looks a lot cleaner now but more importantly it is the standard interface for mathematical optimization solvers written in Julia.
This also means that if there will exist other constraint solvers in the future one can easily swap the…