Skip to main content

oxilean_std/complexity/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use super::functions::*;
6use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
7
8/// DPLL-based propositional SAT solver.
9/// Variables are 1..=n. Clauses are lists of literals (positive = var, negative = -var).
10pub struct DpllSolver {
11    n_vars: usize,
12    clauses: Vec<Vec<i32>>,
13}
14impl DpllSolver {
15    /// Create a new DPLL solver with `n_vars` variables.
16    pub fn new(n_vars: usize) -> Self {
17        Self {
18            n_vars,
19            clauses: vec![],
20        }
21    }
22    /// Add a clause (disjunction of literals).
23    pub fn add_clause(&mut self, clause: Vec<i32>) {
24        self.clauses.push(clause);
25    }
26    /// Solve. Returns `Some(assignment)` if SAT (assignment[i] = value of variable i+1).
27    pub fn solve(&self) -> Option<Vec<bool>> {
28        let mut assignment = vec![None::<bool>; self.n_vars + 1];
29        if self.dpll(&mut assignment) {
30            Some(
31                assignment
32                    .into_iter()
33                    .skip(1)
34                    .map(|v| v.unwrap_or(false))
35                    .collect(),
36            )
37        } else {
38            None
39        }
40    }
41    fn dpll(&self, assignment: &mut Vec<Option<bool>>) -> bool {
42        let mut changed = true;
43        while changed {
44            changed = false;
45            for clause in &self.clauses {
46                let (unset, sat, unit_lit) = self.clause_status(clause, assignment);
47                if sat {
48                    continue;
49                }
50                if unset == 0 {
51                    return false;
52                }
53                if unset == 1 {
54                    let lit = unit_lit.expect(
55                        "unit_lit is Some: unset == 1 means exactly one unset literal was tracked",
56                    );
57                    let var = lit.unsigned_abs() as usize;
58                    let val = lit > 0;
59                    if assignment[var] == Some(!val) {
60                        return false;
61                    }
62                    assignment[var] = Some(val);
63                    changed = true;
64                }
65            }
66        }
67        if self
68            .clauses
69            .iter()
70            .all(|c| self.clause_status(c, assignment).1)
71        {
72            return true;
73        }
74        let var = match (1..=self.n_vars).find(|&v| assignment[v].is_none()) {
75            Some(v) => v,
76            None => return false,
77        };
78        assignment[var] = Some(true);
79        if self.dpll(assignment) {
80            return true;
81        }
82        assignment[var] = Some(false);
83        if self.dpll(assignment) {
84            return true;
85        }
86        assignment[var] = None;
87        false
88    }
89    /// Returns (unset_count, is_satisfied, last_unset_literal)
90    fn clause_status(
91        &self,
92        clause: &[i32],
93        assignment: &[Option<bool>],
94    ) -> (usize, bool, Option<i32>) {
95        let mut unset = 0;
96        let mut last_unset = None;
97        for &lit in clause {
98            let var = lit.unsigned_abs() as usize;
99            match assignment[var] {
100                None => {
101                    unset += 1;
102                    last_unset = Some(lit);
103                }
104                Some(val) => {
105                    if (lit > 0) == val {
106                        return (0, true, None);
107                    }
108                }
109            }
110        }
111        (unset, false, last_unset)
112    }
113}
114/// Evaluate a Boolean circuit on a given input assignment.
115#[allow(dead_code)]
116pub struct CircuitEvaluator {
117    pub gates: Vec<CircuitGate>,
118}
119impl CircuitEvaluator {
120    /// Create a new circuit evaluator.
121    pub fn new() -> Self {
122        Self { gates: vec![] }
123    }
124    /// Add a gate and return its index.
125    pub fn add_gate(&mut self, kind: GateKind, left: Option<usize>, right: Option<usize>) -> usize {
126        let idx = self.gates.len();
127        self.gates.push(CircuitGate { kind, left, right });
128        idx
129    }
130    /// Evaluate the circuit (top gate = last gate index) on `inputs`.
131    pub fn evaluate(&self, inputs: &[bool]) -> bool {
132        let output_gate = self.gates.len().saturating_sub(1);
133        self.eval_gate(output_gate, inputs)
134    }
135    fn eval_gate(&self, idx: usize, inputs: &[bool]) -> bool {
136        let gate = &self.gates[idx];
137        match gate.kind {
138            GateKind::Input(i) => inputs.get(i).copied().unwrap_or(false),
139            GateKind::Const(b) => b,
140            GateKind::And => {
141                let l = gate.left.map(|i| self.eval_gate(i, inputs)).unwrap_or(true);
142                let r = gate
143                    .right
144                    .map(|i| self.eval_gate(i, inputs))
145                    .unwrap_or(true);
146                l && r
147            }
148            GateKind::Or => {
149                let l = gate
150                    .left
151                    .map(|i| self.eval_gate(i, inputs))
152                    .unwrap_or(false);
153                let r = gate
154                    .right
155                    .map(|i| self.eval_gate(i, inputs))
156                    .unwrap_or(false);
157                l || r
158            }
159            GateKind::Not => {
160                let l = gate
161                    .left
162                    .map(|i| self.eval_gate(i, inputs))
163                    .unwrap_or(false);
164                !l
165            }
166        }
167    }
168}
169/// Simple Sudoku solver using backtracking (9×9).
170pub struct SudokuSolver {
171    /// 81-element grid, 0 = empty
172    pub grid: [u8; 81],
173}
174impl SudokuSolver {
175    /// Create a new solver from a 81-element grid.
176    pub fn new(grid: [u8; 81]) -> Self {
177        Self { grid }
178    }
179    /// Solve the Sudoku. Returns true if a solution is found.
180    pub fn solve(&mut self) -> bool {
181        for pos in 0..81 {
182            if self.grid[pos] == 0 {
183                let row = pos / 9;
184                let col = pos % 9;
185                for digit in 1u8..=9 {
186                    if self.is_valid(row, col, digit) {
187                        self.grid[pos] = digit;
188                        if self.solve() {
189                            return true;
190                        }
191                        self.grid[pos] = 0;
192                    }
193                }
194                return false;
195            }
196        }
197        true
198    }
199    fn is_valid(&self, row: usize, col: usize, digit: u8) -> bool {
200        for c in 0..9 {
201            if self.grid[row * 9 + c] == digit {
202                return false;
203            }
204        }
205        for r in 0..9 {
206            if self.grid[r * 9 + col] == digit {
207                return false;
208            }
209        }
210        let br = (row / 3) * 3;
211        let bc = (col / 3) * 3;
212        for r in br..br + 3 {
213            for c in bc..bc + 3 {
214                if self.grid[r * 9 + c] == digit {
215                    return false;
216                }
217            }
218        }
219        true
220    }
221}
222/// Evaluate a Boolean circuit represented as a DAG.
223/// Gates: 0 = AND, 1 = OR, 2 = NOT, 3 = INPUT (index stored in `left`).
224#[derive(Clone, Copy, PartialEq, Eq)]
225pub enum GateKind {
226    And,
227    Or,
228    Not,
229    Input(usize),
230    Const(bool),
231}
232/// Compute rank-based lower bounds for communication complexity.
233/// Uses Gaussian elimination over GF(2) to find rank of the communication matrix.
234#[allow(dead_code)]
235pub struct CommunicationMatrixAnalyzer {
236    /// rows × cols matrix over GF(2)
237    pub matrix: Vec<Vec<u8>>,
238}
239impl CommunicationMatrixAnalyzer {
240    /// Create from a matrix (entries should be 0 or 1).
241    pub fn new(matrix: Vec<Vec<u8>>) -> Self {
242        Self { matrix }
243    }
244    /// Compute the GF(2) rank of the matrix.
245    pub fn rank_gf2(&self) -> usize {
246        let rows = self.matrix.len();
247        if rows == 0 {
248            return 0;
249        }
250        let cols = self.matrix[0].len();
251        let mut mat: Vec<Vec<u8>> = self.matrix.iter().map(|r| r.clone()).collect();
252        let mut rank = 0;
253        let mut pivot_row = 0;
254        for col in 0..cols {
255            let mut found = None;
256            for row in pivot_row..rows {
257                if mat[row][col] == 1 {
258                    found = Some(row);
259                    break;
260                }
261            }
262            if let Some(r) = found {
263                mat.swap(pivot_row, r);
264                for row in 0..rows {
265                    if row != pivot_row && mat[row][col] == 1 {
266                        let pivot = mat[pivot_row].clone();
267                        let target = &mut mat[row];
268                        for j in 0..cols {
269                            target[j] ^= pivot[j];
270                        }
271                    }
272                }
273                rank += 1;
274                pivot_row += 1;
275            }
276        }
277        rank
278    }
279    /// Log2 rank lower bound on deterministic communication complexity.
280    /// Returns floor(log2(r)) where r is the GF(2) rank.
281    pub fn log_rank_lower_bound(&self) -> usize {
282        let r = self.rank_gf2();
283        if r == 0 {
284            return 0;
285        }
286        (usize::BITS - 1 - r.leading_zeros()) as usize
287    }
288}
289#[allow(dead_code)]
290pub struct CircuitGate {
291    pub kind: GateKind,
292    pub left: Option<usize>,
293    pub right: Option<usize>,
294}
295/// Check whether an algorithm runs within FPT time bound f(k) * n^c.
296/// Accepts observed runtime, parameter k, input size n, computable f, and constant c.
297#[allow(dead_code)]
298pub struct ParameterizedAlgorithmChecker {
299    /// Name of the parameter
300    pub param_name: String,
301}
302impl ParameterizedAlgorithmChecker {
303    /// Create a new checker.
304    pub fn new(param_name: &str) -> Self {
305        Self {
306            param_name: param_name.to_string(),
307        }
308    }
309    /// Return true if `observed` ≤ f(k) * n^c (i.e., the bound holds).
310    pub fn check(&self, observed: u64, k: u64, n: u64, f: impl Fn(u64) -> u64, c: u32) -> bool {
311        let fk = f(k);
312        let nc = n.saturating_pow(c);
313        observed <= fk.saturating_mul(nc)
314    }
315    /// Check the standard 2^k * n bound (common FPT patterns).
316    pub fn check_2k_n(&self, observed: u64, k: u64, n: u64) -> bool {
317        let fk = 1u64.checked_shl(k as u32).unwrap_or(u64::MAX);
318        observed <= fk.saturating_mul(n)
319    }
320}
321/// Compute sensitivity and block sensitivity of a Boolean function.
322/// The function is given as a truth table (index = bit string, value = output).
323#[allow(dead_code)]
324pub struct SensitivityChecker {
325    /// Truth table: table[x] = f(x). Length must be 2^n.
326    pub table: Vec<bool>,
327    /// Number of input bits n.
328    pub n: usize,
329}
330impl SensitivityChecker {
331    /// Create from a truth table. Length must be 2^n.
332    pub fn new(table: Vec<bool>) -> Self {
333        let n = if table.is_empty() {
334            0
335        } else {
336            usize::BITS as usize - table.len().leading_zeros() as usize - 1
337        };
338        Self { table, n }
339    }
340    /// Sensitivity of f at input x: number of coordinates i where flipping bit i changes f(x).
341    pub fn sensitivity_at(&self, x: usize) -> usize {
342        let fx = self.table.get(x).copied().unwrap_or(false);
343        (0..self.n)
344            .filter(|&i| {
345                let xp = x ^ (1 << i);
346                self.table.get(xp).copied().unwrap_or(false) != fx
347            })
348            .count()
349    }
350    /// Maximum sensitivity over all inputs.
351    pub fn max_sensitivity(&self) -> usize {
352        (0..self.table.len())
353            .map(|x| self.sensitivity_at(x))
354            .max()
355            .unwrap_or(0)
356    }
357    /// Block sensitivity at input x: max number of disjoint sensitive blocks.
358    pub fn block_sensitivity_at(&self, x: usize) -> usize {
359        let fx = self.table.get(x).copied().unwrap_or(false);
360        let mut used = vec![false; self.n];
361        let mut count = 0;
362        for size in 1..=self.n {
363            for mask in 0..(1usize << self.n) {
364                if mask.count_ones() as usize != size {
365                    continue;
366                }
367                let all_unused = (0..self.n).all(|i| !used[i] || (mask >> i) & 1 == 0);
368                if !all_unused {
369                    continue;
370                }
371                let xp = x ^ mask;
372                if self.table.get(xp).copied().unwrap_or(false) != fx {
373                    for i in 0..self.n {
374                        if (mask >> i) & 1 == 1 {
375                            used[i] = true;
376                        }
377                    }
378                    count += 1;
379                    break;
380                }
381            }
382        }
383        count
384    }
385    /// Maximum block sensitivity over all inputs.
386    pub fn max_block_sensitivity(&self) -> usize {
387        (0..self.table.len())
388            .map(|x| self.block_sensitivity_at(x))
389            .max()
390            .unwrap_or(0)
391    }
392    /// Check Huang's sensitivity theorem: s(f)^2 >= bs(f).
393    pub fn check_huang_theorem(&self) -> bool {
394        let s = self.max_sensitivity();
395        let bs = self.max_block_sensitivity();
396        s * s >= bs
397    }
398}
399/// Evaluate a 2-CNF formula (given as list of clauses over bool variables).
400/// Returns `true` if satisfiable (2-SAT, solved via Kosaraju SCC).
401pub struct TwoSatSolver {
402    n: usize,
403    /// Implication graph: adj[2*i] = positive literal i, adj[2*i+1] = negative literal i
404    adj: Vec<Vec<usize>>,
405    /// Reverse implication graph
406    radj: Vec<Vec<usize>>,
407}
408impl TwoSatSolver {
409    /// Create a new 2-SAT solver for `n` variables.
410    pub fn new(n: usize) -> Self {
411        Self {
412            n,
413            adj: vec![vec![]; 2 * n],
414            radj: vec![vec![]; 2 * n],
415        }
416    }
417    /// Add clause (a OR b). `a` and `b` are literals: 2*var for positive, 2*var+1 for negative.
418    pub fn add_clause(&mut self, a: usize, b: usize) {
419        let na = a ^ 1;
420        let nb = b ^ 1;
421        self.adj[na].push(b);
422        self.adj[nb].push(a);
423        self.radj[b].push(na);
424        self.radj[a].push(nb);
425    }
426    /// Solve. Returns `Some(assignment)` if satisfiable, `None` otherwise.
427    pub fn solve(&self) -> Option<Vec<bool>> {
428        let n2 = 2 * self.n;
429        let mut order: Vec<usize> = Vec::with_capacity(n2);
430        let mut visited = vec![false; n2];
431        for i in 0..n2 {
432            if !visited[i] {
433                self.dfs1(i, &mut visited, &mut order);
434            }
435        }
436        let mut comp = vec![usize::MAX; n2];
437        let mut c = 0;
438        for &v in order.iter().rev() {
439            if comp[v] == usize::MAX {
440                self.dfs2(v, c, &mut comp);
441                c += 1;
442            }
443        }
444        let mut assignment = vec![false; self.n];
445        for i in 0..self.n {
446            if comp[2 * i] == comp[2 * i + 1] {
447                return None;
448            }
449            assignment[i] = comp[2 * i] > comp[2 * i + 1];
450        }
451        Some(assignment)
452    }
453    fn dfs1(&self, v: usize, visited: &mut Vec<bool>, order: &mut Vec<usize>) {
454        visited[v] = true;
455        for &u in &self.adj[v] {
456            if !visited[u] {
457                self.dfs1(u, visited, order);
458            }
459        }
460        order.push(v);
461    }
462    fn dfs2(&self, v: usize, c: usize, comp: &mut Vec<usize>) {
463        comp[v] = c;
464        for &u in &self.radj[v] {
465            if comp[u] == usize::MAX {
466                self.dfs2(u, c, comp);
467            }
468        }
469    }
470}
471/// Simple resolution-based propositional prover.
472/// Works on clauses over integer literals (positive = var, negative = negated var).
473/// Attempts to derive the empty clause (refutation).
474#[allow(dead_code)]
475pub struct ResolutionProverSmall {
476    clauses: Vec<Vec<i32>>,
477}
478impl ResolutionProverSmall {
479    /// Create a new prover.
480    pub fn new() -> Self {
481        Self { clauses: vec![] }
482    }
483    /// Add a clause.
484    pub fn add_clause(&mut self, clause: Vec<i32>) {
485        let mut c = clause;
486        c.sort_unstable();
487        c.dedup();
488        self.clauses.push(c);
489    }
490    /// Attempt to find a resolution refutation (saturation, bounded by `max_steps`).
491    /// Returns true if the empty clause is derived.
492    pub fn refute(&self, max_steps: usize) -> bool {
493        use std::collections::HashSet;
494        let normalize = |c: &Vec<i32>| -> Vec<i32> {
495            let mut v = c.clone();
496            v.sort_unstable();
497            v.dedup();
498            v
499        };
500        let mut known: HashSet<Vec<i32>> = HashSet::new();
501        let mut all_clauses: Vec<Vec<i32>> = vec![];
502        for c in &self.clauses {
503            let n = normalize(c);
504            if n.is_empty() {
505                return true;
506            }
507            if known.insert(n.clone()) {
508                all_clauses.push(n);
509            }
510        }
511        let mut steps = 0;
512        let mut new_start = 0;
513        loop {
514            if steps >= max_steps {
515                break;
516            }
517            let end = all_clauses.len();
518            if new_start >= end {
519                break;
520            }
521            let mut added = vec![];
522            for i in new_start..end {
523                for j in 0..end {
524                    if i == j {
525                        continue;
526                    }
527                    if let Some(resolved) = Self::resolve(&all_clauses[i], &all_clauses[j]) {
528                        let n = normalize(&resolved);
529                        if n.is_empty() {
530                            return true;
531                        }
532                        if known.insert(n.clone()) {
533                            added.push(n);
534                        }
535                    }
536                    steps += 1;
537                    if steps >= max_steps {
538                        break;
539                    }
540                }
541                if steps >= max_steps {
542                    break;
543                }
544            }
545            if added.is_empty() {
546                break;
547            }
548            new_start = end;
549            all_clauses.extend(added);
550        }
551        false
552    }
553    /// Resolve two clauses on a single complementary literal pair.
554    fn resolve(c1: &[i32], c2: &[i32]) -> Option<Vec<i32>> {
555        for &lit in c1 {
556            if c2.contains(&-lit) {
557                let mut result: Vec<i32> = c1
558                    .iter()
559                    .filter(|&&l| l != lit)
560                    .copied()
561                    .chain(c2.iter().filter(|&&l| l != -lit).copied())
562                    .collect();
563                result.sort_unstable();
564                result.dedup();
565                for &l in &result {
566                    if result.contains(&-l) {
567                        return None;
568                    }
569                }
570                return Some(result);
571            }
572        }
573        None
574    }
575}