///|
/// Variable selection strategy used by the depth-first search engine.
pub enum VariableHeuristic {
MinimumRemainingValues
FirstUnassigned
MaximumDegree
DomOverDegree
} derive(Debug, Eq)
///|
/// Value ordering strategy used when branching on a variable.
pub enum ValueHeuristic {
AscendingValues
DescendingValues
MedianFirst
} derive(Debug, Eq)
///|
/// Search behavior that is stable across all supported backends.
pub struct SearchConfig {
mut max_solutions : Int
mut variable_heuristic : VariableHeuristic
mut value_heuristic : ValueHeuristic
mut propagation_rounds : Int
mut enable_learning : Bool
mut node_limit : Int
} derive(Debug)
///|
/// Construct the default deterministic configuration.
pub fn default_search_config() -> SearchConfig {
{
max_solutions: 1,
variable_heuristic: MinimumRemainingValues,
value_heuristic: AscendingValues,
propagation_rounds: 10000,
enable_learning: true,
node_limit: 2147483647,
}
}
///|
/// Construct a configuration suitable for exhaustive enumeration.
pub fn exhaustive_search_config(max_solutions : Int) -> SearchConfig {
let config = default_search_config()
config.max_solutions = if max_solutions < 1 { 1 } else { max_solutions }
config
}
///|
/// Configuration using first-unassigned branching.
pub fn first_unassigned_config() -> SearchConfig {
let config = default_search_config()
config.variable_heuristic = FirstUnassigned
config
}
///|
/// Configuration favoring highly constrained variables.
pub fn maximum_degree_config() -> SearchConfig {
let config = default_search_config()
config.variable_heuristic = MaximumDegree
config
}
///|
/// Configuration using domain-over-degree branching.
pub fn domain_over_degree_config() -> SearchConfig {
let config = default_search_config()
config.variable_heuristic = DomOverDegree
config
}
///|
/// Configuration that tries large values first.
pub fn descending_value_config() -> SearchConfig {
let config = default_search_config()
config.value_heuristic = DescendingValues
config
}
///|
/// Configuration that tries the middle of a domain first.
pub fn median_value_config() -> SearchConfig {
let config = default_search_config()
config.value_heuristic = MedianFirst
config
}
///|
/// Set the solution limit.
pub fn SearchConfig::limit(self : SearchConfig, count : Int) -> SearchConfig {
self.max_solutions = if count < 1 { 1 } else { count }
self
}
///|
/// Select a variable heuristic.
pub fn SearchConfig::choose_variables(
self : SearchConfig,
heuristic : VariableHeuristic,
) -> SearchConfig {
self.variable_heuristic = heuristic
self
}
///|
/// Select a value ordering heuristic.
pub fn SearchConfig::choose_values(
self : SearchConfig,
heuristic : ValueHeuristic,
) -> SearchConfig {
self.value_heuristic = heuristic
self
}
///|
/// Set the maximum number of propagation rounds at one search node.
pub fn SearchConfig::round_limit(
self : SearchConfig,
rounds : Int,
) -> SearchConfig {
self.propagation_rounds = if rounds < 1 { 1 } else { rounds }
self
}
///|
/// Enable or disable lightweight conflict learning.
pub fn SearchConfig::learning(
self : SearchConfig,
enabled : Bool,
) -> SearchConfig {
self.enable_learning = enabled
self
}
///|
/// Cap visited search nodes for interactive or service workloads.
pub fn SearchConfig::node_budget(
self : SearchConfig,
nodes : Int,
) -> SearchConfig {
self.node_limit = if nodes < 1 { 1 } else { nodes }
self
}
///|
/// Counters collected by the solver during the most recent run.
pub struct SearchStats {
mut nodes : Int
mut failures : Int
mut propagations : Int
mut constraint_checks : Int
mut pruned_values : Int
mut solutions : Int
mut maximum_depth : Int
mut restarts : Int
mut learned_conflicts : Int
mut truncated : Bool
} derive(Debug)
///|
/// Empty search statistics.
pub fn empty_search_stats() -> SearchStats {
{
nodes: 0,
failures: 0,
propagations: 0,
constraint_checks: 0,
pruned_values: 0,
solutions: 0,
maximum_depth: 0,
restarts: 0,
learned_conflicts: 0,
truncated: false,
}
}
///|
/// Number of visited search nodes.
pub fn SearchStats::node_count(self : SearchStats) -> Int {
self.nodes
}
///|
/// Number of rejected branches.
pub fn SearchStats::failure_count(self : SearchStats) -> Int {
self.failures
}
///|
/// Number of domain propagation passes.
pub fn SearchStats::propagation_count(self : SearchStats) -> Int {
self.propagations
}
///|
/// Number of individual constraint evaluations.
pub fn SearchStats::check_count(self : SearchStats) -> Int {
self.constraint_checks
}
///|
/// Number of values removed by propagation.
pub fn SearchStats::pruned_count(self : SearchStats) -> Int {
self.pruned_values
}
///|
/// Number of solutions found.
pub fn SearchStats::solution_count(self : SearchStats) -> Int {
self.solutions
}
///|
/// Maximum recursion depth reached.
pub fn SearchStats::depth(self : SearchStats) -> Int {
self.maximum_depth
}
///|
/// Whether a node or propagation budget stopped the search.
pub fn SearchStats::is_truncated(self : SearchStats) -> Bool {
self.truncated
}
///|
/// Render statistics as stable key-value output for CI and benchmarks.
pub fn SearchStats::describe(self : SearchStats) -> String {
"nodes=\{self.nodes}, failures=\{self.failures}, propagations=\{self.propagations}, checks=\{self.constraint_checks}, pruned=\{self.pruned_values}, solutions=\{self.solutions}, depth=\{self.maximum_depth}, restarts=\{self.restarts}, learned=\{self.learned_conflicts}, truncated=\{self.truncated}"
}
///|
/// Direction used by optimization helpers.
pub enum OptimizationDirection {
MinimizeValue
MaximizeValue
} derive(Debug, Eq)
///|
/// Select minimization without constructing the enum directly.
pub fn minimize() -> OptimizationDirection {
MinimizeValue
}
///|
/// Select maximization without constructing the enum directly.
pub fn maximize() -> OptimizationDirection {
MaximizeValue
}
///|
/// A result that includes both a solution and its measured objective value.
pub struct OptimizationResult {
solution : Solution
objective : Int
} derive(Debug)
///|
/// Return the optimized solution.
pub fn OptimizationResult::solution(self : OptimizationResult) -> Solution {
self.solution
}
///|
/// Return the objective value.
pub fn OptimizationResult::objective(self : OptimizationResult) -> Int {
self.objective
}