relmath-rs 0.7.0

Relation-first mathematics and scientific computing in Rust.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
//! Additive annotation-domain and annotated-relation foundations.
//!
//! In this G4 slice, an annotation is a value combined by semiring-like
//! operations and stored in a deterministic annotated relation. Facts are kept
//! in a `BTreeMap`, so iteration order follows fact order. Only non-zero
//! annotations are stored; exact support materialization forgets annotation
//! values and keeps only the facts whose stored annotation is not zero.
//!
//! Unlike [`crate::provenance`], this module does not model witness sets or
//! explanation queries. An annotation here is the stored algebraic value for a
//! fact, not a derivation witness. Relation-level exact support materialization
//! for annotated surfaces is also available generically through
//! [`crate::ExactSupport`].
//!
//! Ordinary exact relations correspond to the boolean semiring, where `false`
//! means absence, `true` means presence, `add` models union-like combination,
//! and `mul` models composition-like chaining.

use std::collections::BTreeMap;

use crate::{
    BinaryRelation, NaryRelation, NaryRelationError, UnaryRelation, traits::FiniteRelation,
};

/// Semiring-like annotation domain for additive relation semantics.
///
/// This trait captures the smallest G4 foundation currently needed by the
/// crate:
///
/// - `zero` models absence or additive identity;
/// - `one` models multiplicative identity;
/// - `add` models union-like combination;
/// - `mul` models composition-like chaining.
///
/// In later annotated relation layers, materializing exact support should keep
/// tuples whose annotation is not zero and forget the annotation value itself.
///
/// # Examples
///
/// ```rust
/// use relmath::annotated::{BooleanSemiring, Semiring};
///
/// fn reachable_via_two_hop_or_direct(
///     direct: BooleanSemiring,
///     first_leg: BooleanSemiring,
///     second_leg: BooleanSemiring,
/// ) -> BooleanSemiring {
///     direct.add(&first_leg.mul(&second_leg))
/// }
///
/// let reachable = reachable_via_two_hop_or_direct(
///     BooleanSemiring::FALSE,
///     BooleanSemiring::TRUE,
///     BooleanSemiring::TRUE,
/// );
///
/// assert!(reachable.is_one());
/// assert!(bool::from(reachable));
/// ```
pub trait Semiring: Clone + Eq {
    /// Returns the additive identity for the annotation domain.
    #[must_use]
    fn zero() -> Self;

    /// Returns the multiplicative identity for the annotation domain.
    #[must_use]
    fn one() -> Self;

    /// Combines two annotations with union-like or choice-like semantics.
    #[must_use]
    fn add(&self, rhs: &Self) -> Self;

    /// Combines two annotations with chaining or composition-like semantics.
    #[must_use]
    fn mul(&self, rhs: &Self) -> Self;

    /// Returns `true` when this annotation is the additive identity.
    #[must_use]
    fn is_zero(&self) -> bool {
        self == &Self::zero()
    }

    /// Returns `true` when this annotation is the multiplicative identity.
    #[must_use]
    fn is_one(&self) -> bool {
        self == &Self::one()
    }
}

/// Deterministic annotated facts over a semiring-like annotation domain.
///
/// `AnnotatedRelation<F, A>` stores at most one annotation per fact. Repeated
/// insertion of the same fact combines annotations by `Semiring::add` in
/// insertion order. Only non-zero annotations are stored, so exact support is
/// the set of facts whose current annotation is not zero.
///
/// Inserting a zero annotation for an absent fact does nothing. Inserting a
/// zero annotation for a present fact also leaves the stored annotation
/// unchanged because zero is the additive identity. If a custom annotation
/// domain combines to zero, the fact is removed from the annotated relation.
/// Unlike provenance in [`crate::provenance`], the stored value is the
/// annotation itself rather than a witness or explanation set.
///
/// This first annotated relation slice focuses on deterministic storage,
/// inspection, and exact support materialization. Annotated set algebra,
/// annotated composition, temporal semantics, and broader uncertainty
/// operators remain later work.
///
/// # Examples
///
/// ```rust
/// use relmath::{
///     BinaryRelation,
///     annotated::{AnnotatedRelation, Semiring},
/// };
///
/// #[derive(Clone, Debug, PartialEq, Eq)]
/// struct Count(u8);
///
/// impl Semiring for Count {
///     fn zero() -> Self {
///         Self(0)
///     }
///
///     fn one() -> Self {
///         Self(1)
///     }
///
///     fn add(&self, rhs: &Self) -> Self {
///         Self(self.0.saturating_add(rhs.0))
///     }
///
///     fn mul(&self, rhs: &Self) -> Self {
///         Self(self.0.saturating_mul(rhs.0))
///     }
/// }
///
/// let mut assignments = AnnotatedRelation::new();
///
/// assert!(assignments.insert(("Alice", "Review"), Count(1)));
/// assert!(assignments.insert(("Alice", "Review"), Count(2)));
/// assert_eq!(
///     assignments.annotation_of(&("Alice", "Review")),
///     Some(&Count(3))
/// );
///
/// let support: BinaryRelation<_, _> = assignments.to_binary_relation();
/// assert_eq!(support.to_vec(), vec![("Alice", "Review")]);
/// ```
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AnnotatedRelation<F: Ord, A: Semiring> {
    facts: BTreeMap<F, A>,
}

