Current section

Files

Jump to
fixpoint lib solver core propagator constraint_graph.ex
Raw

lib/solver/core/propagator/constraint_graph.ex

defmodule CPSolver.Propagator.ConstraintGraph do
@moduledoc """
The constraint graph connects propagators and their variables.
The edge between a propagator and a variable represents a notification
the propagator receives upon varable's domain change.
"""
alias CPSolver.Propagator
alias CPSolver.Variable
@spec create([Propagator.t()] | %{reference() => Propagator.t()}) :: Graph.t()
def create(propagators) when is_list(propagators) do
propagators
|> Enum.map(fn p -> {make_ref(), p} end)
|> Map.new()
|> create()
end
def create(propagators) when is_map(propagators) do
Enum.reduce(propagators, Graph.new(), fn {propagator_id, {propagator_mod, args}} = propagator,
acc ->
args
|> propagator_mod.variables()
|> Enum.reduce(acc, fn var, acc2 ->
Graph.add_vertex(acc2, {:propagator, propagator_id})
|> Graph.label_vertex({:propagator, propagator_id}, propagator)
|> Graph.add_edge({:variable, var.id}, {:propagator, propagator_id},
label: get_propagate_on(var)
)
end)
end)
end
## Get a list of propagator ids that "listen" to the domain change of given variable.
def get_propagator_ids(
constraint_graph,
variable_id,
domain_change
) do
constraint_graph
|> Graph.edges({:variable, variable_id})
|> Enum.flat_map(fn edge ->
(domain_change in edge.label &&
[edge.v2 |> elem(1)]) || []
end)
end
def get_propagator(%Graph{} = graph, propagator_id) do
Graph.vertex_labels(graph, {:propagator, propagator_id}) |> hd
end
def remove_propagator(table_or_graph, propagator_id) do
remove_vertex(table_or_graph, {:propagator, propagator_id})
end
def remove_variable(table_or_graph, variable_id) do
remove_vertex(table_or_graph, {:variable, variable_id})
end
def remove_vertex(graph, vertex) do
Graph.delete_vertex(graph, vertex)
end
defp get_propagate_on(%Variable{} = variable) do
Map.get(variable, :propagate_on, Propagator.to_domain_events(:fixed))
end
end