# # All interval problem in Elixir. # # CSPLib problem number 7 # http://www.cs.st-andrews.ac.uk/~ianm/CSPLib/prob/prob007/index.html # """ # Given the twelve standard pitch-classes (c, c , d, ...), represented by # numbers 0,1,...,11, find a series in which each pitch-class occurs exactly # once and in which the musical intervals between neighbouring notes cover # the full set of intervals from the minor second (1 semitone) to the major # seventh (11 semitones). That is, for each of the intervals, there is a # pair of neigbhouring pitch-classes in the series, between which this # interval appears. The problem of finding such a series can be easily # formulated as an instance of a more general arithmetic problem on Z_n, # the set of integer residues modulo n. Given n in N, find a vector # s = (s_1, ..., s_n), such that (i) s is a permutation of # Z_n = {0,1,...,n-1}; and (ii) the interval vector # v = (|s_2-s_1|, |s_3-s_2|, ... |s_n-s_{n-1}|) is a permutation of # Z_n-{0} = {1,2,...,n-1}. A vector v satisfying these conditions is # called an all-interval series of size n; the problem of finding such # a series is the all-interval series problem of size n. We may also be # interested in finding all possible series of a given size. # """ # # Note: This version is based on a model created by Boris Okner. # # Result for n=8: # x: [4,1,8,2,7,3,5,6] diffs: [3,7,6,5,4,2,1] # x: [5,4,2,6,3,8,1,7] diffs: [1,2,4,3,5,7,6] # x: [2,8,1,6,5,3,7,4] diffs: [6,7,5,1,2,4,3] # x: [5,2,8,1,6,4,3,7] diffs: [3,6,7,5,2,1,4] # x: [3,4,6,2,7,1,8,5] diffs: [1,2,4,5,6,7,3] # x: [2,8,1,6,3,7,5,4] diffs: [6,7,5,3,4,2,1] # x: [5,4,2,8,1,6,3,7] diffs: [1,2,6,7,5,3,4] # x: [4,3,8,1,7,5,2,6] diffs: [1,5,7,6,2,3,4] # x: [5,2,6,4,3,8,1,7] diffs: [3,4,2,1,5,7,6] # x: [2,7,1,8,4,5,3,6] diffs: [5,6,7,4,1,2,3] # x: [3,7,2,8,1,4,6,5] diffs: [4,5,6,7,3,2,1] # x: [4,5,3,6,2,7,1,8] diffs: [1,2,3,4,5,6,7] # x: [3,8,1,7,4,2,6,5] diffs: [5,7,6,3,2,4,1] # x: [4,3,7,5,2,8,1,6] diffs: [1,4,2,3,6,7,5] # x: [4,3,5,8,1,7,2,6] diffs: [1,2,3,7,6,5,4] # # # Comparison with Picat and MiniZinc/Gecode (in seconds) for all solutions: # # n Fixpoint Picat Gecode # ----------------------------- # 8 0.5s 0.005s 0.003s # 9 2.4s 0.024s 0.008s # 10 13.7s 0.048s 0.034s # 11 79.3s 0.297s 0.184s # 12 469.3s 1.584s 0.922s # 13 2982.2s 9.462s 5.178s # # # This program was created by Hakan Kjellerstrand, hakank@gmail.com # See also my Elixir page: http://www.hakank.org/elxir/ # ## Boris Okner: modified to sync with the latest API, ## change naming and result handling. ## defmodule CPSolver.Examples.Hakank.AllInterval do alias CPSolver.IntVariable alias CPSolver.Constraint.AllDifferent.FWC, as: AllDifferent alias CPSolver.Constraint.LessOrEqual alias CPSolver.Constraint.Absolute alias CPSolver.Model import CPSolver.Constraint.Factory def run(n) do x = for i <- 0..(n - 1) do IntVariable.new(1..n, name: "x[#{i}]") end diffs = for i <- 0..(n - 2) do IntVariable.new(1..(n - 1), name: "diffs[#{i}]") end constraints = for k <- 0..(n - 2) do {difference_var, difference_constraint} = subtract(Enum.at(x, k + 1), Enum.at(x, k)) [Absolute.new(difference_var, Enum.at(diffs, k)), ## |difference_var| = diffs[k] difference_constraint] end |> List.flatten() model = Model.new( x ++ diffs, constraints ++ [ AllDifferent.new(x), AllDifferent.new(diffs), # symmetry breaking LessOrEqual.new(Enum.at(x, 0), Enum.at(x, n - 1)), # symmetry breaking LessOrEqual.new(Enum.at(diffs, 0), Enum.at(diffs, 1)) ] ) Logger.configure(level: :info) opts = [ # search: {:first_fail, :indomain_min}, #search: {:first_fail, :indomain_max}, search: {:input_order, :indomain_min}, #search: {:first_fail, :indomain_min}, # search: {:input_order, :indomain_random}, space_threads: 8, timeout: :timer.hours(1), # stop_on: {:max_solutions, 2}, ] {:ok, res} = CPSolver.solve( model, opts ) res.solutions |> Enum.map(fn sol -> x_vals = Enum.slice(sol, 0, n) diffs_vals = Enum.slice(sol, n, n-1) :io.format("x: ~w diffs: ~w~n", [x_vals, diffs_vals]) end) IO.inspect(res.statistics) end end