impl<F: Ord, A: Semiring> AnnotatedRelation<F, A> {
    /// Creates an empty annotated relation.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use relmath::annotated::{AnnotatedRelation, BooleanSemiring};
    /// use relmath::FiniteRelation;
    ///
    /// let relation = AnnotatedRelation::<(&str, &str), BooleanSemiring>::new();
    ///
    /// assert!(relation.is_empty());
    /// ```
    #[must_use]
    pub fn new() -> Self {
        Self {
            facts: BTreeMap::new(),
        }
    }

    /// Creates an annotated relation from `(fact, annotation)` entries.
    ///
    /// Repeated facts combine annotations by `Semiring::add` in iterator order.
    /// Entries with zero annotations do not create stored support.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use relmath::annotated::{AnnotatedRelation, BooleanSemiring};
    ///
    /// let relation = AnnotatedRelation::from_facts([
    ///     (("alice", "read"), BooleanSemiring::TRUE),
    ///     (("alice", "read"), BooleanSemiring::FALSE),
    ///     (("bob", "approve"), BooleanSemiring::FALSE),
    /// ]);
    ///
    /// assert!(relation.contains_fact(&("alice", "read")));
    /// assert!(!relation.contains_fact(&("bob", "approve")));
    /// ```
    #[must_use]
    pub fn from_facts<I>(facts: I) -> Self
    where
        I: IntoIterator<Item = (F, A)>,
    {
        facts.into_iter().collect()
    }

    /// Inserts one annotation for a fact.
    ///
    /// Returns `true` when the stored support or stored annotation changes.
    ///
    /// Repeated facts combine as `current.add(&annotation)`. Facts whose
    /// resulting annotation is zero are removed from the relation.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use relmath::annotated::{AnnotatedRelation, BooleanSemiring};
    ///
    /// let mut relation = AnnotatedRelation::new();
    ///
    /// assert!(!relation.insert(("alice", "read"), BooleanSemiring::FALSE));
    /// assert!(relation.insert(("alice", "read"), BooleanSemiring::TRUE));
    /// assert!(!relation.insert(("alice", "read"), BooleanSemiring::FALSE));
    /// assert!(relation.contains_fact(&("alice", "read")));
    /// ```
    pub fn insert(&mut self, fact: F, annotation: A) -> bool {
        match self.facts.entry(fact) {
            std::collections::btree_map::Entry::Vacant(entry) => {
                if annotation.is_zero() {
                    return false;
                }

                entry.insert(annotation);
                true
            }
            std::collections::btree_map::Entry::Occupied(mut entry) => {
                let combined = entry.get().add(&annotation);

                if combined.is_zero() {
                    entry.remove();
                    true
                } else if entry.get() == &combined {
                    false
                } else {
                    *entry.get_mut() = combined;
                    true
                }
            }
        }
    }

    /// Returns `true` when the relation contains the given fact with a
    /// non-zero annotation.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use relmath::annotated::{AnnotatedRelation, BooleanSemiring};
    ///
    /// let relation = AnnotatedRelation::from_facts([
    ///     (("alice", "read"), BooleanSemiring::TRUE),
    ///     (("bob", "approve"), BooleanSemiring::FALSE),
    /// ]);
    ///
    /// assert!(relation.contains_fact(&("alice", "read")));
    /// assert!(!relation.contains_fact(&("bob", "approve")));
    /// ```
    #[must_use]
    pub fn contains_fact(&self, fact: &F) -> bool {
        self.facts.contains_key(fact)
    }

    /// Returns the stored annotation for one fact.
    ///
    /// When a fact is absent, this returns `None`. Zero annotations are not
    /// stored, so `None` also means there is no stored exact support for that
    /// fact. Unlike [`crate::provenance::ProvenanceRelation::why`], this
    /// returns the stored annotation value itself rather than a witness set.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use relmath::annotated::{AnnotatedRelation, BooleanSemiring};
    ///
    /// let relation = AnnotatedRelation::from_facts([
    ///     (("alice", "read"), BooleanSemiring::TRUE),
    ///     (("bob", "approve"), BooleanSemiring::FALSE),
    /// ]);
    ///
    /// assert_eq!(
    ///     relation.annotation_of(&("alice", "read")),
    ///     Some(&BooleanSemiring::TRUE)
    /// );
    /// assert_eq!(relation.annotation_of(&("bob", "approve")), None);
    /// ```
    #[must_use]
    pub fn annotation_of(&self, fact: &F) -> Option<&A> {
        self.facts.get(fact)
    }

