Skip to main content

qualia_core_db/solvers/symbolic_logic/
mod.rs

1//! Symbolic & Logic Solvers - Zero-Allocation Implementation
2//!
3//! This module provides fixed-size stack-based symbolic and logic solvers for
4//! defeasible reasoning and boolean satisfiability suitable for the #![no_std]
5//! environment of Qualia-DB.
6
7use crate::solvers::SolversError as ExecutionError;
8use crate::solvers::{SolverConfig, SolverResult, SolverState};
9
10/// Forward chaining defeasible reasoning solver
11#[repr(C)]
12pub struct ForwardChainingDefeasible {
13    /// Rule base (fixed size)
14    pub rule_base: [DefeasibleRule; 100],
15    /// Current facts
16    pub facts: [Fact; 50],
17    /// Inference queue
18    pub inference_queue: [usize; 50],
19    /// Queue pointers
20    pub queue_head: u8,
21    pub queue_tail: u8,
22    /// Conflict resolution state
23    pub conflict_state: ConflictState,
24    /// Solver configuration
25    pub config: SolverConfig,
26    /// Solver state
27    pub solver_state: SolverState,
28}
29
30/// Bounded SAT solver for boolean satisfiability
31#[repr(C)]
32pub struct BoundedSatSolver {
33    /// Clause database (fixed size)
34    pub clauses: [Clause; 50],
35    /// Variable assignments
36    pub assignments: [VariableAssignment; 20],
37    /// Decision stack
38    pub decision_stack: [Decision; 20],
39    /// Unit propagation queue
40    pub propagation_queue: [u8; 20],
41    /// Current assignment level
42    pub assignment_level: u8,
43    /// Conflict analysis
44    pub conflict_clause: Option<Clause>,
45    /// Solver configuration
46    pub config: SolverConfig,
47    /// Solver state
48    pub solver_state: SolverState,
49}
50
51/// Defeasible rule structure
52#[repr(C)]
53#[derive(Clone, Copy)]
54pub struct DefeasibleRule {
55    /// Rule identifier
56    pub id: u32,
57    /// Rule type (strict, defeasible, defeater)
58    pub rule_type: RuleType,
59    /// Antecedent literals
60    pub antecedents: [Literal; 5],
61    /// Consequent literal
62    pub consequent: Literal,
63    /// Rule priority
64    pub priority: u16,
65    /// Active flag
66    pub active: bool,
67    /// Fire count
68    pub fire_count: u32,
69}
70
71/// Fact structure
72#[repr(C)]
73#[derive(Clone, Copy)]
74pub struct Fact {
75    /// Fact identifier
76    pub id: u32,
77    /// Literal value
78    pub literal: Literal,
79    /// Supporting rules
80    pub supporting_rules: [u32; 3],
81    /// Defeated flag
82    pub defeated: bool,
83    /// Confidence level
84    pub confidence: f64,
85}
86
87/// Clause structure for SAT solver
88#[repr(C)]
89#[derive(Clone, Copy)]
90pub struct Clause {
91    /// Clause identifier
92    pub id: u32,
93    /// Literals in clause
94    pub literals: [Literal; 5],
95    /// Number of literals
96    pub num_literals: u8,
97    /// Learned flag
98    pub learned: bool,
99    /// Activity score
100    pub activity: f64,
101}
102
103/// Literal structure
104#[repr(C)]
105#[derive(Clone, Copy)]
106pub struct Literal {
107    /// Variable index
108    pub variable: u8,
109    /// Negation flag
110    pub negated: bool,
111}
112
113/// Variable assignment
114#[repr(C)]
115#[derive(Clone, Copy)]
116pub struct VariableAssignment {
117    /// Assignment value
118    pub value: AssignmentValue,
119    /// Assignment level
120    pub level: u8,
121    /// Antecedent clause
122    pub antecedent: Option<u32>,
123}
124
125/// Decision structure
126#[repr(C)]
127#[derive(Clone, Copy)]
128pub struct Decision {
129    /// Variable index
130    pub variable: u8,
131    /// Assignment value
132    pub value: AssignmentValue,
133    /// Decision level
134    pub level: u8,
135}
136
137/// Conflict state for defeasible reasoning
138#[repr(C)]
139#[derive(Clone, Copy)]
140pub struct ConflictState {
141    /// Current conflicts
142    pub conflicts: [Conflict; 10],
143    /// Number of conflicts
144    pub num_conflicts: u8,
145    /// Resolution strategy
146    pub resolution_strategy: ResolutionStrategy,
147}
148
149/// Conflict structure
150#[repr(C)]
151#[derive(Clone, Copy)]
152pub struct Conflict {
153    /// Conflicting facts
154    pub facts: [u32; 3],
155    /// Conflicting rules
156    pub rules: [u32; 3],
157    /// Conflict type
158    pub conflict_type: ConflictType,
159}
160
161/// Defeasible reasoning state
162#[repr(C)]
163#[derive(Clone, Copy)]
164pub struct DefeasibleState {
165    /// Current iteration
166    pub iteration: u32,
167    /// Number of facts derived
168    pub num_facts: u16,
169    /// Number of rules fired
170    pub rules_fired: u16,
171    /// Converged flag
172    pub converged: bool,
173}
174
175/// SAT solver state
176#[repr(C)]
177#[derive(Clone, Copy)]
178pub struct SatState {
179    /// Current iteration
180    pub iteration: u32,
181    /// Number of decisions made
182    pub num_decisions: u16,
183    /// Number of propagations
184    pub num_propagations: u16,
185    /// Satisfiable flag
186    pub satisfiable: Option<bool>,
187}
188
189/// Rule types for defeasible logic
190#[repr(u8)]
191#[derive(Clone, Copy)]
192pub enum RuleType {
193    Strict = 0,
194    Defeasible = 1,
195    Defeater = 2,
196}
197
198/// Assignment values
199#[repr(u8)]
200#[derive(Clone, Copy, PartialEq)]
201pub enum AssignmentValue {
202    False = 0,
203    True = 1,
204    Unassigned = 2,
205}
206
207/// Conflict types
208#[repr(u8)]
209#[derive(Clone, Copy)]
210pub enum ConflictType {
211    Contradiction = 0,
212    Defeat = 1,
213    Preference = 2,
214}
215
216/// Resolution strategies
217#[repr(u8)]
218#[derive(Clone, Copy)]
219pub enum ResolutionStrategy {
220    Priority = 0,
221    Specificity = 1,
222    Recency = 2,
223}
224
225impl ForwardChainingDefeasible {
226    /// Create new defeasible reasoning solver
227    pub fn new(config: SolverConfig) -> Self {
228        Self {
229            rule_base: [DefeasibleRule::default(); 100],
230            facts: [Fact::default(); 50],
231            inference_queue: [0; 50],
232            queue_head: 0,
233            queue_tail: 0,
234            conflict_state: ConflictState::default(),
235            config,
236            solver_state: SolverState::default(),
237        }
238    }
239
240    /// Add rule to rule base
241    pub fn add_rule(&mut self, rule: DefeasibleRule) -> SolverResult<()> {
242        // Find empty slot
243        for i in 0..100 {
244            if self.rule_base[i].id == 0 {
245                self.rule_base[i] = rule;
246                return Ok(());
247            }
248        }
249
250        Err(ExecutionError::CapacityExceeded)
251    }
252
253    /// Add fact to fact base
254    pub fn add_fact(&mut self, fact: Fact) -> SolverResult<()> {
255        // Find empty slot
256        for i in 0..50 {
257            if self.facts[i].id == 0 {
258                self.facts[i] = fact;
259
260                // Queue for inference
261                self.queue_inference(i as u8);
262
263                return Ok(());
264            }
265        }
266
267        Err(ExecutionError::CapacityExceeded)
268    }
269
270    /// Perform forward chaining inference
271    pub fn infer(&mut self) -> SolverResult<DefeasibleState> {
272        self.solver_state.iteration = 0;
273        self.solver_state.converged = false;
274
275        while !self.queue_empty() && self.solver_state.iteration < self.config.max_iterations {
276            // Get next fact from queue
277            let fact_index = self.queue_dequeue();
278
279            // Find applicable rules
280            self.find_applicable_rules(fact_index)?;
281
282            // Apply rules and detect conflicts
283            self.apply_rules()?;
284
285            // Resolve conflicts
286            self.resolve_conflicts()?;
287
288            self.solver_state.iteration += 1;
289        }
290
291        // Check convergence
292        self.solver_state.converged = self.queue_empty();
293
294        Ok(DefeasibleState {
295            iteration: self.solver_state.iteration,
296            num_facts: self.count_facts(),
297            rules_fired: self.count_rules_fired(),
298            converged: self.solver_state.converged,
299        })
300    }
301
302    /// Queue fact for inference
303    fn queue_inference(&mut self, fact_index: u8) {
304        if ((self.queue_tail + 1) % 50) != self.queue_head {
305            self.inference_queue[self.queue_tail as usize] = fact_index as usize;
306            self.queue_tail = (self.queue_tail + 1) % 50;
307        }
308    }
309
310    /// Dequeue fact from inference queue
311    fn queue_dequeue(&mut self) -> u8 {
312        if self.queue_head != self.queue_tail {
313            let fact_index = self.inference_queue[self.queue_head as usize];
314            self.queue_head = (self.queue_head + 1) % 50;
315            fact_index as u8
316        } else {
317            0
318        }
319    }
320
321    /// Check if queue is empty
322    fn queue_empty(&self) -> bool {
323        self.queue_head == self.queue_tail
324    }
325
326    /// Find applicable rules for given fact
327    fn find_applicable_rules(&mut self, fact_index: u8) -> SolverResult<()> {
328        let fact_literal = self.facts[fact_index as usize].literal; // Copy
329
330        // Two-pass approach: collect which rules to fire, then fire them
331        let mut rules_to_fire = [false; 100];
332
333        for i in 0..100 {
334            if self.rule_base[i].id == 0 || !self.rule_base[i].active {
335                continue;
336            }
337
338            for j in 0..5 {
339                if self.rule_base[i].antecedents[j].variable == 0 {
340                    break; // No more antecedents
341                }
342
343                let antecedent = self.rule_base[i].antecedents[j]; // Copy
344                if self.literals_match(&antecedent, &fact_literal) {
345                    self.rule_base[i].fire_count += 1;
346
347                    let rule_copy = self.rule_base[i]; // Copy for immutable check
348                    if self.antecedents_satisfied(&rule_copy) {
349                        rules_to_fire[i] = true;
350                    }
351                }
352            }
353        }
354
355        for i in 0..100 {
356            if rules_to_fire[i] {
357                self.fire_rule(i)?;
358            }
359        }
360
361        Ok(())
362    }
363
364    /// Check if literals match
365    fn literals_match(&self, lit1: &Literal, lit2: &Literal) -> bool {
366        lit1.variable == lit2.variable && lit1.negated == lit2.negated
367    }
368
369    /// Check if all antecedents are satisfied
370    fn antecedents_satisfied(&self, rule: &DefeasibleRule) -> bool {
371        for i in 0..5 {
372            if rule.antecedents[i].variable == 0 {
373                break; // No more antecedents
374            }
375
376            let mut satisfied = false;
377            for j in 0..50 {
378                if self.facts[j].id == 0 {
379                    continue;
380                }
381
382                if self.literals_match(&rule.antecedents[i], &self.facts[j].literal)
383                    && !self.facts[j].defeated
384                {
385                    satisfied = true;
386                    break;
387                }
388            }
389
390            if !satisfied {
391                return false;
392            }
393        }
394
395        true
396    }
397
398    /// Fire rule and derive consequent
399    fn fire_rule(&mut self, rule_index: usize) -> SolverResult<()> {
400        let rule = &self.rule_base[rule_index];
401
402        // Create new fact
403        let new_fact = Fact {
404            id: rule.consequent.variable as u32 + 1000, // Unique ID
405            literal: rule.consequent,
406            supporting_rules: [rule.id, 0, 0],
407            defeated: false,
408            confidence: rule.priority as f64 / 1000.0,
409        };
410
411        // Add fact
412        self.add_fact(new_fact)?;
413
414        Ok(())
415    }
416
417    /// Apply rules and detect conflicts
418    fn apply_rules(&mut self) -> SolverResult<()> {
419        // Check for contradictions
420        for i in 0..50 {
421            if self.facts[i].id == 0 {
422                continue;
423            }
424
425            for j in i + 1..50 {
426                if self.facts[j].id == 0 {
427                    continue;
428                }
429
430                // Check for direct contradiction
431                if self.facts[i].literal.variable == self.facts[j].literal.variable
432                    && self.facts[i].literal.negated != self.facts[j].literal.negated
433                {
434                    // Add conflict
435                    self.add_conflict(i, j, ConflictType::Contradiction)?;
436                }
437            }
438        }
439
440        Ok(())
441    }
442
443    /// Add conflict to conflict state
444    fn add_conflict(
445        &mut self,
446        fact1: usize,
447        fact2: usize,
448        conflict_type: ConflictType,
449    ) -> SolverResult<()> {
450        if self.conflict_state.num_conflicts < 10 {
451            let conflict = Conflict {
452                facts: [self.facts[fact1].id, self.facts[fact2].id, 0],
453                rules: [0, 0, 0],
454                conflict_type,
455            };
456
457            self.conflict_state.conflicts[self.conflict_state.num_conflicts as usize] = conflict;
458            self.conflict_state.num_conflicts += 1;
459        }
460
461        Ok(())
462    }
463
464    /// Resolve conflicts using priority strategy
465    fn resolve_conflicts(&mut self) -> SolverResult<()> {
466        for i in 0..self.conflict_state.num_conflicts as usize {
467            let conflict = self.conflict_state.conflicts[i]; // Copy
468
469            match conflict.conflict_type {
470                ConflictType::Contradiction => {
471                    self.resolve_contradiction(&conflict)?;
472                }
473                ConflictType::Defeat => {
474                    self.resolve_defeat(&conflict)?;
475                }
476                ConflictType::Preference => {
477                    self.resolve_preference(&conflict)?;
478                }
479            }
480        }
481
482        Ok(())
483    }
484
485    /// Resolve contradiction by keeping higher confidence fact
486    fn resolve_contradiction(&mut self, conflict: &Conflict) -> SolverResult<()> {
487        // Find conflicting facts
488        let mut fact1_idx = 0;
489        let mut fact2_idx = 0;
490        let mut fact1_confidence = 0.0;
491        let mut fact2_confidence = 0.0;
492
493        for i in 0..50 {
494            if self.facts[i].id == 0 {
495                continue;
496            }
497
498            if self.facts[i].id == conflict.facts[0] {
499                fact1_idx = i;
500                fact1_confidence = self.facts[i].confidence;
501            }
502            if self.facts[i].id == conflict.facts[1] {
503                fact2_idx = i;
504                fact2_confidence = self.facts[i].confidence;
505            }
506        }
507
508        // Defeat lower confidence fact
509        if fact1_confidence < fact2_confidence {
510            self.facts[fact1_idx].defeated = true;
511        } else {
512            self.facts[fact2_idx].defeated = true;
513        }
514
515        Ok(())
516    }
517
518    /// Resolve defeat using rule priorities
519    fn resolve_defeat(&mut self, _conflict: &Conflict) -> SolverResult<()> {
520        // Simplified defeat resolution
521        // In full implementation, would check rule priorities and specificity
522        Ok(())
523    }
524
525    /// Resolve preference using recency
526    fn resolve_preference(&mut self, _conflict: &Conflict) -> SolverResult<()> {
527        // Simplified preference resolution
528        // In full implementation, would use recency or user preferences
529        Ok(())
530    }
531
532    /// Count active facts
533    fn count_facts(&self) -> u16 {
534        let mut count = 0;
535        for i in 0..50 {
536            if self.facts[i].id != 0 && !self.facts[i].defeated {
537                count += 1;
538            }
539        }
540        count
541    }
542
543    /// Count fired rules
544    fn count_rules_fired(&self) -> u16 {
545        let mut count = 0;
546        for i in 0..100 {
547            if self.rule_base[i].id != 0 && self.rule_base[i].fire_count > 0 {
548                count += 1;
549            }
550        }
551        count
552    }
553
554    /// Get derived facts
555    pub fn get_facts(&self) -> &[Fact; 50] {
556        &self.facts
557    }
558}
559
560impl BoundedSatSolver {
561    /// Create new SAT solver
562    pub fn new(config: SolverConfig) -> Self {
563        Self {
564            clauses: [Clause::default(); 50],
565            assignments: [VariableAssignment::default(); 20],
566            decision_stack: [Decision::default(); 20],
567            propagation_queue: [0; 20],
568            assignment_level: 0,
569            conflict_clause: None,
570            config,
571            solver_state: SolverState::default(),
572        }
573    }
574
575    /// Add clause to clause database
576    pub fn add_clause(&mut self, clause: Clause) -> SolverResult<()> {
577        // Find empty slot
578        for i in 0..50 {
579            if self.clauses[i].id == 0 {
580                self.clauses[i] = clause;
581                return Ok(());
582            }
583        }
584
585        Err(ExecutionError::CapacityExceeded)
586    }
587
588    /// Solve SAT problem
589    pub fn solve(&mut self) -> SolverResult<SatState> {
590        self.solver_state.iteration = 0;
591        self.solver_state.set_satisfiable(None);
592
593        // Initialize assignments
594        for i in 0..20 {
595            self.assignments[i] = VariableAssignment::default();
596        }
597
598        // DPLL algorithm with unit propagation
599        if self.dpll_algorithm()? {
600            self.solver_state.set_satisfiable(Some(true));
601        } else {
602            self.solver_state.set_satisfiable(Some(false));
603        }
604
605        Ok(SatState {
606            iteration: self.solver_state.iteration,
607            num_decisions: self.assignment_level as u16,
608            num_propagations: self.count_propagations(),
609            satisfiable: self.solver_state.satisfiable(),
610        })
611    }
612
613    /// DPLL algorithm implementation
614    fn dpll_algorithm(&mut self) -> SolverResult<bool> {
615        // Unit propagation
616        if !self.unit_propagate()? {
617            return Ok(false); // Conflict detected
618        }
619
620        // Check if all variables are assigned
621        if self.all_variables_assigned() {
622            return Ok(true); // Satisfying assignment found
623        }
624
625        // Choose unassigned variable
626        let var = self.choose_unassigned_variable()?;
627
628        // Try assigning true
629        self.assign_variable(var, AssignmentValue::True, None)?;
630        if self.dpll_algorithm()? {
631            return Ok(true);
632        }
633
634        // Backtrack
635        self.backtrack()?;
636
637        // Try assigning false
638        self.assign_variable(var, AssignmentValue::False, None)?;
639        if self.dpll_algorithm()? {
640            return Ok(true);
641        }
642
643        // Backtrack
644        self.backtrack()?;
645
646        Ok(false)
647    }
648
649    /// Unit propagation
650    fn unit_propagate(&mut self) -> SolverResult<bool> {
651        let mut propagated = true;
652
653        while propagated {
654            propagated = false;
655
656            // Check all clauses for unit clauses
657            for i in 0..50 {
658                if self.clauses[i].id == 0 {
659                    continue;
660                }
661
662                if let Some(unit_literal) = self.is_unit_clause(i)? {
663                    // Propagate unit literal
664                    let value = if unit_literal.negated {
665                        AssignmentValue::False
666                    } else {
667                        AssignmentValue::True
668                    };
669
670                    self.assign_variable(unit_literal.variable, value, Some(self.clauses[i].id))?;
671                    propagated = true;
672                }
673            }
674        }
675
676        Ok(true)
677    }
678
679    /// Check if clause is unit clause
680    fn is_unit_clause(&self, clause_index: usize) -> SolverResult<Option<Literal>> {
681        let clause = &self.clauses[clause_index];
682        let mut unassigned_count = 0;
683        let mut unit_literal = None;
684
685        for i in 0..clause.num_literals as usize {
686            let literal = clause.literals[i];
687            let assignment = &self.assignments[literal.variable as usize];
688
689            match assignment.value {
690                AssignmentValue::Unassigned => {
691                    unassigned_count += 1;
692                    unit_literal = Some(literal);
693                }
694                AssignmentValue::True => {
695                    if !literal.negated {
696                        return Ok(None); // Clause satisfied
697                    }
698                }
699                AssignmentValue::False => {
700                    if literal.negated {
701                        return Ok(None); // Clause satisfied
702                    }
703                }
704            }
705        }
706
707        if unassigned_count == 1 {
708            Ok(unit_literal)
709        } else if unassigned_count == 0 {
710            Err(ExecutionError::Unsatisfiable)
711        } else {
712            Ok(None)
713        }
714    }
715
716    /// Assign variable
717    fn assign_variable(
718        &mut self,
719        var: u8,
720        value: AssignmentValue,
721        antecedent: Option<u32>,
722    ) -> SolverResult<()> {
723        self.assignments[var as usize] = VariableAssignment {
724            value,
725            level: self.assignment_level,
726            antecedent,
727        };
728
729        // Add to propagation queue
730        for i in 0..20 {
731            if self.propagation_queue[i] == 0 {
732                self.propagation_queue[i] = var;
733                break;
734            }
735        }
736
737        Ok(())
738    }
739
740    /// Check if all variables are assigned
741    fn all_variables_assigned(&self) -> bool {
742        for i in 0..20 {
743            if self.assignments[i].value == AssignmentValue::Unassigned {
744                return false;
745            }
746        }
747        true
748    }
749
750    /// Choose unassigned variable
751    fn choose_unassigned_variable(&self) -> SolverResult<u8> {
752        for i in 0..20 {
753            if self.assignments[i].value == AssignmentValue::Unassigned {
754                return Ok(i as u8);
755            }
756        }
757        Err(ExecutionError::InvalidParameters)
758    }
759
760    /// Backtrack to previous decision level
761    fn backtrack(&mut self) -> SolverResult<()> {
762        if self.assignment_level == 0 {
763            return Err(ExecutionError::BacktrackFailed);
764        }
765
766        // Clear assignments at current level
767        for i in 0..20 {
768            if self.assignments[i].level == self.assignment_level {
769                self.assignments[i] = VariableAssignment::default();
770            }
771        }
772
773        self.assignment_level -= 1;
774
775        Ok(())
776    }
777
778    /// Count propagations
779    fn count_propagations(&self) -> u16 {
780        let mut count = 0;
781        for i in 0..20 {
782            if self.assignments[i].antecedent.is_some() {
783                count += 1;
784            }
785        }
786        count
787    }
788
789    /// Get variable assignments
790    pub fn get_assignments(&self) -> &[VariableAssignment; 20] {
791        &self.assignments
792    }
793}
794
795impl Default for DefeasibleRule {
796    fn default() -> Self {
797        Self {
798            id: 0,
799            rule_type: RuleType::Strict,
800            antecedents: [Literal::default(); 5],
801            consequent: Literal::default(),
802            priority: 0,
803            active: true,
804            fire_count: 0,
805        }
806    }
807}
808
809impl Default for Fact {
810    fn default() -> Self {
811        Self {
812            id: 0,
813            literal: Literal::default(),
814            supporting_rules: [0, 0, 0],
815            defeated: false,
816            confidence: 0.0,
817        }
818    }
819}
820
821impl Default for Clause {
822    fn default() -> Self {
823        Self {
824            id: 0,
825            literals: [Literal::default(); 5],
826            num_literals: 0,
827            learned: false,
828            activity: 0.0,
829        }
830    }
831}
832
833impl Default for Literal {
834    fn default() -> Self {
835        Self {
836            variable: 0,
837            negated: false,
838        }
839    }
840}
841
842impl Default for VariableAssignment {
843    fn default() -> Self {
844        Self {
845            value: AssignmentValue::Unassigned,
846            level: 0,
847            antecedent: None,
848        }
849    }
850}
851
852impl Default for Decision {
853    fn default() -> Self {
854        Self {
855            variable: 0,
856            value: AssignmentValue::Unassigned,
857            level: 0,
858        }
859    }
860}
861
862impl Default for ConflictState {
863    fn default() -> Self {
864        Self {
865            conflicts: [Conflict::default(); 10],
866            num_conflicts: 0,
867            resolution_strategy: ResolutionStrategy::Priority,
868        }
869    }
870}
871
872impl Default for Conflict {
873    fn default() -> Self {
874        Self {
875            facts: [0, 0, 0],
876            rules: [0, 0, 0],
877            conflict_type: ConflictType::Contradiction,
878        }
879    }
880}
881
882impl Default for DefeasibleState {
883    fn default() -> Self {
884        Self {
885            iteration: 0,
886            num_facts: 0,
887            rules_fired: 0,
888            converged: false,
889        }
890    }
891}
892
893impl Default for SatState {
894    fn default() -> Self {
895        Self {
896            iteration: 0,
897            num_decisions: 0,
898            num_propagations: 0,
899            satisfiable: None,
900        }
901    }
902}
903
904impl Default for ForwardChainingDefeasible {
905    fn default() -> Self {
906        Self::new(SolverConfig::default())
907    }
908}
909
910impl Default for BoundedSatSolver {
911    fn default() -> Self {
912        Self::new(SolverConfig::default())
913    }
914}
915
916#[cfg(test)]
917mod tests {
918    use super::*;
919
920    #[test]
921    fn test_defeasible_reasoning() {
922        let mut solver = ForwardChainingDefeasible::new(SolverConfig::default());
923
924        // Add rule: A -> B
925        let rule = DefeasibleRule {
926            id: 1,
927            rule_type: RuleType::Strict,
928            antecedents: [
929                Literal {
930                    variable: 1,
931                    negated: false,
932                },
933                Literal {
934                    variable: 0,
935                    negated: false,
936                },
937                Literal {
938                    variable: 0,
939                    negated: false,
940                },
941                Literal {
942                    variable: 0,
943                    negated: false,
944                },
945                Literal {
946                    variable: 0,
947                    negated: false,
948                },
949            ],
950            consequent: Literal {
951                variable: 2,
952                negated: false,
953            },
954            priority: 100,
955            active: true,
956            fire_count: 0,
957        };
958
959        solver.add_rule(rule).unwrap();
960
961        // Add fact: A
962        let fact = Fact {
963            id: 100,
964            literal: Literal {
965                variable: 1,
966                negated: false,
967            },
968            supporting_rules: [0, 0, 0],
969            defeated: false,
970            confidence: 1.0,
971        };
972
973        solver.add_fact(fact).unwrap();
974
975        // Perform inference
976        let result = solver.infer();
977        assert!(result.is_ok());
978
979        let state = result.unwrap();
980        assert!(state.num_facts >= 2); // A and B
981        assert!(state.rules_fired >= 1);
982    }
983
984    #[test]
985    fn test_sat_solver() {
986        let mut solver = BoundedSatSolver::new(SolverConfig::default());
987
988        // Add clause: (A ∨ B ∨ ¬C)
989        let clause = Clause {
990            id: 1,
991            literals: [
992                Literal {
993                    variable: 1,
994                    negated: false,
995                },
996                Literal {
997                    variable: 2,
998                    negated: false,
999                },
1000                Literal {
1001                    variable: 3,
1002                    negated: true,
1003                },
1004                Literal {
1005                    variable: 0,
1006                    negated: false,
1007                },
1008                Literal {
1009                    variable: 0,
1010                    negated: false,
1011                },
1012            ],
1013            num_literals: 3,
1014            learned: false,
1015            activity: 0.0,
1016        };
1017
1018        solver.add_clause(clause).unwrap();
1019
1020        // Solve
1021        let result = solver.solve();
1022        assert!(result.is_ok());
1023
1024        let state = result.unwrap();
1025        assert!(state.satisfiable.is_some());
1026    }
1027
1028    #[test]
1029    fn test_zero_allocation_guarantee() {
1030        // assert_eq!(core::mem::size_of::<ForwardChainingDefeasible>(), ...);
1031        // assert_eq!(core::mem::size_of::<BoundedSatSolver>(), ...);
1032        // assert_eq!(core::mem::size_of::<DefeasibleRule>(), ...);
1033        // assert_eq!(core::mem::size_of::<Fact>(), ...);
1034        // assert_eq!(core::mem::size_of::<Clause>(), ...);
1035        // assert_eq!(core::mem::size_of::<Literal>(), ...);
1036    }
1037}