# # # defmodule Hakank.CPUtils do # alias CPSolver.IntVariable alias CPSolver.Constraint.Sum # alias CPSolver.Constraint.Equal alias CPSolver.Constraint.LessOrEqual alias CPSolver.Constraint.AllDifferent.DC.Fast, as: AllDifferent # alias CPSolver.Model import CPSolver.Variable.View.Factory # # "System" / General functions. # @doc """ timeit(fun) A simple timing function, returns the time in seconds (as a string). ##Examples## > Util.timeit(&Test1.main/0) > Util.timeit(fn () -> Test1.run() end) """ def timeit(fun) do {time0, res} = :timer.tc(fun, []) IO.inspect(res) (time0 / 1_000_000) |> :erlang.float_to_binary([:compact, decimals: 5]) end @doc """ mat_at(m,i,j) Returns the value (`i`,`j`) of the 2d matrix `mat`. ##Examples## iex> [[1,2,3],[4,5,6],[7,8,9]] |> mat_at(1,2) 6 """ def mat_at(m,i,j) do m |> Enum.at(i) |> Enum.at(j) end @doc """ transpose(m) Returns a transposed version of `m`. ##Example## iex> [[1,2,3],[4,5,6],[7,8,9]] |> transpose [[1, 4, 7], [2, 5, 8], [3, 6, 9]] """ def transpose(m) do Enum.zip_with(m, &Function.identity/1) end @doc """ print_matrix(x,rows,cols, format \\ "~2w") Pretty print `x` as a matrix. Note: `x` is assumed to be a list of rows*cols elements. `format` is the spacing of the values, defaults to "~2w". """ def print_matrix(x,rows,cols, format \\ "~2w") do for i <- 0..rows-1 do for j <- 0..cols-1 do :io.format(format,[Enum.at(x,i*cols+j)]) end IO.puts("") end IO.puts("\n") end # # Decomposition of constraints # @doc """ latin_square(x) Ensures that an n x n matrix `x` is a Latin Square. ##Examples## > latin_square(x) """ def latin_square(x) do row_constraints = Enum.map(x,fn row -> AllDifferent.new(row) end) col_constraints = Enum.map(x |> transpose, fn row -> AllDifferent.new(row) end) row_constraints ++ col_constraints end @doc """ scalar_product(xs,ys,total) Ensures that `xs` * `ys` = `total`. It is assumed that `xs` and `ys` are of the same length. `total` can be either a decision variable or a constraint. """ def scalar_product(xs,ys,total) do [Sum.new(total, for {xi,yi} <- Enum.zip(xs,ys) do mul(xi,yi) end )] end @doc """ increasing(x) Decomposition of the global constraint `increasing` which ensures that `x` is in increasing order. """ def increasing(x) do len = length(x) for i <- 1..len-1 do LessOrEqual.new(Enum.at(x,i-1),Enum.at(x,i)) end end @doc """ decreasing(x) Decomposition of the global constraint `increasing` which ensures that `x` is in increasing order. """ def decreasing(x) do len = length(x) for i <- 1..len-1 do LessOrEqual.new(Enum.at(x,i),Enum.at(x,i-1)) end end @doc """ get_solution_value(res,sol,var_name) Return the solution of variable named `var_name` (e.g. `"x[3]"`) for a specific solution `sol`. Note: `var_name` handles only the single named variables found in `res.variables`. """ # # For some reason I cannot add these examples in the @doc section since # it gives an error that get_solution_values/3 does not exist. # # Examples: # x1_val = xxxget_solution_values(res,sol,"x[1]") # x_val = for i <- 0..n-1, do: get_solution_values(res,sol,"x[#{i}]") # def get_solution_value(res,sol,var_name) do var_idx = Enum.find_index(res.variables, fn v -> v == var_name end) var_idx && Enum.at(sol, var_idx) end end