tensorlogic_compiler/passes/
strategy_selection.rs

1//! Automatic strategy selection based on expression analysis.
2//!
3//! This module analyzes TLExpr expressions to recommend optimal compilation
4//! strategies based on expression characteristics, usage context, and optimization goals.
5
6use crate::config::*;
7use tensorlogic_ir::TLExpr;
8
9/// Optimization goals for strategy selection.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum OptimizationGoal {
12    /// Prioritize differentiability for gradient-based training
13    Differentiable,
14    /// Prioritize discrete/Boolean reasoning accuracy
15    DiscreteReasoning,
16    /// Prioritize computational efficiency
17    Performance,
18    /// Balance between differentiability and accuracy
19    Balanced,
20}
21
22/// Expression characteristics that influence strategy selection.
23#[derive(Debug, Clone, Default)]
24pub struct ExpressionProfile {
25    /// Number of AND operations
26    pub and_count: usize,
27    /// Number of OR operations
28    pub or_count: usize,
29    /// Number of NOT operations
30    pub not_count: usize,
31    /// Number of EXISTS quantifiers
32    pub exists_count: usize,
33    /// Number of FORALL quantifiers
34    pub forall_count: usize,
35    /// Number of implications
36    pub implication_count: usize,
37    /// Number of arithmetic operations
38    pub arithmetic_count: usize,
39    /// Number of comparisons
40    pub comparison_count: usize,
41    /// Maximum nesting depth
42    pub max_depth: usize,
43    /// Has nested quantifiers
44    pub has_nested_quantifiers: bool,
45    /// Has negated quantifiers
46    pub has_negated_quantifiers: bool,
47    /// Has arithmetic mixed with logic
48    pub has_mixed_operations: bool,
49}
50
51impl ExpressionProfile {
52    /// Analyze an expression to build its profile.
53    pub fn analyze(expr: &TLExpr) -> Self {
54        let mut profile = Self::default();
55        Self::analyze_recursive(expr, 0, &mut profile);
56        profile
57    }
58
59    fn analyze_recursive(expr: &TLExpr, depth: usize, profile: &mut ExpressionProfile) {
60        profile.max_depth = profile.max_depth.max(depth);
61
62        match expr {
63            TLExpr::And(left, right) => {
64                profile.and_count += 1;
65
66                // Check for mixed operations
67                if Self::contains_arithmetic_op(left) || Self::contains_arithmetic_op(right) {
68                    profile.has_mixed_operations = true;
69                }
70
71                Self::analyze_recursive(left, depth + 1, profile);
72                Self::analyze_recursive(right, depth + 1, profile);
73            }
74            TLExpr::Or(left, right) => {
75                profile.or_count += 1;
76
77                // Check for mixed operations
78                if Self::contains_arithmetic_op(left) || Self::contains_arithmetic_op(right) {
79                    profile.has_mixed_operations = true;
80                }
81
82                Self::analyze_recursive(left, depth + 1, profile);
83                Self::analyze_recursive(right, depth + 1, profile);
84            }
85            TLExpr::Not(inner) => {
86                profile.not_count += 1;
87
88                // Check for negated quantifiers
89                if matches!(**inner, TLExpr::Exists { .. } | TLExpr::ForAll { .. }) {
90                    profile.has_negated_quantifiers = true;
91                }
92
93                Self::analyze_recursive(inner, depth + 1, profile);
94            }
95            TLExpr::Exists { body, .. } => {
96                profile.exists_count += 1;
97
98                // Check for nested quantifiers
99                if Self::contains_quantifier(body) {
100                    profile.has_nested_quantifiers = true;
101                }
102
103                Self::analyze_recursive(body, depth + 1, profile);
104            }
105            TLExpr::ForAll { body, .. } => {
106                profile.forall_count += 1;
107
108                // Check for nested quantifiers
109                if Self::contains_quantifier(body) {
110                    profile.has_nested_quantifiers = true;
111                }
112
113                Self::analyze_recursive(body, depth + 1, profile);
114            }
115            TLExpr::Imply(premise, conclusion) => {
116                profile.implication_count += 1;
117                Self::analyze_recursive(premise, depth + 1, profile);
118                Self::analyze_recursive(conclusion, depth + 1, profile);
119            }
120            TLExpr::Add(left, right)
121            | TLExpr::Sub(left, right)
122            | TLExpr::Mul(left, right)
123            | TLExpr::Div(left, right)
124            | TLExpr::Pow(left, right)
125            | TLExpr::Mod(left, right) => {
126                profile.arithmetic_count += 1;
127
128                // Check for mixed operations
129                if Self::contains_logical_op(left) || Self::contains_logical_op(right) {
130                    profile.has_mixed_operations = true;
131                }
132
133                Self::analyze_recursive(left, depth + 1, profile);
134                Self::analyze_recursive(right, depth + 1, profile);
135            }
136            TLExpr::Min(left, right) | TLExpr::Max(left, right) => {
137                profile.arithmetic_count += 1;
138                Self::analyze_recursive(left, depth + 1, profile);
139                Self::analyze_recursive(right, depth + 1, profile);
140            }
141            TLExpr::Eq(left, right)
142            | TLExpr::Lt(left, right)
143            | TLExpr::Gt(left, right)
144            | TLExpr::Lte(left, right)
145            | TLExpr::Gte(left, right) => {
146                profile.comparison_count += 1;
147                Self::analyze_recursive(left, depth + 1, profile);
148                Self::analyze_recursive(right, depth + 1, profile);
149            }
150            TLExpr::Abs(inner)
151            | TLExpr::Floor(inner)
152            | TLExpr::Ceil(inner)
153            | TLExpr::Round(inner)
154            | TLExpr::Sqrt(inner)
155            | TLExpr::Exp(inner)
156            | TLExpr::Log(inner)
157            | TLExpr::Sin(inner)
158            | TLExpr::Cos(inner)
159            | TLExpr::Tan(inner) => {
160                profile.arithmetic_count += 1;
161                Self::analyze_recursive(inner, depth + 1, profile);
162            }
163            TLExpr::Let {
164                var: _,
165                value,
166                body,
167            } => {
168                Self::analyze_recursive(value, depth + 1, profile);
169                Self::analyze_recursive(body, depth + 1, profile);
170            }
171            TLExpr::IfThenElse {
172                condition,
173                then_branch,
174                else_branch,
175            } => {
176                Self::analyze_recursive(condition, depth + 1, profile);
177                Self::analyze_recursive(then_branch, depth + 1, profile);
178                Self::analyze_recursive(else_branch, depth + 1, profile);
179            }
180            TLExpr::Score(operand) => {
181                Self::analyze_recursive(operand, depth + 1, profile);
182            }
183            TLExpr::Aggregate { body, .. } => {
184                Self::analyze_recursive(body, depth + 1, profile);
185            }
186
187            // Modal/temporal logic operators - not yet implemented, pass through with recursion
188            TLExpr::Box(inner)
189            | TLExpr::Diamond(inner)
190            | TLExpr::Next(inner)
191            | TLExpr::Eventually(inner)
192            | TLExpr::Always(inner) => {
193                Self::analyze_recursive(inner, depth + 1, profile);
194            }
195            TLExpr::Until { before, after } => {
196                Self::analyze_recursive(before, depth + 1, profile);
197                Self::analyze_recursive(after, depth + 1, profile);
198            }
199
200            // Fuzzy logic operators
201            TLExpr::TNorm { left, right, .. } | TLExpr::TCoNorm { left, right, .. } => {
202                Self::analyze_recursive(left, depth + 1, profile);
203                Self::analyze_recursive(right, depth + 1, profile);
204            }
205            TLExpr::FuzzyNot { expr, .. } => {
206                Self::analyze_recursive(expr, depth + 1, profile);
207            }
208            TLExpr::FuzzyImplication {
209                premise,
210                conclusion,
211                ..
212            } => {
213                Self::analyze_recursive(premise, depth + 1, profile);
214                Self::analyze_recursive(conclusion, depth + 1, profile);
215            }
216            TLExpr::SoftExists { body, .. } | TLExpr::SoftForAll { body, .. } => {
217                profile.exists_count += 1;
218                Self::analyze_recursive(body, depth + 1, profile);
219            }
220            TLExpr::WeightedRule { rule, .. } => {
221                Self::analyze_recursive(rule, depth + 1, profile);
222            }
223            TLExpr::ProbabilisticChoice { alternatives } => {
224                for (_prob, expr) in alternatives {
225                    Self::analyze_recursive(expr, depth + 1, profile);
226                }
227            }
228            TLExpr::Release { released, releaser }
229            | TLExpr::WeakUntil {
230                before: released,
231                after: releaser,
232            }
233            | TLExpr::StrongRelease { released, releaser } => {
234                Self::analyze_recursive(released, depth + 1, profile);
235                Self::analyze_recursive(releaser, depth + 1, profile);
236            }
237
238            TLExpr::Pred { .. } | TLExpr::Constant(_) => {
239                // Leaf nodes
240            }
241        }
242    }
243
244    fn contains_quantifier(expr: &TLExpr) -> bool {
245        match expr {
246            TLExpr::Exists { .. } | TLExpr::ForAll { .. } => true,
247            TLExpr::And(l, r)
248            | TLExpr::Or(l, r)
249            | TLExpr::Add(l, r)
250            | TLExpr::Sub(l, r)
251            | TLExpr::Mul(l, r)
252            | TLExpr::Div(l, r)
253            | TLExpr::Pow(l, r)
254            | TLExpr::Mod(l, r)
255            | TLExpr::Min(l, r)
256            | TLExpr::Max(l, r)
257            | TLExpr::Eq(l, r)
258            | TLExpr::Lt(l, r)
259            | TLExpr::Gt(l, r)
260            | TLExpr::Lte(l, r)
261            | TLExpr::Gte(l, r) => Self::contains_quantifier(l) || Self::contains_quantifier(r),
262            TLExpr::Imply(premise, conclusion) => {
263                Self::contains_quantifier(premise) || Self::contains_quantifier(conclusion)
264            }
265            TLExpr::Not(inner)
266            | TLExpr::Score(inner)
267            | TLExpr::Abs(inner)
268            | TLExpr::Floor(inner)
269            | TLExpr::Ceil(inner)
270            | TLExpr::Round(inner)
271            | TLExpr::Sqrt(inner)
272            | TLExpr::Exp(inner)
273            | TLExpr::Log(inner)
274            | TLExpr::Sin(inner)
275            | TLExpr::Cos(inner)
276            | TLExpr::Tan(inner) => Self::contains_quantifier(inner),
277            TLExpr::Let {
278                var: _,
279                value,
280                body,
281            } => Self::contains_quantifier(value) || Self::contains_quantifier(body),
282            TLExpr::IfThenElse {
283                condition,
284                then_branch,
285                else_branch,
286            } => {
287                Self::contains_quantifier(condition)
288                    || Self::contains_quantifier(then_branch)
289                    || Self::contains_quantifier(else_branch)
290            }
291
292            // Modal/temporal logic operators
293            TLExpr::Box(inner)
294            | TLExpr::Diamond(inner)
295            | TLExpr::Next(inner)
296            | TLExpr::Eventually(inner)
297            | TLExpr::Always(inner) => Self::contains_quantifier(inner),
298            TLExpr::Until { before, after } => {
299                Self::contains_quantifier(before) || Self::contains_quantifier(after)
300            }
301
302            // Fuzzy logic operators
303            TLExpr::SoftExists { .. } | TLExpr::SoftForAll { .. } => true,
304            TLExpr::TNorm { left, right, .. } | TLExpr::TCoNorm { left, right, .. } => {
305                Self::contains_quantifier(left) || Self::contains_quantifier(right)
306            }
307            TLExpr::FuzzyNot { expr, .. } => Self::contains_quantifier(expr),
308            TLExpr::FuzzyImplication {
309                premise,
310                conclusion,
311                ..
312            } => Self::contains_quantifier(premise) || Self::contains_quantifier(conclusion),
313            TLExpr::WeightedRule { rule, .. } => Self::contains_quantifier(rule),
314            TLExpr::ProbabilisticChoice { alternatives } => alternatives
315                .iter()
316                .any(|(_prob, expr)| Self::contains_quantifier(expr)),
317            TLExpr::Release { released, releaser }
318            | TLExpr::WeakUntil {
319                before: released,
320                after: releaser,
321            }
322            | TLExpr::StrongRelease { released, releaser } => {
323                Self::contains_quantifier(released) || Self::contains_quantifier(releaser)
324            }
325
326            TLExpr::Pred { .. } | TLExpr::Constant(_) | TLExpr::Aggregate { .. } => false,
327        }
328    }
329
330    fn contains_logical_op(expr: &TLExpr) -> bool {
331        match expr {
332            TLExpr::And(..)
333            | TLExpr::Or(..)
334            | TLExpr::Not(..)
335            | TLExpr::Exists { .. }
336            | TLExpr::ForAll { .. }
337            | TLExpr::Imply(..) => true,
338            TLExpr::Add(l, r)
339            | TLExpr::Sub(l, r)
340            | TLExpr::Mul(l, r)
341            | TLExpr::Div(l, r)
342            | TLExpr::Pow(l, r)
343            | TLExpr::Mod(l, r)
344            | TLExpr::Min(l, r)
345            | TLExpr::Max(l, r)
346            | TLExpr::Eq(l, r)
347            | TLExpr::Lt(l, r)
348            | TLExpr::Gt(l, r)
349            | TLExpr::Lte(l, r)
350            | TLExpr::Gte(l, r) => Self::contains_logical_op(l) || Self::contains_logical_op(r),
351            TLExpr::Score(operand)
352            | TLExpr::Abs(operand)
353            | TLExpr::Floor(operand)
354            | TLExpr::Ceil(operand)
355            | TLExpr::Round(operand)
356            | TLExpr::Sqrt(operand)
357            | TLExpr::Exp(operand)
358            | TLExpr::Log(operand)
359            | TLExpr::Sin(operand)
360            | TLExpr::Cos(operand)
361            | TLExpr::Tan(operand) => Self::contains_logical_op(operand),
362            TLExpr::Let {
363                var: _,
364                value,
365                body,
366            } => Self::contains_logical_op(value) || Self::contains_logical_op(body),
367            TLExpr::IfThenElse {
368                condition,
369                then_branch,
370                else_branch,
371            } => {
372                Self::contains_logical_op(condition)
373                    || Self::contains_logical_op(then_branch)
374                    || Self::contains_logical_op(else_branch)
375            }
376
377            // Modal/temporal logic operators - these are logical operators
378            TLExpr::Box(..)
379            | TLExpr::Diamond(..)
380            | TLExpr::Next(..)
381            | TLExpr::Eventually(..)
382            | TLExpr::Always(..)
383            | TLExpr::Until { .. } => true,
384
385            // Fuzzy logic operators - these are logical operators
386            TLExpr::TNorm { .. }
387            | TLExpr::TCoNorm { .. }
388            | TLExpr::FuzzyNot { .. }
389            | TLExpr::FuzzyImplication { .. }
390            | TLExpr::SoftExists { .. }
391            | TLExpr::SoftForAll { .. }
392            | TLExpr::Release { .. }
393            | TLExpr::WeakUntil { .. }
394            | TLExpr::StrongRelease { .. } => true,
395            TLExpr::WeightedRule { rule, .. } => Self::contains_logical_op(rule),
396            TLExpr::ProbabilisticChoice { alternatives } => alternatives
397                .iter()
398                .any(|(_prob, expr)| Self::contains_logical_op(expr)),
399
400            TLExpr::Pred { .. } | TLExpr::Constant(_) | TLExpr::Aggregate { .. } => false,
401        }
402    }
403
404    fn contains_arithmetic_op(expr: &TLExpr) -> bool {
405        match expr {
406            TLExpr::Add(..)
407            | TLExpr::Sub(..)
408            | TLExpr::Mul(..)
409            | TLExpr::Div(..)
410            | TLExpr::Pow(..)
411            | TLExpr::Mod(..)
412            | TLExpr::Min(..)
413            | TLExpr::Max(..)
414            | TLExpr::Abs(..)
415            | TLExpr::Floor(..)
416            | TLExpr::Ceil(..)
417            | TLExpr::Round(..)
418            | TLExpr::Sqrt(..)
419            | TLExpr::Exp(..)
420            | TLExpr::Log(..)
421            | TLExpr::Sin(..)
422            | TLExpr::Cos(..)
423            | TLExpr::Tan(..) => true,
424            TLExpr::And(l, r)
425            | TLExpr::Or(l, r)
426            | TLExpr::Eq(l, r)
427            | TLExpr::Lt(l, r)
428            | TLExpr::Gt(l, r)
429            | TLExpr::Lte(l, r)
430            | TLExpr::Gte(l, r) => {
431                Self::contains_arithmetic_op(l) || Self::contains_arithmetic_op(r)
432            }
433            TLExpr::Imply(premise, conclusion) => {
434                Self::contains_arithmetic_op(premise) || Self::contains_arithmetic_op(conclusion)
435            }
436            TLExpr::Not(inner) | TLExpr::Score(inner) => Self::contains_arithmetic_op(inner),
437            TLExpr::Exists { body, .. } | TLExpr::ForAll { body, .. } => {
438                Self::contains_arithmetic_op(body)
439            }
440            TLExpr::Let {
441                var: _,
442                value,
443                body,
444            } => Self::contains_arithmetic_op(value) || Self::contains_arithmetic_op(body),
445            TLExpr::IfThenElse {
446                condition,
447                then_branch,
448                else_branch,
449            } => {
450                Self::contains_arithmetic_op(condition)
451                    || Self::contains_arithmetic_op(then_branch)
452                    || Self::contains_arithmetic_op(else_branch)
453            }
454
455            // Modal/temporal logic operators - check recursively
456            TLExpr::Box(inner)
457            | TLExpr::Diamond(inner)
458            | TLExpr::Next(inner)
459            | TLExpr::Eventually(inner)
460            | TLExpr::Always(inner) => Self::contains_arithmetic_op(inner),
461            TLExpr::Until { before, after } => {
462                Self::contains_arithmetic_op(before) || Self::contains_arithmetic_op(after)
463            }
464
465            // Fuzzy logic operators - check recursively
466            TLExpr::TNorm { left, right, .. } | TLExpr::TCoNorm { left, right, .. } => {
467                Self::contains_arithmetic_op(left) || Self::contains_arithmetic_op(right)
468            }
469            TLExpr::FuzzyNot { expr, .. } => Self::contains_arithmetic_op(expr),
470            TLExpr::FuzzyImplication {
471                premise,
472                conclusion,
473                ..
474            } => Self::contains_arithmetic_op(premise) || Self::contains_arithmetic_op(conclusion),
475            TLExpr::SoftExists { body, .. } | TLExpr::SoftForAll { body, .. } => {
476                Self::contains_arithmetic_op(body)
477            }
478            TLExpr::WeightedRule { rule, .. } => Self::contains_arithmetic_op(rule),
479            TLExpr::ProbabilisticChoice { alternatives } => alternatives
480                .iter()
481                .any(|(_prob, expr)| Self::contains_arithmetic_op(expr)),
482            TLExpr::Release { released, releaser }
483            | TLExpr::WeakUntil {
484                before: released,
485                after: releaser,
486            }
487            | TLExpr::StrongRelease { released, releaser } => {
488                Self::contains_arithmetic_op(released) || Self::contains_arithmetic_op(releaser)
489            }
490
491            TLExpr::Pred { .. } | TLExpr::Constant(_) | TLExpr::Aggregate { .. } => false,
492        }
493    }
494
495    /// Calculate a complexity score for the expression.
496    pub fn complexity_score(&self) -> f64 {
497        let op_count = self.and_count
498            + self.or_count
499            + self.not_count
500            + self.exists_count
501            + self.forall_count
502            + self.implication_count
503            + self.arithmetic_count
504            + self.comparison_count;
505
506        let base_score = op_count as f64;
507        let depth_penalty = self.max_depth as f64 * 0.5;
508        let quantifier_penalty = if self.has_nested_quantifiers {
509            10.0
510        } else {
511            0.0
512        };
513        let mixed_penalty = if self.has_mixed_operations { 5.0 } else { 0.0 };
514
515        base_score + depth_penalty + quantifier_penalty + mixed_penalty
516    }
517}
518
519/// Strategy recommendation with confidence score.
520#[derive(Debug, Clone)]
521pub struct StrategyRecommendation {
522    /// Recommended configuration
523    pub config: CompilationConfig,
524    /// Confidence score (0.0 to 1.0)
525    pub confidence: f64,
526    /// Explanation for the recommendation
527    pub rationale: String,
528    /// Alternative configurations (if any)
529    pub alternatives: Vec<(CompilationConfig, f64, String)>,
530}
531
532/// Analyze an expression and recommend compilation strategy.
533///
534/// # Examples
535///
536/// ```
537/// use tensorlogic_compiler::passes::recommend_strategy;
538/// use tensorlogic_compiler::passes::OptimizationGoal;
539/// use tensorlogic_ir::{TLExpr, Term};
540///
541/// // Expression with nested quantifiers
542/// let expr = TLExpr::exists(
543///     "y",
544///     "Person",
545///     TLExpr::forall(
546///         "z",
547///         "Person",
548///         TLExpr::imply(
549///             TLExpr::pred("knows", vec![Term::var("y"), Term::var("z")]),
550///             TLExpr::pred("likes", vec![Term::var("x"), Term::var("z")]),
551///         ),
552///     ),
553/// );
554///
555/// let recommendation = recommend_strategy(&expr, OptimizationGoal::Differentiable);
556/// assert!(recommendation.confidence > 0.5);
557/// println!("Recommendation: {}", recommendation.rationale);
558/// ```
559pub fn recommend_strategy(expr: &TLExpr, goal: OptimizationGoal) -> StrategyRecommendation {
560    let profile = ExpressionProfile::analyze(expr);
561
562    match goal {
563        OptimizationGoal::Differentiable => recommend_for_differentiable(&profile),
564        OptimizationGoal::DiscreteReasoning => recommend_for_discrete(&profile),
565        OptimizationGoal::Performance => recommend_for_performance(&profile),
566        OptimizationGoal::Balanced => recommend_for_balanced(&profile),
567    }
568}
569
570fn recommend_for_differentiable(profile: &ExpressionProfile) -> StrategyRecommendation {
571    let complexity = profile.complexity_score();
572
573    // For expressions with nested quantifiers, use Łukasiewicz for better gradient stability
574    if profile.has_nested_quantifiers {
575        StrategyRecommendation {
576            config: CompilationConfig::fuzzy_lukasiewicz(),
577            confidence: 0.85,
578            rationale: format!(
579                "Łukasiewicz fuzzy logic recommended for nested quantifiers \
580                 (complexity: {:.1}). Fully differentiable with stable gradients.",
581                complexity
582            ),
583            alternatives: vec![(
584                CompilationConfig::soft_differentiable(),
585                0.75,
586                "Standard soft differentiable config (simpler but may have gradient issues)"
587                    .to_string(),
588            )],
589        }
590    }
591    // For expressions with mixed arithmetic and logic
592    else if profile.has_mixed_operations {
593        StrategyRecommendation {
594            config: CompilationConfig::soft_differentiable(),
595            confidence: 0.9,
596            rationale:
597                "Soft differentiable config recommended for mixed arithmetic-logic expressions. \
598                       Provides smooth gradients for both operation types."
599                    .to_string(),
600            alternatives: vec![(
601                CompilationConfig::fuzzy_product(),
602                0.7,
603                "Product fuzzy logic (alternative probabilistic interpretation)".to_string(),
604            )],
605        }
606    }
607    // For simple expressions, standard soft config is best
608    else if complexity < 10.0 {
609        StrategyRecommendation {
610            config: CompilationConfig::soft_differentiable(),
611            confidence: 0.95,
612            rationale: format!(
613                "Standard soft differentiable config recommended for simple expression \
614                 (complexity: {:.1}). Efficient and well-tested.",
615                complexity
616            ),
617            alternatives: vec![],
618        }
619    }
620    // Default to soft differentiable
621    else {
622        StrategyRecommendation {
623            config: CompilationConfig::soft_differentiable(),
624            confidence: 0.8,
625            rationale: format!(
626                "Standard soft differentiable config recommended (complexity: {:.1}).",
627                complexity
628            ),
629            alternatives: vec![(
630                CompilationConfig::fuzzy_lukasiewicz(),
631                0.7,
632                "Łukasiewicz fuzzy logic (alternative for more complex scenarios)".to_string(),
633            )],
634        }
635    }
636}
637
638fn recommend_for_discrete(profile: &ExpressionProfile) -> StrategyRecommendation {
639    // For expressions with negated quantifiers, use Gödel
640    if profile.has_negated_quantifiers {
641        StrategyRecommendation {
642            config: CompilationConfig::fuzzy_godel(),
643            confidence: 0.9,
644            rationale: "Gödel fuzzy logic recommended for negated quantifiers. \
645                       Provides crisp Boolean-like semantics while handling fuzzy values."
646                .to_string(),
647            alternatives: vec![(
648                CompilationConfig::hard_boolean(),
649                0.75,
650                "Pure Boolean logic (strictly discrete but may lose gradient info)".to_string(),
651            )],
652        }
653    }
654    // For simple discrete reasoning
655    else if profile.complexity_score() < 15.0 {
656        StrategyRecommendation {
657            config: CompilationConfig::hard_boolean(),
658            confidence: 0.95,
659            rationale: format!(
660                "Hard Boolean logic recommended for simple discrete reasoning \
661                 (complexity: {:.1}). Provides crisp true/false semantics.",
662                profile.complexity_score()
663            ),
664            alternatives: vec![],
665        }
666    }
667    // For complex discrete reasoning, use Gödel as it's more robust
668    else {
669        StrategyRecommendation {
670            config: CompilationConfig::fuzzy_godel(),
671            confidence: 0.85,
672            rationale: format!(
673                "Gödel fuzzy logic recommended for complex discrete reasoning \
674                 (complexity: {:.1}). More robust than pure Boolean for edge cases.",
675                profile.complexity_score()
676            ),
677            alternatives: vec![(
678                CompilationConfig::hard_boolean(),
679                0.7,
680                "Hard Boolean logic (simpler but less robust)".to_string(),
681            )],
682        }
683    }
684}
685
686fn recommend_for_performance(profile: &ExpressionProfile) -> StrategyRecommendation {
687    let complexity = profile.complexity_score();
688
689    // For simple expressions, use Product fuzzy (efficient)
690    if complexity < 10.0 {
691        StrategyRecommendation {
692            config: CompilationConfig::fuzzy_product(),
693            confidence: 0.9,
694            rationale: format!(
695                "Product fuzzy logic recommended for efficient execution \
696                 (complexity: {:.1}). Uses fast multiplication instead of min/max.",
697                complexity
698            ),
699            alternatives: vec![(
700                CompilationConfig::soft_differentiable(),
701                0.85,
702                "Soft differentiable (similar performance, better gradients)".to_string(),
703            )],
704        }
705    }
706    // For complex expressions with many quantifiers, use probabilistic
707    else if profile.exists_count + profile.forall_count > 3 {
708        StrategyRecommendation {
709            config: CompilationConfig::probabilistic(),
710            confidence: 0.85,
711            rationale: "Probabilistic config recommended for many quantifiers. \
712                       Uses efficient mean reductions instead of sum/product."
713                .to_string(),
714            alternatives: vec![(
715                CompilationConfig::fuzzy_product(),
716                0.75,
717                "Product fuzzy logic (alternative with product reductions)".to_string(),
718            )],
719        }
720    }
721    // Default to soft differentiable (good balance)
722    else {
723        StrategyRecommendation {
724            config: CompilationConfig::soft_differentiable(),
725            confidence: 0.8,
726            rationale: format!(
727                "Soft differentiable config recommended for balanced performance \
728                 (complexity: {:.1}).",
729                complexity
730            ),
731            alternatives: vec![(
732                CompilationConfig::fuzzy_product(),
733                0.75,
734                "Product fuzzy logic (slight performance edge)".to_string(),
735            )],
736        }
737    }
738}
739
740fn recommend_for_balanced(profile: &ExpressionProfile) -> StrategyRecommendation {
741    let complexity = profile.complexity_score();
742
743    // For very simple expressions, use soft differentiable
744    if complexity < 8.0 {
745        StrategyRecommendation {
746            config: CompilationConfig::soft_differentiable(),
747            confidence: 0.9,
748            rationale: format!(
749                "Soft differentiable config recommended for simple balanced use \
750                 (complexity: {:.1}).",
751                complexity
752            ),
753            alternatives: vec![],
754        }
755    }
756    // For moderately complex, use Łukasiewicz (good balance)
757    else if complexity < 25.0 {
758        StrategyRecommendation {
759            config: CompilationConfig::fuzzy_lukasiewicz(),
760            confidence: 0.85,
761            rationale: format!(
762                "Łukasiewicz fuzzy logic recommended for balanced complexity \
763                 (complexity: {:.1}). Good mix of differentiability and accuracy.",
764                complexity
765            ),
766            alternatives: vec![
767                (
768                    CompilationConfig::soft_differentiable(),
769                    0.75,
770                    "Soft differentiable (simpler, slightly better performance)".to_string(),
771                ),
772                (
773                    CompilationConfig::fuzzy_godel(),
774                    0.7,
775                    "Gödel fuzzy logic (more discrete semantics)".to_string(),
776                ),
777            ],
778        }
779    }
780    // For very complex, use probabilistic (robust)
781    else {
782        StrategyRecommendation {
783            config: CompilationConfig::probabilistic(),
784            confidence: 0.8,
785            rationale: format!(
786                "Probabilistic config recommended for complex balanced reasoning \
787                 (complexity: {:.1}). Robust handling of uncertainty.",
788                complexity
789            ),
790            alternatives: vec![(
791                CompilationConfig::fuzzy_lukasiewicz(),
792                0.75,
793                "Łukasiewicz fuzzy logic (more structured reasoning)".to_string(),
794            )],
795        }
796    }
797}
798
799#[cfg(test)]
800mod tests {
801    use super::*;
802    use tensorlogic_ir::Term;
803
804    #[test]
805    fn test_simple_predicate_profile() {
806        let expr = TLExpr::pred("knows", vec![Term::var("x"), Term::var("y")]);
807        let profile = ExpressionProfile::analyze(&expr);
808
809        assert_eq!(profile.and_count, 0);
810        assert_eq!(profile.exists_count, 0);
811        assert_eq!(profile.max_depth, 0);
812        assert!(!profile.has_nested_quantifiers);
813    }
814
815    #[test]
816    fn test_and_or_profile() {
817        let expr = TLExpr::And(
818            Box::new(TLExpr::pred("p", vec![Term::var("x")])),
819            Box::new(TLExpr::Or(
820                Box::new(TLExpr::pred("q", vec![Term::var("y")])),
821                Box::new(TLExpr::pred("r", vec![Term::var("z")])),
822            )),
823        );
824        let profile = ExpressionProfile::analyze(&expr);
825
826        assert_eq!(profile.and_count, 1);
827        assert_eq!(profile.or_count, 1);
828        assert_eq!(profile.max_depth, 2);
829    }
830
831    #[test]
832    fn test_nested_quantifiers_profile() {
833        let expr = TLExpr::exists(
834            "y",
835            "D",
836            TLExpr::forall(
837                "z",
838                "D",
839                TLExpr::pred("p", vec![Term::var("x"), Term::var("y"), Term::var("z")]),
840            ),
841        );
842        let profile = ExpressionProfile::analyze(&expr);
843
844        assert_eq!(profile.exists_count, 1);
845        assert_eq!(profile.forall_count, 1);
846        assert!(profile.has_nested_quantifiers);
847        assert_eq!(profile.max_depth, 2);
848    }
849
850    #[test]
851    fn test_negated_quantifier_profile() {
852        let expr = TLExpr::Not(Box::new(TLExpr::exists(
853            "y",
854            "D",
855            TLExpr::pred("p", vec![Term::var("x"), Term::var("y")]),
856        )));
857        let profile = ExpressionProfile::analyze(&expr);
858
859        assert_eq!(profile.not_count, 1);
860        assert_eq!(profile.exists_count, 1);
861        assert!(profile.has_negated_quantifiers);
862    }
863
864    #[test]
865    fn test_mixed_operations_profile() {
866        let expr = TLExpr::And(
867            Box::new(TLExpr::pred("p", vec![Term::var("x")])),
868            Box::new(TLExpr::Add(
869                Box::new(TLExpr::Constant(1.0)),
870                Box::new(TLExpr::Constant(2.0)),
871            )),
872        );
873        let profile = ExpressionProfile::analyze(&expr);
874
875        assert_eq!(profile.arithmetic_count, 1);
876        assert!(profile.has_mixed_operations);
877    }
878
879    #[test]
880    fn test_recommend_simple_differentiable() {
881        let expr = TLExpr::And(
882            Box::new(TLExpr::pred("p", vec![Term::var("x")])),
883            Box::new(TLExpr::pred("q", vec![Term::var("y")])),
884        );
885
886        let rec = recommend_strategy(&expr, OptimizationGoal::Differentiable);
887        assert_eq!(rec.config, CompilationConfig::soft_differentiable());
888        assert!(rec.confidence > 0.8);
889    }
890
891    #[test]
892    fn test_recommend_complex_differentiable() {
893        let expr = TLExpr::exists(
894            "y",
895            "D",
896            TLExpr::forall(
897                "z",
898                "D",
899                TLExpr::And(
900                    Box::new(TLExpr::pred("p", vec![Term::var("y")])),
901                    Box::new(TLExpr::pred("q", vec![Term::var("z")])),
902                ),
903            ),
904        );
905
906        let rec = recommend_strategy(&expr, OptimizationGoal::Differentiable);
907        // Should recommend Łukasiewicz for nested quantifiers
908        assert_eq!(rec.config, CompilationConfig::fuzzy_lukasiewicz());
909        assert!(rec.confidence > 0.8);
910    }
911
912    #[test]
913    fn test_recommend_discrete() {
914        let expr = TLExpr::And(
915            Box::new(TLExpr::pred("p", vec![Term::var("x")])),
916            Box::new(TLExpr::pred("q", vec![Term::var("y")])),
917        );
918
919        let rec = recommend_strategy(&expr, OptimizationGoal::DiscreteReasoning);
920        assert_eq!(rec.config, CompilationConfig::hard_boolean());
921        assert!(rec.confidence > 0.9);
922    }
923
924    #[test]
925    fn test_recommend_performance() {
926        let expr = TLExpr::And(
927            Box::new(TLExpr::pred("p", vec![Term::var("x")])),
928            Box::new(TLExpr::pred("q", vec![Term::var("y")])),
929        );
930
931        let rec = recommend_strategy(&expr, OptimizationGoal::Performance);
932        assert_eq!(rec.config, CompilationConfig::fuzzy_product());
933        assert!(rec.confidence > 0.8);
934    }
935
936    #[test]
937    fn test_recommend_balanced() {
938        let expr = TLExpr::And(
939            Box::new(TLExpr::pred("p", vec![Term::var("x")])),
940            Box::new(TLExpr::pred("q", vec![Term::var("y")])),
941        );
942
943        let rec = recommend_strategy(&expr, OptimizationGoal::Balanced);
944        assert_eq!(rec.config, CompilationConfig::soft_differentiable());
945        assert!(rec.confidence > 0.8);
946    }
947
948    #[test]
949    fn test_complexity_score() {
950        // Simple expression
951        let simple = TLExpr::pred("p", vec![Term::var("x")]);
952        let simple_profile = ExpressionProfile::analyze(&simple);
953        let simple_score = simple_profile.complexity_score();
954        assert!(simple_score < 5.0);
955
956        // Complex expression
957        let complex = TLExpr::exists(
958            "y",
959            "D",
960            TLExpr::forall(
961                "z",
962                "D",
963                TLExpr::And(
964                    Box::new(TLExpr::Or(
965                        Box::new(TLExpr::pred("p", vec![Term::var("y")])),
966                        Box::new(TLExpr::pred("q", vec![Term::var("z")])),
967                    )),
968                    Box::new(TLExpr::Not(Box::new(TLExpr::pred(
969                        "r",
970                        vec![Term::var("x")],
971                    )))),
972                ),
973            ),
974        );
975        let complex_profile = ExpressionProfile::analyze(&complex);
976        let complex_score = complex_profile.complexity_score();
977        assert!(complex_score > simple_score);
978        assert!(complex_score > 15.0); // Has nested quantifiers penalty
979    }
980}