Current section

Files

Jump to
fixpoint test cpsolver_test.exs
Raw

test/cpsolver_test.exs

defmodule CpSolverTest do
use ExUnit.Case
alias CPSolver.IntVariable
alias CPSolver.Constraint.NotEqual
test "Solves CSP with 2 variables and a single constraint" do
x = IntVariable.new([1, 2])
y = IntVariable.new([0, 1])
model = %{
variables: [x, y],
constraints: [{NotEqual, x, y}]
}
target_pid = self()
solution_handler = fn solution ->
send(target_pid, Enum.sort_by(solution, fn {var, _value} -> var end))
end
{:ok, solver} = CPSolver.solve(model, solution_handler: solution_handler)
Process.sleep(10)
solutions =
Enum.map(1..3, fn _ ->
receive do
sol -> sol
end
end)
# Only 3 solutions
refute_receive _msg, 100
# For all solutions, constraints (x != y and y != z) are satisfied.
assert Enum.all?(solutions, fn variables ->
[x, y] = Enum.map(variables, fn {_id, value} -> value end)
x != y
end)
## Solution, failure and node count from solver state.
assert CPSolver.statistics(solver).solution_count == length(solutions)
## NotEqual never results in failures, unless started with initially
## inconsistent domains.
assert CPSolver.statistics(solver).failure_count == 0
## Note: there are 2 "first fail" distributions:
## 1. Variable 'x' triggers distribution into 2 spaces - (x: 1, y: [0, 1]) and (x: 2, y: [0, 1])).
## 2. First space produces solution (x: 1, y: 0)
## 3. Second space triggers distribution into 2 spaces - (x: 2, y: 0) and (x: 2, y: 1)
## 4. These 2 spaces produce remaining solutions.
## 5. There have been 5 spaces - top one, and 4 as described above, which corresponds
## to 5 nodes.
assert CPSolver.statistics(solver).node_count == 5
end
end