1use std::{
2 collections::{HashMap, HashSet},
3 fmt,
4 hash::Hash,
5 sync::{Arc, OnceLock},
6};
7
8use num::complex::Complex64;
9use serde::{Deserialize, Serialize};
10
11use crate::{
12 ExprGraphError, ExprShapeError, ParamError, ParamResult,
13 parameters::{InitialSpec, ParamState, Parameter},
14};
15
16#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
18pub struct ExprId(u64);
19
20impl ExprId {
21 pub fn from_index(index: usize) -> Self {
23 Self(index as u64)
24 }
25
26 pub fn index(self) -> usize {
28 self.0 as usize
29 }
30}
31
32#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
34pub enum ValueKind {
35 Real,
37 Complex,
39 Vector {
41 len: usize,
43 },
44 Matrix {
46 rows: usize,
48 cols: usize,
50 },
51}
52
53#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
55pub enum NumberClass {
56 Unknown,
58 Real,
60 Imaginary,
62 Complex,
64}
65
66#[derive(Copy, Clone, Debug, PartialEq, Eq)]
68pub struct ExprNodeSemantics {
69 pub value_kind: ValueKind,
71 pub number_class: NumberClass,
73}
74
75fn add_number_class(lhs: NumberClass, rhs: NumberClass) -> NumberClass {
76 use NumberClass::{Complex, Imaginary, Real, Unknown};
77 match (lhs, rhs) {
78 (Real, Real) => Real,
79 (Imaginary, Imaginary) => Imaginary,
80 (Complex, _) | (_, Complex) => Complex,
81 (Unknown, _) | (_, Unknown) => Unknown,
82 _ => Complex,
83 }
84}
85
86fn mul_number_class(lhs: NumberClass, rhs: NumberClass) -> NumberClass {
87 use NumberClass::{Complex, Imaginary, Real, Unknown};
88 match (lhs, rhs) {
89 (Real, Real) | (Imaginary, Imaginary) => Real,
90 (Real, Imaginary) | (Imaginary, Real) => Imaginary,
91 (Complex, _) | (_, Complex) => Complex,
92 (Unknown, _) | (_, Unknown) => Unknown,
93 }
94}
95
96#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
98pub enum ExprDependencyKind {
99 Constant,
101 Parameter,
103 Event,
105 Children,
107}
108
109#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
111pub enum ExprShape {
112 Scalar,
114 Vector {
116 len: usize,
118 },
119 Matrix {
121 rows: usize,
123 cols: usize,
125 },
126}
127
128impl fmt::Display for ExprShape {
129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130 match self {
131 Self::Scalar => write!(f, "scalar"),
132 Self::Vector { len } => write!(f, "vector[{len}]"),
133 Self::Matrix { rows, cols } => write!(f, "matrix[{rows}x{cols}]"),
134 }
135 }
136}
137
138pub trait ComponentIndex {
140 fn component_index(self) -> usize;
142}
143
144impl ComponentIndex for usize {
145 fn component_index(self) -> usize {
146 self
147 }
148}
149
150impl ComponentIndex for i32 {
151 fn component_index(self) -> usize {
152 usize::try_from(self).expect("component index must be nonnegative")
153 }
154}
155
156#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
157pub enum P4Component {
159 E,
161 Px,
163 Py,
165 Pz,
167}
168
169impl P4Component {
170 pub fn label(self) -> &'static str {
172 match self {
173 Self::E => "e",
174 Self::Px => "px",
175 Self::Py => "py",
176 Self::Pz => "pz",
177 }
178 }
179
180 pub fn index(self) -> usize {
182 match self {
183 Self::E => 0,
184 Self::Px => 1,
185 Self::Py => 2,
186 Self::Pz => 3,
187 }
188 }
189}
190
191#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
193pub enum UnaryOp {
194 Neg,
196 Real,
198 Imag,
200 Conj,
202 NormSqr,
204 Sqrt,
206 Exp,
208 Sin,
210 Cos,
212 Log,
214 PowI(i32),
216}
217
218impl UnaryOp {
219 pub fn evaluate(&self, value: Complex64) -> Complex64 {
221 match self {
222 Self::Neg => -value,
223 Self::Real => Complex64::from(value.re),
224 Self::Imag => Complex64::from(value.im),
225 Self::Conj => value.conj(),
226 Self::NormSqr => Complex64::from(value.norm_sqr()),
227 Self::Sqrt => value.sqrt(),
228 Self::Exp => value.exp(),
229 Self::Sin => value.sin(),
230 Self::Cos => value.cos(),
231 Self::Log => value.ln(),
232 Self::PowI(power) => value.powi(*power),
233 }
234 }
235}
236
237#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
239pub enum BinaryOp {
240 Add,
242 Sub,
244 Mul,
246 Div,
248 Atan2,
250}
251
252impl BinaryOp {
253 pub fn evaluate(&self, a: Complex64, b: Complex64) -> Complex64 {
255 match self {
256 Self::Add => a + b,
257 Self::Sub => a - b,
258 Self::Mul => a * b,
259 Self::Div => a / b,
260 Self::Atan2 => Complex64::from(a.re.atan2(b.re)),
261 }
262 }
263}
264
265#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
267pub enum ExprNode {
268 RealConst(f64),
270 ComplexConst(Complex64),
272 ScalarParam(Parameter),
274 EventScalar(Arc<str>),
276 EventP4Component {
278 name: Arc<str>,
280 component: P4Component,
282 },
283 Unary {
285 op: UnaryOp,
287 input: ExprId,
289 },
290 Binary {
292 op: BinaryOp,
294 lhs: ExprId,
296 rhs: ExprId,
298 },
299 NaryAdd {
301 terms: Vec<ExprId>,
303 },
304 NaryMul {
306 factors: Vec<ExprId>,
308 },
309 Complex {
311 re: ExprId,
313 im: ExprId,
315 },
316 Vector {
318 elements: Vec<ExprId>,
320 },
321 Matrix {
323 rows: usize,
325 cols: usize,
327 elements: Vec<ExprId>,
329 },
330 Component {
332 input: ExprId,
334 index: usize,
336 },
337 MatrixElement {
339 input: ExprId,
341 row: usize,
343 col: usize,
345 },
346 MatMul {
348 lhs: ExprId,
350 rhs: ExprId,
352 },
353 MatVec {
355 matrix: ExprId,
357 vector: ExprId,
359 },
360 Dot {
362 lhs: ExprId,
364 rhs: ExprId,
366 },
367 Solve {
369 matrix: ExprId,
371 rhs: ExprId,
373 },
374}
375
376#[doc(hidden)]
382#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
383pub struct ParameterStructuralKey {
384 name: Arc<str>,
385 state: ParameterStateStructuralKey,
386 initial: InitialStructuralKey,
387 bounds: (Option<u64>, Option<u64>),
388 periodic: bool,
389 scale: Option<u64>,
390 unit: Option<Arc<str>>,
391 latex: Option<Arc<str>>,
392 description: Option<Arc<str>>,
393}
394
395#[doc(hidden)]
396#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
397pub enum ParameterStateStructuralKey {
398 Free,
399 Fixed(u64),
400}
401
402#[doc(hidden)]
403#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
404pub enum InitialStructuralKey {
405 Default,
406 Value(u64),
407 Uniform { min: u64, max: u64 },
408}
409
410#[doc(hidden)]
417#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
418pub enum ExprNodeStructuralKey {
419 RealConst(u64),
420 ComplexConst {
421 re: u64,
422 im: u64,
423 },
424 ScalarParam(ParameterStructuralKey),
425 EventScalar(Arc<str>),
426 EventP4Component {
427 name: Arc<str>,
428 component: P4Component,
429 },
430 Unary {
431 op: UnaryOp,
432 input: ExprId,
433 },
434 Binary {
435 op: BinaryOp,
436 lhs: ExprId,
437 rhs: ExprId,
438 },
439 NaryAdd {
440 terms: Vec<ExprId>,
441 },
442 NaryMul {
443 factors: Vec<ExprId>,
444 },
445 Complex {
446 re: ExprId,
447 im: ExprId,
448 },
449 Vector {
450 elements: Vec<ExprId>,
451 },
452 Matrix {
453 rows: usize,
454 cols: usize,
455 elements: Vec<ExprId>,
456 },
457 Component {
458 input: ExprId,
459 index: usize,
460 },
461 MatrixElement {
462 input: ExprId,
463 row: usize,
464 col: usize,
465 },
466 MatMul {
467 lhs: ExprId,
468 rhs: ExprId,
469 },
470 MatVec {
471 matrix: ExprId,
472 vector: ExprId,
473 },
474 Dot {
475 lhs: ExprId,
476 rhs: ExprId,
477 },
478 Solve {
479 matrix: ExprId,
480 rhs: ExprId,
481 },
482}
483
484impl From<&Parameter> for ParameterStructuralKey {
485 fn from(parameter: &Parameter) -> Self {
486 let state = match parameter.state() {
487 ParamState::Free => ParameterStateStructuralKey::Free,
488 ParamState::Fixed(value) => ParameterStateStructuralKey::Fixed(value.to_bits()),
489 };
490 let initial = match parameter.initial_spec() {
491 InitialSpec::Default => InitialStructuralKey::Default,
492 InitialSpec::Value(value) => InitialStructuralKey::Value(value.to_bits()),
493 InitialSpec::Uniform { min, max } => InitialStructuralKey::Uniform {
494 min: min.to_bits(),
495 max: max.to_bits(),
496 },
497 };
498 Self {
499 name: Arc::from(parameter.name()),
500 state,
501 initial,
502 bounds: (
503 parameter.bounds_spec().min.map(f64::to_bits),
504 parameter.bounds_spec().max.map(f64::to_bits),
505 ),
506 periodic: parameter.is_periodic(),
507 scale: parameter.scale().map(f64::to_bits),
508 unit: parameter.unit_label().map(Arc::from),
509 latex: parameter.latex_label().map(Arc::from),
510 description: parameter.description_text().map(Arc::from),
511 }
512 }
513}
514
515impl From<Complex64> for ExprNode {
516 fn from(value: Complex64) -> Self {
517 if value.im == 0.0 {
518 Self::RealConst(value.re)
519 } else {
520 Self::ComplexConst(value)
521 }
522 }
523}
524
525impl ExprNode {
526 pub fn semantics(&self, children: &[ExprNodeSemantics]) -> ExprNodeSemantics {
529 ExprNodeSemantics {
530 value_kind: self.infer_value_kind(children),
531 number_class: self.infer_number_class(children),
532 }
533 }
534
535 fn infer_value_kind(&self, children: &[ExprNodeSemantics]) -> ValueKind {
536 match self {
537 Self::RealConst(_) | Self::ScalarParam(_) => ValueKind::Real,
538 Self::ComplexConst(value) => {
539 if value.im == 0.0 {
540 ValueKind::Real
541 } else {
542 ValueKind::Complex
543 }
544 }
545 Self::EventScalar(_) | Self::EventP4Component { .. } => ValueKind::Real,
546 Self::Unary { op, input } => match op {
547 UnaryOp::Real | UnaryOp::Imag | UnaryOp::NormSqr => ValueKind::Real,
548 UnaryOp::Neg
549 | UnaryOp::Conj
550 | UnaryOp::Sqrt
551 | UnaryOp::Exp
552 | UnaryOp::Sin
553 | UnaryOp::Cos
554 | UnaryOp::Log
555 | UnaryOp::PowI(_) => children[input.index()].value_kind,
556 },
557 Self::Binary { op, lhs, rhs } => {
558 if *op == BinaryOp::Atan2 {
559 return ValueKind::Real;
560 }
561 if children[lhs.index()].value_kind == ValueKind::Real
562 && children[rhs.index()].value_kind == ValueKind::Real
563 {
564 ValueKind::Real
565 } else {
566 ValueKind::Complex
567 }
568 }
569 Self::NaryAdd { terms } => {
570 if terms
571 .iter()
572 .all(|id| children[id.index()].value_kind == ValueKind::Real)
573 {
574 ValueKind::Real
575 } else {
576 ValueKind::Complex
577 }
578 }
579 Self::NaryMul { factors } => {
580 if factors
581 .iter()
582 .all(|id| children[id.index()].value_kind == ValueKind::Real)
583 {
584 ValueKind::Real
585 } else {
586 ValueKind::Complex
587 }
588 }
589 Self::Complex { .. } => ValueKind::Complex,
590 Self::Vector { elements } => ValueKind::Vector {
591 len: elements.len(),
592 },
593 Self::Matrix { rows, cols, .. } => ValueKind::Matrix {
594 rows: *rows,
595 cols: *cols,
596 },
597 Self::Component { input, .. } => match children[input.index()].value_kind {
598 ValueKind::Vector { .. } => ValueKind::Complex,
599 kind => kind,
600 },
601 Self::MatrixElement { .. } | Self::Dot { .. } => ValueKind::Complex,
602 Self::MatMul { lhs, rhs } => {
603 let ValueKind::Matrix { rows, .. } = children[lhs.index()].value_kind else {
604 return ValueKind::Complex;
605 };
606 let ValueKind::Matrix { cols, .. } = children[rhs.index()].value_kind else {
607 return ValueKind::Complex;
608 };
609 ValueKind::Matrix { rows, cols }
610 }
611 Self::MatVec { matrix, .. } => {
612 let ValueKind::Matrix { rows, .. } = children[matrix.index()].value_kind else {
613 return ValueKind::Complex;
614 };
615 ValueKind::Vector { len: rows }
616 }
617 Self::Solve { rhs, .. } => children[rhs.index()].value_kind,
618 }
619 }
620
621 fn infer_number_class(&self, children: &[ExprNodeSemantics]) -> NumberClass {
622 match self {
623 Self::RealConst(_) | Self::ScalarParam(_) => NumberClass::Real,
624 Self::ComplexConst(value) => match (value.re == 0.0, value.im == 0.0) {
625 (_, true) => NumberClass::Real,
626 (true, false) => NumberClass::Imaginary,
627 (false, false) => NumberClass::Complex,
628 },
629 Self::EventScalar(_) | Self::EventP4Component { .. } => NumberClass::Real,
630 Self::Unary { op, input } => match op {
631 UnaryOp::Neg | UnaryOp::Conj => children[input.index()].number_class,
632 UnaryOp::Real | UnaryOp::Imag | UnaryOp::NormSqr => NumberClass::Real,
633 UnaryOp::Exp | UnaryOp::Sin | UnaryOp::Cos | UnaryOp::PowI(_) => {
634 let input = children[input.index()].number_class;
635 if input == NumberClass::Real {
636 NumberClass::Real
637 } else {
638 NumberClass::Unknown
639 }
640 }
641 UnaryOp::Sqrt | UnaryOp::Log => NumberClass::Unknown,
642 },
643 Self::Binary { op, lhs, rhs } => {
644 let lhs = children[lhs.index()].number_class;
645 let rhs = children[rhs.index()].number_class;
646 match op {
647 BinaryOp::Add | BinaryOp::Sub => add_number_class(lhs, rhs),
648 BinaryOp::Mul | BinaryOp::Div => mul_number_class(lhs, rhs),
649 BinaryOp::Atan2 => NumberClass::Real,
650 }
651 }
652 Self::NaryAdd { terms } => {
653 let mut classes = terms.iter().map(|id| children[id.index()].number_class);
654 let Some(first) = classes.next() else {
655 return NumberClass::Real;
656 };
657 classes.fold(first, add_number_class)
658 }
659 Self::NaryMul { factors } => {
660 let mut classes = factors.iter().map(|id| children[id.index()].number_class);
661 let Some(first) = classes.next() else {
662 return NumberClass::Real;
663 };
664 classes.fold(first, mul_number_class)
665 }
666 Self::Complex { .. } => NumberClass::Complex,
667 Self::Vector { .. }
668 | Self::Matrix { .. }
669 | Self::Component { .. }
670 | Self::MatrixElement { .. }
671 | Self::MatMul { .. }
672 | Self::MatVec { .. }
673 | Self::Dot { .. }
674 | Self::Solve { .. } => NumberClass::Unknown,
675 }
676 }
677
678 pub fn dependency_kind(&self) -> ExprDependencyKind {
680 match self {
681 Self::RealConst(_) | Self::ComplexConst(_) => ExprDependencyKind::Constant,
682 Self::ScalarParam(_) => ExprDependencyKind::Parameter,
683 Self::EventScalar(_) | Self::EventP4Component { .. } => ExprDependencyKind::Event,
684 _ => ExprDependencyKind::Children,
685 }
686 }
687
688 #[doc(hidden)]
690 pub fn structural_key(&self) -> ExprNodeStructuralKey {
691 match self {
692 Self::RealConst(value) => ExprNodeStructuralKey::RealConst(value.to_bits()),
693 Self::ComplexConst(value) => ExprNodeStructuralKey::ComplexConst {
694 re: value.re.to_bits(),
695 im: value.im.to_bits(),
696 },
697 Self::ScalarParam(parameter) => {
698 ExprNodeStructuralKey::ScalarParam(ParameterStructuralKey::from(parameter))
699 }
700 Self::EventScalar(name) => ExprNodeStructuralKey::EventScalar(Arc::clone(name)),
701 Self::EventP4Component { name, component } => ExprNodeStructuralKey::EventP4Component {
702 name: Arc::clone(name),
703 component: *component,
704 },
705 Self::Unary { op, input } => ExprNodeStructuralKey::Unary {
706 op: *op,
707 input: *input,
708 },
709 Self::Binary { op, lhs, rhs } => ExprNodeStructuralKey::Binary {
710 op: *op,
711 lhs: *lhs,
712 rhs: *rhs,
713 },
714 Self::NaryAdd { terms } => ExprNodeStructuralKey::NaryAdd {
715 terms: terms.clone(),
716 },
717 Self::NaryMul { factors } => ExprNodeStructuralKey::NaryMul {
718 factors: factors.clone(),
719 },
720 Self::Complex { re, im } => ExprNodeStructuralKey::Complex { re: *re, im: *im },
721 Self::Vector { elements } => ExprNodeStructuralKey::Vector {
722 elements: elements.clone(),
723 },
724 Self::Matrix {
725 rows,
726 cols,
727 elements,
728 } => ExprNodeStructuralKey::Matrix {
729 rows: *rows,
730 cols: *cols,
731 elements: elements.clone(),
732 },
733 Self::Component { input, index } => ExprNodeStructuralKey::Component {
734 input: *input,
735 index: *index,
736 },
737 Self::MatrixElement { input, row, col } => ExprNodeStructuralKey::MatrixElement {
738 input: *input,
739 row: *row,
740 col: *col,
741 },
742 Self::MatMul { lhs, rhs } => ExprNodeStructuralKey::MatMul {
743 lhs: *lhs,
744 rhs: *rhs,
745 },
746 Self::MatVec { matrix, vector } => ExprNodeStructuralKey::MatVec {
747 matrix: *matrix,
748 vector: *vector,
749 },
750 Self::Dot { lhs, rhs } => ExprNodeStructuralKey::Dot {
751 lhs: *lhs,
752 rhs: *rhs,
753 },
754 Self::Solve { matrix, rhs } => ExprNodeStructuralKey::Solve {
755 matrix: *matrix,
756 rhs: *rhs,
757 },
758 }
759 }
760
761 pub fn from_folded_const(value: Complex64) -> Self {
763 if value.im == 0.0 && value.im.is_sign_positive() {
764 Self::RealConst(value.re)
765 } else {
766 Self::ComplexConst(value)
767 }
768 }
769
770 pub fn const_value(&self) -> Option<Complex64> {
772 match self {
773 ExprNode::RealConst(value) => Some(Complex64::from(*value)),
774 ExprNode::ComplexConst(value) => Some(*value),
775 _ => None,
776 }
777 }
778
779 pub fn is_zero(node: &ExprNode) -> bool {
781 node.const_value()
782 .is_some_and(|value| value == Complex64::ZERO)
783 }
784
785 pub fn is_one(node: &ExprNode) -> bool {
787 node.const_value()
788 .is_some_and(|value| value == Complex64::ONE)
789 }
790
791 pub fn children(&self) -> impl ExactSizeIterator<Item = ExprId> + DoubleEndedIterator + '_ {
797 (0..self.child_count()).map(|index| self.child_at(index))
798 }
799
800 pub fn child_ids(&self) -> Vec<ExprId> {
805 self.children().collect()
806 }
807
808 pub fn map_children(&self, mut map: impl FnMut(ExprId) -> ExprId) -> Self {
813 match self {
814 Self::RealConst(_)
815 | Self::ComplexConst(_)
816 | Self::ScalarParam(_)
817 | Self::EventScalar(_)
818 | Self::EventP4Component { .. } => self.clone(),
819 Self::Unary { op, input } => Self::Unary {
820 op: *op,
821 input: map(*input),
822 },
823 Self::Binary { op, lhs, rhs } => Self::Binary {
824 op: *op,
825 lhs: map(*lhs),
826 rhs: map(*rhs),
827 },
828 Self::NaryAdd { terms } => Self::NaryAdd {
829 terms: terms.iter().copied().map(&mut map).collect(),
830 },
831 Self::NaryMul { factors } => Self::NaryMul {
832 factors: factors.iter().copied().map(&mut map).collect(),
833 },
834 Self::Complex { re, im } => Self::Complex {
835 re: map(*re),
836 im: map(*im),
837 },
838 Self::Vector { elements } => Self::Vector {
839 elements: elements.iter().copied().map(&mut map).collect(),
840 },
841 Self::Matrix {
842 rows,
843 cols,
844 elements,
845 } => Self::Matrix {
846 rows: *rows,
847 cols: *cols,
848 elements: elements.iter().copied().map(&mut map).collect(),
849 },
850 Self::Component { input, index } => Self::Component {
851 input: map(*input),
852 index: *index,
853 },
854 Self::MatrixElement { input, row, col } => Self::MatrixElement {
855 input: map(*input),
856 row: *row,
857 col: *col,
858 },
859 Self::MatMul { lhs, rhs } => Self::MatMul {
860 lhs: map(*lhs),
861 rhs: map(*rhs),
862 },
863 Self::MatVec { matrix, vector } => Self::MatVec {
864 matrix: map(*matrix),
865 vector: map(*vector),
866 },
867 Self::Dot { lhs, rhs } => Self::Dot {
868 lhs: map(*lhs),
869 rhs: map(*rhs),
870 },
871 Self::Solve { matrix, rhs } => Self::Solve {
872 matrix: map(*matrix),
873 rhs: map(*rhs),
874 },
875 }
876 }
877
878 fn child_count(&self) -> usize {
879 match self {
880 Self::RealConst(_)
881 | Self::ComplexConst(_)
882 | Self::ScalarParam(_)
883 | Self::EventScalar(_)
884 | Self::EventP4Component { .. } => 0,
885 Self::Unary { .. } | Self::Component { .. } | Self::MatrixElement { .. } => 1,
886 Self::Binary { .. }
887 | Self::Complex { .. }
888 | Self::MatMul { .. }
889 | Self::MatVec { .. }
890 | Self::Dot { .. }
891 | Self::Solve { .. } => 2,
892 Self::NaryAdd { terms } => terms.len(),
893 Self::NaryMul { factors } => factors.len(),
894 Self::Vector { elements } | Self::Matrix { elements, .. } => elements.len(),
895 }
896 }
897
898 fn child_at(&self, index: usize) -> ExprId {
899 match self {
900 Self::Unary { input, .. }
901 | Self::Component { input, .. }
902 | Self::MatrixElement { input, .. } => *input,
903 Self::Binary { lhs, rhs, .. }
904 | Self::Complex { re: lhs, im: rhs }
905 | Self::MatMul { lhs, rhs }
906 | Self::Dot { lhs, rhs } => [*lhs, *rhs][index],
907 Self::MatVec { matrix, vector } => [*matrix, *vector][index],
908 Self::Solve { matrix, rhs } => [*matrix, *rhs][index],
909 Self::NaryAdd { terms } => terms[index],
910 Self::NaryMul { factors } => factors[index],
911 Self::Vector { elements } | Self::Matrix { elements, .. } => elements[index],
912 Self::RealConst(_)
913 | Self::ComplexConst(_)
914 | Self::ScalarParam(_)
915 | Self::EventScalar(_)
916 | Self::EventP4Component { .. } => unreachable!("leaf node has no children"),
917 }
918 }
919}
920
921#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
923pub enum ExprSourceKind {
924 Const,
926 Param,
928 Event,
930 Unary,
932 Binary,
934 Complex,
936 Vector,
938 Matrix,
940 LinearAlgebra,
942}
943
944#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
946pub struct ExprMetadata {
947 source: ExprSourceKind,
948 name: Option<Arc<str>>,
949 tags: Vec<Arc<str>>,
950}
951
952impl ExprMetadata {
953 pub fn new(source: ExprSourceKind) -> Self {
955 Self {
956 source,
957 name: None,
958 tags: Vec::new(),
959 }
960 }
961
962 pub fn source(&self) -> ExprSourceKind {
964 self.source
965 }
966
967 pub fn name(&self) -> Option<&str> {
969 self.name.as_deref()
970 }
971
972 pub fn tags(&self) -> &[Arc<str>] {
974 &self.tags
975 }
976
977 pub fn has_tag(&self, tag: &str) -> bool {
979 self.tags.iter().any(|candidate| candidate.as_ref() == tag)
980 }
981}
982
983#[derive(Clone, Debug)]
985pub struct Expr {
986 node: Arc<DagNode>,
987}
988
989#[derive(Clone, Debug)]
990struct DagNode {
991 kind: DagNodeKind,
992 metadata: ExprMetadata,
993 shape: OnceLock<Result<ExprShape, ExprShapeError>>,
994}
995
996#[derive(Clone, Debug)]
997enum DagNodeKind {
998 RealConst(f64),
999 ComplexConst(Complex64),
1000 ScalarParam(Parameter),
1001 EventScalar(Arc<str>),
1002 EventP4Component {
1003 name: Arc<str>,
1004 component: P4Component,
1005 },
1006 Unary {
1007 op: UnaryOp,
1008 input: Expr,
1009 },
1010 Binary {
1011 op: BinaryOp,
1012 lhs: Expr,
1013 rhs: Expr,
1014 },
1015 Complex {
1016 re: Expr,
1017 im: Expr,
1018 },
1019 Vector {
1020 elements: Vec<Expr>,
1021 },
1022 Matrix {
1023 rows: usize,
1024 cols: usize,
1025 elements: Vec<Expr>,
1026 },
1027 Component {
1028 input: Expr,
1029 index: usize,
1030 },
1031 MatrixElement {
1032 input: Expr,
1033 row: usize,
1034 col: usize,
1035 },
1036 MatMul {
1037 lhs: Expr,
1038 rhs: Expr,
1039 },
1040 MatVec {
1041 matrix: Expr,
1042 vector: Expr,
1043 },
1044 Dot {
1045 lhs: Expr,
1046 rhs: Expr,
1047 },
1048 Solve {
1049 matrix: Expr,
1050 rhs: Expr,
1051 },
1052}
1053
1054impl DagNodeKind {
1055 fn child_count(&self) -> usize {
1056 match self {
1057 Self::RealConst(_)
1058 | Self::ComplexConst(_)
1059 | Self::ScalarParam(_)
1060 | Self::EventScalar(_)
1061 | Self::EventP4Component { .. } => 0,
1062 Self::Unary { .. } | Self::Component { .. } | Self::MatrixElement { .. } => 1,
1063 Self::Binary { .. }
1064 | Self::Complex { .. }
1065 | Self::MatMul { .. }
1066 | Self::MatVec { .. }
1067 | Self::Dot { .. }
1068 | Self::Solve { .. } => 2,
1069 Self::Vector { elements } | Self::Matrix { elements, .. } => elements.len(),
1070 }
1071 }
1072
1073 fn child_at(&self, index: usize) -> &Expr {
1074 match self {
1075 Self::Unary { input, .. }
1076 | Self::Component { input, .. }
1077 | Self::MatrixElement { input, .. } => input,
1078 Self::Binary { lhs, rhs, .. } | Self::MatMul { lhs, rhs } | Self::Dot { lhs, rhs } => {
1079 [lhs, rhs][index]
1080 }
1081 Self::Complex { re, im } => [re, im][index],
1082 Self::MatVec { matrix, vector } => [matrix, vector][index],
1083 Self::Solve { matrix, rhs } => [matrix, rhs][index],
1084 Self::Vector { elements } | Self::Matrix { elements, .. } => &elements[index],
1085 Self::RealConst(_)
1086 | Self::ComplexConst(_)
1087 | Self::ScalarParam(_)
1088 | Self::EventScalar(_)
1089 | Self::EventP4Component { .. } => unreachable!("leaf nodes have no children"),
1090 }
1091 }
1092
1093 fn map_children(&self, mut map: impl FnMut(&Expr) -> Expr) -> Self {
1094 match self {
1095 Self::RealConst(value) => Self::RealConst(*value),
1096 Self::ComplexConst(value) => Self::ComplexConst(*value),
1097 Self::ScalarParam(parameter) => Self::ScalarParam(parameter.clone()),
1098 Self::EventScalar(name) => Self::EventScalar(Arc::clone(name)),
1099 Self::EventP4Component { name, component } => Self::EventP4Component {
1100 name: Arc::clone(name),
1101 component: *component,
1102 },
1103 Self::Unary { op, input } => Self::Unary {
1104 op: *op,
1105 input: map(input),
1106 },
1107 Self::Binary { op, lhs, rhs } => Self::Binary {
1108 op: *op,
1109 lhs: map(lhs),
1110 rhs: map(rhs),
1111 },
1112 Self::Complex { re, im } => Self::Complex {
1113 re: map(re),
1114 im: map(im),
1115 },
1116 Self::Vector { elements } => Self::Vector {
1117 elements: elements.iter().map(&mut map).collect(),
1118 },
1119 Self::Matrix {
1120 rows,
1121 cols,
1122 elements,
1123 } => Self::Matrix {
1124 rows: *rows,
1125 cols: *cols,
1126 elements: elements.iter().map(&mut map).collect(),
1127 },
1128 Self::Component { input, index } => Self::Component {
1129 input: map(input),
1130 index: *index,
1131 },
1132 Self::MatrixElement { input, row, col } => Self::MatrixElement {
1133 input: map(input),
1134 row: *row,
1135 col: *col,
1136 },
1137 Self::MatMul { lhs, rhs } => Self::MatMul {
1138 lhs: map(lhs),
1139 rhs: map(rhs),
1140 },
1141 Self::MatVec { matrix, vector } => Self::MatVec {
1142 matrix: map(matrix),
1143 vector: map(vector),
1144 },
1145 Self::Dot { lhs, rhs } => Self::Dot {
1146 lhs: map(lhs),
1147 rhs: map(rhs),
1148 },
1149 Self::Solve { matrix, rhs } => Self::Solve {
1150 matrix: map(matrix),
1151 rhs: map(rhs),
1152 },
1153 }
1154 }
1155}
1156
1157impl Expr {
1158 fn new(kind: DagNodeKind) -> Self {
1159 let source = source_kind(&kind);
1160 Self {
1161 node: Arc::new(DagNode {
1162 kind,
1163 metadata: ExprMetadata::new(source),
1164 shape: OnceLock::new(),
1165 }),
1166 }
1167 }
1168
1169 pub fn named(self, name: impl Into<Arc<str>>) -> Self {
1171 self.with_metadata(|metadata| metadata.name = Some(name.into()))
1172 }
1173
1174 pub fn tagged(self, tag: impl Into<Arc<str>>) -> Self {
1176 let tag = tag.into();
1177 self.with_metadata(|metadata| {
1178 if !metadata.tags.iter().any(|existing| existing == &tag) {
1179 metadata.tags.push(tag);
1180 }
1181 })
1182 }
1183
1184 pub fn tagged_with(self, tags: impl IntoIterator<Item = impl Into<Arc<str>>>) -> Self {
1186 tags.into_iter().fold(self, Self::tagged)
1187 }
1188
1189 pub fn project_tags<'a>(&self, tags: impl IntoIterator<Item = &'a str>) -> Self {
1193 let tags: Vec<_> = tags.into_iter().collect();
1194 enum Frame {
1195 Visit(Expr),
1196 Rebuild(Expr, usize),
1197 }
1198
1199 let mut projected = Vec::new();
1200 let mut stack = vec![Frame::Visit(self.clone())];
1201 while let Some(frame) = stack.pop() {
1202 match frame {
1203 Frame::Visit(expr) => {
1204 let has_tags = !expr.node.metadata.tags.is_empty();
1205 if has_tags || expr.node.kind.child_count() == 0 {
1206 projected.push(
1207 if !has_tags
1208 || expr
1209 .node
1210 .metadata
1211 .tags
1212 .iter()
1213 .any(|candidate| tags.contains(&candidate.as_ref()))
1214 {
1215 expr.clone()
1216 } else {
1217 expr.zero_like()
1218 },
1219 );
1220 continue;
1221 }
1222 let child_count = expr.node.kind.child_count();
1223 stack.push(Frame::Rebuild(expr.clone(), child_count));
1224 for index in (0..child_count).rev() {
1225 stack.push(Frame::Visit(expr.node.kind.child_at(index).clone()));
1226 }
1227 }
1228 Frame::Rebuild(expr, child_count) => {
1229 let child_start = projected.len() - child_count;
1230 let children = projected.split_off(child_start);
1231 let mut children = children.into_iter();
1232 let kind = expr.node.kind.map_children(|original| {
1233 children.next().unwrap_or_else(|| original.clone())
1234 });
1235 projected.push(
1236 Expr::new(kind)
1237 .with_metadata(|metadata| *metadata = expr.node.metadata.clone()),
1238 );
1239 }
1240 }
1241 }
1242 projected.pop().unwrap_or_else(|| self.clone())
1243 }
1244
1245 fn zero_like(&self) -> Self {
1246 match self
1247 .shape()
1248 .expect("valid expression shapes are cached eagerly")
1249 {
1250 ExprShape::Scalar => Expr::from(0.0),
1251 ExprShape::Vector { len } => vector((0..len).map(|_| Expr::from(0.0))),
1252 ExprShape::Matrix { rows, cols } => {
1253 matrix_from_flat(rows, cols, (0..rows * cols).map(|_| Expr::from(0.0)))
1254 .expect("zero matrix dimensions match")
1255 }
1256 }
1257 }
1258
1259 pub fn real(&self) -> Self {
1261 unary(UnaryOp::Real, self)
1262 }
1263
1264 pub fn imag(&self) -> Self {
1266 unary(UnaryOp::Imag, self)
1267 }
1268
1269 pub fn conj(&self) -> Self {
1271 unary(UnaryOp::Conj, self)
1272 }
1273
1274 pub fn norm_sqr(&self) -> Self {
1276 unary(UnaryOp::NormSqr, self)
1277 }
1278
1279 pub fn sqrt(&self) -> Self {
1281 unary(UnaryOp::Sqrt, self)
1282 }
1283
1284 pub fn exp(&self) -> Self {
1286 unary(UnaryOp::Exp, self)
1287 }
1288
1289 pub fn sin(&self) -> Self {
1291 unary(UnaryOp::Sin, self)
1292 }
1293
1294 pub fn cos(&self) -> Self {
1296 unary(UnaryOp::Cos, self)
1297 }
1298
1299 pub fn acos(&self) -> Self {
1301 atan2((Expr::from(1.0) - self.powi(2)).sqrt(), self)
1302 }
1303
1304 pub fn log(&self) -> Self {
1306 unary(UnaryOp::Log, self)
1307 }
1308
1309 pub fn powi(&self, power: i32) -> Self {
1311 unary(UnaryOp::PowI(power), self)
1312 }
1313
1314 pub fn component(&self, index: impl ComponentIndex) -> Self {
1316 Expr::new(DagNodeKind::Component {
1317 input: self.clone(),
1318 index: index.component_index(),
1319 })
1320 }
1321
1322 pub fn matrix_element(&self, row: usize, col: usize) -> Self {
1324 Expr::new(DagNodeKind::MatrixElement {
1325 input: self.clone(),
1326 row,
1327 col,
1328 })
1329 }
1330
1331 pub fn to_graph(&self) -> ExprGraph {
1333 GraphBuilder::new().build(self)
1334 }
1335
1336 pub fn from_graph(graph: ExprGraph) -> Result<Self, ExprGraphError> {
1344 let ExprGraph {
1345 root,
1346 nodes,
1347 metadata,
1348 } = graph;
1349 let graph = ExprGraph::from_parts(root, nodes, metadata)?;
1350 let mut expressions: Vec<Expr> = Vec::with_capacity(graph.nodes.len());
1351 for (index, node) in graph.nodes.iter().enumerate() {
1352 let child = |id: ExprId| expressions[id.index()].clone();
1353 let expression = match node {
1354 ExprNode::RealConst(value) => Expr::new(DagNodeKind::RealConst(*value)),
1355 ExprNode::ComplexConst(value) => Expr::new(DagNodeKind::ComplexConst(*value)),
1356 ExprNode::ScalarParam(parameter) => {
1357 Expr::new(DagNodeKind::ScalarParam(parameter.clone()))
1358 }
1359 ExprNode::EventScalar(name) => {
1360 Expr::new(DagNodeKind::EventScalar(Arc::clone(name)))
1361 }
1362 ExprNode::EventP4Component { name, component } => {
1363 Expr::new(DagNodeKind::EventP4Component {
1364 name: Arc::clone(name),
1365 component: *component,
1366 })
1367 }
1368 ExprNode::Unary { op, input } => Expr::new(DagNodeKind::Unary {
1369 op: *op,
1370 input: child(*input),
1371 }),
1372 ExprNode::Binary { op, lhs, rhs } => Expr::new(DagNodeKind::Binary {
1373 op: *op,
1374 lhs: child(*lhs),
1375 rhs: child(*rhs),
1376 }),
1377 ExprNode::NaryAdd { terms } => terms
1378 .iter()
1379 .map(|id| child(*id))
1380 .reduce(|lhs, rhs| binary(BinaryOp::Add, &lhs, &rhs))
1381 .unwrap_or_else(|| Expr::from(0.0)),
1382 ExprNode::NaryMul { factors } => factors
1383 .iter()
1384 .map(|id| child(*id))
1385 .reduce(|lhs, rhs| binary(BinaryOp::Mul, &lhs, &rhs))
1386 .unwrap_or_else(|| Expr::from(1.0)),
1387 ExprNode::Complex { re, im } => Expr::new(DagNodeKind::Complex {
1388 re: child(*re),
1389 im: child(*im),
1390 }),
1391 ExprNode::Vector { elements } => Expr::new(DagNodeKind::Vector {
1392 elements: elements.iter().map(|id| child(*id)).collect(),
1393 }),
1394 ExprNode::Matrix {
1395 rows,
1396 cols,
1397 elements,
1398 } => Expr::new(DagNodeKind::Matrix {
1399 rows: *rows,
1400 cols: *cols,
1401 elements: elements.iter().map(|id| child(*id)).collect(),
1402 }),
1403 ExprNode::Component { input, index } => Expr::new(DagNodeKind::Component {
1404 input: child(*input),
1405 index: *index,
1406 }),
1407 ExprNode::MatrixElement { input, row, col } => {
1408 Expr::new(DagNodeKind::MatrixElement {
1409 input: child(*input),
1410 row: *row,
1411 col: *col,
1412 })
1413 }
1414 ExprNode::MatMul { lhs, rhs } => Expr::new(DagNodeKind::MatMul {
1415 lhs: child(*lhs),
1416 rhs: child(*rhs),
1417 }),
1418 ExprNode::MatVec { matrix, vector } => Expr::new(DagNodeKind::MatVec {
1419 matrix: child(*matrix),
1420 vector: child(*vector),
1421 }),
1422 ExprNode::Dot { lhs, rhs } => Expr::new(DagNodeKind::Dot {
1423 lhs: child(*lhs),
1424 rhs: child(*rhs),
1425 }),
1426 ExprNode::Solve { matrix, rhs } => Expr::new(DagNodeKind::Solve {
1427 matrix: child(*matrix),
1428 rhs: child(*rhs),
1429 }),
1430 };
1431 let mut dag = (*expression.node).clone();
1432 dag.metadata = graph.metadata[index].clone();
1433 expressions.push(Expr {
1434 node: Arc::new(dag),
1435 });
1436 }
1437 Ok(expressions[graph.root.index()].clone())
1438 }
1439
1440 pub fn shape(&self) -> Result<ExprShape, ExprShapeError> {
1447 self.node
1448 .shape
1449 .get_or_init(|| self.node.kind.shape())
1450 .clone()
1451 }
1452
1453 fn with_metadata(self, f: impl FnOnce(&mut ExprMetadata)) -> Self {
1454 let mut node = (*self.node).clone();
1455 f(&mut node.metadata);
1456 Self {
1457 node: Arc::new(node),
1458 }
1459 }
1460}
1461
1462impl Serialize for Expr {
1463 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1464 where
1465 S: serde::Serializer,
1466 {
1467 self.to_graph().serialize(serializer)
1468 }
1469}
1470
1471impl<'de> Deserialize<'de> for Expr {
1472 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1473 where
1474 D: serde::Deserializer<'de>,
1475 {
1476 Expr::from_graph(ExprGraph::deserialize(deserializer)?).map_err(serde::de::Error::custom)
1477 }
1478}
1479
1480impl DagNodeKind {
1481 fn shape(&self) -> Result<ExprShape, ExprShapeError> {
1482 match self {
1483 Self::RealConst(_)
1484 | Self::ComplexConst(_)
1485 | Self::ScalarParam(_)
1486 | Self::EventScalar(_)
1487 | Self::EventP4Component { .. } => Ok(ExprShape::Scalar),
1488 Self::Unary { input, .. } => {
1489 input.expect_shape("unary operation", ExprShape::Scalar)?;
1490 Ok(ExprShape::Scalar)
1491 }
1492 Self::Binary { lhs, rhs, .. } => {
1493 lhs.expect_shape("binary operation", ExprShape::Scalar)?;
1494 rhs.expect_shape("binary operation", ExprShape::Scalar)?;
1495 Ok(ExprShape::Scalar)
1496 }
1497 Self::Complex { re, im } => {
1498 re.expect_shape("complex constructor", ExprShape::Scalar)?;
1499 im.expect_shape("complex constructor", ExprShape::Scalar)?;
1500 Ok(ExprShape::Scalar)
1501 }
1502 Self::Vector { elements } => {
1503 for element in elements {
1504 element.expect_shape("vector constructor", ExprShape::Scalar)?;
1505 }
1506 Ok(ExprShape::Vector {
1507 len: elements.len(),
1508 })
1509 }
1510 Self::Matrix {
1511 rows,
1512 cols,
1513 elements,
1514 } => {
1515 let expected = rows.checked_mul(*cols).ok_or_else(|| {
1516 ExprShapeError::new("matrix constructor", "row/column product overflowed")
1517 })?;
1518 if elements.len() != expected {
1519 return Err(ExprShapeError::new(
1520 "matrix constructor",
1521 format!(
1522 "shape {rows}x{cols} requires {expected} elements, got {}",
1523 elements.len()
1524 ),
1525 ));
1526 }
1527 for element in elements {
1528 element.expect_shape("matrix constructor", ExprShape::Scalar)?;
1529 }
1530 Ok(ExprShape::Matrix {
1531 rows: *rows,
1532 cols: *cols,
1533 })
1534 }
1535 Self::Component { input, index } => {
1536 let ExprShape::Vector { len } = input.shape()? else {
1537 return Err(ExprShapeError::new(
1538 "component",
1539 format!("expected vector, got {}", input.shape()?),
1540 ));
1541 };
1542 if *index >= len {
1543 return Err(ExprShapeError::new(
1544 "component",
1545 format!("index {index} is out of bounds for vector[{len}]"),
1546 ));
1547 }
1548 Ok(ExprShape::Scalar)
1549 }
1550 Self::MatrixElement { input, row, col } => {
1551 let ExprShape::Matrix { rows, cols } = input.shape()? else {
1552 return Err(ExprShapeError::new(
1553 "matrix element",
1554 format!("expected matrix, got {}", input.shape()?),
1555 ));
1556 };
1557 if *row >= rows || *col >= cols {
1558 return Err(ExprShapeError::new(
1559 "matrix element",
1560 format!("index ({row}, {col}) is out of bounds for matrix[{rows}x{cols}]"),
1561 ));
1562 }
1563 Ok(ExprShape::Scalar)
1564 }
1565 Self::MatMul { lhs, rhs } => {
1566 let ExprShape::Matrix {
1567 rows: lhs_rows,
1568 cols: lhs_cols,
1569 } = lhs.shape()?
1570 else {
1571 return Err(ExprShapeError::new(
1572 "matrix multiplication",
1573 format!("left input must be a matrix, got {}", lhs.shape()?),
1574 ));
1575 };
1576 let ExprShape::Matrix {
1577 rows: rhs_rows,
1578 cols: rhs_cols,
1579 } = rhs.shape()?
1580 else {
1581 return Err(ExprShapeError::new(
1582 "matrix multiplication",
1583 format!("right input must be a matrix, got {}", rhs.shape()?),
1584 ));
1585 };
1586 if lhs_cols != rhs_rows {
1587 return Err(ExprShapeError::new(
1588 "matrix multiplication",
1589 format!("cannot multiply {lhs_rows}x{lhs_cols} by {rhs_rows}x{rhs_cols}"),
1590 ));
1591 }
1592 Ok(ExprShape::Matrix {
1593 rows: lhs_rows,
1594 cols: rhs_cols,
1595 })
1596 }
1597 Self::MatVec { matrix, vector } => {
1598 let ExprShape::Matrix { rows, cols } = matrix.shape()? else {
1599 return Err(ExprShapeError::new(
1600 "matrix-vector multiplication",
1601 format!("left input must be a matrix, got {}", matrix.shape()?),
1602 ));
1603 };
1604 let ExprShape::Vector { len } = vector.shape()? else {
1605 return Err(ExprShapeError::new(
1606 "matrix-vector multiplication",
1607 format!("right input must be a vector, got {}", vector.shape()?),
1608 ));
1609 };
1610 if cols != len {
1611 return Err(ExprShapeError::new(
1612 "matrix-vector multiplication",
1613 format!("cannot multiply {rows}x{cols} matrix by vector[{len}]"),
1614 ));
1615 }
1616 Ok(ExprShape::Vector { len: rows })
1617 }
1618 Self::Dot { lhs, rhs } => {
1619 let ExprShape::Vector { len: lhs_len } = lhs.shape()? else {
1620 return Err(ExprShapeError::new(
1621 "dot product",
1622 format!("left input must be a vector, got {}", lhs.shape()?),
1623 ));
1624 };
1625 let ExprShape::Vector { len: rhs_len } = rhs.shape()? else {
1626 return Err(ExprShapeError::new(
1627 "dot product",
1628 format!("right input must be a vector, got {}", rhs.shape()?),
1629 ));
1630 };
1631 if lhs_len != rhs_len {
1632 return Err(ExprShapeError::new(
1633 "dot product",
1634 format!("vector lengths differ: {lhs_len} and {rhs_len}"),
1635 ));
1636 }
1637 Ok(ExprShape::Scalar)
1638 }
1639 Self::Solve { matrix, rhs } => {
1640 let ExprShape::Matrix { rows, cols } = matrix.shape()? else {
1641 return Err(ExprShapeError::new(
1642 "linear solve",
1643 format!("left input must be a matrix, got {}", matrix.shape()?),
1644 ));
1645 };
1646 let ExprShape::Vector { len } = rhs.shape()? else {
1647 return Err(ExprShapeError::new(
1648 "linear solve",
1649 format!("right input must be a vector, got {}", rhs.shape()?),
1650 ));
1651 };
1652 if rows != cols || rows != len {
1653 return Err(ExprShapeError::new(
1654 "linear solve",
1655 format!("cannot solve matrix[{rows}x{cols}] against vector[{len}]"),
1656 ));
1657 }
1658 Ok(ExprShape::Vector { len })
1659 }
1660 }
1661 }
1662}
1663
1664impl Expr {
1665 fn expect_shape(
1666 &self,
1667 operation: &'static str,
1668 expected: ExprShape,
1669 ) -> Result<(), ExprShapeError> {
1670 let actual = self.shape()?;
1671 if actual != expected {
1672 return Err(ExprShapeError::new(
1673 operation,
1674 format!("expected {expected}, got {actual}"),
1675 ));
1676 }
1677 Ok(())
1678 }
1679}
1680
1681auto_ops::impl_op_ex!(+ |a: &Expr, b: &Expr| -> Expr { binary(BinaryOp::Add, a, b) });
1682
1683auto_ops::impl_op_ex!(+ |a: &Expr, b: &f64| -> Expr { binary(BinaryOp::Add, a, b) });
1684auto_ops::impl_op_ex!(+ |a: &f64, b: &Expr| -> Expr { binary(BinaryOp::Add, a, b) });
1685
1686auto_ops::impl_op_ex!(+ |a: &Expr, b: &Complex64| -> Expr { binary(BinaryOp::Add, a, b) });
1687auto_ops::impl_op_ex!(+ |a: &Complex64, b: &Expr| -> Expr { binary(BinaryOp::Add, a, b) });
1688
1689auto_ops::impl_op_ex!(+ |a: &Expr, b: &Parameter| -> Expr { binary(BinaryOp::Add, a, b) });
1690auto_ops::impl_op_ex!(+ |a: &Parameter, b: &Expr| -> Expr { binary(BinaryOp::Add, a, b) });
1691
1692auto_ops::impl_op_ex!(+ |a: &Parameter, b: &f64| -> Expr { binary(BinaryOp::Add, a, b) });
1693auto_ops::impl_op_ex!(+ |a: &f64, b: &Parameter| -> Expr { binary(BinaryOp::Add, a, b) });
1694
1695auto_ops::impl_op_ex!(+ |a: &Parameter, b: &Complex64| -> Expr { binary(BinaryOp::Add, a, b) });
1696auto_ops::impl_op_ex!(+ |a: &Complex64, b: &Parameter| -> Expr { binary(BinaryOp::Add, a, b) });
1697
1698auto_ops::impl_op_ex!(+ |a: &Parameter, b: &Parameter| -> Expr { binary(BinaryOp::Add, a, b) });
1699
1700auto_ops::impl_op_ex!(-|a: &Expr, b: &Expr| -> Expr { binary(BinaryOp::Sub, a, b) });
1701
1702auto_ops::impl_op_ex!(-|a: &Expr, b: &f64| -> Expr { binary(BinaryOp::Sub, a, b) });
1703
1704auto_ops::impl_op_ex!(-|a: &f64, b: &Expr| -> Expr { binary(BinaryOp::Sub, a, b) });
1705
1706auto_ops::impl_op_ex!(-|a: &Expr, b: &Complex64| -> Expr { binary(BinaryOp::Sub, a, b) });
1707
1708auto_ops::impl_op_ex!(-|a: &Complex64, b: &Expr| -> Expr { binary(BinaryOp::Sub, a, b) });
1709
1710auto_ops::impl_op_ex!(-|a: &Expr, b: &Parameter| -> Expr { binary(BinaryOp::Sub, a, b) });
1711
1712auto_ops::impl_op_ex!(-|a: &Parameter, b: &Expr| -> Expr { binary(BinaryOp::Sub, a, b) });
1713
1714auto_ops::impl_op_ex!(-|a: &f64, b: &Parameter| -> Expr { binary(BinaryOp::Sub, a, b) });
1715
1716auto_ops::impl_op_ex!(-|a: &Parameter, b: &f64| -> Expr { binary(BinaryOp::Sub, a, b) });
1717
1718auto_ops::impl_op_ex!(-|a: &Complex64, b: &Parameter| -> Expr { binary(BinaryOp::Sub, a, b) });
1719
1720auto_ops::impl_op_ex!(-|a: &Parameter, b: &Complex64| -> Expr { binary(BinaryOp::Sub, a, b) });
1721
1722auto_ops::impl_op_ex!(-|a: &Parameter, b: &Parameter| -> Expr { binary(BinaryOp::Sub, a, b) });
1723
1724auto_ops::impl_op_ex!(*|a: &Expr, b: &Expr| -> Expr { binary(BinaryOp::Mul, a, b) });
1725
1726auto_ops::impl_op_ex!(*|a: &Expr, b: &f64| -> Expr { binary(BinaryOp::Mul, a, b) });
1727auto_ops::impl_op_ex!(*|a: &f64, b: &Expr| -> Expr { binary(BinaryOp::Mul, a, b) });
1728
1729auto_ops::impl_op_ex!(*|a: &Expr, b: &Complex64| -> Expr { binary(BinaryOp::Mul, a, b) });
1730auto_ops::impl_op_ex!(*|a: &Complex64, b: &Expr| -> Expr { binary(BinaryOp::Mul, a, b) });
1731
1732auto_ops::impl_op_ex!(*|a: &Expr, b: &Parameter| -> Expr { binary(BinaryOp::Mul, a, b) });
1733auto_ops::impl_op_ex!(*|a: &Parameter, b: &Expr| -> Expr { binary(BinaryOp::Mul, a, b) });
1734
1735auto_ops::impl_op_ex!(*|a: &f64, b: &Parameter| -> Expr { binary(BinaryOp::Mul, a, b) });
1736auto_ops::impl_op_ex!(*|a: &Parameter, b: &f64| -> Expr { binary(BinaryOp::Mul, a, b) });
1737
1738auto_ops::impl_op_ex!(*|a: &Complex64, b: &Parameter| -> Expr { binary(BinaryOp::Mul, a, b) });
1739auto_ops::impl_op_ex!(*|a: &Parameter, b: &Complex64| -> Expr { binary(BinaryOp::Mul, a, b) });
1740
1741auto_ops::impl_op_ex!(*|a: &Parameter, b: &Parameter| -> Expr { binary(BinaryOp::Mul, a, b) });
1742
1743auto_ops::impl_op_ex!(/ |a: &Expr, b: &Expr| -> Expr {
1744 binary(BinaryOp::Div, a, b)
1745});
1746
1747auto_ops::impl_op_ex!(/ |a: &Expr, b: &Complex64| -> Expr { binary(BinaryOp::Div, a, b) });
1748auto_ops::impl_op_ex!(/ |a: &Complex64, b: &Expr| -> Expr { binary(BinaryOp::Div, a, b) });
1749
1750auto_ops::impl_op_ex!(/ |a: &Expr, b: &f64| -> Expr { binary(BinaryOp::Div, a, b) });
1751auto_ops::impl_op_ex!(/ |a: &f64, b: &Expr| -> Expr { binary(BinaryOp::Div, a, b) });
1752
1753auto_ops::impl_op_ex!(/|a: &Expr, b: &Parameter| -> Expr {
1754 binary(BinaryOp::Div, a, b)
1755});
1756auto_ops::impl_op_ex!(/|a: &Parameter, b: &Expr| -> Expr {
1757 binary(BinaryOp::Div, a, b)
1758});
1759
1760auto_ops::impl_op_ex!(/|a: &f64, b: &Parameter| -> Expr {
1761 binary(BinaryOp::Div, a, b)
1762});
1763auto_ops::impl_op_ex!(/|a: &Parameter, b: &f64| -> Expr {
1764 binary(BinaryOp::Div, a, b)
1765});
1766
1767auto_ops::impl_op_ex!(/|a: &Complex64, b: &Parameter| -> Expr {
1768 binary(BinaryOp::Div, a, b)
1769});
1770auto_ops::impl_op_ex!(/|a: &Parameter, b: &Complex64| -> Expr {
1771 binary(BinaryOp::Div, a, b)
1772});
1773
1774auto_ops::impl_op_ex!(/|a: &Parameter, b: &Parameter| -> Expr {
1775 binary(BinaryOp::Div, a, b)
1776});
1777
1778auto_ops::impl_op_ex!(-|a: &Expr| -> Expr { unary(UnaryOp::Neg, a) });
1779auto_ops::impl_op_ex!(-|a: &Parameter| -> Expr { unary(UnaryOp::Neg, a) });
1780
1781auto_ops::impl_op_ex!(+= |a: &mut Expr, b: &Expr| {
1782 *a = binary(BinaryOp::Add, &*a, b);
1783});
1784auto_ops::impl_op_ex!(+= |a: &mut Expr, b: &f64| {
1785 *a = binary(BinaryOp::Add, &*a, b);
1786});
1787auto_ops::impl_op_ex!(+= |a: &mut Expr, b: &Complex64| {
1788 *a = binary(BinaryOp::Add, &*a, b);
1789});
1790auto_ops::impl_op_ex!(+= |a: &mut Expr, b: &Parameter| {
1791 *a = binary(BinaryOp::Add, &*a, b);
1792});
1793
1794auto_ops::impl_op_ex!(-= |a: &mut Expr, b: &Expr| {
1795 *a = binary(BinaryOp::Sub, &*a, b);
1796});
1797auto_ops::impl_op_ex!(-= |a: &mut Expr, b: &f64| {
1798 *a = binary(BinaryOp::Sub, &*a, b);
1799});
1800auto_ops::impl_op_ex!(-= |a: &mut Expr, b: &Complex64| {
1801 *a = binary(BinaryOp::Sub, &*a, b);
1802});
1803auto_ops::impl_op_ex!(-= |a: &mut Expr, b: &Parameter| {
1804 *a = binary(BinaryOp::Sub, &*a, b);
1805});
1806
1807auto_ops::impl_op_ex!(*= |a: &mut Expr, b: &Expr| {
1808 *a = binary(BinaryOp::Mul, &*a, b);
1809});
1810auto_ops::impl_op_ex!(*= |a: &mut Expr, b: &f64| {
1811 *a = binary(BinaryOp::Mul, &*a, b);
1812});
1813auto_ops::impl_op_ex!(*= |a: &mut Expr, b: &Complex64| {
1814 *a = binary(BinaryOp::Mul, &*a, b);
1815});
1816auto_ops::impl_op_ex!(*= |a: &mut Expr, b: &Parameter| {
1817 *a = binary(BinaryOp::Mul, &*a, b);
1818});
1819
1820auto_ops::impl_op_ex!(/= |a: &mut Expr, b: &Expr| {
1821 *a = binary(BinaryOp::Div, &*a, b);
1822});
1823auto_ops::impl_op_ex!(/= |a: &mut Expr, b: &f64| {
1824 *a = binary(BinaryOp::Div, &*a, b);
1825});
1826auto_ops::impl_op_ex!(/= |a: &mut Expr, b: &Complex64| {
1827 *a = binary(BinaryOp::Div, &*a, b);
1828});
1829auto_ops::impl_op_ex!(/= |a: &mut Expr, b: &Parameter| {
1830 *a = binary(BinaryOp::Div, &*a, b);
1831});
1832
1833impl From<f64> for Expr {
1834 fn from(value: f64) -> Self {
1835 Self::new(DagNodeKind::RealConst(value))
1836 }
1837}
1838
1839impl From<&f64> for Expr {
1840 fn from(value: &f64) -> Self {
1841 Self::new(DagNodeKind::RealConst(*value))
1842 }
1843}
1844
1845impl From<Complex64> for Expr {
1846 fn from(value: Complex64) -> Self {
1847 Self::new(DagNodeKind::ComplexConst(value))
1848 }
1849}
1850
1851impl From<&Complex64> for Expr {
1852 fn from(value: &Complex64) -> Self {
1853 Self::new(DagNodeKind::ComplexConst(*value))
1854 }
1855}
1856
1857impl From<&Expr> for Expr {
1858 fn from(value: &Expr) -> Self {
1859 value.clone()
1860 }
1861}
1862
1863impl From<Parameter> for Expr {
1864 fn from(parameter: Parameter) -> Self {
1865 Expr::new(DagNodeKind::ScalarParam(parameter))
1866 }
1867}
1868
1869impl From<&Parameter> for Expr {
1870 fn from(parameter: &Parameter) -> Self {
1871 parameter.clone().into()
1872 }
1873}
1874
1875pub fn cis(phase: Expr) -> Expr {
1877 phase.cos() + Complex64::I * phase.sin()
1878}
1879
1880pub fn complex(re: impl Into<Expr>, im: impl Into<Expr>) -> Expr {
1882 Expr::new(DagNodeKind::Complex {
1883 re: re.into(),
1884 im: im.into(),
1885 })
1886}
1887
1888pub fn polar_complex(mag: impl Into<Expr>, phase: impl Into<Expr>) -> Expr {
1890 mag.into() * (Complex64::I * phase.into()).exp()
1891}
1892
1893pub fn event_scalar(name: impl Into<Arc<str>>) -> Expr {
1895 Expr::new(DagNodeKind::EventScalar(name.into()))
1896}
1897
1898pub fn event_p4_component(name: impl Into<Arc<str>>, component: P4Component) -> Expr {
1900 Expr::new(DagNodeKind::EventP4Component {
1901 name: name.into(),
1902 component,
1903 })
1904}
1905
1906pub fn atan2(y: impl Into<Expr>, x: impl Into<Expr>) -> Expr {
1908 binary(BinaryOp::Atan2, y, x)
1909}
1910
1911pub fn acos(value: impl Into<Expr>) -> Expr {
1913 value.into().acos()
1914}
1915
1916pub fn vector<E>(elements: impl IntoIterator<Item = E>) -> Expr
1918where
1919 E: Into<Expr>,
1920 Expr: From<E>,
1921{
1922 Expr::new(DagNodeKind::Vector {
1923 elements: elements.into_iter().map(Expr::from).collect(),
1924 })
1925}
1926
1927pub fn matrix<const R: usize, const C: usize, E>(elements: [[E; C]; R]) -> Expr
1929where
1930 E: Into<Expr>,
1931 Expr: From<E>,
1932{
1933 Expr::new(DagNodeKind::Matrix {
1934 rows: R,
1935 cols: C,
1936 elements: elements.into_iter().flatten().map(Expr::from).collect(),
1937 })
1938}
1939
1940pub fn matrix_from_flat<E>(
1948 rows: usize,
1949 cols: usize,
1950 elements: impl IntoIterator<Item = E>,
1951) -> Result<Expr, ExprShapeError>
1952where
1953 E: Into<Expr>,
1954 Expr: From<E>,
1955{
1956 if rows == 0 || cols == 0 {
1957 return Err(ExprShapeError::new(
1958 "matrix constructor",
1959 format!("matrix dimensions must be nonzero, got {rows}x{cols}"),
1960 ));
1961 }
1962 let expected = rows.checked_mul(cols).ok_or_else(|| {
1963 ExprShapeError::new("matrix constructor", "row/column product overflowed")
1964 })?;
1965 let elements = elements.into_iter().map(Expr::from).collect::<Vec<_>>();
1966 if elements.len() != expected {
1967 return Err(ExprShapeError::new(
1968 "matrix constructor",
1969 format!(
1970 "shape {rows}x{cols} requires {expected} elements, got {}",
1971 elements.len()
1972 ),
1973 ));
1974 }
1975 for element in &elements {
1976 element.expect_shape("matrix constructor", ExprShape::Scalar)?;
1977 }
1978 Ok(Expr::new(DagNodeKind::Matrix {
1979 rows,
1980 cols,
1981 elements,
1982 }))
1983}
1984
1985pub fn matmul(lhs: impl Into<Expr>, rhs: impl Into<Expr>) -> Expr {
1987 Expr::new(DagNodeKind::MatMul {
1988 lhs: lhs.into(),
1989 rhs: rhs.into(),
1990 })
1991}
1992
1993pub fn matvec(matrix: impl Into<Expr>, vector: impl Into<Expr>) -> Expr {
1995 Expr::new(DagNodeKind::MatVec {
1996 matrix: matrix.into(),
1997 vector: vector.into(),
1998 })
1999}
2000
2001pub fn dot(lhs: impl Into<Expr>, rhs: impl Into<Expr>) -> Expr {
2003 Expr::new(DagNodeKind::Dot {
2004 lhs: lhs.into(),
2005 rhs: rhs.into(),
2006 })
2007}
2008
2009pub fn solve(matrix: impl Into<Expr>, rhs: impl Into<Expr>) -> Expr {
2011 Expr::new(DagNodeKind::Solve {
2012 matrix: matrix.into(),
2013 rhs: rhs.into(),
2014 })
2015}
2016
2017fn unary(op: UnaryOp, expr: impl Into<Expr>) -> Expr {
2018 Expr::new(DagNodeKind::Unary {
2019 op,
2020 input: expr.into(),
2021 })
2022}
2023
2024fn binary(op: BinaryOp, lhs: impl Into<Expr>, rhs: impl Into<Expr>) -> Expr {
2025 Expr::new(DagNodeKind::Binary {
2026 op,
2027 lhs: lhs.into(),
2028 rhs: rhs.into(),
2029 })
2030}
2031
2032#[derive(Clone, Debug, Serialize, Deserialize)]
2034pub struct ExprGraph {
2035 root: ExprId,
2036 nodes: Vec<ExprNode>,
2037 metadata: Vec<ExprMetadata>,
2038}
2039
2040#[doc(hidden)]
2046pub struct ExprGraphRebuilder<K> {
2047 nodes: Vec<ExprNode>,
2048 metadata: Vec<ExprMetadata>,
2049 remapped: HashMap<K, ExprId>,
2050}
2051
2052#[doc(hidden)]
2053impl<K> ExprGraphRebuilder<K>
2054where
2055 K: Eq + Hash,
2056{
2057 pub fn with_capacity(capacity: usize) -> Self {
2059 Self {
2060 nodes: Vec::with_capacity(capacity),
2061 metadata: Vec::with_capacity(capacity),
2062 remapped: HashMap::with_capacity(capacity),
2063 }
2064 }
2065
2066 pub fn remapped(&self, key: &K) -> Option<ExprId> {
2068 self.remapped.get(key).copied()
2069 }
2070
2071 pub fn nodes(&self) -> &[ExprNode] {
2073 &self.nodes
2074 }
2075
2076 pub fn metadata(&self) -> &[ExprMetadata] {
2078 &self.metadata
2079 }
2080
2081 pub fn alias(&mut self, key: K, id: ExprId) {
2090 assert!(
2091 !self.remapped.contains_key(&key),
2092 "a rebuild key may only be mapped once"
2093 );
2094 assert!(
2095 id.index() < self.nodes.len(),
2096 "a rebuild alias must reference an emitted node"
2097 );
2098 self.remapped.insert(key, id);
2099 }
2100
2101 pub fn emit_anonymous(&mut self, node: ExprNode, metadata: ExprMetadata) -> ExprId {
2110 let id = ExprId::from_index(self.nodes.len());
2111 assert!(
2112 node.children().all(|child| child.index() < id.index()),
2113 "rebuilt expression children must be emitted before their parent"
2114 );
2115 self.nodes.push(node);
2116 self.metadata.push(metadata);
2117 id
2118 }
2119
2120 pub fn emit(&mut self, key: K, node: ExprNode, metadata: ExprMetadata) -> ExprId {
2127 assert!(
2128 !self.remapped.contains_key(&key),
2129 "a rebuild key may only be mapped once"
2130 );
2131 let id = self.emit_anonymous(node, metadata);
2132 self.remapped.insert(key, id);
2133 id
2134 }
2135
2136 pub fn finish(self, root: ExprId) -> Result<ExprGraph, ExprGraphError> {
2138 ExprGraph::from_parts(root, self.nodes, self.metadata)
2139 }
2140}
2141
2142impl ExprGraph {
2143 #[doc(hidden)]
2149 pub fn reachable_post_order(&self, roots: impl IntoIterator<Item = ExprId>) -> Vec<ExprId> {
2150 let roots = roots.into_iter().collect::<Vec<_>>();
2151 let mut visited = HashSet::with_capacity(self.nodes.len());
2152 let mut stack = Vec::new();
2153 let mut order = Vec::new();
2154
2155 for root in roots.into_iter().rev() {
2156 stack.push((root, false));
2157 }
2158 while let Some((id, expanded)) = stack.pop() {
2159 if expanded {
2160 order.push(id);
2161 continue;
2162 }
2163 if self.node(id).is_none() {
2164 continue;
2165 }
2166 if !visited.insert(id) {
2167 continue;
2168 }
2169 stack.push((id, true));
2170 if let Some(node) = self.node(id) {
2171 for child in node.children().rev() {
2172 stack.push((child, false));
2173 }
2174 }
2175 }
2176 order
2177 }
2178
2179 pub fn fix_parameter(&self, name: &str, value: f64) -> ParamResult<Self> {
2187 self.map_parameter(name, |parameter| {
2188 if !parameter.bounds_spec().contains(value) {
2189 return Err(ParamError::FixedValueOutOfBounds {
2190 name: name.to_owned(),
2191 value,
2192 });
2193 }
2194 Ok(parameter.clone().with_fixed_value(value))
2195 })
2196 }
2197
2198 pub fn free_parameter(&self, name: &str) -> ParamResult<Self> {
2205 self.map_parameter(name, |parameter| Ok(parameter.clone().with_free()))
2206 }
2207
2208 fn map_parameter(
2209 &self,
2210 name: &str,
2211 mut map: impl FnMut(&Parameter) -> ParamResult<Parameter>,
2212 ) -> ParamResult<Self> {
2213 let mut found = false;
2214 let mut graph = self.clone();
2215 for node in &mut graph.nodes {
2216 if let ExprNode::ScalarParam(parameter) = node
2217 && parameter.name() == name
2218 {
2219 *parameter = map(parameter)?;
2220 found = true;
2221 }
2222 }
2223 if !found {
2224 return Err(ParamError::UnknownName(name.to_owned()));
2225 }
2226 Ok(graph)
2227 }
2228
2229 pub fn project_tags<'a>(&self, tags: impl IntoIterator<Item = &'a str>) -> Self {
2238 let tags: Vec<_> = tags.into_iter().collect();
2239 let mut rebuild = ExprGraphRebuilder::with_capacity(self.nodes.len());
2240 let root_key = (self.root, false);
2241 let mut visited = HashSet::with_capacity(self.nodes.len());
2242 let mut stack = vec![(root_key, false)];
2243 while let Some((key @ (old, retain_all), expanded)) = stack.pop() {
2244 if expanded {
2245 let old_metadata = &self.metadata[old.index()];
2246 let matches = old_metadata
2247 .tags
2248 .iter()
2249 .any(|tag| tags.contains(&tag.as_ref()));
2250 let node = if !retain_all && !old_metadata.tags.is_empty() && !matches {
2251 ExprNode::RealConst(0.0)
2252 } else {
2253 let retain_children = retain_all || matches;
2254 self.nodes[old.index()].map_children(|child| {
2255 rebuild
2256 .remapped(&(child, retain_children))
2257 .expect("tag projection emits children before parents")
2258 })
2259 };
2260 let metadata = if matches || retain_all {
2261 old_metadata.clone()
2262 } else {
2263 ExprMetadata::new(old_metadata.source)
2264 };
2265 rebuild.emit(key, node, metadata);
2266 continue;
2267 }
2268 if !visited.insert(key) {
2269 continue;
2270 }
2271 stack.push((key, true));
2272 let old_metadata = &self.metadata[old.index()];
2273 let matches = old_metadata
2274 .tags
2275 .iter()
2276 .any(|tag| tags.contains(&tag.as_ref()));
2277 if retain_all || old_metadata.tags.is_empty() || matches {
2278 let retain_children = retain_all || matches;
2279 for child in self.nodes[old.index()].children().rev() {
2280 stack.push(((child, retain_children), false));
2281 }
2282 }
2283 }
2284 let root = rebuild
2285 .remapped(&root_key)
2286 .expect("tag projection emits its root");
2287 rebuild
2288 .finish(root)
2289 .expect("tag projection rebuilds a valid expression graph")
2290 }
2291
2292 pub fn from_parts(
2303 root: ExprId,
2304 nodes: Vec<ExprNode>,
2305 metadata: Vec<ExprMetadata>,
2306 ) -> Result<Self, ExprGraphError> {
2307 if nodes.is_empty() {
2308 return Err(ExprGraphError::Empty);
2309 }
2310 if nodes.len() != metadata.len() {
2311 return Err(ExprGraphError::MetadataLength {
2312 node_len: nodes.len(),
2313 metadata_len: metadata.len(),
2314 });
2315 }
2316 if root.index() >= nodes.len() {
2317 return Err(ExprGraphError::InvalidRoot {
2318 root: root.index(),
2319 node_len: nodes.len(),
2320 });
2321 }
2322 for (index, node) in nodes.iter().enumerate() {
2323 for child in node.children() {
2324 if child.index() >= nodes.len() {
2325 return Err(ExprGraphError::InvalidChild {
2326 node: index,
2327 child: child.index(),
2328 });
2329 }
2330 if child.index() >= index {
2331 return Err(ExprGraphError::InvalidChildOrder {
2332 node: index,
2333 child: child.index(),
2334 });
2335 }
2336 }
2337 }
2338 Ok(Self {
2339 root,
2340 nodes,
2341 metadata,
2342 })
2343 }
2344
2345 pub fn root(&self) -> ExprId {
2347 self.root
2348 }
2349
2350 pub fn node(&self, id: ExprId) -> Option<&ExprNode> {
2352 self.nodes.get(id.index())
2353 }
2354
2355 pub fn nodes(&self) -> &[ExprNode] {
2357 &self.nodes
2358 }
2359
2360 pub fn metadata(&self, id: ExprId) -> Option<&ExprMetadata> {
2362 self.metadata.get(id.index())
2363 }
2364}
2365
2366pub(crate) fn node_children(node: &ExprNode) -> Vec<(String, ExprId)> {
2367 node.children()
2368 .enumerate()
2369 .map(|(index, child)| (node_child_label(node, index), child))
2370 .collect()
2371}
2372
2373fn node_child_label(node: &ExprNode, index: usize) -> String {
2374 match node {
2375 ExprNode::Unary { .. } | ExprNode::Component { .. } | ExprNode::MatrixElement { .. } => {
2376 "input".into()
2377 }
2378 ExprNode::Binary { .. } | ExprNode::MatMul { .. } | ExprNode::Dot { .. } => {
2379 if index == 0 { "lhs" } else { "rhs" }.into()
2380 }
2381 ExprNode::NaryAdd { .. } => format!("term[{index}]"),
2382 ExprNode::NaryMul { .. } => format!("factor[{index}]"),
2383 ExprNode::Complex { .. } => if index == 0 { "re" } else { "im" }.into(),
2384 ExprNode::Vector { .. } => format!("element[{index}]"),
2385 ExprNode::Matrix { cols, .. } => {
2386 format!("element[{},{}]", index / cols, index % cols)
2387 }
2388 ExprNode::MatVec { .. } => if index == 0 { "matrix" } else { "vector" }.into(),
2389 ExprNode::Solve { .. } => if index == 0 { "matrix" } else { "rhs" }.into(),
2390 ExprNode::RealConst(_)
2391 | ExprNode::ComplexConst(_)
2392 | ExprNode::ScalarParam(_)
2393 | ExprNode::EventScalar(_)
2394 | ExprNode::EventP4Component { .. } => unreachable!("leaf nodes have no child labels"),
2395 }
2396}
2397
2398#[derive(Default)]
2399struct GraphBuilder {
2400 nodes: Vec<ExprNode>,
2401 metadata: Vec<ExprMetadata>,
2402 ids: HashMap<usize, ExprId>,
2403}
2404
2405impl GraphBuilder {
2406 fn new() -> Self {
2407 Self::default()
2408 }
2409
2410 fn build(mut self, expr: &Expr) -> ExprGraph {
2411 let mut stack = vec![(expr.clone(), false)];
2412 while let Some((expr, expanded)) = stack.pop() {
2413 let key = Arc::as_ptr(&expr.node) as usize;
2414 if self.ids.contains_key(&key) {
2415 continue;
2416 }
2417 if expanded {
2418 let node = self.lower(&expr.node.kind);
2419 let id = ExprId::from_index(self.nodes.len());
2420 self.nodes.push(node);
2421 self.metadata.push(expr.node.metadata.clone());
2422 self.ids.insert(key, id);
2423 continue;
2424 }
2425 stack.push((expr.clone(), true));
2426 for index in (0..expr.node.kind.child_count()).rev() {
2427 stack.push((expr.node.kind.child_at(index).clone(), false));
2428 }
2429 }
2430 let root = self.id(expr);
2431 ExprGraph {
2432 root,
2433 nodes: self.nodes,
2434 metadata: self.metadata,
2435 }
2436 }
2437
2438 fn id(&self, expr: &Expr) -> ExprId {
2439 let key = Arc::as_ptr(&expr.node) as usize;
2440 self.ids[&key]
2441 }
2442
2443 fn lower(&self, kind: &DagNodeKind) -> ExprNode {
2444 match kind {
2445 DagNodeKind::RealConst(value) => ExprNode::RealConst(*value),
2446 DagNodeKind::ComplexConst(value) => ExprNode::ComplexConst(*value),
2447 DagNodeKind::ScalarParam(parameter) => ExprNode::ScalarParam(parameter.clone()),
2448 DagNodeKind::EventScalar(name) => ExprNode::EventScalar(Arc::clone(name)),
2449 DagNodeKind::EventP4Component { name, component } => ExprNode::EventP4Component {
2450 name: Arc::clone(name),
2451 component: *component,
2452 },
2453 DagNodeKind::Unary { op, input } => {
2454 let input = self.id(input);
2455 ExprNode::Unary { op: *op, input }
2456 }
2457 DagNodeKind::Binary { op, lhs, rhs } => {
2458 let lhs = self.id(lhs);
2459 let rhs = self.id(rhs);
2460 ExprNode::Binary { op: *op, lhs, rhs }
2461 }
2462 DagNodeKind::Complex { re, im } => {
2463 let re = self.id(re);
2464 let im = self.id(im);
2465 ExprNode::Complex { re, im }
2466 }
2467 DagNodeKind::Vector { elements } => ExprNode::Vector {
2468 elements: elements.iter().map(|expr| self.id(expr)).collect(),
2469 },
2470 DagNodeKind::Matrix {
2471 rows,
2472 cols,
2473 elements,
2474 } => ExprNode::Matrix {
2475 rows: *rows,
2476 cols: *cols,
2477 elements: elements.iter().map(|expr| self.id(expr)).collect(),
2478 },
2479 DagNodeKind::Component { input, index } => {
2480 let input = self.id(input);
2481 ExprNode::Component {
2482 input,
2483 index: *index,
2484 }
2485 }
2486 DagNodeKind::MatrixElement { input, row, col } => {
2487 let input = self.id(input);
2488 ExprNode::MatrixElement {
2489 input,
2490 row: *row,
2491 col: *col,
2492 }
2493 }
2494 DagNodeKind::MatMul { lhs, rhs } => {
2495 let lhs = self.id(lhs);
2496 let rhs = self.id(rhs);
2497 ExprNode::MatMul { lhs, rhs }
2498 }
2499 DagNodeKind::MatVec { matrix, vector } => {
2500 let matrix = self.id(matrix);
2501 let vector = self.id(vector);
2502 ExprNode::MatVec { matrix, vector }
2503 }
2504 DagNodeKind::Dot { lhs, rhs } => {
2505 let lhs = self.id(lhs);
2506 let rhs = self.id(rhs);
2507 ExprNode::Dot { lhs, rhs }
2508 }
2509 DagNodeKind::Solve { matrix, rhs } => {
2510 let matrix = self.id(matrix);
2511 let rhs = self.id(rhs);
2512 ExprNode::Solve { matrix, rhs }
2513 }
2514 }
2515 }
2516}
2517
2518fn source_kind(kind: &DagNodeKind) -> ExprSourceKind {
2519 match kind {
2520 DagNodeKind::RealConst(_) | DagNodeKind::ComplexConst(_) => ExprSourceKind::Const,
2521 DagNodeKind::ScalarParam(_) => ExprSourceKind::Param,
2522 DagNodeKind::EventScalar(_) | DagNodeKind::EventP4Component { .. } => ExprSourceKind::Event,
2523 DagNodeKind::Unary { .. } => ExprSourceKind::Unary,
2524 DagNodeKind::Binary { .. } => ExprSourceKind::Binary,
2525 DagNodeKind::Complex { .. } => ExprSourceKind::Complex,
2526 DagNodeKind::Vector { .. } | DagNodeKind::Component { .. } | DagNodeKind::Dot { .. } => {
2527 ExprSourceKind::Vector
2528 }
2529 DagNodeKind::Matrix { .. } | DagNodeKind::MatrixElement { .. } => ExprSourceKind::Matrix,
2530 DagNodeKind::MatMul { .. } | DagNodeKind::MatVec { .. } | DagNodeKind::Solve { .. } => {
2531 ExprSourceKind::LinearAlgebra
2532 }
2533 }
2534}
2535
2536#[cfg(test)]
2537mod tests {
2538 use super::*;
2539 use crate::parameter;
2540
2541 #[test]
2542 fn builds_target_syntax_without_layout_or_context() {
2543 let model = (Complex64::I * parameter!("y", initial : 1.0, bounds : (0.0, 2.0))
2544 + parameter!("x"))
2545 .norm_sqr();
2546
2547 let graph = model.to_graph();
2548 assert!(matches!(
2549 graph.node(graph.root()),
2550 Some(ExprNode::Unary {
2551 op: UnaryOp::NormSqr,
2552 ..
2553 })
2554 ));
2555 }
2556
2557 #[test]
2558 fn parameter_nodes_store_specs_but_do_not_make_layouts() {
2559 let graph = Expr::from(parameter!("x", initial: 1.0)).to_graph();
2560 assert!(matches!(
2561 graph.node(graph.root()),
2562 Some(ExprNode::ScalarParam(spec)) if spec.name() == "x"
2563 ));
2564 }
2565
2566 #[test]
2567 fn complex_constructor_builds_expression_node() {
2568 let graph = complex(parameter!("re"), parameter!("im")).to_graph();
2569
2570 assert!(matches!(
2571 graph.node(graph.root()),
2572 Some(ExprNode::Complex { .. })
2573 ));
2574 }
2575
2576 #[test]
2577 fn polar_complex_lowers_to_expression_graph() {
2578 let graph = polar_complex(parameter!("mag"), parameter!("phase")).to_graph();
2579
2580 assert!(graph.nodes().iter().any(|node| matches!(
2581 node,
2582 ExprNode::Unary {
2583 op: UnaryOp::Exp,
2584 ..
2585 }
2586 )));
2587 }
2588
2589 #[test]
2590 fn metadata_survives_graph_construction() {
2591 let graph = event_scalar("mass")
2592 .named("event mass")
2593 .tagged("data")
2594 .tagged("data")
2595 .to_graph();
2596 let metadata = graph.metadata(graph.root()).unwrap();
2597 assert_eq!(metadata.name(), Some("event mass"));
2598 assert_eq!(metadata.tags(), &[Arc::from("data")]);
2599 assert!(metadata.has_tag("data"));
2600 }
2601
2602 #[test]
2603 fn expressions_round_trip_through_serde_with_metadata() {
2604 let expression = ((parameter!("x", initial: 1.0) + 2.0).named("offset")
2605 * event_scalar("mass").tagged("data"))
2606 .tagged("model");
2607 let encoded = serde_json::to_string(&expression).unwrap();
2608 let decoded: Expr = serde_json::from_str(&encoded).unwrap();
2609
2610 assert_eq!(
2611 serde_json::to_value(expression.to_graph()).unwrap(),
2612 serde_json::to_value(decoded.to_graph()).unwrap()
2613 );
2614 }
2615
2616 #[test]
2617 fn display_formats_graph_as_labeled_tree() {
2618 let graph = ((parameter!("x") + 1.0).named("offset") * event_scalar("mass").tagged("data"))
2619 .to_graph();
2620 let display = graph.display_tree().to_string();
2621
2622 assert!(display.starts_with("ExprGraph(root=#"));
2623 assert!(display.contains("Binary(Mul)"));
2624 assert!(display.contains("┣ lhs:"));
2625 assert!(display.contains("┗ rhs:"));
2626 assert!(display.contains("Binary(Add) name=\"offset\""));
2627 assert!(display.contains("ScalarParam(x)"));
2628 assert!(display.contains("RealConst(1)"));
2629 assert!(display.contains("EventScalar(mass) tags=[data]"));
2630 }
2631
2632 #[test]
2633 fn display_formats_graph_as_expression() {
2634 let costheta = Expr::from(parameter!("costheta"));
2635 let phi = event_scalar("phi");
2636 let p = Expr::from(parameter!("p"));
2637 let phase = Expr::from(7.0) * Complex64::I;
2638 let graph =
2639 (((costheta.powi(2) * phi.sin()) - 5.2).norm_sqr() * p.conj() - phase.exp()).to_graph();
2640
2641 assert_eq!(
2642 graph.to_string(),
2643 "|costheta^2 * sin(phi) - 5.2|^2 * conj(p) - exp(7 * i)"
2644 );
2645 }
2646
2647 #[test]
2648 fn display_parenthesizes_when_precedence_requires_it() {
2649 let a = Expr::from(parameter!("a"));
2650 let b = Expr::from(parameter!("b"));
2651 let c = Expr::from(parameter!("c"));
2652
2653 assert_eq!(
2654 (a.clone() * (b.clone() + c.clone())).to_graph().to_string(),
2655 "a * (b + c)"
2656 );
2657 assert_eq!(
2658 (a.clone() - (b.clone() - c.clone())).to_graph().to_string(),
2659 "a - (b - c)"
2660 );
2661 assert_eq!(((a / b) / c).to_graph().to_string(), "a / b / c");
2662 }
2663
2664 #[test]
2665 fn display_rounds_tiny_float_representation_noise() {
2666 let metadata = ExprMetadata::new(ExprSourceKind::Const);
2667 let graph = ExprGraph::from_parts(
2668 ExprId::from_index(2),
2669 vec![
2670 ExprNode::RealConst(2.9999999999999996),
2671 ExprNode::ComplexConst(Complex64::new(0.30000000000000004, 1.9999999999999998)),
2672 ExprNode::Binary {
2673 op: BinaryOp::Add,
2674 lhs: ExprId::from_index(0),
2675 rhs: ExprId::from_index(1),
2676 },
2677 ],
2678 vec![metadata.clone(), metadata.clone(), metadata],
2679 )
2680 .unwrap();
2681
2682 assert_eq!(graph.to_string(), "3 + 0.3 + 2i");
2683 assert!(graph.display_tree().to_string().contains("RealConst(3)"));
2684 assert!(
2685 graph
2686 .display_tree()
2687 .to_string()
2688 .contains("ComplexConst(0.3 + 2i)")
2689 );
2690 }
2691
2692 #[test]
2693 fn display_formats_p4_components_and_atan2() {
2694 let expr = atan2(
2695 event_p4_component("ks1", P4Component::Py),
2696 event_p4_component("ks1", P4Component::Px),
2697 );
2698
2699 assert_eq!(expr.to_graph().to_string(), "atan2(ks1.py, ks1.px)");
2700 }
2701
2702 #[test]
2703 fn graph_from_parts_validates_structure() {
2704 let metadata = ExprMetadata::new(ExprSourceKind::Const);
2705 let graph = ExprGraph::from_parts(
2706 ExprId::from_index(1),
2707 vec![
2708 ExprNode::RealConst(1.0),
2709 ExprNode::Unary {
2710 op: UnaryOp::Neg,
2711 input: ExprId::from_index(0),
2712 },
2713 ],
2714 vec![metadata.clone(), metadata.clone()],
2715 )
2716 .unwrap();
2717 assert!(matches!(
2718 graph.node(graph.root()),
2719 Some(ExprNode::Unary {
2720 op: UnaryOp::Neg,
2721 ..
2722 })
2723 ));
2724
2725 let err = ExprGraph::from_parts(
2726 ExprId::from_index(0),
2727 vec![ExprNode::RealConst(1.0)],
2728 Vec::new(),
2729 )
2730 .unwrap_err();
2731 assert!(matches!(err, ExprGraphError::MetadataLength { .. }));
2732
2733 let err = ExprGraph::from_parts(
2734 ExprId::from_index(0),
2735 vec![ExprNode::Unary {
2736 op: UnaryOp::Neg,
2737 input: ExprId::from_index(0),
2738 }],
2739 vec![metadata],
2740 )
2741 .unwrap_err();
2742 assert!(matches!(err, ExprGraphError::InvalidChildOrder { .. }));
2743 }
2744
2745 #[test]
2746 fn graph_preserves_unsimplified_expression_shape() {
2747 let graph = (parameter!("x") + 0.0).to_graph();
2748 assert!(matches!(
2749 graph.node(graph.root()),
2750 Some(ExprNode::Binary {
2751 op: BinaryOp::Add,
2752 ..
2753 })
2754 ));
2755 }
2756
2757 #[test]
2758 fn graph_preserves_written_operand_order_for_commutative_ops() {
2759 let left_param = (parameter!("x") + 1.0).to_graph();
2760 assert!(matches!(
2761 left_param.node(left_param.root()),
2762 Some(ExprNode::Binary {
2763 op: BinaryOp::Add,
2764 lhs,
2765 rhs
2766 }) if matches!(left_param.node(*lhs), Some(ExprNode::ScalarParam(parameter)) if parameter.name() == "x")
2767 && matches!(left_param.node(*rhs), Some(ExprNode::RealConst(1.0)))
2768 ));
2769
2770 let right_param = (1.0 + parameter!("x")).to_graph();
2771 assert!(matches!(
2772 right_param.node(right_param.root()),
2773 Some(ExprNode::Binary {
2774 op: BinaryOp::Add,
2775 lhs,
2776 rhs
2777 }) if matches!(right_param.node(*lhs), Some(ExprNode::RealConst(1.0)))
2778 && matches!(right_param.node(*rhs), Some(ExprNode::ScalarParam(parameter)) if parameter.name() == "x")
2779 ));
2780 }
2781
2782 #[test]
2783 fn represents_kmatrix_style_solve_graph() {
2784 let beta = vector([
2785 complex(parameter!("b0_re"), parameter!("b0_im")),
2786 complex(parameter!("b1_re"), parameter!("b1_im")),
2787 ]);
2788 let a = matrix([
2789 [Complex64::new(1.0, 0.0), Complex64::new(0.0, 1.0)],
2790 [Complex64::new(0.0, -1.0), Complex64::new(1.0, 0.0)],
2791 ]);
2792 let graph = solve(a, beta).component(0).to_graph();
2793
2794 assert!(
2795 graph
2796 .nodes()
2797 .iter()
2798 .any(|node| matches!(node, ExprNode::Solve { .. }))
2799 );
2800 }
2801
2802 #[test]
2803 fn graph_builder_preserves_shared_dag_nodes() {
2804 let shared = event_scalar("x").sin();
2805 let expression = vector((0..1_000).map(|_| shared.clone()));
2806 let graph = expression.to_graph();
2807
2808 assert_eq!(graph.nodes().len(), 3);
2809 let ExprNode::Vector { elements } = graph.node(graph.root()).unwrap() else {
2810 panic!("root should be a vector");
2811 };
2812 assert!(elements.windows(2).all(|pair| pair[0] == pair[1]));
2813 }
2814
2815 #[test]
2816 fn expression_projection_preserves_occurrence_rebuild_behavior() {
2817 let shared = event_scalar("x").sin();
2818 let projected = (shared.clone() + shared).project_tags(["selected"]);
2819 let graph = projected.to_graph();
2820
2821 assert_eq!(
2822 graph
2823 .nodes()
2824 .iter()
2825 .filter(|node| matches!(
2826 node,
2827 ExprNode::Unary {
2828 op: UnaryOp::Sin,
2829 ..
2830 }
2831 ))
2832 .count(),
2833 2
2834 );
2835 }
2836
2837 #[test]
2838 fn iterative_construction_and_projection_handle_deep_expressions() {
2839 let mut expression = event_scalar("x");
2840 for _ in 0..10_000 {
2841 expression = expression.sin();
2842 }
2843
2844 let projected = expression.project_tags(["selected"]);
2845 let graph = projected.to_graph();
2846
2847 assert_eq!(graph.nodes().len(), 10_001);
2848 assert_eq!(
2849 graph.reachable_post_order([graph.root()]).len(),
2850 graph.nodes().len()
2851 );
2852
2853 std::mem::forget(expression);
2856 std::mem::forget(projected);
2857 }
2858
2859 #[test]
2860 fn reachable_post_order_preserves_child_order_and_deduplicates_shared_nodes() {
2861 let shared = event_scalar("x").sin();
2862 let graph = (shared.clone() + shared).to_graph();
2863 let order = graph.reachable_post_order([graph.root()]);
2864
2865 assert_eq!(order.len(), graph.nodes().len());
2866 assert_eq!(order.last(), Some(&graph.root()));
2867 for id in order {
2868 for child in graph.node(id).unwrap().children() {
2869 assert!(child.index() < id.index());
2870 }
2871 }
2872 }
2873
2874 #[test]
2875 fn dynamic_matrices_and_shapes_are_checked_eagerly() {
2876 let dynamic = matrix_from_flat(2, 2, [1.0, 2.0, 3.0, 4.0]).unwrap();
2877 assert_eq!(
2878 dynamic.shape().unwrap(),
2879 ExprShape::Matrix { rows: 2, cols: 2 }
2880 );
2881 assert!(matrix_from_flat(2, 2, [1.0, 2.0, 3.0]).is_err());
2882 assert!(matmul(dynamic, matrix([[1.0, 2.0, 3.0]])).shape().is_err());
2883 }
2884
2885 #[test]
2886 fn assignment_operators_build_binary_expression_nodes() {
2887 let mut expr = Expr::from(parameter!("x"));
2888 expr += parameter!("y");
2889 expr -= 1.0;
2890 expr *= Complex64::I;
2891 expr /= Expr::from(parameter!("z"));
2892
2893 let graph = expr.to_graph();
2894 assert!(matches!(
2895 graph.node(graph.root()),
2896 Some(ExprNode::Binary {
2897 op: BinaryOp::Div,
2898 ..
2899 })
2900 ));
2901 assert_eq!(
2902 graph
2903 .nodes()
2904 .iter()
2905 .filter(|node| matches!(node, ExprNode::Binary { .. }))
2906 .count(),
2907 4
2908 );
2909 }
2910
2911 #[test]
2912 fn assignment_operators_accept_borrowed_rhs_values() {
2913 let y = parameter!("y");
2914 let one = 1.0;
2915 let i = Complex64::I;
2916 let z = Expr::from(parameter!("z"));
2917
2918 let mut expr = Expr::from(parameter!("x"));
2919 expr += &y;
2920 expr -= &one;
2921 expr *= &i;
2922 expr /= &z;
2923
2924 let graph = expr.to_graph();
2925 assert!(matches!(
2926 graph.node(graph.root()),
2927 Some(ExprNode::Binary {
2928 op: BinaryOp::Div,
2929 ..
2930 })
2931 ));
2932 }
2933}