qualia_core_db/modalities/likeliness/mod.rs
1//! **Likeliness** — a qualitative, ordinal calculus of expectation (Vector Semantics
2//! §4.2; Kornai's "naive" inference). The **third** uncertainty modality, built as its
3//! own thing rather than folded into `defeasible`/`fuzzy`.
4//!
5//! > Timothy, 2026-06-27: "I'm undecided [whether it folds], which suggests it should
6//! > be done as a new modality." Indecision about the fold is itself the signal that
7//! > this is a distinct calculus — forcing it into an existing modality would distort
8//! > both.
9//!
10//! ## Why it is genuinely distinct
11//!
12//! * Not [`crate::modalities::probabilistic`] (continuous `[0,1]`, Kolmogorov/Bayes):
13//! likeliness is **ordinal** and **non-additive** — `P(x)` and `P(¬x)` need not sum
14//! to a constant, and there is no normalisation. It expresses *how expected* a
15//! proposition is, qualitatively.
16//! * Not [`crate::modalities::fuzzy`] (continuous `[0,1]` set-membership, t-norm):
17//! likeliness is graded **belief/expectation over propositions**, not degree of
18//! set-membership.
19//! * Adjacent to [`crate::modalities::defeasible`] (defaults) but distinct: defeasible
20//! resolves a **crisp** conclusion by rule priority/defeaters; likeliness carries a
21//! **graded degree** on the conclusion and composes it.
22//!
23//! ## The calculus
24//!
25//! Likeliness lives on a symmetric 7-level ordinal scale centred on `Even`. The logical
26//! operators ([`algebra`]) form a **Kleene / De Morgan algebra**: `not` is the
27//! order-reversing involution, `and` is the meet (weakest link), `or` the join (best
28//! alternative). Crucially `or(l, not l)` need *not* be `Certain` and `and(l, not l)`
29//! need *not* be `Impossible` — **no excluded middle, no contradiction collapse** — which
30//! is exactly the non-probabilistic, defeasible character. On top sit the naive
31//! inference rules ([`inference`]): weakest-link modus ponens, chain attenuation, and
32//! defeasible revision. Kernel-class `ElementwiseMap` (trivial CPU; no GPU path, §13).
33
34pub mod algebra;
35pub mod inference;
36
37pub use algebra::{and, combine_premises, combine_routes, not, or};
38pub use inference::{attenuate, infer_chain, modus_ponens, rebut, revise};
39
40/// An ordinal degree of expectation. Stored so `self as i8` is the signed level in
41/// `[-3, +3]`, with `Even = 0` the point of no information.
42#[repr(i8)]
43#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
44pub enum Likeliness {
45 Impossible = -3,
46 VeryUnlikely = -2,
47 Unlikely = -1,
48 Even = 0,
49 Likely = 1,
50 VeryLikely = 2,
51 Certain = 3,
52}
53
54impl Likeliness {
55 pub const MIN_LEVEL: i8 = -3;
56 pub const MAX_LEVEL: i8 = 3;
57
58 /// The signed ordinal level in `[-3, +3]`.
59 pub const fn level(self) -> i8 {
60 self as i8
61 }
62
63 /// The likeliness for a level, saturating to the scale bounds.
64 pub fn from_level(level: i8) -> Self {
65 match level.clamp(Self::MIN_LEVEL, Self::MAX_LEVEL) {
66 -3 => Self::Impossible,
67 -2 => Self::VeryUnlikely,
68 -1 => Self::Unlikely,
69 0 => Self::Even,
70 1 => Self::Likely,
71 2 => Self::VeryLikely,
72 _ => Self::Certain,
73 }
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn level_round_trips_and_orders() {
83 for l in Likeliness::MIN_LEVEL..=Likeliness::MAX_LEVEL {
84 assert_eq!(Likeliness::from_level(l).level(), l);
85 }
86 // Ordinal ordering ascends with expectation.
87 assert!(Likeliness::Impossible < Likeliness::Even);
88 assert!(Likeliness::Even < Likeliness::Certain);
89 assert!(Likeliness::Likely < Likeliness::VeryLikely);
90 }
91
92 #[test]
93 fn from_level_saturates() {
94 assert_eq!(Likeliness::from_level(99), Likeliness::Certain);
95 assert_eq!(Likeliness::from_level(-99), Likeliness::Impossible);
96 }
97}