Skip to main content

tensorlogic_ir/expr/
modal_axioms.rs

1//! Modal logic axiom systems and verification.
2//!
3//! This module implements well-known modal logic axiom systems and provides
4//! tools for verifying modal expressions conform to specific axioms.
5//!
6//! # Modal Logic Systems
7//!
8//! - **K (Basic Modal Logic)**: The weakest normal modal logic
9//!   - Axiom K: □(p → q) → (□p → □q) (Distribution axiom)
10//!   - Necessitation: If ⊢ p, then ⊢ □p
11//!
12//! - **T (Reflexive)**: K + T axiom
13//!   - Axiom T: □p → p (Reflexivity - what is necessary is true)
14//!
15//! - **S4 (Transitive)**: K + T + 4 axiom
16//!   - Axiom 4: □p → □□p (Transitivity - necessity of necessity)
17//!
18//! - **S5 (Euclidean)**: K + T + 5 axiom
19//!   - Axiom 5: ◇p → □◇p (Euclidean property - possibility implies necessary possibility)
20//!   - Equivalent to: S4 + Axiom B (p → □◇p)
21//!
22//! # Example
23//!
24//! ```rust
25//! use tensorlogic_ir::{TLExpr, ModalSystem, verify_axiom_k, verify_axiom_t};
26//!
27//! // Check if an expression satisfies axiom K
28//! let expr = TLExpr::imply(
29//!     TLExpr::modal_box(TLExpr::imply(
30//!         TLExpr::pred("p", vec![]),
31//!         TLExpr::pred("q", vec![])
32//!     )),
33//!     TLExpr::imply(
34//!         TLExpr::modal_box(TLExpr::pred("p", vec![])),
35//!         TLExpr::modal_box(TLExpr::pred("q", vec![]))
36//!     )
37//! );
38//!
39//! assert!(verify_axiom_k(&expr));
40//! ```
41
42use super::TLExpr;
43
44/// Modal logic axiom systems.
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
46pub enum ModalSystem {
47    /// K - Basic modal logic (weakest normal modal logic)
48    K,
49    /// T - Reflexive modal logic (K + reflexivity)
50    T,
51    /// S4 - Transitive modal logic (T + transitivity)
52    S4,
53    /// S5 - Euclidean modal logic (S4 + symmetry/Euclidean property)
54    S5,
55    /// D - Deontic logic (K + seriality: □p → ◇p)
56    D,
57    /// B - Symmetric modal logic (T + symmetry: p → □◇p)
58    B,
59}
60
61impl ModalSystem {
62    /// Get all axioms that this system satisfies.
63    pub fn axioms(&self) -> Vec<&'static str> {
64        match self {
65            ModalSystem::K => vec!["K"],
66            ModalSystem::T => vec!["K", "T"],
67            ModalSystem::S4 => vec!["K", "T", "4"],
68            ModalSystem::S5 => vec!["K", "T", "4", "5"],
69            ModalSystem::D => vec!["K", "D"],
70            ModalSystem::B => vec!["K", "T", "B"],
71        }
72    }
73
74    /// Check if this system includes a specific axiom.
75    pub fn has_axiom(&self, axiom: &str) -> bool {
76        self.axioms().contains(&axiom)
77    }
78
79    /// Get a description of this modal system.
80    pub fn description(&self) -> &'static str {
81        match self {
82            ModalSystem::K => "Basic modal logic - distribution of necessity over implication",
83            ModalSystem::T => "Reflexive logic - what is necessary is true",
84            ModalSystem::S4 => "Transitive logic - necessity of necessity",
85            ModalSystem::S5 => "Euclidean logic - full modal equivalence",
86            ModalSystem::D => "Deontic logic - consistency (obligation implies permission)",
87            ModalSystem::B => "Symmetric logic - truth implies necessary possibility",
88        }
89    }
90}
91
92/// Verify that an expression conforms to Axiom K: □(p → q) → (□p → □q)
93///
94/// The K axiom states that necessity distributes over implication.
95pub fn verify_axiom_k(expr: &TLExpr) -> bool {
96    matches!(
97        expr,
98        TLExpr::Imply(box1, box2) if matches!(
99            (&**box1, &**box2),
100            (
101                TLExpr::Box(impl1),
102                TLExpr::Imply(box_p, box_q)
103            ) if matches!(
104                (&**impl1, &**box_p, &**box_q),
105                (
106                    TLExpr::Imply(p1, q1),
107                    TLExpr::Box(p2),
108                    TLExpr::Box(q2)
109                ) if p1 == p2 && q1 == q2
110            )
111        )
112    )
113}
114
115/// Verify that an expression conforms to Axiom T: □p → p
116///
117/// The T axiom states that what is necessary is true (reflexivity).
118pub fn verify_axiom_t(expr: &TLExpr) -> bool {
119    matches!(
120        expr,
121        TLExpr::Imply(box_p, p) if matches!(
122            (&**box_p, &**p),
123            (TLExpr::Box(inner), outer) if **inner == *outer
124        )
125    )
126}
127
128/// Verify that an expression conforms to Axiom 4: □p → □□p
129///
130/// The 4 axiom states that necessity is transitive.
131pub fn verify_axiom_4(expr: &TLExpr) -> bool {
132    matches!(
133        expr,
134        TLExpr::Imply(box_p, box_box_p) if matches!(
135            (&**box_p, &**box_box_p),
136            (TLExpr::Box(p1), TLExpr::Box(box_p2)) if matches!(
137                &**box_p2,
138                TLExpr::Box(p2) if p1 == p2
139            )
140        )
141    )
142}
143
144/// Verify that an expression conforms to Axiom 5: ◇p → □◇p
145///
146/// The 5 axiom states the Euclidean property.
147pub fn verify_axiom_5(expr: &TLExpr) -> bool {
148    matches!(
149        expr,
150        TLExpr::Imply(dia_p, box_dia_p) if matches!(
151            (&**dia_p, &**box_dia_p),
152            (TLExpr::Diamond(p1), TLExpr::Box(dia_p2)) if matches!(
153                &**dia_p2,
154                TLExpr::Diamond(p2) if p1 == p2
155            )
156        )
157    )
158}
159
160/// Verify that an expression conforms to Axiom D: □p → ◇p
161///
162/// The D axiom states seriality (consistency in deontic logic).
163pub fn verify_axiom_d(expr: &TLExpr) -> bool {
164    matches!(
165        expr,
166        TLExpr::Imply(box_p, dia_p) if matches!(
167            (&**box_p, &**dia_p),
168            (TLExpr::Box(p1), TLExpr::Diamond(p2)) if p1 == p2
169        )
170    )
171}
172
173/// Verify that an expression conforms to Axiom B: p → □◇p
174///
175/// The B axiom states symmetry.
176pub fn verify_axiom_b(expr: &TLExpr) -> bool {
177    matches!(
178        expr,
179        TLExpr::Imply(p, box_dia_p) if matches!(
180            &**box_dia_p,
181            TLExpr::Box(dia_p) if matches!(
182                &**dia_p,
183                TLExpr::Diamond(p2) if **p == **p2
184            )
185        )
186    )
187}
188
189/// Apply axiom K to simplify modal expressions.
190///
191/// Transforms: □(p → q) ∧ □p into □q (modus ponens in modal logic)
192pub fn apply_axiom_k(expr: &TLExpr) -> Option<TLExpr> {
193    if let TLExpr::And(left, right) = expr {
194        // Check for pattern: □(p → q) ∧ □p
195        if let (TLExpr::Box(impl_expr), TLExpr::Box(p_expr)) = (&**left, &**right) {
196            if let TLExpr::Imply(p1, q) = &**impl_expr {
197                if p1 == p_expr {
198                    // We have □(p → q) ∧ □p, so we can derive □q
199                    return Some(TLExpr::modal_box((**q).clone()));
200                }
201            }
202        }
203        // Try the other order: □p ∧ □(p → q)
204        if let (TLExpr::Box(p_expr), TLExpr::Box(impl_expr)) = (&**left, &**right) {
205            if let TLExpr::Imply(p1, q) = &**impl_expr {
206                if p1 == p_expr {
207                    return Some(TLExpr::modal_box((**q).clone()));
208                }
209            }
210        }
211    }
212    None
213}
214
215/// Apply axiom T to simplify modal expressions.
216///
217/// Transforms: □p into p (when reflexivity holds)
218pub fn apply_axiom_t(expr: &TLExpr) -> Option<TLExpr> {
219    if let TLExpr::Box(inner) = expr {
220        Some((**inner).clone())
221    } else {
222        None
223    }
224}
225
226/// Apply axiom 4 to transform modal expressions.
227///
228/// Transforms: □p into □□p (adds modal depth)
229#[allow(dead_code)]
230pub fn apply_axiom_4_forward(expr: &TLExpr) -> Option<TLExpr> {
231    if let TLExpr::Box(_inner) = expr {
232        Some(TLExpr::modal_box(expr.clone()))
233    } else {
234        None
235    }
236}
237
238/// Apply axiom 4 in reverse to simplify.
239///
240/// Transforms: □□p into □p (reduces modal depth)
241#[allow(dead_code)]
242pub fn apply_axiom_4_backward(expr: &TLExpr) -> Option<TLExpr> {
243    if let TLExpr::Box(inner) = expr {
244        if let TLExpr::Box(_) = &**inner {
245            return Some((**inner).clone());
246        }
247    }
248    None
249}
250
251/// Apply axiom 5 to normalize modal expressions.
252///
253/// Transforms: ◇p into □◇p
254#[allow(dead_code)]
255pub fn apply_axiom_5(expr: &TLExpr) -> Option<TLExpr> {
256    if let TLExpr::Diamond(_) = expr {
257        Some(TLExpr::modal_box(expr.clone()))
258    } else {
259        None
260    }
261}
262
263/// Normalize a modal expression according to S5 axioms.
264///
265/// In S5, any sequence of modal operators can be reduced to a single operator.
266/// This function reduces nested modalities.
267pub fn normalize_s5(expr: &TLExpr) -> TLExpr {
268    match expr {
269        // □□p = □p in S5
270        TLExpr::Box(inner) if matches!(**inner, TLExpr::Box(_)) => normalize_s5(inner),
271        // ◇◇p = ◇p in S5
272        TLExpr::Diamond(inner) if matches!(**inner, TLExpr::Diamond(_)) => normalize_s5(inner),
273        // □◇p = ◇p in S5
274        TLExpr::Box(inner) if matches!(**inner, TLExpr::Diamond(_)) => normalize_s5(inner),
275        // ◇□p = □p in S5
276        TLExpr::Diamond(inner) if matches!(**inner, TLExpr::Box(_)) => normalize_s5(inner),
277        // Recurse into subexpressions
278        TLExpr::Box(inner) => TLExpr::modal_box(normalize_s5(inner)),
279        TLExpr::Diamond(inner) => TLExpr::modal_diamond(normalize_s5(inner)),
280        TLExpr::And(l, r) => TLExpr::and(normalize_s5(l), normalize_s5(r)),
281        TLExpr::Or(l, r) => TLExpr::or(normalize_s5(l), normalize_s5(r)),
282        TLExpr::Not(e) => TLExpr::negate(normalize_s5(e)),
283        TLExpr::Imply(l, r) => TLExpr::imply(normalize_s5(l), normalize_s5(r)),
284        _ => expr.clone(),
285    }
286}
287
288/// Calculate the modal depth of an expression.
289///
290/// Returns the maximum nesting level of modal operators.
291pub fn modal_depth(expr: &TLExpr) -> usize {
292    match expr {
293        TLExpr::Box(inner) | TLExpr::Diamond(inner) => 1 + modal_depth(inner),
294        TLExpr::And(l, r) | TLExpr::Or(l, r) | TLExpr::Imply(l, r) => {
295            modal_depth(l).max(modal_depth(r))
296        }
297        TLExpr::Not(e) => modal_depth(e),
298        _ => 0,
299    }
300}
301
302/// Check if an expression is modal-free (contains no modal operators).
303pub fn is_modal_free(expr: &TLExpr) -> bool {
304    modal_depth(expr) == 0
305}
306
307/// Extract all modal subformulas from an expression.
308pub fn extract_modal_subformulas(expr: &TLExpr) -> Vec<TLExpr> {
309    let mut formulas = Vec::new();
310    extract_modal_subformulas_rec(expr, &mut formulas);
311    formulas
312}
313
314fn extract_modal_subformulas_rec(expr: &TLExpr, acc: &mut Vec<TLExpr>) {
315    match expr {
316        TLExpr::Box(_) | TLExpr::Diamond(_) => {
317            acc.push(expr.clone());
318            // Also recurse into inner expression
319            if let TLExpr::Box(inner) | TLExpr::Diamond(inner) = expr {
320                extract_modal_subformulas_rec(inner, acc);
321            }
322        }
323        TLExpr::And(l, r) | TLExpr::Or(l, r) | TLExpr::Imply(l, r) => {
324            extract_modal_subformulas_rec(l, acc);
325            extract_modal_subformulas_rec(r, acc);
326        }
327        TLExpr::Not(e) => extract_modal_subformulas_rec(e, acc),
328        _ => {}
329    }
330}
331
332/// Verify that an expression is a theorem in a given modal system.
333///
334/// This is a simplified verification that checks for basic axiom patterns.
335/// Full verification would require a complete proof system.
336pub fn is_theorem_in_system(expr: &TLExpr, system: ModalSystem) -> bool {
337    // Check if expression matches any axiom in the system
338    let axioms = system.axioms();
339
340    (axioms.contains(&"K") && verify_axiom_k(expr))
341        || (axioms.contains(&"T") && verify_axiom_t(expr))
342        || (axioms.contains(&"4") && verify_axiom_4(expr))
343        || (axioms.contains(&"5") && verify_axiom_5(expr))
344        || (axioms.contains(&"D") && verify_axiom_d(expr))
345        || (axioms.contains(&"B") && verify_axiom_b(expr))
346}
347
348#[cfg(test)]
349mod tests {
350    use super::*;
351
352    #[test]
353    fn test_modal_system_axioms() {
354        assert_eq!(ModalSystem::K.axioms(), vec!["K"]);
355        assert_eq!(ModalSystem::T.axioms(), vec!["K", "T"]);
356        assert_eq!(ModalSystem::S4.axioms(), vec!["K", "T", "4"]);
357        assert_eq!(ModalSystem::S5.axioms(), vec!["K", "T", "4", "5"]);
358    }
359
360    #[test]
361    fn test_verify_axiom_k() {
362        // □(p → q) → (□p → □q)
363        let p = TLExpr::pred("p", vec![]);
364        let q = TLExpr::pred("q", vec![]);
365
366        let expr = TLExpr::imply(
367            TLExpr::modal_box(TLExpr::imply(p.clone(), q.clone())),
368            TLExpr::imply(TLExpr::modal_box(p), TLExpr::modal_box(q)),
369        );
370
371        assert!(verify_axiom_k(&expr));
372    }
373
374    #[test]
375    fn test_verify_axiom_t() {
376        // □p → p
377        let p = TLExpr::pred("p", vec![]);
378        let expr = TLExpr::imply(TLExpr::modal_box(p.clone()), p);
379
380        assert!(verify_axiom_t(&expr));
381    }
382
383    #[test]
384    fn test_verify_axiom_4() {
385        // □p → □□p
386        let p = TLExpr::pred("p", vec![]);
387        let expr = TLExpr::imply(
388            TLExpr::modal_box(p.clone()),
389            TLExpr::modal_box(TLExpr::modal_box(p)),
390        );
391
392        assert!(verify_axiom_4(&expr));
393    }
394
395    #[test]
396    fn test_verify_axiom_5() {
397        // ◇p → □◇p
398        let p = TLExpr::pred("p", vec![]);
399        let expr = TLExpr::imply(
400            TLExpr::modal_diamond(p.clone()),
401            TLExpr::modal_box(TLExpr::modal_diamond(p)),
402        );
403
404        assert!(verify_axiom_5(&expr));
405    }
406
407    #[test]
408    fn test_apply_axiom_k() {
409        // □(p → q) ∧ □p should yield □q
410        let p = TLExpr::pred("p", vec![]);
411        let q = TLExpr::pred("q", vec![]);
412
413        let expr = TLExpr::and(
414            TLExpr::modal_box(TLExpr::imply(p.clone(), q.clone())),
415            TLExpr::modal_box(p),
416        );
417
418        let result = apply_axiom_k(&expr).unwrap();
419        assert_eq!(result, TLExpr::modal_box(q));
420    }
421
422    #[test]
423    fn test_apply_axiom_t() {
424        let p = TLExpr::pred("p", vec![]);
425        let box_p = TLExpr::modal_box(p.clone());
426
427        let result = apply_axiom_t(&box_p).unwrap();
428        assert_eq!(result, p);
429    }
430
431    #[test]
432    fn test_normalize_s5() {
433        // □□p should become □p
434        let p = TLExpr::pred("p", vec![]);
435        let expr = TLExpr::modal_box(TLExpr::modal_box(p.clone()));
436
437        let normalized = normalize_s5(&expr);
438        assert_eq!(normalized, TLExpr::modal_box(p.clone()));
439
440        // □◇p should become ◇p
441        let expr2 = TLExpr::modal_box(TLExpr::modal_diamond(p.clone()));
442        let normalized2 = normalize_s5(&expr2);
443        assert_eq!(normalized2, TLExpr::modal_diamond(p));
444    }
445
446    #[test]
447    fn test_modal_depth() {
448        let p = TLExpr::pred("p", vec![]);
449        assert_eq!(modal_depth(&p), 0);
450
451        let box_p = TLExpr::modal_box(p.clone());
452        assert_eq!(modal_depth(&box_p), 1);
453
454        let box_box_p = TLExpr::modal_box(TLExpr::modal_box(p));
455        assert_eq!(modal_depth(&box_box_p), 2);
456    }
457
458    #[test]
459    fn test_is_modal_free() {
460        let p = TLExpr::pred("p", vec![]);
461        assert!(is_modal_free(&p));
462
463        let box_p = TLExpr::modal_box(p);
464        assert!(!is_modal_free(&box_p));
465    }
466
467    #[test]
468    fn test_extract_modal_subformulas() {
469        let p = TLExpr::pred("p", vec![]);
470        let q = TLExpr::pred("q", vec![]);
471
472        // □p ∧ ◇q
473        let expr = TLExpr::and(TLExpr::modal_box(p.clone()), TLExpr::modal_diamond(q));
474
475        let subformulas = extract_modal_subformulas(&expr);
476        assert_eq!(subformulas.len(), 2);
477    }
478
479    #[test]
480    fn test_is_theorem_in_system() {
481        // Axiom T: □p → p
482        let p = TLExpr::pred("p", vec![]);
483        let axiom_t = TLExpr::imply(TLExpr::modal_box(p.clone()), p);
484
485        assert!(is_theorem_in_system(&axiom_t, ModalSystem::T));
486        assert!(is_theorem_in_system(&axiom_t, ModalSystem::S4));
487        assert!(is_theorem_in_system(&axiom_t, ModalSystem::S5));
488        assert!(!is_theorem_in_system(&axiom_t, ModalSystem::K)); // K doesn't have axiom T
489    }
490}