Packages
fixpoint
0.14.3
0.22.2
0.22.1
0.21.5
0.21.4
0.21.3
0.21.2
0.21.1
0.21.0
0.20.6
0.20.5
0.20.4
0.20.3
0.20.2
0.20.1
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.18.2
0.18.1
0.17.6
0.17.5
0.17.4
0.17.3
0.17.2
0.17.1
0.16.5
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.6
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.9
0.14.8
0.14.7
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.13.5
0.13.4
0.13.2
0.13.1
0.12.9
0.12.8
0.12.7
0.12.6
0.12.5
0.12.4
0.12.2
0.12.1
0.11.8
0.11.7
0.11.6
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.10.7
0.10.6
0.10.5
0.10.4
0.10.3
0.10.2
0.10.1
0.9.12
0.9.11
0.9.10
0.9.9
0.9.8
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.52
0.8.51
0.8.50
0.8.49
0.8.48
0.8.46
0.8.44
0.8.43
0.8.42
0.8.41
0.8.40
0.8.39
0.8.38
0.8.37
0.8.36
0.8.35
0.8.34
0.8.33
0.8.32
0.8.31
0.8.30
0.8.29
0.8.28
0.8.27
0.8.26
0.8.25
0.8.24
0.8.23
0.8.22
0.8.21
0.8.20
0.8.19
0.8.18
0.8.17
0.8.16
0.8.15
0.8.14
0.8.13
0.8.12
0.8.11
0.8.10
0.8.9
0.8.8
0.8.7
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.12
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.3
0.4.2
0.4.1
0.4.0
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.1.3
0.1.2
0.1.1
0.1.0
Constraint Programming Solver
Current section
Files
Jump to
Current section
Files
test/constraints/reified_test.exs
defmodule CPSolverTest.Constraint.Reified do
use ExUnit.Case, async: false
describe "Reification" do
alias CPSolver.Constraint.{Reified, HalfReified, InverseHalfReified}
alias CPSolver.Constraint.{Equal, NotEqual, LessOrEqual, Less, Absolute}
alias CPSolver.IntVariable
alias CPSolver.BooleanVariable
alias CPSolver.Model
test "equivalence: (x `relation` y) <-> b" do
~c"""
MiniZinc model (for verification):
var 0..1: x;
var 0..1: y;
var bool: b;
constraint x <= y <-> b;
Solutions:
x = 1; y = 1; b = true;
x = 0; y = 1; b = true;
x = 1; y = 0; b = false;
x = 0; y = 0; b = true;
"""
x_domain = 0..1
y_domain = 0..1
for p <- [LessOrEqual, Less, Equal, NotEqual, Absolute] do
model1 = make_model(x_domain, y_domain, p, Reified)
{:ok, res} = CPSolver.solve(model1)
assert res.statistics.solution_count == num_sols(p, Reified)
assert Enum.all?(res.solutions, fn s -> check_solution(s, p, Reified) end)
## The order of variables doesn't matter
model2 = make_model(x_domain, y_domain, p, Reified, fn [x, y, b] -> [b, x, y] end)
{:ok, res} = CPSolver.solve(model2)
assert res.statistics.solution_count == num_sols(p, Reified)
assert Enum.all?(res.solutions, fn [b_value, x_value, y_value] = s ->
check_solution([x_value, y_value, b_value], p, Reified)
end)
end
end
test "implication (half-reification): (x `relation` y) -> b" do
~c"""
Minizinc model:
var 0..1: x;
var 0..1: y;
var bool: b;
constraint x <= y -> b;
"""
x_domain = 0..1
y_domain = 0..1
for p <- [LessOrEqual, Less, Equal, NotEqual, Absolute] do
model = make_model(x_domain, y_domain, p, HalfReified)
{:ok, res} = CPSolver.solve(model)
assert res.statistics.solution_count == num_sols(p, HalfReified)
assert Enum.all?(res.solutions, fn s -> check_solution(s, p, HalfReified) end)
end
end
test "inverse implication (inverse half-reification): (x `relation` y) <- b" do
~c"""
Minizinc model:
var 0..1: x;
var 0..1: y;
var bool: b;
constraint x <= y <- b;
"""
x_domain = 0..1
y_domain = 0..1
for p <- [LessOrEqual, Less, Equal, NotEqual, Absolute] do
model = make_model(x_domain, y_domain, p, InverseHalfReified)
{:ok, res} = CPSolver.solve(model)
assert res.statistics.solution_count == num_sols(p, InverseHalfReified)
assert Enum.all?(res.solutions, fn s -> check_solution(s, p, InverseHalfReified) end)
end
end
test "Absolute, reified (both negatives and positives in domains)" do
x_domain = -1..1
y_domain = -1..1
for {mode, expected_num_sols} <- [
{Reified, 9},
{HalfReified, 15},
{InverseHalfReified, 12}
] do
model = make_model(x_domain, y_domain, Absolute, mode)
{:ok, res} = CPSolver.solve(model)
assert Enum.all?(res.solutions, fn s -> check_solution(s, Absolute, mode) end)
assert res.statistics.solution_count == expected_num_sols
end
end
defp make_model(
x_domain,
y_domain,
constraint_mod,
reif_impl,
order_fun \\ &Function.identity/1
) do
x = IntVariable.new(x_domain, name: "x")
y = IntVariable.new(y_domain, name: "y")
b = BooleanVariable.new(name: "b")
le_constraint = constraint_mod.new(x, y)
Model.new(order_fun.([x, y, b]), [reif_impl.new(le_constraint, b)])
end
defp check_solution([x, y, b] = _solution, constraint_impl, reification_mod) do
checker = Map.get(constraint_data(), constraint_impl)[:check_fun]
case reification_mod do
Reified -> (checker.(x, y) && b == 1) || b == 0
HalfReified -> !checker.(x, y) || b == 1
InverseHalfReified -> checker.(x, y) || b == 0
end
end
defp num_sols(constraint_impl, reification_mod) do
get_in(constraint_data(), [constraint_impl, :num_sols, reification_mod])
end
defp constraint_data() do
%{
LessOrEqual => %{
check_fun: fn x, y -> x <= y end,
num_sols: %{Reified => 4, HalfReified => 5, InverseHalfReified => 7}
},
Less => %{
check_fun: fn x, y -> x < y end,
num_sols: %{Reified => 4, HalfReified => 7, InverseHalfReified => 5}
},
Equal => %{
check_fun: fn x, y -> x == y end,
num_sols: %{Reified => 4, HalfReified => 6, InverseHalfReified => 6}
},
NotEqual => %{
check_fun: fn x, y -> x != y end,
num_sols: %{Reified => 4, HalfReified => 6, InverseHalfReified => 6}
},
Absolute => %{
check_fun: fn x, y -> abs(x) == y end,
num_sols: %{Reified => 4, HalfReified => 6, InverseHalfReified => 6}
}
}
end
end
describe "Factory (implication, equivalence, inverse implication)" do
alias CPSolver.Constraint.Factory
alias CPSolver.IntVariable
alias CPSolver.Model
alias CPSolver.Constraint.{LessOrEqual}
test "equivalence" do
model = build_model(1..2, 1..2, 1..2, LessOrEqual, :equiv)
{:ok, res} = CPSolver.solve(model)
assert res.statistics.solution_count == 4
assert Enum.all?(res.solutions, fn [x, y, z | _rest] ->
x <= y && y <= z
end)
end
test "implication" do
model = build_model(1..2, 1..2, 1..2, LessOrEqual, :impl)
{:ok, res} = CPSolver.solve(model)
assert res.statistics.solution_count == 6
assert Enum.all?(res.solutions, fn [x, y, z | _rest] ->
x > y || y <= z
end)
end
test "inverse implication" do
model = build_model(1..2, 1..2, 1..2, LessOrEqual, :inverse_impl)
{:ok, res} = CPSolver.solve(model)
assert res.statistics.solution_count == 6
assert Enum.all?(res.solutions, fn [x, y, z | _rest] ->
x <= y || y > z
end)
end
test "ignoring temporary variables" do
alias CPSolver.IntVariable, as: Variable
alias CPSolver.Constraint.Less
x = Variable.new(1..3, name: "x")
y = Variable.new(4..6, name: "y")
c1 = 1
c2 = 5
~S"""
Minizinc:
var 1..3: x;
var 4..6: y;
constraint (1 < x) -> (5 < y);
"""
impl_model = Factory.impl(Less.new(c1, x), Less.new(c2, y))
model = Model.new([], impl_model.constraints)
{:ok, res} = CPSolver.solve(model)
assert res.statistics.solution_count == 5
## Positions of variables can be arbitrary, if omitted in the model description
x_pos = Enum.find_index(res.variables, fn name -> name == "x" end)
y_pos = Enum.find_index(res.variables, fn name -> name == "y" end)
assert Enum.all?(res.solutions, fn solution ->
x = Enum.at(solution, x_pos)
y = Enum.at(solution, y_pos)
x >= 1 || y > 5
end)
end
defp build_model(x_domain, y_domain, z_domain, constraint, kind) do
x_var = IntVariable.new(x_domain, name: "x")
y_var = IntVariable.new(y_domain, name: "y")
z_var = IntVariable.new(z_domain, name: "z")
%{constraints: constraints, derived_variables: tmp_vars} =
apply(Factory, kind, [constraint.new([x_var, y_var]), constraint.new([y_var, z_var])])
Model.new(
[x_var, y_var, z_var] ++ tmp_vars,
constraints
)
end
end
end