eredu_core/discovery.rs
1//! Versioned logical architecture and capture discovery, independent of execution storage.
2//!
3//! Node IDs, canonical parameter group prefixes, and activation paths are separate
4//! namespaces. A node need not have an observation point. `None` means unknown,
5//! never zero; completeness describes omissions at the stated scope.
6
7use serde::{Deserialize, Serialize};
8
9/// Current wire schema for architecture and observation discovery.
10pub const DISCOVERY_SCHEMA_VERSION: u32 = 1;
11
12/// Coverage of a descriptor, node, or catalog.
13#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
14#[serde(tag = "status", content = "reasons", rename_all = "snake_case")]
15pub enum DescriptionCompleteness {
16 /// All details in the documented logical abstraction are represented.
17 Complete,
18 /// Known omissions; represented facts remain authoritative.
19 Partial(Vec<String>),
20 /// No supported description at this scope.
21 Unsupported(Vec<String>),
22}
23
24/// Semantic dimension; symbols refer to the current operation, not a fixed batch.
25#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
26#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
27pub enum SymbolicDimension {
28 /// An exact positive or zero extent.
29 Known(usize),
30 /// Batch size of the current operation.
31 Batch,
32 /// Sequence length of this operation, usually one for decode.
33 Sequence,
34 /// Flattened batch times sequence rows, used by expert routing tensors.
35 TokenRows,
36 /// Visible prefix length including cached positions.
37 Context,
38 /// Input-dependent media feature positions.
39 MediaPositions,
40 /// Extent is not described.
41 Unknown,
42}
43
44/// Named tensor axis in storage order.
45#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
46pub struct TensorAxis {
47 /// Semantic axis name; independent of checkpoint field spelling.
48 pub name: String,
49 /// Known extent or operation-dependent symbolic dimension.
50 pub dimension: SymbolicDimension,
51}
52
53/// Operation class, interpreted without matching a model-family name.
54#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
55#[serde(rename_all = "snake_case")]
56#[non_exhaustive]
57pub enum ArchitectureNodeKind {
58 /// Token or codebook embedding lookup.
59 Embedding,
60 /// One architecture-defined decoder execution unit.
61 DecoderBlock,
62 /// Normalization of activations.
63 Normalization,
64 /// An attention operation.
65 Attention,
66 /// A stateful token mixer.
67 Mixer,
68 /// Dense feed-forward computation.
69 FeedForward,
70 /// Routed and optional shared expert computation.
71 MixtureOfExperts,
72 /// Expert selection and coefficient computation.
73 Router,
74 /// Bank of selected experts.
75 RoutedExperts,
76 /// Always-on expert branch.
77 SharedExperts,
78 /// Addition of a sublayer contribution and its bypass input.
79 ResidualAdd,
80 /// Combination of parallel branch contributions.
81 Sum,
82 /// Projection into an output domain such as vocabulary logits.
83 OutputHead,
84 /// Host or tensor input processing.
85 Processor,
86 /// Media feature encoder.
87 Encoder,
88 /// Projection of encoded features into decoder space.
89 Projector,
90 /// Assembly of text and media features.
91 ModalityMerge,
92 /// Checkpoint-embedded prediction component.
93 Prediction,
94 /// Temporal/depth frame execution.
95 Realtime,
96 /// Operation whose internal semantics are not described.
97 Opaque,
98}
99
100/// Query/key-value head sharing, independent of receptive field and mechanism.
101#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
102#[serde(rename_all = "snake_case")]
103pub enum HeadSharing {
104 /// One key/value head per query head.
105 MultiHead,
106 /// A single shared key/value head.
107 MultiQuery,
108 /// Multiple query heads share each of several key/value heads.
109 GroupedQuery,
110}
111
112/// Reach of one attention operation.
113#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
114#[serde(tag = "kind", rename_all = "snake_case")]
115pub enum ReceptiveField {
116 /// The complete causal prefix.
117 Full,
118 /// A bounded trailing window.
119 Sliding {
120 /// Maximum number of visible positions.
121 window: usize,
122 },
123 /// A local neighborhood.
124 Local {
125 /// Neighborhood size, when known.
126 window: Option<usize>,
127 },
128}
129
130/// Attention equation class; recurrent linear attention can use both flags below.
131#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
132#[serde(rename_all = "snake_case")]
133pub enum AttentionMechanism {
134 /// Softmax over all candidate scores.
135 Softmax,
136 /// Linear attention with optional recurrent state.
137 Linear,
138 /// Attention over compressed latent key/value representations.
139 Latent,
140 /// Attention over local and compressed sparse history.
141 CompressedSparse,
142}
143
144/// Positional encoding class. Exact scaling may remain outside this abstraction.
145#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
146#[serde(rename_all = "snake_case")]
147pub enum PositionalEncoding {
148 /// Rotary position encoding.
149 Rotary,
150 /// Relative positional features.
151 Relative,
152 /// Learned positional embeddings.
153 Learned,
154 /// No such transformation is applied.
155 None,
156}
157
158/// Independent optional semantic facts for one layer's attention.
159#[derive(Debug, Clone, Default, Eq, PartialEq, Serialize, Deserialize)]
160pub struct AttentionAttributes {
161 /// Relationship between query heads and key/value heads, when applicable.
162 pub head_sharing: Option<HeadSharing>,
163 /// Number of query heads.
164 pub query_heads: Option<usize>,
165 /// Number of key/value heads.
166 pub key_value_heads: Option<usize>,
167 /// Per-head query/key width.
168 pub key_head_dimension: Option<usize>,
169 /// Per-head value width.
170 pub value_head_dimension: Option<usize>,
171 /// Positions visible to this layer; unknown for mixed compressed-history policies.
172 pub receptive_field: Option<ReceptiveField>,
173 /// Equation class of this operator.
174 pub mechanism: Option<AttentionMechanism>,
175 /// Whether execution maintains recurrent state.
176 pub recurrent: Option<bool>,
177 /// Whether future positions are masked.
178 pub causal: Option<bool>,
179 /// Position encoding used by this layer.
180 pub positional_encoding: Option<PositionalEncoding>,
181}
182
183/// Stateful token-mixing equation class.
184#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
185#[serde(rename_all = "snake_case")]
186pub enum MixerMechanism {
187 /// Gated delta recurrent attention.
188 GatedDelta,
189 /// Selective state-space mixer.
190 SelectiveStateSpace,
191 /// Gated causal short convolution.
192 ShortConvolution,
193}
194
195/// Optional geometry for a stateful token mixer.
196#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
197pub struct MixerAttributes {
198 /// Equation class of this operator.
199 pub mechanism: MixerMechanism,
200 /// Whether execution maintains recurrent state.
201 pub recurrent: bool,
202 /// Causal convolution kernel width, when present.
203 pub convolution_width: Option<usize>,
204}
205
206/// Semantic granularity of expert selection.
207#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
208#[serde(rename_all = "snake_case")]
209pub enum RoutingGranularity {
210 /// One decision per token.
211 Token,
212 /// One decision per sequence.
213 Sequence,
214}
215
216/// Router score transformation, independent of top-k normalization.
217#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
218#[serde(rename_all = "snake_case")]
219pub enum RoutingScoreTransform {
220 /// Softmax over all candidate scores.
221 Softmax,
222 /// Softmax applied only after top-k selection.
223 SelectedSoftmax,
224 /// Independent sigmoid-transformed scores.
225 Sigmoid,
226 /// Square root of softplus-transformed scores.
227 SqrtSoftplus,
228 /// Untransformed scores.
229 Identity,
230}
231
232/// Normalization policy for selected route coefficients.
233#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
234#[serde(rename_all = "snake_case")]
235pub enum RoutingNormalization {
236 /// Selected scores are normalized by their sum.
237 SelectedSum,
238 /// Joint normalization over selected routed experts and always-on experts.
239 SelectedAndSharedSum,
240 /// No such transformation is applied.
241 None,
242}
243
244/// Routed and always-on experts are distinct branches, also linked by graph edges.
245#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
246pub struct MoeAttributes {
247 /// Number of independently routed experts.
248 pub routed_experts: usize,
249 /// Number of experts selected per routing decision.
250 pub selected_experts: usize,
251 /// Number of always-on experts; zero explicitly means none.
252 pub shared_experts: Option<usize>,
253 /// Intermediate width of the shared branch, when known.
254 pub shared_expert_width: Option<usize>,
255 /// Whether a learned gate scales the shared contribution.
256 pub shared_expert_gated: Option<bool>,
257 /// Unit on which expert decisions are made.
258 pub granularity: Option<RoutingGranularity>,
259 /// Transformation of router logits into scores.
260 pub score_transform: Option<RoutingScoreTransform>,
261 /// Normalization applied after selecting experts.
262 pub normalization: Option<RoutingNormalization>,
263}
264
265/// A semantic node; containment is separate from data-flow edges.
266#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
267pub struct ArchitectureNode {
268 /// Stable identity within this descriptor.
269 pub id: String,
270 /// Human-readable operation label.
271 pub label: String,
272 /// Typed semantic category.
273 pub kind: ArchitectureNodeKind,
274 /// Containing node, independent of data-flow dependencies.
275 pub parent: Option<String>,
276 /// Zero-based physical layer ordinal, if this node is a decoder unit.
277 pub layer_index: Option<usize>,
278 /// Canonical parameter-group identities referenced by this node.
279 pub parameter_groups: Vec<String>,
280 /// Exact activation selectors associated with this node.
281 pub observation_paths: Vec<String>,
282 /// Output axes in storage order; absent when rank or shape semantics are unknown.
283 pub output_axes: Option<Vec<TensorAxis>>,
284 /// Independent attention properties for this exact layer.
285 pub attention: Option<AttentionAttributes>,
286 /// Stateful mixer properties, when applicable.
287 pub mixer: Option<MixerAttributes>,
288 /// Expert topology and routing policy, when applicable.
289 pub moe: Option<MoeAttributes>,
290 /// Explicit omissions at this scope.
291 pub completeness: DescriptionCompleteness,
292}
293
294/// Semantic role of a logical graph edge.
295#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
296#[serde(rename_all = "snake_case")]
297pub enum ArchitectureEdgeKind {
298 /// Ordinary activation flow.
299 Data,
300 /// Bypass activation consumed by a residual join.
301 Residual,
302 /// Expert-selection control flow.
303 Routing,
304 /// Persistent state dependency.
305 State,
306}
307
308/// Directed logical data flow, including the bypass input of a residual addition.
309#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
310pub struct ArchitectureEdge {
311 /// Source node identity.
312 pub from: String,
313 /// Destination node identity.
314 pub to: String,
315 /// Typed semantic category.
316 pub kind: ArchitectureEdgeKind,
317}
318
319/// Architecture-declared canonical parameter namespace, not an activation selector.
320/// Physical checkpoint aliases and encoding companions remain in checkpoint schemas.
321#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
322pub struct ArchitectureParameterGroup {
323 /// Stable identity within this descriptor.
324 pub id: String,
325 /// Canonical logical checkpoint module prefix; not a physical source key or activation selector.
326 pub canonical_prefix: String,
327}
328
329/// Backend-independent logical graph from an admitted architecture plan.
330#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
331pub struct ArchitectureDescriptor {
332 /// Version of the discovery wire schema.
333 pub schema_version: u32,
334 /// Logical operations and their containment.
335 pub nodes: Vec<ArchitectureNode>,
336 /// Directed logical data flow.
337 pub edges: Vec<ArchitectureEdge>,
338 /// Canonical parameter groups referenced by nodes in this graph.
339 pub parameter_groups: Vec<ArchitectureParameterGroup>,
340 /// Architecture-declared observation points, independent of execution support.
341 pub observations: ObservationCatalog,
342 /// Explicit omissions at this scope.
343 pub completeness: DescriptionCompleteness,
344}
345
346impl ArchitectureDescriptor {
347 /// Finds a node by its stable descriptor identity.
348 pub fn node(&self, id: &str) -> Option<&ArchitectureNode> {
349 self.nodes.iter().find(|node| node.id == id)
350 }
351}
352
353/// Shared declaration used by execution traversal and catalog generation.
354#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
355#[serde(rename_all = "snake_case")]
356pub enum UnitObservation {
357 /// Input boundary of an execution unit.
358 Input,
359 /// Output boundary of an execution unit.
360 Output,
361}
362
363impl UnitObservation {
364 /// Formats the exact selector used by instrumentation and discovery.
365 pub fn path(self, unit: &str) -> String {
366 format!(
367 "{unit}.{}",
368 match self {
369 Self::Input => "input",
370 Self::Output => "output",
371 }
372 )
373 }
374}
375
376/// Fields of the runtime's normalized routing event, shared with host collectors.
377#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
378#[serde(rename_all = "snake_case")]
379pub enum RoutingObservationField {
380 /// Selected expert identifiers.
381 SelectedExperts,
382 /// Selected scores before top-k renormalization.
383 SelectedScores,
384 /// Final coefficients applied to routed contributions.
385 Coefficients,
386 /// Combined routed-expert contribution.
387 RoutedOutput,
388 /// Rank-local routed contribution.
389 LocalRoutedOutput,
390 /// Routed contribution after collective reduction.
391 ReducedRoutedOutput,
392 /// Shared-expert contribution including any shared gate.
393 SharedOutput,
394 /// Combined routed and shared contribution.
395 CombinedOutput,
396}
397
398impl RoutingObservationField {
399 /// Formats the exact selector used by instrumentation and discovery.
400 pub fn path(self, module: &str) -> String {
401 let field = match self {
402 Self::SelectedExperts => "selected_experts",
403 Self::SelectedScores => "selected_scores",
404 Self::Coefficients => "coefficients",
405 Self::RoutedOutput => "routed_output",
406 Self::LocalRoutedOutput => "local_routed_output",
407 Self::ReducedRoutedOutput => "reduced_routed_output",
408 Self::SharedOutput => "shared_output",
409 Self::CombinedOutput => "combined_output",
410 };
411 format!("{module}.routing.{field}")
412 }
413}
414
415/// Tensor element category before native-to-host conversion.
416#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
417#[serde(rename_all = "snake_case")]
418pub enum ObservationDtype {
419 /// Floating-point tensor; native precision is execution-dependent.
420 Floating,
421 /// Integer tensor; signedness is supplied by the captured host value.
422 Integer,
423 /// Boolean tensor.
424 Boolean,
425 /// Element category is not described.
426 Unknown,
427}
428
429/// Portable value category produced by an advertised observation.
430#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
431#[serde(rename_all = "snake_case")]
432pub enum ObservationValueType {
433 /// A complete tensor, materialized through `TensorObservation`.
434 Tensor,
435}
436
437/// Capture timing relative to an intervention at the same path.
438#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
439#[serde(rename_all = "snake_case")]
440pub enum ObservationPosition {
441 /// Value passed to observe, before replacement at this exact point.
442 BeforeIntervention,
443 /// A read-only event after dispatch; no replacement at this point.
444 ReadOnly,
445 /// Value after replacement at this point.
446 AfterIntervention,
447}
448
449/// Conditional mechanism or input required to emit a point.
450#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
451#[serde(rename_all = "snake_case")]
452pub enum ObservationRequirement {
453 /// Requires activation capture through instrumented execution.
454 ActivationHooks,
455 /// Requires normalized routing-event capture.
456 RoutingEvents,
457 /// Requires the corresponding media input.
458 MediaInput,
459 /// Requires the corresponding prediction group to execute.
460 PredictionExecution,
461}
462
463/// One exact implemented tensor observation, not a hypothetical internal value.
464#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
465pub struct ObservationPoint {
466 /// Exact activation path accepted by ObservationSelector::Exact.
467 pub path: String,
468 /// Associated architecture node identity.
469 pub node_id: String,
470 /// Semantic meaning of the captured value.
471 pub meaning: String,
472 /// Portable value category.
473 pub value_type: ObservationValueType,
474 /// Semantic dtype before backend-specific host conversion.
475 pub dtype: ObservationDtype,
476 /// Tensor axes in storage order, when known.
477 pub axes: Option<Vec<TensorAxis>>,
478 /// Availability during a prefill operation.
479 pub prefill: bool,
480 /// Availability during a cached decode operation.
481 pub decode: bool,
482 /// Conditions in addition to phase availability.
483 pub requirements: Vec<ObservationRequirement>,
484 /// Position relative to an intervention at this exact point.
485 pub position: ObservationPosition,
486 /// Full tensor retained until host materialization; bytes depend on runtime shape/dtype.
487 pub retained_bytes: Option<u64>,
488 /// Host materialization bytes when reliably known; otherwise unknown.
489 pub host_bytes: Option<u64>,
490}
491
492/// Versioned set of implemented architecture-declared capture points.
493#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
494pub struct ObservationCatalog {
495 /// Version of the discovery wire schema.
496 pub schema_version: u32,
497 /// Observation points in deterministic path order.
498 pub points: Vec<ObservationPoint>,
499 /// Explicit omissions at this scope.
500 pub completeness: DescriptionCompleteness,
501}
502
503impl ObservationCatalog {
504 /// Finds an implemented observation by its exact selectable path.
505 pub fn get(&self, path: &str) -> Option<&ObservationPoint> {
506 self.points.iter().find(|point| point.path == path)
507 }
508}
509
510/// Side-effect-free host collector facts. Unknown is the conservative default.
511#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Serialize, Deserialize)]
512pub struct ObservationMechanisms {
513 /// The backend collects ordinary activation tensors.
514 pub activation_tensors: bool,
515 /// The backend collects normalized routing-event tensors.
516 pub routing_tensors: bool,
517 /// Floating observations are converted to portable F32 host values.
518 pub floating_to_f32: bool,
519}
520
521/// Phase-specific support under one selected execution configuration.
522#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
523#[serde(tag = "status", content = "reason", rename_all = "snake_case")]
524pub enum ObservationSupportStatus {
525 /// The selected execution emits this point in this phase.
526 Supported,
527 /// Supported if the stated input or execution condition holds.
528 Conditional(String),
529 /// The selected execution cannot emit this observation.
530 Unsupported(String),
531 /// Available facts do not prove capture support.
532 Unverified(String),
533}
534
535/// Support for one architecture-declared path under an exact selected execution.
536#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
537pub struct ObservationSupport {
538 /// Exact activation path accepted by ObservationSelector::Exact.
539 pub path: String,
540 /// Availability during a prefill operation.
541 pub prefill: ObservationSupportStatus,
542 /// Availability during a cached decode operation.
543 pub decode: ObservationSupportStatus,
544 /// Host conversion fact; integer signedness is reported by the captured value.
545 pub floating_to_f32: bool,
546}
547
548/// Versioned per-path support facts, separate from the logical architecture.
549#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
550pub struct ObservationSupportReport {
551 /// Version of the discovery wire schema.
552 pub schema_version: u32,
553 /// Observation points in deterministic path order.
554 pub points: Vec<ObservationSupport>,
555 /// Bounded transformation mechanisms and their execution/storage conditions.
556 #[serde(default)]
557 pub capture: crate::capture::CaptureCapabilities,
558}