« Nonogram in Gecode/R | Main | Comet version 1.1 »

New Gecode/R models: langford, all_interval, alpha, golomb_ruler and some more

I have now started to model some of the Gecode examples, and some other things, in Gecode/R.


Langford's number problem (CSPLib problem 24


Langford's number problem is a classic problem in constraint programming. It is the problem 24 in the CSPLib collection.

The problem is defined as:


Arrange 2 sets of positive integers 1..k to a sequence,
such that, following the first occurence of an integer i,
each subsequent occurrence of i, appears i+1 indices later
than the last.
For example, for k=4, a solution would be 41312432

Also see
* John E. Miller: Langford's Problem
* Encyclopedia of Integer Sequences for the number of solutions for each k
* My MiniZinc model langford2.mzn

The Gecode/R model for this problem is langford.rb.


All interval problem (CSPLib problem 7)


Another CSP classic. CSPLib formulates the problem such:

Given the twelve standard pitch-classes (c, c#, d, ...), represented by
numbers 0,1,...,11, find a series in which each pitch-class occurs exactly
once and in which the musical intervals between neighbouring notes cover
the full set of intervals from the minor second (1 semitone) to the major
seventh (11 semitones).

...


The constraints in the Gecode/R model all_interval.rb are simply

 x_is_an int_var_array(n, 1..n)         # The sequence
diffs_is_an int_var_array(n-1, 1..n-1) # The differences
x.must_be.distinct
diffs.must_be.distinct
(0..n-2).each{|k| diffs[k].must == (x[k+1] - x[k]).abs }

And two symmetry breaking constraints which, for n=12, which reduces the number of unique solution from 1328 (in 11 seconds) to 332 solutions (in 3 seconds).


x[0].must < x[1]
diffs[0].must > diffs[n-2]

The Gecode model all-interval (implemented in C++) solves the problem for n=1000 in 1 seconds. This Gecode/R model takes about 7 seconds after some tweaking with the :strength parameter.

Maybe I should benchmark the propagation strategies as for the Golomb ruler problem, see below.


Golomb ruler (CSPLib problem 6)


Yet another classic. See CSPLib problem number 6 for a formulation, and here for more references. Wikipedia's Golomb ruler has more info.

This Gecode/R model golomb_ruler.rb was quite slow at first. When testing for m = 10 it took about 15 seconds, which was about twice as slow as the MiniZinc model (this is from the MiniZinc online examples).

Gecode's program golomb-ruler solves n = 12 slightly under 1 seconds (with default settings). Something had to be done!

After some tweaking, like setting :strength => :bounds without any satisfying result, I decided on a more "scientific" approach, and benchmarked (some of) the different branch/search strategies for the branch_on method. There is a lot of these in Gecode/R.

After about 30 minutes of running, the ranking list below was completed. Note: the time is for running the specific model, not counting the time for startup of the program.

The winner was

:variable => :largest_size, :value =>: split_min 

which is used for all three variables in the model. Now m = 10 takes about 1 second.


The listing below states the time for the combination of the different strategies as
variable strategy ":" value strategy "->" running time

largest_size:split_min -> 1.114403
smallest_min:split_min -> 5.275035
smallest_min_regret:split_min -> 5.289433
none:split_min -> 5.320299
smallest_degree:split_min -> 5.344738
smallest_degree:min -> 5.551923
none:min -> 5.556326
smallest_min:min -> 5.567722
largest_max_regret:split_min -> 5.621116
smallest_min_regret:min -> 5.657271
largest_max_regret:min -> 5.783116
largest_size:min -> 6.067107
smallest_size:split_min -> 7.006949
largest_min:split_min -> 8.229713
smallest_size:min -> 11.800805
largest_min:split_max -> 14.558584
largest_min:min -> 14.736679
largest_min:max -> 17.301876
largest_size:split_max -> 17.564312
largest_min:med -> 23.655615
largest_size:max -> 26.835556
smallest_min:split_max -> 60.566207
largest_max_regret:split_max -> 60.605995
smallest_min_regret:split_max -> 60.79834
smallest_degree:split_max -> 60.948791
none:split_max -> 63.229398
smallest_size:split_max -> 80.155812
smallest_min_regret:max -> 84.988649
none:max -> 85.208536
smallest_degree:max -> 85.541162
largest_max_regret:max -> 85.64104
smallest_min:max -> 85.859321
smallest_size:max -> 93.474409
none:med -> 102.263753
largest_max_regret:med -> 103.352322
smallest_degree:med -> 103.688327
smallest_min:med -> 103.78953
smallest_size:med -> 131.829423
largest_size:med -> 160.26752
smallest_min_regret:med -> 181.760703

It is quite a span from the fastest to the slowest. It seems that :value => :med is bad for this problem.


alpha (alphametic problem)


Gecode/R model alpha.rb.

This alphametic problem asks for a solution of the equations below. The letters "a".."z" shall be unique assigned one and each of the numbers 1..26.

b+a+l+l+e+t       == 45 
c+e+l+l+o         == 43 
c+o+n+c+e+r+t     == 74 
f+l+u+t+e         == 30 
f+u+g+u+e         == 50 
g+l+e+e           == 66 
j+a+z+z           == 58 
l+y+r+e           == 47 
o+b+o+e           == 53 
o+p+e+r+a         == 65 
p+o+l+k+a         == 59 
q+u+a+r+t+e+t     == 50 
s+a+x+o+p+h+o+n+e == 134
s+c+a+l+e         == 51 
s+o+l+o           == 37 
s+o+n+g           == 61 
s+o+p+r+a+n+o     == 82 
t+h+e+m+e         == 72 
v+i+o+l+i+n       == 100
w+a+l+t+z         == 34 

See also
* the Gecode program alpha.ccp
* the MiniZinc models crypto.mzn, crypto_ip.mzn (integer programming model).

global cardinality

global_cardinality.rb implements the global constraint global_cardinality (the link is to the wonderful collections of global constraints: Global Constraint Catalog).

Nothing fancy, just this method, using the built in method count:

class Array
def global_cardinality(gcc)
@n = self.length
(@n).times{|i|
gcc[i].must == self.count(i)
}
end
end

Please note that this global constraint is just on the modellering level; there is no special (Gecode) code with optimized propagations etc. Even of they are not faster than "inline code", I see global constraints as conceptual building blocks, very useful in the modelling.

I intend to implement more global constraints in the future.


sliding sum


sliding_sum.rb implements another global constraint: sliding_sum, which constrain all sliding (running) sums to be between low and high.