Solution: The Generic Magic Square

The code...

declare
fun {MagicSquare N}
   NN=N*N
   L1N={List.number 1 N 1} % [1..N]
in
   proc {$ Square}
      fun {Field I J}
         Square.((I-1)*N + J)
      end
      proc {Assert F}
         {FD.sum {Map L1N F} '=:' Sum}
      end
      Sum={FD.decl}
   in
      {FD.tuple square NN 1#NN Square}
      {FD.distinct Square}
      %Diagonals
      {Assert fun {$ I} {Field I I} end}
      {Assert fun {$ I} {Field I N+1-I} end}
      %Columns
      {For 1 N 1
       proc {$ I} {Assert fun {$ J} {Field I J} end} end}
      %Rows
      {For 1 N 1
       proc {$ J} {Assert fun {$ I} {Field I J} end} end}
      %Eliminate Symmetries
      {Field 1 1} <: {Field N N}
      {Field N 1} <: {Field 1 N}
      {Field 1 1} <: {Field N 1}
      %Redundant: sum of all fields = (number rows) * Sum
      NN*(NN+1) div 2 =: N*Sum

      {FD.distribute split Square}
   end
end

{ExploreOne {MagicSquare 5}}

...and the search trees for the first three solutions at N=5 with split (left) and naive (right) distribution strategy.

(the problem is quite hard ...3938 nodes had to be explored to find the first solution with domain splitting).

The first three magic squares yopu get with domain splitting are

1#square( 1  2 13 24 25	   2#square( 1  2 13 24 25    3#square( 1  2 13 24 25 
          3 22 19  6 15              3 23 16  8 15              3 23 17  6 16
         23 16 10 11  5             21 19 10  6  9             20 21 11  8  5 
         21  7  9 20  8             22  4 14 20  5             22  4 14 18  7 
         17 18 14  4 12)            18 17 12  7 11)            19 15 10  9 12)


well, they seem to come in packs somehow, don't they?


Markus Löckelt