Current section

Files

Jump to
fixpoint lib solver constraints propagators all_different_fwc.ex
Raw

lib/solver/constraints/propagators/all_different_fwc.ex

defmodule CPSolver.Propagator.AllDifferent.FWC do
use CPSolver.Propagator
@moduledoc """
The forward-checking propagator for AllDifferent constraint.
"""
defp initial_state(args) do
%{unfixed_vars: Enum.to_list(0..(length(args) - 1))}
end
@impl true
def variables(args) do
Enum.map(args, fn x_el -> set_propagate_on(x_el, :fixed) end)
end
@impl true
def filter(args) do
filter(args, initial_state(args))
end
def filter(args, nil) do
filter(args, initial_state(args))
end
@impl true
def filter(all_vars, %{unfixed_vars: unfixed_vars} = _state) do
updated_unfixed_vars = filter_impl(all_vars, unfixed_vars)
{:state, %{unfixed_vars: updated_unfixed_vars}}
end
defp filter_impl(all_vars, unfixed_vars) do
fwc(all_vars, unfixed_vars, MapSet.new(), [], false)
end
## The list of unfixed variables exhausted, and there were no fixed values.
## We stop here
defp fwc(_all_vars, [], _fixed_values, unfixed_ids, false) do
unfixed_ids
end
## The list of unfixed variables exhausted, and some new fixed values showed up.
## We go through unfixed ids we have collected during previous stage again
defp fwc(all_vars, [], fixed_values, ids_to_revisit, true) do
fwc(all_vars, ids_to_revisit, fixed_values, [], false)
end
## There is still some (previously) unfixed values to check
defp fwc(all_vars, [idx | rest], fixed_values, ids_to_revisit, changed?) do
var = Enum.at(all_vars, idx)
remove_all(var, fixed_values)
if fixed?(var) do
## Variable is fixed or was fixed as a result of removing all fixed values
fwc(all_vars, rest, MapSet.put(fixed_values, min(var)), ids_to_revisit, true)
else
## Still not fixed, put it to 'revisit' list
fwc(all_vars, rest, fixed_values, [idx | ids_to_revisit], changed?)
end
end
## Remove values from the domain of variable
defp remove_all(variable, values) do
Enum.map(
values,
fn value ->
remove(variable, value)
end
)
|> Enum.any?(fn res -> res == :fixed end)
end
end