1use sha2::{Digest, Sha256};
2use std::collections::{BTreeMap, BTreeSet};
3
4use super::{
5 invalid_plan, AliasPolicy, BlockedTensorPadding, BufferUsage, CapabilityCatalog, CapabilityId,
6 CompletionRetentionSpec, DimensionConstraint, ElementType, ExecutablePlan, ExecutionPlan,
7 LayoutConstraint, NodeId, OperationPlanningHandle, PlanBuildRequest, PlanNodeResolution,
8 PreparedModelFamily, ProgramTensorSpec, ProgramValueId, ProviderId, ProviderResourcePlan,
9 ResolvedStorageComponent, ResolvedTensorLayout, ResolvedTensorSpec, ResolvedValueBinding,
10 ResolvedValueRole, ResolvedValueStorage, ResolvedWeightBinding, ResourceId, RuntimePolicy,
11 StrideConstraint, TensorContract, TrustedExecutionWeightPlan, VNextError, WeightId,
12 WeightMaterializerId, WeightMaterializerRegistry, WeightMaterializerSelection, WeightSchema,
13 IDENTITY_WEIGHT_MATERIALIZER_ID,
14};
15
16#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct ProgramPlanCompileOptions {
22 tensor_specs: BTreeMap<ProgramValueId, ProgramTensorSpec>,
23 required_capabilities: BTreeMap<NodeId, BTreeSet<CapabilityId>>,
24 preferred_providers: BTreeMap<NodeId, ProviderId>,
25 completion_retention: CompletionRetentionSpec,
26 weight_materializer_selection: WeightMaterializerSelection,
27}
28
29impl ProgramPlanCompileOptions {
30 pub fn new(
31 input_tensor_specs: BTreeMap<ProgramValueId, ProgramTensorSpec>,
32 ) -> Result<Self, VNextError> {
33 for (value_id, tensor) in &input_tensor_specs {
34 tensor.validate(&format!("program input `{value_id}`"))?;
35 }
36 Ok(Self {
37 tensor_specs: input_tensor_specs,
38 required_capabilities: BTreeMap::new(),
39 preferred_providers: BTreeMap::new(),
40 completion_retention: CompletionRetentionSpec::default(),
41 weight_materializer_selection: WeightMaterializerSelection::exact(
42 WeightMaterializerId::new(IDENTITY_WEIGHT_MATERIALIZER_ID)?,
43 ),
44 })
45 }
46
47 pub fn insert_tensor_spec(
50 &mut self,
51 value_id: ProgramValueId,
52 tensor: ProgramTensorSpec,
53 ) -> Result<(), VNextError> {
54 tensor.validate(&format!("program value `{value_id}`"))?;
55 self.tensor_specs.insert(value_id, tensor);
56 Ok(())
57 }
58
59 pub fn require_capability(&mut self, node_id: NodeId, capability: CapabilityId) {
60 self.required_capabilities
61 .entry(node_id)
62 .or_default()
63 .insert(capability);
64 }
65
66 pub fn prefer_provider(&mut self, node_id: NodeId, provider_id: ProviderId) {
67 self.preferred_providers.insert(node_id, provider_id);
68 }
69
70 pub fn retain_completion_value(&mut self, value_id: ProgramValueId) -> bool {
73 self.completion_retention.insert(value_id)
74 }
75
76 pub fn retain_all_outputs_for_determinism(
79 &mut self,
80 family: &PreparedModelFamily,
81 ) -> Result<(), VNextError> {
82 self.completion_retention = CompletionRetentionSpec::for_determinism_outputs(family)?;
83 Ok(())
84 }
85
86 pub fn completion_retention(&self) -> &CompletionRetentionSpec {
87 &self.completion_retention
88 }
89
90 pub fn tensor_specs(&self) -> &BTreeMap<ProgramValueId, ProgramTensorSpec> {
91 &self.tensor_specs
92 }
93
94 pub fn require_weight_materializer(&mut self, materializer_id: WeightMaterializerId) {
95 self.weight_materializer_selection = WeightMaterializerSelection::exact(materializer_id);
96 }
97
98 pub fn require_weight_materializer_selection(
99 &mut self,
100 selection: WeightMaterializerSelection,
101 ) {
102 self.weight_materializer_selection = selection;
103 }
104
105 pub fn require_weight_materializer_with_numeric_quality_artifact(
106 &mut self,
107 materializer_id: WeightMaterializerId,
108 artifact_bytes: impl Into<Vec<u8>>,
109 ) -> Result<(), VNextError> {
110 self.weight_materializer_selection =
111 WeightMaterializerSelection::numeric_quality_artifact(materializer_id, artifact_bytes)?;
112 Ok(())
113 }
114
115 pub fn weight_materializer_id(&self) -> &WeightMaterializerId {
116 self.weight_materializer_selection.materializer_id()
117 }
118
119 pub fn weight_materializer_selection(&self) -> &WeightMaterializerSelection {
120 &self.weight_materializer_selection
121 }
122}
123
124#[derive(Debug, Clone, PartialEq, Eq)]
128pub struct ProgramPlanCompilation {
129 executable: ExecutablePlan,
130 node_resolutions: Vec<PlanNodeResolution>,
131 value_tensors: BTreeMap<ProgramValueId, ResolvedTensorSpec>,
132 completion_retention: CompletionRetentionSpec,
133}
134
135impl ProgramPlanCompilation {
136 pub fn executable(&self) -> &ExecutablePlan {
137 &self.executable
138 }
139
140 pub fn node_resolutions(&self) -> &[PlanNodeResolution] {
141 &self.node_resolutions
142 }
143
144 pub fn value_tensors(&self) -> &BTreeMap<ProgramValueId, ResolvedTensorSpec> {
145 &self.value_tensors
146 }
147
148 pub fn completion_retention(&self) -> &CompletionRetentionSpec {
149 &self.completion_retention
150 }
151
152 pub fn into_parts(
153 self,
154 ) -> (
155 ExecutablePlan,
156 Vec<PlanNodeResolution>,
157 BTreeMap<ProgramValueId, ResolvedTensorSpec>,
158 CompletionRetentionSpec,
159 ) {
160 (
161 self.executable,
162 self.node_resolutions,
163 self.value_tensors,
164 self.completion_retention,
165 )
166 }
167}
168
169pub struct ProgramPlanCompiler;
175
176impl ProgramPlanCompiler {
177 pub fn compile<P: RuntimePolicy>(
178 family: &PreparedModelFamily,
179 catalog: &CapabilityCatalog,
180 policy: &P,
181 planning: &OperationPlanningHandle<'_>,
182 options: &ProgramPlanCompileOptions,
183 ) -> Result<ProgramPlanCompilation, VNextError> {
184 let materializers = WeightMaterializerRegistry::identity_only()?;
185 Self::compile_with_weight_materializers(
186 family,
187 catalog,
188 policy,
189 planning,
190 &materializers,
191 options,
192 )
193 }
194
195 pub fn compile_with_weight_materializers<P: RuntimePolicy>(
196 family: &PreparedModelFamily,
197 catalog: &CapabilityCatalog,
198 policy: &P,
199 planning: &OperationPlanningHandle<'_>,
200 materializers: &WeightMaterializerRegistry,
201 options: &ProgramPlanCompileOptions,
202 ) -> Result<ProgramPlanCompilation, VNextError> {
203 let execution_weights =
204 materializers.select(family, catalog, options.weight_materializer_selection())?;
205 Self::compile_with_execution_weights(
206 family,
207 catalog,
208 policy,
209 planning,
210 options,
211 execution_weights,
212 )
213 }
214
215 fn compile_with_execution_weights<P: RuntimePolicy>(
216 family: &PreparedModelFamily,
217 catalog: &CapabilityCatalog,
218 policy: &P,
219 planning: &OperationPlanningHandle<'_>,
220 options: &ProgramPlanCompileOptions,
221 execution_weights: TrustedExecutionWeightPlan,
222 ) -> Result<ProgramPlanCompilation, VNextError> {
223 validate_compile_options(family, options)?;
224 let value_tensors = infer_value_tensors(family, catalog, options)?;
225 family
226 .numerical_profile()
227 .validate_inferred_boundaries(&value_tensors)?;
228 let family_fingerprint = family.fingerprint()?;
229 let execution_weight_fingerprint = execution_weights.plan().fingerprint()?;
230 let execution_weight_schema = execution_weights.plan().schema();
231
232 let probe_locations = dedicated_weight_locations(
233 family,
234 execution_weight_schema,
235 &execution_weight_fingerprint,
236 )?;
237 let probe_storages = build_value_storages(
238 family,
239 execution_weight_schema,
240 catalog,
241 &value_tensors,
242 &probe_locations,
243 &family_fingerprint,
244 )?;
245 let probe_resolutions = resolve_nodes(
246 family,
247 execution_weight_schema,
248 &family_fingerprint,
249 catalog,
250 policy,
251 planning,
252 options,
253 &value_tensors,
254 &probe_storages,
255 )?;
256 let mut value_alignment_bytes = maximum_value_alignment(&probe_resolutions)?;
257 if !value_alignment_bytes.is_power_of_two() {
258 return Err(invalid_plan(
259 "provider value-alignment evidence is not a power of two",
260 ));
261 }
262
263 let mut final_resolution = None;
264 for _ in 0..=u64::BITS {
265 let arena_locations = arena_weight_locations(
266 family,
267 execution_weight_schema,
268 &execution_weight_fingerprint,
269 value_alignment_bytes,
270 )?;
271 let final_storages = build_value_storages(
272 family,
273 execution_weight_schema,
274 catalog,
275 &value_tensors,
276 &arena_locations,
277 &family_fingerprint,
278 )?;
279 let node_resolutions = resolve_nodes(
280 family,
281 execution_weight_schema,
282 &family_fingerprint,
283 catalog,
284 policy,
285 planning,
286 options,
287 &value_tensors,
288 &final_storages,
289 )?;
290 let observed_alignment = maximum_value_alignment(&node_resolutions)?;
291 if observed_alignment <= value_alignment_bytes {
292 final_resolution = Some(node_resolutions);
293 break;
294 }
295 value_alignment_bytes = observed_alignment;
296 }
297 let node_resolutions = final_resolution.ok_or_else(|| {
298 invalid_plan("provider value alignment did not reach a bounded monotonic fixed point")
299 })?;
300 let plan = ExecutionPlan::build(
301 PlanBuildRequest::new(family, catalog, policy, node_resolutions.clone())?
302 .with_execution_weights(execution_weights)?
303 .with_completion_retention(options.completion_retention.clone())?,
304 )?;
305 let executable = ExecutablePlan::new(plan, catalog.clone())?;
306 Ok(ProgramPlanCompilation {
307 executable,
308 node_resolutions,
309 value_tensors,
310 completion_retention: options.completion_retention.clone(),
311 })
312 }
313}
314
315fn maximum_value_alignment(resolutions: &[PlanNodeResolution]) -> Result<u64, VNextError> {
316 let alignment = resolutions
317 .iter()
318 .flat_map(PlanNodeResolution::provider_resource_candidates)
319 .map(ProviderResourcePlan::value_alignment_bytes)
320 .max()
321 .ok_or_else(|| invalid_plan("program has no provider value-alignment evidence"))?;
322 if alignment == 0 || !alignment.is_power_of_two() {
323 return Err(invalid_plan(
324 "provider value-alignment evidence is not a power of two",
325 ));
326 }
327 Ok(alignment)
328}
329
330#[derive(Debug, Clone)]
331struct WeightComponentLocation {
332 resource_id: ResourceId,
333 offset_bytes: u64,
334 length_bytes: u64,
335 element_type: ElementType,
336}
337
338fn validate_compile_options(
339 family: &PreparedModelFamily,
340 options: &ProgramPlanCompileOptions,
341) -> Result<(), VNextError> {
342 let program = family.program();
343 let nodes = program
344 .blocks()
345 .iter()
346 .flat_map(|block| &block.nodes)
347 .collect::<Vec<_>>();
348 let node_ids = nodes
349 .iter()
350 .map(|node| node.id.clone())
351 .collect::<BTreeSet<_>>();
352 if options
353 .required_capabilities
354 .keys()
355 .chain(options.preferred_providers.keys())
356 .any(|node_id| !node_ids.contains(node_id))
357 {
358 return Err(invalid_plan(
359 "program compile options reference an unknown node",
360 ));
361 }
362
363 let mut known_values = program.inputs().iter().cloned().collect::<BTreeSet<_>>();
364 known_values.extend(program.states().iter().map(|state| state.value_id.clone()));
365 known_values.extend(
366 program
367 .weights()
368 .iter()
369 .map(|weight| weight.value_id.clone()),
370 );
371 known_values.extend(nodes.iter().flat_map(|node| node.outputs.iter().cloned()));
372 if options
373 .tensor_specs
374 .keys()
375 .any(|value_id| !known_values.contains(value_id))
376 {
377 return Err(invalid_plan(
378 "program compile options contain an unknown semantic value",
379 ));
380 }
381 let produced_values = nodes
382 .iter()
383 .flat_map(|node| node.outputs.iter().cloned())
384 .collect::<BTreeSet<_>>();
385 if options
386 .completion_retention
387 .values()
388 .iter()
389 .any(|value_id| !produced_values.contains(value_id))
390 {
391 return Err(invalid_plan(
392 "completion retention must reference a semantic node output",
393 ));
394 }
395 if program
396 .inputs()
397 .iter()
398 .any(|input| !options.tensor_specs.contains_key(input))
399 {
400 return Err(invalid_plan(
401 "every program input requires an explicit canonical tensor capacity",
402 ));
403 }
404 Ok(())
405}
406
407fn infer_value_tensors(
408 family: &PreparedModelFamily,
409 catalog: &CapabilityCatalog,
410 options: &ProgramPlanCompileOptions,
411) -> Result<BTreeMap<ProgramValueId, ResolvedTensorSpec>, VNextError> {
412 let program = family.program();
413 let mut tensors = options
414 .tensor_specs
415 .iter()
416 .map(|(value_id, tensor)| Ok((value_id.clone(), resolved_tensor(tensor)?)))
417 .collect::<Result<BTreeMap<_, _>, VNextError>>()?;
418
419 for weight in program.weights() {
420 insert_exact_tensor(
421 &mut tensors,
422 &weight.value_id,
423 resolved_tensor(&weight.tensor)?,
424 "weight",
425 )?;
426 }
427 for state in program.states() {
428 insert_exact_tensor(
429 &mut tensors,
430 &state.value_id,
431 resolved_tensor(&state.tensor)?,
432 "state",
433 )?;
434 }
435
436 for node in program.blocks().iter().flat_map(|block| &block.nodes) {
437 let operation = catalog.operation_for_node(&node.id, &node.operation_id)?;
438 if node.inputs.len() != operation.inputs.len()
439 || node.outputs.len() != operation.outputs.len()
440 {
441 return Err(invalid_plan(format!(
442 "node `{}` arity differs from operation `{}`",
443 node.id, operation.id
444 )));
445 }
446 let mut symbols = TensorSymbols::default();
450
451 for (ordinal, (value_id, contract)) in node.inputs.iter().zip(&operation.inputs).enumerate()
452 {
453 let tensor = tensors.get(value_id).ok_or_else(|| {
454 invalid_plan(format!(
455 "node `{}` input `{value_id}` has no concrete tensor",
456 node.id
457 ))
458 })?;
459 unify_tensor(
460 contract,
461 tensor,
462 &mut symbols,
463 &node.id,
464 "input",
465 ordinal,
466 value_id,
467 )?;
468 }
469 for (ordinal, (value_id, contract)) in
470 node.outputs.iter().zip(&operation.outputs).enumerate()
471 {
472 if let Some(tensor) = tensors.get(value_id) {
473 unify_tensor(
474 contract,
475 tensor,
476 &mut symbols,
477 &node.id,
478 "output",
479 ordinal,
480 value_id,
481 )?;
482 }
483 }
484 for (value_id, contract) in node.outputs.iter().zip(&operation.outputs) {
485 if !tensors.contains_key(value_id) {
486 let tensor = infer_tensor(contract, &mut symbols, &node.id)?;
487 tensors.insert(value_id.clone(), tensor);
488 }
489 }
490 }
491 if program
492 .outputs()
493 .iter()
494 .any(|output| !tensors.contains_key(output))
495 {
496 return Err(invalid_plan(
497 "program compilation did not resolve every semantic output",
498 ));
499 }
500 Ok(tensors)
501}
502
503fn insert_exact_tensor(
504 tensors: &mut BTreeMap<ProgramValueId, ResolvedTensorSpec>,
505 value_id: &ProgramValueId,
506 tensor: ResolvedTensorSpec,
507 kind: &str,
508) -> Result<(), VNextError> {
509 if tensors
510 .insert(value_id.clone(), tensor.clone())
511 .is_some_and(|existing| existing != tensor)
512 {
513 return Err(invalid_plan(format!(
514 "explicit {kind} tensor `{value_id}` differs from model semantics"
515 )));
516 }
517 Ok(())
518}
519
520fn resolved_tensor(tensor: &ProgramTensorSpec) -> Result<ResolvedTensorSpec, VNextError> {
521 ResolvedTensorSpec::new(
522 tensor.dimensions.clone(),
523 tensor.element_type,
524 tensor.layout.clone(),
525 )
526}
527
528#[derive(Debug, Clone, Default)]
529struct TensorSymbols {
530 dimensions: BTreeMap<String, u64>,
531 strides: BTreeMap<String, u64>,
532}
533
534fn unify_tensor(
535 contract: &TensorContract,
536 tensor: &ResolvedTensorSpec,
537 symbols: &mut TensorSymbols,
538 node_id: &NodeId,
539 role: &str,
540 ordinal: usize,
541 value_id: &ProgramValueId,
542) -> Result<(), VNextError> {
543 let context = format!("node `{node_id}` {role}[{ordinal}] `{value_id}`");
544 if !contract.element_types().contains(&tensor.element_type())
545 || contract.dimensions().len() != tensor.dimensions().len()
546 {
547 return Err(invalid_plan(format!(
548 "{context} rank or dtype differs from its operation contract: actual rank={} dtype={:?}, expected rank={} dtypes={:?}",
549 tensor.dimensions().len(),
550 tensor.element_type(),
551 contract.dimensions().len(),
552 contract.element_types()
553 )));
554 }
555 unify_dimensions(
556 contract.dimensions(),
557 tensor.dimensions(),
558 &mut symbols.dimensions,
559 &context,
560 )?;
561 if !layout_matches(contract.layouts(), tensor.layout(), &mut symbols.strides)? {
562 return Err(invalid_plan(format!(
563 "{context} layout {:?} differs from its operation contract {:?}",
564 tensor.layout(),
565 contract.layouts()
566 )));
567 }
568 Ok(())
569}
570
571fn unify_dimensions(
572 constraints: &[DimensionConstraint],
573 dimensions: &[u64],
574 symbols: &mut BTreeMap<String, u64>,
575 context: &str,
576) -> Result<(), VNextError> {
577 for (axis, (constraint, extent)) in constraints.iter().zip(dimensions).enumerate() {
578 let valid = match constraint {
579 DimensionConstraint::Exact(expected) => expected == extent,
580 DimensionConstraint::Range { minimum, maximum } => {
581 minimum <= extent && extent <= maximum
582 }
583 DimensionConstraint::Symbol(symbol) => bind_symbol(symbols, symbol, *extent),
584 };
585 if !valid {
586 return Err(invalid_plan(format!(
587 "{context} dimension[{axis}]={extent} violates `{constraint:?}`"
588 )));
589 }
590 }
591 Ok(())
592}
593
594fn bind_symbol(symbols: &mut BTreeMap<String, u64>, symbol: &str, value: u64) -> bool {
595 if value == 0 {
596 return false;
597 }
598 match symbols.get(symbol) {
599 Some(existing) => *existing == value,
600 None => {
601 symbols.insert(symbol.to_owned(), value);
602 true
603 }
604 }
605}
606
607fn infer_tensor(
608 contract: &TensorContract,
609 symbols: &mut TensorSymbols,
610 node_id: &NodeId,
611) -> Result<ResolvedTensorSpec, VNextError> {
612 let element_type = contract
613 .element_types()
614 .iter()
615 .copied()
616 .next()
617 .filter(|_| contract.element_types().len() == 1)
618 .ok_or_else(|| {
619 invalid_plan(format!(
620 "node `{node_id}` output dtype is ambiguous; provide an explicit tensor"
621 ))
622 })?;
623 let dimensions = contract
624 .dimensions()
625 .iter()
626 .map(|dimension| match dimension {
627 DimensionConstraint::Exact(value) => Ok(*value),
628 DimensionConstraint::Range { minimum, maximum } if minimum == maximum => Ok(*minimum),
629 DimensionConstraint::Range { .. } => Err(invalid_plan(format!(
630 "node `{node_id}` output range is ambiguous; provide an explicit tensor"
631 ))),
632 DimensionConstraint::Symbol(symbol) => symbols
633 .dimensions
634 .get(symbol)
635 .copied()
636 .ok_or_else(|| {
637 invalid_plan(format!(
638 "node `{node_id}` output symbol `{symbol}` is unresolved; provide an explicit tensor"
639 ))
640 }),
641 })
642 .collect::<Result<Vec<_>, VNextError>>()?;
643 let layout = infer_layout(contract.layouts(), &dimensions, &symbols.strides, node_id)?;
644 ResolvedTensorSpec::new(dimensions, element_type, layout)
645}
646
647fn infer_layout(
648 layouts: &[LayoutConstraint],
649 dimensions: &[u64],
650 stride_symbols: &BTreeMap<String, u64>,
651 node_id: &NodeId,
652) -> Result<ResolvedTensorLayout, VNextError> {
653 if layouts.len() != 1 {
654 return Err(invalid_plan(format!(
655 "node `{node_id}` output layout is ambiguous; provide an explicit tensor"
656 )));
657 }
658 match &layouts[0] {
659 LayoutConstraint::Contiguous => Ok(ResolvedTensorLayout::Contiguous),
660 LayoutConstraint::Strided { strides } => {
661 let byte_strides = strides
662 .iter()
663 .map(|stride| match stride {
664 StrideConstraint::ExactBytes(value) => Ok(*value),
665 StrideConstraint::Symbol(symbol) => {
666 stride_symbols.get(symbol).copied().ok_or_else(|| {
667 invalid_plan(format!(
668 "node `{node_id}` stride symbol `{symbol}` is unresolved"
669 ))
670 })
671 }
672 })
673 .collect::<Result<Vec<_>, VNextError>>()?;
674 Ok(ResolvedTensorLayout::Strided { byte_strides })
675 }
676 LayoutConstraint::Blocked { block, axis_order } => {
677 let divisible = dimensions
678 .iter()
679 .zip(block)
680 .all(|(extent, block)| extent % block == 0);
681 let padding = if divisible {
682 BlockedTensorPadding::Exact
683 } else {
684 let physical_dimensions = axis_order
685 .iter()
686 .map(|axis| {
687 let extent = dimensions[*axis as usize];
688 let block = block[*axis as usize];
689 extent
690 .checked_add(block - 1)
691 .map(|value| value / block * block)
692 .ok_or_else(|| invalid_plan("blocked tensor padding overflows u64"))
693 })
694 .collect::<Result<Vec<_>, VNextError>>()?;
695 BlockedTensorPadding::ZeroFill {
696 physical_dimensions,
697 }
698 };
699 Ok(ResolvedTensorLayout::Blocked {
700 block: block.clone(),
701 axis_order: axis_order.clone(),
702 padding,
703 })
704 }
705 }
706}
707
708fn layout_matches(
709 constraints: &[LayoutConstraint],
710 layout: &ResolvedTensorLayout,
711 symbols: &mut BTreeMap<String, u64>,
712) -> Result<bool, VNextError> {
713 for constraint in constraints {
714 let mut candidate_symbols = symbols.clone();
715 let matches = match (constraint, layout) {
716 (LayoutConstraint::Contiguous, ResolvedTensorLayout::Contiguous) => true,
717 (
718 LayoutConstraint::Strided { strides },
719 ResolvedTensorLayout::Strided { byte_strides },
720 ) if strides.len() == byte_strides.len() => {
721 strides
722 .iter()
723 .zip(byte_strides)
724 .all(|(constraint, stride)| match constraint {
725 StrideConstraint::ExactBytes(expected) => expected == stride,
726 StrideConstraint::Symbol(symbol) => {
727 bind_symbol(&mut candidate_symbols, symbol, *stride)
728 }
729 })
730 }
731 (
732 LayoutConstraint::Blocked { block, axis_order },
733 ResolvedTensorLayout::Blocked {
734 block: actual_block,
735 axis_order: actual_axis_order,
736 ..
737 },
738 ) => block == actual_block && axis_order == actual_axis_order,
739 _ => false,
740 };
741 if matches {
742 *symbols = candidate_symbols;
743 return Ok(true);
744 }
745 }
746 Ok(false)
747}
748
749fn dedicated_weight_locations(
750 family: &PreparedModelFamily,
751 execution_weight_schema: &WeightSchema,
752 execution_weight_fingerprint: &str,
753) -> Result<BTreeMap<WeightId, WeightComponentLocation>, VNextError> {
754 referenced_weight_components(family, execution_weight_schema)?
755 .into_iter()
756 .map(|component| {
757 let length_bytes = component.physical_bytes()?;
758 Ok((
759 component.id.clone(),
760 WeightComponentLocation {
761 resource_id: hashed_resource_id(
762 "weight-probe",
763 execution_weight_fingerprint,
764 component.id.as_str(),
765 )?,
766 offset_bytes: 0,
767 length_bytes,
768 element_type: component.physical_element_type(),
769 },
770 ))
771 })
772 .collect()
773}
774
775fn arena_weight_locations(
776 family: &PreparedModelFamily,
777 execution_weight_schema: &WeightSchema,
778 execution_weight_fingerprint: &str,
779 provider_alignment_bytes: u64,
780) -> Result<BTreeMap<WeightId, WeightComponentLocation>, VNextError> {
781 let components = referenced_weight_components(family, execution_weight_schema)?;
782 let mut arena_ids = BTreeMap::<ElementType, ResourceId>::new();
783 let mut next_offsets = BTreeMap::<ElementType, u64>::new();
784 let mut locations = BTreeMap::new();
785 for component in components {
786 let element_type = component.physical_element_type();
787 let alignment = provider_alignment_bytes.max(element_type.size_bytes());
788 let next_offset = next_offsets.entry(element_type).or_insert(0);
789 let offset_bytes = checked_align_up(*next_offset, alignment)?;
790 let length_bytes = component.physical_bytes()?;
791 *next_offset = offset_bytes
792 .checked_add(length_bytes)
793 .ok_or_else(|| invalid_plan("weight arena byte range overflows u64"))?;
794 let resource_id = arena_ids
795 .entry(element_type)
796 .or_insert(hashed_resource_id(
797 "weight-arena",
798 execution_weight_fingerprint,
799 &serde_json::to_string(&element_type).map_err(|error| {
800 VNextError::Serialization {
801 context: "serialize weight arena element type",
802 message: error.to_string(),
803 }
804 })?,
805 )?)
806 .clone();
807 locations.insert(
808 component.id.clone(),
809 WeightComponentLocation {
810 resource_id,
811 offset_bytes,
812 length_bytes,
813 element_type,
814 },
815 );
816 }
817 Ok(locations)
818}
819
820fn referenced_weight_components<'schema>(
821 family: &PreparedModelFamily,
822 execution_weight_schema: &'schema WeightSchema,
823) -> Result<Vec<&'schema super::super::WeightComponentSpec>, VNextError> {
824 let referenced = family
825 .program()
826 .weights()
827 .iter()
828 .map(|weight| execution_weight_schema.physical_component_refs(&weight.weight_id))
829 .collect::<Result<Vec<_>, VNextError>>()?
830 .into_iter()
831 .flatten()
832 .map(|component| component.id.clone())
833 .collect::<BTreeSet<_>>();
834 Ok(execution_weight_schema
835 .components
836 .iter()
837 .filter(|component| referenced.contains(&component.id))
838 .collect())
839}
840
841fn checked_align_up(value: u64, alignment: u64) -> Result<u64, VNextError> {
842 if alignment == 0 || !alignment.is_power_of_two() {
843 return Err(invalid_plan("weight arena alignment is invalid"));
844 }
845 value
846 .checked_add(alignment - 1)
847 .map(|sum| sum & !(alignment - 1))
848 .ok_or_else(|| invalid_plan("weight arena alignment overflows u64"))
849}
850
851fn hashed_resource_id(
852 kind: &str,
853 family_fingerprint: &str,
854 semantic_identity: &str,
855) -> Result<ResourceId, VNextError> {
856 let digest =
857 Sha256::digest(format!("{kind}\0{family_fingerprint}\0{semantic_identity}").as_bytes());
858 ResourceId::new(format!("resource/{kind}/sha256/{digest:x}"))
859}
860
861fn build_value_storages(
862 family: &PreparedModelFamily,
863 execution_weight_schema: &WeightSchema,
864 catalog: &CapabilityCatalog,
865 tensors: &BTreeMap<ProgramValueId, ResolvedTensorSpec>,
866 weight_locations: &BTreeMap<WeightId, WeightComponentLocation>,
867 family_fingerprint: &str,
868) -> Result<BTreeMap<ProgramValueId, ResolvedValueStorage>, VNextError> {
869 let program = family.program();
870 let mut storages = BTreeMap::new();
871 for input in program.inputs() {
872 storages.insert(
873 input.clone(),
874 activation_storage(input, tensors, family_fingerprint)?,
875 );
876 }
877 for weight in program.weights() {
878 let components = execution_weight_schema
879 .physical_component_refs(&weight.weight_id)?
880 .into_iter()
881 .map(|component| {
882 let location = weight_locations.get(&component.id).ok_or_else(|| {
883 invalid_plan(format!(
884 "weight component `{}` has no physical location",
885 component.id
886 ))
887 })?;
888 ResolvedStorageComponent::new(
889 Some(component.id.clone()),
890 location.resource_id.clone(),
891 location.offset_bytes,
892 location.length_bytes,
893 location.element_type,
894 )
895 })
896 .collect::<Result<Vec<_>, VNextError>>()?;
897 storages.insert(
898 weight.value_id.clone(),
899 ResolvedValueStorage::composite(components)?,
900 );
901 }
902 for state in program.states() {
903 let tensor = tensors
904 .get(&state.value_id)
905 .ok_or_else(|| invalid_plan(format!("state `{}` has no resolved tensor", state.id)))?;
906 let tensor_minimum_bytes = tensor.minimum_storage_bytes()?;
907 let state_minimum_bytes = checked_align_up(
908 state.capacity_demand.minimum_bytes(tensor_minimum_bytes)?,
909 tensor.element_type().size_bytes(),
910 )?;
911 storages.insert(
912 state.value_id.clone(),
913 ResolvedValueStorage::single(
914 hashed_resource_id("state", family_fingerprint, state.id.as_str())?,
915 0,
916 state_minimum_bytes,
917 tensor.element_type(),
918 )?,
919 );
920 }
921 for node in program.blocks().iter().flat_map(|block| &block.nodes) {
922 let operation = catalog.operation_for_node(&node.id, &node.operation_id)?;
923 for (value_id, contract) in node.outputs.iter().zip(&operation.outputs) {
924 let storage = match contract.alias() {
925 AliasPolicy::MustAlias { tensor_index } => {
926 let input = node.inputs.get(*tensor_index as usize).ok_or_else(|| {
927 invalid_plan(format!("node `{}` aliases an absent input", node.id))
928 })?;
929 storages.get(input).cloned().ok_or_else(|| {
930 invalid_plan(format!(
931 "node `{}` alias input `{input}` has no storage",
932 node.id
933 ))
934 })?
935 }
936 AliasPolicy::NoAlias | AliasPolicy::MayAlias { .. } => {
937 activation_storage(value_id, tensors, family_fingerprint)?
938 }
939 };
940 storages.insert(value_id.clone(), storage);
941 }
942 }
943 Ok(storages)
944}
945
946fn activation_storage(
947 value_id: &ProgramValueId,
948 tensors: &BTreeMap<ProgramValueId, ResolvedTensorSpec>,
949 family_fingerprint: &str,
950) -> Result<ResolvedValueStorage, VNextError> {
951 let tensor = tensors
952 .get(value_id)
953 .ok_or_else(|| invalid_plan(format!("activation `{value_id}` has no resolved tensor")))?;
954 ResolvedValueStorage::single(
955 hashed_resource_id("activation", family_fingerprint, value_id.as_str())?,
956 0,
957 tensor.minimum_storage_bytes()?,
958 tensor.element_type(),
959 )
960}
961
962#[allow(clippy::too_many_arguments)]
963fn resolve_nodes<P: RuntimePolicy>(
964 family: &PreparedModelFamily,
965 execution_weight_schema: &WeightSchema,
966 prepared_family_fingerprint: &str,
967 catalog: &CapabilityCatalog,
968 policy: &P,
969 planning: &OperationPlanningHandle<'_>,
970 options: &ProgramPlanCompileOptions,
971 tensors: &BTreeMap<ProgramValueId, ResolvedTensorSpec>,
972 storages: &BTreeMap<ProgramValueId, ResolvedValueStorage>,
973) -> Result<Vec<PlanNodeResolution>, VNextError> {
974 family
975 .program()
976 .blocks()
977 .iter()
978 .flat_map(|block| &block.nodes)
979 .map(|node| {
980 let operation = catalog.operation_for_node(&node.id, &node.operation_id)?;
981 let values = node
982 .inputs
983 .iter()
984 .zip(&operation.inputs)
985 .enumerate()
986 .map(|(ordinal, (value_id, contract))| {
987 resolved_binding(
988 family,
989 execution_weight_schema,
990 value_id,
991 ResolvedValueRole::Input,
992 ordinal as u32,
993 contract,
994 tensors,
995 storages,
996 )
997 })
998 .chain(node.outputs.iter().zip(&operation.outputs).enumerate().map(
999 |(ordinal, (value_id, contract))| {
1000 resolved_binding(
1001 family,
1002 execution_weight_schema,
1003 value_id,
1004 ResolvedValueRole::Output,
1005 ordinal as u32,
1006 contract,
1007 tensors,
1008 storages,
1009 )
1010 },
1011 ))
1012 .collect::<Result<Vec<_>, VNextError>>()?;
1013 PlanNodeResolution::resolve_with_family_fingerprint(
1014 family,
1015 execution_weight_schema,
1016 prepared_family_fingerprint,
1017 catalog,
1018 policy,
1019 planning,
1020 node.id.clone(),
1021 values,
1022 options
1023 .required_capabilities
1024 .get(&node.id)
1025 .cloned()
1026 .unwrap_or_default(),
1027 options.preferred_providers.get(&node.id).cloned(),
1028 )
1029 })
1030 .collect()
1031}
1032
1033fn resolved_binding(
1034 family: &PreparedModelFamily,
1035 execution_weight_schema: &WeightSchema,
1036 value_id: &ProgramValueId,
1037 role: ResolvedValueRole,
1038 ordinal: u32,
1039 contract: &TensorContract,
1040 tensors: &BTreeMap<ProgramValueId, ResolvedTensorSpec>,
1041 storages: &BTreeMap<ProgramValueId, ResolvedValueStorage>,
1042) -> Result<ResolvedValueBinding, VNextError> {
1043 let program_weight = family
1044 .program()
1045 .weights()
1046 .iter()
1047 .find(|weight| weight.value_id == *value_id);
1048 let usage = if program_weight.is_some() {
1049 BufferUsage::Weights
1050 } else if family
1051 .program()
1052 .states()
1053 .iter()
1054 .any(|state| state.value_id == *value_id)
1055 {
1056 BufferUsage::State
1057 } else {
1058 BufferUsage::Activations
1059 };
1060 ResolvedValueBinding::new(
1061 value_id.clone(),
1062 role,
1063 ordinal,
1064 tensors
1065 .get(value_id)
1066 .cloned()
1067 .ok_or_else(|| invalid_plan(format!("value `{value_id}` has no resolved tensor")))?,
1068 contract.access(),
1069 if role == ResolvedValueRole::Input {
1070 AliasPolicy::NoAlias
1071 } else {
1072 contract.alias().clone()
1073 },
1074 usage,
1075 program_weight
1076 .map(|weight| {
1077 ResolvedWeightBinding::from_schema(execution_weight_schema, &weight.weight_id)
1078 })
1079 .transpose()?,
1080 storages
1081 .get(value_id)
1082 .cloned()
1083 .ok_or_else(|| invalid_plan(format!("value `{value_id}` has no resolved storage")))?,
1084 )
1085}
1086
1087#[cfg(test)]
1088mod tests {
1089 use crate::vnext::TensorAccess;
1090
1091 use super::{
1092 infer_tensor, unify_tensor, AliasPolicy, BTreeSet, DimensionConstraint, ElementType,
1093 LayoutConstraint, NodeId, ProgramValueId, ResolvedTensorLayout, ResolvedTensorSpec,
1094 StrideConstraint, TensorContract, TensorSymbols,
1095 };
1096
1097 #[test]
1098 fn dimension_and_stride_symbols_are_independent_domains() {
1099 let contract = TensorContract::new(
1100 vec![DimensionConstraint::Symbol("shared".to_owned())],
1101 BTreeSet::from([ElementType::F32]),
1102 vec![LayoutConstraint::Strided {
1103 strides: vec![StrideConstraint::Symbol("shared".to_owned())],
1104 }],
1105 TensorAccess::Read,
1106 AliasPolicy::NoAlias,
1107 )
1108 .unwrap();
1109 let tensor = ResolvedTensorSpec::new(
1110 vec![4],
1111 ElementType::F32,
1112 ResolvedTensorLayout::Strided {
1113 byte_strides: vec![16],
1114 },
1115 )
1116 .unwrap();
1117 let mut symbols = TensorSymbols::default();
1118 let value_id = ProgramValueId::new("value.symbol-domains").unwrap();
1119 unify_tensor(
1120 &contract,
1121 &tensor,
1122 &mut symbols,
1123 &NodeId::new("node.symbol-domains").unwrap(),
1124 "input",
1125 0,
1126 &value_id,
1127 )
1128 .unwrap();
1129 assert_eq!(symbols.dimensions.get("shared"), Some(&4));
1130 assert_eq!(symbols.strides.get("shared"), Some(&16));
1131 }
1132
1133 #[test]
1134 fn ambiguous_output_range_or_layout_requires_an_explicit_tensor() {
1135 let range = TensorContract::new(
1136 vec![DimensionConstraint::Range {
1137 minimum: 1,
1138 maximum: 8,
1139 }],
1140 BTreeSet::from([ElementType::F32]),
1141 vec![LayoutConstraint::Contiguous],
1142 TensorAccess::Write,
1143 AliasPolicy::NoAlias,
1144 )
1145 .unwrap();
1146 let mut symbols = TensorSymbols::default();
1147 let node_id = NodeId::new("node.ambiguous").unwrap();
1148 assert!(infer_tensor(&range, &mut symbols, &node_id).is_err());
1149
1150 let layouts = TensorContract::new(
1151 vec![DimensionConstraint::Exact(4)],
1152 BTreeSet::from([ElementType::F32]),
1153 vec![
1154 LayoutConstraint::Contiguous,
1155 LayoutConstraint::Strided {
1156 strides: vec![StrideConstraint::ExactBytes(4)],
1157 },
1158 ],
1159 TensorAccess::Write,
1160 AliasPolicy::NoAlias,
1161 )
1162 .unwrap();
1163 assert!(infer_tensor(&layouts, &mut symbols, &node_id).is_err());
1164 }
1165}