Skip to main content

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            // All other expression types (enhancements)
242            _ => {
243                // Leaf or unhandled node
244            }
245        }
246    }
247
248    fn contains_quantifier(expr: &TLExpr) -> bool {
249        match expr {
250            TLExpr::Exists { .. } | TLExpr::ForAll { .. } => true,
251            TLExpr::And(l, r)
252            | TLExpr::Or(l, r)
253            | TLExpr::Add(l, r)
254            | TLExpr::Sub(l, r)
255            | TLExpr::Mul(l, r)
256            | TLExpr::Div(l, r)
257            | TLExpr::Pow(l, r)
258            | TLExpr::Mod(l, r)
259            | TLExpr::Min(l, r)
260            | TLExpr::Max(l, r)
261            | TLExpr::Eq(l, r)
262            | TLExpr::Lt(l, r)
263            | TLExpr::Gt(l, r)
264            | TLExpr::Lte(l, r)
265            | TLExpr::Gte(l, r) => Self::contains_quantifier(l) || Self::contains_quantifier(r),
266            TLExpr::Imply(premise, conclusion) => {
267                Self::contains_quantifier(premise) || Self::contains_quantifier(conclusion)
268            }
269            TLExpr::Not(inner)
270            | TLExpr::Score(inner)
271            | TLExpr::Abs(inner)
272            | TLExpr::Floor(inner)
273            | TLExpr::Ceil(inner)
274            | TLExpr::Round(inner)
275            | TLExpr::Sqrt(inner)
276            | TLExpr::Exp(inner)
277            | TLExpr::Log(inner)
278            | TLExpr::Sin(inner)
279            | TLExpr::Cos(inner)
280            | TLExpr::Tan(inner) => Self::contains_quantifier(inner),
281            TLExpr::Let {
282                var: _,
283                value,
284                body,
285            } => Self::contains_quantifier(value) || Self::contains_quantifier(body),
286            TLExpr::IfThenElse {
287                condition,
288                then_branch,
289                else_branch,
290            } => {
291                Self::contains_quantifier(condition)
292                    || Self::contains_quantifier(then_branch)
293                    || Self::contains_quantifier(else_branch)
294            }
295
296            // Modal/temporal logic operators
297            TLExpr::Box(inner)
298            | TLExpr::Diamond(inner)
299            | TLExpr::Next(inner)
300            | TLExpr::Eventually(inner)
301            | TLExpr::Always(inner) => Self::contains_quantifier(inner),
302            TLExpr::Until { before, after } => {
303                Self::contains_quantifier(before) || Self::contains_quantifier(after)
304            }
305
306            // Fuzzy logic operators
307            TLExpr::SoftExists { .. } | TLExpr::SoftForAll { .. } => true,
308            TLExpr::TNorm { left, right, .. } | TLExpr::TCoNorm { left, right, .. } => {
309                Self::contains_quantifier(left) || Self::contains_quantifier(right)
310            }
311            TLExpr::FuzzyNot { expr, .. } => Self::contains_quantifier(expr),
312            TLExpr::FuzzyImplication {
313                premise,
314                conclusion,
315                ..
316            } => Self::contains_quantifier(premise) || Self::contains_quantifier(conclusion),
317            TLExpr::WeightedRule { rule, .. } => Self::contains_quantifier(rule),
318            TLExpr::ProbabilisticChoice { alternatives } => alternatives
319                .iter()
320                .any(|(_prob, expr)| Self::contains_quantifier(expr)),
321            TLExpr::Release { released, releaser }
322            | TLExpr::WeakUntil {
323                before: released,
324                after: releaser,
325            }
326            | TLExpr::StrongRelease { released, releaser } => {
327                Self::contains_quantifier(released) || Self::contains_quantifier(releaser)
328            }
329
330            TLExpr::Pred { .. } | TLExpr::Constant(_) | TLExpr::Aggregate { .. } => false,
331            // All other expression types (enhancements)
332            _ => false,
333        }
334    }
335
336    fn contains_logical_op(expr: &TLExpr) -> bool {
337        match expr {
338            TLExpr::And(..)
339            | TLExpr::Or(..)
340            | TLExpr::Not(..)
341            | TLExpr::Exists { .. }
342            | TLExpr::ForAll { .. }
343            | TLExpr::Imply(..) => true,
344            TLExpr::Add(l, r)
345            | TLExpr::Sub(l, r)
346            | TLExpr::Mul(l, r)
347            | TLExpr::Div(l, r)
348            | TLExpr::Pow(l, r)
349            | TLExpr::Mod(l, r)
350            | TLExpr::Min(l, r)
351            | TLExpr::Max(l, r)
352            | TLExpr::Eq(l, r)
353            | TLExpr::Lt(l, r)
354            | TLExpr::Gt(l, r)
355            | TLExpr::Lte(l, r)
356            | TLExpr::Gte(l, r) => Self::contains_logical_op(l) || Self::contains_logical_op(r),
357            TLExpr::Score(operand)
358            | TLExpr::Abs(operand)
359            | TLExpr::Floor(operand)
360            | TLExpr::Ceil(operand)
361            | TLExpr::Round(operand)
362            | TLExpr::Sqrt(operand)
363            | TLExpr::Exp(operand)
364            | TLExpr::Log(operand)
365            | TLExpr::Sin(operand)
366            | TLExpr::Cos(operand)
367            | TLExpr::Tan(operand) => Self::contains_logical_op(operand),
368            TLExpr::Let {
369                var: _,
370                value,
371                body,
372            } => Self::contains_logical_op(value) || Self::contains_logical_op(body),
373            TLExpr::IfThenElse {
374                condition,
375                then_branch,
376                else_branch,
377            } => {
378                Self::contains_logical_op(condition)
379                    || Self::contains_logical_op(then_branch)
380                    || Self::contains_logical_op(else_branch)
381            }
382
383            // Modal/temporal logic operators - these are logical operators
384            TLExpr::Box(..)
385            | TLExpr::Diamond(..)
386            | TLExpr::Next(..)
387            | TLExpr::Eventually(..)
388            | TLExpr::Always(..)
389            | TLExpr::Until { .. } => true,
390
391            // Fuzzy logic operators - these are logical operators
392            TLExpr::TNorm { .. }
393            | TLExpr::TCoNorm { .. }
394            | TLExpr::FuzzyNot { .. }
395            | TLExpr::FuzzyImplication { .. }
396            | TLExpr::SoftExists { .. }
397            | TLExpr::SoftForAll { .. }
398            | TLExpr::Release { .. }
399            | TLExpr::WeakUntil { .. }
400            | TLExpr::StrongRelease { .. } => true,
401            TLExpr::WeightedRule { rule, .. } => Self::contains_logical_op(rule),
402            TLExpr::ProbabilisticChoice { alternatives } => alternatives
403                .iter()
404                .any(|(_prob, expr)| Self::contains_logical_op(expr)),
405
406            TLExpr::Pred { .. } | TLExpr::Constant(_) | TLExpr::Aggregate { .. } => false,
407            // All other expression types (enhancements)
408            _ => false,
409        }
410    }
411
412    fn contains_arithmetic_op(expr: &TLExpr) -> bool {
413        match expr {
414            TLExpr::Add(..)
415            | TLExpr::Sub(..)
416            | TLExpr::Mul(..)
417            | TLExpr::Div(..)
418            | TLExpr::Pow(..)
419            | TLExpr::Mod(..)
420            | TLExpr::Min(..)
421            | TLExpr::Max(..)
422            | TLExpr::Abs(..)
423            | TLExpr::Floor(..)
424            | TLExpr::Ceil(..)
425            | TLExpr::Round(..)
426            | TLExpr::Sqrt(..)
427            | TLExpr::Exp(..)
428            | TLExpr::Log(..)
429            | TLExpr::Sin(..)
430            | TLExpr::Cos(..)
431            | TLExpr::Tan(..) => true,
432            TLExpr::And(l, r)
433            | TLExpr::Or(l, r)
434            | TLExpr::Eq(l, r)
435            | TLExpr::Lt(l, r)
436            | TLExpr::Gt(l, r)
437            | TLExpr::Lte(l, r)
438            | TLExpr::Gte(l, r) => {
439                Self::contains_arithmetic_op(l) || Self::contains_arithmetic_op(r)
440            }
441            TLExpr::Imply(premise, conclusion) => {
442                Self::contains_arithmetic_op(premise) || Self::contains_arithmetic_op(conclusion)
443            }
444            TLExpr::Not(inner) | TLExpr::Score(inner) => Self::contains_arithmetic_op(inner),
445            TLExpr::Exists { body, .. } | TLExpr::ForAll { body, .. } => {
446                Self::contains_arithmetic_op(body)
447            }
448            TLExpr::Let {
449                var: _,
450                value,
451                body,
452            } => Self::contains_arithmetic_op(value) || Self::contains_arithmetic_op(body),
453            TLExpr::IfThenElse {
454                condition,
455                then_branch,
456                else_branch,
457            } => {
458                Self::contains_arithmetic_op(condition)
459                    || Self::contains_arithmetic_op(then_branch)
460                    || Self::contains_arithmetic_op(else_branch)
461            }
462
463            // Modal/temporal logic operators - check recursively
464            TLExpr::Box(inner)
465            | TLExpr::Diamond(inner)
466            | TLExpr::Next(inner)
467            | TLExpr::Eventually(inner)
468            | TLExpr::Always(inner) => Self::contains_arithmetic_op(inner),
469            TLExpr::Until { before, after } => {
470                Self::contains_arithmetic_op(before) || Self::contains_arithmetic_op(after)
471            }
472
473            // Fuzzy logic operators - check recursively
474            TLExpr::TNorm { left, right, .. } | TLExpr::TCoNorm { left, right, .. } => {
475                Self::contains_arithmetic_op(left) || Self::contains_arithmetic_op(right)
476            }
477            TLExpr::FuzzyNot { expr, .. } => Self::contains_arithmetic_op(expr),
478            TLExpr::FuzzyImplication {
479                premise,
480                conclusion,
481                ..
482            } => Self::contains_arithmetic_op(premise) || Self::contains_arithmetic_op(conclusion),
483            TLExpr::SoftExists { body, .. } | TLExpr::SoftForAll { body, .. } => {
484                Self::contains_arithmetic_op(body)
485            }
486            TLExpr::WeightedRule { rule, .. } => Self::contains_arithmetic_op(rule),
487            TLExpr::ProbabilisticChoice { alternatives } => alternatives
488                .iter()
489                .any(|(_prob, expr)| Self::contains_arithmetic_op(expr)),
490            TLExpr::Release { released, releaser }
491            | TLExpr::WeakUntil {
492                before: released,
493                after: releaser,
494            }
495            | TLExpr::StrongRelease { released, releaser } => {
496                Self::contains_arithmetic_op(released) || Self::contains_arithmetic_op(releaser)
497            }
498
499            TLExpr::Pred { .. } | TLExpr::Constant(_) | TLExpr::Aggregate { .. } => false,
500            // All other expression types (enhancements)
501            _ => false,
502        }
503    }
504
505    /// Calculate a complexity score for the expression.
506    pub fn complexity_score(&self) -> f64 {
507        let op_count = self.and_count
508            + self.or_count
509            + self.not_count
510            + self.exists_count
511            + self.forall_count
512            + self.implication_count
513            + self.arithmetic_count
514            + self.comparison_count;
515
516        let base_score = op_count as f64;
517        let depth_penalty = self.max_depth as f64 * 0.5;
518        let quantifier_penalty = if self.has_nested_quantifiers {
519            10.0
520        } else {
521            0.0
522        };
523        let mixed_penalty = if self.has_mixed_operations { 5.0 } else { 0.0 };
524
525        base_score + depth_penalty + quantifier_penalty + mixed_penalty
526    }
527}
528
529/// Strategy recommendation with confidence score.
530#[derive(Debug, Clone)]
531pub struct StrategyRecommendation {
532    /// Recommended configuration
533    pub config: CompilationConfig,
534    /// Confidence score (0.0 to 1.0)
535    pub confidence: f64,
536    /// Explanation for the recommendation
537    pub rationale: String,
538    /// Alternative configurations (if any)
539    pub alternatives: Vec<(CompilationConfig, f64, String)>,
540}
541
542/// Analyze an expression and recommend compilation strategy.
543///
544/// # Examples
545///
546/// ```
547/// use tensorlogic_compiler::passes::recommend_strategy;
548/// use tensorlogic_compiler::passes::OptimizationGoal;
549/// use tensorlogic_ir::{TLExpr, Term};
550///
551/// // Expression with nested quantifiers
552/// let expr = TLExpr::exists(
553///     "y",
554///     "Person",
555///     TLExpr::forall(
556///         "z",
557///         "Person",
558///         TLExpr::imply(
559///             TLExpr::pred("knows", vec![Term::var("y"), Term::var("z")]),
560///             TLExpr::pred("likes", vec![Term::var("x"), Term::var("z")]),
561///         ),
562///     ),
563/// );
564///
565/// let recommendation = recommend_strategy(&expr, OptimizationGoal::Differentiable);
566/// assert!(recommendation.confidence > 0.5);
567/// println!("Recommendation: {}", recommendation.rationale);
568/// ```
569pub fn recommend_strategy(expr: &TLExpr, goal: OptimizationGoal) -> StrategyRecommendation {
570    let profile = ExpressionProfile::analyze(expr);
571
572    match goal {
573        OptimizationGoal::Differentiable => recommend_for_differentiable(&profile),
574        OptimizationGoal::DiscreteReasoning => recommend_for_discrete(&profile),
575        OptimizationGoal::Performance => recommend_for_performance(&profile),
576        OptimizationGoal::Balanced => recommend_for_balanced(&profile),
577    }
578}
579
580fn recommend_for_differentiable(profile: &ExpressionProfile) -> StrategyRecommendation {
581    let complexity = profile.complexity_score();
582
583    // For expressions with nested quantifiers, use Łukasiewicz for better gradient stability
584    if profile.has_nested_quantifiers {
585        StrategyRecommendation {
586            config: CompilationConfig::fuzzy_lukasiewicz(),
587            confidence: 0.85,
588            rationale: format!(
589                "Łukasiewicz fuzzy logic recommended for nested quantifiers \
590                 (complexity: {:.1}). Fully differentiable with stable gradients.",
591                complexity
592            ),
593            alternatives: vec![(
594                CompilationConfig::soft_differentiable(),
595                0.75,
596                "Standard soft differentiable config (simpler but may have gradient issues)"
597                    .to_string(),
598            )],
599        }
600    }
601    // For expressions with mixed arithmetic and logic
602    else if profile.has_mixed_operations {
603        StrategyRecommendation {
604            config: CompilationConfig::soft_differentiable(),
605            confidence: 0.9,
606            rationale:
607                "Soft differentiable config recommended for mixed arithmetic-logic expressions. \
608                       Provides smooth gradients for both operation types."
609                    .to_string(),
610            alternatives: vec![(
611                CompilationConfig::fuzzy_product(),
612                0.7,
613                "Product fuzzy logic (alternative probabilistic interpretation)".to_string(),
614            )],
615        }
616    }
617    // For simple expressions, standard soft config is best
618    else if complexity < 10.0 {
619        StrategyRecommendation {
620            config: CompilationConfig::soft_differentiable(),
621            confidence: 0.95,
622            rationale: format!(
623                "Standard soft differentiable config recommended for simple expression \
624                 (complexity: {:.1}). Efficient and well-tested.",
625                complexity
626            ),
627            alternatives: vec![],
628        }
629    }
630    // Default to soft differentiable
631    else {
632        StrategyRecommendation {
633            config: CompilationConfig::soft_differentiable(),
634            confidence: 0.8,
635            rationale: format!(
636                "Standard soft differentiable config recommended (complexity: {:.1}).",
637                complexity
638            ),
639            alternatives: vec![(
640                CompilationConfig::fuzzy_lukasiewicz(),
641                0.7,
642                "Łukasiewicz fuzzy logic (alternative for more complex scenarios)".to_string(),
643            )],
644        }
645    }
646}
647
648fn recommend_for_discrete(profile: &ExpressionProfile) -> StrategyRecommendation {
649    // For expressions with negated quantifiers, use Gödel
650    if profile.has_negated_quantifiers {
651        StrategyRecommendation {
652            config: CompilationConfig::fuzzy_godel(),
653            confidence: 0.9,
654            rationale: "Gödel fuzzy logic recommended for negated quantifiers. \
655                       Provides crisp Boolean-like semantics while handling fuzzy values."
656                .to_string(),
657            alternatives: vec![(
658                CompilationConfig::hard_boolean(),
659                0.75,
660                "Pure Boolean logic (strictly discrete but may lose gradient info)".to_string(),
661            )],
662        }
663    }
664    // For simple discrete reasoning
665    else if profile.complexity_score() < 15.0 {
666        StrategyRecommendation {
667            config: CompilationConfig::hard_boolean(),
668            confidence: 0.95,
669            rationale: format!(
670                "Hard Boolean logic recommended for simple discrete reasoning \
671                 (complexity: {:.1}). Provides crisp true/false semantics.",
672                profile.complexity_score()
673            ),
674            alternatives: vec![],
675        }
676    }
677    // For complex discrete reasoning, use Gödel as it's more robust
678    else {
679        StrategyRecommendation {
680            config: CompilationConfig::fuzzy_godel(),
681            confidence: 0.85,
682            rationale: format!(
683                "Gödel fuzzy logic recommended for complex discrete reasoning \
684                 (complexity: {:.1}). More robust than pure Boolean for edge cases.",
685                profile.complexity_score()
686            ),
687            alternatives: vec![(
688                CompilationConfig::hard_boolean(),
689                0.7,
690                "Hard Boolean logic (simpler but less robust)".to_string(),
691            )],
692        }
693    }
694}
695
696fn recommend_for_performance(profile: &ExpressionProfile) -> StrategyRecommendation {
697    let complexity = profile.complexity_score();
698
699    // For simple expressions, use Product fuzzy (efficient)
700    if complexity < 10.0 {
701        StrategyRecommendation {
702            config: CompilationConfig::fuzzy_product(),
703            confidence: 0.9,
704            rationale: format!(
705                "Product fuzzy logic recommended for efficient execution \
706                 (complexity: {:.1}). Uses fast multiplication instead of min/max.",
707                complexity
708            ),
709            alternatives: vec![(
710                CompilationConfig::soft_differentiable(),
711                0.85,
712                "Soft differentiable (similar performance, better gradients)".to_string(),
713            )],
714        }
715    }
716    // For complex expressions with many quantifiers, use probabilistic
717    else if profile.exists_count + profile.forall_count > 3 {
718        StrategyRecommendation {
719            config: CompilationConfig::probabilistic(),
720            confidence: 0.85,
721            rationale: "Probabilistic config recommended for many quantifiers. \
722                       Uses efficient mean reductions instead of sum/product."
723                .to_string(),
724            alternatives: vec![(
725                CompilationConfig::fuzzy_product(),
726                0.75,
727                "Product fuzzy logic (alternative with product reductions)".to_string(),
728            )],
729        }
730    }
731    // Default to soft differentiable (good balance)
732    else {
733        StrategyRecommendation {
734            config: CompilationConfig::soft_differentiable(),
735            confidence: 0.8,
736            rationale: format!(
737                "Soft differentiable config recommended for balanced performance \
738                 (complexity: {:.1}).",
739                complexity
740            ),
741            alternatives: vec![(
742                CompilationConfig::fuzzy_product(),
743                0.75,
744                "Product fuzzy logic (slight performance edge)".to_string(),
745            )],
746        }
747    }
748}
749
750fn recommend_for_balanced(profile: &ExpressionProfile) -> StrategyRecommendation {
751    let complexity = profile.complexity_score();
752
753    // For very simple expressions, use soft differentiable
754    if complexity < 8.0 {
755        StrategyRecommendation {
756            config: CompilationConfig::soft_differentiable(),
757            confidence: 0.9,
758            rationale: format!(
759                "Soft differentiable config recommended for simple balanced use \
760                 (complexity: {:.1}).",
761                complexity
762            ),
763            alternatives: vec![],
764        }
765    }
766    // For moderately complex, use Łukasiewicz (good balance)
767    else if complexity < 25.0 {
768        StrategyRecommendation {
769            config: CompilationConfig::fuzzy_lukasiewicz(),
770            confidence: 0.85,
771            rationale: format!(
772                "Łukasiewicz fuzzy logic recommended for balanced complexity \
773                 (complexity: {:.1}). Good mix of differentiability and accuracy.",
774                complexity
775            ),
776            alternatives: vec![
777                (
778                    CompilationConfig::soft_differentiable(),
779                    0.75,
780                    "Soft differentiable (simpler, slightly better performance)".to_string(),
781                ),
782                (
783                    CompilationConfig::fuzzy_godel(),
784                    0.7,
785                    "Gödel fuzzy logic (more discrete semantics)".to_string(),
786                ),
787            ],
788        }
789    }
790    // For very complex, use probabilistic (robust)
791    else {
792        StrategyRecommendation {
793            config: CompilationConfig::probabilistic(),
794            confidence: 0.8,
795            rationale: format!(
796                "Probabilistic config recommended for complex balanced reasoning \
797                 (complexity: {:.1}). Robust handling of uncertainty.",
798                complexity
799            ),
800            alternatives: vec![(
801                CompilationConfig::fuzzy_lukasiewicz(),
802                0.75,
803                "Łukasiewicz fuzzy logic (more structured reasoning)".to_string(),
804            )],
805        }
806    }
807}
808
809#[cfg(test)]
810mod tests {
811    use super::*;
812    use tensorlogic_ir::Term;
813
814    #[test]
815    fn test_simple_predicate_profile() {
816        let expr = TLExpr::pred("knows", vec![Term::var("x"), Term::var("y")]);
817        let profile = ExpressionProfile::analyze(&expr);
818
819        assert_eq!(profile.and_count, 0);
820        assert_eq!(profile.exists_count, 0);
821        assert_eq!(profile.max_depth, 0);
822        assert!(!profile.has_nested_quantifiers);
823    }
824
825    #[test]
826    fn test_and_or_profile() {
827        let expr = TLExpr::And(
828            Box::new(TLExpr::pred("p", vec![Term::var("x")])),
829            Box::new(TLExpr::Or(
830                Box::new(TLExpr::pred("q", vec![Term::var("y")])),
831                Box::new(TLExpr::pred("r", vec![Term::var("z")])),
832            )),
833        );
834        let profile = ExpressionProfile::analyze(&expr);
835
836        assert_eq!(profile.and_count, 1);
837        assert_eq!(profile.or_count, 1);
838        assert_eq!(profile.max_depth, 2);
839    }
840
841    #[test]
842    fn test_nested_quantifiers_profile() {
843        let expr = TLExpr::exists(
844            "y",
845            "D",
846            TLExpr::forall(
847                "z",
848                "D",
849                TLExpr::pred("p", vec![Term::var("x"), Term::var("y"), Term::var("z")]),
850            ),
851        );
852        let profile = ExpressionProfile::analyze(&expr);
853
854        assert_eq!(profile.exists_count, 1);
855        assert_eq!(profile.forall_count, 1);
856        assert!(profile.has_nested_quantifiers);
857        assert_eq!(profile.max_depth, 2);
858    }
859
860    #[test]
861    fn test_negated_quantifier_profile() {
862        let expr = TLExpr::Not(Box::new(TLExpr::exists(
863            "y",
864            "D",
865            TLExpr::pred("p", vec![Term::var("x"), Term::var("y")]),
866        )));
867        let profile = ExpressionProfile::analyze(&expr);
868
869        assert_eq!(profile.not_count, 1);
870        assert_eq!(profile.exists_count, 1);
871        assert!(profile.has_negated_quantifiers);
872    }
873
874    #[test]
875    fn test_mixed_operations_profile() {
876        let expr = TLExpr::And(
877            Box::new(TLExpr::pred("p", vec![Term::var("x")])),
878            Box::new(TLExpr::Add(
879                Box::new(TLExpr::Constant(1.0)),
880                Box::new(TLExpr::Constant(2.0)),
881            )),
882        );
883        let profile = ExpressionProfile::analyze(&expr);
884
885        assert_eq!(profile.arithmetic_count, 1);
886        assert!(profile.has_mixed_operations);
887    }
888
889    #[test]
890    fn test_recommend_simple_differentiable() {
891        let expr = TLExpr::And(
892            Box::new(TLExpr::pred("p", vec![Term::var("x")])),
893            Box::new(TLExpr::pred("q", vec![Term::var("y")])),
894        );
895
896        let rec = recommend_strategy(&expr, OptimizationGoal::Differentiable);
897        assert_eq!(rec.config, CompilationConfig::soft_differentiable());
898        assert!(rec.confidence > 0.8);
899    }
900
901    #[test]
902    fn test_recommend_complex_differentiable() {
903        let expr = TLExpr::exists(
904            "y",
905            "D",
906            TLExpr::forall(
907                "z",
908                "D",
909                TLExpr::And(
910                    Box::new(TLExpr::pred("p", vec![Term::var("y")])),
911                    Box::new(TLExpr::pred("q", vec![Term::var("z")])),
912                ),
913            ),
914        );
915
916        let rec = recommend_strategy(&expr, OptimizationGoal::Differentiable);
917        // Should recommend Łukasiewicz for nested quantifiers
918        assert_eq!(rec.config, CompilationConfig::fuzzy_lukasiewicz());
919        assert!(rec.confidence > 0.8);
920    }
921
922    #[test]
923    fn test_recommend_discrete() {
924        let expr = TLExpr::And(
925            Box::new(TLExpr::pred("p", vec![Term::var("x")])),
926            Box::new(TLExpr::pred("q", vec![Term::var("y")])),
927        );
928
929        let rec = recommend_strategy(&expr, OptimizationGoal::DiscreteReasoning);
930        assert_eq!(rec.config, CompilationConfig::hard_boolean());
931        assert!(rec.confidence > 0.9);
932    }
933
934    #[test]
935    fn test_recommend_performance() {
936        let expr = TLExpr::And(
937            Box::new(TLExpr::pred("p", vec![Term::var("x")])),
938            Box::new(TLExpr::pred("q", vec![Term::var("y")])),
939        );
940
941        let rec = recommend_strategy(&expr, OptimizationGoal::Performance);
942        assert_eq!(rec.config, CompilationConfig::fuzzy_product());
943        assert!(rec.confidence > 0.8);
944    }
945
946    #[test]
947    fn test_recommend_balanced() {
948        let expr = TLExpr::And(
949            Box::new(TLExpr::pred("p", vec![Term::var("x")])),
950            Box::new(TLExpr::pred("q", vec![Term::var("y")])),
951        );
952
953        let rec = recommend_strategy(&expr, OptimizationGoal::Balanced);
954        assert_eq!(rec.config, CompilationConfig::soft_differentiable());
955        assert!(rec.confidence > 0.8);
956    }
957
958    #[test]
959    fn test_complexity_score() {
960        // Simple expression
961        let simple = TLExpr::pred("p", vec![Term::var("x")]);
962        let simple_profile = ExpressionProfile::analyze(&simple);
963        let simple_score = simple_profile.complexity_score();
964        assert!(simple_score < 5.0);
965
966        // Complex expression
967        let complex = TLExpr::exists(
968            "y",
969            "D",
970            TLExpr::forall(
971                "z",
972                "D",
973                TLExpr::And(
974                    Box::new(TLExpr::Or(
975                        Box::new(TLExpr::pred("p", vec![Term::var("y")])),
976                        Box::new(TLExpr::pred("q", vec![Term::var("z")])),
977                    )),
978                    Box::new(TLExpr::Not(Box::new(TLExpr::pred(
979                        "r",
980                        vec![Term::var("x")],
981                    )))),
982                ),
983            ),
984        );
985        let complex_profile = ExpressionProfile::analyze(&complex);
986        let complex_score = complex_profile.complexity_score();
987        assert!(complex_score > simple_score);
988        assert!(complex_score > 15.0); // Has nested quantifiers penalty
989    }
990}