The model we developed to solve the cryptarithmetic puzzle (see The cryptarithmetic puzzle problem and a first model) was quite simple.
The first model proposed to solve the Golumb Ruler Problem in the two previous sections is more complex. We suppose our model is theoretically correct. How do we know we gave the right model to the solver, i.e. how do we know that our implementation is correct? In this section, we present two tools to debug the model we passed to the solver: the DebugString() method and via the default command line flags of the CP solver.
Most of the mathematical classes in or-tools inherit from the BaseObject class. Its only public method is a virtual DebugString(). If you are curious or just in doubt about the object you just constructed, DebugString() is for you.
Let’s have a closer look at the constraints that model the inner structure of the Golomb ruler of order :
const int n = 5;
...
for (int i = 2; i <= n - 1; ++i) {
for (int j = 0; j < n-i; ++j) {
...
c = s.MakeEquality(Y[index], v2);
s.AddConstraint(c);
LOG(INFO) << c->DebugString();
}
}
The output is:
...: Y_4(1..24) == Var<(Y_1(1..24) + Y_0(1..24))>(2..48)
...: Y_5(1..24) == Var<(Y_2(1..24) + Y_1(1..24))>(2..48)
...: Y_6(1..24) == Var<(Y_3(1..24) + Y_2(1..24))>(2..48)
...: Y_7(1..24) == Var<(Y_2(1..24) + Var<(Y_1(1..24) +
Y_0(1..24))>(2..48))>(3..72)
...: Y_8(1..24) == Var<(Y_3(1..24) + Var<(Y_2(1..24) +
Y_1(1..24))>(2..48))>(3..72)
...: Y_9(1..24) == Var<(Y_3(1..24) + Var<(Y_2(1..24) +
Var<(Y_1(1..24) + Y_0(1..24))>(2..48))>(3..72))>(4..96)
These are exactly the constraints listed in Figure The inner structure of a Golomb ruler of order 5..
By default, the CP solver is able to return some information about the model. If you try
./golomb1 --help
in the terminal, you get all possible command line flags. For the file constraint_solver.cc, these are:
Flags from src/constraint_solver/constraint_solver.cc:
-cp_export_file (Export model to file using CPModelProto.)
type: string default: ""
-cp_model_stats (use StatisticsModelVisitor on model before solving.)
type: bool default: false
-cp_name_cast_variables (Name variables casted from expressions)
type: bool default: false
-cp_name_variables (Force all variables to have names.)
type: bool default: false
-cp_no_solve (Force failure at the beginning of a search.)
type: bool default: false
-cp_print_model (use PrintModelVisitor on model before solving.)
type: bool default: false
-cp_profile_file (Export profiling overview to file.)
type: string default: ""
-cp_show_constraints (show all constraints added to the solver.)
type: bool default: false
-cp_trace_propagation (Trace propagation events (constraint and demon
executions, variable modifications).)
type: bool default: false
-cp_trace_search (Trace search events)
type: bool default: false
-cp_verbose_fail (Verbose output when failing.)
type: bool default: false
We are interested in the constraints. Invoking
./golomb1 --n=5 --cp_no_solve --cp_show_constraints
gives us:
...: BoundsAllDifferent(Y_0(1..24), Y_1(1..24), Y_2(1..24), Y_3(1..24),
Y_4(1..24), Y_5(1..24), Y_6(1..24), Y_7(1..24), Y_8(1..24),
Y_9(1..24))
This is the AllDifferent constraint on bounds where we see all the variables with their initial domains.
Then:
...: cast((Y_1(1..24) + Y_0(1..24)), Var<(Y_1(1..24) + Y_0(1..24))>
(2..48))
The cast to transform the sum into an IntVar.
And then:
...: Y_4(1..24) == Var<(Y_1(1..24) + Y_0(1..24))>(2..48)
...: Y_5(1..24) == Var<(Y_2(1..24) + Y_1(1..24))>(2..48)
...: Y_6(1..24) == Var<(Y_3(1..24) + Y_2(1..24))>(2..48)
...: Y_7(1..24) == Var<(Y_2(1..24) + Var<(Y_1(1..24) +
Y_0(1..24))>(2..48))>(3..72)
...: Y_8(1..24) == Var<(Y_3(1..24) + Var<(Y_2(1..24) +
Y_1(1..24))>(2..48))>(3..72)
...: Y_9(1..24) == Var<(Y_3(1..24) + Var<(Y_2(1..24) + Var<(Y_1(1..24) +
Y_0(1..24))>(2..48))>(3..72))>(4..96)
...: Forcing early failure
...: Check failed: (collector->solution_count()) == (1)
Aborted
All this output was generated from the following line in constraint_solver.cc:
LOG(INFO) << c->DebugString();
where c is a pointer to a Constraint.
Invoking
./golomb1 --n=5 --cp_no_solve --cp_model_stats
we obtain some statistics about the model:
...: Model has:
...: - 17 constraints.
...: * 1 AllDifferent
...: * 6 Equal
...: * 10 CastExpressionIntoVariable
...: - 20 integer variables.
...: - 10 integer expressions.
...: * 10 Sum
...: - 10 expressions casted into variables.
...: - 0 interval variables.
...: - 0 sequence variables.
...: - 2 model extensions.
...: * 1 VariableGroup
...: * 1 Objective
Indeed, we have 1 AllDifferent constraint, 6 equality constraints and 10 IntVar variables. Where does the rest come from?
To construct the equality constraints, we cast 10 times integer expressions into IntVar (remember the ...->Var() calls), hence the 10 integer expressions, the 10 supplementary IntVar variables and the 10 sums. The 2 model extensions are the objective OptimizeVar variable and the std::vector array of IntVar variables (VariableGroup).
Try the other flags!