Skip to main content

mesh_sieve/accelerator/
plan.rs

1//! Frozen topology plans.
2
3use std::collections::HashMap;
4
5use crate::topology::bounds::PayloadLike;
6use crate::topology::point::PointId;
7use crate::topology::sieve::FrozenSieveCsr;
8
9use super::{AcceleratorBackend, AcceleratorError, DeviceValue};
10
11/// Versions/epochs captured when an execution plan is compiled.
12#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
13pub struct PlanEpochs {
14    /// Version of the mutable topology from which the frozen CSR was made.
15    pub topology: u64,
16    /// Version of an associated field atlas, when applicable.
17    pub atlas: u64,
18    /// Epoch incremented by the caller when coordinates/geometry change.
19    pub geometry: u64,
20}
21
22impl PlanEpochs {
23    /// Reject use after topology, atlas, or geometry changes.
24    pub fn validate(self, current: Self) -> Result<(), AcceleratorError> {
25        if self.topology != current.topology {
26            return Err(AcceleratorError::StaleTopologyPlan {
27                expected: self.topology,
28                found: current.topology,
29            });
30        }
31        if self.atlas != current.atlas {
32            return Err(AcceleratorError::StaleAtlasPlan {
33                expected: self.atlas,
34                found: current.atlas,
35            });
36        }
37        if self.geometry != current.geometry {
38            return Err(AcceleratorError::StaleGeometryPlan {
39                expected: self.geometry,
40                found: current.geometry,
41            });
42        }
43        Ok(())
44    }
45}
46
47/// Device-resident dense CSR topology.
48pub struct DeviceTopology<B: AcceleratorBackend> {
49    /// Version of the source topology.
50    pub topology_version: u64,
51    /// Dense-index to stable point identifier.
52    pub point_ids: B::Buffer<u64>,
53    /// Cone CSR row offsets.
54    pub cone_offsets: B::Buffer<u32>,
55    /// Cone dense point indices.
56    pub cone_points: B::Buffer<u32>,
57    /// Support CSR row offsets.
58    pub support_offsets: B::Buffer<u32>,
59    /// Support dense point indices.
60    pub support_points: B::Buffer<u32>,
61    /// Number of points in the dense chart.
62    pub point_count: usize,
63    /// Number of directed cone incidences.
64    pub incidence_count: usize,
65}
66
67/// A device topology plus the host-only stable-ID lookup needed to compile
68/// operation-specific plans.
69pub struct DeviceMeshPlan<B: AcceleratorBackend> {
70    /// Device CSR arrays.
71    pub topology: DeviceTopology<B>,
72    /// Stable point ID to dense device index. This map is never uploaded.
73    pub index_of: HashMap<PointId, u32>,
74}
75
76impl<B: AcceleratorBackend> DeviceMeshPlan<B> {
77    /// Compile an immutable CSR topology into device arrays.
78    pub fn compile<T: PayloadLike>(
79        backend: &B,
80        frozen: &FrozenSieveCsr<PointId, T>,
81        topology_version: u64,
82    ) -> Result<Self, AcceleratorError> {
83        checked_u32(frozen.point_of.len(), "point count")?;
84        checked_u32(frozen.out_dsts.len(), "cone incidence count")?;
85        checked_u32(frozen.in_srcs.len(), "support incidence count")?;
86        let point_ids: Vec<u64> = frozen.point_of.iter().map(PointId::get).collect();
87        let upload = |result: Result<B::Buffer<u32>, B::Error>| {
88            result.map_err(|e| AcceleratorError::DeviceTransferFailed(e.to_string()))
89        };
90        let point_ids = backend
91            .upload(&point_ids)
92            .map_err(|e| AcceleratorError::DeviceTransferFailed(e.to_string()))?;
93        let cone_offsets = upload(backend.upload(&frozen.out_offsets))?;
94        let cone_points = upload(backend.upload(&frozen.out_dsts))?;
95        let support_offsets = upload(backend.upload(&frozen.in_offsets))?;
96        let support_points = upload(backend.upload(&frozen.in_srcs))?;
97        Ok(Self {
98            topology: DeviceTopology {
99                topology_version,
100                point_ids,
101                cone_offsets,
102                cone_points,
103                support_offsets,
104                support_points,
105                point_count: frozen.point_of.len(),
106                incidence_count: frozen.out_dsts.len(),
107            },
108            index_of: frozen.index_of.clone(),
109        })
110    }
111
112    /// Ensure this plan still corresponds to the current topology version.
113    pub fn validate_topology(&self, current: u64) -> Result<(), AcceleratorError> {
114        if self.topology.topology_version == current {
115            Ok(())
116        } else {
117            Err(AcceleratorError::StaleTopologyPlan {
118                expected: self.topology.topology_version,
119                found: current,
120            })
121        }
122    }
123}
124
125pub(crate) fn checked_u32(value: usize, what: &'static str) -> Result<u32, AcceleratorError> {
126    u32::try_from(value).map_err(|_| AcceleratorError::IndexOverflow { what, value })
127}
128
129pub(crate) fn upload<T: DeviceValue, B: AcceleratorBackend>(
130    backend: &B,
131    values: &[T],
132) -> Result<B::Buffer<T>, AcceleratorError> {
133    backend
134        .upload(values)
135        .map_err(|e| AcceleratorError::DeviceTransferFailed(e.to_string()))
136}