use yufmath::core::{Expression, Number};
use yufmath::engine::{
LazyExpression, DependencyGraph, LazyState,
ParallelComputeEngine, TaskScheduler, ExpressionPreprocessor,
};
use yufmath::engine::compute::BasicComputeEngine;
use yufmath::api::config::ParallelConfig;
use std::sync::Arc;
use std::time::Duration;
#[test]
fn test_lazy_expression_basic() {
let expr = Expression::add(
Expression::number(2.into()),
Expression::number(3.into())
);
let lazy_expr = LazyExpression::new(1, expr.clone());
assert_eq!(lazy_expr.id(), 1);
assert_eq!(lazy_expr.original(), &expr);
assert!(matches!(lazy_expr.state(), LazyState::Pending));
assert!(!lazy_expr.is_computed());
assert!(!lazy_expr.is_failed());
assert!(!lazy_expr.is_computing());
assert!(lazy_expr.can_compute()); }
#[test]
fn test_lazy_expression_computation() {
let engine = BasicComputeEngine::new();
let expr = Expression::add(
Expression::number(2.into()),
Expression::number(3.into())
);
let lazy_expr = LazyExpression::new(1, expr);
let result = lazy_expr.force_compute(&engine).unwrap();
assert!(lazy_expr.is_computed());
assert_eq!(lazy_expr.get_result(), Some(result.clone()));
if let Expression::Number(n) = result {
assert_eq!(n, Number::from(5));
} else {
panic!("期望得到数值结果");
}
}
#[test]
fn test_lazy_expression_with_custom_compute_fn() {
let engine = BasicComputeEngine::new();
let expr = Expression::variable("x");
let compute_fn = |_expr: &Expression, _engine: &dyn yufmath::engine::ComputeEngine| {
Ok(Expression::number(42.into()))
};
let lazy_expr = LazyExpression::with_compute_fn(1, expr, compute_fn);
let result = lazy_expr.force_compute(&engine).unwrap();
assert!(lazy_expr.is_computed());
if let Expression::Number(n) = result {
assert_eq!(n, Number::from(42));
} else {
panic!("期望得到数值结果");
}
}
#[test]
fn test_dependency_graph_basic() {
let mut graph = DependencyGraph::new();
let expr1 = graph.add_expression(Expression::variable("x"));
let expr2 = graph.add_expression(Expression::variable("y"));
let expr3 = graph.add_expression(Expression::add(
Expression::variable("x"),
Expression::variable("y")
));
assert_eq!(expr1.id(), 1);
assert_eq!(expr2.id(), 2);
assert_eq!(expr3.id(), 3);
graph.add_dependency(expr3.id(), expr1.id()).unwrap();
graph.add_dependency(expr3.id(), expr2.id()).unwrap();
let stats = graph.get_stats();
assert_eq!(stats.total_expressions, 3);
assert_eq!(stats.pending_expressions, 3);
assert_eq!(stats.total_dependencies, 2);
}
#[test]
fn test_dependency_graph_topological_sort() {
let mut graph = DependencyGraph::new();
let expr_x = graph.add_expression(Expression::variable("x"));
let expr_y = graph.add_expression(Expression::variable("y"));
let expr_z = graph.add_expression(Expression::variable("z"));
let expr_sum = graph.add_expression(Expression::add(
Expression::variable("x"),
Expression::variable("y")
));
let expr_product = graph.add_expression(Expression::multiply(
Expression::add(Expression::variable("x"), Expression::variable("y")),
Expression::variable("z")
));
graph.add_dependency(expr_sum.id(), expr_x.id()).unwrap();
graph.add_dependency(expr_sum.id(), expr_y.id()).unwrap();
graph.add_dependency(expr_product.id(), expr_sum.id()).unwrap();
graph.add_dependency(expr_product.id(), expr_z.id()).unwrap();
let sorted = graph.topological_sort().unwrap();
assert_eq!(sorted.len(), 5);
let pos_x = sorted.iter().position(|&id| id == expr_x.id()).unwrap();
let pos_y = sorted.iter().position(|&id| id == expr_y.id()).unwrap();
let pos_z = sorted.iter().position(|&id| id == expr_z.id()).unwrap();
let pos_sum = sorted.iter().position(|&id| id == expr_sum.id()).unwrap();
let pos_product = sorted.iter().position(|&id| id == expr_product.id()).unwrap();
assert!(pos_x < pos_sum);
assert!(pos_y < pos_sum);
assert!(pos_z < pos_product);
assert!(pos_sum < pos_product);
}
#[test]
fn test_dependency_graph_parallel_groups() {
let mut graph = DependencyGraph::new();
let expr_x = graph.add_expression(Expression::variable("x"));
let expr_y = graph.add_expression(Expression::variable("y"));
let expr_z = graph.add_expression(Expression::variable("z"));
let expr_sum = graph.add_expression(Expression::add(
Expression::variable("x"),
Expression::variable("y")
));
let expr_double_z = graph.add_expression(Expression::multiply(
Expression::variable("z"),
Expression::number(2.into())
));
let expr_final = graph.add_expression(Expression::add(
Expression::add(Expression::variable("x"), Expression::variable("y")),
Expression::multiply(Expression::variable("z"), Expression::number(2.into()))
));
graph.add_dependency(expr_sum.id(), expr_x.id()).unwrap();
graph.add_dependency(expr_sum.id(), expr_y.id()).unwrap();
graph.add_dependency(expr_double_z.id(), expr_z.id()).unwrap();
graph.add_dependency(expr_final.id(), expr_sum.id()).unwrap();
graph.add_dependency(expr_final.id(), expr_double_z.id()).unwrap();
let groups = graph.get_parallel_groups().unwrap();
assert_eq!(groups.len(), 3);
assert_eq!(groups[0].len(), 3); assert_eq!(groups[1].len(), 2); assert_eq!(groups[2].len(), 1); }
#[test]
fn test_dependency_graph_cycle_detection() {
let mut graph = DependencyGraph::new();
let expr1 = graph.add_expression(Expression::variable("x"));
let expr2 = graph.add_expression(Expression::variable("y"));
graph.add_dependency(expr2.id(), expr1.id()).unwrap();
let result = graph.add_dependency(expr1.id(), expr2.id());
assert!(result.is_err());
}
#[test]
fn test_dependency_graph_computation() {
let engine = BasicComputeEngine::new();
let mut graph = DependencyGraph::new();
let expr1 = graph.add_expression(Expression::number(2.into()));
let expr2 = graph.add_expression(Expression::number(3.into()));
let expr3 = graph.add_expression(Expression::add(
Expression::number(2.into()),
Expression::number(3.into())
));
graph.add_dependency(expr3.id(), expr1.id()).unwrap();
graph.add_dependency(expr3.id(), expr2.id()).unwrap();
let sorted = graph.topological_sort().unwrap();
for expr_id in sorted {
if let Some(expr) = graph.get_expression(expr_id) {
expr.force_compute(&engine).unwrap();
}
}
assert!(expr1.is_computed());
assert!(expr2.is_computed());
assert!(expr3.is_computed());
if let Some(Expression::Number(n)) = expr3.get_result() {
assert_eq!(n, Number::from(5));
} else {
panic!("期望得到数值结果");
}
}
#[test]
fn test_task_scheduler() {
let config = ParallelConfig::default();
let scheduler = TaskScheduler::new(config);
let expr = Expression::add(
Expression::number(2.into()),
Expression::number(3.into())
);
let lazy_expr = Arc::new(LazyExpression::new(1, expr));
let task_id = scheduler.add_task(lazy_expr.clone());
assert_eq!(scheduler.pending_count(), 1);
assert_eq!(scheduler.running_count(), 0);
assert_eq!(scheduler.completed_count(), 0);
let task = scheduler.get_next_task().unwrap();
assert_eq!(task.id, task_id);
assert_eq!(scheduler.pending_count(), 0);
assert_eq!(scheduler.running_count(), 1);
let result = Expression::number(5.into());
scheduler.complete_task(task_id, Ok(result.clone()));
assert_eq!(scheduler.running_count(), 0);
assert_eq!(scheduler.completed_count(), 1);
let task_result = scheduler.get_task_result(task_id).unwrap();
assert!(task_result.is_ok());
assert_eq!(task_result.unwrap(), result);
}
#[test]
fn test_parallel_compute_engine() {
let base_engine = Arc::new(BasicComputeEngine::new());
let config = ParallelConfig::default();
let parallel_engine = ParallelComputeEngine::new(base_engine, config).unwrap();
let expressions = vec![
Expression::add(Expression::number(1.into()), Expression::number(2.into())),
Expression::multiply(Expression::number(3.into()), Expression::number(4.into())),
Expression::subtract(Expression::number(10.into()), Expression::number(5.into())),
Expression::divide(Expression::number(20.into()), Expression::number(4.into())),
];
let results = parallel_engine.compute_parallel(expressions);
assert_eq!(results.len(), 4);
for result in &results {
assert!(result.is_ok());
}
if let Ok(Expression::Number(n)) = &results[0] {
assert_eq!(*n, Number::from(3));
}
if let Ok(Expression::Number(n)) = &results[1] {
assert_eq!(*n, Number::from(12));
}
if let Ok(Expression::Number(n)) = &results[2] {
assert_eq!(*n, Number::from(5));
}
if let Ok(Expression::Number(n)) = &results[3] {
assert_eq!(*n, Number::from(5));
}
}
#[test]
fn test_parallel_compute_with_dependencies() {
let base_engine = Arc::new(BasicComputeEngine::new());
let config = ParallelConfig::default();
let parallel_engine = ParallelComputeEngine::new(base_engine, config).unwrap();
let mut graph = DependencyGraph::new();
let expr1 = graph.add_expression(Expression::number(1.into()));
let expr2 = graph.add_expression(Expression::number(2.into()));
let expr3 = graph.add_expression(Expression::number(3.into()));
let expr_sum = graph.add_expression(Expression::add(
Expression::number(1.into()),
Expression::number(2.into())
));
let expr_product = graph.add_expression(Expression::multiply(
Expression::add(Expression::number(1.into()), Expression::number(2.into())),
Expression::number(3.into())
));
graph.add_dependency(expr_sum.id(), expr1.id()).unwrap();
graph.add_dependency(expr_sum.id(), expr2.id()).unwrap();
graph.add_dependency(expr_product.id(), expr_sum.id()).unwrap();
graph.add_dependency(expr_product.id(), expr3.id()).unwrap();
parallel_engine.compute_with_dependencies(&mut graph).unwrap();
assert!(expr1.is_computed());
assert!(expr2.is_computed());
assert!(expr3.is_computed());
assert!(expr_sum.is_computed());
assert!(expr_product.is_computed());
if let Some(Expression::Number(n)) = expr_product.get_result() {
assert_eq!(n, Number::from(9)); } else {
panic!("期望得到数值结果");
}
}
#[test]
fn test_expression_preprocessor() {
let config = ParallelConfig::default();
let preprocessor = ExpressionPreprocessor::new(config);
let expr = Expression::add(
Expression::number(2.into()),
Expression::number(3.into())
);
let folded = preprocessor.preprocess(&expr);
if let Expression::Number(n) = folded {
assert_eq!(n, Number::from(5));
} else {
panic!("常量折叠失败");
}
}
#[test]
fn test_expression_preprocessor_algebraic_rules() {
let config = ParallelConfig::default();
let preprocessor = ExpressionPreprocessor::new(config);
let expr = Expression::add(
Expression::variable("x"),
Expression::number(0.into())
);
let simplified = preprocessor.preprocess(&expr);
assert_eq!(simplified, Expression::variable("x"));
let expr = Expression::multiply(
Expression::variable("x"),
Expression::number(1.into())
);
let simplified = preprocessor.preprocess(&expr);
assert_eq!(simplified, Expression::variable("x"));
let expr = Expression::multiply(
Expression::variable("x"),
Expression::number(0.into())
);
let simplified = preprocessor.preprocess(&expr);
assert_eq!(simplified, Expression::number(0.into()));
}
#[test]
fn test_parallelization_analysis() {
let config = ParallelConfig::default();
let preprocessor = ExpressionPreprocessor::new(config);
let simple_expr = Expression::add(
Expression::variable("x"),
Expression::number(1.into())
);
let analysis = preprocessor.analyze_parallelization_potential(&simple_expr);
assert!(analysis.complexity > 0);
assert!(!analysis.recommended_parallel);
let complex_expr = Expression::add(
Expression::multiply(
Expression::add(Expression::variable("x"), Expression::variable("y")),
Expression::add(Expression::variable("z"), Expression::variable("w"))
),
Expression::multiply(
Expression::add(Expression::variable("a"), Expression::variable("b")),
Expression::add(Expression::variable("c"), Expression::variable("d"))
)
);
let analysis = preprocessor.analyze_parallelization_potential(&complex_expr);
assert!(analysis.complexity > 10); assert!(analysis.independent_parts_count > 1);
assert!(analysis.estimated_speedup > 1.0);
}
#[test]
fn test_scheduler_stats() {
let base_engine = Arc::new(BasicComputeEngine::new());
let config = ParallelConfig::default();
let parallel_engine = ParallelComputeEngine::new(base_engine, config).unwrap();
let stats = parallel_engine.get_scheduler_stats();
assert_eq!(stats.pending_tasks, 0);
assert_eq!(stats.running_tasks, 0);
assert_eq!(stats.completed_tasks, 0);
assert!(!stats.is_running);
}
#[test]
fn test_lazy_expression_reset() {
let engine = BasicComputeEngine::new();
let expr = Expression::add(
Expression::number(2.into()),
Expression::number(3.into())
);
let lazy_expr = LazyExpression::new(1, expr);
lazy_expr.force_compute(&engine).unwrap();
assert!(lazy_expr.is_computed());
lazy_expr.reset();
assert!(matches!(lazy_expr.state(), LazyState::Pending));
assert!(!lazy_expr.is_computed());
}
#[test]
fn test_dependency_graph_cleanup() {
let engine = BasicComputeEngine::new();
let mut graph = DependencyGraph::new();
let expr1 = graph.add_expression(Expression::number(1.into()));
let expr2 = graph.add_expression(Expression::number(2.into()));
expr1.force_compute(&engine).unwrap();
expr2.force_compute(&engine).unwrap();
let stats_before = graph.get_stats();
assert_eq!(stats_before.total_expressions, 2);
assert_eq!(stats_before.computed_expressions, 2);
graph.cleanup_completed();
let stats_after = graph.get_stats();
assert_eq!(stats_after.total_expressions, 0);
}
#[test]
fn test_parallel_config() {
let config = ParallelConfig::new()
.with_enabled(true)
.with_thread_count(4)
.with_complexity_threshold(50)
.with_max_parallel_tasks(8);
assert!(config.enabled);
assert_eq!(config.thread_count, Some(4));
assert_eq!(config.complexity_threshold, 50);
assert_eq!(config.max_parallel_tasks, 8);
}
#[test]
fn test_compute_task_weight() {
let expr = Expression::variable("x");
let lazy_expr = Arc::new(LazyExpression::new(1, expr));
let task = yufmath::engine::ComputeTask::new(1, lazy_expr);
let weight = task.weight();
assert!(weight >= 0.0); }