At last 2: A Nonogram solver using regular written in "all MiniZinc"
The model: nonogram_create_automaton2.mzn.
So I went got back to the drawing board and wondered how to do this. The solution I've found is not pretty, in fact it is hairy, very hairy: nonogram_create_automaton.mzn.
It is now as fast as nonogram_regular.mzn for solvers that use optimized version of
nonogram_regular.mzn
In At last, a Nonogram solver using regular constraint in MiniZinc I wrote about a Nonogram solver in MiniZinc using theregular solver, nonogram_regular.mzn.
The drawback of this version is that an external program (make_nonogram_automata.pl) was needed to convert the Nonogram patterns (clues) to an automaton (finite states) for use in the regular constraint.
nonogram_create_automaton.mzn
In an update some hours later in the same blog post, the variant nonogram_create_automaton.mzn was mentioned. This is an "all MiniZinc" solution where the model also calculating the automata. The drawback of this was that the states isvar int (decision variables), so it couldn't use the optimized regular constraint that some solvers (e.g. Gecode/FlatZinc) has implemented (this optimized version of regular is in fact the reason that Gecode/FlatZinc is so fast for the nonogram_regular.mzn model.) Instead a tweaked version of MiniZinc's standard regular constrant was used.
nonogram_create_automaton2.mzn
After a short discussion about this with Mikael Lagerkvist (of the Gecode team) the other day, we agreed that it would be nice thing to have the states calculated as "par" variables (i.e. not decision variables), thus the optimizedregular could be used.
So I went got back to the drawing board and wondered how to do this. The solution I've found is not pretty, in fact it is hairy, very hairy: nonogram_create_automaton.mzn.
It is now as fast as nonogram_regular.mzn for solvers that use optimized version of
regular, especially on the P200 problem.
Problem instances
Here are the problem instances I have tested (they are the same as used before.)- Bear: nonogram_bear.dzn
- Car: nonogram_car.dzn
- Castle: nonogram_castle.dzn
- Crocodile: nonogram_crocodile.dzn
- Difficult: nonogram_difficult.dzn
- Dragonfly: nonogram_dragonfly.dzn
- Griddler: nonogram_griddler.dzn
- Hard: nonogram_hard.dzn
- Hen: nonogram_hen.dzn
- House: nonogram_house.dzn
- Lambda: nonogram_lambda.dzn
- n2: nonogram_n2.dzn
- n3: nonogram_n3.dzn
- n4: nonogram_n4.dzn
- n5: nonogram_n5.dzn
- n6: nonogram_n6.dzn
- Nonunique: nonogram_nonunique.dzn
- Optima: nonogram_optima.dzn
- P199: nonogram_p199.dzn
- P200: nonogram_p200.dzn
- ps: nonogram_ps.dzn
- Simple: nonogram_simple.dzn
- Simple2: nonogram_simple2.dzn
- Soccer player: nonogram_soccer_player.dzn
- t1: nonogram_t1.dzn
- t3: nonogram_t3.dzn
Comparison of calculating of the states
Now, let's compare the two different versions of calculating the automaton states.First method: using decision variables
This is the version that is used in nonogram_create_automaton.mzn. The states are represented by the 2-dimensional arraystates.
states[1,1] = 1
/\
states[1,2] = 2
/\
forall(i in 2..len-1) (
if i in zero_positions then
states[i,1] = i+1 /\
states[i,2] = 0 /\
states[i+1,1] = i+1 /\
states[i+1,2] = i+2
else
if not(i - 1 in zero_positions) then
states[i,1] = 0 /\
states[i,2] = i+1
else
true
endif
endif
)
/\
states[len,1] = len
/\
states[len,2] = 0
Quite neat, and relatively easy to understand.
Second method: no decision variables
And this is the non-decision variable version of calculating the finite states used in nonogram_create_automaton2.mzn. Note that states are represented by a 1-dimensional array, which is - as I have understood it - a requirement for this kind of initialisation of an array. It is also the cause of this hairyness.
array[1..2*len] of 0..len*2: states =
[1, 2] ++
[
if i div 2 in zero_positions then
if i mod 2 = 0 then
0
else
(i div 2) + 1
endif
elseif (i-1) div 2 in zero_positions then
if i mod 2 = 0 then
(i div 2)+1
else
(i div 2)+2
endif
else
if not( (((i-1) div 2) - 1) in zero_positions) then
if i mod 2 = 0 then
(i div 2) + 1
else
if (i div 2) + 1 in zero_positions then
(i div 2) + 2
else
0
endif
endif
else
if i mod 2 = 0 then
(i div 2) + 1
else
if not((i div 2) + 1 in zero_positions) then
0
else
(i div 2) + 2
endif
endif
endif
endif
| i in 3..2*(len-1)]
++
[len, 0]
It could most probably be done in a more elegant fashion. And maybe I will think more about this later on.