# # 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. # """ # # NOPE: I need an abs/2 constraint! # # This program was created by Hakan Kjellerstrand, hakank@gmail.com # See also my Elixir page: http://www.hakank.org/elxir/ # defmodule AllInterval do alias CPSolver.IntVariable alias CPSolver.Constraint.AllDifferent.FWC, as: AllDifferent # alias CPSolver.Constraint.Sum alias CPSolver.Constraint.LessOrEqual alias CPSolver.Constraint.Absolute alias CPSolver.Model # alias CPSolver.Objective import CPSolver.Constraint.Factory # import CPSolver.Variable.View.Factory def main() do n = 8 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)) ## |dirrerence_var| = diffs[k] [Absolute.new(difference_var, Enum.at(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: {:input_order, :indomain_random}, # search: {:first_fail, :indomain_random}, space_threads: 12, timeout: :timer.minutes(5) # stop_on: {:max_solutions, 2}, ] {:ok, _res} = CPSolver.solve_sync( model, opts ) # IO.inspect(res.statistics) # res.solutions # |> Enum.map(fn solution -> Enum.take(solution, 15) end) end end