Constraint Solver Part 6: Backtrack and Sum constraint

Series: Constraint Solver in Julia

This is part 6 of the series about: How to build a Constraint Programming solver?

We talked a while about one special constraint type: alldifferent and there especially about sudokus.

Let’s get into the next constraint type in this blog and also a small change in backtracking.

Backtracking

First about backtracking. At the moment we use recursion and it’s kind of the obvious choice for it but it has its limitations regarding recursion limit and I normally don’t find it easy to debug.

The first part of this post is how to change it in a way that we don’t use recursion anymore.

There are some ways of doing this:

  • Building an actual tree structure
  • Building a queue which shows the leaf nodes of a tree
  • Basically do the same as recursion but without it

The first one has the obvious disadvantage that we have to save everything but it’s easy to do everything for example not only Depth-First-Search (DFS).

I use the second option in Juniper.jl as we normally use Best-First-Search (BFS) there and we don’t need to store the tree structure. In that case the information of each node is quite small. Only the bounds of each variable so we split a node into two branches like x <= 2 and x > 2. There is no constraint propagation in the sense as we have it here.

In this project we would basically need to save the whole search space for each node which is too much I think.

Therefore I choose option 3. This limits us to DFS which is fine as long as we only deal with satisfiable problems I think.

Okay so how do we do it?

I created a backtrack object which defines what we have done in that particular node or to be more precise any information we need to remove if we need to backtrack:

mutable struct BacktrackObj
    variable_idx        :: Int
    pval_idx            :: Int
    pvals               :: Vector{Int}
    constraint_idx      :: Vector{Int}
    pruned              :: Vector{Vector{Int}}

    BacktrackObj() = new()
end

We want to know which variable we fixed, which possible values this variable had when we started and in which iteration we are in that process. Additionally if we pruned some values we want to know which constraint was involved because we need the indices and then how many values we pruned.

Remember: pruned in ConstraintOutput is just a vector of numbers which show how many values we removed from a variable.

If we call backtrack! we have:

pvals = values(com.search_space[ind])
backtrack_obj = BacktrackObj()
backtrack_obj.variable_idx = ind
backtrack_obj.pval_idx = 0
backtrack_obj.pvals = pvals
backtrack_vec = BacktrackObj[backtrack_obj]

so simple fill the object and at the moment we didn’t prune anything so we leave constraint_idx and pruned empty.

while length(backtrack_vec) > 0
    backtrack_obj = backtrack_vec[end]
    backtrack_obj.pval_idx += 1
    ind = backtrack_obj.variable_idx

We’ve set pval_idx to 0 before so we increase it…

Turing.jl Performance Updates

By: Dean Markwick's Blog -- Julia

Re-posted from: https://dm13450.github.io/2019/09/20/Turing-Update.html

One of the creators of the Turing.jl package messaged me a few weeks ago to ask me to rerun my benchmarking tests of the different samplers because they had made some performance improvements. So here I am, being the independent third party to verify these improvements. I hope I can see them and I don’t want to ruin anyone’s Friday night!

In my last blog post (here) I didn’t actually record what version of Julia or Turing I was using which is a bit annoying. I’m 99% certain I was using Julia 1.1 and looking at the Turing releases I imagine it might have been version 0.6.14. My current version of Turing is 0.6.17, but I think I might have updated it at some point after the original blog post so a bit tricky to tell what was used. My plan for this rerun is to update Julia to 1.2 and Turing to the latest version in the package manager, rerun the notebook and post the results.

So after doing exactly that here are the results.

svg

A stunning improvement and quite frankly I’m seriously impressed. Looking at my previous post, NUTS was the slowest with almost 5 seconds for 1000 iterations. Using the latest version of Turing and you get an average of about 0.4 seconds for 1000 iterations so a 10x speed up. Not bad at all! So well done to the Turing team, really knocked it out the park with these latest updates.

In conclusion it looks like Julia 1.2 and Turing 0.6.23 have resulted in a massive performance update for the package and I’m looking forward to where it goes next.

Profiling tool wins and woes

By: n JuliaLang - The Julia programming language n

Re-posted from: https://julialang.org/blog/2019/09/profilers/index.html

Profiling tools are awesome. They let us see what actually is affecting our program performance. Profiling tools also are terrible. They lie to us and give us confusing information. They also have some surprisingly new developments: brendangregg's often cloned flamegraphs tool was created in 2011! So here I will be investigating some ways to make our profile reports better; and looking at ways in which they commonly break, to raise awareness of those artifacts in the reports. Instruments.app bad result