Skip to main content

eredu_runtime/
backend.rs

1//! Narrow capability contracts implemented by execution backends.
2
3use eredu_checkpoint::{
4    recipe::DerivedWeightRecipe,
5    store::{CheckpointLease, CheckpointSource},
6};
7use eredu_core::{BoundedCompletion, Completion, Submission};
8use eredu_nn::NeuralBackend;
9
10use crate::CommunicationPeerCounts;
11
12/// Submits backend-native work and retains values through exact completion.
13pub trait SubmissionBackend: NeuralBackend {
14    /// Backend executor, queue, stream, or equivalent submission context.
15    type Executor: ?Sized;
16    /// Owned executor used for an independently schedulable graph lane.
17    type OwnedExecutor: std::borrow::Borrow<Self::Executor>;
18    /// Exact completion object for one submission.
19    type Completion: Completion;
20
21    /// Creates independently schedulable executors on the same backend device.
22    fn fork_executors(
23        executor: &Self::Executor,
24        count: usize,
25    ) -> Result<Vec<Self::OwnedExecutor>, <Self::Completion as Completion>::Error>;
26
27    /// Submits evaluation of backend-native values on one executor.
28    fn submit<'a, I>(
29        executor: &Self::Executor,
30        values: I,
31    ) -> Result<Self::Completion, <Self::Completion as Completion>::Error>
32    where
33        Self::Tensor: 'a,
34        I: IntoIterator<Item = &'a Self::Tensor>;
35
36    /// Orders future work on `executor` after an exact producer completion.
37    fn order_after(
38        completion: &Self::Completion,
39        executor: &Self::Executor,
40    ) -> Result<(), <Self::Completion as Completion>::Error>;
41
42    /// Retains an owned value until `completion` has completed exactly.
43    fn retain_until_complete<T: Send + 'static>(
44        executor: &Self::Executor,
45        completion: &Self::Completion,
46        value: T,
47    ) -> Result<(), <Self::Completion as Completion>::Error>;
48}
49
50/// Materializes and binds checkpoint data to backend-native parameter slots.
51pub trait ParameterBackend: NeuralBackend {
52    /// One backend-native parameter slot.
53    type Parameter: 'static;
54    /// Materialized backend-native checkpoint weight.
55    type MaterializedWeight;
56    /// Backend context used only while realizing checkpoint parameters.
57    type MaterializationContext: ?Sized;
58    /// In-flight guard retaining encoded sources through exact realization completion.
59    type Materialization;
60    /// Backend-specific loading failure.
61    type ParameterError: std::error::Error + Send + Sync + 'static;
62
63    /// Validates that one neutral recipe can be represented by this backend.
64    ///
65    /// This is a metadata-only selection gate. Implementations must not acquire
66    /// payload leases, allocate native tensors, or submit backend work.
67    fn preflight_recipe(
68        recipe: &DerivedWeightRecipe,
69        source: &dyn CheckpointSource,
70    ) -> Result<(), Self::ParameterError>;
71
72    /// Lowers one format-preserving encoded lease into a native weight.
73    fn materialize(
74        lease: CheckpointLease,
75        context: &Self::MaterializationContext,
76    ) -> Result<Self::Materialization, Self::ParameterError>;
77
78    /// Lowers a validated neutral recipe directly into a native weight.
79    fn materialize_recipe(
80        recipe: &DerivedWeightRecipe,
81        source: &dyn CheckpointSource,
82        context: &Self::MaterializationContext,
83    ) -> Result<Self::Materialization, Self::ParameterError>;
84
85    /// Borrows the native weight retained by an in-flight materialization.
86    fn materialized_weight(materialization: &Self::Materialization) -> &Self::MaterializedWeight;
87
88    /// Waits for this exact realization and releases its encoded source lease.
89    fn finish_materialization(
90        materialization: Self::Materialization,
91    ) -> Result<Self::MaterializedWeight, Self::ParameterError>;
92
93    /// Creates another native handle to identical materialized storage without
94    /// rereading or rematerializing checkpoint data.
95    fn share_materialized_weight(
96        weight: &Self::MaterializedWeight,
97    ) -> Result<Self::MaterializedWeight, Self::ParameterError>;
98
99    /// Validates destination shape/storage compatibility without publication.
100    fn validate_bind(
101        parameter: &Self::Parameter,
102        weight: &Self::MaterializedWeight,
103    ) -> Result<(), Self::ParameterError>;
104
105    /// Binds one materialized weight to its destination parameter.
106    ///
107    /// This operation is infallible so orchestration can validate an entire
108    /// atomic unit before publishing any destination.
109    fn bind(parameter: &mut Self::Parameter, weight: Self::MaterializedWeight);
110}
111
112/// Promotes and demotes backend-native storage without changing its semantics.
113pub trait TransferBackend: SubmissionBackend + ParameterBackend {
114    /// Backend-owned host representation.
115    type HostBuffer;
116    /// In-flight transfer guard retaining all source and destination storage.
117    type Transfer: Completion<Error = Self::TransferError>;
118    /// Backend-specific transfer failure.
119    type TransferError: std::error::Error + Send + Sync + 'static;
120
121    /// Promotes host storage into a materialized execution weight.
122    fn promote(
123        executor: &Self::Executor,
124        host: &Self::HostBuffer,
125    ) -> Result<(Self::MaterializedWeight, Self::Transfer), Self::TransferError>;
126
127    /// Demotes a materialized execution weight into backend-owned host storage.
128    fn demote(
129        executor: &Self::Executor,
130        weight: &Self::MaterializedWeight,
131    ) -> Result<(Self::HostBuffer, Self::Transfer), Self::TransferError>;
132}
133
134/// Collective operations available to distributed runtime policies.
135pub trait CollectiveBackend: SubmissionBackend {
136    /// Backend-native collective group.
137    type Group: ?Sized;
138    /// Backend-specific collective failure.
139    type CollectiveError: std::error::Error + Send + Sync + 'static;
140
141    /// Reduces a tensor across the selected group.
142    fn all_reduce(
143        value: Self::Tensor,
144        group: &Self::Group,
145        executor: &Self::Executor,
146    ) -> Result<Self::Tensor, Self::CollectiveError>;
147
148    /// Gathers a tensor across the selected group.
149    fn all_gather(
150        value: Self::Tensor,
151        group: &Self::Group,
152        executor: &Self::Executor,
153    ) -> Result<Self::Tensor, Self::CollectiveError>;
154
155    /// Exchanges tensor partitions across the selected group.
156    fn all_to_all(
157        value: Self::Tensor,
158        group: &Self::Group,
159        executor: &Self::Executor,
160    ) -> Result<Self::Tensor, Self::CollectiveError>;
161}
162
163/// Common opaque handles and exact completion used by communication extensions.
164///
165/// This trait deliberately declares no operation. Backends implement only the
166/// fine-grained operation traits selected for a concrete architecture.
167pub trait CommunicationBackend: SubmissionBackend {
168    /// Backend-native realization of one opaque communication group.
169    type CommunicationGroup: ?Sized;
170    /// Backend-native realization of one opaque directed route.
171    type CommunicationRoute: ?Sized;
172    /// Exact completion retaining tensors, buffers, groups, routes, and streams.
173    type CommunicationCompletion: Completion<Error = Self::CommunicationError>
174        + BoundedCompletion<Error = Self::CommunicationError>;
175    /// Stable mechanism failure with no architecture-family policy.
176    type CommunicationError: std::error::Error + Send + Sync + 'static;
177
178    /// Submits evaluation of rank-local tensor dependencies before a
179    /// communication-readiness agreement.
180    ///
181    /// The returned communication completion must retain every submitted
182    /// tensor and native execution resource through exact completion or safe
183    /// cancellation teardown. This operation does not select or infer a
184    /// collective group.
185    fn submit_local_dependencies<'a, I>(
186        values: I,
187        executor: &Self::Executor,
188    ) -> Result<Submission<(), Self::CommunicationCompletion>, Self::CommunicationError>
189    where
190        Self::Tensor: 'a,
191        I: IntoIterator<Item = &'a Self::Tensor>;
192}
193
194/// Sum reduction on an opaque communication group.
195pub trait SumReductionBackend: CommunicationBackend {
196    /// Submits one elementwise sum and returns its exact completion.
197    fn all_reduce_sum(
198        value: Self::Tensor,
199        group: &Self::CommunicationGroup,
200        executor: &Self::Executor,
201    ) -> Result<Submission<Self::Tensor, Self::CommunicationCompletion>, Self::CommunicationError>;
202}
203
204/// Equal-size gathering on an opaque communication group.
205pub trait EvenGatherBackend: CommunicationBackend {
206    /// Gathers equal-sized values and concatenates in member order on `axis`.
207    fn all_gather_even(
208        value: Self::Tensor,
209        axis: usize,
210        group: &Self::CommunicationGroup,
211        executor: &Self::Executor,
212    ) -> Result<Submission<Self::Tensor, Self::CommunicationCompletion>, Self::CommunicationError>;
213}
214
215/// Unequal-size gathering on an opaque communication group.
216pub trait UnevenGatherBackend: CommunicationBackend {
217    /// Gathers values and concatenates in member order using exact element counts.
218    fn all_gather_uneven(
219        value: Self::Tensor,
220        counts: &[usize],
221        axis: usize,
222        group: &Self::CommunicationGroup,
223        executor: &Self::Executor,
224    ) -> Result<Submission<Self::Tensor, Self::CommunicationCompletion>, Self::CommunicationError>;
225}
226
227/// Variable-count exchange on an opaque communication group.
228pub trait VariableAllToAllBackend: CommunicationBackend {
229    /// Exchanges exact per-peer partitions on `axis` and returns exact completion.
230    fn variable_all_to_all(
231        value: Self::Tensor,
232        counts: &CommunicationPeerCounts,
233        axis: usize,
234        group: &Self::CommunicationGroup,
235        executor: &Self::Executor,
236    ) -> Result<Submission<Self::Tensor, Self::CommunicationCompletion>, Self::CommunicationError>;
237}
238
239/// Ordered point-to-point boundary transfer on one opaque route.
240pub trait PointToPointBackend: CommunicationBackend {
241    /// Sends or receives the route's exact ordered tensor bundle.
242    #[allow(
243        clippy::type_complexity,
244        reason = "the signature exposes the tensor bundle and exact completion without erasure"
245    )]
246    fn send_receive(
247        values: Vec<RoleExactBoundaryValue<Self::Tensor>>,
248        route: &Self::CommunicationRoute,
249        executor: &Self::Executor,
250    ) -> Result<
251        Submission<Vec<Self::Tensor>, Self::CommunicationCompletion>,
252        Self::CommunicationError,
253    >;
254}
255
256/// One logical boundary tensor coupled to the exact in-band header that must
257/// be transmitted with its payload.
258///
259/// A point-to-point implementation must place `header` and the byte
260/// representation of `tensor` in the same native message. On receive it must
261/// compare the bytes actually received with `header` before its completion can
262/// report success. Returning a backend-synthesized tag does not satisfy this
263/// contract.
264#[derive(Debug, Clone, Eq, PartialEq)]
265pub struct RoleExactBoundaryValue<T> {
266    header: Vec<u8>,
267    tensor: T,
268}
269
270impl<T> RoleExactBoundaryValue<T> {
271    pub(crate) fn new(header: Vec<u8>, tensor: T) -> Self {
272        Self { header, tensor }
273    }
274
275    /// Exact expected in-band header bytes.
276    pub fn header(&self) -> &[u8] {
277        &self.header
278    }
279
280    /// Logical tensor payload.
281    pub const fn tensor(&self) -> &T {
282        &self.tensor
283    }
284
285    /// Consumes the framed value into its expected header and payload.
286    pub fn into_parts(self) -> (Vec<u8>, T) {
287        (self.header, self.tensor)
288    }
289}
290
291/// Root-to-group publication on an opaque communication group.
292pub trait BroadcastBackend: CommunicationBackend {
293    /// Broadcasts one tensor from an ordered member index.
294    fn broadcast(
295        value: Self::Tensor,
296        root: usize,
297        group: &Self::CommunicationGroup,
298        executor: &Self::Executor,
299    ) -> Result<Submission<Self::Tensor, Self::CommunicationCompletion>, Self::CommunicationError>;
300}
301
302/// Payload-free agreement on an opaque communication group.
303pub trait BarrierBackend: CommunicationBackend {
304    /// Submits a barrier and returns its exact completion.
305    fn barrier(
306        group: &Self::CommunicationGroup,
307        executor: &Self::Executor,
308    ) -> Result<Self::CommunicationCompletion, Self::CommunicationError>;
309}
310
311/// All-rank success-status agreement on an opaque communication group.
312///
313/// Unlike a barrier, this operation carries one boolean status from every
314/// member and returns `true` only when every submitted status was `true`.
315pub trait FailureAgreementBackend: CommunicationBackend {
316    /// Backend-owned result whose host boolean becomes authoritative only after
317    /// exact communication completion.
318    type FailureAgreementOutput;
319
320    /// Submits one local phase status without reading the lazy result eagerly.
321    fn agree_success(
322        local_success: bool,
323        group: &Self::CommunicationGroup,
324        executor: &Self::Executor,
325    ) -> Result<
326        Submission<Self::FailureAgreementOutput, Self::CommunicationCompletion>,
327        Self::CommunicationError,
328    >;
329
330    /// Resolves the completed backend result without starting new native work.
331    fn resolve_failure_agreement(
332        output: Self::FailureAgreementOutput,
333    ) -> Result<bool, Self::CommunicationError>;
334}