logicaffeine_language/semantics/
kripke.rs

1//! Kripke Semantics Lowering Pass
2//!
3//! Transforms modal operators into explicit possible world semantics:
4//! - Diamond P (force <= 0.5) → Exists w'(Accessible(w, w') And P(w'))
5//! - Box P (force > 0.5) → ForAll w'(Accessible(w, w') Implies P(w'))
6
7use logicaffeine_base::Arena;
8use crate::ast::{LogicExpr, ModalDomain, ModalVector, NeoEventData, QuantifierKind, Term};
9use logicaffeine_base::{Interner, Symbol};
10use crate::token::TokenType;
11
12/// Context for tracking world variables during Kripke lowering.
13///
14/// For hardware verification, `clock_counter` tracks discrete timesteps
15/// and `domain_hint` disambiguates temporal vs modal lowering when
16/// the context is ambiguous.
17pub struct KripkeContext {
18    world_counter: u32,
19    current_world: Symbol,
20    /// Discrete timestep counter for hardware clock cycles.
21    /// Incremented when generating Next_Temporal worlds.
22    clock_counter: u32,
23    /// Hint for the current modal domain being lowered.
24    /// Set to `Some(Temporal)` when processing temporal operators,
25    /// enabling domain-aware disambiguation.
26    domain_hint: Option<ModalDomain>,
27}
28
29impl KripkeContext {
30    pub fn new(interner: &mut Interner) -> Self {
31        Self {
32            world_counter: 0,
33            current_world: interner.intern("w0"),
34            clock_counter: 0,
35            domain_hint: None,
36        }
37    }
38
39    pub fn fresh_world(&mut self, interner: &mut Interner) -> Symbol {
40        self.world_counter += 1;
41        interner.intern(&format!("w{}", self.world_counter))
42    }
43
44    /// Get the current clock counter value (discrete timestep).
45    pub fn clock_counter(&self) -> u32 {
46        self.clock_counter
47    }
48
49    /// Get the current domain hint.
50    pub fn domain_hint(&self) -> Option<ModalDomain> {
51        self.domain_hint
52    }
53
54    /// Advance the clock counter by one tick (for Next_Temporal).
55    fn tick_clock(&mut self) {
56        self.clock_counter += 1;
57    }
58
59    /// Set the domain hint for the current lowering context.
60    fn set_domain_hint(&mut self, domain: ModalDomain) {
61        self.domain_hint = Some(domain);
62    }
63
64    /// Clear the domain hint.
65    fn clear_domain_hint(&mut self) {
66        self.domain_hint = None;
67    }
68}
69
70/// Apply Kripke lowering to transform modal operators into explicit world quantification.
71///
72/// This transforms surface modal operators (`◇`, `□`) into explicit First-Order Logic
73/// with possible world semantics, enabling Z3 verification.
74///
75/// ## Example
76/// Surface: `◇Fly(x)` (John can fly)
77/// Deep: `∃w'(Accessible(w₀, w') ∧ Fly(x, w'))` (There exists an accessible world where John flies)
78pub fn apply_kripke_lowering<'a>(
79    expr: &'a LogicExpr<'a>,
80    expr_arena: &'a Arena<LogicExpr<'a>>,
81    term_arena: &'a Arena<Term<'a>>,
82    interner: &mut Interner,
83) -> &'a LogicExpr<'a> {
84    let mut ctx = KripkeContext::new(interner);
85    lower_expr(expr, &mut ctx, expr_arena, term_arena, interner)
86}
87
88fn lower_expr<'a>(
89    expr: &'a LogicExpr<'a>,
90    ctx: &mut KripkeContext,
91    expr_arena: &'a Arena<LogicExpr<'a>>,
92    term_arena: &'a Arena<Term<'a>>,
93    interner: &mut Interner,
94) -> &'a LogicExpr<'a> {
95    match expr {
96        LogicExpr::Modal { vector, operand } => {
97            lower_modal(vector, operand, ctx, expr_arena, term_arena, interner)
98        }
99
100        LogicExpr::Predicate { name, args, world } => {
101            if world.is_none() {
102                // Add current world to predicate
103                expr_arena.alloc(LogicExpr::Predicate {
104                    name: *name,
105                    args: *args,
106                    world: Some(ctx.current_world),
107                })
108            } else {
109                expr
110            }
111        }
112
113        LogicExpr::Quantifier { kind, variable, body, island_id } => {
114            let new_body = lower_expr(body, ctx, expr_arena, term_arena, interner);
115            expr_arena.alloc(LogicExpr::Quantifier {
116                kind: *kind,
117                variable: *variable,
118                body: new_body,
119                island_id: *island_id,
120            })
121        }
122
123        LogicExpr::BinaryOp { left, op, right } => {
124            let new_left = lower_expr(left, ctx, expr_arena, term_arena, interner);
125            let new_right = lower_expr(right, ctx, expr_arena, term_arena, interner);
126            expr_arena.alloc(LogicExpr::BinaryOp {
127                left: new_left,
128                op: op.clone(),
129                right: new_right,
130            })
131        }
132
133        LogicExpr::UnaryOp { op, operand } => {
134            let new_operand = lower_expr(operand, ctx, expr_arena, term_arena, interner);
135            expr_arena.alloc(LogicExpr::UnaryOp {
136                op: op.clone(),
137                operand: new_operand,
138            })
139        }
140
141        LogicExpr::NeoEvent(data) => {
142            // Set the world on the event to the current world
143            if data.world.is_none() {
144                expr_arena.alloc(LogicExpr::NeoEvent(Box::new(NeoEventData {
145                    event_var: data.event_var,
146                    verb: data.verb,
147                    roles: data.roles,
148                    modifiers: data.modifiers,
149                    suppress_existential: data.suppress_existential,
150                    world: Some(ctx.current_world),
151                })))
152            } else {
153                expr
154            }
155        }
156
157        LogicExpr::Temporal { operator, body } => {
158            use crate::ast::logic::TemporalOperator;
159            // Set domain hint for temporal lowering
160            ctx.set_domain_hint(ModalDomain::Temporal);
161            let result = match operator {
162                // LTL operators → Kripke world quantification
163                TemporalOperator::Always => {
164                    // G(φ) → ∀w'(Accessible_Temporal(w, w') → φ(w'))
165                    lower_temporal_unary(
166                        body, ctx, expr_arena, term_arena, interner,
167                        "Accessible_Temporal", true,
168                    )
169                }
170                TemporalOperator::Eventually => {
171                    // F(φ) → ∃w'(Reachable_Temporal(w, w') ∧ φ(w'))
172                    lower_temporal_unary(
173                        body, ctx, expr_arena, term_arena, interner,
174                        "Reachable_Temporal", false,
175                    )
176                }
177                TemporalOperator::BoundedEventually(n) => {
178                    // F≤n(φ) — preserve bound for SVA synthesis (##[0:n])
179                    let lowered_body = lower_expr(body, ctx, expr_arena, term_arena, interner);
180                    expr_arena.alloc(LogicExpr::Temporal {
181                        operator: TemporalOperator::BoundedEventually(*n),
182                        body: lowered_body,
183                    })
184                }
185                TemporalOperator::Next => {
186                    // X(φ) → ∀w'(Next_Temporal(w, w') → φ(w'))
187                    ctx.tick_clock();
188                    lower_temporal_unary(
189                        body, ctx, expr_arena, term_arena, interner,
190                        "Next_Temporal", true,
191                    )
192                }
193                // Priorian tense operators — pass through (linguistic, not hardware)
194                TemporalOperator::Past | TemporalOperator::Future => {
195                    let new_body = lower_expr(body, ctx, expr_arena, term_arena, interner);
196                    expr_arena.alloc(LogicExpr::Temporal {
197                        operator: *operator,
198                        body: new_body,
199                    })
200                }
201            };
202            ctx.clear_domain_hint();
203            result
204        }
205
206        LogicExpr::TemporalBinary { operator, left, right } => {
207            // φ U ψ → ψ(w) ∨ (φ(w) ∧ ∃w'(Next_Temporal(w,w') ∧ (φ U ψ)(w')))
208            // For now: lower both operands with world threading
209            let new_left = lower_expr(left, ctx, expr_arena, term_arena, interner);
210            let new_right = lower_expr(right, ctx, expr_arena, term_arena, interner);
211
212            // Generate Next_Temporal accessibility for the recursive step
213            let source_world = ctx.current_world;
214            let target_world = ctx.fresh_world(interner);
215            let next_name = interner.intern("Next_Temporal");
216            let accessibility = expr_arena.alloc(LogicExpr::Predicate {
217                name: next_name,
218                args: term_arena.alloc_slice([
219                    Term::Variable(source_world),
220                    Term::Variable(target_world),
221                ]),
222                world: None,
223            });
224
225            // Build: right(w) ∨ (left(w) ∧ ∃w'(Next_Temporal(w,w') ∧ ...))
226            let recursive_body = expr_arena.alloc(LogicExpr::BinaryOp {
227                left: accessibility,
228                op: crate::token::TokenType::And,
229                right: expr_arena.alloc(LogicExpr::TemporalBinary {
230                    operator: *operator,
231                    left: new_left,
232                    right: new_right,
233                }),
234            });
235            let existential = expr_arena.alloc(LogicExpr::Quantifier {
236                kind: QuantifierKind::Existential,
237                variable: target_world,
238                body: recursive_body,
239                island_id: 0,
240            });
241            let left_and_next = expr_arena.alloc(LogicExpr::BinaryOp {
242                left: new_left,
243                op: crate::token::TokenType::And,
244                right: existential,
245            });
246            expr_arena.alloc(LogicExpr::BinaryOp {
247                left: new_right,
248                op: crate::token::TokenType::Or,
249                right: left_and_next,
250            })
251        }
252
253        LogicExpr::Aspectual { operator, body } => {
254            let new_body = lower_expr(body, ctx, expr_arena, term_arena, interner);
255            expr_arena.alloc(LogicExpr::Aspectual {
256                operator: *operator,
257                body: new_body,
258            })
259        }
260
261        LogicExpr::Voice { operator, body } => {
262            let new_body = lower_expr(body, ctx, expr_arena, term_arena, interner);
263            expr_arena.alloc(LogicExpr::Voice {
264                operator: *operator,
265                body: new_body,
266            })
267        }
268
269        LogicExpr::Lambda { variable, body } => {
270            let new_body = lower_expr(body, ctx, expr_arena, term_arena, interner);
271            expr_arena.alloc(LogicExpr::Lambda {
272                variable: *variable,
273                body: new_body,
274            })
275        }
276
277        LogicExpr::App { function, argument } => {
278            let new_function = lower_expr(function, ctx, expr_arena, term_arena, interner);
279            let new_argument = lower_expr(argument, ctx, expr_arena, term_arena, interner);
280            expr_arena.alloc(LogicExpr::App {
281                function: new_function,
282                argument: new_argument,
283            })
284        }
285
286        LogicExpr::Intensional { operator, content } => {
287            let new_content = lower_expr(content, ctx, expr_arena, term_arena, interner);
288            expr_arena.alloc(LogicExpr::Intensional {
289                operator: *operator,
290                content: new_content,
291            })
292        }
293
294        LogicExpr::Control { verb, subject, object, infinitive } => {
295            let new_infinitive = lower_expr(infinitive, ctx, expr_arena, term_arena, interner);
296            expr_arena.alloc(LogicExpr::Control {
297                verb: *verb,
298                subject: *subject,
299                object: *object,
300                infinitive: new_infinitive,
301            })
302        }
303
304        LogicExpr::Scopal { operator, body } => {
305            let new_body = lower_expr(body, ctx, expr_arena, term_arena, interner);
306            expr_arena.alloc(LogicExpr::Scopal {
307                operator: *operator,
308                body: new_body,
309            })
310        }
311
312        LogicExpr::Question { wh_variable, body } => {
313            let new_body = lower_expr(body, ctx, expr_arena, term_arena, interner);
314            expr_arena.alloc(LogicExpr::Question {
315                wh_variable: *wh_variable,
316                body: new_body,
317            })
318        }
319
320        LogicExpr::YesNoQuestion { body } => {
321            let new_body = lower_expr(body, ctx, expr_arena, term_arena, interner);
322            expr_arena.alloc(LogicExpr::YesNoQuestion { body: new_body })
323        }
324
325        LogicExpr::Focus { kind, focused, scope } => {
326            let new_scope = lower_expr(scope, ctx, expr_arena, term_arena, interner);
327            expr_arena.alloc(LogicExpr::Focus {
328                kind: *kind,
329                focused: *focused,
330                scope: new_scope,
331            })
332        }
333
334        LogicExpr::Distributive { predicate } => {
335            let new_predicate = lower_expr(predicate, ctx, expr_arena, term_arena, interner);
336            expr_arena.alloc(LogicExpr::Distributive {
337                predicate: new_predicate,
338            })
339        }
340
341        LogicExpr::Counterfactual { antecedent, consequent } => {
342            let new_antecedent = lower_expr(antecedent, ctx, expr_arena, term_arena, interner);
343            let new_consequent = lower_expr(consequent, ctx, expr_arena, term_arena, interner);
344            expr_arena.alloc(LogicExpr::Counterfactual {
345                antecedent: new_antecedent,
346                consequent: new_consequent,
347            })
348        }
349
350        LogicExpr::Event { predicate, adverbs } => {
351            let new_predicate = lower_expr(predicate, ctx, expr_arena, term_arena, interner);
352            expr_arena.alloc(LogicExpr::Event {
353                predicate: new_predicate,
354                adverbs: *adverbs,
355            })
356        }
357
358        LogicExpr::Imperative { action } => {
359            let new_action = lower_expr(action, ctx, expr_arena, term_arena, interner);
360            expr_arena.alloc(LogicExpr::Imperative { action: new_action })
361        }
362
363        LogicExpr::Causal { effect, cause } => {
364            let new_effect = lower_expr(effect, ctx, expr_arena, term_arena, interner);
365            let new_cause = lower_expr(cause, ctx, expr_arena, term_arena, interner);
366            expr_arena.alloc(LogicExpr::Causal {
367                effect: new_effect,
368                cause: new_cause,
369            })
370        }
371
372        LogicExpr::Presupposition { assertion, presupposition } => {
373            let new_assertion = lower_expr(assertion, ctx, expr_arena, term_arena, interner);
374            let new_presupposition = lower_expr(presupposition, ctx, expr_arena, term_arena, interner);
375            expr_arena.alloc(LogicExpr::Presupposition {
376                assertion: new_assertion,
377                presupposition: new_presupposition,
378            })
379        }
380
381        LogicExpr::TemporalAnchor { anchor, body } => {
382            let new_body = lower_expr(body, ctx, expr_arena, term_arena, interner);
383            expr_arena.alloc(LogicExpr::TemporalAnchor {
384                anchor: *anchor,
385                body: new_body,
386            })
387        }
388
389        LogicExpr::GroupQuantifier { group_var, count, member_var, restriction, body } => {
390            let new_restriction = lower_expr(restriction, ctx, expr_arena, term_arena, interner);
391            let new_body = lower_expr(body, ctx, expr_arena, term_arena, interner);
392            expr_arena.alloc(LogicExpr::GroupQuantifier {
393                group_var: *group_var,
394                count: *count,
395                member_var: *member_var,
396                restriction: new_restriction,
397                body: new_body,
398            })
399        }
400
401        // Leaf nodes that don't need transformation
402        LogicExpr::Identity { .. }
403        | LogicExpr::Metaphor { .. }
404        | LogicExpr::Categorical(_)
405        | LogicExpr::Relation(_)
406        | LogicExpr::Atom(_)
407        | LogicExpr::Superlative { .. }
408        | LogicExpr::Comparative { .. }
409        | LogicExpr::SpeechAct { .. } => expr,
410    }
411}
412
413fn lower_modal<'a>(
414    vector: &ModalVector,
415    operand: &'a LogicExpr<'a>,
416    ctx: &mut KripkeContext,
417    expr_arena: &'a Arena<LogicExpr<'a>>,
418    term_arena: &'a Arena<Term<'a>>,
419    interner: &mut Interner,
420) -> &'a LogicExpr<'a> {
421    let source_world = ctx.current_world;
422    let target_world = ctx.fresh_world(interner);
423
424    // Lower operand with new current world
425    let old_world = ctx.current_world;
426    ctx.current_world = target_world;
427    let lowered_operand = lower_expr(operand, ctx, expr_arena, term_arena, interner);
428    ctx.current_world = old_world;
429
430    // Create accessibility predicate based on modal domain
431    let access_name = match vector.domain {
432        ModalDomain::Alethic => interner.intern("Accessible_Alethic"),
433        ModalDomain::Deontic => interner.intern("Accessible_Deontic"),
434        ModalDomain::Temporal => interner.intern("Accessible_Temporal"),
435    };
436
437    let accessibility = expr_arena.alloc(LogicExpr::Predicate {
438        name: access_name,
439        args: term_arena.alloc_slice([
440            Term::Variable(source_world),
441            Term::Variable(target_world),
442        ]),
443        world: None, // Accessibility predicate is a meta-level relation
444    });
445
446    if vector.force > 0.5 {
447        // Necessity (Box): ForAll w'(Accessible(w, w') -> P(w'))
448        let implication = expr_arena.alloc(LogicExpr::BinaryOp {
449            left: accessibility,
450            op: TokenType::Implies,
451            right: lowered_operand,
452        });
453        expr_arena.alloc(LogicExpr::Quantifier {
454            kind: QuantifierKind::Universal,
455            variable: target_world,
456            body: implication,
457            island_id: 0,
458        })
459    } else {
460        // Possibility (Diamond): Exists w'(Accessible(w, w') ∧ P(w'))
461        let conjunction = expr_arena.alloc(LogicExpr::BinaryOp {
462            left: accessibility,
463            op: TokenType::And,
464            right: lowered_operand,
465        });
466        expr_arena.alloc(LogicExpr::Quantifier {
467            kind: QuantifierKind::Existential,
468            variable: target_world,
469            body: conjunction,
470            island_id: 0,
471        })
472    }
473}
474
475/// Lower a unary LTL temporal operator into Kripke world quantification.
476///
477/// `is_universal`: true for G/X (∀w'), false for F (∃w')
478fn lower_temporal_unary<'a>(
479    body: &'a LogicExpr<'a>,
480    ctx: &mut KripkeContext,
481    expr_arena: &'a Arena<LogicExpr<'a>>,
482    term_arena: &'a Arena<Term<'a>>,
483    interner: &mut Interner,
484    predicate_name: &str,
485    is_universal: bool,
486) -> &'a LogicExpr<'a> {
487    let source_world = ctx.current_world;
488    let target_world = ctx.fresh_world(interner);
489
490    // Lower body with new current world
491    let old_world = ctx.current_world;
492    ctx.current_world = target_world;
493    let lowered_body = lower_expr(body, ctx, expr_arena, term_arena, interner);
494    ctx.current_world = old_world;
495
496    // Create temporal accessibility predicate
497    let access_name = interner.intern(predicate_name);
498    let accessibility = expr_arena.alloc(LogicExpr::Predicate {
499        name: access_name,
500        args: term_arena.alloc_slice([
501            Term::Variable(source_world),
502            Term::Variable(target_world),
503        ]),
504        world: None,
505    });
506
507    if is_universal {
508        // G/X: ∀w'(Accessible_Temporal(w, w') → φ(w'))
509        let implication = expr_arena.alloc(LogicExpr::BinaryOp {
510            left: accessibility,
511            op: TokenType::Implies,
512            right: lowered_body,
513        });
514        expr_arena.alloc(LogicExpr::Quantifier {
515            kind: QuantifierKind::Universal,
516            variable: target_world,
517            body: implication,
518            island_id: 0,
519        })
520    } else {
521        // F: ∃w'(Reachable_Temporal(w, w') ∧ φ(w'))
522        let conjunction = expr_arena.alloc(LogicExpr::BinaryOp {
523            left: accessibility,
524            op: TokenType::And,
525            right: lowered_body,
526        });
527        expr_arena.alloc(LogicExpr::Quantifier {
528            kind: QuantifierKind::Existential,
529            variable: target_world,
530            body: conjunction,
531            island_id: 0,
532        })
533    }
534}