Packages
fixpoint
0.2.3
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/propagation/propagator_thread_test.exs
defmodule CPSolverTest.Propagator.Thread do
use ExUnit.Case
import ExUnit.CaptureLog
import CPSolver.Test.Helpers
describe "Propagator thread" do
alias CPSolver.Propagator.Thread, as: PropagatorThread
alias CPSolver.Store.Registry, as: Store
alias CPSolver.ConstraintStore
alias CPSolver.IntVariable
alias CPSolver.Variable
alias CPSolver.Propagator.NotEqual
@domain_changes CPSolver.Common.domain_changes()
setup do
Logger.configure(level: :debug)
on_exit(fn -> Logger.configure(level: :error) end)
end
@entailment_str "Propagator is entailed"
test "create propagator thread" do
x = 1..1
y = -5..5
variables = Enum.map([x, y], fn d -> IntVariable.new(d) end)
{:ok, [x_var, y_var] = bound_vars, _store} = ConstraintStore.create_store(Store, variables)
{:ok, propagator_thread} =
PropagatorThread.create_thread(self(), {NotEqual, bound_vars},
propagate_on: @domain_changes
)
## Propagator thread subscribes to its variables
assert Enum.all?(bound_vars, fn var -> propagator_thread in Variable.subscribers(var) end)
## ...filters its variables upon start (happens in handle_continue, so needs a small timeout here)
Process.sleep(5)
refute Variable.contains?(y_var, 1)
## ...receives variable update notifications
assert capture_log([level: :debug], fn ->
Variable.removeBelow(y_var, 0)
Process.sleep(10)
end) =~ "Propagation triggered"
## ...triggers filtering on receiving update notifications
assert capture_log([level: :debug], fn ->
Variable.remove(x_var, 1)
Process.sleep(10)
end) =~ "Failure for variable #{inspect(x_var.id)}"
end
test "entailment with initially unfixed variables" do
x = 0..2
y = -5..5
variables = Enum.map([x, y], fn d -> IntVariable.new(d) end)
{:ok, [x_var, y_var] = bound_vars, store} = ConstraintStore.create_store(Store, variables)
{:ok, propagator_thread} = PropagatorThread.create_thread(self(), {NotEqual, bound_vars})
Process.sleep(10)
refute capture_log([level: :debug], fn ->
Store.update(store, x_var, :fix, [1])
Process.sleep(10)
end) =~ @entailment_str
entailment_log =
capture_log([level: :debug], fn ->
Store.update(store, y_var, :fix, [2])
Process.sleep(10)
end)
## An entailment happens exactly once
assert number_of_occurences(entailment_log, @entailment_str) == 1
## Propagator thread discards itself on entailment
Process.sleep(10)
refute Process.alive?(propagator_thread)
end
test "entailment with initially fixed variables" do
x = 0..0
y = 1..1
variables = Enum.map([x, y], fn d -> IntVariable.new(d) end)
{:ok, bound_vars, _store} = ConstraintStore.create_store(Store, variables)
assert capture_log([level: :debug], fn ->
{:ok, propagator_thread} =
PropagatorThread.create_thread(self(), {NotEqual, bound_vars})
## Propagator thread discards itself on entailment
Process.sleep(10)
refute Process.alive?(propagator_thread)
end) =~ @entailment_str
end
test "Starting/stopping propagator subscribes it to/unsubscribes it from its variables" do
x = 0..2
y = -5..5
variables = Enum.map([x, y], fn d -> IntVariable.new(d) end)
{:ok, vars, _store} = ConstraintStore.create_store(Store, variables)
{:ok, propagator_thread} = PropagatorThread.create_thread(self(), {NotEqual, vars})
assert Enum.all?(vars, fn v -> propagator_thread in Variable.subscribers(v) end)
PropagatorThread.dispose(propagator_thread)
Process.sleep(10)
refute Process.alive?(propagator_thread)
refute Enum.any?(vars, fn v -> propagator_thread in Variable.subscribers(v) end)
end
test "stability" do
x = 0..5
y = 1..3
variables = Enum.map([x, y], fn d -> IntVariable.new(d) end)
{:ok, [x_var, y_var] = vars, store} = ConstraintStore.create_store(Store, variables)
## Detects stability on a startup
assert capture_log([level: :debug], fn ->
{:ok, _propagator_thread} =
PropagatorThread.create_thread(self(), {NotEqual, vars})
Process.sleep(10)
end) =~ "is stable"
## Filtering that leaves unfixed variable(s) (x_var in this case) should
## (eventually) put propagator into 'stable' state
assert capture_log([level: :debug], fn ->
Store.update(store, y_var, :fix, [1])
Process.sleep(10)
end) =~ "is stable"
## Fixing all variables (i.e., entailment)
## does not result in stability.
refute capture_log([level: :debug], fn ->
Store.update(store, x_var, :fix, [0])
Process.sleep(10)
end) =~ "is stable"
end
test "propagator failure" do
x = 1..1
y = 1..2
z = 2..2
variables = Enum.map([x, y, z], fn d -> IntVariable.new(d) end)
{:ok, [x_var, y_var, z_var] = _vars, _store} =
ConstraintStore.create_store(Store, variables)
{:ok, _threadXY} =
PropagatorThread.create_thread(self(), {NotEqual, [x_var, y_var]}, id: "X != Y")
{:ok, _threadYZ} =
PropagatorThread.create_thread(self(), {NotEqual, [y_var, z_var]}, id: "Y != Z")
Process.sleep(5)
assert 1 == Variable.min(x_var)
## Non-deterministic failure - fails on either 'y' or 'z', depending on which propagator fixes first.
assert :fail == Variable.min(z_var) || :fail == Variable.min(y_var)
end
end
end