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 }
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 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 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 TLExpr::Box(..)
379 | TLExpr::Diamond(..)
380 | TLExpr::Next(..)
381 | TLExpr::Eventually(..)
382 | TLExpr::Always(..)
383 | TLExpr::Until { .. } => true,
384
385 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 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 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 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#[derive(Debug, Clone)]
521pub struct StrategyRecommendation {
522 pub config: CompilationConfig,
524 pub confidence: f64,
526 pub rationale: String,
528 pub alternatives: Vec<(CompilationConfig, f64, String)>,
530}
531
532pub 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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); }
980}