    /// Returns an iterator over facts and annotations in deterministic fact
    /// order.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use relmath::annotated::{AnnotatedRelation, BooleanSemiring};
    ///
    /// let relation = AnnotatedRelation::from_facts([
    ///     (("alice", "admin"), BooleanSemiring::TRUE),
    ///     (("bob", "reviewer"), BooleanSemiring::TRUE),
    /// ]);
    ///
    /// assert_eq!(
    ///     relation
    ///         .iter()
    ///         .map(|(fact, annotation)| (*fact, *annotation))
    ///         .collect::<Vec<_>>(),
    ///     vec![
    ///         (("alice", "admin"), BooleanSemiring::TRUE),
    ///         (("bob", "reviewer"), BooleanSemiring::TRUE),
    ///     ]
    /// );
    /// ```
    pub fn iter(&self) -> impl Iterator<Item = (&F, &A)> {
        self.facts.iter()
    }

    /// Returns the exact support relation of stored facts as a unary relation.
    ///
    /// This materialization forgets annotation values and keeps only which
    /// facts are currently present with a non-zero annotation. Generic code
    /// can access the same relation-level boundary through
    /// [`crate::ExactSupport::exact_support`].
    ///
    /// # Examples
    ///
    /// ```rust
    /// use relmath::annotated::{AnnotatedRelation, BooleanSemiring};
    ///
    /// let relation = AnnotatedRelation::from_facts([
    ///     (("alice", "admin"), BooleanSemiring::TRUE),
    ///     (("bob", "reviewer"), BooleanSemiring::TRUE),
    ///     (("bob", "reviewer"), BooleanSemiring::FALSE),
    /// ]);
    ///
    /// assert_eq!(
    ///     relation.support().to_vec(),
    ///     vec![("alice", "admin"), ("bob", "reviewer")]
    /// );
    /// ```
    #[must_use]
    pub fn support(&self) -> UnaryRelation<F>
    where
        F: Clone,
    {
        self.facts.keys().cloned().collect()
    }
}

impl<F: Ord, A: Semiring> Default for AnnotatedRelation<F, A> {
    fn default() -> Self {
        Self::new()
    }
}

impl<F: Ord, A: Semiring> FiniteRelation for AnnotatedRelation<F, A> {
    fn len(&self) -> usize {
        self.facts.len()
    }
}

impl<F: Ord, A: Semiring> FromIterator<(F, A)> for AnnotatedRelation<F, A> {
    fn from_iter<I: IntoIterator<Item = (F, A)>>(iter: I) -> Self {
        let mut relation = Self::new();
        relation.extend(iter);
        relation
    }
}

impl<F: Ord, A: Semiring> Extend<(F, A)> for AnnotatedRelation<F, A> {
    fn extend<I: IntoIterator<Item = (F, A)>>(&mut self, iter: I) {
        for (fact, annotation) in iter {
            self.insert(fact, annotation);
        }
    }
}

impl<T: Ord + Clone, A: Semiring> AnnotatedRelation<T, A> {
    /// Converts scalar facts into a unary relation support set.
    ///
    /// Generic code can access the same unary materialization boundary through
    /// [`crate::ToExactUnaryRelation`].
    ///
    /// # Examples
    ///
    /// ```rust
    /// use relmath::annotated::{AnnotatedRelation, BooleanSemiring};
    ///
    /// let concepts = AnnotatedRelation::from_facts([
    ///     ("Closure", BooleanSemiring::TRUE),
    ///     ("Relations", BooleanSemiring::TRUE),
    /// ]);
    ///
    /// assert_eq!(
    ///     concepts.to_unary_relation().to_vec(),
    ///     vec!["Closure", "Relations"]
    /// );
    /// ```
    #[must_use]
    pub fn to_unary_relation(&self) -> UnaryRelation<T> {
        self.support()
    }
}

impl<A0: Ord + Clone, B0: Ord + Clone, A: Semiring> AnnotatedRelation<(A0, B0), A> {
    /// Converts pair facts into a binary relation support set.
    ///
    /// Generic code can access the same binary materialization boundary
    /// through [`crate::ToExactBinaryRelation`].
    ///
    /// # Examples
    ///
    /// ```rust
    /// use relmath::annotated::{AnnotatedRelation, BooleanSemiring};
    ///
    /// let permissions = AnnotatedRelation::from_facts([
    ///     (("Alice", "read"), BooleanSemiring::TRUE),
    ///     (("Bob", "approve"), BooleanSemiring::TRUE),
    /// ]);
    ///
    /// assert_eq!(
    ///     permissions.to_binary_relation().to_vec(),
    ///     vec![("Alice", "read"), ("Bob", "approve")]
    /// );
    /// ```
    #[must_use]
    pub fn to_binary_relation(&self) -> BinaryRelation<A0, B0> {
        self.facts.keys().cloned().collect()
    }
}

