Skip to main content

tensorlogic_ir/expr/
mod.rs

1//! TensorLogic expressions (TLExpr).
2
3pub mod ac_matching;
4pub mod advanced_analysis;
5pub mod advanced_rewriting;
6mod analysis;
7pub mod confluence;
8pub mod defuzzification;
9pub mod distributive_laws;
10mod domain_validation;
11pub mod ltl_ctl_utilities;
12pub mod modal_axioms;
13pub mod modal_equivalences;
14pub mod normal_forms;
15pub mod optimization;
16pub mod optimization_pipeline;
17pub mod probabilistic_reasoning;
18pub mod rewriting;
19pub mod strategy_selector;
20pub mod temporal_equivalences;
21mod validation;
22
23use serde::{Deserialize, Serialize};
24
25use crate::term::Term;
26
27/// Aggregation operation type.
28#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
29pub enum AggregateOp {
30    /// Count the number of elements
31    Count,
32    /// Sum of all elements
33    Sum,
34    /// Average (mean) of elements
35    Average,
36    /// Maximum element
37    Max,
38    /// Minimum element
39    Min,
40    /// Product of all elements
41    Product,
42    /// Any (existential - true if any element is true)
43    Any,
44    /// All (universal - true if all elements are true)
45    All,
46}
47
48/// T-norm (triangular norm) kinds for fuzzy AND operations.
49/// A t-norm is a binary operation T: \[0,1\] × \[0,1\] → \[0,1\] that is:
50/// - Commutative: T(a,b) = T(b,a)
51/// - Associative: T(a,T(b,c)) = T(T(a,b),c)
52/// - Monotonic: If a ≤ b then T(a,c) ≤ T(b,c)
53/// - Has 1 as identity: T(a,1) = a
54#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
55pub enum TNormKind {
56    /// Minimum t-norm (Gödel): T(a,b) = min(a,b)
57    /// Standard fuzzy AND, also known as Zadeh t-norm
58    Minimum,
59
60    /// Product t-norm: T(a,b) = a * b
61    /// Probabilistic interpretation of independence
62    Product,
63
64    /// Łukasiewicz t-norm: T(a,b) = max(0, a + b - 1)
65    /// Strong conjunction in Łukasiewicz logic
66    Lukasiewicz,
67
68    /// Drastic t-norm: T(a,b) = { b if a=1, a if b=1, 0 otherwise }
69    /// Most restrictive t-norm
70    Drastic,
71
72    /// Nilpotent minimum: T(a,b) = { min(a,b) if a+b>1, 0 otherwise }
73    NilpotentMinimum,
74
75    /// Hamacher product: T(a,b) = ab/(a+b-ab) for a,b > 0
76    /// Generalizes product t-norm
77    Hamacher,
78}
79
80/// T-conorm (triangular conorm) kinds for fuzzy OR operations.
81/// A t-conorm is the dual of a t-norm: S(a,b) = 1 - T(1-a, 1-b)
82#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
83pub enum TCoNormKind {
84    /// Maximum t-conorm (Gödel): S(a,b) = max(a,b)
85    /// Standard fuzzy OR, dual of minimum t-norm
86    Maximum,
87
88    /// Probabilistic sum: S(a,b) = a + b - a*b
89    /// Dual of product t-norm
90    ProbabilisticSum,
91
92    /// Łukasiewicz t-conorm: S(a,b) = min(1, a + b)
93    /// Bounded sum, dual of Łukasiewicz t-norm
94    BoundedSum,
95
96    /// Drastic t-conorm: S(a,b) = { b if a=0, a if b=0, 1 otherwise }
97    /// Most permissive t-conorm, dual of drastic t-norm
98    Drastic,
99
100    /// Nilpotent maximum: S(a,b) = { max(a,b) if a+b<1, 1 otherwise }
101    /// Dual of nilpotent minimum
102    NilpotentMaximum,
103
104    /// Hamacher sum: dual of Hamacher product
105    Hamacher,
106}
107
108/// Fuzzy negation kinds.
109#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
110pub enum FuzzyNegationKind {
111    /// Standard fuzzy negation: N(a) = 1 - a
112    Standard,
113
114    /// Sugeno negation: N(a) = (1-a)/(1+λa) for λ > -1
115    /// Parameterized family of negations
116    Sugeno {
117        /// Lambda parameter, must be > -1
118        lambda: i32, // Using i32 to maintain Eq trait; actual value is lambda/100
119    },
120
121    /// Yager negation: N(a) = (1 - a^w)^(1/w) for w > 0
122    Yager {
123        /// w parameter stored as integer (actual = w/10)
124        w: u32,
125    },
126}
127
128/// Fuzzy implication operator kinds.
129#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
130pub enum FuzzyImplicationKind {
131    /// Gödel implication: I(a,b) = { 1 if a≤b, b otherwise }
132    Godel,
133
134    /// Łukasiewicz implication: I(a,b) = min(1, 1-a+b)
135    Lukasiewicz,
136
137    /// Reichenbach implication: I(a,b) = 1 - a + ab
138    Reichenbach,
139
140    /// Kleene-Dienes implication: I(a,b) = max(1-a, b)
141    KleeneDienes,
142
143    /// Rescher implication: I(a,b) = { 1 if a≤b, 0 otherwise }
144    Rescher,
145
146    /// Goguen implication: I(a,b) = { 1 if a≤b, b/a otherwise }
147    Goguen,
148}
149
150#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
151pub enum TLExpr {
152    Pred {
153        name: String,
154        args: Vec<Term>,
155    },
156    And(Box<TLExpr>, Box<TLExpr>),
157    Or(Box<TLExpr>, Box<TLExpr>),
158    Not(Box<TLExpr>),
159    Exists {
160        var: String,
161        domain: String,
162        body: Box<TLExpr>,
163    },
164    ForAll {
165        var: String,
166        domain: String,
167        body: Box<TLExpr>,
168    },
169    Imply(Box<TLExpr>, Box<TLExpr>),
170    Score(Box<TLExpr>),
171
172    // Arithmetic operations
173    Add(Box<TLExpr>, Box<TLExpr>),
174    Sub(Box<TLExpr>, Box<TLExpr>),
175    Mul(Box<TLExpr>, Box<TLExpr>),
176    Div(Box<TLExpr>, Box<TLExpr>),
177    Pow(Box<TLExpr>, Box<TLExpr>),
178    Mod(Box<TLExpr>, Box<TLExpr>),
179    Min(Box<TLExpr>, Box<TLExpr>),
180    Max(Box<TLExpr>, Box<TLExpr>),
181
182    // Unary mathematical operations
183    Abs(Box<TLExpr>),
184    Floor(Box<TLExpr>),
185    Ceil(Box<TLExpr>),
186    Round(Box<TLExpr>),
187    Sqrt(Box<TLExpr>),
188    Exp(Box<TLExpr>),
189    Log(Box<TLExpr>),
190    Sin(Box<TLExpr>),
191    Cos(Box<TLExpr>),
192    Tan(Box<TLExpr>),
193
194    // Comparison operations
195    Eq(Box<TLExpr>, Box<TLExpr>),
196    Lt(Box<TLExpr>, Box<TLExpr>),
197    Gt(Box<TLExpr>, Box<TLExpr>),
198    Lte(Box<TLExpr>, Box<TLExpr>),
199    Gte(Box<TLExpr>, Box<TLExpr>),
200
201    // Conditional expression
202    IfThenElse {
203        condition: Box<TLExpr>,
204        then_branch: Box<TLExpr>,
205        else_branch: Box<TLExpr>,
206    },
207
208    // Numeric literal
209    Constant(f64),
210
211    // Aggregation operations (re-enabled with explicit output tracking support)
212    Aggregate {
213        op: AggregateOp,
214        var: String,
215        domain: String,
216        body: Box<TLExpr>,
217        /// Optional group-by variables
218        group_by: Option<Vec<String>>,
219    },
220
221    // Let binding for local variable definitions
222    Let {
223        var: String,
224        value: Box<TLExpr>,
225        body: Box<TLExpr>,
226    },
227
228    // Modal logic operators
229    /// Necessity operator (□, "box"): something is necessarily true
230    /// In all possible worlds or states, the expression holds
231    Box(Box<TLExpr>),
232
233    /// Possibility operator (◇, "diamond"): something is possibly true
234    /// In at least one possible world or state, the expression holds
235    /// Related to Box by: ◇P = ¬□¬P
236    Diamond(Box<TLExpr>),
237
238    // Temporal logic operators
239    /// Next operator (X): true in the next state
240    Next(Box<TLExpr>),
241
242    /// Eventually operator (F): true in some future state
243    Eventually(Box<TLExpr>),
244
245    /// Always/Globally operator (G): true in all future states
246    Always(Box<TLExpr>),
247
248    /// Until operator (U): first expression holds until second becomes true
249    Until {
250        before: Box<TLExpr>,
251        after: Box<TLExpr>,
252    },
253
254    // Fuzzy logic operators
255    /// T-norm (fuzzy AND) with specified semantics
256    TNorm {
257        kind: TNormKind,
258        left: Box<TLExpr>,
259        right: Box<TLExpr>,
260    },
261
262    /// T-conorm (fuzzy OR) with specified semantics
263    TCoNorm {
264        kind: TCoNormKind,
265        left: Box<TLExpr>,
266        right: Box<TLExpr>,
267    },
268
269    /// Fuzzy negation with specified semantics
270    FuzzyNot {
271        kind: FuzzyNegationKind,
272        expr: Box<TLExpr>,
273    },
274
275    /// Fuzzy implication operator
276    FuzzyImplication {
277        kind: FuzzyImplicationKind,
278        premise: Box<TLExpr>,
279        conclusion: Box<TLExpr>,
280    },
281
282    // Probabilistic operators
283    /// Soft existential quantifier with temperature parameter
284    /// Temperature controls how "soft" the quantifier is:
285    /// - Low temperature (→0): approaches hard max (standard exists)
286    /// - High temperature: smoother aggregation (log-sum-exp)
287    SoftExists {
288        var: String,
289        domain: String,
290        body: Box<TLExpr>,
291        /// Temperature parameter (default: 1.0)
292        temperature: f64,
293    },
294
295    /// Soft universal quantifier with temperature parameter
296    /// Temperature controls how "soft" the quantifier is:
297    /// - Low temperature (→0): approaches hard min (standard forall)
298    /// - High temperature: smoother aggregation
299    SoftForAll {
300        var: String,
301        domain: String,
302        body: Box<TLExpr>,
303        /// Temperature parameter (default: 1.0)
304        temperature: f64,
305    },
306
307    /// Weighted rule with confidence/probability
308    /// Used in probabilistic logic programming
309    WeightedRule {
310        weight: f64,
311        rule: Box<TLExpr>,
312    },
313
314    /// Probabilistic choice between alternatives with given probabilities
315    /// Probabilities should sum to 1.0
316    ProbabilisticChoice {
317        alternatives: Vec<(f64, TLExpr)>, // (probability, expression) pairs
318    },
319
320    // Extended temporal logic (LTL properties)
321    /// Release operator (R): dual of Until
322    /// P R Q means Q holds until and including when P becomes true
323    Release {
324        released: Box<TLExpr>,
325        releaser: Box<TLExpr>,
326    },
327
328    /// Weak Until (W): P W Q means P holds until Q, but Q may never hold
329    WeakUntil {
330        before: Box<TLExpr>,
331        after: Box<TLExpr>,
332    },
333
334    /// Strong Release (M): dual of weak until
335    StrongRelease {
336        released: Box<TLExpr>,
337        releaser: Box<TLExpr>,
338    },
339
340    // ====== ALPHA.3 ENHANCEMENTS ======
341
342    // Higher-order logic
343    /// Lambda abstraction: λvar. body
344    /// Creates a function that binds var in body
345    Lambda {
346        var: String,
347        /// Optional type annotation for the parameter
348        var_type: Option<String>,
349        body: Box<TLExpr>,
350    },
351
352    /// Function application: Apply(f, arg) represents f(arg)
353    Apply {
354        function: Box<TLExpr>,
355        argument: Box<TLExpr>,
356    },
357
358    // Set theory operations
359    /// Set membership: elem ∈ set
360    SetMembership {
361        element: Box<TLExpr>,
362        set: Box<TLExpr>,
363    },
364
365    /// Set union: A ∪ B
366    SetUnion {
367        left: Box<TLExpr>,
368        right: Box<TLExpr>,
369    },
370
371    /// Set intersection: A ∩ B
372    SetIntersection {
373        left: Box<TLExpr>,
374        right: Box<TLExpr>,
375    },
376
377    /// Set difference: A \ B
378    SetDifference {
379        left: Box<TLExpr>,
380        right: Box<TLExpr>,
381    },
382
383    /// Set cardinality: |S|
384    SetCardinality {
385        set: Box<TLExpr>,
386    },
387
388    /// Empty set: ∅
389    EmptySet,
390
391    /// Set comprehension: { var : domain | condition }
392    SetComprehension {
393        var: String,
394        domain: String,
395        condition: Box<TLExpr>,
396    },
397
398    // Counting quantifiers
399    /// Counting existential quantifier: ∃≥k x. P(x)
400    /// "There exist at least k elements satisfying P"
401    CountingExists {
402        var: String,
403        domain: String,
404        body: Box<TLExpr>,
405        /// Minimum count threshold
406        min_count: usize,
407    },
408
409    /// Counting universal quantifier: ∀≥k x. P(x)
410    /// "At least k elements satisfy P"
411    CountingForAll {
412        var: String,
413        domain: String,
414        body: Box<TLExpr>,
415        /// Minimum count threshold
416        min_count: usize,
417    },
418
419    /// Exact count quantifier: ∃=k x. P(x)
420    /// "Exactly k elements satisfy P"
421    ExactCount {
422        var: String,
423        domain: String,
424        body: Box<TLExpr>,
425        /// Exact count required
426        count: usize,
427    },
428
429    /// Majority quantifier: Majority x. P(x)
430    /// "More than half of the elements satisfy P"
431    Majority {
432        var: String,
433        domain: String,
434        body: Box<TLExpr>,
435    },
436
437    // Fixed-point operators
438    /// Least fixed point (mu): μX. F(X)
439    /// Used for inductive definitions
440    LeastFixpoint {
441        /// Variable representing the fixed point
442        var: String,
443        /// Function body that references var
444        body: Box<TLExpr>,
445    },
446
447    /// Greatest fixed point (nu): νX. F(X)
448    /// Used for coinductive definitions
449    GreatestFixpoint {
450        /// Variable representing the fixed point
451        var: String,
452        /// Function body that references var
453        body: Box<TLExpr>,
454    },
455
456    // Hybrid logic
457    /// Nominal (named state/world): @i
458    /// Represents a specific named state in a model
459    Nominal {
460        name: String,
461    },
462
463    /// Satisfaction operator: @i φ
464    /// "Formula φ is true at the nominal state i"
465    At {
466        nominal: String,
467        formula: Box<TLExpr>,
468    },
469
470    /// Universal modality: E φ
471    /// "φ is true in some state reachable from here"
472    Somewhere {
473        formula: Box<TLExpr>,
474    },
475
476    /// Universal modality (dual): A φ
477    /// "φ is true in all reachable states"
478    Everywhere {
479        formula: Box<TLExpr>,
480    },
481
482    // Constraint programming
483    /// All-different constraint: all variables must have different values
484    AllDifferent {
485        variables: Vec<String>,
486    },
487
488    /// Global cardinality constraint
489    /// Each value in values must occur at least min and at most max times in variables
490    GlobalCardinality {
491        variables: Vec<String>,
492        values: Vec<TLExpr>,
493        min_occurrences: Vec<usize>,
494        max_occurrences: Vec<usize>,
495    },
496
497    // Abductive reasoning
498    /// Abducible literal: can be assumed true for explanation
499    Abducible {
500        name: String,
501        cost: f64, // Cost of assuming this literal
502    },
503
504    /// Explanation marker: indicates this part needs explanation
505    Explain {
506        formula: Box<TLExpr>,
507    },
508
509    // Pattern matching
510    /// Symbol literal — a named identifier comparable by equality.
511    /// Used as the RHS of `Eq` when lowering `MatchPattern::ConstSymbol`.
512    SymbolLiteral(String),
513
514    /// Pattern-matching expression: `match scrutinee { arm* }`.
515    /// The last arm MUST use `MatchPattern::Wildcard` (enforced by validation).
516    Match {
517        scrutinee: Box<TLExpr>,
518        arms: Vec<(crate::pattern::MatchPattern, Box<TLExpr>)>,
519    },
520}
521
522impl TLExpr {
523    pub fn pred(name: impl Into<String>, args: Vec<Term>) -> Self {
524        TLExpr::Pred {
525            name: name.into(),
526            args,
527        }
528    }
529
530    pub fn and(left: TLExpr, right: TLExpr) -> Self {
531        TLExpr::And(Box::new(left), Box::new(right))
532    }
533
534    pub fn or(left: TLExpr, right: TLExpr) -> Self {
535        TLExpr::Or(Box::new(left), Box::new(right))
536    }
537
538    pub fn negate(expr: TLExpr) -> Self {
539        TLExpr::Not(Box::new(expr))
540    }
541
542    pub fn exists(var: impl Into<String>, domain: impl Into<String>, body: TLExpr) -> Self {
543        TLExpr::Exists {
544            var: var.into(),
545            domain: domain.into(),
546            body: Box::new(body),
547        }
548    }
549
550    pub fn forall(var: impl Into<String>, domain: impl Into<String>, body: TLExpr) -> Self {
551        TLExpr::ForAll {
552            var: var.into(),
553            domain: domain.into(),
554            body: Box::new(body),
555        }
556    }
557
558    pub fn imply(premise: TLExpr, conclusion: TLExpr) -> Self {
559        TLExpr::Imply(Box::new(premise), Box::new(conclusion))
560    }
561
562    pub fn score(expr: TLExpr) -> Self {
563        TLExpr::Score(Box::new(expr))
564    }
565
566    // Arithmetic operations
567    #[allow(clippy::should_implement_trait)]
568    pub fn add(left: TLExpr, right: TLExpr) -> Self {
569        TLExpr::Add(Box::new(left), Box::new(right))
570    }
571
572    #[allow(clippy::should_implement_trait)]
573    pub fn sub(left: TLExpr, right: TLExpr) -> Self {
574        TLExpr::Sub(Box::new(left), Box::new(right))
575    }
576
577    #[allow(clippy::should_implement_trait)]
578    pub fn mul(left: TLExpr, right: TLExpr) -> Self {
579        TLExpr::Mul(Box::new(left), Box::new(right))
580    }
581
582    #[allow(clippy::should_implement_trait)]
583    pub fn div(left: TLExpr, right: TLExpr) -> Self {
584        TLExpr::Div(Box::new(left), Box::new(right))
585    }
586
587    pub fn pow(left: TLExpr, right: TLExpr) -> Self {
588        TLExpr::Pow(Box::new(left), Box::new(right))
589    }
590
591    pub fn modulo(left: TLExpr, right: TLExpr) -> Self {
592        TLExpr::Mod(Box::new(left), Box::new(right))
593    }
594
595    pub fn min(left: TLExpr, right: TLExpr) -> Self {
596        TLExpr::Min(Box::new(left), Box::new(right))
597    }
598
599    pub fn max(left: TLExpr, right: TLExpr) -> Self {
600        TLExpr::Max(Box::new(left), Box::new(right))
601    }
602
603    // Unary mathematical operations
604    pub fn abs(expr: TLExpr) -> Self {
605        TLExpr::Abs(Box::new(expr))
606    }
607
608    pub fn floor(expr: TLExpr) -> Self {
609        TLExpr::Floor(Box::new(expr))
610    }
611
612    pub fn ceil(expr: TLExpr) -> Self {
613        TLExpr::Ceil(Box::new(expr))
614    }
615
616    pub fn round(expr: TLExpr) -> Self {
617        TLExpr::Round(Box::new(expr))
618    }
619
620    pub fn sqrt(expr: TLExpr) -> Self {
621        TLExpr::Sqrt(Box::new(expr))
622    }
623
624    pub fn exp(expr: TLExpr) -> Self {
625        TLExpr::Exp(Box::new(expr))
626    }
627
628    pub fn log(expr: TLExpr) -> Self {
629        TLExpr::Log(Box::new(expr))
630    }
631
632    pub fn sin(expr: TLExpr) -> Self {
633        TLExpr::Sin(Box::new(expr))
634    }
635
636    pub fn cos(expr: TLExpr) -> Self {
637        TLExpr::Cos(Box::new(expr))
638    }
639
640    pub fn tan(expr: TLExpr) -> Self {
641        TLExpr::Tan(Box::new(expr))
642    }
643
644    // Comparison operations
645    pub fn eq(left: TLExpr, right: TLExpr) -> Self {
646        TLExpr::Eq(Box::new(left), Box::new(right))
647    }
648
649    pub fn lt(left: TLExpr, right: TLExpr) -> Self {
650        TLExpr::Lt(Box::new(left), Box::new(right))
651    }
652
653    pub fn gt(left: TLExpr, right: TLExpr) -> Self {
654        TLExpr::Gt(Box::new(left), Box::new(right))
655    }
656
657    pub fn lte(left: TLExpr, right: TLExpr) -> Self {
658        TLExpr::Lte(Box::new(left), Box::new(right))
659    }
660
661    pub fn gte(left: TLExpr, right: TLExpr) -> Self {
662        TLExpr::Gte(Box::new(left), Box::new(right))
663    }
664
665    // Conditional
666    pub fn if_then_else(condition: TLExpr, then_branch: TLExpr, else_branch: TLExpr) -> Self {
667        TLExpr::IfThenElse {
668            condition: Box::new(condition),
669            then_branch: Box::new(then_branch),
670            else_branch: Box::new(else_branch),
671        }
672    }
673
674    // Constant
675    pub fn constant(value: f64) -> Self {
676        TLExpr::Constant(value)
677    }
678
679    // Aggregation operations
680    pub fn aggregate(
681        op: AggregateOp,
682        var: impl Into<String>,
683        domain: impl Into<String>,
684        body: TLExpr,
685    ) -> Self {
686        TLExpr::Aggregate {
687            op,
688            var: var.into(),
689            domain: domain.into(),
690            body: Box::new(body),
691            group_by: None,
692        }
693    }
694
695    pub fn aggregate_with_group_by(
696        op: AggregateOp,
697        var: impl Into<String>,
698        domain: impl Into<String>,
699        body: TLExpr,
700        group_by: Vec<String>,
701    ) -> Self {
702        TLExpr::Aggregate {
703            op,
704            var: var.into(),
705            domain: domain.into(),
706            body: Box::new(body),
707            group_by: Some(group_by),
708        }
709    }
710
711    pub fn count(var: impl Into<String>, domain: impl Into<String>, body: TLExpr) -> Self {
712        Self::aggregate(AggregateOp::Count, var, domain, body)
713    }
714
715    pub fn sum(var: impl Into<String>, domain: impl Into<String>, body: TLExpr) -> Self {
716        Self::aggregate(AggregateOp::Sum, var, domain, body)
717    }
718
719    pub fn average(var: impl Into<String>, domain: impl Into<String>, body: TLExpr) -> Self {
720        Self::aggregate(AggregateOp::Average, var, domain, body)
721    }
722
723    pub fn max_agg(var: impl Into<String>, domain: impl Into<String>, body: TLExpr) -> Self {
724        Self::aggregate(AggregateOp::Max, var, domain, body)
725    }
726
727    pub fn min_agg(var: impl Into<String>, domain: impl Into<String>, body: TLExpr) -> Self {
728        Self::aggregate(AggregateOp::Min, var, domain, body)
729    }
730
731    pub fn product(var: impl Into<String>, domain: impl Into<String>, body: TLExpr) -> Self {
732        Self::aggregate(AggregateOp::Product, var, domain, body)
733    }
734
735    // Let binding
736    pub fn let_binding(var: impl Into<String>, value: TLExpr, body: TLExpr) -> Self {
737        TLExpr::Let {
738            var: var.into(),
739            value: Box::new(value),
740            body: Box::new(body),
741        }
742    }
743
744    // Modal logic operators
745    /// Create a Box (necessity) operator.
746    ///
747    /// □P: "P is necessarily true" - holds in all possible worlds/states
748    pub fn modal_box(expr: TLExpr) -> Self {
749        TLExpr::Box(Box::new(expr))
750    }
751
752    /// Create a Diamond (possibility) operator.
753    ///
754    /// ◇P: "P is possibly true" - holds in at least one possible world/state
755    pub fn modal_diamond(expr: TLExpr) -> Self {
756        TLExpr::Diamond(Box::new(expr))
757    }
758
759    // Temporal logic operators
760    /// Create a Next operator.
761    ///
762    /// XP: "P is true in the next state"
763    pub fn next(expr: TLExpr) -> Self {
764        TLExpr::Next(Box::new(expr))
765    }
766
767    /// Create an Eventually operator.
768    ///
769    /// FP: "P will eventually be true" - true in some future state
770    pub fn eventually(expr: TLExpr) -> Self {
771        TLExpr::Eventually(Box::new(expr))
772    }
773
774    /// Create an Always operator.
775    ///
776    /// GP: "P is always true" - true in all future states
777    pub fn always(expr: TLExpr) -> Self {
778        TLExpr::Always(Box::new(expr))
779    }
780
781    /// Create an Until operator.
782    ///
783    /// P U Q: "P holds until Q becomes true"
784    pub fn until(before: TLExpr, after: TLExpr) -> Self {
785        TLExpr::Until {
786            before: Box::new(before),
787            after: Box::new(after),
788        }
789    }
790
791    // Fuzzy logic builders
792
793    /// Create a T-norm (fuzzy AND) operation with specified semantics.
794    pub fn tnorm(kind: TNormKind, left: TLExpr, right: TLExpr) -> Self {
795        TLExpr::TNorm {
796            kind,
797            left: Box::new(left),
798            right: Box::new(right),
799        }
800    }
801
802    /// Create a minimum T-norm (standard fuzzy AND).
803    pub fn fuzzy_and(left: TLExpr, right: TLExpr) -> Self {
804        Self::tnorm(TNormKind::Minimum, left, right)
805    }
806
807    /// Create a product T-norm (probabilistic AND).
808    pub fn probabilistic_and(left: TLExpr, right: TLExpr) -> Self {
809        Self::tnorm(TNormKind::Product, left, right)
810    }
811
812    /// Create a T-conorm (fuzzy OR) operation with specified semantics.
813    pub fn tconorm(kind: TCoNormKind, left: TLExpr, right: TLExpr) -> Self {
814        TLExpr::TCoNorm {
815            kind,
816            left: Box::new(left),
817            right: Box::new(right),
818        }
819    }
820
821    /// Create a maximum T-conorm (standard fuzzy OR).
822    pub fn fuzzy_or(left: TLExpr, right: TLExpr) -> Self {
823        Self::tconorm(TCoNormKind::Maximum, left, right)
824    }
825
826    /// Create a probabilistic sum T-conorm.
827    pub fn probabilistic_or(left: TLExpr, right: TLExpr) -> Self {
828        Self::tconorm(TCoNormKind::ProbabilisticSum, left, right)
829    }
830
831    /// Create a fuzzy negation with specified semantics.
832    pub fn fuzzy_not(kind: FuzzyNegationKind, expr: TLExpr) -> Self {
833        TLExpr::FuzzyNot {
834            kind,
835            expr: Box::new(expr),
836        }
837    }
838
839    /// Create a standard fuzzy negation (1 - x).
840    pub fn standard_fuzzy_not(expr: TLExpr) -> Self {
841        Self::fuzzy_not(FuzzyNegationKind::Standard, expr)
842    }
843
844    /// Create a fuzzy implication with specified semantics.
845    pub fn fuzzy_imply(kind: FuzzyImplicationKind, premise: TLExpr, conclusion: TLExpr) -> Self {
846        TLExpr::FuzzyImplication {
847            kind,
848            premise: Box::new(premise),
849            conclusion: Box::new(conclusion),
850        }
851    }
852
853    // Probabilistic operators builders
854
855    /// Create a soft existential quantifier with temperature parameter.
856    ///
857    /// # Arguments
858    /// * `var` - Variable name
859    /// * `domain` - Domain name
860    /// * `body` - Expression body
861    /// * `temperature` - Temperature parameter (default 1.0). Lower = harder max.
862    pub fn soft_exists(
863        var: impl Into<String>,
864        domain: impl Into<String>,
865        body: TLExpr,
866        temperature: f64,
867    ) -> Self {
868        TLExpr::SoftExists {
869            var: var.into(),
870            domain: domain.into(),
871            body: Box::new(body),
872            temperature,
873        }
874    }
875
876    /// Create a soft universal quantifier with temperature parameter.
877    ///
878    /// # Arguments
879    /// * `var` - Variable name
880    /// * `domain` - Domain name
881    /// * `body` - Expression body
882    /// * `temperature` - Temperature parameter (default 1.0). Lower = harder min.
883    pub fn soft_forall(
884        var: impl Into<String>,
885        domain: impl Into<String>,
886        body: TLExpr,
887        temperature: f64,
888    ) -> Self {
889        TLExpr::SoftForAll {
890            var: var.into(),
891            domain: domain.into(),
892            body: Box::new(body),
893            temperature,
894        }
895    }
896
897    /// Create a weighted rule with confidence/probability.
898    ///
899    /// # Arguments
900    /// * `weight` - Weight/confidence (typically in \[0,1\] for probabilities)
901    /// * `rule` - The rule expression
902    pub fn weighted_rule(weight: f64, rule: TLExpr) -> Self {
903        TLExpr::WeightedRule {
904            weight,
905            rule: Box::new(rule),
906        }
907    }
908
909    /// Create a probabilistic choice between alternatives.
910    ///
911    /// # Arguments
912    /// * `alternatives` - Vector of (probability, expression) pairs. Should sum to 1.0.
913    pub fn probabilistic_choice(alternatives: Vec<(f64, TLExpr)>) -> Self {
914        TLExpr::ProbabilisticChoice { alternatives }
915    }
916
917    // Extended temporal logic builders
918
919    /// Create a Release operator (R).
920    ///
921    /// P R Q: "Q holds until and including when P becomes true"
922    pub fn release(released: TLExpr, releaser: TLExpr) -> Self {
923        TLExpr::Release {
924            released: Box::new(released),
925            releaser: Box::new(releaser),
926        }
927    }
928
929    /// Create a Weak Until operator (W).
930    ///
931    /// P W Q: "P holds until Q, but Q may never hold"
932    pub fn weak_until(before: TLExpr, after: TLExpr) -> Self {
933        TLExpr::WeakUntil {
934            before: Box::new(before),
935            after: Box::new(after),
936        }
937    }
938
939    /// Create a Strong Release operator (M).
940    ///
941    /// P M Q: Dual of weak until
942    pub fn strong_release(released: TLExpr, releaser: TLExpr) -> Self {
943        TLExpr::StrongRelease {
944            released: Box::new(released),
945            releaser: Box::new(releaser),
946        }
947    }
948
949    // ====== ALPHA.3 ENHANCEMENT BUILDERS ======
950
951    // Higher-order logic builders
952
953    /// Create a lambda abstraction: λvar. body
954    ///
955    /// # Arguments
956    /// * `var` - The parameter name
957    /// * `var_type` - Optional type annotation for the parameter
958    /// * `body` - The function body
959    pub fn lambda(var: impl Into<String>, var_type: Option<String>, body: TLExpr) -> Self {
960        TLExpr::Lambda {
961            var: var.into(),
962            var_type,
963            body: Box::new(body),
964        }
965    }
966
967    /// Create a lambda abstraction without type annotation.
968    pub fn lambda_untyped(var: impl Into<String>, body: TLExpr) -> Self {
969        Self::lambda(var, None, body)
970    }
971
972    /// Create a function application: f(arg)
973    ///
974    /// # Arguments
975    /// * `function` - The function to apply
976    /// * `argument` - The argument to apply to the function
977    pub fn apply(function: TLExpr, argument: TLExpr) -> Self {
978        TLExpr::Apply {
979            function: Box::new(function),
980            argument: Box::new(argument),
981        }
982    }
983
984    // Set theory builders
985
986    /// Create a set membership test: elem ∈ set
987    pub fn set_membership(element: TLExpr, set: TLExpr) -> Self {
988        TLExpr::SetMembership {
989            element: Box::new(element),
990            set: Box::new(set),
991        }
992    }
993
994    /// Create a set union: A ∪ B
995    pub fn set_union(left: TLExpr, right: TLExpr) -> Self {
996        TLExpr::SetUnion {
997            left: Box::new(left),
998            right: Box::new(right),
999        }
1000    }
1001
1002    /// Create a set intersection: A ∩ B
1003    pub fn set_intersection(left: TLExpr, right: TLExpr) -> Self {
1004        TLExpr::SetIntersection {
1005            left: Box::new(left),
1006            right: Box::new(right),
1007        }
1008    }
1009
1010    /// Create a set difference: A \ B
1011    pub fn set_difference(left: TLExpr, right: TLExpr) -> Self {
1012        TLExpr::SetDifference {
1013            left: Box::new(left),
1014            right: Box::new(right),
1015        }
1016    }
1017
1018    /// Create a set cardinality expression: |S|
1019    pub fn set_cardinality(set: TLExpr) -> Self {
1020        TLExpr::SetCardinality { set: Box::new(set) }
1021    }
1022
1023    /// Create an empty set: ∅
1024    pub fn empty_set() -> Self {
1025        TLExpr::EmptySet
1026    }
1027
1028    /// Create a set comprehension: { var : domain | condition }
1029    pub fn set_comprehension(
1030        var: impl Into<String>,
1031        domain: impl Into<String>,
1032        condition: TLExpr,
1033    ) -> Self {
1034        TLExpr::SetComprehension {
1035            var: var.into(),
1036            domain: domain.into(),
1037            condition: Box::new(condition),
1038        }
1039    }
1040
1041    // Counting quantifier builders
1042
1043    /// Create a counting existential quantifier: ∃≥k x. P(x)
1044    ///
1045    /// "There exist at least k elements satisfying P"
1046    pub fn counting_exists(
1047        var: impl Into<String>,
1048        domain: impl Into<String>,
1049        body: TLExpr,
1050        min_count: usize,
1051    ) -> Self {
1052        TLExpr::CountingExists {
1053            var: var.into(),
1054            domain: domain.into(),
1055            body: Box::new(body),
1056            min_count,
1057        }
1058    }
1059
1060    /// Create a counting universal quantifier: ∀≥k x. P(x)
1061    ///
1062    /// "At least k elements satisfy P"
1063    pub fn counting_forall(
1064        var: impl Into<String>,
1065        domain: impl Into<String>,
1066        body: TLExpr,
1067        min_count: usize,
1068    ) -> Self {
1069        TLExpr::CountingForAll {
1070            var: var.into(),
1071            domain: domain.into(),
1072            body: Box::new(body),
1073            min_count,
1074        }
1075    }
1076
1077    /// Create an exact count quantifier: ∃=k x. P(x)
1078    ///
1079    /// "Exactly k elements satisfy P"
1080    pub fn exact_count(
1081        var: impl Into<String>,
1082        domain: impl Into<String>,
1083        body: TLExpr,
1084        count: usize,
1085    ) -> Self {
1086        TLExpr::ExactCount {
1087            var: var.into(),
1088            domain: domain.into(),
1089            body: Box::new(body),
1090            count,
1091        }
1092    }
1093
1094    /// Create a majority quantifier: Majority x. P(x)
1095    ///
1096    /// "More than half of the elements satisfy P"
1097    pub fn majority(var: impl Into<String>, domain: impl Into<String>, body: TLExpr) -> Self {
1098        TLExpr::Majority {
1099            var: var.into(),
1100            domain: domain.into(),
1101            body: Box::new(body),
1102        }
1103    }
1104
1105    // Fixed-point operator builders
1106
1107    /// Create a least fixed point: μX. F(X)
1108    ///
1109    /// Used for inductive definitions (smallest solution)
1110    pub fn least_fixpoint(var: impl Into<String>, body: TLExpr) -> Self {
1111        TLExpr::LeastFixpoint {
1112            var: var.into(),
1113            body: Box::new(body),
1114        }
1115    }
1116
1117    /// Create a greatest fixed point: νX. F(X)
1118    ///
1119    /// Used for coinductive definitions (largest solution)
1120    pub fn greatest_fixpoint(var: impl Into<String>, body: TLExpr) -> Self {
1121        TLExpr::GreatestFixpoint {
1122            var: var.into(),
1123            body: Box::new(body),
1124        }
1125    }
1126
1127    // Hybrid logic builders
1128
1129    /// Create a nominal (named state): @i
1130    pub fn nominal(name: impl Into<String>) -> Self {
1131        TLExpr::Nominal { name: name.into() }
1132    }
1133
1134    /// Create a satisfaction operator: @i φ
1135    ///
1136    /// "Formula φ is true at the nominal state i"
1137    pub fn at(nominal: impl Into<String>, formula: TLExpr) -> Self {
1138        TLExpr::At {
1139            nominal: nominal.into(),
1140            formula: Box::new(formula),
1141        }
1142    }
1143
1144    /// Create a "somewhere" modality: E φ
1145    ///
1146    /// "φ is true in some reachable state"
1147    pub fn somewhere(formula: TLExpr) -> Self {
1148        TLExpr::Somewhere {
1149            formula: Box::new(formula),
1150        }
1151    }
1152
1153    /// Create an "everywhere" modality: A φ
1154    ///
1155    /// "φ is true in all reachable states"
1156    pub fn everywhere(formula: TLExpr) -> Self {
1157        TLExpr::Everywhere {
1158            formula: Box::new(formula),
1159        }
1160    }
1161
1162    // Constraint programming builders
1163
1164    /// Create an all-different constraint.
1165    ///
1166    /// All variables must have different values.
1167    pub fn all_different(variables: Vec<String>) -> Self {
1168        TLExpr::AllDifferent { variables }
1169    }
1170
1171    /// Create a global cardinality constraint.
1172    ///
1173    /// Each value must occur within specified bounds in the variables.
1174    pub fn global_cardinality(
1175        variables: Vec<String>,
1176        values: Vec<TLExpr>,
1177        min_occurrences: Vec<usize>,
1178        max_occurrences: Vec<usize>,
1179    ) -> Self {
1180        TLExpr::GlobalCardinality {
1181            variables,
1182            values,
1183            min_occurrences,
1184            max_occurrences,
1185        }
1186    }
1187
1188    // Abductive reasoning builders
1189
1190    /// Create an abducible literal.
1191    ///
1192    /// Can be assumed true for explanation with the given cost.
1193    pub fn abducible(name: impl Into<String>, cost: f64) -> Self {
1194        TLExpr::Abducible {
1195            name: name.into(),
1196            cost,
1197        }
1198    }
1199
1200    /// Mark a formula as needing explanation.
1201    pub fn explain(formula: TLExpr) -> Self {
1202        TLExpr::Explain {
1203            formula: Box::new(formula),
1204        }
1205    }
1206
1207    /// Create a symbol literal expression.
1208    pub fn symbol_literal(s: impl Into<String>) -> Self {
1209        TLExpr::SymbolLiteral(s.into())
1210    }
1211
1212    /// Create a pattern-matching expression.
1213    ///
1214    /// # Arguments
1215    /// * `scrutinee` — The expression being matched.
1216    /// * `arms` — List of `(MatchPattern, body)` pairs; last must be `Wildcard`.
1217    pub fn match_expr(
1218        scrutinee: TLExpr,
1219        arms: Vec<(crate::pattern::MatchPattern, TLExpr)>,
1220    ) -> Self {
1221        TLExpr::Match {
1222            scrutinee: Box::new(scrutinee),
1223            arms: arms.into_iter().map(|(p, b)| (p, Box::new(b))).collect(),
1224        }
1225    }
1226
1227    /// Substitute a variable with an expression throughout this formula.
1228    ///
1229    /// This performs capture-avoiding substitution, respecting variable shadowing
1230    /// in quantifiers, lambda abstractions, and let bindings.
1231    ///
1232    /// # Arguments
1233    ///
1234    /// * `var` - The variable name to replace
1235    /// * `value` - The expression to substitute in place of the variable
1236    ///
1237    /// # Example
1238    ///
1239    /// ```rust
1240    /// use tensorlogic_ir::{TLExpr, Term};
1241    ///
1242    /// // P(x) ∧ Q(x)
1243    /// let p = TLExpr::pred("P", vec![Term::var("x")]);
1244    /// let q = TLExpr::pred("Q", vec![Term::var("x")]);
1245    /// let expr = TLExpr::and(p.clone(), q);
1246    ///
1247    /// // Substitute x with y
1248    /// let y = TLExpr::pred("y", vec![]);
1249    /// let result = expr.substitute("x", &y);
1250    /// ```
1251    pub fn substitute(&self, var: &str, value: &TLExpr) -> Self {
1252        optimization::substitution::substitute(self, var, value)
1253    }
1254}