Skip to main content

arcweight/semiring/
boolean.rs

1//! Boolean semiring implementation.
2//!
3//! This module provides the Boolean semiring $`(\{0, 1\}, \vee, \wedge, 0, 1)`$
4//! for unweighted automata and recognition problems where addition is logical OR
5//! and multiplication is logical AND.
6//!
7//! # References
8//!
9//! - Kuich, W., & Salomaa, A. (1986). *Semirings, Automata, Languages*. EATCS
10//!   Monographs on Theoretical Computer Science, Vol. 5. Springer-Verlag.
11
12use super::traits::*;
13use core::fmt;
14use core::ops::{Add, Mul};
15use core::str::FromStr;
16use num_traits::{One, Zero};
17
18/// Boolean weight for recognition and reachability analysis.
19///
20/// The Boolean semiring provides the simplest non-trivial semiring structure,
21/// corresponding to unweighted finite state automata and basic recognition problems.
22/// In this semiring, "addition" implements logical OR (path existence), while
23/// "multiplication" implements logical AND (path conjunction).
24///
25/// # Mathematical Definition
26///
27/// | Operation | Definition |
28/// |-----------|------------|
29/// | $`a \oplus b`$ | $`a \vee b`$ (logical OR) |
30/// | $`a \otimes b`$ | $`a \wedge b`$ (logical AND) |
31/// | $`\bar{0}`$ | $`\mathrm{false}`$ |
32/// | $`\bar{1}`$ | $`\mathrm{true}`$ |
33/// | $`a^*`$ | $`\mathrm{true}`$ |
34///
35/// # Algebraic Properties
36///
37/// - **Commutative:** Both $`\oplus`$ and $`\otimes`$ are commutative
38/// - **Idempotent:** $`a \oplus a = a`$ and $`a \otimes a = a`$
39/// - **Path:** $`a \oplus b \in \{a, b\}`$
40/// - **Naturally Ordered:** Forms a complete Boolean lattice
41/// - **Star Semiring:** Kleene closure is always $`\bar{1}`$
42///
43/// # Mathematical Semantics
44///
45/// - **Value Range:** Boolean values $`\{0, 1\} \cong \{\bot, \top\}`$
46/// - **Addition ($`\oplus`$):** $`a \vee b`$ (logical OR) - combines alternative paths
47/// - **Multiplication ($`\otimes`$):** $`a \wedge b`$ (logical AND) - requires all conditions
48/// - **Zero ($`\bar{0}`$):** $`\mathrm{false}`$ - represents rejection/failure/no path
49/// - **One ($`\bar{1}`$):** $`\mathrm{true}`$ - represents acceptance/success/valid path
50///
51/// # Use Cases
52///
53/// ## Regular Expression Matching
54/// ```rust
55/// use arcweight::prelude::*;
56///
57/// // Build pattern matcher FST
58/// let mut fst = VectorFst::<BooleanWeight>::new();
59/// let s0 = fst.add_state();
60/// let s1 = fst.add_state();
61///
62/// fst.set_start(s0);
63/// fst.set_final(s1, BooleanWeight::one());  // Accept state
64///
65/// // Add pattern transition: match 'a'
66/// fst.add_arc(s0, Arc::new(
67///     'a' as u32,
68///     'a' as u32,
69///     BooleanWeight::one(),  // Valid transition
70///     s1
71/// ));
72///
73/// // The FST now accepts strings containing 'a'
74/// ```
75///
76/// ## Graph Reachability
77/// ```rust
78/// use arcweight::prelude::*;
79///
80/// // Check if path exists between nodes
81/// let path_exists = BooleanWeight::new(true);
82/// let no_path = BooleanWeight::new(false);
83///
84/// // Combine multiple path possibilities
85/// let reachable = path_exists
86///     .plus(&no_path)
87///     .plus(&path_exists);  // true ∨ false ∨ true = true
88///
89/// // Check if all intermediate nodes are valid
90/// let validᵢntermediate = BooleanWeight::new(true);
91/// let path_validity = reachable.times(&validᵢntermediate);  // true ∧ true = true
92/// ```
93///
94/// ## Set Membership Testing
95/// ```rust
96/// use arcweight::prelude::*;
97///
98/// // Dictionary lookup FST
99/// let word_exists = BooleanWeight::new(true);   // Word found in dictionary
100/// let word_missing = BooleanWeight::new(false); // Word not found
101///
102/// // Multiple dictionary checks
103/// let found_anywhere = word_missing
104///     .plus(&word_exists);  // false ∨ true = true (found in at least one)
105///
106/// // Require presence in multiple dictionaries
107/// let found_everywhere = word_exists
108///     .times(&word_exists); // true ∧ true = true (found in all)
109/// ```
110///
111/// ## Constraint Satisfaction
112/// ```rust
113/// use arcweight::prelude::*;
114///
115/// // Grammar rule satisfaction
116/// let rule1_satisfied = BooleanWeight::new(true);
117/// let rule2_satisfied = BooleanWeight::new(false);
118/// let rule3_satisfied = BooleanWeight::new(true);
119///
120/// // All rules must be satisfied (conjunction)
121/// let all_rules = rule1_satisfied
122///     .times(&rule2_satisfied)
123///     .times(&rule3_satisfied);  // true ∧ false ∧ true = false
124///
125/// // At least one rule satisfied (disjunction)
126/// let any_rule = rule1_satisfied
127///     .plus(&rule2_satisfied)
128///     .plus(&rule3_satisfied);   // true ∨ false ∨ true = true
129/// ```
130///
131/// # Working with FSTs
132///
133/// ```rust
134/// use arcweight::prelude::*;
135///
136/// let true_val = BooleanWeight::new(true);
137/// let false_val = BooleanWeight::new(false);
138///
139/// // Addition is logical OR (alternative paths)
140/// let or_result = true_val + false_val;
141/// assert_eq!(or_result, BooleanWeight::new(true));
142///
143/// // Multiplication is logical AND (path conjunction)
144/// let and_result = true_val * false_val;
145/// assert_eq!(and_result, BooleanWeight::new(false));
146///
147/// // Idempotency properties
148/// assert_eq!(true_val + true_val, true_val);   // true ∨ true = true
149/// assert_eq!(false_val * false_val, false_val); // false ∧ false = false
150///
151/// // Identity elements
152/// assert_eq!(BooleanWeight::zero(), BooleanWeight::new(false));  // Additive identity
153/// assert_eq!(BooleanWeight::one(), BooleanWeight::new(true));    // Multiplicative identity
154/// ```
155///
156/// # Algebraic Properties
157///
158/// The Boolean semiring forms a complete Boolean algebra with important properties:
159///
160/// - **Idempotent:** `a ⊕ a = a` and `a ⊗ a = a`
161/// - **Commutative:** `a ⊕ b = b ⊕ a` and `a ⊗ b = b ⊗ a`
162/// - **Associative:** `(a ⊕ b) ⊕ c = a ⊕ (b ⊕ c)`
163/// - **Distributive:** `a ⊗ (b ⊕ c) = (a ⊗ b) ⊕ (a ⊗ c)`
164/// - **Absorptive:** `a ⊕ (a ⊗ b) = a` and `a ⊗ (a ⊕ b) = a`
165/// - **Complemented:** Each element has a complement (negation)
166///
167/// # Performance Characteristics
168///
169/// - **Arithmetic:** Both OR and AND operations are O(1) bit operations
170/// - **Memory:** 1 byte per weight (single bool with padding)
171/// - **Comparison:** Extremely fast integer comparison
172/// - **Hash/Eq:** Optimal for hash maps and sets
173/// - **Specialized:** Can be bit-packed for massive state spaces
174///
175/// # Advanced Usage
176///
177/// ## Kleene Star
178/// ```rust
179/// use arcweight::prelude::*;
180///
181/// let weight = BooleanWeight::new(true);
182/// let star_result = weight.star();  // Always true for non-zero elements
183/// assert_eq!(star_result, BooleanWeight::one());
184/// ```
185///
186/// ## Logical Operations
187/// ```rust
188/// use arcweight::prelude::*;
189///
190/// // De Morgan's laws can be implemented using semiring operations
191/// let a = BooleanWeight::new(true);
192/// let b = BooleanWeight::new(false);
193///
194/// // ¬(a ∧ b) ≡ ¬a ∨ ¬b (requires external negation function)
195/// // Boolean semiring provides the base operations for logical reasoning
196/// ```
197///
198/// # Integration with FST Algorithms
199///
200/// Boolean weights work optimally with all FST algorithms:
201/// - **Composition:** Combines recognizers and transducers
202/// - **Union:** Creates alternatives in recognition
203/// - **Determinization:** Produces minimal recognizers
204/// - **Shortest Path:** Finds any accepting path (since all have same weight)
205///
206/// # See Also
207///
208/// - [Core Concepts - Boolean Semiring](../../docs/core-concepts/semirings.md#boolean-semiring) for mathematical background
209/// - [`TropicalWeight`](crate::semiring::TropicalWeight) for optimization problems
210/// - [`compose()`](crate::algorithms::compose) for building complex recognizers from Boolean FSTs
211#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
212#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
213pub struct BooleanWeight(bool);
214
215impl BooleanWeight {
216    /// Create a new boolean weight
217    pub const fn new(value: bool) -> Self {
218        Self(value)
219    }
220}
221
222impl fmt::Display for BooleanWeight {
223    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224        let value = self.0;
225        write!(f, "{value}")
226    }
227}
228
229impl Zero for BooleanWeight {
230    fn zero() -> Self {
231        Self::new(false)
232    }
233
234    fn is_zero(&self) -> bool {
235        !self.0
236    }
237}
238
239impl One for BooleanWeight {
240    fn one() -> Self {
241        Self::new(true)
242    }
243}
244
245impl Add for BooleanWeight {
246    type Output = Self;
247
248    fn add(self, rhs: Self) -> Self::Output {
249        Self(self.0 || rhs.0)
250    }
251}
252
253impl Mul for BooleanWeight {
254    type Output = Self;
255
256    fn mul(self, rhs: Self) -> Self::Output {
257        Self(self.0 && rhs.0)
258    }
259}
260
261impl Semiring for BooleanWeight {
262    type Value = bool;
263
264    fn new(value: Self::Value) -> Self {
265        Self::new(value)
266    }
267
268    fn value(&self) -> &Self::Value {
269        &self.0
270    }
271
272    fn properties() -> SemiringProperties {
273        SemiringProperties {
274            left_semiring: true,
275            right_semiring: true,
276            commutative: true,
277            idempotent: true,
278            path: true,
279        }
280    }
281}
282
283impl NaturallyOrderedSemiring for BooleanWeight {}
284
285impl StarSemiring for BooleanWeight {
286    fn star(&self) -> Self {
287        Self::one()
288    }
289}
290
291impl FromStr for BooleanWeight {
292    type Err = std::str::ParseBoolError;
293
294    fn from_str(s: &str) -> Result<Self, Self::Err> {
295        s.parse::<bool>().map(Self::new)
296    }
297}
298
299#[cfg(test)]
300mod tests {
301    use super::*;
302    use num_traits::{One, Zero};
303
304    #[test]
305    fn test_boolean_weight_creation() {
306        let w_true = BooleanWeight::new(true);
307        let w_false = BooleanWeight::new(false);
308
309        assert!(*w_true.value());
310        assert!(!*w_false.value());
311    }
312
313    #[test]
314    fn test_boolean_zero_one() {
315        let zero = BooleanWeight::zero();
316        let one = BooleanWeight::one();
317
318        assert!(Semiring::is_zero(&zero));
319        assert!(Semiring::is_one(&one));
320        assert!(!*zero.value());
321        assert!(*one.value());
322    }
323
324    #[test]
325    fn test_boolean_addition() {
326        let w_true = BooleanWeight::new(true);
327        let w_false = BooleanWeight::new(false);
328
329        // OR operation
330        assert!(*w_true.plus(&w_false).value());
331        assert!(*w_false.plus(&w_true).value());
332        assert!(!*w_false.plus(&w_false).value());
333        assert!(*w_true.plus(&w_true).value());
334    }
335
336    #[test]
337    fn test_boolean_multiplication() {
338        let w_true = BooleanWeight::new(true);
339        let w_false = BooleanWeight::new(false);
340
341        // AND operation
342        assert!(!*w_true.times(&w_false).value());
343        assert!(!*w_false.times(&w_true).value());
344        assert!(!*w_false.times(&w_false).value());
345        assert!(*w_true.times(&w_true).value());
346    }
347
348    #[test]
349    fn test_boolean_idempotence() {
350        let w_true = BooleanWeight::new(true);
351        let w_false = BooleanWeight::new(false);
352
353        // w + w = w (idempotent)
354        assert_eq!(w_true.plus(&w_true), w_true);
355        assert_eq!(w_false.plus(&w_false), w_false);
356    }
357
358    #[test]
359    fn test_boolean_display() {
360        let w_true = BooleanWeight::new(true);
361        let w_false = BooleanWeight::new(false);
362
363        assert_eq!(format!("{w_true}"), "true");
364        assert_eq!(format!("{w_false}"), "false");
365    }
366
367    #[test]
368    fn test_boolean_star() {
369        let w_true = BooleanWeight::new(true);
370        let w_false = BooleanWeight::new(false);
371
372        // Star is always one for boolean semiring
373        assert_eq!(w_true.star(), BooleanWeight::one());
374        assert_eq!(w_false.star(), BooleanWeight::one());
375    }
376
377    #[test]
378    fn test_boolean_properties() {
379        let props = BooleanWeight::properties();
380        assert!(props.left_semiring);
381        assert!(props.right_semiring);
382        assert!(props.commutative);
383        assert!(props.idempotent);
384        assert!(props.path);
385    }
386
387    #[test]
388    fn test_boolean_from_str() {
389        assert_eq!(
390            BooleanWeight::from_str("true").unwrap(),
391            BooleanWeight::new(true)
392        );
393        assert_eq!(
394            BooleanWeight::from_str("false").unwrap(),
395            BooleanWeight::new(false)
396        );
397    }
398
399    #[test]
400    fn test_boolean_operator_overloads() {
401        let w_true = BooleanWeight::new(true);
402        let w_false = BooleanWeight::new(false);
403
404        // Test + operator (OR)
405        assert_eq!(w_true + w_false, BooleanWeight::new(true));
406        assert_eq!(w_false + w_false, BooleanWeight::new(false));
407
408        // Test * operator (AND)
409        assert_eq!(w_true * w_false, BooleanWeight::new(false));
410        assert_eq!(w_true * w_true, BooleanWeight::new(true));
411    }
412
413    #[test]
414    fn test_boolean_identity_laws() {
415        let w = BooleanWeight::new(true);
416        let zero = BooleanWeight::zero();
417        let one = BooleanWeight::one();
418
419        // Additive identity
420        assert_eq!(w + zero, w);
421        assert_eq!(zero + w, w);
422
423        // Multiplicative identity
424        assert_eq!(w * one, w);
425        assert_eq!(one * w, w);
426
427        // Annihilation by zero
428        assert!(Semiring::is_zero(&(w * zero)));
429        assert!(Semiring::is_zero(&(zero * w)));
430    }
431
432    #[test]
433    fn test_boolean_semiring_axioms() {
434        let a = BooleanWeight::new(true);
435        let b = BooleanWeight::new(false);
436        let c = BooleanWeight::new(true);
437
438        // Associativity of addition
439        assert_eq!((a + b) + c, a + (b + c));
440
441        // Associativity of multiplication
442        assert_eq!((a * b) * c, a * (b * c));
443
444        // Commutativity of addition
445        assert_eq!(a + b, b + a);
446
447        // Commutativity of multiplication
448        assert_eq!(a * b, b * a);
449
450        // Distributivity
451        assert_eq!((a + b) * c, (a * c) + (b * c));
452    }
453
454    #[test]
455    fn test_boolean_correctness() {
456        // Test all combinations
457        let t = BooleanWeight::new(true);
458        let f = BooleanWeight::new(false);
459
460        // OR truth table
461        assert_eq!(f + f, f);
462        assert_eq!(f + t, t);
463        assert_eq!(t + f, t);
464        assert_eq!(t + t, t);
465
466        // AND truth table
467        assert_eq!(f * f, f);
468        assert_eq!(f * t, f);
469        assert_eq!(t * f, f);
470        assert_eq!(t * t, t);
471    }
472}