Packages
fixpoint
0.4.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/store/store.ex
defmodule CPSolver.ConstraintStore do
@moduledoc """
Constraint store is a key-value store, where `key` is a variable id,
and `value` is a implementation-dependent structure that allows to
update and keep track of variables' domains.
"""
#################
alias CPSolver.{Common, Variable}
alias CPSolver.Propagator.ConstraintGraph
require Logger
@type get_operation :: Common.domain_get_operation() | nil
@type update_operation :: Common.domain_update_operation()
@mandatory_notifications [:fixed, :fail]
def default_store() do
CPSolver.Store.ETS
end
### Callbacks
## Tell basic constraints (a.k.a, domains) to a constraint store
@callback create(variables :: Enum.t(), opts :: Keyword.t()) ::
{:ok, any()} | {:error, any()}
## Get variable details
@callback get(store :: any(), variable :: Variable.t(), get_operation(), [any()]) ::
{:ok, any()} | {:error, any()}
@callback update(store :: any(), variable :: Variable.t(), update_operation(), [any()]) ::
any()
@callback update_domain(store :: any(), variable :: Variable.t(), update_operation(), [any()]) ::
any()
@callback dispose(store :: any(), variables :: [Variable.t()]) :: :ok | :not_found
@callback domain(store :: any(), variable :: Variable.t()) :: {:ok, any()} | {:error, any()}
@callback on_fail(store :: any(), variable :: Variable.t()) :: any()
@callback on_no_change(store :: any(), variable :: Variable.t()) :: any()
@callback on_change(
store :: any(),
variable :: Variable.t(),
change :: Common.domain_change()
) :: any()
@callback get_variables(store :: any()) :: [any()]
@callback subscribe(
store :: any(),
subscriptions :: [%{pid: pid(), variable: any(), events: [any()]}]
) :: :ok | :not_found
### API
defmacro __using__(_) do
quote do
@behaviour CPSolver.ConstraintStore
@domain_events CPSolver.Common.domain_events()
require Logger
def update(store, variable, operation, args) do
update_domain(store, variable, operation, args)
|> tap(fn
:fail ->
on_fail(store, variable)
:no_change ->
on_no_change(store, variable)
change when change in @domain_events ->
on_change(store, variable, change)
end)
end
defoverridable update: 4
end
end
def default_store_opts() do
[space: self(), store_impl: default_store()]
end
def create_store(variables, opts \\ [])
def create_store(variables, opts) do
opts = Keyword.merge(default_store_opts(), opts)
space = Keyword.get(opts, :space)
store_impl = Keyword.get(opts, :store_impl)
constraint_graph = Keyword.get(opts, :constraint_graph)
{:ok, store_handle} = store_impl.create(variables, opts)
store = %{
space: space,
handle: store_handle,
store_impl: store_impl,
constraint_graph: constraint_graph
}
{:ok,
Enum.map(variables, fn var ->
var
|> Map.put(:id, var.id)
|> Map.put(:name, var.name)
|> Map.put(:store, store)
end), store}
end
def domain(%{handle: handle, store_impl: store_impl} = _store, variable) do
store_impl.domain(handle, variable)
end
def get(%{handle: handle, store_impl: store_impl} = _store, variable, operation, args \\ []) do
store_impl.get(handle, variable, operation, args)
end
def subscribe(%{handle: handle, store_impl: store_impl} = _store, variables) do
store_impl.subscribe(handle, variables)
end
def update(
%{handle: handle, store_impl: store_impl} = _store,
variable,
operation,
args \\ []
) do
store_impl.update(handle, variable, operation, args)
end
def get_variables(%{handle: handle, store_impl: store_impl} = _store) do
store_impl.get_variables(handle)
end
def dispose(%{handle: handle, store_impl: store_impl} = _store, variables) do
store_impl.dispose(handle, variables)
end
def normalize_subscription(%{variable: variable, events: events} = subscription) do
%{subscription | variable: variable_id(variable), events: normalize_events(events)}
end
def notify(_variable, _event, opts \\ [])
def notify(_variable, :no_change, _opts) do
:ignore
end
@doc """
Notify on variable event.
If space = nil (the store is detached from space), then notifications are passed directly to propagators.
Otherwise they go to the space process.
"""
def notify(%{id: var_id, subscriptions: subscriptions, store: store} = variable, event, opts) do
(store.constraint_graph &&
(
## Don't notify the propagator that fired this event.
source_propagator = Keyword.get(opts, :source)
propagator_refs =
ConstraintGraph.get_propagators(store.constraint_graph, var_id, event)
|> List.delete(source_propagator)
notify_space(variable, event, propagator_refs)
)) ||
(
propagator_pids =
Enum.flat_map(subscriptions, fn s -> (notify_subscriber?(s, event) && [s.pid]) || [] end)
Enum.each(propagator_pids, fn pid -> notify_process(pid, var_id, event) end)
)
end
defp notify_subscriber?(%{events: events} = _subscription, event) do
event in (@mandatory_notifications ++ events)
end
defp notify_space(%{id: var_id, store: store} = _variable, :fail, _propagator_pids) do
notify_process(store.space, var_id, :fail)
end
defp notify_space(
%{id: var_id, store: store} = _variable,
domain_change,
propagator_pids
)
when is_list(propagator_pids) do
length(propagator_pids) > 0 &&
notify_process(store.space, var_id, {domain_change, propagator_pids})
end
defp notify_process(pid, var_id, event) do
send(pid, {event, var_id})
end
def variable_id(%Variable{id: id}) do
id
end
def variable_id(id) do
id
end
defp normalize_events(events) do
## :fixed and :fail are mandatory
[:fixed, :fail | events]
|> Enum.uniq()
end
end