impl<T: Ord + Clone, A: Semiring> AnnotatedRelation<Vec<T>, A> {
    /// Converts row facts into an n-ary relation with the given schema.
    ///
    /// The explicit schema preserves the current exact n-ary boundary where row
    /// values do not themselves encode column names. This method reuses the
    /// current `NaryRelation` validation rules, so duplicate or blank column
    /// names and row-arity mismatches return `NaryRelationError`. Generic code
    /// can access the same n-ary materialization boundary through
    /// [`crate::ToExactNaryRelation`].
    ///
    /// # Examples
    ///
    /// ```rust
    /// use relmath::annotated::{AnnotatedRelation, BooleanSemiring};
    ///
    /// let completion = AnnotatedRelation::from_facts([
    ///     (vec!["Alice", "Math", "passed"], BooleanSemiring::TRUE),
    ///     (vec!["Bob", "Physics", "passed"], BooleanSemiring::TRUE),
    /// ]);
    ///
    /// let relation = completion
    ///     .to_nary_relation(["student", "course", "status"])
    ///     .expect("expected valid support relation");
    ///
    /// assert_eq!(
    ///     relation.to_rows(),
    ///     vec![
    ///         vec!["Alice", "Math", "passed"],
    ///         vec!["Bob", "Physics", "passed"],
    ///     ]
    /// );
    /// ```
    pub fn to_nary_relation<I, S>(&self, schema: I) -> Result<NaryRelation<T>, NaryRelationError>
    where
        I: IntoIterator<Item = S>,
        S: Into<String>,
    {
        NaryRelation::from_rows(schema, self.facts.keys().cloned())
    }
}

/// Boolean semiring for ordinary exact relation semantics.
///
/// `BooleanSemiring` is the first built-in annotation family in G4. It keeps
/// the exact meaning already used by the published unary, binary, and n-ary
/// relations:
///
/// - `false` is zero and represents absence;
/// - `true` is one and represents presence or identity;
/// - `add` uses logical OR;
/// - `mul` uses logical AND.
///
/// # Examples
///
/// ```rust
/// use relmath::annotated::{BooleanSemiring, Semiring};
///
/// let direct = BooleanSemiring::FALSE;
/// let first_leg = BooleanSemiring::TRUE;
/// let second_leg = BooleanSemiring::TRUE;
///
/// assert_eq!(direct.add(&first_leg), BooleanSemiring::TRUE);
/// assert_eq!(first_leg.mul(&second_leg), BooleanSemiring::TRUE);
/// assert!(BooleanSemiring::zero().is_zero());
/// assert!(BooleanSemiring::one().is_one());
/// ```
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BooleanSemiring(bool);

impl BooleanSemiring {
    /// The additive identity and absence value for the boolean semiring.
    pub const FALSE: Self = Self(false);

    /// The multiplicative identity and present value for the boolean semiring.
    pub const TRUE: Self = Self(true);

    /// Creates a boolean semiring value from a raw boolean.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use relmath::annotated::BooleanSemiring;
    ///
    /// assert_eq!(BooleanSemiring::new(false), BooleanSemiring::FALSE);
    /// assert_eq!(BooleanSemiring::new(true), BooleanSemiring::TRUE);
    /// ```
    #[must_use]
    pub const fn new(value: bool) -> Self {
        Self(value)
    }

    /// Returns the raw boolean stored by this semiring value.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use relmath::annotated::BooleanSemiring;
    ///
    /// assert!(!BooleanSemiring::FALSE.value());
    /// assert!(BooleanSemiring::TRUE.value());
    /// ```
    #[must_use]
    pub const fn value(self) -> bool {
        self.0
    }
}

impl From<bool> for BooleanSemiring {
    fn from(value: bool) -> Self {
        Self::new(value)
    }
}

impl From<BooleanSemiring> for bool {
    fn from(value: BooleanSemiring) -> Self {
        value.value()
    }
}

impl Semiring for BooleanSemiring {
    fn zero() -> Self {
        Self::FALSE
    }

    fn one() -> Self {
        Self::TRUE
    }

    fn add(&self, rhs: &Self) -> Self {
        Self(self.0 || rhs.0)
    }

    fn mul(&self, rhs: &Self) -> Self {
        Self(self.0 && rhs.0)
    }
}