Constraint Programming
en.wikipedia.org
en.wikipedia.org
I learned about it when a friend gave me a programming challenge: https://gcanyon.wordpress.com/2009/10/28/a-programming-puzzl... She was going to work in PHP, I wrote a solution in J, and a commenter solved it in MiniZinc. Here is their solution in MiniZinc: http://www.hakank.org/minizinc/einav_puzzle.mzn
this is no fault of MiniZinc, I simply thought that an SMT solver would be sufficient and it wasn't. I need a mixed-integer or non-linear program solver, and ended up approaching it with a computer algebra system to generate my models and passing it to Gurobi or HiGHS.
tl;dr there's different types of hard, and SMT solvers are only good at some types of hard.
""" CP-SAT is a discrete optimization solver built on top of a SAT engine. It is available within the OR-Tools open-source repository (website: https://developers.google.com/optimization, github repository: https://github.com/google/or-tools). It has won multiple gold medals at the MiniZinc challenge (https://www.minizinc.org/challenge.html) since its debut in 2017.
The CP-SAT solver is architectured around five components: The base layer is a clause learning SAT solver. Above the SAT layer sits a Constraint Programming (CP) module with Boolean, integer and interval variables, and standard integer, scheduling and routing constraints. Alongside the CP solver, a simplex provides a global linear relaxation. Its integration with the CP and SAT layers enable the CP-SAT solver to solve MIP problems with the same techniques as (commercial) MIP solvers: relaxation, cuts, heuristics and duality based techniques. Both the CP and MIP modules rely on a unified protobuf representation of the model that can serve as a file format, as well as an intermediate representation of the model during all phases of the solve (input format, presolved model, LNS fragment). On top, the search layer implements a robust information-sharing portfolio of specialized workers that offers both good and fast solutions, and superior optimality proving capabilities.
This work was presented at the CPAIOR 2020 masterclass. The recording is available on youtube (https://www.youtube.com/watch?v=lmy1ddn4cyw). """
A really nice thing with MiniZinc is that the same model can be used with both CP silvers like OR-Tools and MIP solvers like CPLEX and Gurobi.
In general, I think MiniZinc is great precisely because it is a structured way to generate a list of constraints, since this makes it possible to integrate into a variety of solvers. Sure, sometimes the generation and the interchange format can be a bit heavy and I would like better ways to manage that, but it is often still worth it IMHO.
The fact that the language is based on a relational semantics with the unfolding into a list of constraints as a model makes it possible to understand how predicates, nested Boolean contexts, reification, undefined expressions, and local variables all work in conjunction. It is a hard enough problem to solve as is, and I think adding semantic complexity on top of it would make it too hard to do.
I can't use the Gurobi support, sadly, since my Gurobi instance is on another box (where I don't have MMA), and I needed to benchmark against HiGHS, CBC etc.
Here are some other constraint modelling implementations of this problem: http://hakank.org/common_cp_models/#einavpuzzle .
Edit to add: holy moses, all those solutions! I need to reach out to Einav to let her know she is famous :-)
It has very good documentation, reasonable IDE and AFAIK three great courses on Coursera for beginners. I have been teaching it myself[1] and all my students were amazed how quickly one can develop a working prototype for real-life industrial problems.
[1] https://gitlab.com/agh-courses/2021-2022/constraint-programm...
For non linear models:
* Gecode (included in the MiniZincIDE distribtion)
* JaCoP (https://github.com/radsz/jacop)
* OptiMathSAT (https://optimathsat.disi.unitn.it/pages/fznreference.html)
For linear models:
* CBC (included in the MiniZincIDE distribution)
Also, I would encourage you to generate some interesting instances and submit to the MiniZinc challenge (last years call for problems: https://www.minizinc.org/challenge2022/call_for_problems.htm...). It is a good way to get your problems into the hands of solver developers.
Our current model is here https://github.com/draperlaboratory/VIBES/blob/main/resource... We don't have any parameter files committed to the repo, they are generated by the compiler. It has been on my todo list for a while to completely refactor this model. Currently, the constraint solve can take 10s on our hardest problems so far, which would be nice to get down, but not our biggest blocker.
Even if 10 seconds is often fast enough for solving a problem, I can imagine that it would be good to get down. From the code I guess that you use Chuffed, have you also tested other solvers? OR-Tools with parallel solving feels like the standard thing to try. I can also imagine that the time will start to go up significantly if larger patches are specified, but perhaps that is not a very common use-case.
Having some example instances for ease of testing would be fun, and make it easier for a drive-by constraint programmer to try it out. Hoping to see it in the competition next year. Problems that are mostly reasonably fast to solve can still be interesting IMHO, especially when they are probably only fast because some solvers (Chuffed/OR-Tools) have very good automatic heuristics.
I've been trying to build a programming language for UI designers, and one of the goals is to let designers describe layouts using constraint-based code that reads as close to natural language as possible.
Something like:
// "the blue box is directly beneath the red box"
BlueBox {
top: @RedBox.bottom
}
// "the red box is 10 pixels to the right of the blue box
RedBox {
left: @BlueBox.right + 10
}For example https://norvig.com/sudoku.html uses constraint propagation to solve sudoku puzzles in Python.
If you give me a choice between implementing a solver in an imperative language or a website in Prolog, I'll choose writing the solver. Every time.
Problems in the context of a programming environment where you're already using other languages. Introducing a specialized one does not necessarily make much sense.
if the problem is simple to describe, then sure, write a solver or use a binding. I'm just used to the problems being pretty intense and sophisticated.
I've found having a REPL in a symbolic (term-rewriting) language to be invaluable, and can't imagine going back to for-loops and endless maps of symbol tables chugging out text files for debugging.
Why would I use an external language instead of a library in my existing language? It really isn't as hard as you indicate.
For example, to optimize the grid, you might have a Python app that imports the GUROBI solver module and tells GUROBI to optimize. The advantage is all the file IO, directory, and database stuff gets to be handled with Python (easy easy).
https://www.amazon.de/-/en/HARIDI-SEIF/dp/8120326857
It is using a back then and even more so today obscure programing language Oz with it' sole implementation Mozart http://mozart2.org/
I created a short introductory video to the code as well. [2]
Backtracking solvers for constraint satisfaction problems (CSPs) are a subset of the world of constraint programming, as described in the linked Wikipedia article.
0: https://classicproblems.com
1: https://freecontent.manning.com/constraint-satisfaction-prob...
Constraint Programming "clicks" for me a lot more than SAT solvers, which feel more mystical. BDDs and MDDs also deserve a mention, as BDDs kinda solve the #P-complete problem (counting-NP complete problem: not only finding one valid solution, but the count of all valid solutions to an NP complete situation. Many optimization problems are #P complete, as you want to iterate over all of the valid solutions and find the best one.
Any recommendations on a "this but for dribbling primates" introduction?
Simplex is provably within the polynomial bounds of complexity, ie: simplex is a so called "easy" problem in complexity theory.
--------
You're right that simplex is applied to optimization problems. Constraint programming could be seen as a "more generic" optimizer, in that it can handle integer-optimization (simplex cannot handle integer optimization at all).
Of course, integer-optimization is NP complete, which means that constraint programming is innately "inefficient" or "hard" in terms of complexity.
Constraint programming is very broad — it encompasses many types of problems. Some of them are in P, some (most?) are in NP. In principle you can use the simplex algorithm for solving certain constraint programming problems, but mostly when people talk about constraint programming they refer to different techniques.
IE: Knapsack problem, Traveling Salesman, etc. etc.
Today, it seems more popular to use "3-SAT Solvers". But constraint programming is "more logical" for some problems to get converted into rather than 3-SAT. Consider 3-SAT and Constraint Programming to be two different "assembly languages" that you can compile your problem into, and then have a generic solver into.
-----------
For example: Sudoku can be "compiled" into the 3SAT problem: https://www.mit.edu/~6.005/sp12/psets/ps2/ps2.html
But it also can be "compiled" into the Constraint-problem, which then gets solved by a constraint solver. https://sonalake.com/latest/constraint-programming-solving-s...
------------
Personally speaking, I think that the constraint solver problem is "easier to compile into" rather than the 3SAT problem, especially when you consider generic constraints that have very efficient solutions.
For example, it is difficult to think of the optimal alldifferent constraint within 3SAT, but in a constraint-solver framework, its just... well... alldifferent. So you can describe Sudoku as the following:
Domain = {1 2 3 4 5 6 7 8 9}
Alldifferent(X11, X12, X13, X14, X15, X16, X17, X18, X19)
Alldifferent(X21, X22, X23, X24, X25, X26, X27, X28, X29)
Alldifferent(X31, X32, X33, X34, X35, X36, X37, X38, X39)
...
Alldifferent(X91, X92, X93, X94, X95, X96, X97, X98, X99)
------------------------------------------------------------------------
Alldifferent(X11, X21, X31, X41, X51, X61, X71, X81, X91)
Alldifferent(X12, X22, X32, X42, X52, X62, X72, X82, X92)
Alldifferent(X13, X23, X33, X43, X53, X63, X73, X83, X93)
...
Alldifferent(X19, X29, X39, X49, X59, X69, X79, X89, X99)
------------------------------------------------------------------------
Alldifferent(X11, X12, X13, X21, X22, X23, X31, X32, X33)
...
Alldifferent(X77, X78, X79, X87, X88, X89, X97, X98, X99)
That is: numbers are 1 through 9, and constrain the problem such that each row is alldifferent, and each column is all different, and each "3x3 square" is all different.----------
IIRC, the Alldifferent constraint can be efficiently solved using maximum flow. So you write a specialized solver for alldifferent using maximum flow (faster than 3SAT / NP Completeness), and this maximum-flow local solution can be "plugged into" the rest of the constraint solver problem, and integrates cleanly with the rest of the solver's NP-completeness search.
There are also intermediate representations. For instance, pseudo-boolean retains most of the power of SAT solvers, while at the same time making problems much easier to express in terms of number of constraints needed. For instance, your Alldifferent(X11, X12, X13, X14, X15, X16, X17, X18, X19) constraint would be written as "x11 + x12 + x13 + x14 + x15 + x16 + x17 + x18 + x19 = 1". Same expressive power, on this problem.
I'm more comfortable with constraint-programming than 3SAT / SMT / etc. etc. So I'm probably getting something wrong here.
I don't think you're correct on this issue? "x11 + x12 = 1" means that "x11 or x12 are 1, and the other is zero".
EDIT: That is, the 9-set of solutions you described is:
{1, 0, 0, 0, 0, 0, 0, 0, 0}
{0, 1, 0, 0, 0, 0, 0, 0, 0}
{0, 0, 1, 0, 0, 0, 0, 0, 0}
{0, 0, 0, 1, 0, 0, 0, 0, 0}
{0, 0, 0, 0, 1, 0, 0, 0, 0}
{0, 0, 0, 0, 0, 1, 0, 0, 0}
{0, 0, 0, 0, 0, 0, 1, 0, 0}
{0, 0, 0, 0, 0, 0, 0, 1, 0}
{0, 0, 0, 0, 0, 0, 0, 0, 1}
Meanwhile, Alldifferent(X11, X12, X13, X14, X15, X16, X17, X18, X19) represents the following 9-factorial set of solutions: {1, 2, 3, 4, 5, 6, 7, 8, 9}
{1, 2, 3, 4, 5, 6, 7, 9, 8}
{1, 2, 3, 4, 5, 6, 9, 8, 7}
{1, 2, 3, 4, 5, 6, 9, 7, 8}
{1, 2, 3, 4, 5, 9, 6, 7, 8}
{1, 2, 3, 4, 5, 9, 6, 8, 7}
{1, 2, 3, 4, 5, 9, 7, 6, 8}
{1, 2, 3, 4, 5, 9, 7, 8, 6}
{1, 2, 3, 4, 5, 9, 8, 6, 7}
{1, 2, 3, 4, 5, 9, 8, 7, 6}
....
{9, 8, 7, 6, 5, 4, 3, 2, 1}
That is, the entire set of permutations of the domain of all 9 variables participating in the constraint. What I've listed above is a *complete* Sudoku solver within the constraint-programming mindset.--------
In most of the 3SAT / SMT solvers for Sudoku I've seen, you set up the variables as: "X111, X112, X113, X114...", which means "X11 == 1", and "X11 == 2", etc. etc. So you then do "X111 + X112 + X113... X119 == 1", meaning "X11 is either 1, 2, 3, 4, 5... 9".
Meanwhile, this entire process is simply "Domain of X11 = {1, 2, 3, 4, 5, 6, 7, 8, 9}" in the constraint-processing world, because variables can have arbitrarily sized (though finite) domains in constraint programming.
That is to say, I'm pretty sure you need 999 == 729 variables to brute-force represent the problem in 3SAT / SMT? Meanwhile, Constraint Solver trivially represents it with 81-variables (but each of these 81 variables, X11 through X99, have a domain of size 9: {1, 2, 3, 4, 5, 6, 7, 8, 9}).
There might be an easier way to represent Sudoku in 3SAT/SMT world, but again, I'm not very good with that world. I "just" know that 3SAT / SMT is a "similar but different" kind of solver and barely have used them.
--------
One can argue that "constraint programming" is simply 3SAT / SMT-solving except with arbitrarily sized domains. Rather than solely using 0 and 1 or boolean functions.
I've been told that "because everything is {0, 1}, more optimizations / inferences can be discovered" in 3SAT/SMT solvers compared to constraint programmers. Meanwhile, constraint programmers benefit from generic constraints that have a polynomial class subsolution (ie: using maximum flow, or other simpler algorithms to solve the subproblem).
It'd take a rather well-studied expert to know whether 3SAT/SMT is better for a particular problem vs constraint-programming.
and
> In most of the 3SAT / SMT solvers for Sudoku I've seen, you set up the variables as: "X111, X112, X113, X114...", which means "X11 == 1", and "X11 == 2", etc. etc. So you then do "X111 + X112 + X113... X119 == 1", meaning "X11 is either 1, 2, 3, 4, 5... 9".
Yes, I've been a bit to fast here. You're absolutely right.
> I've been told that "because everything is {0, 1}, more optimizations / inferences can be discovered" in 3SAT/SMT solvers compared to constraint programmers. Meanwhile, constraint programmers benefit from generic constraints that have a polynomial class subsolution (ie: using maximum flow, or other simpler algorithms to solve the subproblem).
Yes. SAT solvers very often outperform CSP solvers nowadays. But SAT solvers tend to lose the structure of the problem because they are so low-level. The most famous problem they fail to solve is the pigeonhole problem.
If you have, say, the problem "is there a way to put 4 pigeons into 3 different holes, knowing that there can be at most 1 pigeon per hole and at least 1 hole per pigeon?", the answer is obviously "no", but if you try to use a propositional logic representation (in other words, using a SAT solver), the solving track will grow exponentially (4 vs 3 workds well, but SAT solvers will struggle to solve 11 vs 10 in a reasonable amount of time). Pseudo-boolean solvers will be able to using "cutting-planes" strategies that work well for this specific set of problems, but these are slower in the general case.
Plus, modeling your problem in CNF to feed it to a SAT solver is usually incredibly complex and can lead to gigantic formulas if you're not careful. So most of the time, it really makes sense to first use a CSP representation, and fall back to a CNF representation if you're not satisfied with the results for some reason.