minizinc

Constraints reported as UNSATISFIABLE when I know there is a solution (rookie error?)


As a first-time MiniZinc user I have tried to use it to code up a logic puzzle I already know the solution to. However, when I attempt to code up all of the puzzle clues and run the code it reports back =====UNSATISFIABLE=====. The code below runs but if either of the two commented out constraints are uncommented the solution is not found.

It's more than likely that I've made a mistake here somewhere but I am struggling to find it. Any help greatly appreciated!

% Christmas Puzzle 2021

include "alldifferent.mzn";

% There are 9 reindeer numbered 1 to 9 and there are 9 different types of food.
% The reindeer fly on three test flights and are fed one of the foods prior to
% each test flight.

set of int: REINDEER = 1..9;
set of int: FOOD = 1..9;
set of int: FLIGHT = 1..3;

% Each reindeer has a favourite food

array[REINDEER] of var FOOD: favourite;
array[FLIGHT, REINDEER] of var FOOD: fed;

%%% CLUES %%%

% Reindeer 2’s favourite food is 4
constraint favourite[2] == 4;

% Before test flight 1, reindeer 2 was given food 4
constraint fed[1, 4] == 4;

% Before test flight 2, reindeer 2 was given food 4
constraint fed[2, 4] == 4;

% Before test flight 3, reindeer 2 was given food 6
constraint fed[3, 4] == 6;

% Before test flight 3, a total of 4 reindeer were given the wrong food
constraint count([fed[3, r] - favourite[r] | r in REINDEER], 0, 5);

% Reindeer 4’s favourite food is a factor of 607
constraint favourite[4] == 1;

% Reindeer 4 was given the same food before all three test flights.
constraint fed[1, 4] == fed[2, 4];
% =====UNSATISFIABLE===== 
%constraint fed[1, 4] == fed[3, 4];

% Before test flight 1, reindeer 8 was given food 3
constraint fed[1, 8] == 3;

% Before test flight 1, a total of 2 reindeer were given the wrong food.
constraint count([fed[1, r] - favourite[r] | r in REINDEER], 0, 7);

% Before test flight 1, reindeer 9 was given food 6
constraint fed[1, 9] == 6;

% Before test flight 2, reindeer 9 was given food 5
constraint fed[2, 9] == 5;

% Before test flight 3, reindeer 9 was given food 1
constraint fed[3, 9] == 1;

% Before test flight 2, reindeer 4 was not given food 9
constraint fed[2, 4] != 9;

% Before test flight 2, 2 reindeer were given the wrong food
% =====UNSATISFIABLE===== 
%constraint count([fed[2, r] - favourite[r] | r in REINDEER], 0, 7);

% Reindeer 1 was given food 1 before all three test flights
constraint fed[1, 1] == 1;
constraint fed[2, 1] == 1;
constraint fed[3, 1] == 1;

% Before test flight 2, all the reindeer were given different foods
constraint all_different([fed[2, r] | r in REINDEER]);

% Before test flight 1, reindeer 7 was not given food 7
constraint fed[1, 7] != 7;

% Before test flight 2, reindeer 8 was given food 2
constraint fed[2, 8] == 2;

% Before test flight 3, reindeer 5 was not given food 7
constraint fed[3, 5] !=7;

% Before test flight 3, 3 reindeer had the food equal to their number
constraint count([fed[3, r] - r | r in REINDEER], 0, 3);

% Before test flight 3, reindeer 7 was given food that is a factor of 148
constraint fed[3, 7] == 1 \/ fed[3, 7] == 2 \/ fed[3, 7] == 4;

% Before test flight 3, reindeer 7 was not given food 1
constraint fed[3, 7] != 1;

% Before test flight 3, no reindeer was given food 2
constraint forall([fed[3, r] != 2 | r in REINDEER]);

% Before test flight 1, reindeer 7 was not given food 9
constraint fed[1, 7] != 9;

solve satisfy;

output [
  "Favourites    = ", show(favourite), "\n",
  "Test flight 1 = ", show([fed[1, r] | r in REINDEER]), "\n",
  "Test flight 2 = ", show([fed[2, r] | r in REINDEER]), "\n",
  "Test flight 3 = ", show([fed[3, r] | r in REINDEER]), "\n"
  ];

Note that the following results should satisfy all of the clues:

Favourites    = [1, 4, 3, 1, 9, 6, 8, 3, 5]
Test flight 1 = [1, 4, 3, 7, 9, 6, 8, 3, 6]
Test flight 2 = [1, 4, 3, 7, 9, 6, 8, 2, 5]
Test flight 3 = [1, 6, 3, 7, 9, 6, 4, 3, 1]

I don't know if there's a way to debug by working backwards from this solution to see which constraints (if any) are not met.


Solution

  • There are some typos in the first constraints, probably copy/paste issues. Here are the changes:

    % Before test flight 1, reindeer 2 was given food 4
    % constraint fed[1, 4] == 4; % ORIG
    constraint fed[1, 2] == 4; % hakank
    
    % Before test flight 2, reindeer 2 was given food 4
    % constraint fed[2, 4] == 4;  % ORIG
    constraint fed[2, 2] == 4; % hakank
    
    % Before test flight 3, reindeer 2 was given food 6
    % constraint fed[3, 4] == 6; % ORIG
    constraint fed[3, 2] == 6;  % hakank
    
    

    Now the model outputs the unique solution:

    Favourites    = [1, 4, 3, 1, 9, 6, 8, 3, 5]
    Test flight 1 = [1, 4, 3, 7, 9, 6, 8, 3, 6]
    Test flight 2 = [1, 4, 3, 7, 9, 6, 8, 2, 5]
    Test flight 3 = [1, 6, 3, 7, 9, 6, 4, 3, 1]
    ----------
    ==========
    

    There is one way to debug an UNSAT model systematically in MiniZinc: using the findMUS solver (MUS=Minimal Unsatisfiable Subsets). It might show places of the conflicts. See more about findMUS here: https://www.minizinc.org/doc-2.5.5/en/find_mus.html

    I actually tried findMUS first, but it didn't help much in this case. Instead, I looked through all the clues and compared with the constraints.