Packages
fixpoint
0.1.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/variables/variable.ex
defmodule CPSolver.Variable do
defstruct [:id, :name, :domain, :space]
@type t :: %__MODULE__{
id: reference(),
name: String.t(),
domain: Domain.t()
}
alias CPSolver.Variable
alias CPSolver.Store.Registry, as: Store
alias CPSolver.Utils
alias CPSolver.DefaultDomain, as: Domain
require Logger
@callback new(values :: Enum.t(), opts :: Keyword.t()) :: Variable.t()
defmacro __using__(_) do
quote do
@behaviour CPSolver.Variable
def new(values, opts \\ default_opts()) do
%Variable{
id: make_ref(),
domain: Domain.new(values)
}
end
defp default_opts() do
[domain_impl: CPSolver.DefaultDomain]
end
defoverridable new: 2
end
end
def domain(variable) do
store_op(:domain, variable)
end
def size(variable) do
store_op(:size, variable)
end
def fixed?(%{domain: domain} = variable) do
Domain.fixed?(domain) ||
store_op(:fixed?, variable)
end
def fixed?(variable) do
store_op(:fixed?, variable)
end
def min(variable) do
store_op(:min, variable)
end
def max(variable) do
store_op(:max, variable)
end
def contains?(variable, value) do
store_op(:contains?, variable, value)
end
def remove(variable, value) do
store_op(:remove, variable, value)
end
def removeAbove(variable, value) do
store_op(:removeAbove, variable, value)
end
def removeBelow(variable, value) do
store_op(:removeBelow, variable, value)
end
def fix(variable, value) do
store_op(:fix, variable, value)
end
defp store_op(op, variable, value) when op in [:remove, :removeAbove, :removeBelow, :fix] do
Store.update(variable.space, variable, op, [value])
end
defp store_op(op, variable, value) when op in [:contains?] do
Store.get(variable.space, variable, op, [value])
end
defp store_op(op, variable) when op in [:size, :fixed?, :min, :max] do
Store.get(variable.space, variable, op)
end
defp store_op(:domain, variable) do
Store.domain(variable.space, variable)
end
def subscribers(variable) do
Utils.subscribers({:variable, variable.id})
end
def bind_variables(space, variables) do
Enum.map(variables, fn var -> bind(var, space) end)
end
def bind(variable, space) do
Map.put(variable, :space, space)
end
def set_store(variable, store) do
Map.put(variable, :store, store)
end
end
defmodule CPSolver.Variable.Agent do
alias CPSolver.DefaultDomain, as: Domain
alias CPSolver.Store.Registry, as: StoreRegistry
alias CPSolver.Utils
require Logger
@behaviour GenServer
def create(space, %{id: id} = variable) do
{:ok, _} = Registry.register(StoreRegistry, id, space)
{:ok, _pid} =
GenServer.start_link(__MODULE__, variable, name: StoreRegistry.variable_proc_id(variable))
end
def dispose(variable) do
topic = {variable, variable.id}
Enum.each(Utils.subscribers(topic), fn s -> Utils.unsubscribe(s, topic) end)
GenServer.stop(StoreRegistry.variable_proc_id(variable))
end
@impl true
@spec init(atom | %{:domain => any, optional(any) => any}) :: {:ok, :gb_sets.set(any)}
def init(variable) do
{:ok, Domain.new(variable.domain)}
end
def operation(var, operation, args \\ []) do
GenServer.call(StoreRegistry.variable_proc_id(var), {var, operation, args})
end
@impl true
def handle_call({var, op, _args}, _from, :fail) do
handle_op_on_failed_var(var, op)
{:reply, :fail, :fail}
end
## Read ops
def handle_call({_var, :domain, _args}, _from, domain) do
{:reply, domain, domain}
end
def handle_call({_var, op, args}, _from, domain)
when op in [:size, :fixed?, :min, :max, :contains?] do
{:reply, apply(Domain, op, [domain | args]), domain}
end
## Write ops
def handle_call({var, op, args}, _from, domain)
when op in [:remove, :removeAbove, :removeBelow, :fix] do
{result, updated_domain} =
case apply(Domain, op, [domain | args]) do
:fail ->
{:fail, :fail}
|> tap(fn _ -> handle_failure(var) end)
:none ->
{:no_change, domain}
|> tap(fn _ -> handle_domain_no_change(var) end)
{domain_change, new_domain} ->
{domain_change, new_domain}
|> tap(fn _ -> handle_domain_change(domain_change, var, new_domain) end)
end
{:reply, result, updated_domain}
end
defp handle_op_on_failed_var(var, operation) do
Logger.warning(
"Attempt to request #{inspect(operation)} on failed variable #{inspect(var.id)}"
)
end
defp handle_failure(var) do
Logger.debug("Failure for variable #{inspect(var.id)}")
## TODO: notify space (and maybe don't notify propagators)
publish(var, {:fail, var.id})
end
defp handle_domain_no_change(_var) do
:ok
end
defp handle_domain_change(domain_change, var, _domain) do
publish(var, {domain_change, var.id})
|> tap(fn _ ->
Logger.debug("Domain change (#{domain_change}) for #{inspect(var.id)}")
end)
end
defp publish(var, message) do
Utils.publish({:variable, var.id}, message)
end
end