Skip to main content

meta_language/
semantics.rs

1/// Many-valued semantic truth value: the `logic-model.belnap-four-valued` model of
2/// `src/data/foundation-models.json`, one logic among the recorded models rather than a
3/// built-in universal logic.
4#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5pub enum TruthValue {
6    True,
7    False,
8    Unknown,
9    Both,
10}
11
12impl TruthValue {
13    /// Logical conjunction.
14    #[must_use]
15    pub const fn and(self, other: Self) -> Self {
16        match (self, other) {
17            (Self::False, _) | (_, Self::False) => Self::False,
18            (Self::True, value) | (value, Self::True) => value,
19            (Self::Both, _) | (_, Self::Both) => Self::Both,
20            (Self::Unknown, Self::Unknown) => Self::Unknown,
21        }
22    }
23
24    /// Logical disjunction.
25    #[must_use]
26    pub const fn or(self, other: Self) -> Self {
27        match (self, other) {
28            (Self::True, _) | (_, Self::True) => Self::True,
29            (Self::False, value) | (value, Self::False) => value,
30            (Self::Both, _) | (_, Self::Both) => Self::Both,
31            (Self::Unknown, Self::Unknown) => Self::Unknown,
32        }
33    }
34
35    /// Logical negation.
36    #[must_use]
37    pub const fn negate(self) -> Self {
38        match self {
39            Self::True => Self::False,
40            Self::False => Self::True,
41            Self::Unknown => Self::Unknown,
42            Self::Both => Self::Both,
43        }
44    }
45}
46
47const PROBABILITY_SCALE_BASIS_POINTS: u16 = 10_000;
48const PROBABILITY_SCALE_U32: u32 = 10_000;
49const PROBABILITY_SCALE_U128: u128 = 10_000;
50
51/// Fixed-point probability stored as basis points from `0` to `10_000`.
52#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
53pub struct Probability {
54    basis_points: u16,
55}
56
57impl Probability {
58    /// Probability `0.0`.
59    pub const ZERO: Self = Self { basis_points: 0 };
60
61    /// Probability `1.0`.
62    pub const ONE: Self = Self {
63        basis_points: PROBABILITY_SCALE_BASIS_POINTS,
64    };
65
66    /// Creates a probability from basis points, where `10_000` means certainty.
67    #[must_use]
68    pub const fn from_basis_points(basis_points: u16) -> Option<Self> {
69        if basis_points <= PROBABILITY_SCALE_BASIS_POINTS {
70            Some(Self { basis_points })
71        } else {
72            None
73        }
74    }
75
76    /// Creates a probability from a ratio, rounded to the nearest basis point.
77    #[must_use]
78    pub fn from_ratio(numerator: u64, denominator: u64) -> Option<Self> {
79        if denominator == 0 || numerator > denominator {
80            return None;
81        }
82
83        let scaled = (u128::from(numerator) * PROBABILITY_SCALE_U128 + u128::from(denominator) / 2)
84            / u128::from(denominator);
85        let basis_points =
86            u16::try_from(scaled).expect("scaled probability must fit into basis points");
87
88        Self::from_basis_points(basis_points)
89    }
90
91    /// Returns the fixed-point probability in basis points.
92    #[must_use]
93    pub const fn basis_points(self) -> u16 {
94        self.basis_points
95    }
96
97    /// Returns `1 - self`.
98    #[must_use]
99    pub const fn complement(self) -> Self {
100        Self {
101            basis_points: PROBABILITY_SCALE_BASIS_POINTS - self.basis_points,
102        }
103    }
104}
105
106/// Probabilistic truth value for confidence links: the `logic-model.probabilistic-truth`
107/// model of `src/data/foundation-models.json`, one logic among the recorded models.
108#[derive(Clone, Copy, Debug, PartialEq, Eq)]
109pub struct ProbabilisticTruthValue {
110    true_probability: Probability,
111}
112
113impl ProbabilisticTruthValue {
114    /// Creates a probabilistic truth value from the probability of truth.
115    #[must_use]
116    pub const fn new(true_probability: Probability) -> Self {
117        Self { true_probability }
118    }
119
120    /// Creates a probabilistic truth value from a ratio.
121    #[must_use]
122    pub fn from_ratio(numerator: u64, denominator: u64) -> Option<Self> {
123        Some(Self::new(Probability::from_ratio(numerator, denominator)?))
124    }
125
126    /// Probability that the proposition is true.
127    #[must_use]
128    pub const fn true_probability(self) -> Probability {
129        self.true_probability
130    }
131
132    /// Probability that the proposition is false.
133    #[must_use]
134    pub const fn false_probability(self) -> Probability {
135        self.true_probability.complement()
136    }
137
138    /// Logical negation, represented as probability complement.
139    #[must_use]
140    pub const fn negate(self) -> Self {
141        Self::new(self.false_probability())
142    }
143
144    /// Independent probabilistic conjunction.
145    #[must_use]
146    pub fn and(self, other: Self) -> Self {
147        Self::new(multiply_probabilities(
148            self.true_probability(),
149            other.true_probability(),
150        ))
151    }
152
153    /// Independent probabilistic disjunction.
154    #[must_use]
155    pub fn or(self, other: Self) -> Self {
156        self.negate().and(other.negate()).negate()
157    }
158}
159
160fn multiply_probabilities(left: Probability, right: Probability) -> Probability {
161    let product = u32::from(left.basis_points()) * u32::from(right.basis_points());
162    let rounded = (product + PROBABILITY_SCALE_U32 / 2) / PROBABILITY_SCALE_U32;
163    let basis_points =
164        u16::try_from(rounded).expect("probability product must fit into basis points");
165
166    Probability::from_basis_points(basis_points).expect("probability product stays in range")
167}