Packages
fixpoint
0.8.26
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/core/propagator/propagator.ex
defmodule CPSolver.Propagator do
@type propagator_event :: :domain_change | :bound_change | :min_change | :max_change | :fixed
@callback reset(args :: list(), state :: map()) :: map() | nil
@callback filter(args :: list()) :: {:state, map()} | :stable | :fail | propagator_event()
@callback filter(args :: list(), state :: map() | nil) ::
{:state, map()} | :stable | :fail | propagator_event()
@callback filter(args :: list(), state :: map() | nil, changes :: map()) ::
{:state, map()} | :stable | :fail | propagator_event()
@callback variables(args :: list()) :: list()
@callback arguments(args :: list()) :: Arrays.t()
alias CPSolver.Variable
alias CPSolver.Variable.View
alias CPSolver.Propagator.Variable, as: PropagatorVariable
alias CPSolver.DefaultDomain, as: Domain
alias CPSolver.ConstraintStore
alias CPSolver.Utils.TupleArray
defmacro __using__(_) do
quote do
alias CPSolver.Propagator
alias CPSolver.Variable.Interface
alias CPSolver.DefaultDomain, as: Domain
import CPSolver.Propagator.Variable
@behaviour Propagator
def new(args) do
Propagator.new(__MODULE__, arguments(args))
end
def arguments(args) do
args
end
def reset(_args, state) do
state
end
def filter(args, _propagator_state) do
filter(args)
end
def filter(args, propagator_state, _incoming_changes) do
filter(args, propagator_state)
end
def variables(args) do
Propagator.default_variables_impl(args)
end
defoverridable arguments: 1, variables: 1, reset: 2, filter: 2, filter: 3
end
end
def propagator_events() do
[:domain_change, :bound_change, :min_change, :max_change, :fixed]
end
def default_variables_impl(args) do
args
|> Enum.reject(fn arg -> is_constant_arg(arg) end)
end
def new(mod, args, opts \\ []) do
id = Keyword.get_lazy(opts, :id, fn -> make_ref() end)
name = Keyword.get(opts, :name, id)
%{
id: id,
name: name,
mod: mod,
args: args
}
end
def variables(%{mod: mod, args: args} = _propagator) do
args
|> Enum.with_index()
|> Enum.map(fn {arg, idx} ->
(is_constant_arg(arg) && arg) ||
Map.put(arg, :arg_position, idx)
end)
|> mod.variables()
end
def reset(%{mod: mod, args: args} = propagator) do
Map.put(propagator, :state, mod.reset(args, Map.get(propagator, :state)))
end
def filter(%{mod: mod, args: args} = propagator, opts \\ []) do
PropagatorVariable.reset_variable_ops()
store = Keyword.get(opts, :store)
state = propagator[:state]
ConstraintStore.set_store(store)
## Propagation changes
## The propagation may reshedule the filtering and pass the changes that woke
## the propagator.
incoming_changes = Keyword.get(opts, :changes) || %{}
## We will reset the state if required.
## Reset will be forced when the space starts propagation.
reset? = Keyword.get(opts, :reset?, false)
try do
state = (reset? && mod.reset(args, state)) || state
mod.filter(args, state, incoming_changes)
catch
:fail ->
:fail
else
:fail ->
:fail
:stable ->
:stable
result ->
get_filter_changes(result)
end
end
## How propagator events map to domain events
def to_domain_events(:domain_change) do
[:domain_change, :min_change, :max_change, :fixed]
end
def to_domain_events(:bound_change) do
[:min_change, :max_change, :fixed]
end
def to_domain_events(:min_change) do
[:min_change, :fixed]
end
def to_domain_events(:max_change) do
[:max_change, :fixed]
end
def to_domain_events(_fixed) do
[:fixed]
end
@spec get_filter_changes(term()) ::
%{:changes => map(), :state => map(), active?: boolean()}
defp get_filter_changes(propagator_active?) when is_boolean(propagator_active?) do
%{
changes: PropagatorVariable.get_variable_ops(),
active?: propagator_active?,
state: nil
}
end
defp get_filter_changes({:state, state}) do
get_filter_changes(true)
|> Map.put(:state, state)
|> Map.put(:active?, Map.get(state, :active?, true))
end
defp get_filter_changes(result) do
get_filter_changes(result != :passive)
end
def bind_to_variables(propagator, indexed_variables, var_field) do
bound_args =
propagator.args
|> arg_map(fn arg -> bind_to_variable(arg, indexed_variables, var_field) end)
Map.put(propagator, :args, bound_args)
end
defp bind_to_variable(%Variable{id: id} = var, indexed_variables, var_field) do
field_value = Map.get(indexed_variables, id) |> Map.get(var_field)
Map.put(var, var_field, field_value)
end
defp bind_to_variable(%View{variable: variable} = view, indexed_variables, var_field) do
bound_var = bind_to_variable(variable, indexed_variables, var_field)
Map.put(view, :variable, bound_var)
end
defp bind_to_variable(const, _indexed_variables, _var_field) do
const
end
def is_constant_arg(%Variable{} = _arg) do
false
end
def is_constant_arg(%View{} = _arg) do
false
end
def is_constant_arg(_other) do
true
end
def arg_at(args, pos) when is_tuple(args) do
TupleArray.at(args, pos)
end
def arg_at(args, pos) do
Arrays.get(args, pos)
end
def arg_map(args, mapper) when is_function(mapper) and is_list(args) do
Enum.map(args, mapper)
end
def arg_map(args, mapper) when is_function(mapper) and is_tuple(args) do
TupleArray.map(args, mapper)
end
def arg_map(args, mapper) when is_function(mapper) do
Arrays.map(args, mapper)
end
def args_to_list(args) when is_tuple(args) do
Tuple.to_list(args)
end
def args_to_list(args) do
args
end
end