use super::{
advanced_analysis::{ComplexityMetrics, OperatorCounts},
optimization_pipeline::{OptimizationLevel, OptimizationPass, PipelineConfig},
TLExpr,
};
#[derive(Clone, Debug, PartialEq)]
pub struct ExpressionProfile {
pub operator_counts: OperatorCounts,
pub complexity: ComplexityMetrics,
pub has_quantifiers: bool,
pub has_modal: bool,
pub has_temporal: bool,
pub has_fuzzy: bool,
pub has_constants: bool,
pub size: usize,
}
impl ExpressionProfile {
pub fn analyze(expr: &TLExpr) -> Self {
let operator_counts = OperatorCounts::from_expr(expr);
let complexity = ComplexityMetrics::from_expr(expr);
Self {
has_quantifiers: operator_counts.quantifiers > 0,
has_modal: operator_counts.modal > 0,
has_temporal: operator_counts.temporal > 0,
has_fuzzy: operator_counts.fuzzy > 0,
has_constants: operator_counts.constants > 0,
size: operator_counts.total,
operator_counts,
complexity,
}
}
pub fn is_simple(&self) -> bool {
self.size <= 10 && self.complexity.max_depth <= 3
}
pub fn is_complex(&self) -> bool {
self.size > 50 || self.complexity.max_depth > 10
}
pub fn needs_distribution(&self) -> bool {
self.operator_counts.logical > 5
}
pub fn has_constant_opportunities(&self) -> bool {
self.has_constants && self.operator_counts.arithmetic > 0
}
}
#[derive(Clone, Copy, Debug)]
pub struct StrategySelector {
_default_level: OptimizationLevel,
}
impl Default for StrategySelector {
fn default() -> Self {
Self {
_default_level: OptimizationLevel::Standard,
}
}
}
impl StrategySelector {
pub fn new(default_level: OptimizationLevel) -> Self {
Self {
_default_level: default_level,
}
}
pub fn select_level(&self, profile: &ExpressionProfile) -> OptimizationLevel {
if profile.is_simple() {
return OptimizationLevel::Basic;
}
if profile.is_complex()
&& (profile.has_modal || profile.has_temporal || profile.has_quantifiers)
{
return OptimizationLevel::Aggressive;
}
OptimizationLevel::Standard
}
pub fn select_passes(&self, profile: &ExpressionProfile) -> Vec<OptimizationPass> {
let mut passes = Vec::new();
if profile.has_constants {
passes.push(OptimizationPass::ConstantFolding);
passes.push(OptimizationPass::ConstantPropagation);
}
passes.push(OptimizationPass::AlgebraicSimplification);
if profile.operator_counts.logical > 3 {
passes.push(OptimizationPass::NegationNormalForm);
}
if profile.has_modal {
passes.push(OptimizationPass::ModalEquivalences);
passes.push(OptimizationPass::DistributiveModal);
}
if profile.has_temporal {
passes.push(OptimizationPass::TemporalEquivalences);
}
if profile.has_quantifiers && profile.operator_counts.quantifiers > 2 {
passes.push(OptimizationPass::DistributiveQuantifiers);
}
if profile.needs_distribution() {
passes.push(OptimizationPass::DistributiveAndOverOr);
}
passes
}
pub fn recommend_config(&self, expr: &TLExpr) -> PipelineConfig {
let profile = ExpressionProfile::analyze(expr);
let level = self.select_level(&profile);
let custom_passes = self.select_passes(&profile);
let max_iterations = if profile.is_complex() { 15 } else { 10 };
PipelineConfig::with_level(level)
.with_custom_passes(custom_passes)
.with_max_iterations(max_iterations)
}
pub fn should_optimize_aggressively(&self, expr: &TLExpr) -> bool {
let profile = ExpressionProfile::analyze(expr);
matches!(self.select_level(&profile), OptimizationLevel::Aggressive)
}
}
pub fn auto_optimize(expr: TLExpr) -> (TLExpr, super::optimization_pipeline::OptimizationMetrics) {
let selector = StrategySelector::default();
let config = selector.recommend_config(&expr);
let pipeline = super::optimization_pipeline::OptimizationPipeline::new(config);
pipeline.optimize(expr)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Term;
#[test]
fn test_profile_simple_expression() {
let expr = TLExpr::pred("P", vec![Term::var("x")]);
let profile = ExpressionProfile::analyze(&expr);
assert!(profile.is_simple());
assert!(!profile.is_complex());
assert!(!profile.has_constants);
}
#[test]
fn test_profile_complex_expression() {
let mut expr = TLExpr::pred("P", vec![Term::var("x")]);
for _ in 0..15 {
expr = TLExpr::and(expr.clone(), TLExpr::pred("Q", vec![Term::var("y")]));
}
let profile = ExpressionProfile::analyze(&expr);
assert!(profile.is_complex());
assert!(!profile.is_simple());
}
#[test]
fn test_profile_with_constants() {
let expr = TLExpr::add(TLExpr::constant(1.0), TLExpr::constant(2.0));
let profile = ExpressionProfile::analyze(&expr);
assert!(profile.has_constants);
assert!(profile.has_constant_opportunities());
}
#[test]
fn test_profile_with_quantifiers() {
let expr = TLExpr::forall("x", "D", TLExpr::pred("P", vec![Term::var("x")]));
let profile = ExpressionProfile::analyze(&expr);
assert!(profile.has_quantifiers);
}
#[test]
fn test_profile_with_modal() {
let expr = TLExpr::modal_box(TLExpr::pred("P", vec![Term::var("x")]));
let profile = ExpressionProfile::analyze(&expr);
assert!(profile.has_modal);
}
#[test]
fn test_profile_with_temporal() {
let expr = TLExpr::eventually(TLExpr::pred("P", vec![Term::var("x")]));
let profile = ExpressionProfile::analyze(&expr);
assert!(profile.has_temporal);
}
#[test]
fn test_selector_simple_expression() {
let expr = TLExpr::pred("P", vec![Term::var("x")]);
let selector = StrategySelector::default();
let profile = ExpressionProfile::analyze(&expr);
let level = selector.select_level(&profile);
assert_eq!(level, OptimizationLevel::Basic);
}
#[test]
fn test_selector_complex_modal_expression() {
let mut expr = TLExpr::modal_box(TLExpr::pred("P", vec![Term::var("x")]));
for _ in 0..12 {
expr = TLExpr::and(expr.clone(), TLExpr::modal_box(TLExpr::pred("Q", vec![])));
}
let selector = StrategySelector::default();
let profile = ExpressionProfile::analyze(&expr);
let level = selector.select_level(&profile);
assert_eq!(level, OptimizationLevel::Aggressive);
}
#[test]
fn test_selector_pass_selection() {
let expr = TLExpr::and(
TLExpr::constant(1.0),
TLExpr::modal_box(TLExpr::pred("P", vec![Term::var("x")])),
);
let selector = StrategySelector::default();
let profile = ExpressionProfile::analyze(&expr);
let passes = selector.select_passes(&profile);
assert!(passes.contains(&OptimizationPass::ConstantFolding));
assert!(passes.contains(&OptimizationPass::ModalEquivalences));
}
#[test]
fn test_recommend_config() {
let expr = TLExpr::and(
TLExpr::constant(1.0),
TLExpr::pred("P", vec![Term::var("x")]),
);
let selector = StrategySelector::default();
let config = selector.recommend_config(&expr);
assert_eq!(config.level, OptimizationLevel::Basic);
assert!(config.custom_passes.is_some());
}
#[test]
fn test_auto_optimize() {
let expr = TLExpr::and(
TLExpr::constant(1.0),
TLExpr::pred("P", vec![Term::var("x")]),
);
let (optimized, metrics) = auto_optimize(expr);
assert!(metrics.passes_applied > 0);
assert_eq!(optimized, TLExpr::pred("P", vec![Term::var("x")]));
}
#[test]
fn test_should_optimize_aggressively() {
let simple_expr = TLExpr::pred("P", vec![Term::var("x")]);
let selector = StrategySelector::default();
assert!(!selector.should_optimize_aggressively(&simple_expr));
let mut complex_expr = TLExpr::modal_box(TLExpr::pred("P", vec![Term::var("x")]));
for _ in 0..12 {
complex_expr = TLExpr::and(
complex_expr.clone(),
TLExpr::modal_box(TLExpr::pred("Q", vec![])),
);
}
assert!(selector.should_optimize_aggressively(&complex_expr));
}
#[test]
fn test_needs_distribution() {
let mut expr = TLExpr::pred("P", vec![Term::var("x")]);
for i in 0..7 {
expr = TLExpr::and(
expr,
TLExpr::or(
TLExpr::pred("Q", vec![Term::var(format!("x{}", i))]),
TLExpr::pred("R", vec![Term::var(format!("y{}", i))]),
),
);
}
let profile = ExpressionProfile::analyze(&expr);
assert!(profile.needs_distribution());
}
}