csp-solver 0.4.0

Generic constraint satisfaction problem solver with backtracking, AC-3 constraint propagation, and ordering heuristics.
Documentation
//! Pre-built constraint dependency graph with flat arena storage.

use crate::constraint::{ConstraintEnum, VarId};
use crate::domain::Domain;

#[derive(Debug, Clone)]
struct ArenaIndex {
    entries: Vec<(u32, u32)>,
}

impl ArenaIndex {
    fn new(count: usize) -> Self {
        Self {
            entries: vec![(0, 0); count],
        }
    }

    fn get<'a>(&self, idx: usize, pool: &'a [u32]) -> &'a [u32] {
        let (offset, len) = self.entries[idx];
        &pool[offset as usize..(offset + len) as usize]
    }
}

#[derive(Debug, Clone)]
pub struct Adjacency {
    pool: Vec<u32>,
    var_constraints: ArenaIndex,
    constraint_neighbors: ArenaIndex,
    var_neighbors: ArenaIndex,
}

impl Adjacency {
    pub fn build<D: Domain>(num_vars: usize, constraints: &[ConstraintEnum<D>]) -> Self
    where
        D::Value: PartialEq,
    {
        let num_constraints = constraints.len();

        let mut vc_lists: Vec<Vec<u32>> = vec![Vec::new(); num_vars];
        for (ci, c) in constraints.iter().enumerate() {
            for &v in c.scope() {
                vc_lists[v as usize].push(ci as u32);
            }
        }

        let mut cn_lists: Vec<Vec<u32>> = vec![Vec::new(); num_constraints];
        for vc in &vc_lists {
            for &ci in vc {
                for &cj in vc {
                    if ci != cj {
                        cn_lists[ci as usize].push(cj);
                    }
                }
            }
        }
        for neighbors in &mut cn_lists {
            neighbors.sort_unstable();
            neighbors.dedup();
        }

        let mut vn_lists: Vec<Vec<u32>> = vec![Vec::new(); num_vars];
        for c in constraints {
            let scope = c.scope();
            for &vi in scope {
                for &vj in scope {
                    if vi != vj {
                        vn_lists[vi as usize].push(vj);
                    }
                }
            }
        }
        for neighbors in &mut vn_lists {
            neighbors.sort_unstable();
            neighbors.dedup();
        }

        let total_len: usize = vc_lists.iter().map(|v| v.len()).sum::<usize>()
            + cn_lists.iter().map(|v| v.len()).sum::<usize>()
            + vn_lists.iter().map(|v| v.len()).sum::<usize>();
        let mut pool = Vec::with_capacity(total_len);

        let mut var_constraints = ArenaIndex::new(num_vars);
        for (i, list) in vc_lists.iter().enumerate() {
            let offset = pool.len() as u32;
            pool.extend_from_slice(list);
            var_constraints.entries[i] = (offset, list.len() as u32);
        }

        let mut constraint_neighbors = ArenaIndex::new(num_constraints);
        for (i, list) in cn_lists.iter().enumerate() {
            let offset = pool.len() as u32;
            pool.extend_from_slice(list);
            constraint_neighbors.entries[i] = (offset, list.len() as u32);
        }

        let mut var_neighbors = ArenaIndex::new(num_vars);
        for (i, list) in vn_lists.iter().enumerate() {
            let offset = pool.len() as u32;
            pool.extend_from_slice(list);
            var_neighbors.entries[i] = (offset, list.len() as u32);
        }

        Self {
            pool,
            var_constraints,
            constraint_neighbors,
            var_neighbors,
        }
    }

    pub fn constraints_for(&self, var: VarId) -> &[u32] {
        self.var_constraints.get(var as usize, &self.pool)
    }

    pub fn neighbors_of_constraint(&self, ci: usize) -> &[u32] {
        self.constraint_neighbors.get(ci, &self.pool)
    }

    pub fn neighbors_of_var(&self, var: VarId) -> &[u32] {
        self.var_neighbors.get(var as usize, &self.pool)
    }
}