1use super::super::saved::{
5 invalid, product, read_complex, read_f64, read_matrix, read_stored, total, write_matrix,
6 write_stored,
7};
8use super::{PortWaves, TrForward, TrPorts, TrResidual};
9use crate::{
10 Complex, Result,
11 numerics::Jet,
12 saved::{Reader, SavedState, Writer},
13};
14use nalgebra::DMatrix;
15
16impl TrResidual {
17 pub fn state_size(n: usize, columns: usize, groups: usize) -> Result<usize> {
19 if n == 0 || columns == 0 || groups == 0 {
20 return Err(invalid());
21 }
22 total(&[
23 140,
24 product(&[32, n, n])?,
25 product(&[48, n, columns])?,
26 product(&[528, groups])?,
27 product(&[9, n])?,
28 product(&[24, columns])?,
29 ])
30 }
31}
32
33impl SavedState for TrResidual {
34 fn save_state(&self) -> Result<Vec<u8>> {
35 let (n, columns, groups) = self.input_shape();
36 let mut writer = Writer::new(Self::state_size(n, columns, groups)?);
37 for dimension in [n, columns, groups, self.ports.axis, self.ports.direction] {
38 writer.usize(dimension);
39 }
40 writer.byte(u8::from(self.ports.helicity));
41 writer.byte(u8::from(self.fixed_q));
42 for matrix in &self.matrices {
43 write_stored(&mut writer, matrix);
44 }
45 write_matrix(&mut writer, &self.incident);
46 for &value in self.ports.ks.iter().flatten().chain(&self.ports.zs) {
47 writer.complex(value);
48 }
49 for &value in self.ports.q.iter().flatten() {
50 writer.f64(value);
51 }
52 for &(group, pol) in &self.ports.modes {
53 writer.usize(group);
54 writer.byte(pol);
55 }
56 for &value in self.forward.value.iter().chain(&self.forward.flux) {
57 writer.f64(value);
58 }
59 for matrix in &self.forward.outgoing {
60 write_matrix(&mut writer, matrix);
61 }
62 for value in self
63 .forward
64 .waves
65 .iter()
66 .flatten()
67 .flatten()
68 .flatten()
69 .flatten()
70 {
71 writer.complex(value.value);
72 }
73 Ok(writer.finish())
74 }
75
76 fn from_state(bytes: &[u8]) -> Result<Self> {
77 let mut reader = Reader::new(bytes);
78 let n = reader.usize()?;
79 let columns = reader.usize()?;
80 let groups = reader.usize()?;
81 let axis = reader.usize()?;
82 let direction = reader.usize()?;
83 let helicity = reader.byte()?;
84 let fixed_q = reader.byte()?;
85 if axis > 2
86 || direction > 1
87 || helicity > 1
88 || fixed_q > 1
89 || bytes.len() != Self::state_size(n, columns, groups)?
90 {
91 return Err(invalid());
92 }
93 let matrices = [read_stored(&mut reader, n)?, read_stored(&mut reader, n)?];
94 let incident = read_matrix(&mut reader, n, columns)?;
95 let mut ks = [[Complex::default(); 2]; 2];
96 let mut zs = [Complex::default(); 2];
97 for value in ks.iter_mut().flatten().chain(&mut zs) {
98 *value = read_complex(&mut reader)?;
99 }
100 let q = (0..groups)
101 .map(|_| Ok([read_f64(&mut reader)?, read_f64(&mut reader)?]))
102 .collect::<Result<Vec<_>>>()?;
103 let modes = (0..n)
104 .map(|_| Ok((reader.usize()?, reader.byte()?)))
105 .collect::<Result<Vec<_>>>()?;
106 let ports = TrPorts {
107 ks,
108 zs,
109 q,
110 modes,
111 axis,
112 direction,
113 helicity: helicity != 0,
114 };
115 ports.validate()?;
116 let mut value = DMatrix::zeros(2, columns);
117 for v in value.iter_mut() {
118 *v = read_f64(&mut reader)?;
119 }
120 let flux = (0..columns)
121 .map(|_| {
122 let flux = read_f64(&mut reader)?;
123 if flux > 0.0 { Ok(flux) } else { Err(invalid()) }
124 })
125 .collect::<Result<Vec<_>>>()?;
126 let outgoing = [
127 read_matrix(&mut reader, n, columns)?,
128 read_matrix(&mut reader, n, columns)?,
129 ];
130 let mut waves: Vec<PortWaves<0>> = vec![Default::default(); groups];
131 for value in waves.iter_mut().flatten().flatten().flatten().flatten() {
132 *value = Jet::constant(read_complex(&mut reader)?);
133 }
134 reader.finish()?;
135 Ok(Self {
136 matrices,
137 incident,
138 ports,
139 forward: TrForward {
140 value,
141 flux,
142 outgoing,
143 waves,
144 },
145 fixed_q: fixed_q != 0,
146 })
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153 use crate::{
154 linalg::view,
155 smatrix::{self, Blocks},
156 };
157
158 #[test]
159 fn tr_roundtrip_preserves_saved_fields_derivatives_and_matrix_layout() {
160 let n = 2;
161 let matrix = DMatrix::from_row_slice(
162 n,
163 n,
164 &[
165 Complex::new(0.8, 0.1),
166 Complex::new(0.1, 0.03),
167 Complex::new(-0.03, 0.07),
168 Complex::new(0.7, 0.12),
169 ],
170 );
171 let reflected = &matrix * Complex::from(0.1);
172 let incident = DMatrix::from_element(n, 3, Complex::new(0.3, -0.1));
173 let ports = TrPorts {
174 ks: [[Complex::new(1.3, 0.03); 2], [Complex::new(1.2, 0.04); 2]],
175 zs: [Complex::new(0.9, 0.01), Complex::new(1.1, -0.01)],
176 q: vec![[0.2, -0.1]],
177 modes: vec![(0, 0), (0, 1)],
178 axis: 2,
179 helicity: true,
180 direction: 0,
181 };
182 let original = smatrix::tr(
184 [view(&matrix).transpose(), view(&reflected)],
185 incident.clone(),
186 ports,
187 false,
188 )
189 .unwrap();
190 let mut bytes = original.save_state().unwrap();
191 assert_eq!(bytes.len(), TrResidual::state_size(n, 3, 1).unwrap());
192 assert!(TrResidual::from_state(&bytes[..bytes.len() - 1]).is_err());
193 let mut extra = bytes.clone();
194 extra.push(0);
195 assert!(TrResidual::from_state(&extra).is_err());
196 let restored = TrResidual::from_state(&bytes).unwrap();
197 assert!(restored.matrices[0].row_major);
198 assert!(!restored.matrices[1].row_major);
199 assert_eq!(original.value(), restored.value());
200 let matrices: Blocks = std::array::from_fn(|_| &matrix * Complex::from(0.03));
201 let ks = [[Complex::new(0.1, -0.02); 2]; 2];
202 let zs = [Complex::new(0.03, -0.01); 2];
203 let q = [[0.02, -0.01]];
204 assert_eq!(
205 original
206 .pushforward(&matrices, &incident, ks, zs, &q)
207 .unwrap(),
208 restored
209 .pushforward(&matrices, &incident, ks, zs, &q)
210 .unwrap()
211 );
212 let cotangent = DMatrix::from_element(2, 3, 0.4);
213 let (a, b) = (
214 original.pullback(&cotangent).unwrap(),
215 restored.pullback(&cotangent).unwrap(),
216 );
217 assert_eq!(a.matrices, b.matrices);
218 assert_eq!(a.incident, b.incident);
219 assert_eq!(a.ks, b.ks);
220 assert_eq!(a.zs, b.zs);
221 assert_eq!(a.q, b.q);
222 bytes[..8].copy_from_slice(&u64::MAX.to_le_bytes());
223 assert!(TrResidual::from_state(&bytes).is_err());
224 }
225}