csp_solver/solver/
propagate.rs1use crate::adjacency::Adjacency;
4use crate::constraint::{ConstraintEnum, VarId};
5use crate::domain::Domain;
6use crate::variable::Variable;
7use crate::SolveStats;
8
9pub fn forward_check<D: Domain>(
13 var: VarId,
14 variables: &mut [Variable<D>],
15 constraints: &[ConstraintEnum<D>],
16 adjacency: &Adjacency,
17 assignment: &mut [Option<D::Value>],
18 stats: &mut SolveStats,
19 depth: usize,
20) -> bool
21where
22 D::Value: PartialEq,
23{
24 let mut val_buf: Vec<D::Value> = Vec::new();
25
26 for &neighbor in adjacency.neighbors_of_var(var) {
27 if assignment[neighbor as usize].is_some() {
28 continue;
29 }
30
31 val_buf.clear();
33 val_buf.extend(variables[neighbor as usize].domain.iter());
34
35 for val in &val_buf {
36 assignment[neighbor as usize] = Some(val.clone());
37
38 let mut valid = true;
39 for &ci in adjacency.constraints_for(neighbor) {
40 let ci = ci as usize;
41 let scope = constraints[ci].scope();
42 if scope.iter().all(|&v| assignment[v as usize].is_some())
43 && !constraints[ci].check(assignment)
44 {
45 valid = false;
46 break;
47 }
48 }
49
50 assignment[neighbor as usize] = None;
51
52 if !valid {
53 variables[neighbor as usize].prune(val, depth);
54 stats.propagations += 1;
55 }
56 }
57
58 if variables[neighbor as usize].domain.is_empty() {
59 return true;
60 }
61 }
62
63 false
64}
65
66pub fn ac_fc<D: Domain>(
68 var: VarId,
69 variables: &mut [Variable<D>],
70 constraints: &[ConstraintEnum<D>],
71 adjacency: &Adjacency,
72 assignment: &mut [Option<D::Value>],
73 stats: &mut SolveStats,
74 depth: usize,
75) -> bool
76where
77 D::Value: PartialEq,
78{
79 if forward_check(var, variables, constraints, adjacency, assignment, stats, depth) {
80 return true;
81 }
82
83 let mut worklist: Vec<VarId> = Vec::new();
84 for &neighbor in adjacency.neighbors_of_var(var) {
85 if assignment[neighbor as usize].is_none()
86 && variables[neighbor as usize].domain.is_singleton()
87 {
88 worklist.push(neighbor);
89 }
90 }
91
92 let mut visited = vec![false; variables.len()];
93 visited[var as usize] = true;
94
95 while let Some(v) = worklist.pop() {
96 if visited[v as usize] {
97 continue;
98 }
99 visited[v as usize] = true;
100
101 let singleton_val = variables[v as usize].domain.singleton_value().unwrap();
102 assignment[v as usize] = Some(singleton_val);
103
104 if forward_check(v, variables, constraints, adjacency, assignment, stats, depth) {
105 assignment[v as usize] = None;
106 return true;
107 }
108
109 assignment[v as usize] = None;
110
111 for &neighbor in adjacency.neighbors_of_var(v) {
112 if assignment[neighbor as usize].is_none()
113 && !visited[neighbor as usize]
114 && variables[neighbor as usize].domain.is_singleton()
115 {
116 worklist.push(neighbor);
117 }
118 }
119 }
120
121 false
122}