1#[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}; use scirs2_core::Complex64;
13use std::collections::HashMap;
14use std::fmt;
15
16#[derive(Debug, Clone, PartialEq)]
18pub enum SymbolicExpression {
19 Constant(f64),
21
22 ComplexConstant(Complex64),
24
25 Variable(String),
27
28 #[cfg(feature = "symbolic")]
30 SymEngine(SymEngine),
31
32 #[cfg(not(feature = "symbolic"))]
34 Simple(SimpleExpression),
35}
36
37#[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 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 pub const fn complex_constant(value: Complex64) -> Self {
64 Self::ComplexConstant(value)
65 }
66
67 pub fn variable(name: &str) -> Self {
69 Self::Variable(name.to_string())
70 }
71
72 #[cfg(feature = "symbolic")]
74 pub const fn from_symengine(expr: SymEngine) -> Self {
75 Self::SymEngine(expr)
76 }
77
78 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 Self::parse_simple(expr)
87 }
88 }
89 }
90
91 #[cfg(not(feature = "symbolic"))]
92 {
93 Self::parse_simple(expr)
94 }
95 }
96
97 fn parse_simple(expr: &str) -> QuantRS2Result<Self> {
99 let trimmed = expr.trim();
100
101 if let Ok(value) = trimmed.parse::<f64>() {
103 return Ok(Self::Constant(value));
104 }
105
106 Ok(Self::Variable(trimmed.to_string()))
108 }
109
110 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 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 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 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 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 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 Err(QuantRS2Error::UnsupportedOperation(
294 "Simple expression substitution not yet implemented".to_string(),
295 ))
296 }
297 }
298 }
299
300 #[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 #[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
330impl 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 (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 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), };
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), };
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 (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 (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 (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#[cfg(feature = "symbolic")]
589pub mod calculus {
590 use super::*;
591
592 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 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 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 pub fn limit(
635 expr: &SymbolicExpression,
636 var: &str,
637 value: f64,
638 ) -> QuantRS2Result<SymbolicExpression> {
639 match expr {
640 SymbolicExpression::SymEngine(sym_expr) => {
641 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 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()), }
661 }
662
663 pub fn simplify(expr: &SymbolicExpression) -> QuantRS2Result<SymbolicExpression> {
665 match expr {
666 SymbolicExpression::SymEngine(sym_expr) => {
667 Ok(SymbolicExpression::SymEngine(sym_expr.simplify()))
669 }
670 _ => Ok(expr.clone()),
671 }
672 }
673
674 fn integrate_symengine(expr: &SymEngine, var: &str) -> QuantRS2Result<SymEngine> {
679 let var_expr = SymEngine::symbol(var);
680
681 if !expr.free_symbols().contains(var) {
684 return Ok(expr.mul(&var_expr));
685 }
686
687 if expr.as_symbol() == Some(var) {
689 let two = SymEngine::int(2);
690 return Ok(var_expr.pow(&two).div(&two));
691 }
692
693 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 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 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 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 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 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 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
781pub mod matrix {
783 use super::*;
784 use scirs2_core::ndarray::Array2;
785
786 #[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 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 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 #[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 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 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 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 assert!(!expr.is_constant());
984 }
985 _ => {
986 assert!(!expr.is_constant());
988 }
989 }
990 }
991
992 #[cfg(feature = "symbolic")]
993 #[test]
994 fn test_symengine_evaluate() {
995 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 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 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 let expr = SymbolicExpression::from_symengine_str("2*x");
1076 let integrated = calculus::integrate(&expr, "x").expect("integrating 2x should succeed");
1077
1078 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 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 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 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 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 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}