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}