%
% Steiner triplets in ASP.
%
%  http://www.probp.com/examples/clpset/steiner.pl
% """
% The ternary Steiner problem of order n is to find n(n-1)/6 sets of elements
% in {1,2,...,n} such that each set contains three elements and any two
% sets have at most one element in common.
%
% For example, the following shows a solution for size n=7:
%
%     {1,2,3}, {1,4,5}, {1,6,7}, {2,4,6}, {2,5,7}, {3,4,7}, {3,5,6}
%
% Problem taken from:
% C. Gervet: Interval Propagation to Reason about Sets: Definition and
%            Implementation of a PracticalLanguage,
%            Constraints, An International Journal, vol.1, pp.191-246, 1997.
% """
%
% Also see:
% - http://mathworld.wolfram.com/SteinerTripleSystem.html
%   """
%   The numbers of nonisomorphic Steiner triple systems S(v) of orders
%   v=7, 9, 13, 15, 19, ... (i.e., 6k+1,3) are 1, 1, 2, 80, 11084874829, ...
%   """
%
% - http://en.wikipedia.org/wiki/Steiner_system
%

% This was created by Hakan Kjellerstrand, hakank@gmail.com
%

% Licenced under CC-BY-4.0 : http://creativecommons.org/licenses/by/4.0/

% Some statistics:
%  n    time   Choices Conflicts Restarts
%  --------------------------------------
%   7    0.01s    183     116      1
%   9    0.02s    412     136      1
%  13    0.33s   6269    2385      6
%  15    2.35s  22801   13755     10
%  19    > 2h

#const n = 7.

#const nb = n * (n-1) / 6.

% domain
val(1..n).
sets(1..nb).

0 { x(Set, Val)  } 1 :- sets(Set), val(Val).

% 3 values of each set
3 { x(Set, Val) : val(Val) } 3 :- sets(Set).
3 { x(Set, Val) : sets(Set) } 3 :- val(Val).

% count the number of common occurrences of each pair of Sets
check(Set1, Set2, Val, N) :-
sets(Set1;Set2),
Set1 < Set2,
val(Val),
N = #count { Val,Set1,Set2:x(Set1,Val), x(Set2,Val) }.

% ensure that there are at most 1 occurrence
% with the same value  (i.e. N=2) for any two sets.
:- sets(Set1), sets(Set2), Set1 < Set2, 2 #count{Set1,Set2:check(Set1, Set2, Val, 2)}.

%
% ad hoc symmetry breaking (slow)
% (What I would really like to do is
% to sort the sets lexicographically.)
%
% :- sets(Set1;Set2), Set1 < Set2,
%     S1 = #sum{Val1:x(Set1, Val1)},
%     S2 = #sum{Val2:x(Set2, Val2)},
%     S1 >= S2.

#show x/2.