Skip to main content

quantrs2_symengine_pure/scirs2_bridge/
complex.rs

1//! Complex number integration with SciRS2.
2//!
3//! This module provides conversion between symbolic expressions and
4//! SciRS2's Complex64 type.
5
6use scirs2_core::Complex64;
7
8use crate::error::{SymEngineError, SymEngineResult};
9use crate::expr::Expression;
10
11/// Convert a symbolic expression to a Complex64 if possible.
12///
13/// # Errors
14/// Returns an error if the expression cannot be evaluated to a complex number.
15pub fn to_complex64(expr: &Expression) -> SymEngineResult<Complex64> {
16    // First try to evaluate as real
17    if let Some(re) = expr.to_f64() {
18        return Ok(Complex64::new(re, 0.0));
19    }
20
21    // Check if it's a pure imaginary (i * value)
22    // This is a simplified check - full implementation would parse the expression tree
23
24    Err(SymEngineError::eval(
25        "Cannot convert symbolic expression to Complex64 - try evaluating with specific values",
26    ))
27}
28
29/// Create an expression from a Complex64.
30///
31/// Returns a simplified expression when possible (e.g., real for pure real numbers).
32pub fn from_complex64(c: Complex64) -> Expression {
33    Expression::from_complex64(c)
34}
35
36/// Evaluate a symbolic expression to a Complex64 with given variable values.
37///
38/// Supports full complex arithmetic including expressions containing the
39/// imaginary unit `I`, complex variable substitution, and all standard
40/// mathematical functions.
41///
42/// # Arguments
43/// * `expr` - The expression to evaluate
44/// * `values` - Map of variable names to complex values
45///
46/// # Errors
47/// Returns an error if evaluation fails (undefined variable, division by zero, etc.).
48pub fn eval_complex(
49    expr: &Expression,
50    values: &std::collections::HashMap<String, Complex64>,
51) -> SymEngineResult<Complex64> {
52    crate::eval::evaluate_complex_with_complex_values(expr, values)
53}
54
55/// Create complex arithmetic expressions.
56pub mod complex_ops {
57    use super::*;
58
59    /// Create a complex number expression a + bi
60    pub fn complex(re: f64, im: f64) -> Expression {
61        Expression::from_complex64(Complex64::new(re, im))
62    }
63
64    /// Create a pure imaginary number i*b
65    pub fn imag(b: f64) -> Expression {
66        Expression::float_unchecked(b) * Expression::i()
67    }
68
69    /// Polar form: r * e^(iθ)
70    pub fn polar(r: f64, theta: f64) -> Expression {
71        let c = Complex64::from_polar(r, theta);
72        Expression::from_complex64(c)
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn test_from_complex64_real() {
82        let c = Complex64::new(2.5, 0.0);
83        let expr = from_complex64(c);
84        assert!(expr.is_number());
85    }
86
87    #[test]
88    fn test_from_complex64_pure_imag() {
89        let c = Complex64::new(0.0, 2.0);
90        let expr = from_complex64(c);
91        // Should be 2 * I
92        assert!(!expr.is_symbol());
93    }
94
95    #[test]
96    fn test_from_complex64_general() {
97        let c = Complex64::new(3.0, 4.0);
98        let expr = from_complex64(c);
99        // Should be 3 + 4*I
100        assert!(!expr.is_symbol());
101    }
102
103    #[test]
104    fn test_complex_ops() {
105        use complex_ops::*;
106
107        let c = complex(1.0, 2.0);
108        let i = imag(3.0);
109        let p = polar(1.0, std::f64::consts::FRAC_PI_2);
110
111        assert!(!c.is_symbol());
112        assert!(!i.is_symbol());
113        assert!(!p.is_symbol());
114    }
115
116    // =========================================================================
117    // eval_complex tests (Stub 2c)
118    // =========================================================================
119
120    #[test]
121    fn test_eval_complex_pure_real() {
122        // Expression 7.0 with no variables → Complex(7.0, 0.0)
123        let expr = Expression::float_unchecked(7.0);
124        let values = std::collections::HashMap::new();
125        let result = eval_complex(&expr, &values).expect("should evaluate pure real");
126        assert!((result.re - 7.0).abs() < 1e-10);
127        assert!(result.im.abs() < 1e-10);
128    }
129
130    #[test]
131    fn test_eval_complex_real_plus_imag() {
132        // Expression 2.0 + 3.0*I → Complex(2.0, 3.0)
133        let two = Expression::float_unchecked(2.0);
134        let three = Expression::float_unchecked(3.0);
135        let i = Expression::i();
136        let expr = two + three * i;
137
138        let values = std::collections::HashMap::new();
139        let result = eval_complex(&expr, &values).expect("should evaluate 2+3i");
140        assert!((result.re - 2.0).abs() < 1e-10);
141        assert!((result.im - 3.0).abs() < 1e-10);
142    }
143
144    #[test]
145    fn test_eval_complex_with_complex_var() {
146        // z where z = 1 + 2i
147        let z = Expression::symbol("z");
148        let mut values = std::collections::HashMap::new();
149        values.insert("z".to_string(), Complex64::new(1.0, 2.0));
150
151        let result = eval_complex(&z, &values).expect("should evaluate complex variable");
152        assert!((result.re - 1.0).abs() < 1e-10);
153        assert!((result.im - 2.0).abs() < 1e-10);
154    }
155
156    #[test]
157    fn test_eval_complex_pure_imaginary() {
158        // Expression 5.0 * I → Complex(0.0, 5.0)
159        let five = Expression::float_unchecked(5.0);
160        let i = Expression::i();
161        let expr = five * i;
162
163        let values = std::collections::HashMap::new();
164        let result = eval_complex(&expr, &values).expect("should evaluate pure imaginary");
165        assert!(result.re.abs() < 1e-10);
166        assert!((result.im - 5.0).abs() < 1e-10);
167    }
168
169    #[test]
170    fn test_eval_complex_i_squared() {
171        // I * I = -1
172        let i1 = Expression::i();
173        let i2 = Expression::i();
174        let expr = i1 * i2;
175
176        let values = std::collections::HashMap::new();
177        let result = eval_complex(&expr, &values).expect("I*I should be -1");
178        assert!((result.re - (-1.0)).abs() < 1e-10);
179        assert!(result.im.abs() < 1e-10);
180    }
181}