Packages
fixpoint
0.22.2
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
lib/solver/constraints/propagators/all_different/all_different_bc.ex
defmodule CPSolver.Propagator.AllDifferent.BC do
use CPSolver.Propagator
import CPSolver.Utils.MutableArray
alias CPSolver.Utils.MutableArray
alias CPSolver.Utils.MutableOrder
@moduledoc """
A fast and simple algorithm for bounds consistency of the alldifferent constraint
(L´opez et al., 2003)
"""
@impl true
def arguments(args) do
Vector.new(args)
end
@impl true
def variables(args) do
Enum.map(args, fn x_el -> set_propagate_on(x_el, :bound_change) end)
end
@impl true
def filter(vars, state, changes) do
updated_state = update_state(vars, state, changes)
filter_impl(vars, updated_state, changes)
{:state, updated_state}
end
defp initialize_state(vars) do
n = Vector.size(vars)
{_, lbs, ubs} = Enum.reduce(vars, {0, [], []}, fn var, {idx, min_acc, max_acc} ->
{idx + 1, [min(var) | min_acc], [max(var) | max_acc]}
end)
%{n: n,
minsorted_order: MutableOrder.new(Enum.reverse(lbs)),
maxsorted_order: MutableOrder.new(Enum.reverse(ubs)),
tree: make_array(2 * n + 2),
diffs: make_array(2 * n + 2),
hall: make_array(2 * n + 2),
bounds: make_array(2 * n + 2),
minrank: make_array(n),
maxrank: make_array(n)
}
end
defp update_state(vars, _state, _changes) do
initialize_state(vars)
end
defp prepare(%{n: n,
minsorted_order: minsorted,
maxsorted_order: maxsorted,
tree: tree,
diffs: diffs,
hall: hall,
bounds: bounds,
minrank: minrank,
maxrank: maxrank
} = state) do
last_min = MutableOrder.get(minsorted, 0)
last_max = MutableOrder.get(maxsorted, 0) + 1
last_bound = last_min - 2
last_min_idx = 0
last_max_idx = 0
last_bound_idx = 0
array_update(bounds, 0, last_bound)
res =
Enum.reduce(
1..(2 * n),
%{
last_min: last_min,
last_max: last_max,
last_bound: last_bound,
last_min_idx: last_min_idx,
last_max_idx: last_max_idx,
last_bound_idx: last_bound_idx
},
fn _idx,
%{
last_min: last_min,
last_max: last_max,
last_bound: last_bound,
last_min_idx: last_min_idx,
last_max_idx: last_max_idx
} = acc ->
cond do
last_min_idx < n && last_min <= last_max ->
## LB values first
acc =
if last_min > last_bound do
## Record new bounds value and advance bounds index
acc
|> Map.put(:last_bound, last_min)
|> Map.put(:last_bound_idx, acc.last_bound_idx + 1)
|> tap(fn acc_ -> array_update(bounds, acc_.last_bound_idx, acc_.last_bound) end)
else
acc
end
## Update minrank
array_update(minrank, array_get(minsorted.sort_index, acc.last_min_idx), acc.last_bound_idx)
## Advance last min idx and record new last min value
acc = Map.put(acc, :last_min_idx, last_min_idx + 1)
if acc.last_min_idx < n do
Map.put(acc, :last_min,
MutableOrder.get(minsorted, acc.last_min_idx))
else
acc
end
true ->
## Switch to UB values
acc =
if last_max > last_bound do
## Record new bounds value and advance bounds index
acc
|> Map.put(:last_bound, last_max)
|> Map.put(:last_bound_idx, acc.last_bound_idx + 1)
|> tap(fn acc_ -> array_update(bounds, acc_.last_bound_idx, acc_.last_bound) end)
else
acc
end
## Update maxrank
array_update(maxrank, array_get(maxsorted.sort_index, acc.last_max_idx), acc.last_bound_idx)
## Advance last max index and record new max value
if last_max_idx + 1 < n do
acc = Map.put(acc, :last_max_idx, last_max_idx + 1)
Map.put(
acc,
:last_max,
MutableOrder.get(maxsorted, acc.last_max_idx) + 1
)
else
acc
end
end
end
)
array_update(bounds, res.last_bound_idx + 1, array_get(bounds, res.last_bound_idx) + 2)
Map.put(state, :n_bounds, res.last_bound_idx)
|> Map.put(:tree, tree)
|> Map.put(:diffs, diffs)
|> Map.put(:hall, hall)
|> Map.put(:bounds, bounds)
|> Map.put(:minrank, minrank)
|> Map.put(:maxrank, maxrank)
end
defp filter_impl(
vars,
state,
changes
) do
state = prepare(state)
filtered? = filter_lower(vars, state)
filtered? = filter_upper(vars, state) || filtered?
filtered? && filter_impl(vars, state, changes)
state
end
defp filter_lower(
args,
%{
bounds: bounds,
minsorted_order: minsorted_order,
maxsorted_order: maxsorted_order,
minrank: minrank,
maxrank: maxrank,
tree: tree,
hall: hall,
diffs: diffs
} = state
) do
## Initialize internal structures
for idx <- 1..(state.n_bounds + 1) do
array_update(tree, idx, idx - 1)
array_update(hall, idx, idx - 1)
array_update(diffs, idx, array_get(bounds, idx) - array_get(bounds, idx - 1))
end
for {var_idx, _ub} <- MutableOrder.to_sorted(maxsorted_order), reduce: false do
filter_acc? ->
x = array_get(minrank, var_idx)
y = array_get(maxrank, var_idx)
z = pathmax(tree, x + 1)
j = array_get(tree, z)
array_add(diffs, z, -1)
z =
if array_get(diffs, z) == 0 do
array_update(tree, z, z + 1)
pathmax(tree, array_get(tree, z))
|> tap(fn z -> array_update(tree, z, j) end)
else
z
end
pathset(tree, x + 1, z, z)
if array_get(diffs, z) < array_get(bounds, z) - array_get(bounds, y), do: fail()
hall_x = array_get(hall, x)
if hall_x > x do
w = pathmax(hall, hall_x)
pathset(hall, x, w, w)
new_min = array_get(bounds, w)
res = removeBelow(args[var_idx], new_min)
filter_acc? ||
(res != :no_change)
|> tap(fn changed? ->
changed? && MutableOrder.update(minsorted_order, {var_idx, new_min})
end)
# ]
else
filter_acc?
end
|> tap(fn _ ->
if array_get(diffs, z) == array_get(bounds, z) - array_get(bounds, y) do
pathset(hall, array_get(hall, y), j - 1, y)
array_update(hall, y, j - 1)
end
end)
end
end
defp filter_upper(
args,
%{
bounds: bounds,
maxsorted_order: maxsorted_order,
minsorted_order: minsorted_order,
minrank: minrank,
maxrank: maxrank,
tree: tree,
hall: hall,
diffs: diffs
} = state
) do
## Initialize internal structures
for idx <- 0..state.n_bounds do
array_update(tree, idx, idx + 1)
array_update(hall, idx, idx + 1)
array_update(diffs, idx, array_get(bounds, idx + 1) - array_get(bounds, idx))
end
for {var_idx, _lb} <- MutableOrder.to_sorted(minsorted_order, :desc), reduce: false do
filter_acc? ->
x = array_get(maxrank, var_idx)
y = array_get(minrank, var_idx)
z = pathmin(tree, x - 1)
j = array_get(tree, z)
array_add(diffs, z, -1)
z =
if array_get(diffs, z) == 0 do
array_update(tree, z, z - 1)
pathmin(tree, array_get(tree, z))
|> tap(fn z -> array_update(tree, z, j) end)
else
z
end
pathset(tree, x - 1, z, z)
if array_get(diffs, z) < array_get(bounds, y) - array_get(bounds, z), do: fail()
hall_x = array_get(hall, x)
if hall_x < x do
w = pathmin(hall, hall_x)
pathset(hall, x, w, w)
new_max = array_get(bounds, w) - 1
res = removeAbove(args[var_idx], new_max)
filter_acc? ||
(res != :no_change)
|> tap(fn changed? ->
changed? && MutableOrder.update(maxsorted_order, {var_idx, new_max})
end)
else
filter_acc?
end
|> tap(fn _ ->
if array_get(diffs, z) == array_get(bounds, y) - array_get(bounds, z) do
pathset(hall, array_get(hall, y), j + 1, y)
array_update(hall, y, j + 1)
end
end)
end
end
defp pathmax(tree, i) do
case array_get(tree, i) do
n when n > i -> pathmax(tree, n)
_le -> i
end
end
defp pathmin(tree, i) do
case array_get(tree, i) do
n when n < i -> pathmin(tree, n)
_ge -> i
end
end
defp pathset(tree, path_start, path_end, to) do
next = path_start
prev = next
if prev == path_end do
tree
else
next = array_get(tree, prev)
array_update(tree, prev, to)
pathset(tree, next, path_end, to)
end
end
defp fail() do
throw(:fail)
end
defp make_array(arity) when is_integer(arity) do
MutableArray.new(arity)
end
end