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
510impl TLExpr {
511    pub fn pred(name: impl Into<String>, args: Vec<Term>) -> Self {
512        TLExpr::Pred {
513            name: name.into(),
514            args,
515        }
516    }
517
518    pub fn and(left: TLExpr, right: TLExpr) -> Self {
519        TLExpr::And(Box::new(left), Box::new(right))
520    }
521
522    pub fn or(left: TLExpr, right: TLExpr) -> Self {
523        TLExpr::Or(Box::new(left), Box::new(right))
524    }
525
526    pub fn negate(expr: TLExpr) -> Self {
527        TLExpr::Not(Box::new(expr))
528    }
529
530    pub fn exists(var: impl Into<String>, domain: impl Into<String>, body: TLExpr) -> Self {
531        TLExpr::Exists {
532            var: var.into(),
533            domain: domain.into(),
534            body: Box::new(body),
535        }
536    }
537
538    pub fn forall(var: impl Into<String>, domain: impl Into<String>, body: TLExpr) -> Self {
539        TLExpr::ForAll {
540            var: var.into(),
541            domain: domain.into(),
542            body: Box::new(body),
543        }
544    }
545
546    pub fn imply(premise: TLExpr, conclusion: TLExpr) -> Self {
547        TLExpr::Imply(Box::new(premise), Box::new(conclusion))
548    }
549
550    pub fn score(expr: TLExpr) -> Self {
551        TLExpr::Score(Box::new(expr))
552    }
553
554    // Arithmetic operations
555    #[allow(clippy::should_implement_trait)]
556    pub fn add(left: TLExpr, right: TLExpr) -> Self {
557        TLExpr::Add(Box::new(left), Box::new(right))
558    }
559
560    #[allow(clippy::should_implement_trait)]
561    pub fn sub(left: TLExpr, right: TLExpr) -> Self {
562        TLExpr::Sub(Box::new(left), Box::new(right))
563    }
564
565    #[allow(clippy::should_implement_trait)]
566    pub fn mul(left: TLExpr, right: TLExpr) -> Self {
567        TLExpr::Mul(Box::new(left), Box::new(right))
568    }
569
570    #[allow(clippy::should_implement_trait)]
571    pub fn div(left: TLExpr, right: TLExpr) -> Self {
572        TLExpr::Div(Box::new(left), Box::new(right))
573    }
574
575    pub fn pow(left: TLExpr, right: TLExpr) -> Self {
576        TLExpr::Pow(Box::new(left), Box::new(right))
577    }
578
579    pub fn modulo(left: TLExpr, right: TLExpr) -> Self {
580        TLExpr::Mod(Box::new(left), Box::new(right))
581    }
582
583    pub fn min(left: TLExpr, right: TLExpr) -> Self {
584        TLExpr::Min(Box::new(left), Box::new(right))
585    }
586
587    pub fn max(left: TLExpr, right: TLExpr) -> Self {
588        TLExpr::Max(Box::new(left), Box::new(right))
589    }
590
591    // Unary mathematical operations
592    pub fn abs(expr: TLExpr) -> Self {
593        TLExpr::Abs(Box::new(expr))
594    }
595
596    pub fn floor(expr: TLExpr) -> Self {
597        TLExpr::Floor(Box::new(expr))
598    }
599
600    pub fn ceil(expr: TLExpr) -> Self {
601        TLExpr::Ceil(Box::new(expr))
602    }
603
604    pub fn round(expr: TLExpr) -> Self {
605        TLExpr::Round(Box::new(expr))
606    }
607
608    pub fn sqrt(expr: TLExpr) -> Self {
609        TLExpr::Sqrt(Box::new(expr))
610    }
611
612    pub fn exp(expr: TLExpr) -> Self {
613        TLExpr::Exp(Box::new(expr))
614    }
615
616    pub fn log(expr: TLExpr) -> Self {
617        TLExpr::Log(Box::new(expr))
618    }
619
620    pub fn sin(expr: TLExpr) -> Self {
621        TLExpr::Sin(Box::new(expr))
622    }
623
624    pub fn cos(expr: TLExpr) -> Self {
625        TLExpr::Cos(Box::new(expr))
626    }
627
628    pub fn tan(expr: TLExpr) -> Self {
629        TLExpr::Tan(Box::new(expr))
630    }
631
632    // Comparison operations
633    pub fn eq(left: TLExpr, right: TLExpr) -> Self {
634        TLExpr::Eq(Box::new(left), Box::new(right))
635    }
636
637    pub fn lt(left: TLExpr, right: TLExpr) -> Self {
638        TLExpr::Lt(Box::new(left), Box::new(right))
639    }
640
641    pub fn gt(left: TLExpr, right: TLExpr) -> Self {
642        TLExpr::Gt(Box::new(left), Box::new(right))
643    }
644
645    pub fn lte(left: TLExpr, right: TLExpr) -> Self {
646        TLExpr::Lte(Box::new(left), Box::new(right))
647    }
648
649    pub fn gte(left: TLExpr, right: TLExpr) -> Self {
650        TLExpr::Gte(Box::new(left), Box::new(right))
651    }
652
653    // Conditional
654    pub fn if_then_else(condition: TLExpr, then_branch: TLExpr, else_branch: TLExpr) -> Self {
655        TLExpr::IfThenElse {
656            condition: Box::new(condition),
657            then_branch: Box::new(then_branch),
658            else_branch: Box::new(else_branch),
659        }
660    }
661
662    // Constant
663    pub fn constant(value: f64) -> Self {
664        TLExpr::Constant(value)
665    }
666
667    // Aggregation operations
668    pub fn aggregate(
669        op: AggregateOp,
670        var: impl Into<String>,
671        domain: impl Into<String>,
672        body: TLExpr,
673    ) -> Self {
674        TLExpr::Aggregate {
675            op,
676            var: var.into(),
677            domain: domain.into(),
678            body: Box::new(body),
679            group_by: None,
680        }
681    }
682
683    pub fn aggregate_with_group_by(
684        op: AggregateOp,
685        var: impl Into<String>,
686        domain: impl Into<String>,
687        body: TLExpr,
688        group_by: Vec<String>,
689    ) -> Self {
690        TLExpr::Aggregate {
691            op,
692            var: var.into(),
693            domain: domain.into(),
694            body: Box::new(body),
695            group_by: Some(group_by),
696        }
697    }
698
699    pub fn count(var: impl Into<String>, domain: impl Into<String>, body: TLExpr) -> Self {
700        Self::aggregate(AggregateOp::Count, var, domain, body)
701    }
702
703    pub fn sum(var: impl Into<String>, domain: impl Into<String>, body: TLExpr) -> Self {
704        Self::aggregate(AggregateOp::Sum, var, domain, body)
705    }
706
707    pub fn average(var: impl Into<String>, domain: impl Into<String>, body: TLExpr) -> Self {
708        Self::aggregate(AggregateOp::Average, var, domain, body)
709    }
710
711    pub fn max_agg(var: impl Into<String>, domain: impl Into<String>, body: TLExpr) -> Self {
712        Self::aggregate(AggregateOp::Max, var, domain, body)
713    }
714
715    pub fn min_agg(var: impl Into<String>, domain: impl Into<String>, body: TLExpr) -> Self {
716        Self::aggregate(AggregateOp::Min, var, domain, body)
717    }
718
719    pub fn product(var: impl Into<String>, domain: impl Into<String>, body: TLExpr) -> Self {
720        Self::aggregate(AggregateOp::Product, var, domain, body)
721    }
722
723    // Let binding
724    pub fn let_binding(var: impl Into<String>, value: TLExpr, body: TLExpr) -> Self {
725        TLExpr::Let {
726            var: var.into(),
727            value: Box::new(value),
728            body: Box::new(body),
729        }
730    }
731
732    // Modal logic operators
733    /// Create a Box (necessity) operator.
734    ///
735    /// □P: "P is necessarily true" - holds in all possible worlds/states
736    pub fn modal_box(expr: TLExpr) -> Self {
737        TLExpr::Box(Box::new(expr))
738    }
739
740    /// Create a Diamond (possibility) operator.
741    ///
742    /// ◇P: "P is possibly true" - holds in at least one possible world/state
743    pub fn modal_diamond(expr: TLExpr) -> Self {
744        TLExpr::Diamond(Box::new(expr))
745    }
746
747    // Temporal logic operators
748    /// Create a Next operator.
749    ///
750    /// XP: "P is true in the next state"
751    pub fn next(expr: TLExpr) -> Self {
752        TLExpr::Next(Box::new(expr))
753    }
754
755    /// Create an Eventually operator.
756    ///
757    /// FP: "P will eventually be true" - true in some future state
758    pub fn eventually(expr: TLExpr) -> Self {
759        TLExpr::Eventually(Box::new(expr))
760    }
761
762    /// Create an Always operator.
763    ///
764    /// GP: "P is always true" - true in all future states
765    pub fn always(expr: TLExpr) -> Self {
766        TLExpr::Always(Box::new(expr))
767    }
768
769    /// Create an Until operator.
770    ///
771    /// P U Q: "P holds until Q becomes true"
772    pub fn until(before: TLExpr, after: TLExpr) -> Self {
773        TLExpr::Until {
774            before: Box::new(before),
775            after: Box::new(after),
776        }
777    }
778
779    // Fuzzy logic builders
780
781    /// Create a T-norm (fuzzy AND) operation with specified semantics.
782    pub fn tnorm(kind: TNormKind, left: TLExpr, right: TLExpr) -> Self {
783        TLExpr::TNorm {
784            kind,
785            left: Box::new(left),
786            right: Box::new(right),
787        }
788    }
789
790    /// Create a minimum T-norm (standard fuzzy AND).
791    pub fn fuzzy_and(left: TLExpr, right: TLExpr) -> Self {
792        Self::tnorm(TNormKind::Minimum, left, right)
793    }
794
795    /// Create a product T-norm (probabilistic AND).
796    pub fn probabilistic_and(left: TLExpr, right: TLExpr) -> Self {
797        Self::tnorm(TNormKind::Product, left, right)
798    }
799
800    /// Create a T-conorm (fuzzy OR) operation with specified semantics.
801    pub fn tconorm(kind: TCoNormKind, left: TLExpr, right: TLExpr) -> Self {
802        TLExpr::TCoNorm {
803            kind,
804            left: Box::new(left),
805            right: Box::new(right),
806        }
807    }
808
809    /// Create a maximum T-conorm (standard fuzzy OR).
810    pub fn fuzzy_or(left: TLExpr, right: TLExpr) -> Self {
811        Self::tconorm(TCoNormKind::Maximum, left, right)
812    }
813
814    /// Create a probabilistic sum T-conorm.
815    pub fn probabilistic_or(left: TLExpr, right: TLExpr) -> Self {
816        Self::tconorm(TCoNormKind::ProbabilisticSum, left, right)
817    }
818
819    /// Create a fuzzy negation with specified semantics.
820    pub fn fuzzy_not(kind: FuzzyNegationKind, expr: TLExpr) -> Self {
821        TLExpr::FuzzyNot {
822            kind,
823            expr: Box::new(expr),
824        }
825    }
826
827    /// Create a standard fuzzy negation (1 - x).
828    pub fn standard_fuzzy_not(expr: TLExpr) -> Self {
829        Self::fuzzy_not(FuzzyNegationKind::Standard, expr)
830    }
831
832    /// Create a fuzzy implication with specified semantics.
833    pub fn fuzzy_imply(kind: FuzzyImplicationKind, premise: TLExpr, conclusion: TLExpr) -> Self {
834        TLExpr::FuzzyImplication {
835            kind,
836            premise: Box::new(premise),
837            conclusion: Box::new(conclusion),
838        }
839    }
840
841    // Probabilistic operators builders
842
843    /// Create a soft existential quantifier with temperature parameter.
844    ///
845    /// # Arguments
846    /// * `var` - Variable name
847    /// * `domain` - Domain name
848    /// * `body` - Expression body
849    /// * `temperature` - Temperature parameter (default 1.0). Lower = harder max.
850    pub fn soft_exists(
851        var: impl Into<String>,
852        domain: impl Into<String>,
853        body: TLExpr,
854        temperature: f64,
855    ) -> Self {
856        TLExpr::SoftExists {
857            var: var.into(),
858            domain: domain.into(),
859            body: Box::new(body),
860            temperature,
861        }
862    }
863
864    /// Create a soft universal quantifier with temperature parameter.
865    ///
866    /// # Arguments
867    /// * `var` - Variable name
868    /// * `domain` - Domain name
869    /// * `body` - Expression body
870    /// * `temperature` - Temperature parameter (default 1.0). Lower = harder min.
871    pub fn soft_forall(
872        var: impl Into<String>,
873        domain: impl Into<String>,
874        body: TLExpr,
875        temperature: f64,
876    ) -> Self {
877        TLExpr::SoftForAll {
878            var: var.into(),
879            domain: domain.into(),
880            body: Box::new(body),
881            temperature,
882        }
883    }
884
885    /// Create a weighted rule with confidence/probability.
886    ///
887    /// # Arguments
888    /// * `weight` - Weight/confidence (typically in \[0,1\] for probabilities)
889    /// * `rule` - The rule expression
890    pub fn weighted_rule(weight: f64, rule: TLExpr) -> Self {
891        TLExpr::WeightedRule {
892            weight,
893            rule: Box::new(rule),
894        }
895    }
896
897    /// Create a probabilistic choice between alternatives.
898    ///
899    /// # Arguments
900    /// * `alternatives` - Vector of (probability, expression) pairs. Should sum to 1.0.
901    pub fn probabilistic_choice(alternatives: Vec<(f64, TLExpr)>) -> Self {
902        TLExpr::ProbabilisticChoice { alternatives }
903    }
904
905    // Extended temporal logic builders
906
907    /// Create a Release operator (R).
908    ///
909    /// P R Q: "Q holds until and including when P becomes true"
910    pub fn release(released: TLExpr, releaser: TLExpr) -> Self {
911        TLExpr::Release {
912            released: Box::new(released),
913            releaser: Box::new(releaser),
914        }
915    }
916
917    /// Create a Weak Until operator (W).
918    ///
919    /// P W Q: "P holds until Q, but Q may never hold"
920    pub fn weak_until(before: TLExpr, after: TLExpr) -> Self {
921        TLExpr::WeakUntil {
922            before: Box::new(before),
923            after: Box::new(after),
924        }
925    }
926
927    /// Create a Strong Release operator (M).
928    ///
929    /// P M Q: Dual of weak until
930    pub fn strong_release(released: TLExpr, releaser: TLExpr) -> Self {
931        TLExpr::StrongRelease {
932            released: Box::new(released),
933            releaser: Box::new(releaser),
934        }
935    }
936
937    // ====== ALPHA.3 ENHANCEMENT BUILDERS ======
938
939    // Higher-order logic builders
940
941    /// Create a lambda abstraction: λvar. body
942    ///
943    /// # Arguments
944    /// * `var` - The parameter name
945    /// * `var_type` - Optional type annotation for the parameter
946    /// * `body` - The function body
947    pub fn lambda(var: impl Into<String>, var_type: Option<String>, body: TLExpr) -> Self {
948        TLExpr::Lambda {
949            var: var.into(),
950            var_type,
951            body: Box::new(body),
952        }
953    }
954
955    /// Create a lambda abstraction without type annotation.
956    pub fn lambda_untyped(var: impl Into<String>, body: TLExpr) -> Self {
957        Self::lambda(var, None, body)
958    }
959
960    /// Create a function application: f(arg)
961    ///
962    /// # Arguments
963    /// * `function` - The function to apply
964    /// * `argument` - The argument to apply to the function
965    pub fn apply(function: TLExpr, argument: TLExpr) -> Self {
966        TLExpr::Apply {
967            function: Box::new(function),
968            argument: Box::new(argument),
969        }
970    }
971
972    // Set theory builders
973
974    /// Create a set membership test: elem ∈ set
975    pub fn set_membership(element: TLExpr, set: TLExpr) -> Self {
976        TLExpr::SetMembership {
977            element: Box::new(element),
978            set: Box::new(set),
979        }
980    }
981
982    /// Create a set union: A ∪ B
983    pub fn set_union(left: TLExpr, right: TLExpr) -> Self {
984        TLExpr::SetUnion {
985            left: Box::new(left),
986            right: Box::new(right),
987        }
988    }
989
990    /// Create a set intersection: A ∩ B
991    pub fn set_intersection(left: TLExpr, right: TLExpr) -> Self {
992        TLExpr::SetIntersection {
993            left: Box::new(left),
994            right: Box::new(right),
995        }
996    }
997
998    /// Create a set difference: A \ B
999    pub fn set_difference(left: TLExpr, right: TLExpr) -> Self {
1000        TLExpr::SetDifference {
1001            left: Box::new(left),
1002            right: Box::new(right),
1003        }
1004    }
1005
1006    /// Create a set cardinality expression: |S|
1007    pub fn set_cardinality(set: TLExpr) -> Self {
1008        TLExpr::SetCardinality { set: Box::new(set) }
1009    }
1010
1011    /// Create an empty set: ∅
1012    pub fn empty_set() -> Self {
1013        TLExpr::EmptySet
1014    }
1015
1016    /// Create a set comprehension: { var : domain | condition }
1017    pub fn set_comprehension(
1018        var: impl Into<String>,
1019        domain: impl Into<String>,
1020        condition: TLExpr,
1021    ) -> Self {
1022        TLExpr::SetComprehension {
1023            var: var.into(),
1024            domain: domain.into(),
1025            condition: Box::new(condition),
1026        }
1027    }
1028
1029    // Counting quantifier builders
1030
1031    /// Create a counting existential quantifier: ∃≥k x. P(x)
1032    ///
1033    /// "There exist at least k elements satisfying P"
1034    pub fn counting_exists(
1035        var: impl Into<String>,
1036        domain: impl Into<String>,
1037        body: TLExpr,
1038        min_count: usize,
1039    ) -> Self {
1040        TLExpr::CountingExists {
1041            var: var.into(),
1042            domain: domain.into(),
1043            body: Box::new(body),
1044            min_count,
1045        }
1046    }
1047
1048    /// Create a counting universal quantifier: ∀≥k x. P(x)
1049    ///
1050    /// "At least k elements satisfy P"
1051    pub fn counting_forall(
1052        var: impl Into<String>,
1053        domain: impl Into<String>,
1054        body: TLExpr,
1055        min_count: usize,
1056    ) -> Self {
1057        TLExpr::CountingForAll {
1058            var: var.into(),
1059            domain: domain.into(),
1060            body: Box::new(body),
1061            min_count,
1062        }
1063    }
1064
1065    /// Create an exact count quantifier: ∃=k x. P(x)
1066    ///
1067    /// "Exactly k elements satisfy P"
1068    pub fn exact_count(
1069        var: impl Into<String>,
1070        domain: impl Into<String>,
1071        body: TLExpr,
1072        count: usize,
1073    ) -> Self {
1074        TLExpr::ExactCount {
1075            var: var.into(),
1076            domain: domain.into(),
1077            body: Box::new(body),
1078            count,
1079        }
1080    }
1081
1082    /// Create a majority quantifier: Majority x. P(x)
1083    ///
1084    /// "More than half of the elements satisfy P"
1085    pub fn majority(var: impl Into<String>, domain: impl Into<String>, body: TLExpr) -> Self {
1086        TLExpr::Majority {
1087            var: var.into(),
1088            domain: domain.into(),
1089            body: Box::new(body),
1090        }
1091    }
1092
1093    // Fixed-point operator builders
1094
1095    /// Create a least fixed point: μX. F(X)
1096    ///
1097    /// Used for inductive definitions (smallest solution)
1098    pub fn least_fixpoint(var: impl Into<String>, body: TLExpr) -> Self {
1099        TLExpr::LeastFixpoint {
1100            var: var.into(),
1101            body: Box::new(body),
1102        }
1103    }
1104
1105    /// Create a greatest fixed point: νX. F(X)
1106    ///
1107    /// Used for coinductive definitions (largest solution)
1108    pub fn greatest_fixpoint(var: impl Into<String>, body: TLExpr) -> Self {
1109        TLExpr::GreatestFixpoint {
1110            var: var.into(),
1111            body: Box::new(body),
1112        }
1113    }
1114
1115    // Hybrid logic builders
1116
1117    /// Create a nominal (named state): @i
1118    pub fn nominal(name: impl Into<String>) -> Self {
1119        TLExpr::Nominal { name: name.into() }
1120    }
1121
1122    /// Create a satisfaction operator: @i φ
1123    ///
1124    /// "Formula φ is true at the nominal state i"
1125    pub fn at(nominal: impl Into<String>, formula: TLExpr) -> Self {
1126        TLExpr::At {
1127            nominal: nominal.into(),
1128            formula: Box::new(formula),
1129        }
1130    }
1131
1132    /// Create a "somewhere" modality: E φ
1133    ///
1134    /// "φ is true in some reachable state"
1135    pub fn somewhere(formula: TLExpr) -> Self {
1136        TLExpr::Somewhere {
1137            formula: Box::new(formula),
1138        }
1139    }
1140
1141    /// Create an "everywhere" modality: A φ
1142    ///
1143    /// "φ is true in all reachable states"
1144    pub fn everywhere(formula: TLExpr) -> Self {
1145        TLExpr::Everywhere {
1146            formula: Box::new(formula),
1147        }
1148    }
1149
1150    // Constraint programming builders
1151
1152    /// Create an all-different constraint.
1153    ///
1154    /// All variables must have different values.
1155    pub fn all_different(variables: Vec<String>) -> Self {
1156        TLExpr::AllDifferent { variables }
1157    }
1158
1159    /// Create a global cardinality constraint.
1160    ///
1161    /// Each value must occur within specified bounds in the variables.
1162    pub fn global_cardinality(
1163        variables: Vec<String>,
1164        values: Vec<TLExpr>,
1165        min_occurrences: Vec<usize>,
1166        max_occurrences: Vec<usize>,
1167    ) -> Self {
1168        TLExpr::GlobalCardinality {
1169            variables,
1170            values,
1171            min_occurrences,
1172            max_occurrences,
1173        }
1174    }
1175
1176    // Abductive reasoning builders
1177
1178    /// Create an abducible literal.
1179    ///
1180    /// Can be assumed true for explanation with the given cost.
1181    pub fn abducible(name: impl Into<String>, cost: f64) -> Self {
1182        TLExpr::Abducible {
1183            name: name.into(),
1184            cost,
1185        }
1186    }
1187
1188    /// Mark a formula as needing explanation.
1189    pub fn explain(formula: TLExpr) -> Self {
1190        TLExpr::Explain {
1191            formula: Box::new(formula),
1192        }
1193    }
1194
1195    /// Substitute a variable with an expression throughout this formula.
1196    ///
1197    /// This performs capture-avoiding substitution, respecting variable shadowing
1198    /// in quantifiers, lambda abstractions, and let bindings.
1199    ///
1200    /// # Arguments
1201    ///
1202    /// * `var` - The variable name to replace
1203    /// * `value` - The expression to substitute in place of the variable
1204    ///
1205    /// # Example
1206    ///
1207    /// ```rust
1208    /// use tensorlogic_ir::{TLExpr, Term};
1209    ///
1210    /// // P(x) ∧ Q(x)
1211    /// let p = TLExpr::pred("P", vec![Term::var("x")]);
1212    /// let q = TLExpr::pred("Q", vec![Term::var("x")]);
1213    /// let expr = TLExpr::and(p.clone(), q);
1214    ///
1215    /// // Substitute x with y
1216    /// let y = TLExpr::pred("y", vec![]);
1217    /// let result = expr.substitute("x", &y);
1218    /// ```
1219    pub fn substitute(&self, var: &str, value: &TLExpr) -> Self {
1220        optimization::substitution::substitute(self, var, value)
1221    }
1222}