aisimulate_core/engine/generalized/contracts.rs
1// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::num::NonZeroU32;
5
6use anyhow::Result;
7
8/// Stable identity of one logical mock engine.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub struct EngineIdentity {
11 /// Stable logical worker ID.
12 pub worker_id: u64,
13}
14
15impl EngineIdentity {
16 /// Construct an engine identity from its stable worker ID.
17 pub const fn new(worker_id: u64) -> Self {
18 Self { worker_id }
19 }
20
21 /// Return the identity of one attention-DP rank.
22 pub const fn rank(self, dp_rank: u32, dp_size: NonZeroU32) -> RankIdentity {
23 RankIdentity {
24 worker_id: self.worker_id,
25 dp_rank,
26 dp_size,
27 }
28 }
29}
30
31/// Stable identity of one scheduler core within a logical engine.
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
33pub struct RankIdentity {
34 /// Stable logical worker ID shared by sibling ranks.
35 pub worker_id: u64,
36 /// Zero-based attention-DP rank.
37 pub dp_rank: u32,
38 /// Total ranks in this logical engine's attention-DP group.
39 pub dp_size: NonZeroU32,
40}
41
42/// Construction parameters for a generalized engine.
43#[derive(Debug, Clone, PartialEq, Eq)]
44pub struct GeneralizedEngineConfig<C> {
45 /// Number of scheduler cores in the attention-DP group.
46 pub dp_size: NonZeroU32,
47 /// Configuration cloned into each rank core.
48 pub rank: C,
49}
50
51impl<C> GeneralizedEngineConfig<C> {
52 /// Construct a single-rank engine configuration.
53 pub const fn single_rank(rank: C) -> Self {
54 Self {
55 dp_size: NonZeroU32::MIN,
56 rank,
57 }
58 }
59
60 /// Construct an attention-DP engine configuration.
61 pub const fn attention_dp(dp_size: NonZeroU32, rank: C) -> Self {
62 Self { dp_size, rank }
63 }
64}
65
66/// Context supplied while a rank applies a scheduler command.
67#[derive(Debug, Clone, Copy, PartialEq)]
68pub struct CommandContext {
69 /// Driver clock at which the command becomes visible.
70 pub now_ms: f64,
71 /// Whether this logical engine has a committed pass awaiting completion.
72 ///
73 /// This is group-wide. It is `true` for an otherwise idle sibling rank
74 /// while another rank is executing, preserving the attention-DP barrier.
75 pub pass_in_flight: bool,
76}
77
78impl CommandContext {
79 /// Whether a command may immediately admit work into the current pass.
80 pub const fn allow_immediate_admission(self) -> bool {
81 !self.pass_in_flight
82 }
83}
84
85/// A command addressed to one attention-DP rank.
86#[derive(Debug, Clone, PartialEq, Eq)]
87pub struct SchedulerCommand<C> {
88 /// Target attention-DP rank.
89 pub dp_rank: u32,
90 /// Rank-engine-specific, runtime-neutral command payload.
91 pub command: C,
92}
93
94impl<C> SchedulerCommand<C> {
95 /// Address a command to a rank.
96 pub const fn new(dp_rank: u32, command: C) -> Self {
97 Self { dp_rank, command }
98 }
99}
100
101/// Effects produced by one rank.
102#[derive(Debug, Clone, PartialEq, Eq)]
103pub struct RankEffects<T> {
104 /// Rank that produced the effects.
105 pub dp_rank: u32,
106 /// Rank-engine-specific effects.
107 pub effects: T,
108}
109
110/// Effects produced by zero or more ranks of a logical engine.
111#[derive(Debug, Clone, PartialEq, Eq)]
112pub struct EngineEffects<T> {
113 /// Effects in stable DP-rank order.
114 pub by_rank: Vec<RankEffects<T>>,
115}
116
117impl<T> EngineEffects<T> {
118 fn empty() -> Self {
119 Self {
120 by_rank: Vec::new(),
121 }
122 }
123
124 pub(crate) fn one(dp_rank: u32, effects: T) -> Self {
125 Self {
126 by_rank: vec![RankEffects { dp_rank, effects }],
127 }
128 }
129
130 /// Return `true` when no rank produced effects.
131 pub fn is_empty(&self) -> bool {
132 self.by_rank.is_empty()
133 }
134
135 /// Consume the wrapper and return effects in stable DP-rank order.
136 pub fn into_by_rank(self) -> Vec<RankEffects<T>> {
137 self.by_rank
138 }
139}
140
141impl<T> Default for EngineEffects<T> {
142 fn default() -> Self {
143 Self::empty()
144 }
145}
146
147/// Opaque ID of a committed logical-engine pass.
148#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
149pub struct PassId(pub(crate) u64);
150
151impl PassId {
152 /// Return the monotonically increasing per-engine sequence number.
153 pub const fn get(self) -> u64 {
154 self.0
155 }
156}
157
158/// Rank-local status for a bounded same-timestamp scheduler retry.
159///
160/// This is a driver hint, not an observable effect or ordinary progress.
161/// Drivers evaluate it only after an effect-free, zero-duration pass.
162#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
163pub enum SameTimestampRetry {
164 /// This rank has no internal same-timestamp convergence protocol.
165 #[default]
166 NotApplicable,
167 /// Internal scheduler state changed and another pass may expose work.
168 Retry,
169 /// The rank's internal state no longer changes at this timestamp.
170 Exhausted,
171}
172
173/// Eager execution result returned by a rank core.
174///
175/// `start_effects` may become visible immediately. `pending` is retained by
176/// the generalized engine and handed back to the rank only at the shared
177/// completion boundary.
178#[derive(Debug)]
179pub struct RankPass<S, P> {
180 /// Modeled completion time for this rank.
181 pub end_ms: f64,
182 /// Rank-local bounded retry status.
183 pub same_timestamp_retry: SameTimestampRetry,
184 /// Effects visible at pass start.
185 pub start_effects: S,
186 /// Rank-private state needed to finish the pass.
187 pub pending: P,
188}
189
190/// Pass-start effects from one rank.
191#[derive(Debug, Clone, PartialEq)]
192pub struct RankPassStarted<T> {
193 /// Rank that executed work.
194 pub dp_rank: u32,
195 /// This rank's modeled completion time before group alignment.
196 pub rank_end_ms: f64,
197 /// Effects visible at pass start.
198 pub effects: T,
199}
200
201/// A logical pass committed by
202/// [`GeneralizedMockerEngine::execute_pass`](super::GeneralizedMockerEngine::execute_pass).
203#[derive(Debug, Clone, PartialEq)]
204pub struct EnginePassStarted<T> {
205 /// ID supplied later to
206 /// [`GeneralizedMockerEngine::complete_pass`](super::GeneralizedMockerEngine::complete_pass).
207 pub pass_id: PassId,
208 /// Driver time at which the pass was committed.
209 pub started_at_ms: f64,
210 /// Shared completion boundary, equal to the slowest executed rank.
211 pub end_ms: f64,
212 /// Total sibling ranks held by the barrier, including idle ranks.
213 pub participating_ranks: NonZeroU32,
214 /// Grouped retry status at [`Self::started_at_ms`]. A retry request from
215 /// any executed rank takes precedence over an exhausted sibling.
216 pub same_timestamp_retry: SameTimestampRetry,
217 /// Start effects from ranks that had work, in stable rank order.
218 pub by_rank: Vec<RankPassStarted<T>>,
219}
220
221/// A logical pass released at its shared completion boundary.
222#[derive(Debug, Clone, PartialEq)]
223pub struct EnginePassCompleted<T> {
224 /// ID of the completed pass.
225 pub pass_id: PassId,
226 /// Completion effects from executed ranks and idle siblings that released
227 /// deferred work at the shared group boundary.
228 pub effects: EngineEffects<T>,
229}
230
231/// One scheduler/KV/timing core.
232///
233/// Implementations own all single-rank scheduler state. The generalized layer
234/// owns attention-DP grouping and never inspects the command/effect payloads.
235///
236/// `execute_pass` eagerly commits a non-preemptive batch. Implementations must
237/// not expose pass-end effects until `complete_pass` receives the retained
238/// `PendingPass`.
239pub trait RankEngine: Sized {
240 /// Rank construction configuration.
241 type Config;
242 /// Scheduler command payload.
243 type Command;
244 /// Effects of applying one command.
245 type CommandEffects;
246 /// Effects visible when a pass starts.
247 type PassStartEffects;
248 /// Opaque state retained between pass start and completion.
249 type PendingPass;
250 /// Effects visible when a pass completes.
251 type PassCompletionEffects;
252 /// Effects produced by deadline-driven internal work.
253 type InternalEffects;
254
255 /// Construct one rank core.
256 fn new(identity: RankIdentity, config: &Self::Config) -> Result<Self>;
257
258 /// Apply one scheduler command.
259 ///
260 /// `pending_pass` is the eagerly committed pass for this rank, when this
261 /// rank participated in the logical engine's current in-flight pass.
262 /// Commands such as cancellation may mutate it to suppress effects that
263 /// were computed at pass start but must no longer become visible at pass
264 /// completion. A logical attention-DP pass can be in flight while this is
265 /// `None` when only sibling ranks participated; use
266 /// [`CommandContext::pass_in_flight`] for the group-wide state.
267 ///
268 /// This operation must be error-atomic: returning `Err` must leave both
269 /// the rank and `pending_pass` unchanged. Command errors are recoverable
270 /// at the generalized boundary because a command targets only one rank;
271 /// use a successful command effect to represent any committed mutation.
272 fn apply_command_effects(
273 &mut self,
274 command: Self::Command,
275 context: CommandContext,
276 pending_pass: Option<&mut Self::PendingPass>,
277 ) -> Result<Self::CommandEffects>;
278
279 /// Whether this rank can commit a pass.
280 fn is_ready(&self) -> bool;
281
282 /// Whether a ready rank is blocked only on externally commanded state.
283 ///
284 /// This is narrower than having queued work. Implementations return true
285 /// only when a later command can release retained ownership that prevents
286 /// the ready work from advancing. Drivers use this signal to avoid
287 /// repeatedly executing an effect-free, zero-duration pass while keeping
288 /// genuine scheduler livelocks visible.
289 fn waiting_for_external_command(&self) -> bool {
290 false
291 }
292
293 /// Eagerly commit one non-preemptive pass.
294 fn execute_pass(
295 &mut self,
296 now_ms: f64,
297 ) -> Result<RankPass<Self::PassStartEffects, Self::PendingPass>>;
298
299 /// Release the effects of a previously committed pass.
300 fn complete_pass(
301 &mut self,
302 pending: Self::PendingPass,
303 end_ms: f64,
304 ) -> Result<Self::PassCompletionEffects>;
305
306 /// Cross the shared completion boundary without a rank-local pass.
307 ///
308 /// Attention-DP ranks that had no work when a sibling pass started still
309 /// participate in the group barrier. Implementations may use this hook to
310 /// release effects deferred while [`CommandContext::pass_in_flight`] was
311 /// true. `end_ms` is the modeled shared group boundary (the maximum rank
312 /// end), even when a wall-clock driver calls `complete_pass` later.
313 /// Returning `None` means the idle rank has no boundary effects.
314 fn complete_idle_group_pass(
315 &mut self,
316 _started_at_ms: f64,
317 _end_ms: f64,
318 ) -> Result<Option<Self::PassCompletionEffects>> {
319 Ok(None)
320 }
321
322 /// Earliest deadline for independently modeled internal work.
323 ///
324 /// The generalized engine masks this deadline while a grouped pass is in
325 /// flight. A physical deadline that falls inside a model step becomes
326 /// scheduler-visible only when that shared pass completes.
327 fn next_internal_deadline_ms(&self) -> Option<f64>;
328
329 /// Process internal work due at `now_ms`.
330 ///
331 /// Callers may invoke this method defensively with `pass_in_flight=true`;
332 /// implementations must return without mutating rank state in that case.
333 fn process_internal_work(
334 &mut self,
335 now_ms: f64,
336 pass_in_flight: bool,
337 ) -> Result<Self::InternalEffects>;
338
339 /// Whether this rank owns no request, pass, or internal work.
340 fn is_drained(&self) -> bool;
341}