One of my class at EPITA is about Constraint Programming. This is a technique to solve problems with

  • A huge number of possible solutions. (2^1000 is not uncommon)
  • Discrete variables (they can take a bounded number of values).

I wanted to share this method with you because it is able to solve an impressive number of problems without writing any line of code. How? Because it is declarative. You write the problem (CSP: Constraint Satisfaction Problem) and the solver is in charge of finding the solution.

Here are some basic examples. I used the trial version of IBM ILOG for all those examples.

Sudoku

The Sudoku is a perfect target for Constraint Programming.

// Variables
range Size = 0..8;
dvar int Sudoku[Size][Size] in 1..9;
 
// Constraints
subject to {
  forall(i in Size) {
    forall (j, k in Size: j != k) {
      // Lines & Columns
      Sudoku[i][j] != Sudoku[i][k];
      Sudoku[j][i] != Sudoku[k][i];
 
      // Squares
      Sudoku[(i % 3) * 3 + (j % 3)]
          [(i div 3) * 3 + (j div 3)]
      != 
      Sudoku[(i % 3) * 3 + (k % 3)]
          [(i div 3) * 3 + (k div 3)];
    }
  }
 
  // Initial Input  
  forall (i, j in Size) {
    PreSolve[i][j] != 0 => Sudoku[i][j] == PreSolve[i][j];
  }
}

Sadly, Constraint Programming allows to solve instantly all the sudokus. I chose the World Hardest Sudoku as an example.

PreSolve = [
[0 0 5  3 0 0  0 0 0]
[8 0 0  0 0 0  0 2 0]
[0 7 0  0 1 0  5 0 0]
 
[4 0 0  0 0 5  3 0 0]
[0 1 0  0 7 0  0 0 6]
[0 0 3  2 0 0  0 8 0]
 
[0 6 0  5 0 0  0 0 9]
[0 0 4  0 0 0  0 3 0]
[0 0 0  0 0 9  7 0 0]
];
 
// Solution
[1 4 5  3 2 7  6 9 8]
[8 3 9  6 5 4  1 2 7]
[6 7 2  9 1 8  5 4 3]
 
[4 9 6  1 8 5  3 7 2]
[2 1 8  4 7 3  9 5 6]
[7 5 3  2 9 6  4 8 1]
 
[3 6 7  5 4 2  8 1 9]
[9 8 4  7 6 1  2 3 5]
[5 2 1  8 3 9  7 6 4]
  • Number of branches : 8
  • Number of fails : 0
  • Total memory usage : 1.1 Mb
  • Time spent in solve : 0.02s

17x17 Challenge

I discovered the 17x17 Challenge. You have to assign one color per element of a matrix such as there is not any rectangle with all the corners of the same color.

// Variables
dvar int Board[1..N][1..N] in 1..C;
 
// Constraints
subject to {
  forall (i, j in 1..(N - 1)) {
    forall (w in 1..(N - i), h in 1..(N - j)) { 
      ! (Board[i][j] == Board[i + w][j] 
      && Board[i][j + h] == Board[i + w][j + h]
      && Board[i][j + h] == Board[i + w][j]);
    }
  }
}
// Input
int C = 14;
int N = 4;
 
// Solution 14x14
[1 1 2 4 3 3 2 1 4 4 1 3 4 4]
[2 4 1 1 2 4 3 2 1 3 4 4 4 3]
[2 3 4 3 1 2 2 3 2 3 4 2 1 4]
[4 2 3 2 3 1 4 1 3 3 2 2 4 1]
[4 2 3 3 1 3 2 4 1 2 1 4 2 4]
[1 3 3 1 1 1 4 4 2 4 4 1 3 3]
[3 2 2 4 2 4 4 1 2 3 3 1 1 2]
[3 1 1 4 4 1 1 2 3 2 4 3 1 3]
[3 3 2 1 3 4 3 4 4 2 1 2 1 1]
[1 4 3 2 2 2 4 3 1 2 3 3 1 4]
[3 1 4 1 4 3 4 3 4 2 2 4 3 2]
[1 4 4 4 2 3 3 4 3 1 3 2 2 1]
[4 4 2 3 4 2 3 2 1 4 2 3 2 1]
[4 1 4 3 3 4 2 2 1 1 3 1 3 2]
  • Number of branches : 1,586,955
  • Number of fails : 774,500
  • Total memory usage : 13.6 Mb
  • Time spent in solve : 89.40s
  • Search speed (br. / s) : 17,750.3

