1use crate::config::*;
7use tensorlogic_ir::TLExpr;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum OptimizationGoal {
12 Differentiable,
14 DiscreteReasoning,
16 Performance,
18 Balanced,
20}
21
22#[derive(Debug, Clone, Default)]
24pub struct ExpressionProfile {
25 pub and_count: usize,
27 pub or_count: usize,
29 pub not_count: usize,
31 pub exists_count: usize,
33 pub forall_count: usize,
35 pub implication_count: usize,
37 pub arithmetic_count: usize,
39 pub comparison_count: usize,
41 pub max_depth: usize,
43 pub has_nested_quantifiers: bool,
45 pub has_negated_quantifiers: bool,
47 pub has_mixed_operations: bool,
49}
50
51impl ExpressionProfile {
52 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 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 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 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 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 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 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 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 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 }
241 _ => {
243 }
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 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 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 _ => 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 TLExpr::Box(..)
385 | TLExpr::Diamond(..)
386 | TLExpr::Next(..)
387 | TLExpr::Eventually(..)
388 | TLExpr::Always(..)
389 | TLExpr::Until { .. } => true,
390
391 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 _ => 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 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 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 _ => false,
502 }
503 }
504
505 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#[derive(Debug, Clone)]
531pub struct StrategyRecommendation {
532 pub config: CompilationConfig,
534 pub confidence: f64,
536 pub rationale: String,
538 pub alternatives: Vec<(CompilationConfig, f64, String)>,
540}
541
542pub 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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); }
990}