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) => {
130 if let Ok(value) = expr.to_string().parse::<f64>() {
133 Ok(value)
134 } else {
135 Err(QuantRS2Error::UnsupportedOperation(
136 "SymEngine evaluation not yet implemented".to_string(),
137 ))
138 }
139 }
140
141 #[cfg(not(feature = "symbolic"))]
142 Self::Simple(simple_expr) => Self::evaluate_simple(simple_expr, variables),
143 }
144 }
145
146 pub fn evaluate_complex(
148 &self,
149 variables: &HashMap<String, Complex64>,
150 ) -> QuantRS2Result<Complex64> {
151 match self {
152 Self::Constant(value) => Ok(Complex64::new(*value, 0.0)),
153 Self::ComplexConstant(value) => Ok(*value),
154 Self::Variable(name) => variables
155 .get(name)
156 .copied()
157 .ok_or_else(|| QuantRS2Error::InvalidInput(format!("Variable '{name}' not found"))),
158
159 #[cfg(feature = "symbolic")]
160 Self::SymEngine(_) => Err(QuantRS2Error::UnsupportedOperation(
161 "Complex SymEngine evaluation not yet implemented".to_string(),
162 )),
163
164 #[cfg(not(feature = "symbolic"))]
165 Self::Simple(simple_expr) => Self::evaluate_simple_complex(simple_expr, variables),
166 }
167 }
168
169 #[cfg(not(feature = "symbolic"))]
170 fn evaluate_simple(
171 expr: &SimpleExpression,
172 variables: &HashMap<String, f64>,
173 ) -> QuantRS2Result<f64> {
174 match expr {
175 SimpleExpression::Add(a, b) => Ok(a.evaluate(variables)? + b.evaluate(variables)?),
176 SimpleExpression::Sub(a, b) => Ok(a.evaluate(variables)? - b.evaluate(variables)?),
177 SimpleExpression::Mul(a, b) => Ok(a.evaluate(variables)? * b.evaluate(variables)?),
178 SimpleExpression::Div(a, b) => {
179 let b_val = b.evaluate(variables)?;
180 if b_val.abs() < 1e-12 {
181 Err(QuantRS2Error::DivisionByZero)
182 } else {
183 Ok(a.evaluate(variables)? / b_val)
184 }
185 }
186 SimpleExpression::Pow(a, b) => Ok(a.evaluate(variables)?.powf(b.evaluate(variables)?)),
187 SimpleExpression::Sin(a) => Ok(a.evaluate(variables)?.sin()),
188 SimpleExpression::Cos(a) => Ok(a.evaluate(variables)?.cos()),
189 SimpleExpression::Exp(a) => Ok(a.evaluate(variables)?.exp()),
190 SimpleExpression::Log(a) => {
191 let a_val = a.evaluate(variables)?;
192 if a_val <= 0.0 {
193 Err(QuantRS2Error::InvalidInput(
194 "Logarithm of non-positive number".to_string(),
195 ))
196 } else {
197 Ok(a_val.ln())
198 }
199 }
200 }
201 }
202
203 #[cfg(not(feature = "symbolic"))]
204 fn evaluate_simple_complex(
205 expr: &SimpleExpression,
206 variables: &HashMap<String, Complex64>,
207 ) -> QuantRS2Result<Complex64> {
208 let real_vars: HashMap<String, f64> = variables
210 .iter()
211 .filter_map(|(k, v)| {
212 if v.im.abs() < 1e-12 {
213 Some((k.clone(), v.re))
214 } else {
215 None
216 }
217 })
218 .collect();
219
220 let real_result = Self::evaluate_simple(expr, &real_vars)?;
221 Ok(Complex64::new(real_result, 0.0))
222 }
223
224 pub fn variables(&self) -> Vec<String> {
226 match self {
227 Self::Constant(_) | Self::ComplexConstant(_) => Vec::new(),
228 Self::Variable(name) => vec![name.clone()],
229
230 #[cfg(feature = "symbolic")]
231 Self::SymEngine(_) => {
232 Vec::new()
234 }
235
236 #[cfg(not(feature = "symbolic"))]
237 Self::Simple(simple_expr) => Self::variables_simple(simple_expr),
238 }
239 }
240
241 #[cfg(not(feature = "symbolic"))]
242 fn variables_simple(expr: &SimpleExpression) -> Vec<String> {
243 match expr {
244 SimpleExpression::Add(a, b)
245 | SimpleExpression::Sub(a, b)
246 | SimpleExpression::Mul(a, b)
247 | SimpleExpression::Div(a, b)
248 | SimpleExpression::Pow(a, b) => {
249 let mut vars = a.variables();
250 vars.extend(b.variables());
251 vars.sort();
252 vars.dedup();
253 vars
254 }
255 SimpleExpression::Sin(a)
256 | SimpleExpression::Cos(a)
257 | SimpleExpression::Exp(a)
258 | SimpleExpression::Log(a) => a.variables(),
259 }
260 }
261
262 pub const fn is_constant(&self) -> bool {
264 match self {
265 Self::Constant(_) | Self::ComplexConstant(_) => true,
266 Self::Variable(_) => false,
267
268 #[cfg(feature = "symbolic")]
269 Self::SymEngine(_) => {
270 false
272 }
273
274 #[cfg(not(feature = "symbolic"))]
275 Self::Simple(_) => false,
276 }
277 }
278
279 pub fn substitute(&self, substitutions: &HashMap<String, Self>) -> QuantRS2Result<Self> {
281 match self {
282 Self::Constant(_) | Self::ComplexConstant(_) => Ok(self.clone()),
283 Self::Variable(name) => Ok(substitutions
284 .get(name)
285 .cloned()
286 .unwrap_or_else(|| self.clone())),
287
288 #[cfg(feature = "symbolic")]
289 Self::SymEngine(_) => {
290 Err(QuantRS2Error::UnsupportedOperation(
292 "SymEngine substitution not yet implemented".to_string(),
293 ))
294 }
295
296 #[cfg(not(feature = "symbolic"))]
297 Self::Simple(_) => {
298 Err(QuantRS2Error::UnsupportedOperation(
300 "Simple expression substitution not yet implemented".to_string(),
301 ))
302 }
303 }
304 }
305}
306
307impl std::ops::Add for SymbolicExpression {
309 type Output = Self;
310
311 fn add(self, rhs: Self) -> Self::Output {
312 #[cfg(feature = "symbolic")]
313 {
314 match (self, rhs) {
315 (Self::Constant(a), Self::Constant(b)) => Self::Constant(a + b),
317 (Self::SymEngine(a), Self::SymEngine(b)) => Self::SymEngine(a + b),
318 (a, b) => {
319 let a_sym = match a {
321 Self::Constant(val) => SymEngine::from(val),
322 Self::Variable(name) => SymEngine::symbol(&name),
323 Self::SymEngine(expr) => expr,
324 _ => return Self::Constant(0.0), };
326 let b_sym = match b {
327 Self::Constant(val) => SymEngine::from(val),
328 Self::Variable(name) => SymEngine::symbol(&name),
329 Self::SymEngine(expr) => expr,
330 _ => return Self::Constant(0.0), };
332 Self::SymEngine(a_sym + b_sym)
333 }
334 }
335 }
336
337 #[cfg(not(feature = "symbolic"))]
338 {
339 match (self, rhs) {
340 (Self::Constant(a), Self::Constant(b)) => Self::Constant(a + b),
341 (a, b) => Self::Simple(SimpleExpression::Add(Box::new(a), Box::new(b))),
342 }
343 }
344 }
345}
346
347impl std::ops::Sub for SymbolicExpression {
348 type Output = Self;
349
350 fn sub(self, rhs: Self) -> Self::Output {
351 #[cfg(feature = "symbolic")]
352 {
353 match (self, rhs) {
354 (Self::Constant(a), Self::Constant(b)) => Self::Constant(a - b),
356 (Self::SymEngine(a), Self::SymEngine(b)) => Self::SymEngine(a - b),
357 (a, b) => {
358 let a_sym = match a {
359 Self::Constant(val) => SymEngine::from(val),
360 Self::Variable(name) => SymEngine::symbol(&name),
361 Self::SymEngine(expr) => expr,
362 _ => return Self::Constant(0.0),
363 };
364 let b_sym = match b {
365 Self::Constant(val) => SymEngine::from(val),
366 Self::Variable(name) => SymEngine::symbol(&name),
367 Self::SymEngine(expr) => expr,
368 _ => return Self::Constant(0.0),
369 };
370 Self::SymEngine(a_sym - b_sym)
371 }
372 }
373 }
374
375 #[cfg(not(feature = "symbolic"))]
376 {
377 match (self, rhs) {
378 (Self::Constant(a), Self::Constant(b)) => Self::Constant(a - b),
379 (a, b) => Self::Simple(SimpleExpression::Sub(Box::new(a), Box::new(b))),
380 }
381 }
382 }
383}
384
385impl std::ops::Mul for SymbolicExpression {
386 type Output = Self;
387
388 fn mul(self, rhs: Self) -> Self::Output {
389 #[cfg(feature = "symbolic")]
390 {
391 match (self, rhs) {
392 (Self::Constant(a), Self::Constant(b)) => Self::Constant(a * b),
394 (Self::SymEngine(a), Self::SymEngine(b)) => Self::SymEngine(a * b),
395 (a, b) => {
396 let a_sym = match a {
397 Self::Constant(val) => SymEngine::from(val),
398 Self::Variable(name) => SymEngine::symbol(&name),
399 Self::SymEngine(expr) => expr,
400 _ => return Self::Constant(0.0),
401 };
402 let b_sym = match b {
403 Self::Constant(val) => SymEngine::from(val),
404 Self::Variable(name) => SymEngine::symbol(&name),
405 Self::SymEngine(expr) => expr,
406 _ => return Self::Constant(0.0),
407 };
408 Self::SymEngine(a_sym * b_sym)
409 }
410 }
411 }
412
413 #[cfg(not(feature = "symbolic"))]
414 {
415 match (self, rhs) {
416 (Self::Constant(a), Self::Constant(b)) => Self::Constant(a * b),
417 (a, b) => Self::Simple(SimpleExpression::Mul(Box::new(a), Box::new(b))),
418 }
419 }
420 }
421}
422
423impl std::ops::Div for SymbolicExpression {
424 type Output = Self;
425
426 fn div(self, rhs: Self) -> Self::Output {
427 #[cfg(feature = "symbolic")]
428 {
429 match (self, rhs) {
430 (Self::Constant(a), Self::Constant(b)) => {
432 if b.abs() < 1e-12 {
433 Self::Constant(f64::INFINITY)
434 } else {
435 Self::Constant(a / b)
436 }
437 }
438 (Self::SymEngine(a), Self::SymEngine(b)) => Self::SymEngine(a / b),
439 (a, b) => {
440 let a_sym = match a {
441 Self::Constant(val) => SymEngine::from(val),
442 Self::Variable(name) => SymEngine::symbol(&name),
443 Self::SymEngine(expr) => expr,
444 _ => return Self::Constant(0.0),
445 };
446 let b_sym = match b {
447 Self::Constant(val) => SymEngine::from(val),
448 Self::Variable(name) => SymEngine::symbol(&name),
449 Self::SymEngine(expr) => expr,
450 _ => return Self::Constant(1.0),
451 };
452 Self::SymEngine(a_sym / b_sym)
453 }
454 }
455 }
456
457 #[cfg(not(feature = "symbolic"))]
458 {
459 match (self, rhs) {
460 (Self::Constant(a), Self::Constant(b)) => {
461 if b.abs() < 1e-12 {
462 Self::Constant(f64::INFINITY)
463 } else {
464 Self::Constant(a / b)
465 }
466 }
467 (a, b) => Self::Simple(SimpleExpression::Div(Box::new(a), Box::new(b))),
468 }
469 }
470 }
471}
472
473impl fmt::Display for SymbolicExpression {
474 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
475 match self {
476 Self::Constant(value) => write!(f, "{value}"),
477 Self::ComplexConstant(value) => {
478 if value.im == 0.0 {
479 write!(f, "{}", value.re)
480 } else if value.re == 0.0 {
481 write!(f, "{}*I", value.im)
482 } else {
483 write!(f, "{} + {}*I", value.re, value.im)
484 }
485 }
486 Self::Variable(name) => write!(f, "{name}"),
487
488 #[cfg(feature = "symbolic")]
489 Self::SymEngine(expr) => write!(f, "{expr}"),
490
491 #[cfg(not(feature = "symbolic"))]
492 Self::Simple(expr) => Self::display_simple(expr, f),
493 }
494 }
495}
496
497#[cfg(not(feature = "symbolic"))]
498impl SymbolicExpression {
499 fn display_simple(expr: &SimpleExpression, f: &mut fmt::Formatter<'_>) -> fmt::Result {
500 match expr {
501 SimpleExpression::Add(a, b) => write!(f, "({a} + {b})"),
502 SimpleExpression::Sub(a, b) => write!(f, "({a} - {b})"),
503 SimpleExpression::Mul(a, b) => write!(f, "({a} * {b})"),
504 SimpleExpression::Div(a, b) => write!(f, "({a} / {b})"),
505 SimpleExpression::Pow(a, b) => write!(f, "({a} ^ {b})"),
506 SimpleExpression::Sin(a) => write!(f, "sin({a})"),
507 SimpleExpression::Cos(a) => write!(f, "cos({a})"),
508 SimpleExpression::Exp(a) => write!(f, "exp({a})"),
509 SimpleExpression::Log(a) => write!(f, "log({a})"),
510 }
511 }
512}
513
514impl From<f64> for SymbolicExpression {
515 fn from(value: f64) -> Self {
516 Self::Constant(value)
517 }
518}
519
520impl From<Complex64> for SymbolicExpression {
521 fn from(value: Complex64) -> Self {
522 if value.im == 0.0 {
523 Self::Constant(value.re)
524 } else {
525 Self::ComplexConstant(value)
526 }
527 }
528}
529
530impl From<&str> for SymbolicExpression {
531 fn from(name: &str) -> Self {
532 Self::Variable(name.to_string())
533 }
534}
535
536impl Zero for SymbolicExpression {
537 fn zero() -> Self {
538 Self::Constant(0.0)
539 }
540
541 fn is_zero(&self) -> bool {
542 match self {
543 Self::Constant(val) => *val == 0.0,
544 Self::ComplexConstant(val) => val.is_zero(),
545 _ => false,
546 }
547 }
548}
549
550impl One for SymbolicExpression {
551 fn one() -> Self {
552 Self::Constant(1.0)
553 }
554
555 fn is_one(&self) -> bool {
556 match self {
557 Self::Constant(val) => *val == 1.0,
558 Self::ComplexConstant(val) => val.is_one(),
559 _ => false,
560 }
561 }
562}
563
564#[cfg(feature = "symbolic")]
566pub mod calculus {
567 use super::*;
568
569 pub fn diff(expr: &SymbolicExpression, var: &str) -> QuantRS2Result<SymbolicExpression> {
571 match expr {
572 SymbolicExpression::SymEngine(sym_expr) => {
573 let var_expr = SymEngine::symbol(var);
574 let result = sym_expr.diff(&var_expr);
576 Ok(SymbolicExpression::SymEngine(result))
577 }
578 _ => Err(QuantRS2Error::UnsupportedOperation(
579 "Differentiation requires SymEngine expressions".to_string(),
580 )),
581 }
582 }
583
584 pub fn integrate(expr: &SymbolicExpression, var: &str) -> QuantRS2Result<SymbolicExpression> {
588 match expr {
589 SymbolicExpression::SymEngine(sym_expr) => {
590 let var_expr = SymEngine::symbol(var);
593 let _ = var_expr; Ok(SymbolicExpression::SymEngine(sym_expr.clone()))
597 }
598 _ => Err(QuantRS2Error::UnsupportedOperation(
599 "Integration requires SymEngine expressions".to_string(),
600 )),
601 }
602 }
603
604 pub fn limit(
607 expr: &SymbolicExpression,
608 var: &str,
609 value: f64,
610 ) -> QuantRS2Result<SymbolicExpression> {
611 match expr {
612 SymbolicExpression::SymEngine(sym_expr) => {
613 let var_expr = SymEngine::symbol(var);
615 let value_expr = SymEngine::from(value);
616 let result = sym_expr.substitute(&var_expr, &value_expr);
617 Ok(SymbolicExpression::SymEngine(result))
618 }
619 _ => Err(QuantRS2Error::UnsupportedOperation(
620 "Limit computation requires SymEngine expressions".to_string(),
621 )),
622 }
623 }
624
625 pub fn expand(expr: &SymbolicExpression) -> QuantRS2Result<SymbolicExpression> {
627 match expr {
628 SymbolicExpression::SymEngine(sym_expr) => {
629 Ok(SymbolicExpression::SymEngine(sym_expr.expand()))
630 }
631 _ => Ok(expr.clone()), }
633 }
634
635 pub fn simplify(expr: &SymbolicExpression) -> QuantRS2Result<SymbolicExpression> {
637 match expr {
638 SymbolicExpression::SymEngine(sym_expr) => {
639 Ok(SymbolicExpression::SymEngine(sym_expr.simplify()))
641 }
642 _ => Ok(expr.clone()),
643 }
644 }
645}
646
647pub mod matrix {
649 use super::*;
650 use scirs2_core::ndarray::Array2;
651
652 #[derive(Debug, Clone)]
654 pub struct SymbolicMatrix {
655 pub rows: usize,
656 pub cols: usize,
657 pub elements: Vec<Vec<SymbolicExpression>>,
658 }
659
660 impl SymbolicMatrix {
661 pub fn new(rows: usize, cols: usize) -> Self {
663 let elements = vec![vec![SymbolicExpression::zero(); cols]; rows];
664 Self {
665 rows,
666 cols,
667 elements,
668 }
669 }
670
671 pub fn identity(size: usize) -> Self {
673 let mut matrix = Self::new(size, size);
674 for i in 0..size {
675 matrix.elements[i][i] = SymbolicExpression::one();
676 }
677 matrix
678 }
679
680 #[allow(unused_variables)]
682 pub fn rotation_x(theta: SymbolicExpression) -> Self {
683 let mut matrix = Self::new(2, 2);
684
685 #[cfg(feature = "symbolic")]
686 {
687 let half_theta = theta / SymbolicExpression::constant(2.0);
688 let inner_expr = match &half_theta {
689 SymbolicExpression::SymEngine(expr) => expr.clone(),
690 _ => return matrix,
691 };
692 let cos_expr = SymbolicExpression::SymEngine(
693 quantrs2_symengine_pure::ops::trig::cos(&inner_expr),
694 );
695 let sin_expr = SymbolicExpression::SymEngine(
696 quantrs2_symengine_pure::ops::trig::sin(&inner_expr),
697 );
698
699 matrix.elements[0][0] = cos_expr.clone();
700 matrix.elements[0][1] =
701 SymbolicExpression::complex_constant(Complex64::new(0.0, -1.0))
702 * sin_expr.clone();
703 matrix.elements[1][0] =
704 SymbolicExpression::complex_constant(Complex64::new(0.0, -1.0)) * sin_expr;
705 matrix.elements[1][1] = cos_expr;
706 }
707
708 #[cfg(not(feature = "symbolic"))]
709 {
710 matrix.elements[0][0] = SymbolicExpression::parse("cos(theta/2)")
712 .unwrap_or_else(|_| SymbolicExpression::one());
713 matrix.elements[0][1] = SymbolicExpression::parse("-i*sin(theta/2)")
714 .unwrap_or_else(|_| SymbolicExpression::zero());
715 matrix.elements[1][0] = SymbolicExpression::parse("-i*sin(theta/2)")
716 .unwrap_or_else(|_| SymbolicExpression::zero());
717 matrix.elements[1][1] = SymbolicExpression::parse("cos(theta/2)")
718 .unwrap_or_else(|_| SymbolicExpression::one());
719 }
720
721 matrix
722 }
723
724 pub fn evaluate(
726 &self,
727 variables: &HashMap<String, f64>,
728 ) -> QuantRS2Result<Array2<Complex64>> {
729 let mut result = Array2::<Complex64>::zeros((self.rows, self.cols));
730
731 for i in 0..self.rows {
732 for j in 0..self.cols {
733 let complex_vars: HashMap<String, Complex64> = variables
734 .iter()
735 .map(|(k, v)| (k.clone(), Complex64::new(*v, 0.0)))
736 .collect();
737
738 let value = self.elements[i][j].evaluate_complex(&complex_vars)?;
739 result[[i, j]] = value;
740 }
741 }
742
743 Ok(result)
744 }
745
746 pub fn multiply(&self, other: &Self) -> QuantRS2Result<Self> {
748 if self.cols != other.rows {
749 return Err(QuantRS2Error::InvalidInput(
750 "Matrix dimensions don't match for multiplication".to_string(),
751 ));
752 }
753
754 let mut result = Self::new(self.rows, other.cols);
755
756 for i in 0..self.rows {
757 for j in 0..other.cols {
758 let mut sum = SymbolicExpression::zero();
759 for k in 0..self.cols {
760 let product = self.elements[i][k].clone() * other.elements[k][j].clone();
761 sum = sum + product;
762 }
763 result.elements[i][j] = sum;
764 }
765 }
766
767 Ok(result)
768 }
769 }
770
771 impl fmt::Display for SymbolicMatrix {
772 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
773 writeln!(f, "SymbolicMatrix[{}x{}]:", self.rows, self.cols)?;
774 for row in &self.elements {
775 write!(f, "[")?;
776 for (j, elem) in row.iter().enumerate() {
777 if j > 0 {
778 write!(f, ", ")?;
779 }
780 write!(f, "{elem}")?;
781 }
782 writeln!(f, "]")?;
783 }
784 Ok(())
785 }
786 }
787}
788
789#[cfg(test)]
790mod tests {
791 use super::*;
792
793 #[test]
794 fn test_symbolic_expression_creation() {
795 let const_expr = SymbolicExpression::constant(std::f64::consts::PI);
796 assert!(const_expr.is_constant());
797
798 let var_expr = SymbolicExpression::variable("x");
799 assert!(!var_expr.is_constant());
800 assert_eq!(var_expr.variables(), vec!["x"]);
801 }
802
803 #[test]
804 fn test_symbolic_arithmetic() {
805 let a = SymbolicExpression::constant(2.0);
806 let b = SymbolicExpression::constant(3.0);
807 let sum = a + b;
808
809 match sum {
810 SymbolicExpression::Constant(value) => assert_eq!(value, 5.0),
811 _ => panic!("Expected constant result"),
812 }
813 }
814
815 #[test]
816 fn test_symbolic_evaluation() {
817 let mut vars = HashMap::new();
818 vars.insert("x".to_string(), 2.0);
819
820 let var_expr = SymbolicExpression::variable("x");
821 let result = var_expr
822 .evaluate(&vars)
823 .expect("Failed to evaluate expression in test_symbolic_evaluation");
824 assert_eq!(result, 2.0);
825 }
826
827 #[test]
828 fn test_symbolic_matrix() {
829 let matrix = matrix::SymbolicMatrix::identity(2);
830 assert_eq!(matrix.rows, 2);
831 assert_eq!(matrix.cols, 2);
832 assert!(matrix.elements[0][0].is_one());
833 assert!(matrix.elements[1][1].is_one());
834 assert!(matrix.elements[0][1].is_zero());
835 }
836
837 #[cfg(feature = "symbolic")]
838 #[test]
839 fn test_symengine_integration() {
840 let expr = SymbolicExpression::parse("x^2")
841 .expect("Failed to parse expression in test_symengine_integration");
842 match expr {
843 SymbolicExpression::SymEngine(_) => {
844 assert!(!expr.is_constant());
846 }
847 _ => {
848 assert!(!expr.is_constant());
850 }
851 }
852 }
853}