defmodule CPSolver.Search.Partition do
alias CPSolver.Variable.Interface
alias CPSolver.Search.ValueSelector.{Min, Max, Random, Split}
require Logger
def initialize(partition, _space_data) do
## TODO:
# strategy(partition).initialize(space_data)
partition
end
def partition(variable, value_choice) do
{:ok, partition_impl(variable, value_choice)}
end
defp partition_impl(variable, value) when is_integer(value) do
partition_by_fix(variable, value)
end
defp partition_impl(variable, value_choice) when is_function(value_choice) do
partition_impl(variable, value_choice.(variable))
end
defp partition_impl(variable, value_choice) when is_atom(value_choice) do
impl = strategy(value_choice)
selected_value = impl.select_value(variable)
case impl.partition(selected_value) do
reduction when is_function(reduction, 1) ->
reduction.(variable)
functions when is_list(functions) ->
functions
end
|> Enum.map(fn
reduction when is_map(reduction) ->
reduction
reduction when is_function(reduction, 1) ->
%{variable.id => reduction}
end)
end
defp strategy(:indomain_min) do
Min
end
defp strategy(:indomain_max) do
Max
end
defp strategy(:indomain_random) do
Random
end
defp strategy(:indomain_split) do
Split
end
defp strategy(impl) when is_atom(impl) do
if Code.ensure_loaded(impl) == {:module, impl} && function_exported?(impl, :select, 2) do
impl
else
throw({:unknown_strategy, impl})
end
end
## Default partitioning
def partition_by_fix(variable, value) when is_integer(value) do
[
# Equal.new(variable, value)
fixed_value_partition(variable, value),
# NotEqual.new(variable, value)
removed_value_partition(variable, value)
]
end
def fixed_value_partition(variable, value) do
new(
variable,
fn variable -> Interface.fix(variable, value) end
)
end
def removed_value_partition(variable, value) do
new(
variable,
fn variable -> Interface.remove(variable, value) end
)
end
## Here we build a reduction that removes multiple values
## from the domain of variable.
def remove_multiple_values_partition(variable, values) do
new(
variable,
fn variable ->
Enum.reduce(values, Map.new(), fn val, acc ->
if Interface.contains?(variable, val) do
Map.put(acc, variable.id, Interface.remove(variable, val))
else
acc
end
end)
end
)
end
def new(variable, reduction) when is_function(reduction, 1) do
%{variable.id => reduction}
end
end