Skip to main content

sim_lib_interference_compute/
dense.rs

1//! Checked dense host assembly for the canonical CPU Tensor lowering.
2
3use std::sync::Arc;
4
5use sim_kernel::{Cx, Symbol};
6use sim_lib_interference_core::{InterferenceProblem, SamplingPlane};
7use sim_lib_numbers_tensor::{
8    CpuTensorExecutor, Tensor, TypedTensorStorage, domains, parse_f32_literal_cell,
9};
10
11use crate::{LoweredTile, LoweringError, LoweringPlan, PhaseBudget, PreflightCheck, TileProfile};
12
13/// Execution and phase-range evidence retained with a dense f32 field.
14#[derive(Clone, Debug, PartialEq)]
15pub struct DenseExecutionEvidence {
16    executor: Symbol,
17    tiles: usize,
18    max_segments_per_tensor: usize,
19    submissions: usize,
20    phase_limit_rad: f64,
21    predicted_max_abs_psi_rad: f64,
22    observed_max_abs_psi_rad: f64,
23    predicted_geometry_error_rad: f64,
24    predicted_roundoff_error_rad: f64,
25}
26
27impl DenseExecutionEvidence {
28    /// Returns the canonical executor that accepted the lowering.
29    pub fn executor(&self) -> &Symbol {
30        &self.executor
31    }
32
33    /// Returns the number of physical plane tiles.
34    pub fn tiles(&self) -> usize {
35        self.tiles
36    }
37
38    /// Returns the largest admitted segment count for one tile Tensor.
39    pub fn max_segments_per_tensor(&self) -> usize {
40        self.max_segments_per_tensor
41    }
42
43    /// Returns the number of completed executor flushes.
44    pub fn submissions(&self) -> usize {
45        self.submissions
46    }
47
48    /// Returns the admitted upper bound for every residual phase argument.
49    pub fn phase_limit_rad(&self) -> f64 {
50        self.phase_limit_rad
51    }
52
53    /// Returns the conservative tile-radius phase bound.
54    pub fn predicted_max_abs_psi_rad(&self) -> f64 {
55        self.predicted_max_abs_psi_rad
56    }
57
58    /// Returns the largest residual phase computed during source preflight.
59    pub fn observed_max_abs_psi_rad(&self) -> f64 {
60        self.observed_max_abs_psi_rad
61    }
62
63    /// Returns the largest predicted phase error from lowered local geometry.
64    pub fn predicted_geometry_error_rad(&self) -> f64 {
65        self.predicted_geometry_error_rad
66    }
67
68    /// Returns the largest predicted phase error from f32 arithmetic.
69    pub fn predicted_roundoff_error_rad(&self) -> f64 {
70        self.predicted_roundoff_error_rad
71    }
72}
73
74/// A complete finite row-major phasor field evaluated as canonical f32 Tensors.
75#[derive(Clone, Debug, PartialEq)]
76pub struct DenseF32Field {
77    rows: usize,
78    columns: usize,
79    real: Vec<f32>,
80    imaginary: Vec<f32>,
81    evidence: DenseExecutionEvidence,
82}
83
84impl DenseF32Field {
85    /// Returns the number of rows.
86    pub fn rows(&self) -> usize {
87        self.rows
88    }
89
90    /// Returns the number of columns.
91    pub fn columns(&self) -> usize {
92        self.columns
93    }
94
95    /// Returns the row-major real component plane.
96    pub fn real(&self) -> &[f32] {
97        &self.real
98    }
99
100    /// Returns the row-major imaginary component plane.
101    pub fn imaginary(&self) -> &[f32] {
102        &self.imaginary
103    }
104
105    /// Returns one cell's Cartesian components.
106    pub fn cell(&self, row: usize, column: usize) -> Option<(f32, f32)> {
107        let index = row.checked_mul(self.columns)?.checked_add(column)?;
108        (row < self.rows && column < self.columns)
109            .then(|| (self.real[index], self.imaginary[index]))
110    }
111
112    /// Returns the executor, tiling, and phase-bound evidence.
113    pub fn evidence(&self) -> &DenseExecutionEvidence {
114        &self.evidence
115    }
116}
117
118/// Executes the normalized lowering through the canonical dense f32 CPU executor.
119///
120/// This path deliberately ignores any active environment executor. It is the
121/// portable arithmetic baseline used to compare modeled and physical providers.
122pub fn solve_dense_f32_cpu(
123    cx: &mut Cx,
124    problem: &InterferenceProblem,
125    plane: SamplingPlane,
126    phase_budget: PhaseBudget,
127    tile_profile: TileProfile,
128) -> Result<DenseF32Field, LoweringError> {
129    let plan = LoweringPlan::preflight_with_executor(
130        cx,
131        problem,
132        plane,
133        phase_budget,
134        tile_profile,
135        Arc::new(CpuTensorExecutor::new()),
136    )?;
137    let tiles = plan.execute(cx)?;
138    assemble_dense_field(plane, &plan, tiles)
139}
140
141fn assemble_dense_field(
142    plane: SamplingPlane,
143    plan: &LoweringPlan,
144    tiles: Vec<LoweredTile>,
145) -> Result<DenseF32Field, LoweringError> {
146    let cells = plane.cell_count();
147    let mut real = zeroed_component("real", cells)?;
148    let mut imaginary = zeroed_component("imaginary", cells)?;
149    let mut submissions = 0_usize;
150    let mut max_segments_per_tensor = 0_usize;
151
152    for lowered in &tiles {
153        let tile = lowered.tile();
154        max_segments_per_tensor = max_segments_per_tensor.max(tile.segments_per_tensor());
155        submissions = submissions
156            .checked_add(lowered.submissions().len())
157            .ok_or_else(|| {
158                LoweringError::new(
159                    PreflightCheck::Execution,
160                    "dense submission count overflowed usize",
161                )
162            })?;
163        place_component(
164            "real",
165            plane,
166            tile.row_start(),
167            tile.column_start(),
168            tile.rows(),
169            tile.columns(),
170            tensor_f32_cells(lowered.real())?.as_ref(),
171            &mut real,
172        )?;
173        place_component(
174            "imaginary",
175            plane,
176            tile.row_start(),
177            tile.column_start(),
178            tile.rows(),
179            tile.columns(),
180            tensor_f32_cells(lowered.imaginary())?.as_ref(),
181            &mut imaginary,
182        )?;
183    }
184
185    let estimate = plan.max_phase_estimate();
186    let phase_budget = plan.phase_budget();
187    let evidence = DenseExecutionEvidence {
188        executor: plan.executor_card().symbol.clone(),
189        tiles: tiles.len(),
190        max_segments_per_tensor,
191        submissions,
192        phase_limit_rad: phase_budget.max_abs_residual_phase_rad,
193        predicted_max_abs_psi_rad: plan.tile_plan().conservative_max_abs_phase_rad(),
194        observed_max_abs_psi_rad: estimate.max_abs_residual_phase_rad,
195        predicted_geometry_error_rad: estimate.max_predicted_geometry_error_rad,
196        predicted_roundoff_error_rad: estimate.max_predicted_roundoff_error_rad,
197    };
198    Ok(DenseF32Field {
199        rows: plane.rows(),
200        columns: plane.columns(),
201        real,
202        imaginary,
203        evidence,
204    })
205}
206
207fn zeroed_component(name: &'static str, cells: usize) -> Result<Vec<f32>, LoweringError> {
208    let mut values = Vec::new();
209    values.try_reserve_exact(cells).map_err(|_| {
210        LoweringError::new(
211            PreflightCheck::ResultAllocation,
212            format!("cannot reserve {cells} dense {name} cells"),
213        )
214    })?;
215    values.resize(cells, 0.0);
216    Ok(values)
217}
218
219#[allow(clippy::too_many_arguments)]
220fn place_component(
221    name: &'static str,
222    plane: SamplingPlane,
223    row_start: usize,
224    column_start: usize,
225    rows: usize,
226    columns: usize,
227    source: &[f32],
228    destination: &mut [f32],
229) -> Result<(), LoweringError> {
230    let expected = rows.checked_mul(columns).ok_or_else(|| {
231        LoweringError::new(
232            PreflightCheck::Shape,
233            format!("{name} tile shape overflowed usize"),
234        )
235    })?;
236    if source.len() != expected {
237        return Err(LoweringError::new(
238            PreflightCheck::Execution,
239            format!(
240                "{name} tile has {} cells for shape [{rows}, {columns}]",
241                source.len()
242            ),
243        ));
244    }
245    for (local_row, row_cells) in source.chunks_exact(columns).enumerate() {
246        let global_row = row_start + local_row;
247        let start = global_row
248            .checked_mul(plane.columns())
249            .and_then(|offset| offset.checked_add(column_start))
250            .ok_or_else(|| {
251                LoweringError::new(
252                    PreflightCheck::Shape,
253                    format!("{name} tile destination offset overflowed usize"),
254                )
255            })?;
256        let end = start.checked_add(columns).ok_or_else(|| {
257            LoweringError::new(
258                PreflightCheck::Shape,
259                format!("{name} tile destination end overflowed usize"),
260            )
261        })?;
262        let destination_row = destination.get_mut(start..end).ok_or_else(|| {
263            LoweringError::new(
264                PreflightCheck::Shape,
265                format!("{name} tile lies outside the admitted plane"),
266            )
267        })?;
268        destination_row.copy_from_slice(row_cells);
269    }
270    Ok(())
271}
272
273fn tensor_f32_cells(tensor: &Tensor) -> Result<Arc<[f32]>, LoweringError> {
274    if tensor.dtype() != &domains::f32() {
275        return Err(LoweringError::new(
276            PreflightCheck::Execution,
277            format!(
278                "dense component dtype is {}, expected numbers/f32",
279                tensor.dtype()
280            ),
281        ));
282    }
283    let storage = tensor.materialize().map_err(|error| {
284        LoweringError::new(
285            PreflightCheck::Execution,
286            format!("cannot materialize dense component: {error}"),
287        )
288    })?;
289    let cells = if let Some(typed) = storage.as_any().downcast_ref::<TypedTensorStorage<f32>>() {
290        typed.cells()
291    } else {
292        (0..storage.len())
293            .map(|index| {
294                let value = storage.cell(index).map_err(|error| {
295                    LoweringError::new(
296                        PreflightCheck::Execution,
297                        format!("cannot observe dense component cell {index}: {error}"),
298                    )
299                })?;
300                parse_f32_literal_cell(&value).ok_or_else(|| {
301                    LoweringError::new(
302                        PreflightCheck::Execution,
303                        format!("dense component cell {index} is not a canonical f32"),
304                    )
305                })
306            })
307            .collect::<Result<Vec<_>, _>>()?
308            .into()
309    };
310    if let Some((index, value)) = cells
311        .iter()
312        .copied()
313        .enumerate()
314        .find(|(_, value)| !value.is_finite())
315    {
316        return Err(LoweringError::new(
317            PreflightCheck::Execution,
318            format!("dense component cell {index} is non-finite: {value}"),
319        ));
320    }
321    Ok(cells)
322}