Skip to main content

quantrs2_core/
symbolic.rs

1//! Symbolic computation module for QuantRS2
2//!
3//! This module provides symbolic computation capabilities using SymEngine,
4//! enabling symbolic parameter manipulation, calculus operations, and
5//! advanced mathematical analysis for quantum circuits and algorithms.
6
7#[cfg(feature = "symbolic")]
8pub use quantrs2_symengine_pure::{Expression as SymEngine, SymEngineError, SymEngineResult};
9
10use crate::error::{QuantRS2Error, QuantRS2Result};
11use scirs2_core::num_traits::{One, Zero}; // SciRS2 POLICY compliant
12use scirs2_core::Complex64;
13use std::collections::HashMap;
14use std::fmt;
15
16/// A symbolic expression that can represent constants, variables, or complex expressions
17#[derive(Debug, Clone, PartialEq)]
18pub enum SymbolicExpression {
19    /// Constant floating-point value
20    Constant(f64),
21
22    /// Complex constant value
23    ComplexConstant(Complex64),
24
25    /// Variable with a name
26    Variable(String),
27
28    /// SymEngine expression (only available with "symbolic" feature)
29    #[cfg(feature = "symbolic")]
30    SymEngine(SymEngine),
31
32    /// Simple arithmetic expression for when SymEngine is not available
33    #[cfg(not(feature = "symbolic"))]
34    Simple(SimpleExpression),
35}
36
37/// Simple expression representation for when SymEngine is not available
38#[cfg(not(feature = "symbolic"))]
39#[derive(Debug, Clone, PartialEq)]
40pub enum SimpleExpression {
41    Add(Box<SymbolicExpression>, Box<SymbolicExpression>),
42    Sub(Box<SymbolicExpression>, Box<SymbolicExpression>),
43    Mul(Box<SymbolicExpression>, Box<SymbolicExpression>),
44    Div(Box<SymbolicExpression>, Box<SymbolicExpression>),
45    Pow(Box<SymbolicExpression>, Box<SymbolicExpression>),
46    Sin(Box<SymbolicExpression>),
47    Cos(Box<SymbolicExpression>),
48    Exp(Box<SymbolicExpression>),
49    Log(Box<SymbolicExpression>),
50}
51
52impl SymbolicExpression {
53    /// Create a constant expression
54    pub const fn constant(value: f64) -> Self {
55        Self::Constant(value)
56    }
57
58    pub const fn zero() -> Self {
59        Self::Constant(0.0)
60    }
61
62    /// Create a complex constant expression
63    pub const fn complex_constant(value: Complex64) -> Self {
64        Self::ComplexConstant(value)
65    }
66
67    /// Create a variable expression
68    pub fn variable(name: &str) -> Self {
69        Self::Variable(name.to_string())
70    }
71
72    /// Create a SymEngine expression (requires "symbolic" feature)
73    #[cfg(feature = "symbolic")]
74    pub const fn from_symengine(expr: SymEngine) -> Self {
75        Self::SymEngine(expr)
76    }
77
78    /// Parse an expression from a string
79    pub fn parse(expr: &str) -> QuantRS2Result<Self> {
80        #[cfg(feature = "symbolic")]
81        {
82            match quantrs2_symengine_pure::parser::parse(expr) {
83                Ok(sym_expr) => Ok(Self::SymEngine(sym_expr)),
84                Err(_) => {
85                    // Fallback to simple parsing
86                    Self::parse_simple(expr)
87                }
88            }
89        }
90
91        #[cfg(not(feature = "symbolic"))]
92        {
93            Self::parse_simple(expr)
94        }
95    }
96
97    /// Simple expression parsing (fallback)
98    fn parse_simple(expr: &str) -> QuantRS2Result<Self> {
99        let trimmed = expr.trim();
100
101        // Try to parse as a number
102        if let Ok(value) = trimmed.parse::<f64>() {
103            return Ok(Self::Constant(value));
104        }
105
106        // Otherwise treat as a variable
107        Ok(Self::Variable(trimmed.to_string()))
108    }
109
110    /// Evaluate the expression with given variable values
111    pub fn evaluate(&self, variables: &HashMap<String, f64>) -> QuantRS2Result<f64> {
112        match self {
113            Self::Constant(value) => Ok(*value),
114            Self::ComplexConstant(value) => {
115                if value.im.abs() < 1e-12 {
116                    Ok(value.re)
117                } else {
118                    Err(QuantRS2Error::InvalidInput(
119                        "Cannot evaluate complex expression to real number".to_string(),
120                    ))
121                }
122            }
123            Self::Variable(name) => variables
124                .get(name)
125                .copied()
126                .ok_or_else(|| QuantRS2Error::InvalidInput(format!("Variable '{name}' not found"))),
127
128            #[cfg(feature = "symbolic")]
129            Self::SymEngine(expr) => expr
130                .eval(variables)
131                .map_err(|e| QuantRS2Error::UnsupportedOperation(e.to_string())),
132
133            #[cfg(not(feature = "symbolic"))]
134            Self::Simple(simple_expr) => Self::evaluate_simple(simple_expr, variables),
135        }
136    }
137
138    /// Evaluate complex expression with given variable values
139    pub fn evaluate_complex(
140        &self,
141        variables: &HashMap<String, Complex64>,
142    ) -> QuantRS2Result<Complex64> {
143        match self {
144            Self::Constant(value) => Ok(Complex64::new(*value, 0.0)),
145            Self::ComplexConstant(value) => Ok(*value),
146            Self::Variable(name) => variables
147                .get(name)
148                .copied()
149                .ok_or_else(|| QuantRS2Error::InvalidInput(format!("Variable '{name}' not found"))),
150
151            #[cfg(feature = "symbolic")]
152            Self::SymEngine(expr) => {
153                quantrs2_symengine_pure::eval::evaluate_complex_with_complex_values(expr, variables)
154                    .map_err(|e| QuantRS2Error::UnsupportedOperation(e.to_string()))
155            }
156
157            #[cfg(not(feature = "symbolic"))]
158            Self::Simple(simple_expr) => Self::evaluate_simple_complex(simple_expr, variables),
159        }
160    }
161
162    #[cfg(not(feature = "symbolic"))]
163    fn evaluate_simple(
164        expr: &SimpleExpression,
165        variables: &HashMap<String, f64>,
166    ) -> QuantRS2Result<f64> {
167        match expr {
168            SimpleExpression::Add(a, b) => Ok(a.evaluate(variables)? + b.evaluate(variables)?),
169            SimpleExpression::Sub(a, b) => Ok(a.evaluate(variables)? - b.evaluate(variables)?),
170            SimpleExpression::Mul(a, b) => Ok(a.evaluate(variables)? * b.evaluate(variables)?),
171            SimpleExpression::Div(a, b) => {
172                let b_val = b.evaluate(variables)?;
173                if b_val.abs() < 1e-12 {
174                    Err(QuantRS2Error::DivisionByZero)
175                } else {
176                    Ok(a.evaluate(variables)? / b_val)
177                }
178            }
179            SimpleExpression::Pow(a, b) => Ok(a.evaluate(variables)?.powf(b.evaluate(variables)?)),
180            SimpleExpression::Sin(a) => Ok(a.evaluate(variables)?.sin()),
181            SimpleExpression::Cos(a) => Ok(a.evaluate(variables)?.cos()),
182            SimpleExpression::Exp(a) => Ok(a.evaluate(variables)?.exp()),
183            SimpleExpression::Log(a) => {
184                let a_val = a.evaluate(variables)?;
185                if a_val <= 0.0 {
186                    Err(QuantRS2Error::InvalidInput(
187                        "Logarithm of non-positive number".to_string(),
188                    ))
189                } else {
190                    Ok(a_val.ln())
191                }
192            }
193        }
194    }
195
196    #[cfg(not(feature = "symbolic"))]
197    fn evaluate_simple_complex(
198        expr: &SimpleExpression,
199        variables: &HashMap<String, Complex64>,
200    ) -> QuantRS2Result<Complex64> {
201        // Convert variables to real for this simple implementation
202        let real_vars: HashMap<String, f64> = variables
203            .iter()
204            .filter_map(|(k, v)| {
205                if v.im.abs() < 1e-12 {
206                    Some((k.clone(), v.re))
207                } else {
208                    None
209                }
210            })
211            .collect();
212
213        let real_result = Self::evaluate_simple(expr, &real_vars)?;
214        Ok(Complex64::new(real_result, 0.0))
215    }
216
217    /// Get all variable names in the expression
218    pub fn variables(&self) -> Vec<String> {
219        match self {
220            Self::Constant(_) | Self::ComplexConstant(_) => Vec::new(),
221            Self::Variable(name) => vec![name.clone()],
222
223            #[cfg(feature = "symbolic")]
224            Self::SymEngine(expr) => {
225                let mut vars: Vec<String> = expr.free_symbols().into_iter().collect();
226                vars.sort();
227                vars
228            }
229
230            #[cfg(not(feature = "symbolic"))]
231            Self::Simple(simple_expr) => Self::variables_simple(simple_expr),
232        }
233    }
234
235    #[cfg(not(feature = "symbolic"))]
236    fn variables_simple(expr: &SimpleExpression) -> Vec<String> {
237        match expr {
238            SimpleExpression::Add(a, b)
239            | SimpleExpression::Sub(a, b)
240            | SimpleExpression::Mul(a, b)
241            | SimpleExpression::Div(a, b)
242            | SimpleExpression::Pow(a, b) => {
243                let mut vars = a.variables();
244                vars.extend(b.variables());
245                vars.sort();
246                vars.dedup();
247                vars
248            }
249            SimpleExpression::Sin(a)
250            | SimpleExpression::Cos(a)
251            | SimpleExpression::Exp(a)
252            | SimpleExpression::Log(a) => a.variables(),
253        }
254    }
255
256    /// Check if the expression is constant (has no variables)
257    pub fn is_constant(&self) -> bool {
258        match self {
259            Self::Constant(_) | Self::ComplexConstant(_) => true,
260            Self::Variable(_) => false,
261
262            #[cfg(feature = "symbolic")]
263            Self::SymEngine(expr) => expr.free_symbols().is_empty(),
264
265            #[cfg(not(feature = "symbolic"))]
266            Self::Simple(_) => false,
267        }
268    }
269
270    /// Substitute variables with expressions
271    pub fn substitute(&self, substitutions: &HashMap<String, Self>) -> QuantRS2Result<Self> {
272        match self {
273            Self::Constant(_) | Self::ComplexConstant(_) => Ok(self.clone()),
274            Self::Variable(name) => Ok(substitutions
275                .get(name)
276                .cloned()
277                .unwrap_or_else(|| self.clone())),
278
279            #[cfg(feature = "symbolic")]
280            Self::SymEngine(expr) => {
281                let mut result = expr.clone();
282                for (name, replacement) in substitutions {
283                    let var_expr = SymEngine::symbol(name);
284                    let value_expr = replacement.to_symengine_expr()?;
285                    result = result.substitute(&var_expr, &value_expr);
286                }
287                Ok(Self::SymEngine(result))
288            }
289
290            #[cfg(not(feature = "symbolic"))]
291            Self::Simple(_) => {
292                // Would implement simple expression substitution
293                Err(QuantRS2Error::UnsupportedOperation(
294                    "Simple expression substitution not yet implemented".to_string(),
295                ))
296            }
297        }
298    }
299
300    /// Convert this `SymbolicExpression` into a `quantrs2_symengine_pure::Expression`.
301    ///
302    /// Used internally for routing operations through the SymEngine backend.
303    ///
304    /// # Errors
305    /// Returns `UnsupportedOperation` when the variant cannot be losslessly converted.
306    #[cfg(feature = "symbolic")]
307    pub fn to_symengine_expr(&self) -> QuantRS2Result<SymEngine> {
308        match self {
309            Self::SymEngine(e) => Ok(e.clone()),
310            Self::Constant(c) => Ok(SymEngine::from(*c)),
311            Self::Variable(name) => Ok(SymEngine::symbol(name)),
312            Self::ComplexConstant(c) => Ok(SymEngine::from_complex64(*c)),
313        }
314    }
315
316    /// Parse an expression string using the SymEngine backend (requires `symbolic` feature).
317    ///
318    /// Falls back gracefully to a `Variable` node when parsing fails.
319    #[cfg(feature = "symbolic")]
320    pub fn from_symengine_str(input: &str) -> Self {
321        match quantrs2_symengine_pure::parser::parse(input) {
322            Ok(expr) => Self::SymEngine(expr),
323            Err(_) => {
324                Self::parse_simple(input).unwrap_or_else(|_| Self::Variable(input.to_string()))
325            }
326        }
327    }
328}
329
330// Arithmetic operations for SymbolicExpression
331impl std::ops::Add for SymbolicExpression {
332    type Output = Self;
333
334    fn add(self, rhs: Self) -> Self::Output {
335        #[cfg(feature = "symbolic")]
336        {
337            match (self, rhs) {
338                // Optimize constant addition
339                (Self::Constant(a), Self::Constant(b)) => Self::Constant(a + b),
340                (Self::SymEngine(a), Self::SymEngine(b)) => Self::SymEngine(a + b),
341                (a, b) => {
342                    // Convert to SymEngine if possible
343                    let a_sym = match a {
344                        Self::Constant(val) => SymEngine::from(val),
345                        Self::Variable(name) => SymEngine::symbol(&name),
346                        Self::SymEngine(expr) => expr,
347                        _ => return Self::Constant(0.0), // Fallback
348                    };
349                    let b_sym = match b {
350                        Self::Constant(val) => SymEngine::from(val),
351                        Self::Variable(name) => SymEngine::symbol(&name),
352                        Self::SymEngine(expr) => expr,
353                        _ => return Self::Constant(0.0), // Fallback
354                    };
355                    Self::SymEngine(a_sym + b_sym)
356                }
357            }
358        }
359
360        #[cfg(not(feature = "symbolic"))]
361        {
362            match (self, rhs) {
363                (Self::Constant(a), Self::Constant(b)) => Self::Constant(a + b),
364                (a, b) => Self::Simple(SimpleExpression::Add(Box::new(a), Box::new(b))),
365            }
366        }
367    }
368}
369
370impl std::ops::Sub for SymbolicExpression {
371    type Output = Self;
372
373    fn sub(self, rhs: Self) -> Self::Output {
374        #[cfg(feature = "symbolic")]
375        {
376            match (self, rhs) {
377                // Optimize constant subtraction
378                (Self::Constant(a), Self::Constant(b)) => Self::Constant(a - b),
379                (Self::SymEngine(a), Self::SymEngine(b)) => Self::SymEngine(a - b),
380                (a, b) => {
381                    let a_sym = match a {
382                        Self::Constant(val) => SymEngine::from(val),
383                        Self::Variable(name) => SymEngine::symbol(&name),
384                        Self::SymEngine(expr) => expr,
385                        _ => return Self::Constant(0.0),
386                    };
387                    let b_sym = match b {
388                        Self::Constant(val) => SymEngine::from(val),
389                        Self::Variable(name) => SymEngine::symbol(&name),
390                        Self::SymEngine(expr) => expr,
391                        _ => return Self::Constant(0.0),
392                    };
393                    Self::SymEngine(a_sym - b_sym)
394                }
395            }
396        }
397
398        #[cfg(not(feature = "symbolic"))]
399        {
400            match (self, rhs) {
401                (Self::Constant(a), Self::Constant(b)) => Self::Constant(a - b),
402                (a, b) => Self::Simple(SimpleExpression::Sub(Box::new(a), Box::new(b))),
403            }
404        }
405    }
406}
407
408impl std::ops::Mul for SymbolicExpression {
409    type Output = Self;
410
411    fn mul(self, rhs: Self) -> Self::Output {
412        #[cfg(feature = "symbolic")]
413        {
414            match (self, rhs) {
415                // Optimize constant multiplication
416                (Self::Constant(a), Self::Constant(b)) => Self::Constant(a * b),
417                (Self::SymEngine(a), Self::SymEngine(b)) => Self::SymEngine(a * b),
418                (a, b) => {
419                    let a_sym = match a {
420                        Self::Constant(val) => SymEngine::from(val),
421                        Self::Variable(name) => SymEngine::symbol(&name),
422                        Self::SymEngine(expr) => expr,
423                        _ => return Self::Constant(0.0),
424                    };
425                    let b_sym = match b {
426                        Self::Constant(val) => SymEngine::from(val),
427                        Self::Variable(name) => SymEngine::symbol(&name),
428                        Self::SymEngine(expr) => expr,
429                        _ => return Self::Constant(0.0),
430                    };
431                    Self::SymEngine(a_sym * b_sym)
432                }
433            }
434        }
435
436        #[cfg(not(feature = "symbolic"))]
437        {
438            match (self, rhs) {
439                (Self::Constant(a), Self::Constant(b)) => Self::Constant(a * b),
440                (a, b) => Self::Simple(SimpleExpression::Mul(Box::new(a), Box::new(b))),
441            }
442        }
443    }
444}
445
446impl std::ops::Div for SymbolicExpression {
447    type Output = Self;
448
449    fn div(self, rhs: Self) -> Self::Output {
450        #[cfg(feature = "symbolic")]
451        {
452            match (self, rhs) {
453                // Optimize constant division
454                (Self::Constant(a), Self::Constant(b)) => {
455                    if b.abs() < 1e-12 {
456                        Self::Constant(f64::INFINITY)
457                    } else {
458                        Self::Constant(a / b)
459                    }
460                }
461                (Self::SymEngine(a), Self::SymEngine(b)) => Self::SymEngine(a / b),
462                (a, b) => {
463                    let a_sym = match a {
464                        Self::Constant(val) => SymEngine::from(val),
465                        Self::Variable(name) => SymEngine::symbol(&name),
466                        Self::SymEngine(expr) => expr,
467                        _ => return Self::Constant(0.0),
468                    };
469                    let b_sym = match b {
470                        Self::Constant(val) => SymEngine::from(val),
471                        Self::Variable(name) => SymEngine::symbol(&name),
472                        Self::SymEngine(expr) => expr,
473                        _ => return Self::Constant(1.0),
474                    };
475                    Self::SymEngine(a_sym / b_sym)
476                }
477            }
478        }
479
480        #[cfg(not(feature = "symbolic"))]
481        {
482            match (self, rhs) {
483                (Self::Constant(a), Self::Constant(b)) => {
484                    if b.abs() < 1e-12 {
485                        Self::Constant(f64::INFINITY)
486                    } else {
487                        Self::Constant(a / b)
488                    }
489                }
490                (a, b) => Self::Simple(SimpleExpression::Div(Box::new(a), Box::new(b))),
491            }
492        }
493    }
494}
495
496impl fmt::Display for SymbolicExpression {
497    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
498        match self {
499            Self::Constant(value) => write!(f, "{value}"),
500            Self::ComplexConstant(value) => {
501                if value.im == 0.0 {
502                    write!(f, "{}", value.re)
503                } else if value.re == 0.0 {
504                    write!(f, "{}*I", value.im)
505                } else {
506                    write!(f, "{} + {}*I", value.re, value.im)
507                }
508            }
509            Self::Variable(name) => write!(f, "{name}"),
510
511            #[cfg(feature = "symbolic")]
512            Self::SymEngine(expr) => write!(f, "{expr}"),
513
514            #[cfg(not(feature = "symbolic"))]
515            Self::Simple(expr) => Self::display_simple(expr, f),
516        }
517    }
518}
519
520#[cfg(not(feature = "symbolic"))]
521impl SymbolicExpression {
522    fn display_simple(expr: &SimpleExpression, f: &mut fmt::Formatter<'_>) -> fmt::Result {
523        match expr {
524            SimpleExpression::Add(a, b) => write!(f, "({a} + {b})"),
525            SimpleExpression::Sub(a, b) => write!(f, "({a} - {b})"),
526            SimpleExpression::Mul(a, b) => write!(f, "({a} * {b})"),
527            SimpleExpression::Div(a, b) => write!(f, "({a} / {b})"),
528            SimpleExpression::Pow(a, b) => write!(f, "({a} ^ {b})"),
529            SimpleExpression::Sin(a) => write!(f, "sin({a})"),
530            SimpleExpression::Cos(a) => write!(f, "cos({a})"),
531            SimpleExpression::Exp(a) => write!(f, "exp({a})"),
532            SimpleExpression::Log(a) => write!(f, "log({a})"),
533        }
534    }
535}
536
537impl From<f64> for SymbolicExpression {
538    fn from(value: f64) -> Self {
539        Self::Constant(value)
540    }
541}
542
543impl From<Complex64> for SymbolicExpression {
544    fn from(value: Complex64) -> Self {
545        if value.im == 0.0 {
546            Self::Constant(value.re)
547        } else {
548            Self::ComplexConstant(value)
549        }
550    }
551}
552
553impl From<&str> for SymbolicExpression {
554    fn from(name: &str) -> Self {
555        Self::Variable(name.to_string())
556    }
557}
558
559impl Zero for SymbolicExpression {
560    fn zero() -> Self {
561        Self::Constant(0.0)
562    }
563
564    fn is_zero(&self) -> bool {
565        match self {
566            Self::Constant(val) => *val == 0.0,
567            Self::ComplexConstant(val) => val.is_zero(),
568            _ => false,
569        }
570    }
571}
572
573impl One for SymbolicExpression {
574    fn one() -> Self {
575        Self::Constant(1.0)
576    }
577
578    fn is_one(&self) -> bool {
579        match self {
580            Self::Constant(val) => *val == 1.0,
581            Self::ComplexConstant(val) => val.is_one(),
582            _ => false,
583        }
584    }
585}
586
587/// Symbolic calculus operations
588#[cfg(feature = "symbolic")]
589pub mod calculus {
590    use super::*;
591
592    /// Differentiate an expression with respect to a variable
593    pub fn diff(expr: &SymbolicExpression, var: &str) -> QuantRS2Result<SymbolicExpression> {
594        match expr {
595            SymbolicExpression::SymEngine(sym_expr) => {
596                let var_expr = SymEngine::symbol(var);
597                // Use the Expression::diff() method directly
598                let result = sym_expr.diff(&var_expr);
599                Ok(SymbolicExpression::SymEngine(result))
600            }
601            _ => Err(QuantRS2Error::UnsupportedOperation(
602                "Differentiation requires SymEngine expressions".to_string(),
603            )),
604        }
605    }
606
607    /// Indefinite integration of an expression with respect to `var`.
608    ///
609    /// Implements the tractable cases supported by the pure-Rust SymEngine backend, which
610    /// has no native `integrate`: the power rule (`∫x^n dx = x^(n+1)/(n+1)` for `n != -1`),
611    /// integration of constants (`∫c dx = c·x`), the linear variable (`∫x dx = x^2/2`),
612    /// linearity over sums (`∫(f+g) = ∫f + ∫g`), and pulling out constant factors
613    /// (`∫c·f = c·∫f`). The constant of integration is omitted (indefinite integral up to
614    /// a constant). Cases the backend cannot integrate symbolically (e.g. `1/x`,
615    /// transcendental functions) return an honest [`QuantRS2Error::UnsupportedOperation`]
616    /// rather than the unchanged input.
617    pub fn integrate(expr: &SymbolicExpression, var: &str) -> QuantRS2Result<SymbolicExpression> {
618        match expr {
619            SymbolicExpression::SymEngine(sym_expr) => {
620                let integrated = integrate_symengine(sym_expr, var)?;
621                Ok(SymbolicExpression::SymEngine(integrated.expand()))
622            }
623            _ => Err(QuantRS2Error::UnsupportedOperation(
624                "Integration requires SymEngine expressions".to_string(),
625            )),
626        }
627    }
628
629    /// Compute the limit of an expression as `var` approaches `value`.
630    ///
631    /// This evaluates the limit by direct substitution, which is exact for functions that
632    /// are continuous at `value`. It does NOT resolve indeterminate forms (e.g. `0/0`,
633    /// `∞/∞`); for a continuous expression the substituted result is the true limit.
634    pub fn limit(
635        expr: &SymbolicExpression,
636        var: &str,
637        value: f64,
638    ) -> QuantRS2Result<SymbolicExpression> {
639        match expr {
640            SymbolicExpression::SymEngine(sym_expr) => {
641                // Limit by substitution (exact for functions continuous at `value`).
642                let var_expr = SymEngine::symbol(var);
643                let value_expr = SymEngine::from(value);
644                let result = sym_expr.substitute(&var_expr, &value_expr);
645                Ok(SymbolicExpression::SymEngine(result))
646            }
647            _ => Err(QuantRS2Error::UnsupportedOperation(
648                "Limit computation requires SymEngine expressions".to_string(),
649            )),
650        }
651    }
652
653    /// Expand an expression
654    pub fn expand(expr: &SymbolicExpression) -> QuantRS2Result<SymbolicExpression> {
655        match expr {
656            SymbolicExpression::SymEngine(sym_expr) => {
657                Ok(SymbolicExpression::SymEngine(sym_expr.expand()))
658            }
659            _ => Ok(expr.clone()), // No expansion needed for simple expressions
660        }
661    }
662
663    /// Simplify an expression
664    pub fn simplify(expr: &SymbolicExpression) -> QuantRS2Result<SymbolicExpression> {
665        match expr {
666            SymbolicExpression::SymEngine(sym_expr) => {
667                // Use the simplify method from the pure Rust implementation
668                Ok(SymbolicExpression::SymEngine(sym_expr.simplify()))
669            }
670            _ => Ok(expr.clone()),
671        }
672    }
673
674    /// Recursive symbolic integration over the pure-Rust SymEngine expression tree.
675    ///
676    /// Implements the power rule, constant rule, sum linearity, and constant-factor
677    /// extraction. Returns an honest error for forms the backend cannot integrate.
678    fn integrate_symengine(expr: &SymEngine, var: &str) -> QuantRS2Result<SymEngine> {
679        let var_expr = SymEngine::symbol(var);
680
681        // Case 1: expression is constant with respect to `var` (does not contain it).
682        // ∫c dx = c·x
683        if !expr.free_symbols().contains(var) {
684            return Ok(expr.mul(&var_expr));
685        }
686
687        // Case 2: expression is exactly the integration variable. ∫x dx = x^2 / 2
688        if expr.as_symbol() == Some(var) {
689            let two = SymEngine::int(2);
690            return Ok(var_expr.pow(&two).div(&two));
691        }
692
693        // Case 3: sum — integrate term-by-term (linearity). ∫(f+g) = ∫f + ∫g
694        if let Some(terms) = expr.as_add() {
695            let mut acc: Option<SymEngine> = None;
696            for term in &terms {
697                let integrated = integrate_symengine(term, var)?;
698                acc = Some(match acc {
699                    Some(prev) => prev.add(&integrated),
700                    None => integrated,
701                });
702            }
703            return acc.ok_or_else(|| {
704                QuantRS2Error::UnsupportedOperation(
705                    "empty sum encountered during integration".to_string(),
706                )
707            });
708        }
709
710        // Case 4: product — pull out factors that are constant w.r.t. `var`.
711        // ∫(c·f) = c·∫f. Only one variable-dependent factor is supported.
712        if let Some(factors) = expr.as_mul() {
713            let mut constant_part: Option<SymEngine> = None;
714            let mut variable_part: Option<SymEngine> = None;
715            for factor in &factors {
716                if factor.free_symbols().contains(var) {
717                    if variable_part.is_some() {
718                        // Two variable-dependent factors (e.g. x·sin(x)) need integration
719                        // by parts, which the backend does not support.
720                        return Err(QuantRS2Error::UnsupportedOperation(format!(
721                            "symbolic integration of the product '{expr}' is not supported by the pure-Rust backend"
722                        )));
723                    }
724                    variable_part = Some(factor.clone());
725                } else {
726                    constant_part = Some(match constant_part {
727                        Some(prev) => prev.mul(factor),
728                        None => factor.clone(),
729                    });
730                }
731            }
732            let variable_part = variable_part.ok_or_else(|| {
733                QuantRS2Error::UnsupportedOperation(format!(
734                    "could not isolate a variable factor in product '{expr}'"
735                ))
736            })?;
737            let integrated_variable = integrate_symengine(&variable_part, var)?;
738            return Ok(match constant_part {
739                Some(c) => c.mul(&integrated_variable),
740                None => integrated_variable,
741            });
742        }
743
744        // Case 5: power — power rule for x^n with constant exponent n != -1.
745        // ∫x^n dx = x^(n+1)/(n+1)
746        if let Some((base, exponent)) = expr.as_pow() {
747            let base_is_var = base.as_symbol() == Some(var);
748            let exponent_const = !exponent.free_symbols().contains(var);
749            if base_is_var && exponent_const {
750                if let Some(n) = exponent.to_f64() {
751                    if (n + 1.0).abs() < 1e-12 {
752                        // ∫x^(-1) dx = ln|x|, which the backend cannot represent.
753                        return Err(QuantRS2Error::UnsupportedOperation(
754                            "integration of x^(-1) (yields ln|x|) is not supported by the pure-Rust backend"
755                                .to_string(),
756                        ));
757                    }
758                    // Build the new exponent exactly when integral, else as a float.
759                    let new_exponent = if (n - n.round()).abs() < 1e-12 {
760                        SymEngine::int(n.round() as i64 + 1)
761                    } else {
762                        SymEngine::float(n + 1.0).map_err(|e| {
763                            QuantRS2Error::ComputationError(format!(
764                                "failed to build exponent during integration: {e:?}"
765                            ))
766                        })?
767                    };
768                    let divisor = new_exponent.clone();
769                    return Ok(base.pow(&new_exponent).div(&divisor));
770                }
771            }
772        }
773
774        // No supported antiderivative form matched — be honest.
775        Err(QuantRS2Error::UnsupportedOperation(format!(
776            "symbolic integration of '{expr}' with respect to '{var}' is not supported by the pure-Rust backend"
777        )))
778    }
779}
780
781/// Symbolic matrix operations for quantum gates
782pub mod matrix {
783    use super::*;
784    use scirs2_core::ndarray::Array2;
785
786    /// A symbolic matrix for representing quantum gates
787    #[derive(Debug, Clone)]
788    pub struct SymbolicMatrix {
789        pub rows: usize,
790        pub cols: usize,
791        pub elements: Vec<Vec<SymbolicExpression>>,
792    }
793
794    impl SymbolicMatrix {
795        /// Create a new symbolic matrix
796        pub fn new(rows: usize, cols: usize) -> Self {
797            let elements = vec![vec![SymbolicExpression::zero(); cols]; rows];
798            Self {
799                rows,
800                cols,
801                elements,
802            }
803        }
804
805        /// Create an identity matrix
806        pub fn identity(size: usize) -> Self {
807            let mut matrix = Self::new(size, size);
808            for i in 0..size {
809                matrix.elements[i][i] = SymbolicExpression::one();
810            }
811            matrix
812        }
813
814        /// Create a symbolic rotation matrix around X-axis
815        #[allow(unused_variables)]
816        pub fn rotation_x(theta: SymbolicExpression) -> Self {
817            let mut matrix = Self::new(2, 2);
818
819            #[cfg(feature = "symbolic")]
820            {
821                let half_theta = theta / SymbolicExpression::constant(2.0);
822                let inner_expr = match &half_theta {
823                    SymbolicExpression::SymEngine(expr) => expr.clone(),
824                    _ => return matrix,
825                };
826                let cos_expr = SymbolicExpression::SymEngine(
827                    quantrs2_symengine_pure::ops::trig::cos(&inner_expr),
828                );
829                let sin_expr = SymbolicExpression::SymEngine(
830                    quantrs2_symengine_pure::ops::trig::sin(&inner_expr),
831                );
832
833                matrix.elements[0][0] = cos_expr.clone();
834                matrix.elements[0][1] =
835                    SymbolicExpression::complex_constant(Complex64::new(0.0, -1.0))
836                        * sin_expr.clone();
837                matrix.elements[1][0] =
838                    SymbolicExpression::complex_constant(Complex64::new(0.0, -1.0)) * sin_expr;
839                matrix.elements[1][1] = cos_expr;
840            }
841
842            #[cfg(not(feature = "symbolic"))]
843            {
844                // Simplified representation
845                matrix.elements[0][0] = SymbolicExpression::parse("cos(theta/2)")
846                    .unwrap_or_else(|_| SymbolicExpression::one());
847                matrix.elements[0][1] = SymbolicExpression::parse("-i*sin(theta/2)")
848                    .unwrap_or_else(|_| SymbolicExpression::zero());
849                matrix.elements[1][0] = SymbolicExpression::parse("-i*sin(theta/2)")
850                    .unwrap_or_else(|_| SymbolicExpression::zero());
851                matrix.elements[1][1] = SymbolicExpression::parse("cos(theta/2)")
852                    .unwrap_or_else(|_| SymbolicExpression::one());
853            }
854
855            matrix
856        }
857
858        /// Evaluate the matrix with given variable values
859        pub fn evaluate(
860            &self,
861            variables: &HashMap<String, f64>,
862        ) -> QuantRS2Result<Array2<Complex64>> {
863            let mut result = Array2::<Complex64>::zeros((self.rows, self.cols));
864
865            for i in 0..self.rows {
866                for j in 0..self.cols {
867                    let complex_vars: HashMap<String, Complex64> = variables
868                        .iter()
869                        .map(|(k, v)| (k.clone(), Complex64::new(*v, 0.0)))
870                        .collect();
871
872                    let value = self.elements[i][j].evaluate_complex(&complex_vars)?;
873                    result[[i, j]] = value;
874                }
875            }
876
877            Ok(result)
878        }
879
880        /// Matrix multiplication
881        pub fn multiply(&self, other: &Self) -> QuantRS2Result<Self> {
882            if self.cols != other.rows {
883                return Err(QuantRS2Error::InvalidInput(
884                    "Matrix dimensions don't match for multiplication".to_string(),
885                ));
886            }
887
888            let mut result = Self::new(self.rows, other.cols);
889
890            for i in 0..self.rows {
891                for j in 0..other.cols {
892                    let mut sum = SymbolicExpression::zero();
893                    for k in 0..self.cols {
894                        let product = self.elements[i][k].clone() * other.elements[k][j].clone();
895                        sum = sum + product;
896                    }
897                    result.elements[i][j] = sum;
898                }
899            }
900
901            Ok(result)
902        }
903    }
904
905    impl fmt::Display for SymbolicMatrix {
906        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
907            writeln!(f, "SymbolicMatrix[{}x{}]:", self.rows, self.cols)?;
908            for row in &self.elements {
909                write!(f, "[")?;
910                for (j, elem) in row.iter().enumerate() {
911                    if j > 0 {
912                        write!(f, ", ")?;
913                    }
914                    write!(f, "{elem}")?;
915                }
916                writeln!(f, "]")?;
917            }
918            Ok(())
919        }
920    }
921}
922
923#[cfg(test)]
924mod tests {
925    use super::*;
926
927    #[test]
928    fn test_symbolic_expression_creation() {
929        let const_expr = SymbolicExpression::constant(std::f64::consts::PI);
930        assert!(const_expr.is_constant());
931
932        let var_expr = SymbolicExpression::variable("x");
933        assert!(!var_expr.is_constant());
934        assert_eq!(var_expr.variables(), vec!["x"]);
935    }
936
937    #[test]
938    fn test_symbolic_arithmetic() {
939        let a = SymbolicExpression::constant(2.0);
940        let b = SymbolicExpression::constant(3.0);
941        let sum = a + b;
942
943        assert!(
944            matches!(sum, SymbolicExpression::Constant(_)),
945            "Expected constant result, got: {:?}",
946            sum
947        );
948        if let SymbolicExpression::Constant(value) = sum {
949            assert_eq!(value, 5.0);
950        }
951    }
952
953    #[test]
954    fn test_symbolic_evaluation() {
955        let mut vars = HashMap::new();
956        vars.insert("x".to_string(), 2.0);
957
958        let var_expr = SymbolicExpression::variable("x");
959        let result = var_expr
960            .evaluate(&vars)
961            .expect("Failed to evaluate expression in test_symbolic_evaluation");
962        assert_eq!(result, 2.0);
963    }
964
965    #[test]
966    fn test_symbolic_matrix() {
967        let matrix = matrix::SymbolicMatrix::identity(2);
968        assert_eq!(matrix.rows, 2);
969        assert_eq!(matrix.cols, 2);
970        assert!(matrix.elements[0][0].is_one());
971        assert!(matrix.elements[1][1].is_one());
972        assert!(matrix.elements[0][1].is_zero());
973    }
974
975    #[cfg(feature = "symbolic")]
976    #[test]
977    fn test_symengine_integration() {
978        let expr = SymbolicExpression::parse("x^2")
979            .expect("Failed to parse expression in test_symengine_integration");
980        match expr {
981            SymbolicExpression::SymEngine(_) => {
982                // Test SymEngine functionality
983                assert!(!expr.is_constant());
984            }
985            _ => {
986                // Fallback to simple parsing
987                assert!(!expr.is_constant());
988            }
989        }
990    }
991
992    #[cfg(feature = "symbolic")]
993    #[test]
994    fn test_symengine_evaluate() {
995        // Build x^2 + 2*x + 1 symbolically and evaluate at x=3  (expected: 16)
996        let expr = SymbolicExpression::from_symengine_str("x^2 + 2*x + 1");
997        let mut vars = HashMap::new();
998        vars.insert("x".to_string(), 3.0);
999        let result = expr
1000            .evaluate(&vars)
1001            .expect("evaluate should succeed for x^2+2x+1 at x=3");
1002        assert!((result - 16.0).abs() < 1e-10, "expected 16.0, got {result}");
1003    }
1004
1005    #[cfg(feature = "symbolic")]
1006    #[test]
1007    fn test_symengine_evaluate_complex() {
1008        // Evaluate I*x at x=1  =>  0 + 1i
1009        let expr = SymbolicExpression::from_symengine_str("I*x");
1010        let mut vars = HashMap::new();
1011        vars.insert("x".to_string(), Complex64::new(1.0, 0.0));
1012        let result = expr
1013            .evaluate_complex(&vars)
1014            .expect("evaluate_complex should succeed for I*x at x=1");
1015        assert!(
1016            result.re.abs() < 1e-10,
1017            "real part should be 0, got {}",
1018            result.re
1019        );
1020        assert!(
1021            (result.im - 1.0).abs() < 1e-10,
1022            "imaginary part should be 1, got {}",
1023            result.im
1024        );
1025    }
1026
1027    #[cfg(feature = "symbolic")]
1028    #[test]
1029    fn test_symengine_variables() {
1030        let expr = SymbolicExpression::from_symengine_str("x + y");
1031        let vars = expr.variables();
1032        assert_eq!(vars.len(), 2, "expected 2 variables, got {:?}", vars);
1033        assert!(vars.contains(&"x".to_string()));
1034        assert!(vars.contains(&"y".to_string()));
1035    }
1036
1037    #[cfg(feature = "symbolic")]
1038    #[test]
1039    fn test_symengine_is_constant() {
1040        let const_expr = SymbolicExpression::from_symengine_str("42");
1041        assert!(
1042            const_expr.is_constant(),
1043            "numeric literal should be constant"
1044        );
1045
1046        let var_expr = SymbolicExpression::from_symengine_str("x + 1");
1047        assert!(
1048            !var_expr.is_constant(),
1049            "expression with variable should not be constant"
1050        );
1051    }
1052
1053    #[cfg(feature = "symbolic")]
1054    #[test]
1055    fn test_symengine_substitute() {
1056        // Substitute x=2 into x+1, expect 3
1057        let expr = SymbolicExpression::from_symengine_str("x + 1");
1058        let mut subs = HashMap::new();
1059        subs.insert("x".to_string(), SymbolicExpression::constant(2.0));
1060        let substituted = expr.substitute(&subs).expect("substitute should succeed");
1061        let result = substituted
1062            .evaluate(&HashMap::new())
1063            .expect("evaluate should succeed after substitution");
1064        assert!(
1065            (result - 3.0).abs() < 1e-10,
1066            "expected 3.0 after substituting x=2 in x+1, got {result}"
1067        );
1068    }
1069
1070    #[cfg(feature = "symbolic")]
1071    #[test]
1072    fn test_calculus_integrate_power_rule() {
1073        // ∫ 2x dx == x^2 (+C). Verify by differentiating the result back to 2x and by
1074        // evaluating the antiderivative: at x=3 it must equal 9.
1075        let expr = SymbolicExpression::from_symengine_str("2*x");
1076        let integrated = calculus::integrate(&expr, "x").expect("integrating 2x should succeed");
1077
1078        // Evaluate the antiderivative at x = 3 -> expect 9 (x^2 with C=0).
1079        let mut vars = HashMap::new();
1080        vars.insert("x".to_string(), 3.0);
1081        let value = integrated
1082            .evaluate(&vars)
1083            .expect("evaluating the integral should succeed");
1084        assert!(
1085            (value - 9.0).abs() < 1e-9,
1086            "∫2x dx at x=3 should be 9 (x^2), got {value}"
1087        );
1088
1089        // Differentiating the antiderivative must recover the integrand 2x.
1090        let derivative =
1091            calculus::diff(&integrated, "x").expect("differentiating the integral should succeed");
1092        let d_value = derivative
1093            .evaluate(&vars)
1094            .expect("evaluating the derivative should succeed");
1095        assert!(
1096            (d_value - 6.0).abs() < 1e-9,
1097            "d/dx of ∫2x dx at x=3 should be 6 (=2x), got {d_value}"
1098        );
1099
1100        // It must NOT be the unchanged input (the old fabrication): the input 2x
1101        // evaluated at x=3 is 6, the integral is 9 — they differ.
1102        let input_value = expr
1103            .evaluate(&vars)
1104            .expect("evaluating the input should succeed");
1105        assert!(
1106            (value - input_value).abs() > 1e-6,
1107            "integral must differ from the unchanged input"
1108        );
1109    }
1110
1111    #[cfg(feature = "symbolic")]
1112    #[test]
1113    fn test_calculus_integrate_constant_and_power() {
1114        // ∫ x^2 dx -> x^3/3; evaluated at x=3 -> 9.
1115        let expr = SymbolicExpression::from_symengine_str("x^2");
1116        let integrated = calculus::integrate(&expr, "x").expect("integrating x^2 should succeed");
1117        let mut vars = HashMap::new();
1118        vars.insert("x".to_string(), 3.0);
1119        let value = integrated
1120            .evaluate(&vars)
1121            .expect("evaluating x^3/3 should succeed");
1122        assert!(
1123            (value - 9.0).abs() < 1e-9,
1124            "∫x^2 dx at x=3 should be 9 (x^3/3), got {value}"
1125        );
1126
1127        // ∫ 5 dx -> 5x; at x=4 -> 20.
1128        let c_expr = SymbolicExpression::from_symengine_str("5");
1129        let c_int =
1130            calculus::integrate(&c_expr, "x").expect("integrating a constant should succeed");
1131        let mut vars4 = HashMap::new();
1132        vars4.insert("x".to_string(), 4.0);
1133        let c_val = c_int
1134            .evaluate(&vars4)
1135            .expect("evaluating 5x should succeed");
1136        assert!(
1137            (c_val - 20.0).abs() < 1e-9,
1138            "∫5 dx at x=4 should be 20 (5x), got {c_val}"
1139        );
1140    }
1141
1142    #[cfg(feature = "symbolic")]
1143    #[test]
1144    fn test_calculus_integrate_unsupported_returns_error() {
1145        // ∫ 1/x dx = ln|x|, which the pure-Rust backend cannot represent. It must return
1146        // an HONEST error, not the unchanged input.
1147        let expr = SymbolicExpression::from_symengine_str("x^(-1)");
1148        let result = calculus::integrate(&expr, "x");
1149        assert!(
1150            result.is_err(),
1151            "integrating x^(-1) must return an honest error, got {result:?}"
1152        );
1153    }
1154}