1use std::{
2 collections::HashMap,
3 fmt,
4 sync::{Arc, OnceLock},
5};
6
7use num::complex::Complex64;
8use serde::{Deserialize, Serialize};
9
10use crate::{ExprGraphError, ExprShapeError, ParamError, ParamResult, parameters::Parameter};
11
12#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
14pub struct ExprId(u64);
15
16impl ExprId {
17 pub fn from_index(index: usize) -> Self {
19 Self(index as u64)
20 }
21
22 pub fn index(self) -> usize {
24 self.0 as usize
25 }
26}
27
28#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
30pub enum ValueKind {
31 Real,
33 Complex,
35 Vector {
37 len: usize,
39 },
40 Matrix {
42 rows: usize,
44 cols: usize,
46 },
47}
48
49#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
51pub enum ExprShape {
52 Scalar,
54 Vector {
56 len: usize,
58 },
59 Matrix {
61 rows: usize,
63 cols: usize,
65 },
66}
67
68impl fmt::Display for ExprShape {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 match self {
71 Self::Scalar => write!(f, "scalar"),
72 Self::Vector { len } => write!(f, "vector[{len}]"),
73 Self::Matrix { rows, cols } => write!(f, "matrix[{rows}x{cols}]"),
74 }
75 }
76}
77
78pub trait ComponentIndex {
80 fn component_index(self) -> usize;
82}
83
84impl ComponentIndex for usize {
85 fn component_index(self) -> usize {
86 self
87 }
88}
89
90impl ComponentIndex for i32 {
91 fn component_index(self) -> usize {
92 usize::try_from(self).expect("component index must be nonnegative")
93 }
94}
95
96#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
97pub enum P4Component {
99 E,
101 Px,
103 Py,
105 Pz,
107}
108
109impl P4Component {
110 pub fn label(self) -> &'static str {
112 match self {
113 Self::E => "e",
114 Self::Px => "px",
115 Self::Py => "py",
116 Self::Pz => "pz",
117 }
118 }
119
120 pub fn index(self) -> usize {
122 match self {
123 Self::E => 0,
124 Self::Px => 1,
125 Self::Py => 2,
126 Self::Pz => 3,
127 }
128 }
129}
130
131#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
133pub enum UnaryOp {
134 Neg,
136 Real,
138 Imag,
140 Conj,
142 NormSqr,
144 Sqrt,
146 Exp,
148 Sin,
150 Cos,
152 Log,
154 PowI(i32),
156}
157
158impl UnaryOp {
159 pub fn evaluate(&self, value: Complex64) -> Complex64 {
161 match self {
162 Self::Neg => -value,
163 Self::Real => Complex64::from(value.re),
164 Self::Imag => Complex64::from(value.im),
165 Self::Conj => value.conj(),
166 Self::NormSqr => Complex64::from(value.norm_sqr()),
167 Self::Sqrt => value.sqrt(),
168 Self::Exp => value.exp(),
169 Self::Sin => value.sin(),
170 Self::Cos => value.cos(),
171 Self::Log => value.ln(),
172 Self::PowI(power) => value.powi(*power),
173 }
174 }
175}
176
177#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
179pub enum BinaryOp {
180 Add,
182 Sub,
184 Mul,
186 Div,
188 Atan2,
190}
191
192impl BinaryOp {
193 pub fn evaluate(&self, a: Complex64, b: Complex64) -> Complex64 {
195 match self {
196 Self::Add => a + b,
197 Self::Sub => a - b,
198 Self::Mul => a * b,
199 Self::Div => a / b,
200 Self::Atan2 => Complex64::from(a.re.atan2(b.re)),
201 }
202 }
203}
204
205#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
207pub enum ExprNode {
208 RealConst(f64),
210 ComplexConst(Complex64),
212 ScalarParam(Parameter),
214 EventScalar(Arc<str>),
216 EventP4Component {
218 name: Arc<str>,
220 component: P4Component,
222 },
223 Unary {
225 op: UnaryOp,
227 input: ExprId,
229 },
230 Binary {
232 op: BinaryOp,
234 lhs: ExprId,
236 rhs: ExprId,
238 },
239 NaryAdd {
241 terms: Vec<ExprId>,
243 },
244 NaryMul {
246 factors: Vec<ExprId>,
248 },
249 Complex {
251 re: ExprId,
253 im: ExprId,
255 },
256 Vector {
258 elements: Vec<ExprId>,
260 },
261 Matrix {
263 rows: usize,
265 cols: usize,
267 elements: Vec<ExprId>,
269 },
270 Component {
272 input: ExprId,
274 index: usize,
276 },
277 MatrixElement {
279 input: ExprId,
281 row: usize,
283 col: usize,
285 },
286 MatMul {
288 lhs: ExprId,
290 rhs: ExprId,
292 },
293 MatVec {
295 matrix: ExprId,
297 vector: ExprId,
299 },
300 Dot {
302 lhs: ExprId,
304 rhs: ExprId,
306 },
307 Solve {
309 matrix: ExprId,
311 rhs: ExprId,
313 },
314}
315
316impl From<Complex64> for ExprNode {
317 fn from(value: Complex64) -> Self {
318 if value.im == 0.0 {
319 Self::RealConst(value.re)
320 } else {
321 Self::ComplexConst(value)
322 }
323 }
324}
325
326impl ExprNode {
327 pub fn from_folded_const(value: Complex64) -> Self {
329 if value.im == 0.0 && value.im.is_sign_positive() {
330 Self::RealConst(value.re)
331 } else {
332 Self::ComplexConst(value)
333 }
334 }
335
336 pub fn const_value(&self) -> Option<Complex64> {
338 match self {
339 ExprNode::RealConst(value) => Some(Complex64::from(*value)),
340 ExprNode::ComplexConst(value) => Some(*value),
341 _ => None,
342 }
343 }
344
345 pub fn is_zero(node: &ExprNode) -> bool {
347 node.const_value()
348 .is_some_and(|value| value == Complex64::ZERO)
349 }
350
351 pub fn is_one(node: &ExprNode) -> bool {
353 node.const_value()
354 .is_some_and(|value| value == Complex64::ONE)
355 }
356
357 pub fn child_ids(&self) -> Vec<ExprId> {
359 match self {
360 Self::RealConst(_)
361 | Self::ComplexConst(_)
362 | Self::ScalarParam(_)
363 | Self::EventScalar(_)
364 | Self::EventP4Component { .. } => Vec::new(),
365 Self::Unary { input, .. }
366 | Self::Component { input, .. }
367 | Self::MatrixElement { input, .. } => vec![*input],
368 Self::Binary { lhs, rhs, .. }
369 | Self::Complex { re: lhs, im: rhs }
370 | Self::MatMul { lhs, rhs }
371 | Self::Dot { lhs, rhs } => vec![*lhs, *rhs],
372 Self::NaryAdd { terms } => terms.clone(),
373 Self::NaryMul { factors } => factors.clone(),
374 Self::Vector { elements } | Self::Matrix { elements, .. } => elements.clone(),
375 Self::MatVec { matrix, vector } => vec![*matrix, *vector],
376 Self::Solve { matrix, rhs } => vec![*matrix, *rhs],
377 }
378 }
379}
380
381#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
383pub enum ExprSourceKind {
384 Const,
386 Param,
388 Event,
390 Unary,
392 Binary,
394 Complex,
396 Vector,
398 Matrix,
400 LinearAlgebra,
402}
403
404#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
406pub struct ExprMetadata {
407 source: ExprSourceKind,
408 name: Option<Arc<str>>,
409 tags: Vec<Arc<str>>,
410}
411
412impl ExprMetadata {
413 pub fn new(source: ExprSourceKind) -> Self {
415 Self {
416 source,
417 name: None,
418 tags: Vec::new(),
419 }
420 }
421
422 pub fn source(&self) -> ExprSourceKind {
424 self.source
425 }
426
427 pub fn name(&self) -> Option<&str> {
429 self.name.as_deref()
430 }
431
432 pub fn tags(&self) -> &[Arc<str>] {
434 &self.tags
435 }
436
437 pub fn has_tag(&self, tag: &str) -> bool {
439 self.tags.iter().any(|candidate| candidate.as_ref() == tag)
440 }
441}
442
443#[derive(Clone, Debug)]
445pub struct Expr {
446 node: Arc<DagNode>,
447}
448
449#[derive(Clone, Debug)]
450struct DagNode {
451 kind: DagNodeKind,
452 metadata: ExprMetadata,
453 shape: OnceLock<Result<ExprShape, ExprShapeError>>,
454}
455
456#[derive(Clone, Debug)]
457enum DagNodeKind {
458 RealConst(f64),
459 ComplexConst(Complex64),
460 ScalarParam(Parameter),
461 EventScalar(Arc<str>),
462 EventP4Component {
463 name: Arc<str>,
464 component: P4Component,
465 },
466 Unary {
467 op: UnaryOp,
468 input: Expr,
469 },
470 Binary {
471 op: BinaryOp,
472 lhs: Expr,
473 rhs: Expr,
474 },
475 Complex {
476 re: Expr,
477 im: Expr,
478 },
479 Vector {
480 elements: Vec<Expr>,
481 },
482 Matrix {
483 rows: usize,
484 cols: usize,
485 elements: Vec<Expr>,
486 },
487 Component {
488 input: Expr,
489 index: usize,
490 },
491 MatrixElement {
492 input: Expr,
493 row: usize,
494 col: usize,
495 },
496 MatMul {
497 lhs: Expr,
498 rhs: Expr,
499 },
500 MatVec {
501 matrix: Expr,
502 vector: Expr,
503 },
504 Dot {
505 lhs: Expr,
506 rhs: Expr,
507 },
508 Solve {
509 matrix: Expr,
510 rhs: Expr,
511 },
512}
513
514impl Expr {
515 fn new(kind: DagNodeKind) -> Self {
516 let source = source_kind(&kind);
517 Self {
518 node: Arc::new(DagNode {
519 kind,
520 metadata: ExprMetadata::new(source),
521 shape: OnceLock::new(),
522 }),
523 }
524 }
525
526 pub fn named(self, name: impl Into<Arc<str>>) -> Self {
528 self.with_metadata(|metadata| metadata.name = Some(name.into()))
529 }
530
531 pub fn tagged(self, tag: impl Into<Arc<str>>) -> Self {
533 let tag = tag.into();
534 self.with_metadata(|metadata| {
535 if !metadata.tags.iter().any(|existing| existing == &tag) {
536 metadata.tags.push(tag);
537 }
538 })
539 }
540
541 pub fn tagged_with(self, tags: impl IntoIterator<Item = impl Into<Arc<str>>>) -> Self {
543 tags.into_iter().fold(self, Self::tagged)
544 }
545
546 pub fn project_tags<'a>(&self, tags: impl IntoIterator<Item = &'a str>) -> Self {
550 let tags: Vec<_> = tags.into_iter().collect();
551 self.project_tags_inner(&tags)
552 }
553
554 fn project_tags_inner(&self, tags: &[&str]) -> Self {
555 if !self.node.metadata.tags.is_empty() {
556 return if self
557 .node
558 .metadata
559 .tags
560 .iter()
561 .any(|candidate| tags.contains(&candidate.as_ref()))
562 {
563 self.clone()
564 } else {
565 self.zero_like()
566 };
567 }
568
569 let kind = match &self.node.kind {
570 DagNodeKind::RealConst(_)
571 | DagNodeKind::ComplexConst(_)
572 | DagNodeKind::ScalarParam(_)
573 | DagNodeKind::EventScalar(_)
574 | DagNodeKind::EventP4Component { .. } => return self.clone(),
575 DagNodeKind::Unary { op, input } => DagNodeKind::Unary {
576 op: *op,
577 input: input.project_tags_inner(tags),
578 },
579 DagNodeKind::Binary { op, lhs, rhs } => DagNodeKind::Binary {
580 op: *op,
581 lhs: lhs.project_tags_inner(tags),
582 rhs: rhs.project_tags_inner(tags),
583 },
584 DagNodeKind::Complex { re, im } => DagNodeKind::Complex {
585 re: re.project_tags_inner(tags),
586 im: im.project_tags_inner(tags),
587 },
588 DagNodeKind::Vector { elements } => DagNodeKind::Vector {
589 elements: elements
590 .iter()
591 .map(|value| value.project_tags_inner(tags))
592 .collect(),
593 },
594 DagNodeKind::Matrix {
595 rows,
596 cols,
597 elements,
598 } => DagNodeKind::Matrix {
599 rows: *rows,
600 cols: *cols,
601 elements: elements
602 .iter()
603 .map(|value| value.project_tags_inner(tags))
604 .collect(),
605 },
606 DagNodeKind::Component { input, index } => DagNodeKind::Component {
607 input: input.project_tags_inner(tags),
608 index: *index,
609 },
610 DagNodeKind::MatrixElement { input, row, col } => DagNodeKind::MatrixElement {
611 input: input.project_tags_inner(tags),
612 row: *row,
613 col: *col,
614 },
615 DagNodeKind::MatMul { lhs, rhs } => DagNodeKind::MatMul {
616 lhs: lhs.project_tags_inner(tags),
617 rhs: rhs.project_tags_inner(tags),
618 },
619 DagNodeKind::MatVec { matrix, vector } => DagNodeKind::MatVec {
620 matrix: matrix.project_tags_inner(tags),
621 vector: vector.project_tags_inner(tags),
622 },
623 DagNodeKind::Dot { lhs, rhs } => DagNodeKind::Dot {
624 lhs: lhs.project_tags_inner(tags),
625 rhs: rhs.project_tags_inner(tags),
626 },
627 DagNodeKind::Solve { matrix, rhs } => DagNodeKind::Solve {
628 matrix: matrix.project_tags_inner(tags),
629 rhs: rhs.project_tags_inner(tags),
630 },
631 };
632 Expr::new(kind).with_metadata(|metadata| *metadata = self.node.metadata.clone())
633 }
634
635 fn zero_like(&self) -> Self {
636 match self
637 .shape()
638 .expect("valid expression shapes are cached eagerly")
639 {
640 ExprShape::Scalar => Expr::from(0.0),
641 ExprShape::Vector { len } => vector((0..len).map(|_| Expr::from(0.0))),
642 ExprShape::Matrix { rows, cols } => {
643 matrix_from_flat(rows, cols, (0..rows * cols).map(|_| Expr::from(0.0)))
644 .expect("zero matrix dimensions match")
645 }
646 }
647 }
648
649 pub fn real(&self) -> Self {
651 unary(UnaryOp::Real, self)
652 }
653
654 pub fn imag(&self) -> Self {
656 unary(UnaryOp::Imag, self)
657 }
658
659 pub fn conj(&self) -> Self {
661 unary(UnaryOp::Conj, self)
662 }
663
664 pub fn norm_sqr(&self) -> Self {
666 unary(UnaryOp::NormSqr, self)
667 }
668
669 pub fn sqrt(&self) -> Self {
671 unary(UnaryOp::Sqrt, self)
672 }
673
674 pub fn exp(&self) -> Self {
676 unary(UnaryOp::Exp, self)
677 }
678
679 pub fn sin(&self) -> Self {
681 unary(UnaryOp::Sin, self)
682 }
683
684 pub fn cos(&self) -> Self {
686 unary(UnaryOp::Cos, self)
687 }
688
689 pub fn acos(&self) -> Self {
691 atan2((Expr::from(1.0) - self.powi(2)).sqrt(), self)
692 }
693
694 pub fn log(&self) -> Self {
696 unary(UnaryOp::Log, self)
697 }
698
699 pub fn powi(&self, power: i32) -> Self {
701 unary(UnaryOp::PowI(power), self)
702 }
703
704 pub fn component(&self, index: impl ComponentIndex) -> Self {
706 Expr::new(DagNodeKind::Component {
707 input: self.clone(),
708 index: index.component_index(),
709 })
710 }
711
712 pub fn matrix_element(&self, row: usize, col: usize) -> Self {
714 Expr::new(DagNodeKind::MatrixElement {
715 input: self.clone(),
716 row,
717 col,
718 })
719 }
720
721 pub fn to_graph(&self) -> ExprGraph {
723 GraphBuilder::new().build(self)
724 }
725
726 pub fn from_graph(graph: ExprGraph) -> Result<Self, ExprGraphError> {
734 let ExprGraph {
735 root,
736 nodes,
737 metadata,
738 } = graph;
739 let graph = ExprGraph::from_parts(root, nodes, metadata)?;
740 let mut expressions: Vec<Expr> = Vec::with_capacity(graph.nodes.len());
741 for (index, node) in graph.nodes.iter().enumerate() {
742 let child = |id: ExprId| expressions[id.index()].clone();
743 let expression = match node {
744 ExprNode::RealConst(value) => Expr::new(DagNodeKind::RealConst(*value)),
745 ExprNode::ComplexConst(value) => Expr::new(DagNodeKind::ComplexConst(*value)),
746 ExprNode::ScalarParam(parameter) => {
747 Expr::new(DagNodeKind::ScalarParam(parameter.clone()))
748 }
749 ExprNode::EventScalar(name) => {
750 Expr::new(DagNodeKind::EventScalar(Arc::clone(name)))
751 }
752 ExprNode::EventP4Component { name, component } => {
753 Expr::new(DagNodeKind::EventP4Component {
754 name: Arc::clone(name),
755 component: *component,
756 })
757 }
758 ExprNode::Unary { op, input } => Expr::new(DagNodeKind::Unary {
759 op: *op,
760 input: child(*input),
761 }),
762 ExprNode::Binary { op, lhs, rhs } => Expr::new(DagNodeKind::Binary {
763 op: *op,
764 lhs: child(*lhs),
765 rhs: child(*rhs),
766 }),
767 ExprNode::NaryAdd { terms } => terms
768 .iter()
769 .map(|id| child(*id))
770 .reduce(|lhs, rhs| binary(BinaryOp::Add, &lhs, &rhs))
771 .unwrap_or_else(|| Expr::from(0.0)),
772 ExprNode::NaryMul { factors } => factors
773 .iter()
774 .map(|id| child(*id))
775 .reduce(|lhs, rhs| binary(BinaryOp::Mul, &lhs, &rhs))
776 .unwrap_or_else(|| Expr::from(1.0)),
777 ExprNode::Complex { re, im } => Expr::new(DagNodeKind::Complex {
778 re: child(*re),
779 im: child(*im),
780 }),
781 ExprNode::Vector { elements } => Expr::new(DagNodeKind::Vector {
782 elements: elements.iter().map(|id| child(*id)).collect(),
783 }),
784 ExprNode::Matrix {
785 rows,
786 cols,
787 elements,
788 } => Expr::new(DagNodeKind::Matrix {
789 rows: *rows,
790 cols: *cols,
791 elements: elements.iter().map(|id| child(*id)).collect(),
792 }),
793 ExprNode::Component { input, index } => Expr::new(DagNodeKind::Component {
794 input: child(*input),
795 index: *index,
796 }),
797 ExprNode::MatrixElement { input, row, col } => {
798 Expr::new(DagNodeKind::MatrixElement {
799 input: child(*input),
800 row: *row,
801 col: *col,
802 })
803 }
804 ExprNode::MatMul { lhs, rhs } => Expr::new(DagNodeKind::MatMul {
805 lhs: child(*lhs),
806 rhs: child(*rhs),
807 }),
808 ExprNode::MatVec { matrix, vector } => Expr::new(DagNodeKind::MatVec {
809 matrix: child(*matrix),
810 vector: child(*vector),
811 }),
812 ExprNode::Dot { lhs, rhs } => Expr::new(DagNodeKind::Dot {
813 lhs: child(*lhs),
814 rhs: child(*rhs),
815 }),
816 ExprNode::Solve { matrix, rhs } => Expr::new(DagNodeKind::Solve {
817 matrix: child(*matrix),
818 rhs: child(*rhs),
819 }),
820 };
821 let mut dag = (*expression.node).clone();
822 dag.metadata = graph.metadata[index].clone();
823 expressions.push(Expr {
824 node: Arc::new(dag),
825 });
826 }
827 Ok(expressions[graph.root.index()].clone())
828 }
829
830 pub fn shape(&self) -> Result<ExprShape, ExprShapeError> {
837 self.node
838 .shape
839 .get_or_init(|| self.node.kind.shape())
840 .clone()
841 }
842
843 fn with_metadata(self, f: impl FnOnce(&mut ExprMetadata)) -> Self {
844 let mut node = (*self.node).clone();
845 f(&mut node.metadata);
846 Self {
847 node: Arc::new(node),
848 }
849 }
850}
851
852impl Serialize for Expr {
853 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
854 where
855 S: serde::Serializer,
856 {
857 self.to_graph().serialize(serializer)
858 }
859}
860
861impl<'de> Deserialize<'de> for Expr {
862 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
863 where
864 D: serde::Deserializer<'de>,
865 {
866 Expr::from_graph(ExprGraph::deserialize(deserializer)?).map_err(serde::de::Error::custom)
867 }
868}
869
870impl DagNodeKind {
871 fn shape(&self) -> Result<ExprShape, ExprShapeError> {
872 match self {
873 Self::RealConst(_)
874 | Self::ComplexConst(_)
875 | Self::ScalarParam(_)
876 | Self::EventScalar(_)
877 | Self::EventP4Component { .. } => Ok(ExprShape::Scalar),
878 Self::Unary { input, .. } => {
879 input.expect_shape("unary operation", ExprShape::Scalar)?;
880 Ok(ExprShape::Scalar)
881 }
882 Self::Binary { lhs, rhs, .. } => {
883 lhs.expect_shape("binary operation", ExprShape::Scalar)?;
884 rhs.expect_shape("binary operation", ExprShape::Scalar)?;
885 Ok(ExprShape::Scalar)
886 }
887 Self::Complex { re, im } => {
888 re.expect_shape("complex constructor", ExprShape::Scalar)?;
889 im.expect_shape("complex constructor", ExprShape::Scalar)?;
890 Ok(ExprShape::Scalar)
891 }
892 Self::Vector { elements } => {
893 for element in elements {
894 element.expect_shape("vector constructor", ExprShape::Scalar)?;
895 }
896 Ok(ExprShape::Vector {
897 len: elements.len(),
898 })
899 }
900 Self::Matrix {
901 rows,
902 cols,
903 elements,
904 } => {
905 let expected = rows.checked_mul(*cols).ok_or_else(|| {
906 ExprShapeError::new("matrix constructor", "row/column product overflowed")
907 })?;
908 if elements.len() != expected {
909 return Err(ExprShapeError::new(
910 "matrix constructor",
911 format!(
912 "shape {rows}x{cols} requires {expected} elements, got {}",
913 elements.len()
914 ),
915 ));
916 }
917 for element in elements {
918 element.expect_shape("matrix constructor", ExprShape::Scalar)?;
919 }
920 Ok(ExprShape::Matrix {
921 rows: *rows,
922 cols: *cols,
923 })
924 }
925 Self::Component { input, index } => {
926 let ExprShape::Vector { len } = input.shape()? else {
927 return Err(ExprShapeError::new(
928 "component",
929 format!("expected vector, got {}", input.shape()?),
930 ));
931 };
932 if *index >= len {
933 return Err(ExprShapeError::new(
934 "component",
935 format!("index {index} is out of bounds for vector[{len}]"),
936 ));
937 }
938 Ok(ExprShape::Scalar)
939 }
940 Self::MatrixElement { input, row, col } => {
941 let ExprShape::Matrix { rows, cols } = input.shape()? else {
942 return Err(ExprShapeError::new(
943 "matrix element",
944 format!("expected matrix, got {}", input.shape()?),
945 ));
946 };
947 if *row >= rows || *col >= cols {
948 return Err(ExprShapeError::new(
949 "matrix element",
950 format!("index ({row}, {col}) is out of bounds for matrix[{rows}x{cols}]"),
951 ));
952 }
953 Ok(ExprShape::Scalar)
954 }
955 Self::MatMul { lhs, rhs } => {
956 let ExprShape::Matrix {
957 rows: lhs_rows,
958 cols: lhs_cols,
959 } = lhs.shape()?
960 else {
961 return Err(ExprShapeError::new(
962 "matrix multiplication",
963 format!("left input must be a matrix, got {}", lhs.shape()?),
964 ));
965 };
966 let ExprShape::Matrix {
967 rows: rhs_rows,
968 cols: rhs_cols,
969 } = rhs.shape()?
970 else {
971 return Err(ExprShapeError::new(
972 "matrix multiplication",
973 format!("right input must be a matrix, got {}", rhs.shape()?),
974 ));
975 };
976 if lhs_cols != rhs_rows {
977 return Err(ExprShapeError::new(
978 "matrix multiplication",
979 format!("cannot multiply {lhs_rows}x{lhs_cols} by {rhs_rows}x{rhs_cols}"),
980 ));
981 }
982 Ok(ExprShape::Matrix {
983 rows: lhs_rows,
984 cols: rhs_cols,
985 })
986 }
987 Self::MatVec { matrix, vector } => {
988 let ExprShape::Matrix { rows, cols } = matrix.shape()? else {
989 return Err(ExprShapeError::new(
990 "matrix-vector multiplication",
991 format!("left input must be a matrix, got {}", matrix.shape()?),
992 ));
993 };
994 let ExprShape::Vector { len } = vector.shape()? else {
995 return Err(ExprShapeError::new(
996 "matrix-vector multiplication",
997 format!("right input must be a vector, got {}", vector.shape()?),
998 ));
999 };
1000 if cols != len {
1001 return Err(ExprShapeError::new(
1002 "matrix-vector multiplication",
1003 format!("cannot multiply {rows}x{cols} matrix by vector[{len}]"),
1004 ));
1005 }
1006 Ok(ExprShape::Vector { len: rows })
1007 }
1008 Self::Dot { lhs, rhs } => {
1009 let ExprShape::Vector { len: lhs_len } = lhs.shape()? else {
1010 return Err(ExprShapeError::new(
1011 "dot product",
1012 format!("left input must be a vector, got {}", lhs.shape()?),
1013 ));
1014 };
1015 let ExprShape::Vector { len: rhs_len } = rhs.shape()? else {
1016 return Err(ExprShapeError::new(
1017 "dot product",
1018 format!("right input must be a vector, got {}", rhs.shape()?),
1019 ));
1020 };
1021 if lhs_len != rhs_len {
1022 return Err(ExprShapeError::new(
1023 "dot product",
1024 format!("vector lengths differ: {lhs_len} and {rhs_len}"),
1025 ));
1026 }
1027 Ok(ExprShape::Scalar)
1028 }
1029 Self::Solve { matrix, rhs } => {
1030 let ExprShape::Matrix { rows, cols } = matrix.shape()? else {
1031 return Err(ExprShapeError::new(
1032 "linear solve",
1033 format!("left input must be a matrix, got {}", matrix.shape()?),
1034 ));
1035 };
1036 let ExprShape::Vector { len } = rhs.shape()? else {
1037 return Err(ExprShapeError::new(
1038 "linear solve",
1039 format!("right input must be a vector, got {}", rhs.shape()?),
1040 ));
1041 };
1042 if rows != cols || rows != len {
1043 return Err(ExprShapeError::new(
1044 "linear solve",
1045 format!("cannot solve matrix[{rows}x{cols}] against vector[{len}]"),
1046 ));
1047 }
1048 Ok(ExprShape::Vector { len })
1049 }
1050 }
1051 }
1052}
1053
1054impl Expr {
1055 fn expect_shape(
1056 &self,
1057 operation: &'static str,
1058 expected: ExprShape,
1059 ) -> Result<(), ExprShapeError> {
1060 let actual = self.shape()?;
1061 if actual != expected {
1062 return Err(ExprShapeError::new(
1063 operation,
1064 format!("expected {expected}, got {actual}"),
1065 ));
1066 }
1067 Ok(())
1068 }
1069}
1070
1071auto_ops::impl_op_ex!(+ |a: &Expr, b: &Expr| -> Expr { binary(BinaryOp::Add, a, b) });
1072
1073auto_ops::impl_op_ex!(+ |a: &Expr, b: &f64| -> Expr { binary(BinaryOp::Add, a, b) });
1074auto_ops::impl_op_ex!(+ |a: &f64, b: &Expr| -> Expr { binary(BinaryOp::Add, a, b) });
1075
1076auto_ops::impl_op_ex!(+ |a: &Expr, b: &Complex64| -> Expr { binary(BinaryOp::Add, a, b) });
1077auto_ops::impl_op_ex!(+ |a: &Complex64, b: &Expr| -> Expr { binary(BinaryOp::Add, a, b) });
1078
1079auto_ops::impl_op_ex!(+ |a: &Expr, b: &Parameter| -> Expr { binary(BinaryOp::Add, a, b) });
1080auto_ops::impl_op_ex!(+ |a: &Parameter, b: &Expr| -> Expr { binary(BinaryOp::Add, a, b) });
1081
1082auto_ops::impl_op_ex!(+ |a: &Parameter, b: &f64| -> Expr { binary(BinaryOp::Add, a, b) });
1083auto_ops::impl_op_ex!(+ |a: &f64, b: &Parameter| -> Expr { binary(BinaryOp::Add, a, b) });
1084
1085auto_ops::impl_op_ex!(+ |a: &Parameter, b: &Complex64| -> Expr { binary(BinaryOp::Add, a, b) });
1086auto_ops::impl_op_ex!(+ |a: &Complex64, b: &Parameter| -> Expr { binary(BinaryOp::Add, a, b) });
1087
1088auto_ops::impl_op_ex!(+ |a: &Parameter, b: &Parameter| -> Expr { binary(BinaryOp::Add, a, b) });
1089
1090auto_ops::impl_op_ex!(-|a: &Expr, b: &Expr| -> Expr { binary(BinaryOp::Sub, a, b) });
1091
1092auto_ops::impl_op_ex!(-|a: &Expr, b: &f64| -> Expr { binary(BinaryOp::Sub, a, b) });
1093
1094auto_ops::impl_op_ex!(-|a: &f64, b: &Expr| -> Expr { binary(BinaryOp::Sub, a, b) });
1095
1096auto_ops::impl_op_ex!(-|a: &Expr, b: &Complex64| -> Expr { binary(BinaryOp::Sub, a, b) });
1097
1098auto_ops::impl_op_ex!(-|a: &Complex64, b: &Expr| -> Expr { binary(BinaryOp::Sub, a, b) });
1099
1100auto_ops::impl_op_ex!(-|a: &Expr, b: &Parameter| -> Expr { binary(BinaryOp::Sub, a, b) });
1101
1102auto_ops::impl_op_ex!(-|a: &Parameter, b: &Expr| -> Expr { binary(BinaryOp::Sub, a, b) });
1103
1104auto_ops::impl_op_ex!(-|a: &f64, b: &Parameter| -> Expr { binary(BinaryOp::Sub, a, b) });
1105
1106auto_ops::impl_op_ex!(-|a: &Parameter, b: &f64| -> Expr { binary(BinaryOp::Sub, a, b) });
1107
1108auto_ops::impl_op_ex!(-|a: &Complex64, b: &Parameter| -> Expr { binary(BinaryOp::Sub, a, b) });
1109
1110auto_ops::impl_op_ex!(-|a: &Parameter, b: &Complex64| -> Expr { binary(BinaryOp::Sub, a, b) });
1111
1112auto_ops::impl_op_ex!(-|a: &Parameter, b: &Parameter| -> Expr { binary(BinaryOp::Sub, a, b) });
1113
1114auto_ops::impl_op_ex!(*|a: &Expr, b: &Expr| -> Expr { binary(BinaryOp::Mul, a, b) });
1115
1116auto_ops::impl_op_ex!(*|a: &Expr, b: &f64| -> Expr { binary(BinaryOp::Mul, a, b) });
1117auto_ops::impl_op_ex!(*|a: &f64, b: &Expr| -> Expr { binary(BinaryOp::Mul, a, b) });
1118
1119auto_ops::impl_op_ex!(*|a: &Expr, b: &Complex64| -> Expr { binary(BinaryOp::Mul, a, b) });
1120auto_ops::impl_op_ex!(*|a: &Complex64, b: &Expr| -> Expr { binary(BinaryOp::Mul, a, b) });
1121
1122auto_ops::impl_op_ex!(*|a: &Expr, b: &Parameter| -> Expr { binary(BinaryOp::Mul, a, b) });
1123auto_ops::impl_op_ex!(*|a: &Parameter, b: &Expr| -> Expr { binary(BinaryOp::Mul, a, b) });
1124
1125auto_ops::impl_op_ex!(*|a: &f64, b: &Parameter| -> Expr { binary(BinaryOp::Mul, a, b) });
1126auto_ops::impl_op_ex!(*|a: &Parameter, b: &f64| -> Expr { binary(BinaryOp::Mul, a, b) });
1127
1128auto_ops::impl_op_ex!(*|a: &Complex64, b: &Parameter| -> Expr { binary(BinaryOp::Mul, a, b) });
1129auto_ops::impl_op_ex!(*|a: &Parameter, b: &Complex64| -> Expr { binary(BinaryOp::Mul, a, b) });
1130
1131auto_ops::impl_op_ex!(*|a: &Parameter, b: &Parameter| -> Expr { binary(BinaryOp::Mul, a, b) });
1132
1133auto_ops::impl_op_ex!(/ |a: &Expr, b: &Expr| -> Expr {
1134 binary(BinaryOp::Div, a, b)
1135});
1136
1137auto_ops::impl_op_ex!(/ |a: &Expr, b: &Complex64| -> Expr { binary(BinaryOp::Div, a, b) });
1138auto_ops::impl_op_ex!(/ |a: &Complex64, b: &Expr| -> Expr { binary(BinaryOp::Div, a, b) });
1139
1140auto_ops::impl_op_ex!(/ |a: &Expr, b: &f64| -> Expr { binary(BinaryOp::Div, a, b) });
1141auto_ops::impl_op_ex!(/ |a: &f64, b: &Expr| -> Expr { binary(BinaryOp::Div, a, b) });
1142
1143auto_ops::impl_op_ex!(/|a: &Expr, b: &Parameter| -> Expr {
1144 binary(BinaryOp::Div, a, b)
1145});
1146auto_ops::impl_op_ex!(/|a: &Parameter, b: &Expr| -> Expr {
1147 binary(BinaryOp::Div, a, b)
1148});
1149
1150auto_ops::impl_op_ex!(/|a: &f64, b: &Parameter| -> Expr {
1151 binary(BinaryOp::Div, a, b)
1152});
1153auto_ops::impl_op_ex!(/|a: &Parameter, b: &f64| -> Expr {
1154 binary(BinaryOp::Div, a, b)
1155});
1156
1157auto_ops::impl_op_ex!(/|a: &Complex64, b: &Parameter| -> Expr {
1158 binary(BinaryOp::Div, a, b)
1159});
1160auto_ops::impl_op_ex!(/|a: &Parameter, b: &Complex64| -> Expr {
1161 binary(BinaryOp::Div, a, b)
1162});
1163
1164auto_ops::impl_op_ex!(/|a: &Parameter, b: &Parameter| -> Expr {
1165 binary(BinaryOp::Div, a, b)
1166});
1167
1168auto_ops::impl_op_ex!(-|a: &Expr| -> Expr { unary(UnaryOp::Neg, a) });
1169auto_ops::impl_op_ex!(-|a: &Parameter| -> Expr { unary(UnaryOp::Neg, a) });
1170
1171auto_ops::impl_op_ex!(+= |a: &mut Expr, b: &Expr| {
1172 *a = binary(BinaryOp::Add, &*a, b);
1173});
1174auto_ops::impl_op_ex!(+= |a: &mut Expr, b: &f64| {
1175 *a = binary(BinaryOp::Add, &*a, b);
1176});
1177auto_ops::impl_op_ex!(+= |a: &mut Expr, b: &Complex64| {
1178 *a = binary(BinaryOp::Add, &*a, b);
1179});
1180auto_ops::impl_op_ex!(+= |a: &mut Expr, b: &Parameter| {
1181 *a = binary(BinaryOp::Add, &*a, b);
1182});
1183
1184auto_ops::impl_op_ex!(-= |a: &mut Expr, b: &Expr| {
1185 *a = binary(BinaryOp::Sub, &*a, b);
1186});
1187auto_ops::impl_op_ex!(-= |a: &mut Expr, b: &f64| {
1188 *a = binary(BinaryOp::Sub, &*a, b);
1189});
1190auto_ops::impl_op_ex!(-= |a: &mut Expr, b: &Complex64| {
1191 *a = binary(BinaryOp::Sub, &*a, b);
1192});
1193auto_ops::impl_op_ex!(-= |a: &mut Expr, b: &Parameter| {
1194 *a = binary(BinaryOp::Sub, &*a, b);
1195});
1196
1197auto_ops::impl_op_ex!(*= |a: &mut Expr, b: &Expr| {
1198 *a = binary(BinaryOp::Mul, &*a, b);
1199});
1200auto_ops::impl_op_ex!(*= |a: &mut Expr, b: &f64| {
1201 *a = binary(BinaryOp::Mul, &*a, b);
1202});
1203auto_ops::impl_op_ex!(*= |a: &mut Expr, b: &Complex64| {
1204 *a = binary(BinaryOp::Mul, &*a, b);
1205});
1206auto_ops::impl_op_ex!(*= |a: &mut Expr, b: &Parameter| {
1207 *a = binary(BinaryOp::Mul, &*a, b);
1208});
1209
1210auto_ops::impl_op_ex!(/= |a: &mut Expr, b: &Expr| {
1211 *a = binary(BinaryOp::Div, &*a, b);
1212});
1213auto_ops::impl_op_ex!(/= |a: &mut Expr, b: &f64| {
1214 *a = binary(BinaryOp::Div, &*a, b);
1215});
1216auto_ops::impl_op_ex!(/= |a: &mut Expr, b: &Complex64| {
1217 *a = binary(BinaryOp::Div, &*a, b);
1218});
1219auto_ops::impl_op_ex!(/= |a: &mut Expr, b: &Parameter| {
1220 *a = binary(BinaryOp::Div, &*a, b);
1221});
1222
1223impl From<f64> for Expr {
1224 fn from(value: f64) -> Self {
1225 Self::new(DagNodeKind::RealConst(value))
1226 }
1227}
1228
1229impl From<&f64> for Expr {
1230 fn from(value: &f64) -> Self {
1231 Self::new(DagNodeKind::RealConst(*value))
1232 }
1233}
1234
1235impl From<Complex64> for Expr {
1236 fn from(value: Complex64) -> Self {
1237 Self::new(DagNodeKind::ComplexConst(value))
1238 }
1239}
1240
1241impl From<&Complex64> for Expr {
1242 fn from(value: &Complex64) -> Self {
1243 Self::new(DagNodeKind::ComplexConst(*value))
1244 }
1245}
1246
1247impl From<&Expr> for Expr {
1248 fn from(value: &Expr) -> Self {
1249 value.clone()
1250 }
1251}
1252
1253impl From<Parameter> for Expr {
1254 fn from(parameter: Parameter) -> Self {
1255 Expr::new(DagNodeKind::ScalarParam(parameter))
1256 }
1257}
1258
1259impl From<&Parameter> for Expr {
1260 fn from(parameter: &Parameter) -> Self {
1261 parameter.clone().into()
1262 }
1263}
1264
1265pub fn cis(phase: Expr) -> Expr {
1267 phase.cos() + Complex64::I * phase.sin()
1268}
1269
1270pub fn complex(re: impl Into<Expr>, im: impl Into<Expr>) -> Expr {
1272 Expr::new(DagNodeKind::Complex {
1273 re: re.into(),
1274 im: im.into(),
1275 })
1276}
1277
1278pub fn polar_complex(mag: impl Into<Expr>, phase: impl Into<Expr>) -> Expr {
1280 mag.into() * (Complex64::I * phase.into()).exp()
1281}
1282
1283pub fn event_scalar(name: impl Into<Arc<str>>) -> Expr {
1285 Expr::new(DagNodeKind::EventScalar(name.into()))
1286}
1287
1288pub fn event_p4_component(name: impl Into<Arc<str>>, component: P4Component) -> Expr {
1290 Expr::new(DagNodeKind::EventP4Component {
1291 name: name.into(),
1292 component,
1293 })
1294}
1295
1296pub fn atan2(y: impl Into<Expr>, x: impl Into<Expr>) -> Expr {
1298 binary(BinaryOp::Atan2, y, x)
1299}
1300
1301pub fn acos(value: impl Into<Expr>) -> Expr {
1303 value.into().acos()
1304}
1305
1306pub fn vector<E>(elements: impl IntoIterator<Item = E>) -> Expr
1308where
1309 E: Into<Expr>,
1310 Expr: From<E>,
1311{
1312 Expr::new(DagNodeKind::Vector {
1313 elements: elements.into_iter().map(Expr::from).collect(),
1314 })
1315}
1316
1317pub fn matrix<const R: usize, const C: usize, E>(elements: [[E; C]; R]) -> Expr
1319where
1320 E: Into<Expr>,
1321 Expr: From<E>,
1322{
1323 Expr::new(DagNodeKind::Matrix {
1324 rows: R,
1325 cols: C,
1326 elements: elements.into_iter().flatten().map(Expr::from).collect(),
1327 })
1328}
1329
1330pub fn matrix_from_flat<E>(
1338 rows: usize,
1339 cols: usize,
1340 elements: impl IntoIterator<Item = E>,
1341) -> Result<Expr, ExprShapeError>
1342where
1343 E: Into<Expr>,
1344 Expr: From<E>,
1345{
1346 if rows == 0 || cols == 0 {
1347 return Err(ExprShapeError::new(
1348 "matrix constructor",
1349 format!("matrix dimensions must be nonzero, got {rows}x{cols}"),
1350 ));
1351 }
1352 let expected = rows.checked_mul(cols).ok_or_else(|| {
1353 ExprShapeError::new("matrix constructor", "row/column product overflowed")
1354 })?;
1355 let elements = elements.into_iter().map(Expr::from).collect::<Vec<_>>();
1356 if elements.len() != expected {
1357 return Err(ExprShapeError::new(
1358 "matrix constructor",
1359 format!(
1360 "shape {rows}x{cols} requires {expected} elements, got {}",
1361 elements.len()
1362 ),
1363 ));
1364 }
1365 for element in &elements {
1366 element.expect_shape("matrix constructor", ExprShape::Scalar)?;
1367 }
1368 Ok(Expr::new(DagNodeKind::Matrix {
1369 rows,
1370 cols,
1371 elements,
1372 }))
1373}
1374
1375pub fn matmul(lhs: impl Into<Expr>, rhs: impl Into<Expr>) -> Expr {
1377 Expr::new(DagNodeKind::MatMul {
1378 lhs: lhs.into(),
1379 rhs: rhs.into(),
1380 })
1381}
1382
1383pub fn matvec(matrix: impl Into<Expr>, vector: impl Into<Expr>) -> Expr {
1385 Expr::new(DagNodeKind::MatVec {
1386 matrix: matrix.into(),
1387 vector: vector.into(),
1388 })
1389}
1390
1391pub fn dot(lhs: impl Into<Expr>, rhs: impl Into<Expr>) -> Expr {
1393 Expr::new(DagNodeKind::Dot {
1394 lhs: lhs.into(),
1395 rhs: rhs.into(),
1396 })
1397}
1398
1399pub fn solve(matrix: impl Into<Expr>, rhs: impl Into<Expr>) -> Expr {
1401 Expr::new(DagNodeKind::Solve {
1402 matrix: matrix.into(),
1403 rhs: rhs.into(),
1404 })
1405}
1406
1407fn unary(op: UnaryOp, expr: impl Into<Expr>) -> Expr {
1408 Expr::new(DagNodeKind::Unary {
1409 op,
1410 input: expr.into(),
1411 })
1412}
1413
1414fn binary(op: BinaryOp, lhs: impl Into<Expr>, rhs: impl Into<Expr>) -> Expr {
1415 Expr::new(DagNodeKind::Binary {
1416 op,
1417 lhs: lhs.into(),
1418 rhs: rhs.into(),
1419 })
1420}
1421
1422#[derive(Clone, Debug, Serialize, Deserialize)]
1424pub struct ExprGraph {
1425 root: ExprId,
1426 nodes: Vec<ExprNode>,
1427 metadata: Vec<ExprMetadata>,
1428}
1429
1430impl ExprGraph {
1431 pub fn fix_parameter(&self, name: &str, value: f64) -> ParamResult<Self> {
1439 self.map_parameter(name, |parameter| {
1440 if !parameter.bounds_spec().contains(value) {
1441 return Err(ParamError::FixedValueOutOfBounds {
1442 name: name.to_owned(),
1443 value,
1444 });
1445 }
1446 Ok(parameter.clone().with_fixed_value(value))
1447 })
1448 }
1449
1450 pub fn free_parameter(&self, name: &str) -> ParamResult<Self> {
1457 self.map_parameter(name, |parameter| Ok(parameter.clone().with_free()))
1458 }
1459
1460 fn map_parameter(
1461 &self,
1462 name: &str,
1463 mut map: impl FnMut(&Parameter) -> ParamResult<Parameter>,
1464 ) -> ParamResult<Self> {
1465 let mut found = false;
1466 let mut graph = self.clone();
1467 for node in &mut graph.nodes {
1468 if let ExprNode::ScalarParam(parameter) = node
1469 && parameter.name() == name
1470 {
1471 *parameter = map(parameter)?;
1472 found = true;
1473 }
1474 }
1475 if !found {
1476 return Err(ParamError::UnknownName(name.to_owned()));
1477 }
1478 Ok(graph)
1479 }
1480
1481 pub fn project_tags<'a>(&self, tags: impl IntoIterator<Item = &'a str>) -> Self {
1486 let tags: Vec<_> = tags.into_iter().collect();
1487 let mut nodes = Vec::new();
1488 let mut metadata = Vec::new();
1489 let mut remapped = HashMap::new();
1490 let root = self.project_node(
1491 self.root,
1492 &tags,
1493 false,
1494 &mut nodes,
1495 &mut metadata,
1496 &mut remapped,
1497 );
1498 Self {
1499 root,
1500 nodes,
1501 metadata,
1502 }
1503 }
1504
1505 fn project_node(
1506 &self,
1507 old: ExprId,
1508 tags: &[&str],
1509 retain_all: bool,
1510 nodes: &mut Vec<ExprNode>,
1511 metadata: &mut Vec<ExprMetadata>,
1512 remapped: &mut HashMap<(ExprId, bool), ExprId>,
1513 ) -> ExprId {
1514 if let Some(id) = remapped.get(&(old, retain_all)) {
1515 return *id;
1516 }
1517 let old_metadata = &self.metadata[old.index()];
1518 let matches = old_metadata
1519 .tags
1520 .iter()
1521 .any(|tag| tags.contains(&tag.as_ref()));
1522 let node = if !retain_all && !old_metadata.tags.is_empty() && !matches {
1523 ExprNode::RealConst(0.0)
1524 } else {
1525 let retain_children = retain_all || matches;
1526 let map =
1527 |id, nodes: &mut Vec<_>, metadata: &mut Vec<_>, remapped: &mut HashMap<_, _>| {
1528 self.project_node(id, tags, retain_children, nodes, metadata, remapped)
1529 };
1530 match &self.nodes[old.index()] {
1531 ExprNode::RealConst(value) => ExprNode::RealConst(*value),
1532 ExprNode::ComplexConst(value) => ExprNode::ComplexConst(*value),
1533 ExprNode::ScalarParam(value) => ExprNode::ScalarParam(value.clone()),
1534 ExprNode::EventScalar(value) => ExprNode::EventScalar(Arc::clone(value)),
1535 ExprNode::EventP4Component { name, component } => ExprNode::EventP4Component {
1536 name: Arc::clone(name),
1537 component: *component,
1538 },
1539 ExprNode::Unary { op, input } => ExprNode::Unary {
1540 op: *op,
1541 input: map(*input, nodes, metadata, remapped),
1542 },
1543 ExprNode::Binary { op, lhs, rhs } => ExprNode::Binary {
1544 op: *op,
1545 lhs: map(*lhs, nodes, metadata, remapped),
1546 rhs: map(*rhs, nodes, metadata, remapped),
1547 },
1548 ExprNode::NaryAdd { terms } => ExprNode::NaryAdd {
1549 terms: terms
1550 .iter()
1551 .map(|id| map(*id, nodes, metadata, remapped))
1552 .collect(),
1553 },
1554 ExprNode::NaryMul { factors } => ExprNode::NaryMul {
1555 factors: factors
1556 .iter()
1557 .map(|id| map(*id, nodes, metadata, remapped))
1558 .collect(),
1559 },
1560 ExprNode::Complex { re, im } => ExprNode::Complex {
1561 re: map(*re, nodes, metadata, remapped),
1562 im: map(*im, nodes, metadata, remapped),
1563 },
1564 ExprNode::Vector { elements } => ExprNode::Vector {
1565 elements: elements
1566 .iter()
1567 .map(|id| map(*id, nodes, metadata, remapped))
1568 .collect(),
1569 },
1570 ExprNode::Matrix {
1571 rows,
1572 cols,
1573 elements,
1574 } => ExprNode::Matrix {
1575 rows: *rows,
1576 cols: *cols,
1577 elements: elements
1578 .iter()
1579 .map(|id| map(*id, nodes, metadata, remapped))
1580 .collect(),
1581 },
1582 ExprNode::Component { input, index } => ExprNode::Component {
1583 input: map(*input, nodes, metadata, remapped),
1584 index: *index,
1585 },
1586 ExprNode::MatrixElement { input, row, col } => ExprNode::MatrixElement {
1587 input: map(*input, nodes, metadata, remapped),
1588 row: *row,
1589 col: *col,
1590 },
1591 ExprNode::MatMul { lhs, rhs } => ExprNode::MatMul {
1592 lhs: map(*lhs, nodes, metadata, remapped),
1593 rhs: map(*rhs, nodes, metadata, remapped),
1594 },
1595 ExprNode::MatVec { matrix, vector } => ExprNode::MatVec {
1596 matrix: map(*matrix, nodes, metadata, remapped),
1597 vector: map(*vector, nodes, metadata, remapped),
1598 },
1599 ExprNode::Dot { lhs, rhs } => ExprNode::Dot {
1600 lhs: map(*lhs, nodes, metadata, remapped),
1601 rhs: map(*rhs, nodes, metadata, remapped),
1602 },
1603 ExprNode::Solve { matrix, rhs } => ExprNode::Solve {
1604 matrix: map(*matrix, nodes, metadata, remapped),
1605 rhs: map(*rhs, nodes, metadata, remapped),
1606 },
1607 }
1608 };
1609 let id = ExprId::from_index(nodes.len());
1610 nodes.push(node);
1611 metadata.push(if matches || retain_all {
1612 old_metadata.clone()
1613 } else {
1614 ExprMetadata::new(old_metadata.source)
1615 });
1616 remapped.insert((old, retain_all), id);
1617 id
1618 }
1619
1620 pub fn from_parts(
1631 root: ExprId,
1632 nodes: Vec<ExprNode>,
1633 metadata: Vec<ExprMetadata>,
1634 ) -> Result<Self, ExprGraphError> {
1635 if nodes.is_empty() {
1636 return Err(ExprGraphError::Empty);
1637 }
1638 if nodes.len() != metadata.len() {
1639 return Err(ExprGraphError::MetadataLength {
1640 node_len: nodes.len(),
1641 metadata_len: metadata.len(),
1642 });
1643 }
1644 if root.index() >= nodes.len() {
1645 return Err(ExprGraphError::InvalidRoot {
1646 root: root.index(),
1647 node_len: nodes.len(),
1648 });
1649 }
1650 for (index, node) in nodes.iter().enumerate() {
1651 for child in node_child_ids(node) {
1652 if child.index() >= nodes.len() {
1653 return Err(ExprGraphError::InvalidChild {
1654 node: index,
1655 child: child.index(),
1656 });
1657 }
1658 if child.index() >= index {
1659 return Err(ExprGraphError::InvalidChildOrder {
1660 node: index,
1661 child: child.index(),
1662 });
1663 }
1664 }
1665 }
1666 Ok(Self {
1667 root,
1668 nodes,
1669 metadata,
1670 })
1671 }
1672
1673 pub fn root(&self) -> ExprId {
1675 self.root
1676 }
1677
1678 pub fn node(&self, id: ExprId) -> Option<&ExprNode> {
1680 self.nodes.get(id.index())
1681 }
1682
1683 pub fn nodes(&self) -> &[ExprNode] {
1685 &self.nodes
1686 }
1687
1688 pub fn metadata(&self, id: ExprId) -> Option<&ExprMetadata> {
1690 self.metadata.get(id.index())
1691 }
1692
1693 pub fn display_tree(&self) -> crate::ExprGraphTreeDisplay<'_> {
1695 crate::ExprGraphTreeDisplay::new(self)
1696 }
1697
1698 pub fn display_dot(&self) -> crate::ExprGraphDotDisplay<'_> {
1700 crate::ExprGraphDotDisplay::new(self)
1701 }
1702
1703 fn format_expression(&self, id: ExprId) -> String {
1704 self.format_child(id, ExprPrecedence::Lowest, false)
1705 }
1706
1707 fn format_child(
1708 &self,
1709 id: ExprId,
1710 parent_precedence: ExprPrecedence,
1711 parenthesize_equal: bool,
1712 ) -> String {
1713 let (text, precedence) = self.format_node_expression(id);
1714 if precedence < parent_precedence || (parenthesize_equal && precedence == parent_precedence)
1715 {
1716 format!("({text})")
1717 } else {
1718 text
1719 }
1720 }
1721
1722 fn format_node_expression(&self, id: ExprId) -> (String, ExprPrecedence) {
1723 let Some(node) = self.node(id) else {
1724 return (format!("<missing #{}>", id.index()), ExprPrecedence::Atom);
1725 };
1726
1727 match node {
1728 ExprNode::RealConst(value) => (Self::format_real_number(*value), ExprPrecedence::Atom),
1729 ExprNode::ComplexConst(value) => self.format_complex_const(*value),
1730 ExprNode::ScalarParam(parameter) => (parameter.name().to_owned(), ExprPrecedence::Atom),
1731 ExprNode::EventScalar(name) => (name.to_string(), ExprPrecedence::Atom),
1732 ExprNode::EventP4Component { name, component } => (
1733 format!("{name}.{}", component.label()),
1734 ExprPrecedence::Atom,
1735 ),
1736 ExprNode::Unary { op, input } => self.format_unary_expression(*op, *input),
1737 ExprNode::Binary { op, lhs, rhs } => self.format_binary_expression(*op, *lhs, *rhs),
1738 ExprNode::NaryAdd { terms } => self.format_sum_expression(terms),
1739 ExprNode::NaryMul { factors } => self.format_product_expression(factors),
1740 ExprNode::Complex { re, im } => (
1741 format!(
1742 "complex({}, {})",
1743 self.format_expression(*re),
1744 self.format_expression(*im)
1745 ),
1746 ExprPrecedence::Atom,
1747 ),
1748 ExprNode::Vector { elements } => (
1749 format!(
1750 "[{}]",
1751 elements
1752 .iter()
1753 .map(|id| self.format_expression(*id))
1754 .collect::<Vec<_>>()
1755 .join(", ")
1756 ),
1757 ExprPrecedence::Atom,
1758 ),
1759 ExprNode::Matrix {
1760 rows,
1761 cols,
1762 elements,
1763 } => {
1764 let rows = (0..*rows)
1765 .map(|row| {
1766 let start = row * *cols;
1767 let end = start + *cols;
1768 format!(
1769 "[{}]",
1770 elements[start..end]
1771 .iter()
1772 .map(|id| self.format_expression(*id))
1773 .collect::<Vec<_>>()
1774 .join(", ")
1775 )
1776 })
1777 .collect::<Vec<_>>()
1778 .join(", ");
1779 (format!("[{rows}]"), ExprPrecedence::Atom)
1780 }
1781 ExprNode::Component { input, index } => (
1782 format!(
1783 "{}[{index}]",
1784 self.format_child(*input, ExprPrecedence::Postfix, false)
1785 ),
1786 ExprPrecedence::Postfix,
1787 ),
1788 ExprNode::MatrixElement { input, row, col } => (
1789 format!(
1790 "{}[{row}, {col}]",
1791 self.format_child(*input, ExprPrecedence::Postfix, false)
1792 ),
1793 ExprPrecedence::Postfix,
1794 ),
1795 ExprNode::MatMul { lhs, rhs } => (
1796 format!(
1797 "matmul({}, {})",
1798 self.format_expression(*lhs),
1799 self.format_expression(*rhs)
1800 ),
1801 ExprPrecedence::Atom,
1802 ),
1803 ExprNode::MatVec { matrix, vector } => (
1804 format!(
1805 "matvec({}, {})",
1806 self.format_expression(*matrix),
1807 self.format_expression(*vector)
1808 ),
1809 ExprPrecedence::Atom,
1810 ),
1811 ExprNode::Dot { lhs, rhs } => (
1812 format!(
1813 "dot({}, {})",
1814 self.format_expression(*lhs),
1815 self.format_expression(*rhs)
1816 ),
1817 ExprPrecedence::Atom,
1818 ),
1819 ExprNode::Solve { matrix, rhs } => (
1820 format!(
1821 "solve({}, {})",
1822 self.format_expression(*matrix),
1823 self.format_expression(*rhs)
1824 ),
1825 ExprPrecedence::Atom,
1826 ),
1827 }
1828 }
1829
1830 fn format_unary_expression(&self, op: UnaryOp, input: ExprId) -> (String, ExprPrecedence) {
1831 match op {
1832 UnaryOp::Neg => (
1833 format!("-{}", self.format_child(input, ExprPrecedence::Unary, true)),
1834 ExprPrecedence::Unary,
1835 ),
1836 UnaryOp::Real => (
1837 self.format_call_expression("real", input),
1838 ExprPrecedence::Atom,
1839 ),
1840 UnaryOp::Imag => (
1841 self.format_call_expression("imag", input),
1842 ExprPrecedence::Atom,
1843 ),
1844 UnaryOp::Conj => (
1845 self.format_call_expression("conj", input),
1846 ExprPrecedence::Atom,
1847 ),
1848 UnaryOp::NormSqr => (
1849 format!("|{}|^2", self.format_expression(input)),
1850 ExprPrecedence::Pow,
1851 ),
1852 UnaryOp::Sqrt => (
1853 self.format_call_expression("sqrt", input),
1854 ExprPrecedence::Atom,
1855 ),
1856 UnaryOp::Exp => (
1857 self.format_call_expression("exp", input),
1858 ExprPrecedence::Atom,
1859 ),
1860 UnaryOp::Sin => (
1861 self.format_call_expression("sin", input),
1862 ExprPrecedence::Atom,
1863 ),
1864 UnaryOp::Cos => (
1865 self.format_call_expression("cos", input),
1866 ExprPrecedence::Atom,
1867 ),
1868 UnaryOp::Log => (
1869 self.format_call_expression("log", input),
1870 ExprPrecedence::Atom,
1871 ),
1872 UnaryOp::PowI(power) => {
1873 let exponent = if power < 0 {
1874 format!("({power})")
1875 } else {
1876 power.to_string()
1877 };
1878 (
1879 format!(
1880 "{}^{exponent}",
1881 self.format_child(input, ExprPrecedence::Pow, true)
1882 ),
1883 ExprPrecedence::Pow,
1884 )
1885 }
1886 }
1887 }
1888
1889 fn format_call_expression(&self, name: &str, input: ExprId) -> String {
1890 format!("{name}({})", self.format_expression(input))
1891 }
1892
1893 fn format_binary_expression(
1894 &self,
1895 op: BinaryOp,
1896 lhs: ExprId,
1897 rhs: ExprId,
1898 ) -> (String, ExprPrecedence) {
1899 match op {
1900 BinaryOp::Add => self.format_sum_expression(&[lhs, rhs]),
1901 BinaryOp::Sub => {
1902 let lhs = self.format_child(lhs, ExprPrecedence::Add, false);
1903 let rhs = self.format_child(rhs, ExprPrecedence::Add, true);
1904 (format!("{lhs} - {rhs}"), ExprPrecedence::Add)
1905 }
1906 BinaryOp::Mul => self.format_product_expression(&[lhs, rhs]),
1907 BinaryOp::Div => {
1908 let lhs = self.format_child(lhs, ExprPrecedence::Mul, false);
1909 let rhs = self.format_child(rhs, ExprPrecedence::Mul, true);
1910 (format!("{lhs} / {rhs}"), ExprPrecedence::Mul)
1911 }
1912 BinaryOp::Atan2 => (
1913 format!(
1914 "atan2({}, {})",
1915 self.format_expression(lhs),
1916 self.format_expression(rhs)
1917 ),
1918 ExprPrecedence::Atom,
1919 ),
1920 }
1921 }
1922
1923 fn format_sum_expression(&self, terms: &[ExprId]) -> (String, ExprPrecedence) {
1924 let mut formatted = String::new();
1925 for term in terms {
1926 let (negative, term) = self.format_signed_term(*term);
1927 if formatted.is_empty() {
1928 if negative {
1929 formatted.push('-');
1930 }
1931 formatted.push_str(&term);
1932 } else if negative {
1933 formatted.push_str(" - ");
1934 formatted.push_str(&term);
1935 } else {
1936 formatted.push_str(" + ");
1937 formatted.push_str(&term);
1938 }
1939 }
1940 if formatted.is_empty() {
1941 formatted.push('0');
1942 }
1943 (formatted, ExprPrecedence::Add)
1944 }
1945
1946 fn format_signed_term(&self, id: ExprId) -> (bool, String) {
1947 match self.node(id) {
1948 Some(ExprNode::RealConst(value)) if *value < 0.0 => {
1949 (true, Self::format_real_number(-*value))
1950 }
1951 Some(ExprNode::Unary {
1952 op: UnaryOp::Neg,
1953 input,
1954 }) => (true, self.format_child(*input, ExprPrecedence::Add, false)),
1955 Some(ExprNode::NaryMul { factors }) => {
1956 let (negative, product) = self.format_product_parts(factors);
1957 (negative, product)
1958 }
1959 _ => (false, self.format_child(id, ExprPrecedence::Add, false)),
1960 }
1961 }
1962
1963 fn format_product_expression(&self, factors: &[ExprId]) -> (String, ExprPrecedence) {
1964 let (negative, product) = self.format_product_parts(factors);
1965 if negative {
1966 (format!("-{product}"), ExprPrecedence::Unary)
1967 } else {
1968 (product, ExprPrecedence::Mul)
1969 }
1970 }
1971
1972 fn format_product_parts(&self, factors: &[ExprId]) -> (bool, String) {
1973 let mut negative = false;
1974 let mut pieces = Vec::new();
1975
1976 for factor in factors {
1977 match self.node(*factor) {
1978 Some(ExprNode::RealConst(value)) if *value < 0.0 => {
1979 negative = !negative;
1980 if *value != -1.0 || factors.len() == 1 {
1981 pieces.push(Self::format_real_number(-*value));
1982 }
1983 }
1984 Some(ExprNode::Unary {
1985 op: UnaryOp::Neg,
1986 input,
1987 }) => {
1988 negative = !negative;
1989 pieces.push(self.format_child(*input, ExprPrecedence::Mul, false));
1990 }
1991 _ => pieces.push(self.format_child(*factor, ExprPrecedence::Mul, false)),
1992 }
1993 }
1994
1995 if pieces.is_empty() {
1996 pieces.push("1".to_owned());
1997 }
1998
1999 (negative, pieces.join(" * "))
2000 }
2001
2002 fn format_complex_const(&self, value: Complex64) -> (String, ExprPrecedence) {
2003 match (value.re, value.im) {
2004 (re, 0.0) => (Self::format_real_number(re), ExprPrecedence::Atom),
2005 (0.0, im) => (Self::format_imaginary_unit(im), ExprPrecedence::Atom),
2006 (re, im) if im < 0.0 => (
2007 format!(
2008 "{} - {}",
2009 Self::format_real_number(re),
2010 Self::format_imaginary_unit(-im)
2011 ),
2012 ExprPrecedence::Add,
2013 ),
2014 (re, im) => (
2015 format!(
2016 "{} + {}",
2017 Self::format_real_number(re),
2018 Self::format_imaginary_unit(im)
2019 ),
2020 ExprPrecedence::Add,
2021 ),
2022 }
2023 }
2024
2025 fn format_real_number(value: f64) -> String {
2026 let Some((value, decimals)) = Self::nearby_simple_decimal(value) else {
2027 return value.to_string();
2028 };
2029
2030 if decimals == 0 {
2031 return value.to_string();
2032 }
2033
2034 let mut formatted = format!("{value:.decimals$}");
2035 while formatted.contains('.') && formatted.ends_with('0') {
2036 formatted.pop();
2037 }
2038 if formatted.ends_with('.') {
2039 formatted.pop();
2040 }
2041 formatted
2042 }
2043
2044 fn nearby_simple_decimal(value: f64) -> Option<(f64, usize)> {
2045 if !value.is_finite() {
2046 return None;
2047 }
2048
2049 for decimals in 0..=12 {
2050 let scale = 10_f64.powi(decimals as i32);
2051 let rounded = (value * scale).round() / scale;
2052 if Self::nearly_equal(value, rounded) {
2053 return Some((rounded, decimals));
2054 }
2055 }
2056
2057 None
2058 }
2059
2060 fn nearly_equal(lhs: f64, rhs: f64) -> bool {
2061 (lhs - rhs).abs() <= f64::EPSILON * lhs.abs().max(rhs.abs()).max(1.0) * 16.0
2062 }
2063
2064 fn format_imaginary_unit(value: f64) -> String {
2065 match value {
2066 1.0 => "i".to_owned(),
2067 -1.0 => "-i".to_owned(),
2068 value => format!("{}i", Self::format_real_number(value)),
2069 }
2070 }
2071
2072 pub(crate) fn node_label(&self, id: ExprId, node: &ExprNode) -> String {
2073 let mut label = match node {
2074 ExprNode::RealConst(value) => {
2075 format!(
2076 "#{} RealConst({})",
2077 id.index(),
2078 Self::format_real_number(*value)
2079 )
2080 }
2081 ExprNode::ComplexConst(value) => {
2082 let (value, _) = self.format_complex_const(*value);
2083 format!("#{} ComplexConst({value})", id.index())
2084 }
2085 ExprNode::ScalarParam(parameter) => {
2086 format!("#{} ScalarParam({})", id.index(), parameter.name())
2087 }
2088 ExprNode::EventScalar(name) => format!("#{} EventScalar({name})", id.index()),
2089 ExprNode::EventP4Component { name, component } => {
2090 format!(
2091 "#{} EventP4Component({name}.{})",
2092 id.index(),
2093 component.label()
2094 )
2095 }
2096 ExprNode::Unary { op, .. } => format!("#{} Unary({op:?})", id.index()),
2097 ExprNode::Binary { op, .. } => format!("#{} Binary({op:?})", id.index()),
2098 ExprNode::NaryAdd { terms } => {
2099 format!("#{} NaryAdd(len={})", id.index(), terms.len())
2100 }
2101 ExprNode::NaryMul { factors } => {
2102 format!("#{} NaryMul(len={})", id.index(), factors.len())
2103 }
2104 ExprNode::Complex { .. } => format!("#{} Complex", id.index()),
2105 ExprNode::Vector { elements } => {
2106 format!("#{} Vector(len={})", id.index(), elements.len())
2107 }
2108 ExprNode::Matrix { rows, cols, .. } => {
2109 format!("#{} Matrix({rows}x{cols})", id.index())
2110 }
2111 ExprNode::Component { index, .. } => {
2112 format!("#{} Component(index={index})", id.index())
2113 }
2114 ExprNode::MatrixElement { row, col, .. } => {
2115 format!("#{} MatrixElement(row={row}, col={col})", id.index())
2116 }
2117 ExprNode::MatMul { .. } => format!("#{} MatMul", id.index()),
2118 ExprNode::MatVec { .. } => format!("#{} MatVec", id.index()),
2119 ExprNode::Dot { .. } => format!("#{} Dot", id.index()),
2120 ExprNode::Solve { .. } => format!("#{} Solve", id.index()),
2121 };
2122
2123 if let Some(metadata) = self.metadata(id) {
2124 if let Some(name) = metadata.name() {
2125 label.push_str(&format!(" name=\"{name}\""));
2126 }
2127 if !metadata.tags().is_empty() {
2128 label.push_str(" tags=[");
2129 for (index, tag) in metadata.tags().iter().enumerate() {
2130 if index != 0 {
2131 label.push_str(", ");
2132 }
2133 label.push_str(tag);
2134 }
2135 label.push(']');
2136 }
2137 }
2138
2139 label
2140 }
2141}
2142
2143impl fmt::Display for ExprGraph {
2144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2145 f.write_str(&self.format_expression(self.root))
2146 }
2147}
2148
2149#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
2150enum ExprPrecedence {
2151 Lowest,
2152 Add,
2153 Mul,
2154 Unary,
2155 Pow,
2156 Postfix,
2157 Atom,
2158}
2159
2160pub(crate) fn node_children(node: &ExprNode) -> Vec<(String, ExprId)> {
2161 match node {
2162 ExprNode::RealConst(_)
2163 | ExprNode::ComplexConst(_)
2164 | ExprNode::ScalarParam(_)
2165 | ExprNode::EventScalar(_)
2166 | ExprNode::EventP4Component { .. } => Vec::new(),
2167 ExprNode::Unary { input, .. } => vec![("input".into(), *input)],
2168 ExprNode::Binary { lhs, rhs, .. } => vec![("lhs".into(), *lhs), ("rhs".into(), *rhs)],
2169 ExprNode::NaryAdd { terms } => terms
2170 .iter()
2171 .enumerate()
2172 .map(|(index, id)| (format!("term[{index}]"), *id))
2173 .collect(),
2174 ExprNode::NaryMul { factors } => factors
2175 .iter()
2176 .enumerate()
2177 .map(|(index, id)| (format!("factor[{index}]"), *id))
2178 .collect(),
2179 ExprNode::Complex { re, im } => vec![("re".into(), *re), ("im".into(), *im)],
2180 ExprNode::Vector { elements } => elements
2181 .iter()
2182 .enumerate()
2183 .map(|(index, id)| (format!("element[{index}]"), *id))
2184 .collect(),
2185 ExprNode::Matrix { cols, elements, .. } => elements
2186 .iter()
2187 .enumerate()
2188 .map(|(index, id)| (format!("element[{},{}]", index / cols, index % cols), *id))
2189 .collect(),
2190 ExprNode::Component { input, .. } | ExprNode::MatrixElement { input, .. } => {
2191 vec![("input".into(), *input)]
2192 }
2193 ExprNode::MatMul { lhs, rhs } | ExprNode::Dot { lhs, rhs } => {
2194 vec![("lhs".into(), *lhs), ("rhs".into(), *rhs)]
2195 }
2196 ExprNode::MatVec { matrix, vector } => {
2197 vec![("matrix".into(), *matrix), ("vector".into(), *vector)]
2198 }
2199 ExprNode::Solve { matrix, rhs } => vec![("matrix".into(), *matrix), ("rhs".into(), *rhs)],
2200 }
2201}
2202
2203fn node_child_ids(node: &ExprNode) -> Vec<ExprId> {
2204 node_children(node)
2205 .into_iter()
2206 .map(|(_, child)| child)
2207 .collect()
2208}
2209
2210#[derive(Default)]
2211struct GraphBuilder {
2212 nodes: Vec<ExprNode>,
2213 metadata: Vec<ExprMetadata>,
2214 ids: HashMap<usize, ExprId>,
2215}
2216
2217impl GraphBuilder {
2218 fn new() -> Self {
2219 Self::default()
2220 }
2221
2222 fn build(mut self, expr: &Expr) -> ExprGraph {
2223 let root = self.visit(expr);
2224 ExprGraph {
2225 root,
2226 nodes: self.nodes,
2227 metadata: self.metadata,
2228 }
2229 }
2230
2231 fn visit(&mut self, expr: &Expr) -> ExprId {
2232 let key = Arc::as_ptr(&expr.node) as usize;
2233 if let Some(id) = self.ids.get(&key) {
2234 return *id;
2235 }
2236 let node = match &expr.node.kind {
2237 DagNodeKind::RealConst(value) => ExprNode::RealConst(*value),
2238 DagNodeKind::ComplexConst(value) => ExprNode::ComplexConst(*value),
2239 DagNodeKind::ScalarParam(parameter) => ExprNode::ScalarParam(parameter.clone()),
2240 DagNodeKind::EventScalar(name) => ExprNode::EventScalar(Arc::clone(name)),
2241 DagNodeKind::EventP4Component { name, component } => ExprNode::EventP4Component {
2242 name: Arc::clone(name),
2243 component: *component,
2244 },
2245 DagNodeKind::Unary { op, input } => {
2246 let input = self.visit(input);
2247 ExprNode::Unary { op: *op, input }
2248 }
2249 DagNodeKind::Binary { op, lhs, rhs } => {
2250 let lhs = self.visit(lhs);
2251 let rhs = self.visit(rhs);
2252 ExprNode::Binary { op: *op, lhs, rhs }
2253 }
2254 DagNodeKind::Complex { re, im } => {
2255 let re = self.visit(re);
2256 let im = self.visit(im);
2257 ExprNode::Complex { re, im }
2258 }
2259 DagNodeKind::Vector { elements } => ExprNode::Vector {
2260 elements: elements.iter().map(|expr| self.visit(expr)).collect(),
2261 },
2262 DagNodeKind::Matrix {
2263 rows,
2264 cols,
2265 elements,
2266 } => ExprNode::Matrix {
2267 rows: *rows,
2268 cols: *cols,
2269 elements: elements.iter().map(|expr| self.visit(expr)).collect(),
2270 },
2271 DagNodeKind::Component { input, index } => {
2272 let input = self.visit(input);
2273 ExprNode::Component {
2274 input,
2275 index: *index,
2276 }
2277 }
2278 DagNodeKind::MatrixElement { input, row, col } => {
2279 let input = self.visit(input);
2280 ExprNode::MatrixElement {
2281 input,
2282 row: *row,
2283 col: *col,
2284 }
2285 }
2286 DagNodeKind::MatMul { lhs, rhs } => {
2287 let lhs = self.visit(lhs);
2288 let rhs = self.visit(rhs);
2289 ExprNode::MatMul { lhs, rhs }
2290 }
2291 DagNodeKind::MatVec { matrix, vector } => {
2292 let matrix = self.visit(matrix);
2293 let vector = self.visit(vector);
2294 ExprNode::MatVec { matrix, vector }
2295 }
2296 DagNodeKind::Dot { lhs, rhs } => {
2297 let lhs = self.visit(lhs);
2298 let rhs = self.visit(rhs);
2299 ExprNode::Dot { lhs, rhs }
2300 }
2301 DagNodeKind::Solve { matrix, rhs } => {
2302 let matrix = self.visit(matrix);
2303 let rhs = self.visit(rhs);
2304 ExprNode::Solve { matrix, rhs }
2305 }
2306 };
2307
2308 let id = ExprId::from_index(self.nodes.len());
2309 self.nodes.push(node);
2310 self.metadata.push(expr.node.metadata.clone());
2311 self.ids.insert(key, id);
2312 id
2313 }
2314}
2315
2316fn source_kind(kind: &DagNodeKind) -> ExprSourceKind {
2317 match kind {
2318 DagNodeKind::RealConst(_) | DagNodeKind::ComplexConst(_) => ExprSourceKind::Const,
2319 DagNodeKind::ScalarParam(_) => ExprSourceKind::Param,
2320 DagNodeKind::EventScalar(_) | DagNodeKind::EventP4Component { .. } => ExprSourceKind::Event,
2321 DagNodeKind::Unary { .. } => ExprSourceKind::Unary,
2322 DagNodeKind::Binary { .. } => ExprSourceKind::Binary,
2323 DagNodeKind::Complex { .. } => ExprSourceKind::Complex,
2324 DagNodeKind::Vector { .. } | DagNodeKind::Component { .. } | DagNodeKind::Dot { .. } => {
2325 ExprSourceKind::Vector
2326 }
2327 DagNodeKind::Matrix { .. } | DagNodeKind::MatrixElement { .. } => ExprSourceKind::Matrix,
2328 DagNodeKind::MatMul { .. } | DagNodeKind::MatVec { .. } | DagNodeKind::Solve { .. } => {
2329 ExprSourceKind::LinearAlgebra
2330 }
2331 }
2332}
2333
2334#[cfg(test)]
2335mod tests {
2336 use super::*;
2337 use crate::parameter;
2338
2339 #[test]
2340 fn builds_target_syntax_without_layout_or_context() {
2341 let model = (Complex64::I * parameter!("y", initial : 1.0, bounds : (0.0, 2.0))
2342 + parameter!("x"))
2343 .norm_sqr();
2344
2345 let graph = model.to_graph();
2346 assert!(matches!(
2347 graph.node(graph.root()),
2348 Some(ExprNode::Unary {
2349 op: UnaryOp::NormSqr,
2350 ..
2351 })
2352 ));
2353 }
2354
2355 #[test]
2356 fn parameter_nodes_store_specs_but_do_not_make_layouts() {
2357 let graph = Expr::from(parameter!("x", initial: 1.0)).to_graph();
2358 assert!(matches!(
2359 graph.node(graph.root()),
2360 Some(ExprNode::ScalarParam(spec)) if spec.name() == "x"
2361 ));
2362 }
2363
2364 #[test]
2365 fn complex_constructor_builds_expression_node() {
2366 let graph = complex(parameter!("re"), parameter!("im")).to_graph();
2367
2368 assert!(matches!(
2369 graph.node(graph.root()),
2370 Some(ExprNode::Complex { .. })
2371 ));
2372 }
2373
2374 #[test]
2375 fn polar_complex_lowers_to_expression_graph() {
2376 let graph = polar_complex(parameter!("mag"), parameter!("phase")).to_graph();
2377
2378 assert!(graph.nodes().iter().any(|node| matches!(
2379 node,
2380 ExprNode::Unary {
2381 op: UnaryOp::Exp,
2382 ..
2383 }
2384 )));
2385 }
2386
2387 #[test]
2388 fn metadata_survives_graph_construction() {
2389 let graph = event_scalar("mass")
2390 .named("event mass")
2391 .tagged("data")
2392 .tagged("data")
2393 .to_graph();
2394 let metadata = graph.metadata(graph.root()).unwrap();
2395 assert_eq!(metadata.name(), Some("event mass"));
2396 assert_eq!(metadata.tags(), &[Arc::from("data")]);
2397 assert!(metadata.has_tag("data"));
2398 }
2399
2400 #[test]
2401 fn expressions_round_trip_through_serde_with_metadata() {
2402 let expression = ((parameter!("x", initial: 1.0) + 2.0).named("offset")
2403 * event_scalar("mass").tagged("data"))
2404 .tagged("model");
2405 let encoded = serde_json::to_string(&expression).unwrap();
2406 let decoded: Expr = serde_json::from_str(&encoded).unwrap();
2407
2408 assert_eq!(
2409 serde_json::to_value(expression.to_graph()).unwrap(),
2410 serde_json::to_value(decoded.to_graph()).unwrap()
2411 );
2412 }
2413
2414 #[test]
2415 fn display_formats_graph_as_labeled_tree() {
2416 let graph = ((parameter!("x") + 1.0).named("offset") * event_scalar("mass").tagged("data"))
2417 .to_graph();
2418 let display = graph.display_tree().to_string();
2419
2420 assert!(display.starts_with("ExprGraph(root=#"));
2421 assert!(display.contains("Binary(Mul)"));
2422 assert!(display.contains("┣ lhs:"));
2423 assert!(display.contains("┗ rhs:"));
2424 assert!(display.contains("Binary(Add) name=\"offset\""));
2425 assert!(display.contains("ScalarParam(x)"));
2426 assert!(display.contains("RealConst(1)"));
2427 assert!(display.contains("EventScalar(mass) tags=[data]"));
2428 }
2429
2430 #[test]
2431 fn display_formats_graph_as_expression() {
2432 let costheta = Expr::from(parameter!("costheta"));
2433 let phi = event_scalar("phi");
2434 let p = Expr::from(parameter!("p"));
2435 let phase = Expr::from(7.0) * Complex64::I;
2436 let graph =
2437 (((costheta.powi(2) * phi.sin()) - 5.2).norm_sqr() * p.conj() - phase.exp()).to_graph();
2438
2439 assert_eq!(
2440 graph.to_string(),
2441 "|costheta^2 * sin(phi) - 5.2|^2 * conj(p) - exp(7 * i)"
2442 );
2443 }
2444
2445 #[test]
2446 fn display_parenthesizes_when_precedence_requires_it() {
2447 let a = Expr::from(parameter!("a"));
2448 let b = Expr::from(parameter!("b"));
2449 let c = Expr::from(parameter!("c"));
2450
2451 assert_eq!(
2452 (a.clone() * (b.clone() + c.clone())).to_graph().to_string(),
2453 "a * (b + c)"
2454 );
2455 assert_eq!(
2456 (a.clone() - (b.clone() - c.clone())).to_graph().to_string(),
2457 "a - (b - c)"
2458 );
2459 assert_eq!(((a / b) / c).to_graph().to_string(), "a / b / c");
2460 }
2461
2462 #[test]
2463 fn display_rounds_tiny_float_representation_noise() {
2464 let metadata = ExprMetadata::new(ExprSourceKind::Const);
2465 let graph = ExprGraph::from_parts(
2466 ExprId::from_index(2),
2467 vec![
2468 ExprNode::RealConst(2.9999999999999996),
2469 ExprNode::ComplexConst(Complex64::new(0.30000000000000004, 1.9999999999999998)),
2470 ExprNode::Binary {
2471 op: BinaryOp::Add,
2472 lhs: ExprId::from_index(0),
2473 rhs: ExprId::from_index(1),
2474 },
2475 ],
2476 vec![metadata.clone(), metadata.clone(), metadata],
2477 )
2478 .unwrap();
2479
2480 assert_eq!(graph.to_string(), "3 + 0.3 + 2i");
2481 assert!(graph.display_tree().to_string().contains("RealConst(3)"));
2482 assert!(
2483 graph
2484 .display_tree()
2485 .to_string()
2486 .contains("ComplexConst(0.3 + 2i)")
2487 );
2488 }
2489
2490 #[test]
2491 fn display_formats_p4_components_and_atan2() {
2492 let expr = atan2(
2493 event_p4_component("ks1", P4Component::Py),
2494 event_p4_component("ks1", P4Component::Px),
2495 );
2496
2497 assert_eq!(expr.to_graph().to_string(), "atan2(ks1.py, ks1.px)");
2498 }
2499
2500 #[test]
2501 fn graph_from_parts_validates_structure() {
2502 let metadata = ExprMetadata::new(ExprSourceKind::Const);
2503 let graph = ExprGraph::from_parts(
2504 ExprId::from_index(1),
2505 vec![
2506 ExprNode::RealConst(1.0),
2507 ExprNode::Unary {
2508 op: UnaryOp::Neg,
2509 input: ExprId::from_index(0),
2510 },
2511 ],
2512 vec![metadata.clone(), metadata.clone()],
2513 )
2514 .unwrap();
2515 assert!(matches!(
2516 graph.node(graph.root()),
2517 Some(ExprNode::Unary {
2518 op: UnaryOp::Neg,
2519 ..
2520 })
2521 ));
2522
2523 let err = ExprGraph::from_parts(
2524 ExprId::from_index(0),
2525 vec![ExprNode::RealConst(1.0)],
2526 Vec::new(),
2527 )
2528 .unwrap_err();
2529 assert!(matches!(err, ExprGraphError::MetadataLength { .. }));
2530
2531 let err = ExprGraph::from_parts(
2532 ExprId::from_index(0),
2533 vec![ExprNode::Unary {
2534 op: UnaryOp::Neg,
2535 input: ExprId::from_index(0),
2536 }],
2537 vec![metadata],
2538 )
2539 .unwrap_err();
2540 assert!(matches!(err, ExprGraphError::InvalidChildOrder { .. }));
2541 }
2542
2543 #[test]
2544 fn graph_preserves_unsimplified_expression_shape() {
2545 let graph = (parameter!("x") + 0.0).to_graph();
2546 assert!(matches!(
2547 graph.node(graph.root()),
2548 Some(ExprNode::Binary {
2549 op: BinaryOp::Add,
2550 ..
2551 })
2552 ));
2553 }
2554
2555 #[test]
2556 fn graph_preserves_written_operand_order_for_commutative_ops() {
2557 let left_param = (parameter!("x") + 1.0).to_graph();
2558 assert!(matches!(
2559 left_param.node(left_param.root()),
2560 Some(ExprNode::Binary {
2561 op: BinaryOp::Add,
2562 lhs,
2563 rhs
2564 }) if matches!(left_param.node(*lhs), Some(ExprNode::ScalarParam(parameter)) if parameter.name() == "x")
2565 && matches!(left_param.node(*rhs), Some(ExprNode::RealConst(1.0)))
2566 ));
2567
2568 let right_param = (1.0 + parameter!("x")).to_graph();
2569 assert!(matches!(
2570 right_param.node(right_param.root()),
2571 Some(ExprNode::Binary {
2572 op: BinaryOp::Add,
2573 lhs,
2574 rhs
2575 }) if matches!(right_param.node(*lhs), Some(ExprNode::RealConst(1.0)))
2576 && matches!(right_param.node(*rhs), Some(ExprNode::ScalarParam(parameter)) if parameter.name() == "x")
2577 ));
2578 }
2579
2580 #[test]
2581 fn represents_kmatrix_style_solve_graph() {
2582 let beta = vector([
2583 complex(parameter!("b0_re"), parameter!("b0_im")),
2584 complex(parameter!("b1_re"), parameter!("b1_im")),
2585 ]);
2586 let a = matrix([
2587 [Complex64::new(1.0, 0.0), Complex64::new(0.0, 1.0)],
2588 [Complex64::new(0.0, -1.0), Complex64::new(1.0, 0.0)],
2589 ]);
2590 let graph = solve(a, beta).component(0).to_graph();
2591
2592 assert!(
2593 graph
2594 .nodes()
2595 .iter()
2596 .any(|node| matches!(node, ExprNode::Solve { .. }))
2597 );
2598 }
2599
2600 #[test]
2601 fn graph_builder_preserves_shared_dag_nodes() {
2602 let shared = event_scalar("x").sin();
2603 let expression = vector((0..1_000).map(|_| shared.clone()));
2604 let graph = expression.to_graph();
2605
2606 assert_eq!(graph.nodes().len(), 3);
2607 let ExprNode::Vector { elements } = graph.node(graph.root()).unwrap() else {
2608 panic!("root should be a vector");
2609 };
2610 assert!(elements.windows(2).all(|pair| pair[0] == pair[1]));
2611 }
2612
2613 #[test]
2614 fn dynamic_matrices_and_shapes_are_checked_eagerly() {
2615 let dynamic = matrix_from_flat(2, 2, [1.0, 2.0, 3.0, 4.0]).unwrap();
2616 assert_eq!(
2617 dynamic.shape().unwrap(),
2618 ExprShape::Matrix { rows: 2, cols: 2 }
2619 );
2620 assert!(matrix_from_flat(2, 2, [1.0, 2.0, 3.0]).is_err());
2621 assert!(matmul(dynamic, matrix([[1.0, 2.0, 3.0]])).shape().is_err());
2622 }
2623
2624 #[test]
2625 fn assignment_operators_build_binary_expression_nodes() {
2626 let mut expr = Expr::from(parameter!("x"));
2627 expr += parameter!("y");
2628 expr -= 1.0;
2629 expr *= Complex64::I;
2630 expr /= Expr::from(parameter!("z"));
2631
2632 let graph = expr.to_graph();
2633 assert!(matches!(
2634 graph.node(graph.root()),
2635 Some(ExprNode::Binary {
2636 op: BinaryOp::Div,
2637 ..
2638 })
2639 ));
2640 assert_eq!(
2641 graph
2642 .nodes()
2643 .iter()
2644 .filter(|node| matches!(node, ExprNode::Binary { .. }))
2645 .count(),
2646 4
2647 );
2648 }
2649
2650 #[test]
2651 fn assignment_operators_accept_borrowed_rhs_values() {
2652 let y = parameter!("y");
2653 let one = 1.0;
2654 let i = Complex64::I;
2655 let z = Expr::from(parameter!("z"));
2656
2657 let mut expr = Expr::from(parameter!("x"));
2658 expr += &y;
2659 expr -= &one;
2660 expr *= &i;
2661 expr /= &z;
2662
2663 let graph = expr.to_graph();
2664 assert!(matches!(
2665 graph.node(graph.root()),
2666 Some(ExprNode::Binary {
2667 op: BinaryOp::Div,
2668 ..
2669 })
2670 ));
2671 }
2672}