1use crate::{ComputationGraph, JitError, JitResult, Node, NodeId};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::sync::{Arc, RwLock};
10
11pub struct ConstantEvaluator {
13 config: ConstEvalConfig,
14 constant_cache: Arc<RwLock<HashMap<NodeId, ConstantValue>>>,
15 evaluation_context: EvaluationContext,
16}
17
18#[derive(Debug, Clone)]
20pub struct ConstEvalConfig {
21 pub enable_constant_folding: bool,
23
24 pub enable_dead_code_elimination: bool,
26
27 pub enable_branch_elimination: bool,
29
30 pub enable_loop_unrolling: bool,
32
33 pub max_evaluation_steps: usize,
35
36 pub max_memory_usage: usize,
38
39 pub enable_aggressive_propagation: bool,
41
42 pub max_recursion_depth: usize,
44
45 pub cache_size: usize,
47}
48
49impl Default for ConstEvalConfig {
50 fn default() -> Self {
51 Self {
52 enable_constant_folding: true,
53 enable_dead_code_elimination: true,
54 enable_branch_elimination: true,
55 enable_loop_unrolling: true,
56 max_evaluation_steps: 10000,
57 max_memory_usage: 64 * 1024 * 1024, enable_aggressive_propagation: false,
59 max_recursion_depth: 100,
60 cache_size: 1000,
61 }
62 }
63}
64
65#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
67pub enum ConstantValue {
68 Bool(bool),
70
71 Int(i64),
73
74 UInt(u64),
76
77 Float(f64),
79
80 String(String),
82
83 Array(Vec<ConstantValue>),
85
86 Tensor {
88 shape: Vec<usize>,
89 data: Vec<f64>,
90 dtype: String,
91 },
92
93 Complex { real: f64, imag: f64 },
95
96 None,
98
99 Undefined,
101}
102
103#[derive(Debug, Clone)]
105pub struct EvaluationContext {
106 variables: HashMap<String, ConstantValue>,
108
109 functions: HashMap<String, FunctionDefinition>,
111
112 depth: usize,
114
115 steps: usize,
117
118 memory_usage: usize,
120}
121
122#[derive(Debug, Clone)]
124pub struct FunctionDefinition {
125 pub name: String,
126 pub parameters: Vec<String>,
127 pub body: Vec<Instruction>,
128 pub return_type: Option<String>,
129}
130
131#[derive(Debug, Clone)]
133pub enum Instruction {
134 LoadConstant(ConstantValue),
136
137 LoadVariable(String),
139
140 Store(String),
142
143 BinaryOp {
145 op: BinaryOperator,
146 left: Box<Instruction>,
147 right: Box<Instruction>,
148 },
149
150 UnaryOp {
152 op: UnaryOperator,
153 operand: Box<Instruction>,
154 },
155
156 Call {
158 function: String,
159 args: Vec<Instruction>,
160 },
161
162 Conditional {
164 condition: Box<Instruction>,
165 then_branch: Box<Instruction>,
166 else_branch: Box<Instruction>,
167 },
168
169 Loop {
171 condition: Box<Instruction>,
172 body: Vec<Instruction>,
173 max_iterations: Option<usize>,
174 },
175
176 Index {
178 array: Box<Instruction>,
179 index: Box<Instruction>,
180 },
181
182 Array(Vec<Instruction>),
184
185 Tensor {
187 shape: Vec<usize>,
188 data: Vec<Instruction>,
189 },
190}
191
192#[derive(Debug, Clone, Copy, PartialEq)]
194pub enum BinaryOperator {
195 Add,
196 Sub,
197 Mul,
198 Div,
199 Mod,
200 Pow,
201 And,
202 Or,
203 Xor,
204 Lt,
205 Le,
206 Gt,
207 Ge,
208 Eq,
209 Ne,
210 BitAnd,
211 BitOr,
212 BitXor,
213 Shl,
214 Shr,
215}
216
217#[derive(Debug, Clone, Copy, PartialEq)]
219pub enum UnaryOperator {
220 Neg,
221 Not,
222 BitNot,
223 Abs,
224 Sin,
225 Cos,
226 Tan,
227 Log,
228 Exp,
229 Sqrt,
230 Floor,
231 Ceil,
232 Round,
233}
234
235#[derive(Debug, Clone)]
237pub struct EvaluationResult {
238 pub constants_found: Vec<(NodeId, ConstantValue)>,
239 pub dead_code_nodes: Vec<NodeId>,
240 pub eliminable_branches: Vec<NodeId>,
241 pub unrollable_loops: Vec<(NodeId, usize)>,
242 pub propagation_opportunities: Vec<PropagationOpportunity>,
243}
244
245#[derive(Debug, Clone)]
247pub struct PropagationOpportunity {
248 pub from_node: NodeId,
249 pub to_nodes: Vec<NodeId>,
250 pub constant_value: ConstantValue,
251 pub estimated_benefit: f64,
252}
253
254impl ConstantEvaluator {
255 pub fn new(config: ConstEvalConfig) -> Self {
257 Self {
258 config,
259 constant_cache: Arc::new(RwLock::new(HashMap::new())),
260 evaluation_context: EvaluationContext::new(),
261 }
262 }
263
264 pub fn evaluate_constants(&mut self, graph: &ComputationGraph) -> JitResult<EvaluationResult> {
266 let mut result = EvaluationResult {
267 constants_found: Vec::new(),
268 dead_code_nodes: Vec::new(),
269 eliminable_branches: Vec::new(),
270 unrollable_loops: Vec::new(),
271 propagation_opportunities: Vec::new(),
272 };
273
274 self.evaluation_context.reset();
276
277 let sorted_nodes = graph
279 .topological_sort()
280 .map_err(|e| JitError::CompilationError(format!("{:?}", e)))?;
281
282 for node_id in sorted_nodes {
283 if let Some(node) = graph.node(node_id) {
284 if let Some(constant_value) = self.try_evaluate_node(node, node_id)? {
286 result
287 .constants_found
288 .push((node_id, constant_value.clone()));
289
290 if let Ok(mut cache) = self.constant_cache.write() {
292 cache.insert(node_id, constant_value.clone());
293 }
294
295 result.propagation_opportunities.extend(
297 self.analyze_propagation_opportunities(graph, node_id, &constant_value)?,
298 );
299 }
300
301 if self.is_dead_code(node)? {
303 result.dead_code_nodes.push(node_id);
304 }
305
306 if self.is_eliminable_branch(node)? {
308 result.eliminable_branches.push(node_id);
309 }
310
311 if let Some(iterations) = self.get_unroll_count(node)? {
313 result.unrollable_loops.push((node_id, iterations));
314 }
315 }
316 }
317
318 Ok(result)
319 }
320
321 pub fn apply_optimizations(
323 &self,
324 graph: &mut ComputationGraph,
325 result: &EvaluationResult,
326 ) -> JitResult<usize> {
327 let mut optimizations_applied = 0;
328
329 if self.config.enable_constant_folding {
331 optimizations_applied += self.apply_constant_folding(graph, &result.constants_found)?;
332 }
333
334 if self.config.enable_dead_code_elimination {
336 optimizations_applied +=
337 self.apply_dead_code_elimination(graph, &result.dead_code_nodes)?;
338 }
339
340 if self.config.enable_branch_elimination {
342 optimizations_applied +=
343 self.apply_branch_elimination(graph, &result.eliminable_branches)?;
344 }
345
346 if self.config.enable_loop_unrolling {
348 optimizations_applied += self.apply_loop_unrolling(graph, &result.unrollable_loops)?;
349 }
350
351 if self.config.enable_aggressive_propagation {
353 optimizations_applied +=
354 self.apply_constant_propagation(graph, &result.propagation_opportunities)?;
355 }
356
357 Ok(optimizations_applied)
358 }
359
360 fn try_evaluate_node(
362 &mut self,
363 node: &Node,
364 node_id: NodeId,
365 ) -> JitResult<Option<ConstantValue>> {
366 if let Ok(cache) = self.constant_cache.read() {
368 if let Some(cached_value) = cache.get(&node_id) {
369 return Ok(Some(cached_value.clone()));
370 }
371 }
372
373 if self.evaluation_context.steps >= self.config.max_evaluation_steps {
375 return Ok(None);
376 }
377
378 if self.evaluation_context.depth >= self.config.max_recursion_depth {
379 return Ok(None);
380 }
381
382 if self.evaluation_context.memory_usage >= self.config.max_memory_usage {
383 return Ok(None);
384 }
385
386 self.evaluation_context.steps += 1;
387 self.evaluation_context.depth += 1;
388
389 let result = match node.operation_type() {
390 "constant" => self.evaluate_constant_node(node),
391 "add" => self.evaluate_binary_op(node, BinaryOperator::Add),
392 "sub" => self.evaluate_binary_op(node, BinaryOperator::Sub),
393 "mul" => self.evaluate_binary_op(node, BinaryOperator::Mul),
394 "div" => self.evaluate_binary_op(node, BinaryOperator::Div),
395 "pow" => self.evaluate_binary_op(node, BinaryOperator::Pow),
396 "neg" => self.evaluate_unary_op(node, UnaryOperator::Neg),
397 "abs" => self.evaluate_unary_op(node, UnaryOperator::Abs),
398 "sin" => self.evaluate_unary_op(node, UnaryOperator::Sin),
399 "cos" => self.evaluate_unary_op(node, UnaryOperator::Cos),
400 "exp" => self.evaluate_unary_op(node, UnaryOperator::Exp),
401 "log" => self.evaluate_unary_op(node, UnaryOperator::Log),
402 "sqrt" => self.evaluate_unary_op(node, UnaryOperator::Sqrt),
403 _ => Ok(None), };
405
406 self.evaluation_context.depth -= 1;
407 result
408 }
409
410 fn evaluate_constant_node(&self, node: &Node) -> JitResult<Option<ConstantValue>> {
411 if let Some(value_attr) = node.get_attribute("value") {
412 let value_str = match value_attr {
413 crate::graph::Attribute::String(s) => s,
414 crate::graph::Attribute::Int(i) => return Ok(Some(ConstantValue::Int(*i))),
415 crate::graph::Attribute::Float(f) => return Ok(Some(ConstantValue::Float(*f))),
416 crate::graph::Attribute::Bool(b) => return Ok(Some(ConstantValue::Bool(*b))),
417 _ => return Ok(None),
418 };
419
420 if value_str == "true" {
422 Ok(Some(ConstantValue::Bool(true)))
423 } else if value_str == "false" {
424 Ok(Some(ConstantValue::Bool(false)))
425 } else if let Ok(int_val) = value_str.parse::<i64>() {
426 Ok(Some(ConstantValue::Int(int_val)))
427 } else if let Ok(float_val) = value_str.parse::<f64>() {
428 Ok(Some(ConstantValue::Float(float_val)))
429 } else {
430 Ok(Some(ConstantValue::String(value_str.clone())))
431 }
432 } else {
433 Ok(None)
434 }
435 }
436
437 fn evaluate_binary_op(
438 &mut self,
439 _node: &Node,
440 _op: BinaryOperator,
441 ) -> JitResult<Option<ConstantValue>> {
442 Ok(None)
445 }
446
447 fn evaluate_unary_op(
448 &mut self,
449 _node: &Node,
450 _op: UnaryOperator,
451 ) -> JitResult<Option<ConstantValue>> {
452 Ok(None)
455 }
456
457 fn apply_binary_operation(
458 &self,
459 op: BinaryOperator,
460 left: &ConstantValue,
461 right: &ConstantValue,
462 ) -> JitResult<Option<ConstantValue>> {
463 match (left, right) {
464 (ConstantValue::Int(a), ConstantValue::Int(b)) => {
465 let result = match op {
466 BinaryOperator::Add => ConstantValue::Int(a + b),
467 BinaryOperator::Sub => ConstantValue::Int(a - b),
468 BinaryOperator::Mul => ConstantValue::Int(a * b),
469 BinaryOperator::Div => {
470 if *b != 0 {
471 ConstantValue::Int(a / b)
472 } else {
473 return Ok(None); }
475 }
476 BinaryOperator::Mod => {
477 if *b != 0 {
478 ConstantValue::Int(a % b)
479 } else {
480 return Ok(None); }
482 }
483 BinaryOperator::Pow => ConstantValue::Float((*a as f64).powf(*b as f64)),
484 BinaryOperator::Lt => ConstantValue::Bool(a < b),
485 BinaryOperator::Le => ConstantValue::Bool(a <= b),
486 BinaryOperator::Gt => ConstantValue::Bool(a > b),
487 BinaryOperator::Ge => ConstantValue::Bool(a >= b),
488 BinaryOperator::Eq => ConstantValue::Bool(a == b),
489 BinaryOperator::Ne => ConstantValue::Bool(a != b),
490 BinaryOperator::BitAnd => ConstantValue::Int(a & b),
491 BinaryOperator::BitOr => ConstantValue::Int(a | b),
492 BinaryOperator::BitXor => ConstantValue::Int(a ^ b),
493 _ => return Ok(None),
494 };
495 Ok(Some(result))
496 }
497 (ConstantValue::Float(a), ConstantValue::Float(b)) => {
498 let result = match op {
499 BinaryOperator::Add => ConstantValue::Float(a + b),
500 BinaryOperator::Sub => ConstantValue::Float(a - b),
501 BinaryOperator::Mul => ConstantValue::Float(a * b),
502 BinaryOperator::Div => {
503 if *b != 0.0 {
504 ConstantValue::Float(a / b)
505 } else {
506 return Ok(None); }
508 }
509 BinaryOperator::Pow => ConstantValue::Float(a.powf(*b)),
510 BinaryOperator::Lt => ConstantValue::Bool(a < b),
511 BinaryOperator::Le => ConstantValue::Bool(a <= b),
512 BinaryOperator::Gt => ConstantValue::Bool(a > b),
513 BinaryOperator::Ge => ConstantValue::Bool(a >= b),
514 BinaryOperator::Eq => ConstantValue::Bool((a - b).abs() < f64::EPSILON),
515 BinaryOperator::Ne => ConstantValue::Bool((a - b).abs() >= f64::EPSILON),
516 _ => return Ok(None),
517 };
518 Ok(Some(result))
519 }
520 (ConstantValue::Bool(a), ConstantValue::Bool(b)) => {
521 let result = match op {
522 BinaryOperator::And => ConstantValue::Bool(*a && *b),
523 BinaryOperator::Or => ConstantValue::Bool(*a || *b),
524 BinaryOperator::Xor => ConstantValue::Bool(*a ^ *b),
525 BinaryOperator::Eq => ConstantValue::Bool(a == b),
526 BinaryOperator::Ne => ConstantValue::Bool(a != b),
527 _ => return Ok(None),
528 };
529 Ok(Some(result))
530 }
531 (ConstantValue::Int(a), ConstantValue::Float(_b)) => {
533 self.apply_binary_operation(op, &ConstantValue::Float(*a as f64), right)
534 }
535 (ConstantValue::Float(_a), ConstantValue::Int(b)) => {
536 self.apply_binary_operation(op, left, &ConstantValue::Float(*b as f64))
537 }
538 _ => Ok(None), }
540 }
541
542 fn apply_unary_operation(
543 &self,
544 op: UnaryOperator,
545 value: &ConstantValue,
546 ) -> JitResult<Option<ConstantValue>> {
547 match value {
548 ConstantValue::Int(a) => {
549 let result = match op {
550 UnaryOperator::Neg => ConstantValue::Int(-a),
551 UnaryOperator::Abs => ConstantValue::Int(a.abs()),
552 UnaryOperator::BitNot => ConstantValue::Int(!a),
553 _ => return Ok(None),
554 };
555 Ok(Some(result))
556 }
557 ConstantValue::Float(a) => {
558 let result = match op {
559 UnaryOperator::Neg => ConstantValue::Float(-a),
560 UnaryOperator::Abs => ConstantValue::Float(a.abs()),
561 UnaryOperator::Sin => ConstantValue::Float(a.sin()),
562 UnaryOperator::Cos => ConstantValue::Float(a.cos()),
563 UnaryOperator::Tan => ConstantValue::Float(a.tan()),
564 UnaryOperator::Log => {
565 if *a > 0.0 {
566 ConstantValue::Float(a.ln())
567 } else {
568 return Ok(None); }
570 }
571 UnaryOperator::Exp => ConstantValue::Float(a.exp()),
572 UnaryOperator::Sqrt => {
573 if *a >= 0.0 {
574 ConstantValue::Float(a.sqrt())
575 } else {
576 return Ok(None); }
578 }
579 UnaryOperator::Floor => ConstantValue::Float(a.floor()),
580 UnaryOperator::Ceil => ConstantValue::Float(a.ceil()),
581 UnaryOperator::Round => ConstantValue::Float(a.round()),
582 _ => return Ok(None),
583 };
584 Ok(Some(result))
585 }
586 ConstantValue::Bool(a) => {
587 let result = match op {
588 UnaryOperator::Not => ConstantValue::Bool(!a),
589 _ => return Ok(None),
590 };
591 Ok(Some(result))
592 }
593 _ => Ok(None),
594 }
595 }
596
597 fn is_dead_code(&self, node: &Node) -> JitResult<bool> {
598 if node.has_side_effects() {
600 return Ok(false);
601 }
602
603 Ok(false)
607 }
608
609 fn is_eliminable_branch(&self, node: &Node) -> JitResult<bool> {
610 if node.operation_type() != "branch" && node.operation_type() != "if" {
611 return Ok(false);
612 }
613
614 Ok(false)
620 }
621
622 fn get_unroll_count(&self, node: &Node) -> JitResult<Option<usize>> {
623 if node.operation_type() != "loop" && node.operation_type() != "for" {
624 return Ok(None);
625 }
626
627 if let Some(iterations_attr) = node.get_attribute("iterations") {
629 let iterations = match iterations_attr {
630 crate::graph::Attribute::Int(i) => *i as usize,
631 crate::graph::Attribute::String(s) => {
632 if let Ok(val) = s.parse::<usize>() {
633 val
634 } else {
635 return Ok(None);
636 }
637 }
638 _ => return Ok(None),
639 };
640
641 if iterations <= 16 {
643 return Ok(Some(iterations));
644 }
645 }
646
647 Ok(None)
648 }
649
650 fn analyze_propagation_opportunities(
651 &self,
652 graph: &ComputationGraph,
653 constant_node_id: NodeId,
654 constant_value: &ConstantValue,
655 ) -> JitResult<Vec<PropagationOpportunity>> {
656 let mut opportunities = Vec::new();
657
658 if let Some(_constant_node) = graph.get_node(constant_node_id) {
659 let outputs = graph.get_node_outputs(constant_node_id);
660 let mut to_nodes = Vec::new();
661
662 for output_id in outputs {
663 if let Some(output_node) = graph.get_node(output_id) {
664 if self.can_benefit_from_constant(output_node, constant_value) {
666 to_nodes.push(output_id);
667 }
668 }
669 }
670
671 if !to_nodes.is_empty() {
672 let estimated_benefit =
673 self.estimate_propagation_benefit(&to_nodes, constant_value);
674 opportunities.push(PropagationOpportunity {
675 from_node: constant_node_id,
676 to_nodes,
677 constant_value: constant_value.clone(),
678 estimated_benefit,
679 });
680 }
681 }
682
683 Ok(opportunities)
684 }
685
686 fn can_benefit_from_constant(&self, node: &Node, _constant_value: &ConstantValue) -> bool {
687 match node.operation_type() {
689 "add" | "sub" | "mul" | "div" | "pow" => true,
690 "branch" | "if" => true,
691 "loop" | "for" => true,
692 _ => false,
693 }
694 }
695
696 fn estimate_propagation_benefit(
697 &self,
698 _to_nodes: &[NodeId],
699 _constant_value: &ConstantValue,
700 ) -> f64 {
701 0.1 * _to_nodes.len() as f64
703 }
704
705 fn apply_constant_folding(
707 &self,
708 graph: &mut ComputationGraph,
709 constants: &[(NodeId, ConstantValue)],
710 ) -> JitResult<usize> {
711 let mut applied = 0;
712
713 for (node_id, constant_value) in constants {
714 if let Some(node) = graph.get_node_mut(*node_id) {
715 let graph_constant_value = match constant_value {
717 ConstantValue::Int(i) => crate::graph::ConstantValue::IntScalar(*i),
718 ConstantValue::Float(f) => crate::graph::ConstantValue::Scalar(*f),
719 _ => crate::graph::ConstantValue::Scalar(0.0), };
721 node.op = crate::graph::Operation::Constant(crate::graph::ConstantInfo {
722 value: graph_constant_value,
723 });
724 node.set_attribute(
725 "value".to_string(),
726 match constant_value {
727 ConstantValue::Bool(b) => crate::graph::Attribute::Bool(*b),
728 ConstantValue::Int(i) => crate::graph::Attribute::Int(*i),
729 ConstantValue::Float(f) => crate::graph::Attribute::Float(*f),
730 ConstantValue::String(s) => crate::graph::Attribute::String(s.clone()),
731 _ => crate::graph::Attribute::String(constant_value.to_string()),
732 },
733 );
734 applied += 1;
735 }
736 }
737
738 Ok(applied)
739 }
740
741 fn apply_dead_code_elimination(
742 &self,
743 graph: &mut ComputationGraph,
744 dead_nodes: &[NodeId],
745 ) -> JitResult<usize> {
746 let mut applied = 0;
747
748 for &node_id in dead_nodes {
749 if graph.remove_node(node_id).is_some() {
750 applied += 1;
751 }
752 }
753
754 Ok(applied)
755 }
756
757 fn apply_branch_elimination(
758 &self,
759 graph: &mut ComputationGraph,
760 eliminable_branches: &[NodeId],
761 ) -> JitResult<usize> {
762 let mut applied = 0;
763
764 for &node_id in eliminable_branches {
765 if let Some(_node) = graph.node(node_id) {
766 let inputs = graph.get_node_inputs(node_id);
767 if !inputs.is_empty() {
768 if let Ok(cache) = self.constant_cache.read() {
769 if let Some(ConstantValue::Bool(condition)) = cache.get(&inputs[0]) {
770 let branch_index = if *condition { 1 } else { 2 };
772 if inputs.len() > branch_index {
773 match graph.replace_node_with_input(node_id, inputs[branch_index]) {
775 Ok(_) => {
776 log::debug!(
777 "Successfully eliminated branch by replacing with {}",
778 if *condition { "true" } else { "false" }
779 );
780 applied += 1;
781 }
782 Err(e) => {
783 log::warn!("Failed to eliminate branch: {}", e);
784 }
785 }
786 }
787 }
788 }
789 }
790 }
791 }
792
793 Ok(applied)
794 }
795
796 fn apply_loop_unrolling(
797 &self,
798 graph: &mut ComputationGraph,
799 unrollable_loops: &[(NodeId, usize)],
800 ) -> JitResult<usize> {
801 let mut applied = 0;
802
803 for &(node_id, iterations) in unrollable_loops {
804 if let Some(loop_node) = graph.get_node(node_id) {
805 if let Some(loop_body) = loop_node.get_attribute("body") {
807 let body_str = match loop_body {
808 crate::graph::Attribute::String(s) => s,
809 _ => continue,
810 };
811 let unrolled_body = self.create_unrolled_body(body_str, iterations)?;
812
813 match graph.replace_node_with_sequence(node_id, &unrolled_body) {
815 Ok(_) => {
816 log::debug!(
817 "Successfully unrolled loop with {} iterations",
818 iterations
819 );
820 applied += 1;
821 }
822 Err(e) => {
823 log::warn!("Failed to unroll loop: {}", e);
824 }
825 }
826 }
827 }
828 }
829
830 Ok(applied)
831 }
832
833 fn apply_constant_propagation(
834 &self,
835 _graph: &mut ComputationGraph,
836 _opportunities: &[PropagationOpportunity],
837 ) -> JitResult<usize> {
838 Ok(0)
841 }
842
843 fn create_unrolled_body(
844 &self,
845 _loop_body: &str,
846 iterations: usize,
847 ) -> JitResult<Vec<crate::Node>> {
848 use crate::graph::{Node, Operation};
849 use torsh_core::{DType, DeviceType, Shape};
850
851 let mut unrolled_nodes = Vec::new();
853
854 for i in 0..iterations {
855 let node = Node::new(Operation::Input, format!("unrolled_iter_{}", i))
861 .with_output_shapes(vec![Some(Shape::new(vec![1]))])
862 .with_dtypes(vec![DType::F32])
863 .with_device(DeviceType::Cpu);
864
865 unrolled_nodes.push(node);
866 }
867
868 Ok(unrolled_nodes)
869 }
870}
871
872impl EvaluationContext {
873 fn new() -> Self {
874 Self {
875 variables: HashMap::new(),
876 functions: HashMap::new(),
877 depth: 0,
878 steps: 0,
879 memory_usage: 0,
880 }
881 }
882
883 fn reset(&mut self) {
884 self.variables.clear();
885 self.depth = 0;
886 self.steps = 0;
887 self.memory_usage = 0;
888 }
889}
890
891impl ConstantValue {
892 pub fn to_string(&self) -> String {
894 match self {
895 ConstantValue::Bool(b) => b.to_string(),
896 ConstantValue::Int(i) => i.to_string(),
897 ConstantValue::UInt(u) => u.to_string(),
898 ConstantValue::Float(f) => f.to_string(),
899 ConstantValue::String(s) => s.clone(),
900 ConstantValue::Array(arr) => {
901 format!(
902 "[{}]",
903 arr.iter()
904 .map(|v| v.to_string())
905 .collect::<Vec<_>>()
906 .join(", ")
907 )
908 }
909 ConstantValue::Tensor { shape, data, dtype } => {
910 format!("Tensor({:?}, {:?}, {})", shape, data, dtype)
911 }
912 ConstantValue::Complex { real, imag } => {
913 format!("{}+{}i", real, imag)
914 }
915 ConstantValue::None => "None".to_string(),
916 ConstantValue::Undefined => "Undefined".to_string(),
917 }
918 }
919
920 pub fn is_truthy(&self) -> bool {
922 match self {
923 ConstantValue::Bool(b) => *b,
924 ConstantValue::Int(i) => *i != 0,
925 ConstantValue::UInt(u) => *u != 0,
926 ConstantValue::Float(f) => *f != 0.0,
927 ConstantValue::String(s) => !s.is_empty(),
928 ConstantValue::Array(arr) => !arr.is_empty(),
929 ConstantValue::Tensor { data, .. } => !data.is_empty(),
930 ConstantValue::Complex { real, imag } => *real != 0.0 || *imag != 0.0,
931 ConstantValue::None => false,
932 ConstantValue::Undefined => false,
933 }
934 }
935
936 pub fn type_name(&self) -> &'static str {
938 match self {
939 ConstantValue::Bool(_) => "bool",
940 ConstantValue::Int(_) => "int",
941 ConstantValue::UInt(_) => "uint",
942 ConstantValue::Float(_) => "float",
943 ConstantValue::String(_) => "string",
944 ConstantValue::Array(_) => "array",
945 ConstantValue::Tensor { .. } => "tensor",
946 ConstantValue::Complex { .. } => "complex",
947 ConstantValue::None => "none",
948 ConstantValue::Undefined => "undefined",
949 }
950 }
951}
952
953#[cfg(test)]
954mod tests {
955 use super::*;
956
957 #[test]
958 fn test_constant_evaluator_creation() {
959 let config = ConstEvalConfig::default();
960 let evaluator = ConstantEvaluator::new(config);
961 assert!(evaluator.config.enable_constant_folding);
962 }
963
964 #[test]
965 fn test_binary_operations() {
966 let evaluator = ConstantEvaluator::new(ConstEvalConfig::default());
967
968 let left = ConstantValue::Int(5);
969 let right = ConstantValue::Int(3);
970
971 let result = evaluator
972 .apply_binary_operation(BinaryOperator::Add, &left, &right)
973 .unwrap()
974 .unwrap();
975 assert_eq!(result, ConstantValue::Int(8));
976
977 let result = evaluator
978 .apply_binary_operation(BinaryOperator::Mul, &left, &right)
979 .unwrap()
980 .unwrap();
981 assert_eq!(result, ConstantValue::Int(15));
982 }
983
984 #[test]
985 fn test_unary_operations() {
986 let evaluator = ConstantEvaluator::new(ConstEvalConfig::default());
987
988 let value = ConstantValue::Float(4.0);
989 let result = evaluator
990 .apply_unary_operation(UnaryOperator::Sqrt, &value)
991 .unwrap()
992 .unwrap();
993 assert_eq!(result, ConstantValue::Float(2.0));
994
995 let value = ConstantValue::Int(-5);
996 let result = evaluator
997 .apply_unary_operation(UnaryOperator::Abs, &value)
998 .unwrap()
999 .unwrap();
1000 assert_eq!(result, ConstantValue::Int(5));
1001 }
1002
1003 #[test]
1004 fn test_constant_value_operations() {
1005 let bool_val = ConstantValue::Bool(true);
1006 assert!(bool_val.is_truthy());
1007 assert_eq!(bool_val.type_name(), "bool");
1008
1009 let int_val = ConstantValue::Int(42);
1010 assert_eq!(int_val.to_string(), "42");
1011
1012 let float_val = ConstantValue::Float(3.14);
1013 assert_eq!(float_val.type_name(), "float");
1014 }
1015
1016 #[test]
1017 fn test_evaluation_context() {
1018 let mut context = EvaluationContext::new();
1019 assert_eq!(context.depth, 0);
1020 assert_eq!(context.steps, 0);
1021
1022 context.depth = 5;
1023 context.steps = 100;
1024 context.reset();
1025
1026 assert_eq!(context.depth, 0);
1027 assert_eq!(context.steps, 0);
1028 }
1029}