For grids smaller than 14x14, the result is found instantly. A 14x14 takes 1min30. And for 15x15, I let it run during 24hours without any result :( This naive approach will not give a solution any time soon for a 17x17 grid.

N-Queens

The 8-Queen problem and it's generalization to a NxN board is trivial to write in Constraint Programming.

There are three ways to code the problem. The unknown can be the board (boolean for the Queen presence) or the Queen position. A naive Queen position can be expressed with two coordinates X and Y. If we analyze the problem just a bit, we see that only one Queen can be per column. So we just store the column position of all the Queens.

The methods are sorted by speed of execution. The Board version is much slower than the Column one.

Board

dvar boolean Board[Size][Size];
 
subject to {
  forall (i in Size) {
    // One Queen per Line
    sum (j in Size) (Board[i][j]) == 1;
 
    // One Queen per Column
    sum (j in Size) (Board[j][i]) == 1;
  }
 
  forall (i, j, k, l in Size: i != k && j != l) {
    // One Queen per Diagonal
    abs(i - j) == abs(k - l) => Board[i][j] + Board[k][l] < = 1;
  }	
}

Queen

dvar int QueenX[Size] in Size;
dvar int QueenY[Size] in Size;
 
subject to {
  forall (i, j in Size: i != j) {
    // One Queen per Line
    QueenX[i] != QueenX[j];
 
    // One Queen per Column
    QueenY[i] != QueenY[j];
 
    // Diagonals
    abs(QueenX[i] - QueenX[j]) != abs(QueenY[i] - QueenY[j]);
  }
}

Column

range Size = 1..N;
 
dvar int Column[Size] in Size;
 
subject to {
  forall (i, j in Size: i != j) {
    // One Queen per Line
    Column[i] != Column[j];
 
    // Diagonal
    abs(Column[i] - Column[j]) != abs(i - j);
  }
}

Here is an example of result for a standard 8x8 board.

int N = 8;
 
|---|---|---|---|---|---|---|---|
|   |   |   |   |   | X |   |   |
|---|---|---|---|---|---|---|---|
|   |   |   | X |   |   |   |   |
|---|---|---|---|---|---|---|---|
|   | X |   |   |   |   |   |   |
|---|---|---|---|---|---|---|---|
|   |   |   |   |   |   |   | X |
|---|---|---|---|---|---|---|---|
|   |   |   |   | X |   |   |   |
|---|---|---|---|---|---|---|---|
|   |   |   |   |   |   | X |   |
|---|---|---|---|---|---|---|---|
| X |   |   |   |   |   |   |   |
|---|---|---|---|---|---|---|---|
|   |   | X |   |   |   |   |   |
|---|---|---|---|---|---|---|---|

Since the 8x8 example is instant to process, I made it run on a 100x100 board. You have to believe me to know that the result is correct :)

int N = 100;
Column = [65 42 71 32 58 90 88 93 61 19 76 56 67 89 23 10 15 60
  70 52 28 40 1 95 22 85 63 43 54 29 4 24 96 68 73 18 3 38 26 100 34
  99 17 14 6 79 37 49 51 72 62 57 59 45 78 25 94 46 86 13 77 5 35 41
  82 97 12 48 8 91 21 98 87 84 7 9 66 81 92 75 39 50 11 80 36 2 27 74
  64 20 16 47 55 30 33 31 53 69 83 44];
  • Number of branches : 30,958
  • Number of fails : 12,535
  • Total memory usage : 17.4 Mb
  • Time spent in solve : 12.17s

Send More Money

Here we try to solve the Send More Money problem. We have to find what are the unique values for S, E, N, D, M ... such as this equation is true:

   S E N D
 + M O R E
 ---------
 M O N E Y

The implementation is fairly straightforward. We use 4 more variables that will be the carries.

{string} Letters = {"S", "E", "N", "D", "M", "O", "R", "Y"};
range Digit = 0..9;
 
dvar int Values[Letters] in Digit;
dvar int Carry[1..4] in 0..1;
 
subject to {
  allDifferent (Values);
 
  Values["M"] != 0;
 
                              Carry[4] == Values["M"];
  Values["S"] + Values["M"] + Carry[3] == Values["O"] + 10 * Carry[4];
  Values["E"] + Values["O"] + Carry[2] == Values["N"] + 10 * Carry[3];
  Values["N"] + Values["R"] + Carry[1] == Values["E"] + 10 * Carry[2];
  Values["D"] + Values["E"]            == Values["Y"] + 10 * Carry[1];
}

And we get instantly the following result:

   9 5 6 7
 + 1 0 8 5
 ---------
 1 0 6 5 2

Behind the scene, the algorithm used is Branch & Bound. It uses Look-ahead to reduce the domain definition of the variables and Backjumping in order to backtrack to the first instanced variable that caused a problem. There are heuristics to know the order of the variables to instanciate. A common approach is to try first the "hardest" variables in order to remove as many branches as possible.

If you want to know more, here are some links:

If you liked this article, you might be interested in my Twitter feed as well.
 
 

Related Posts

  • October 5, 2011 -- Javascript Presentation (1)
    The talk is over. Check out the Slides & Video. For several months now I've been surveying my friends and teachers at EPITA and I came to the conclusion that they have absolutly no idea what Javascript really is. In order to help them discover a language that is getting a lot of traction n...
  • August 23, 2011 -- Javascript – Hook Technique (4)
    Let's go back 5 years ago during the World of Warcraft beta. I was working on Cosmos UI, a projects that aimed to improve the World of Warcraft interface. As interface modification was not officially supported by Blizzard, we went ahead and directly modify the game files written in Lua and XM...
  • September 24, 2011 -- Javascript: Cyclic Object Detection (3)
    URLON.stringify() suffer from a problem, when passed an object that contains a cycle, it will never stop. This article shows 3 techniques in order to detect if an object is cyclical. Edit the object: Mark In order to detect a cycle in an object, the method we learn at school is to mark each v...
  • November 5, 2011 -- Simulated Annealing Project (0)
    For a school project, I have to implement Simulated Annealing meta heuristic. Thanks to many open source web tools, I've been able to quickly do the project and have a pretty display. CoffeeScript, Raphael, Highcharts, Three.js, Twitter Bootstrap, jQuery and Web Workers. .hover-border img {...
  • October 28, 2011 -- JSPP – Morph C++ Into Javascript (Paper) (0)
    6 months ago, I wrote the blog post "JSPP - Morph C++ into Javascript". My supervisor at the LRDE (R&D Lab of EPITA), Didier Verna, found it interesting and told me that it could be worth a publication. With his great help, I've written my first article. We have submitted it to two conferences (o...