1use super::*;
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12pub enum SemanticLearningPhase {
13 Alignment = 0,
14 Fast = 1,
15 Consolidation = 2,
16}
17
18impl SemanticLearningPhase {
19 pub fn from_code(code: u64) -> Result<Self, SemanticTransitionError> {
20 match code {
21 0 => Ok(Self::Alignment),
22 1 => Ok(Self::Fast),
23 2 => Ok(Self::Consolidation),
24 _ => Err(publication_input_error("unknown learning phase")),
25 }
26 }
27}
28
29#[derive(Clone, Debug, PartialEq, Eq)]
33pub enum SemanticLearningCopyReset {
34 Preserve {
35 role: u64,
36 index: u64,
37 },
38 Zero {
39 role: u64,
40 index: u64,
41 },
42 MasterFromEffective {
43 index: u64,
44 effective_role: u64,
45 effective_index: u64,
46 },
47 Phase {
48 index: u64,
49 },
50}
51
52impl SemanticLearningCopyReset {
53 fn key(&self) -> (u64, u64) {
54 match *self {
55 Self::Preserve { role, index } | Self::Zero { role, index } => (role, index),
56 Self::MasterFromEffective { index, .. } => (21, index),
57 Self::Phase { index } => (23, index),
58 }
59 }
60}
61
62#[derive(Clone, Debug)]
67pub struct SemanticLearningPhaseTransition {
68 pub source: SemanticLearningPhase,
69 pub target: SemanticLearningPhase,
70 pub phase_index: u64,
71 pub completed_updates_index: u64,
72 pub recipe: Vec<SemanticLearningCopyReset>,
73 pub admission: Vec<u8>,
74}
75
76#[derive(Clone, Debug, PartialEq, Eq)]
79pub struct SemanticLearningPhaseRecord {
80 pub source: SemanticLearningPhase,
81 pub target: SemanticLearningPhase,
82 pub phase_index: u64,
83 pub completed_updates_index: u64,
84 pub predecessor: SemanticPublishedIdentity,
85 pub model_generation: u64,
86 pub completed_updates: u64,
87 pub recipe_digest: Identity256,
88 pub recipe: Vec<SemanticLearningCopyReset>,
89 pub admission: Vec<u8>,
90}
91
92impl SemanticLearningPhaseTransition {
93 pub fn recipe_digest(&self) -> Identity256 {
94 let mut digest = Sha256::new();
95 digest.update(b"xlog.learning-phase.copy-reset.v1\0");
96 for value in [
97 self.source as u64,
98 self.target as u64,
99 self.phase_index,
100 self.completed_updates_index,
101 self.recipe.len() as u64,
102 ] {
103 digest.update(value.to_le_bytes());
104 }
105 for operation in &self.recipe {
106 let (role, index) = operation.key();
107 let (code, source_role, source_index) = match *operation {
108 SemanticLearningCopyReset::Preserve { .. } => (0, 0, 0),
109 SemanticLearningCopyReset::Zero { .. } => (1, 0, 0),
110 SemanticLearningCopyReset::MasterFromEffective {
111 effective_role,
112 effective_index,
113 ..
114 } => (2, effective_role, effective_index),
115 SemanticLearningCopyReset::Phase { .. } => (3, 0, 0),
116 };
117 for value in [role, index, code, source_role, source_index] {
118 digest.update(value.to_le_bytes());
119 }
120 }
121 Identity256::from_bytes(digest.finalize().into())
122 }
123
124 pub(super) fn apply(
125 &self,
126 material: &mut PublicationMaterial,
127 ) -> Result<(SemanticLearningPhaseRecord, Option<LearningFoldPlan>), SemanticTransitionError>
128 {
129 if !matches!(
130 (self.source, self.target),
131 (
132 SemanticLearningPhase::Alignment,
133 SemanticLearningPhase::Fast
134 ) | (
135 SemanticLearningPhase::Fast,
136 SemanticLearningPhase::Consolidation
137 ) | (
138 SemanticLearningPhase::Consolidation,
139 SemanticLearningPhase::Fast
140 )
141 ) || self.admission.is_empty()
142 || self.phase_index == self.completed_updates_index
143 {
144 return Err(publication_input_error(
145 "learning transition lacks its permitted boundary or original native admission",
146 ));
147 }
148 material.require_successful_recompute()?;
149 let phase = scalar_i64(material, (23, self.phase_index))?;
150 let updates = scalar_i64(material, (23, self.completed_updates_index))?;
151 if phase != self.source as i64 || updates < 0 {
152 return Err(publication_input_error(
153 "learning transition differs from the selected phase or cumulative progress",
154 ));
155 }
156 if let Some(previous) = material.learning_phases.last() {
157 if previous.target != self.source
158 || previous.phase_index != self.phase_index
159 || previous.completed_updates_index != self.completed_updates_index
160 || previous.completed_updates > updates as u64
161 {
162 return Err(publication_input_error(
163 "learning transition changed its retained phase lineage",
164 ));
165 }
166 } else if self.source != SemanticLearningPhase::Alignment {
167 return Err(publication_input_error(
168 "learning lineage must begin with admitted alignment",
169 ));
170 }
171 let expected = material
172 .layouts
173 .keys()
174 .filter(|(role, _)| (18..=25).contains(role))
175 .copied()
176 .collect::<BTreeSet<_>>();
177 let keys = self
178 .recipe
179 .iter()
180 .map(SemanticLearningCopyReset::key)
181 .collect::<Vec<_>>();
182 if keys.windows(2).any(|pair| pair[0] >= pair[1])
183 || keys.iter().copied().collect::<BTreeSet<_>>() != expected
184 || !self.recipe.contains(&SemanticLearningCopyReset::Phase {
185 index: self.phase_index,
186 })
187 || !self.recipe.contains(&SemanticLearningCopyReset::Preserve {
188 role: 23,
189 index: self.completed_updates_index,
190 })
191 {
192 return Err(publication_input_error("copy/reset recipe must cover every model and learning view exactly once in native order"));
193 }
194 let fold = if self.source == SemanticLearningPhase::Fast
195 && self.target == SemanticLearningPhase::Consolidation
196 {
197 Some(LearningFoldPlan::prepare(material, self)?)
198 } else {
199 None
200 };
201 if fold.is_none() {
204 let original = &material.model_allocations;
205 let mut allocations = original.clone();
206 for operation in &self.recipe {
207 if matches!(operation, SemanticLearningCopyReset::Preserve { .. }) {
208 continue;
209 }
210 visit_expected_values(
211 material,
212 operation,
213 self.target,
214 self.phase_index,
215 |allocation, offset, value| {
216 allocations[allocation][offset..offset + value.len()]
217 .copy_from_slice(value);
218 Ok(())
219 },
220 )?;
221 }
222 for operation in &self.recipe {
223 visit_expected_values(
224 material,
225 operation,
226 self.target,
227 self.phase_index,
228 |allocation, offset, value| {
229 if allocations[allocation][offset..offset + value.len()] != *value {
230 return Err(publication_input_error(
231 "copy/reset recipe has conflicting effects on shared physical views",
232 ));
233 }
234 Ok(())
235 },
236 )?;
237 }
238 material.model_allocations = allocations;
239 for range in &mut material.ranges {
240 if matches!(range.range.role, 18..=25) {
241 let (allocation, offset) = material
242 .model_memory
243 .location(range.range.role, range.range.index)?;
244 range.bytes = material.model_allocations[allocation]
245 [offset..offset + range.bytes.len()]
246 .to_vec();
247 }
248 }
249 }
250 let header = material.bank.header;
251 Ok((
252 SemanticLearningPhaseRecord {
253 source: self.source,
254 target: self.target,
255 phase_index: self.phase_index,
256 completed_updates_index: self.completed_updates_index,
257 predecessor: SemanticPublishedIdentity {
258 instance: header.instance,
259 word: header.publication_word,
260 logical_digest: header.logical_digest,
261 state_digest: header.state_digest,
262 },
263 model_generation: header.model_generation,
264 completed_updates: updates as u64,
265 recipe_digest: self.recipe_digest(),
266 recipe: self.recipe.clone(),
267 admission: self.admission.clone(),
268 },
269 fold,
270 ))
271 }
272}
273
274fn scalar_i64(
275 material: &PublicationMaterial,
276 key: (u64, u64),
277) -> Result<i64, SemanticTransitionError> {
278 let layout = material
279 .layouts
280 .get(&key)
281 .ok_or_else(|| publication_input_error("learning scalar is absent"))?;
282 if layout.rank != 0 || layout.scalar_type != 7 || layout.element_bytes != 8 {
283 return Err(publication_input_error(
284 "learning phase and progress need their actual I64 scalar views",
285 ));
286 }
287 let (allocation, offset) = material.model_memory.location(key.0, key.1)?;
288 let bytes = material.model_allocations[allocation]
289 .get(offset..offset + 8)
290 .ok_or(SemanticTransitionError::ObservationMismatch)?;
291 Ok(i64::from_le_bytes(bytes.try_into().map_err(|_| {
292 SemanticTransitionError::ObservationMismatch
293 })?))
294}
295
296fn element_offsets<'a>(
297 material: &'a PublicationMaterial,
298 key: (u64, u64),
299) -> Result<
300 (
301 usize,
302 impl Iterator<Item = Result<usize, SemanticTransitionError>> + 'a,
303 ),
304 SemanticTransitionError,
305> {
306 let layout = material
307 .layouts
308 .get(&key)
309 .ok_or(SemanticTransitionError::ObservationMismatch)?;
310 let (allocation, base) = material.model_memory.location(key.0, key.1)?;
311 let rank =
312 usize::try_from(layout.rank).map_err(|_| SemanticTransitionError::ObservationMismatch)?;
313 if rank > 4 {
314 return Err(SemanticTransitionError::ObservationMismatch);
315 }
316 let count = layout.dimensions[..rank]
317 .iter()
318 .try_fold(1u64, |n, &dimension| {
319 n.checked_mul(dimension)
320 .ok_or(SemanticTransitionError::GenerationExhausted)
321 })?;
322 let offsets = (0..count).map(move |mut position| {
323 let mut offset = base as u64;
324 for axis in (0..rank).rev() {
325 let coordinate = position % layout.dimensions[axis];
326 position /= layout.dimensions[axis];
327 offset = coordinate
328 .checked_mul(layout.strides_bytes[axis])
329 .and_then(|stride| offset.checked_add(stride))
330 .ok_or(SemanticTransitionError::GenerationExhausted)?;
331 }
332 let end = offset
333 .checked_add(layout.element_bytes)
334 .ok_or(SemanticTransitionError::GenerationExhausted)?;
335 if end > material.model_memory.allocation_bytes[allocation] {
336 return Err(SemanticTransitionError::ObservationMismatch);
337 }
338 usize::try_from(offset).map_err(|_| SemanticTransitionError::GenerationExhausted)
339 });
340 Ok((allocation, offsets))
341}
342
343fn visit_expected_values(
344 material: &PublicationMaterial,
345 operation: &SemanticLearningCopyReset,
346 target: SemanticLearningPhase,
347 phase_index: u64,
348 mut visit: impl FnMut(usize, usize, &[u8]) -> Result<(), SemanticTransitionError>,
349) -> Result<(), SemanticTransitionError> {
350 let key = operation.key();
351 let width = material.layouts[&key].element_bytes as usize;
352 if width == 0 || width > 8 {
353 return Err(SemanticTransitionError::ObservationMismatch);
354 }
355 let (allocation, offsets) = element_offsets(material, key)?;
356 let mut effective_offsets = None;
357 match *operation {
358 SemanticLearningCopyReset::Zero { role, .. } if !matches!(role, 21 | 24 | 25) => {
359 return Err(publication_input_error(
360 "phase reset cannot zero effective weights, masks, rates or loss scale",
361 ))
362 }
363 SemanticLearningCopyReset::Phase { index } if index != phase_index => {
364 return Err(publication_input_error(
365 "recipe writes another learning phase",
366 ))
367 }
368 SemanticLearningCopyReset::MasterFromEffective {
369 effective_role,
370 effective_index,
371 ..
372 } => {
373 let effective_key = (effective_role, effective_index);
374 let layout = &material.layouts[&key];
375 let effective = material.layouts.get(&effective_key).ok_or_else(|| {
376 publication_input_error("master reset has no original effective parameter")
377 })?;
378 if !(18..=20).contains(&effective_role)
379 || effective.scalar_type != 5
380 || effective.element_bytes != 2
381 || layout.scalar_type != 6
382 || width != 4
383 || effective.rank != layout.rank
384 || effective.dimensions != layout.dimensions
385 {
386 return Err(publication_input_error("master reset requires the actual matching BF16 effective and FP32 master views"));
387 }
388 effective_offsets = Some(element_offsets(material, effective_key)?);
389 }
390 _ => {}
391 }
392 let phase = (target as i64).to_le_bytes();
393 for offset in offsets {
394 let offset = offset?;
395 let mut converted = [0u8; 4];
396 let expected = match *operation {
397 SemanticLearningCopyReset::Preserve { .. } => {
398 &material.model_allocations[allocation][offset..offset + width]
399 }
400 SemanticLearningCopyReset::Zero { .. } => &[0u8; 8][..width],
401 SemanticLearningCopyReset::Phase { .. } => &phase,
402 SemanticLearningCopyReset::MasterFromEffective { .. } => {
403 let (source_allocation, offsets) = effective_offsets
404 .as_mut()
405 .ok_or(SemanticTransitionError::ObservationMismatch)?;
406 let offset = offsets
407 .next()
408 .ok_or(SemanticTransitionError::ObservationMismatch)??;
409 let bytes = &material.model_allocations[*source_allocation][offset..offset + 2];
410 let bits = u32::from(u16::from_le_bytes([bytes[0], bytes[1]])) << 16;
411 if !f32::from_bits(bits).is_finite() {
412 return Err(publication_input_error(
413 "master reset encountered a nonfinite accepted effective weight",
414 ));
415 }
416 converted.copy_from_slice(&bits.to_le_bytes());
417 &converted
418 }
419 };
420 visit(allocation, offset, expected)?;
421 }
422 Ok(())
423}
424
425struct FoldAssignment {
426 key: (u64, u64),
427 mode: u64,
428 source: Option<(u64, u64)>,
429 factors: Option<((u64, u64), (u64, u64))>,
430}
431
432pub(super) struct LearningFoldPlan {
435 assignments: Vec<FoldAssignment>,
436 scale_bits: u32,
437 phase: u64,
438}
439
440fn fold_error() -> SemanticTransitionError {
441 publication_input_error(
442 "adapter absorption differs from its original model contract or physical views",
443 )
444}
445
446fn schema_array(
447 value: &serde_json::Value,
448) -> Result<&Vec<serde_json::Value>, SemanticTransitionError> {
449 value.as_array().ok_or_else(fold_error)
450}
451
452fn tagged<'a>(
453 value: &'a serde_json::Value,
454 tag: &str,
455) -> Result<&'a serde_json::Value, SemanticTransitionError> {
456 let pair = schema_array(value)?;
457 if pair.len() != 2 || pair[0].as_str() != Some(tag) {
458 return Err(fold_error());
459 }
460 Ok(&pair[1])
461}
462
463fn original_float(value: &serde_json::Value) -> Result<f32, SemanticTransitionError> {
464 let text = tagged(value, "float")?.as_str().ok_or_else(fold_error)?;
467 let (negative, text) = text.strip_prefix('-').map_or((false, text), |s| (true, s));
468 let (mantissa, exponent) = text
469 .strip_prefix("0x")
470 .and_then(|s| s.split_once('p'))
471 .ok_or_else(fold_error)?;
472 let (whole, fraction) = mantissa.split_once('.').ok_or_else(fold_error)?;
473 if whole.len() != 1 || fraction.is_empty() || fraction.len() > 13 {
474 return Err(fold_error());
475 }
476 let significand =
477 u64::from_str_radix(&format!("{whole}{fraction}"), 16).map_err(|_| fold_error())?;
478 let exponent = exponent.parse::<i32>().map_err(|_| fold_error())?;
479 if !(-1022..=1023).contains(&exponent) || significand > (1u64 << 53) - 1 {
480 return Err(fold_error());
481 }
482 let value = (significand as f64 / (1u64 << (4 * fraction.len())) as f64) * 2f64.powi(exponent);
483 let value = if negative { -value } else { value } as f32;
484 if !value.is_finite() {
485 return Err(fold_error());
486 }
487 Ok(value)
488}
489
490fn view_key(
491 value: &serde_json::Value,
492 material: &PublicationMaterial,
493) -> Result<(u64, u64), SemanticTransitionError> {
494 let fields = schema_array(&value["layout"])?;
495 if fields.len() != 8 {
496 return Err(fold_error());
497 }
498 let word = |i: usize| fields[i].as_u64().ok_or_else(fold_error);
499 let key = (word(0)?, word(1)?);
500 let layout = material.layouts.get(&key).ok_or_else(fold_error)?;
501 if [
502 layout.role,
503 layout.index,
504 layout.element_bytes,
505 layout.scalar_type,
506 layout.rank,
507 layout.logical_axis,
508 ] != [word(0)?, word(1)?, word(2)?, word(3)?, word(4)?, word(5)?]
509 || !matches!(key.0, 18..=25)
510 {
511 return Err(fold_error());
512 }
513 for (field, expected) in [(6, layout.dimensions), (7, layout.strides_bytes)] {
514 let actual = schema_array(&fields[field])?;
515 if actual.len() != 4
516 || actual
517 .iter()
518 .zip(expected)
519 .any(|(v, n)| v.as_u64() != Some(n))
520 {
521 return Err(fold_error());
522 }
523 }
524 Ok(key)
525}
526
527impl LearningFoldPlan {
528 fn prepare(
529 material: &PublicationMaterial,
530 transition: &SemanticLearningPhaseTransition,
531 ) -> Result<Self, SemanticTransitionError> {
532 let record = material
533 .ranges
534 .iter()
535 .find(|r| (r.range.role, r.range.index) == (44, 0))
536 .ok_or_else(fold_error)?;
537 let schema = retained_model_schema(&record.bytes, material.contract.model_contract_layout)?
538 .ok_or_else(fold_error)?;
539 let model = &schema["model"];
540 let physical = model["physical"].as_object().ok_or_else(fold_error)?;
541 let learning = &model["learning"];
542 let views = schema_array(&learning["views"])?;
543 let keys = views
544 .iter()
545 .map(|v| view_key(v, material))
546 .collect::<Result<Vec<_>, _>>()?;
547 let expected = material
548 .layouts
549 .keys()
550 .filter(|(role, _)| matches!(role, 18..=25))
551 .copied()
552 .collect::<BTreeSet<_>>();
553 if keys.iter().copied().collect::<BTreeSet<_>>() != expected || keys.len() != expected.len()
554 {
555 return Err(fold_error());
556 }
557 let mut allocation_classes = BTreeMap::new();
558 let mut storage_classes = BTreeMap::new();
559 for (view, key) in views.iter().zip(&keys) {
560 let geometry = &view["geometry"];
561 let layout = material.layouts[key];
562 let native_view = material
563 .model_memory
564 .views
565 .iter()
566 .find(|v| (v.role, v.index) == *key)
567 .ok_or_else(fold_error)?;
568 let storage = &material.model_memory.storages[native_view.storage as usize];
569 let word = |name: &str| geometry[name].as_u64().ok_or_else(fold_error);
570 let allocation = geometry["allocation"].as_str().ok_or_else(fold_error)?;
571 let storage_class = geometry["storage"].as_str().ok_or_else(fold_error)?;
572 let dimensions = schema_array(&geometry["shape"])?;
573 let strides = schema_array(&geometry["stride"])?;
574 let dtype = match geometry["dtype"].as_str() {
575 Some("torch.uint8") => (1, 1),
576 Some("torch.uint32") => (2, 4),
577 Some("torch.uint64") => (3, 8),
578 Some("torch.float16") => (4, 2),
579 Some("torch.bfloat16") => (5, 2),
580 Some("torch.float32") => (6, 4),
581 Some("torch.int64") => (7, 8),
582 Some("torch.bool") => (8, 1),
583 _ => return Err(fold_error()),
584 };
585 if dimensions.len() != layout.rank as usize
586 || dtype != (layout.scalar_type, layout.element_bytes)
587 || strides.len() != dimensions.len()
588 || dimensions
589 .iter()
590 .zip(layout.dimensions)
591 .any(|(d, n)| d.as_u64() != Some(n))
592 || strides.iter().zip(layout.strides_bytes).any(|(s, n)| {
593 s.as_u64().and_then(|s| s.checked_mul(layout.element_bytes)) != Some(n)
594 })
595 || word("offset")?.checked_mul(layout.element_bytes)
596 != Some(native_view.byte_offset)
597 || word("span_bytes")? != storage.span_bytes
598 || word("allocation_byte_offset")? != storage.byte_offset
599 || word("allocation_span_bytes")?
600 != material.model_memory.allocation_bytes[storage.allocation as usize]
601 || allocation_classes
602 .insert(allocation, storage.allocation)
603 .is_some_and(|a| a != storage.allocation)
604 || storage_classes
605 .insert(storage_class, native_view.storage)
606 .is_some_and(|s| s != native_view.storage)
607 {
608 return Err(fold_error());
609 }
610 }
611 if allocation_classes
612 .values()
613 .copied()
614 .collect::<BTreeSet<_>>()
615 .len()
616 != allocation_classes.len()
617 || storage_classes
618 .values()
619 .copied()
620 .collect::<BTreeSet<_>>()
621 .len()
622 != storage_classes.len()
623 {
624 return Err(fold_error());
625 }
626 let key_at = |value: &serde_json::Value| {
627 value
628 .as_u64()
629 .and_then(|i| usize::try_from(i).ok())
630 .and_then(|i| keys.get(i))
631 .copied()
632 .ok_or_else(fold_error)
633 };
634 let mut aliases = BTreeMap::new();
635 let mut owner_aliases = BTreeMap::new();
636 let mut masters = BTreeMap::new();
637 let mut owners = BTreeSet::new();
638 if key_at(&learning["shared"]["phase"])? != (23, transition.phase_index)
639 || key_at(&learning["shared"]["completed_updates"])?
640 != (23, transition.completed_updates_index)
641 {
642 return Err(fold_error());
643 }
644 for leaf in schema_array(&learning["leaves"])? {
645 let key = key_at(&leaf["effective"])?;
646 let view = &views[leaf["effective"].as_u64().ok_or_else(fold_error)? as usize];
647 let owner = view["owner"].as_str().ok_or_else(fold_error)?;
648 let entry = physical.get(owner).ok_or_else(fold_error)?;
649 if !owners.insert(owner) {
650 return Err(fold_error());
651 }
652 if entry["kind"].as_str() != Some("parameter")
653 || entry["geometry"] != view["geometry"]
654 || entry["aliases"] != leaf["aliases"]
655 || !(18..=20).contains(&key.0)
656 {
657 return Err(fold_error());
658 }
659 let names = schema_array(&leaf["aliases"])?;
660 if names.is_empty() || owner_aliases.insert(key, names.clone()).is_some() {
661 return Err(fold_error());
662 }
663 for name in names {
664 if aliases
665 .insert(name.as_str().ok_or_else(fold_error)?.to_owned(), key)
666 .is_some()
667 {
668 return Err(fold_error());
669 }
670 }
671 if !leaf["master"].is_null() && masters.insert(key_at(&leaf["master"])?, key).is_some()
672 {
673 return Err(fold_error());
674 }
675 }
676 for buffer in schema_array(&learning["buffers"])? {
677 let key = key_at(&buffer["tensor"])?;
678 let view = &views[buffer["tensor"].as_u64().ok_or_else(fold_error)? as usize];
679 let entry = physical
680 .get(view["owner"].as_str().ok_or_else(fold_error)?)
681 .ok_or_else(fold_error)?;
682 if !owners.insert(view["owner"].as_str().ok_or_else(fold_error)?) {
683 return Err(fold_error());
684 }
685 if entry["kind"].as_str() != Some("buffer")
686 || entry["geometry"] != view["geometry"]
687 || entry["aliases"] != buffer["aliases"]
688 {
689 return Err(fold_error());
690 }
691 for name in schema_array(&buffer["aliases"])? {
692 if aliases
693 .insert(name.as_str().ok_or_else(fold_error)?.to_owned(), key)
694 .is_some()
695 {
696 return Err(fold_error());
697 }
698 }
699 }
700 if owners != physical.keys().map(String::as_str).collect::<BTreeSet<_>>() {
701 return Err(fold_error());
702 }
703 let state = schema_array(&model["roots"]["adapter"]["modules"][""][1])?;
704 let mut fields = BTreeMap::new();
705 for field in state {
706 let field = schema_array(field)?;
707 if field.len() != 2
708 || fields
709 .insert(field[0].as_str().ok_or_else(fold_error)?, &field[1])
710 .is_some()
711 {
712 return Err(fold_error());
713 }
714 }
715 let field = |name: &str| fields.get(name).copied().ok_or_else(fold_error);
716 let targets = schema_array(tagged(field("target_names")?, "tuple")?)?;
717 let shapes = schema_array(tagged(field("target_shapes")?, "tuple")?)?;
718 let rank = tagged(field("rank")?, "int")?
719 .as_u64()
720 .filter(|r| *r > 0)
721 .ok_or_else(fold_error)?;
722 let scale = original_float(field("scale")?)?;
723 if targets.is_empty()
724 || targets.len() != shapes.len()
725 || original_float(field("dropout")?)? != 0.0
726 || tagged(field("initialization_identity")?, "str")?
727 .as_str()
728 .is_none_or(str::is_empty)
729 {
730 return Err(fold_error());
731 }
732 let mut assignments = Vec::new();
733 for operation in &transition.recipe {
734 let key = operation.key();
735 let source = match *operation {
736 SemanticLearningCopyReset::MasterFromEffective {
737 effective_role,
738 effective_index,
739 ..
740 } => {
741 let effective = (effective_role, effective_index);
742 if masters.get(&key) != Some(&effective) {
743 return Err(fold_error());
744 }
745 let layout = material.layouts[&key];
746 let original = material.layouts.get(&effective).ok_or_else(fold_error)?;
747 if key.0 != 21
748 || layout.scalar_type != 6
749 || layout.element_bytes != 4
750 || original.scalar_type != 5
751 || original.element_bytes != 2
752 || layout.rank != original.rank
753 || layout.dimensions != original.dimensions
754 {
755 return Err(fold_error());
756 }
757 Some(effective)
758 }
759 SemanticLearningCopyReset::Zero { role, .. } if !matches!(role, 21 | 24 | 25) => {
760 return Err(fold_error())
761 }
762 SemanticLearningCopyReset::Phase { index } if index != transition.phase_index => {
763 return Err(fold_error())
764 }
765 _ => None,
766 };
767 assignments.push(FoldAssignment {
768 key,
769 mode: match operation {
770 SemanticLearningCopyReset::Preserve { .. } => 0,
771 SemanticLearningCopyReset::Zero { .. } => 1,
772 SemanticLearningCopyReset::MasterFromEffective { .. } => 2,
773 SemanticLearningCopyReset::Phase { .. } => 3,
774 },
775 source,
776 factors: None,
777 });
778 }
779 if masters
780 .keys()
781 .any(|key| !assignments.iter().any(|a| a.key == *key && a.mode == 2))
782 {
783 return Err(fold_error());
784 }
785 for leaf in schema_array(&learning["leaves"])? {
786 for field in ["m", "v", "t", "gradient", "presence"] {
787 if !leaf[field].is_null() {
788 let key = key_at(&leaf[field])?;
789 if !assignments.iter().any(|a| a.key == key && a.mode == 1) {
790 return Err(fold_error());
791 }
792 }
793 }
794 }
795 let accumulation = key_at(&learning["shared"]["accumulation"])?;
796 if !assignments
797 .iter()
798 .any(|a| a.key == accumulation && a.mode == 1)
799 {
800 return Err(fold_error());
801 }
802 let mut replaced = BTreeSet::new();
803 let mut adapted_aliases = BTreeSet::new();
804 let mut previous = None;
805 for (index, (target, shape)) in targets.iter().zip(shapes).enumerate() {
806 let target = tagged(target, "str")?.as_str().ok_or_else(fold_error)?;
807 if previous.is_some_and(|p| p >= target) {
808 return Err(fold_error());
809 }
810 previous = Some(target);
811 let shape = schema_array(tagged(shape, "tuple")?)?;
812 if shape.len() != 2 {
813 return Err(fold_error());
814 }
815 let rows = tagged(&shape[0], "int")?
816 .as_u64()
817 .filter(|n| *n > 0)
818 .ok_or_else(fold_error)?;
819 let columns = tagged(&shape[1], "int")?
820 .as_u64()
821 .filter(|n| *n > 0)
822 .ok_or_else(fold_error)?;
823 let names = [
824 format!("model.{target}.weight"),
825 format!("adapter.residuals.{index}.down"),
826 format!("adapter.residuals.{index}.up"),
827 format!("adapter.residuals.{index}.neutral_down"),
828 ];
829 let actual = names
830 .iter()
831 .map(|name| aliases.get(name).copied().ok_or_else(fold_error))
832 .collect::<Result<Vec<_>, _>>()?;
833 let [weight, down, up, neutral] = actual.as_slice() else {
834 return Err(fold_error());
835 };
836 for (key, dims, role) in [
837 (*weight, [rows, columns, 0, 0], 18),
838 (*down, [rank, columns, 0, 0], 19),
839 (*up, [rows, rank, 0, 0], 19),
840 (*neutral, [rank, columns, 0, 0], 19),
841 ] {
842 let layout = material.layouts[&key];
843 if key.0 != role
844 || layout.rank != 2
845 || layout.dimensions != dims
846 || !matches!((layout.scalar_type, layout.element_bytes), (5, 2) | (6, 4))
847 || (key == *neutral && layout.scalar_type != material.layouts[down].scalar_type)
848 || !transition
849 .recipe
850 .contains(&SemanticLearningCopyReset::Preserve {
851 role: key.0,
852 index: key.1,
853 })
854 {
855 return Err(fold_error());
856 }
857 }
858 for key in [*weight, *down, *up] {
859 replaced.insert(key);
860 }
861 for name in &names[..3] {
862 adapted_aliases.insert(name.clone());
863 }
864 assignments.push(FoldAssignment {
865 key: *weight,
866 mode: 4,
867 source: None,
868 factors: Some((*up, *down)),
869 });
870 assignments.push(FoldAssignment {
871 key: *down,
872 mode: 5,
873 source: Some(*neutral),
874 factors: None,
875 });
876 assignments.push(FoldAssignment {
877 key: *up,
878 mode: 1,
879 source: None,
880 factors: None,
881 });
882 }
883 assignments.retain(|a| {
884 a.mode != 0
885 || !replaced.contains(&a.key)
886 || owner_aliases.get(&a.key).is_some_and(|names| {
887 names
888 .iter()
889 .any(|n| n.as_str().is_none_or(|n| !adapted_aliases.contains(n)))
890 })
891 });
892 Ok(Self {
893 assignments,
894 scale_bits: scale.to_bits(),
895 phase: transition.target as u64,
896 })
897 }
898}
899
900#[repr(C)]
901#[derive(Clone, Copy, Default)]
902pub(super) struct FoldDeviceAssignment {
903 original: u64,
904 scratch: u64,
905 expected: u64,
906 source: u64,
907 up: u64,
908 down: u64,
909 count: u64,
910 mode: u64,
911 phase: u64,
912 scale_bits: u64,
913 serial_scatter: u64,
914 layout: SemanticTensorLayout,
915 source_layout: SemanticTensorLayout,
916 up_layout: SemanticTensorLayout,
917 down_layout: SemanticTensorLayout,
918}
919
920unsafe impl DeviceRepr for FoldDeviceAssignment {}
921const _: () = assert!(size_of::<FoldDeviceAssignment>() == 536);
922
923fn packed_layout(
924 mut layout: SemanticTensorLayout,
925) -> Result<SemanticTensorLayout, SemanticTransitionError> {
926 let mut stride = layout.element_bytes;
927 for axis in (0..layout.rank as usize).rev() {
928 layout.strides_bytes[axis] = stride;
929 stride = stride
930 .checked_mul(layout.dimensions[axis])
931 .ok_or(SemanticTransitionError::GenerationExhausted)?;
932 }
933 Ok(layout)
934}
935
936impl SemanticTransitionSession {
937 pub(super) fn apply_learning_fold(
938 &mut self,
939 plan: &LearningFoldPlan,
940 ) -> Result<(), SemanticTransitionError> {
941 let storage = Arc::clone(
942 self.publication
943 .as_ref()
944 .ok_or(SemanticTransitionError::NotBound)?,
945 );
946 let pointer = |key: (u64, u64), bank: usize| {
947 let (allocation, offset) = storage.model_memory.location(key.0, key.1)?;
948 storage.allocations[storage.model_slots[allocation][bank]]
949 .device_ptr_value()
950 .checked_add(offset as u64)
951 .ok_or(SemanticTransitionError::GenerationExhausted)
952 };
953 let mut outputs = Vec::new();
954 let mut effective_outputs = BTreeMap::new();
955 for assignment in &plan.assignments {
956 let layout = storage.layouts[&assignment.key];
957 let count =
958 layout.dimensions[..layout.rank as usize]
959 .iter()
960 .try_fold(1u64, |n, &d| {
961 n.checked_mul(d)
962 .ok_or(SemanticTransitionError::GenerationExhausted)
963 })?;
964 let bytes = count
965 .checked_mul(layout.element_bytes)
966 .and_then(|n| usize::try_from(n).ok())
967 .ok_or(SemanticTransitionError::GenerationExhausted)?;
968 let output = if assignment.mode == 0 || bytes == 0 {
969 None
970 } else {
971 Some(allocate_publication::<u8>(&self.provider, bytes)?)
972 };
973 if matches!(assignment.mode, 1 | 4 | 5) {
974 effective_outputs.entry(assignment.key).or_insert_with(|| {
975 output
976 .as_ref()
977 .map_or(0, TrackedCudaSlice::device_ptr_value)
978 });
979 }
980 outputs.push(output);
981 }
982 let mut descriptors = Vec::new();
983 for (assignment, output) in plan.assignments.iter().zip(&outputs) {
984 let layout = storage.layouts[&assignment.key];
985 let mut descriptor = FoldDeviceAssignment {
986 original: pointer(assignment.key, 0)?,
987 scratch: pointer(assignment.key, 1)?,
988 expected: output
989 .as_ref()
990 .map_or(0, TrackedCudaSlice::device_ptr_value),
991 mode: assignment.mode,
992 phase: plan.phase,
993 scale_bits: u64::from(plan.scale_bits),
994 layout,
995 ..Default::default()
996 };
997 descriptor.count =
998 layout.dimensions[..layout.rank as usize]
999 .iter()
1000 .try_fold(1u64, |n, &d| {
1001 n.checked_mul(d)
1002 .ok_or(SemanticTransitionError::GenerationExhausted)
1003 })?;
1004 if let Some(source) = assignment.source {
1005 descriptor.source_layout = storage.layouts[&source];
1006 if assignment.mode == 2 && effective_outputs.contains_key(&source) {
1007 descriptor.source = effective_outputs[&source];
1008 descriptor.source_layout = packed_layout(descriptor.source_layout)?;
1009 } else {
1010 descriptor.source = pointer(source, 0)?;
1011 }
1012 }
1013 if let Some((up, down)) = assignment.factors {
1014 descriptor.up = pointer(up, 0)?;
1015 descriptor.down = pointer(down, 0)?;
1016 descriptor.up_layout = storage.layouts[&up];
1017 descriptor.down_layout = storage.layouts[&down];
1018 }
1019 let mut axes = (0..layout.rank as usize)
1023 .filter(|&i| layout.dimensions[i] > 1)
1024 .collect::<Vec<_>>();
1025 axes.sort_unstable_by_key(|&i| layout.strides_bytes[i]);
1026 let mut span = layout.element_bytes;
1027 for axis in axes {
1028 if layout.strides_bytes[axis] < span {
1029 descriptor.serial_scatter = 1;
1030 }
1031 span = span
1032 .checked_add(
1033 (layout.dimensions[axis] - 1)
1034 .checked_mul(layout.strides_bytes[axis])
1035 .ok_or(SemanticTransitionError::GenerationExhausted)?,
1036 )
1037 .ok_or(SemanticTransitionError::GenerationExhausted)?;
1038 }
1039 descriptors.push(descriptor);
1040 }
1041 let status = allocate_publication::<u64>(&self.provider, 1)?;
1042 upload_publication(&self.provider, &[0u64], &status)?;
1043 let execute = self
1044 .provider
1045 .device()
1046 .inner()
1047 .get_func("xlog_semantic_transition", "semantic_learning_fold")
1048 .ok_or_else(|| runtime_error("kernel lookup", "learning absorption unavailable"))?;
1049 let mut recorder = self.domain.new_strict_recorder();
1050 storage.record(&mut recorder);
1051 recorder.read_write(&status);
1052 for output in outputs.iter().flatten() {
1053 recorder.read_write(output);
1054 }
1055 enqueue_recorded(&self.domain, &mut self.poisoned, recorder, |enqueue| {
1056 for stage in 0..4u64 {
1059 for descriptor in &descriptors {
1060 if descriptor.count == 0
1061 || (stage == 0 && matches!(descriptor.mode, 0 | 2))
1062 || (stage == 1 && descriptor.mode != 2)
1063 || (stage == 2 && descriptor.mode == 0)
1064 {
1065 continue;
1066 }
1067 let blocks = descriptor.count.div_ceil(256).min(65535) as u32;
1068 unsafe {
1072 execute.clone().launch_in(
1073 enqueue,
1074 LaunchConfig {
1075 grid_dim: (blocks, 1, 1),
1076 block_dim: (256, 1, 1),
1077 shared_mem_bytes: 0,
1078 },
1079 (*descriptor, stage, status.device_ptr_value()),
1080 )
1081 }
1082 .map_err(|error| XlogError::Kernel(error.to_string()))?;
1083 }
1084 }
1085 Ok::<(), XlogError>(())
1086 })?;
1087 wait_on_stream(
1088 &self.stream,
1089 &mut self.poisoned,
1090 &mut self.stream_waits,
1091 "cold adapter absorption and physical alias check",
1092 CudaStream::synchronize,
1093 )?;
1094 if self.publication_read(status.view())?[0] != 0 {
1095 return Err(publication_input_error("adapter absorption encountered nonfinite numerics or conflicting physical alias bytes"));
1096 }
1097 let mut recorder = self.domain.new_strict_recorder();
1098 storage.record(&mut recorder);
1099 enqueue_recorded(&self.domain, &mut self.poisoned, recorder, |enqueue| {
1100 for slots in &storage.model_slots {
1101 let destination = &storage.allocations[slots[0]];
1102 if destination.is_empty() {
1103 continue;
1104 }
1105 unsafe {
1108 sys::cuMemcpyDtoDAsync_v2(
1109 destination.device_ptr_value(),
1110 storage.allocations[slots[1]].device_ptr_value(),
1111 destination.len(),
1112 enqueue.stream().cu_stream(),
1113 )
1114 }
1115 .result()
1116 .map_err(|error| XlogError::Kernel(error.to_string()))?;
1117 }
1118 Ok::<(), XlogError>(())
1119 })?;
1120 wait_on_stream(
1121 &self.stream,
1122 &mut self.poisoned,
1123 &mut self.stream_waits,
1124 "cold absorbed candidate assignment",
1125 CudaStream::synchronize,
1126 )
1127 }
1128}
1129
1130pub(super) fn encode_history(
1131 history: &[SemanticLearningPhaseRecord],
1132 bytes: &mut Vec<u8>,
1133) -> Result<(), SemanticTransitionError> {
1134 material_u32(
1135 bytes,
1136 u32::try_from(history.len()).map_err(|_| SemanticTransitionError::GenerationExhausted)?,
1137 );
1138 for record in history {
1139 for value in [
1140 record.source as u64,
1141 record.target as u64,
1142 record.phase_index,
1143 record.completed_updates_index,
1144 record.predecessor.word,
1145 record.model_generation,
1146 record.completed_updates,
1147 ] {
1148 material_u64(bytes, value);
1149 }
1150 for digest in [
1151 record.predecessor.instance,
1152 record.predecessor.logical_digest,
1153 record.predecessor.state_digest,
1154 record.recipe_digest,
1155 ] {
1156 bytes.extend_from_slice(digest.as_bytes());
1157 }
1158 material_u32(
1159 bytes,
1160 u32::try_from(record.recipe.len())
1161 .map_err(|_| SemanticTransitionError::GenerationExhausted)?,
1162 );
1163 for operation in &record.recipe {
1164 let (role, index) = operation.key();
1165 let (code, source_role, source_index) = match *operation {
1166 SemanticLearningCopyReset::Preserve { .. } => (0, 0, 0),
1167 SemanticLearningCopyReset::Zero { .. } => (1, 0, 0),
1168 SemanticLearningCopyReset::MasterFromEffective {
1169 effective_role,
1170 effective_index,
1171 ..
1172 } => (2, effective_role, effective_index),
1173 SemanticLearningCopyReset::Phase { .. } => (3, 0, 0),
1174 };
1175 for value in [role, index, code, source_role, source_index] {
1176 material_u64(bytes, value);
1177 }
1178 }
1179 material_bytes(bytes, &record.admission).map_err(SemanticTransitionError::Semantic)?;
1180 }
1181 Ok(())
1182}
1183
1184pub(super) fn decode_history(
1185 reader: &mut SemanticMaterialReader<'_>,
1186) -> Result<Vec<SemanticLearningPhaseRecord>, SemanticTransitionError> {
1187 let count = reader
1188 .count(7 * 8 + 4 * 32 + 4)
1189 .map_err(SemanticTransitionError::Semantic)?;
1190 let mut history = Vec::with_capacity(count);
1191 for _ in 0..count {
1192 let mut next = || reader.u64().map_err(SemanticTransitionError::Semantic);
1193 let source = SemanticLearningPhase::from_code(next()?)?;
1194 let target = SemanticLearningPhase::from_code(next()?)?;
1195 let phase_index = next()?;
1196 let completed_updates_index = next()?;
1197 let word = next()?;
1198 let model_generation = next()?;
1199 let completed_updates = next()?;
1200 let mut identity = || -> Result<Identity256, SemanticTransitionError> {
1201 Ok(Identity256::from_bytes(
1202 reader
1203 .take(32)
1204 .map_err(SemanticTransitionError::Semantic)?
1205 .try_into()
1206 .map_err(|_| SemanticTransitionError::ObservationMismatch)?,
1207 ))
1208 };
1209 let instance = identity()?;
1210 let logical_digest = identity()?;
1211 let state_digest = identity()?;
1212 let recipe_digest = identity()?;
1213 let recipe_count = reader
1214 .count(5 * 8)
1215 .map_err(SemanticTransitionError::Semantic)?;
1216 let mut recipe = Vec::with_capacity(recipe_count);
1217 for _ in 0..recipe_count {
1218 let role = reader.u64().map_err(SemanticTransitionError::Semantic)?;
1219 let index = reader.u64().map_err(SemanticTransitionError::Semantic)?;
1220 let code = reader.u64().map_err(SemanticTransitionError::Semantic)?;
1221 let source_role = reader.u64().map_err(SemanticTransitionError::Semantic)?;
1222 let source_index = reader.u64().map_err(SemanticTransitionError::Semantic)?;
1223 recipe.push(match code {
1224 0 if source_role == 0 && source_index == 0 => {
1225 SemanticLearningCopyReset::Preserve { role, index }
1226 }
1227 1 if source_role == 0 && source_index == 0 => {
1228 SemanticLearningCopyReset::Zero { role, index }
1229 }
1230 2 if role == 21 => SemanticLearningCopyReset::MasterFromEffective {
1231 index,
1232 effective_role: source_role,
1233 effective_index: source_index,
1234 },
1235 3 if role == 23 && source_role == 0 && source_index == 0 => {
1236 SemanticLearningCopyReset::Phase { index }
1237 }
1238 _ => {
1239 return Err(publication_input_error(
1240 "checkpoint learning recipe has an invalid operation",
1241 ))
1242 }
1243 });
1244 }
1245 let admission = reader
1246 .bytes()
1247 .map_err(SemanticTransitionError::Semantic)?
1248 .to_vec();
1249 history.push(SemanticLearningPhaseRecord {
1250 source,
1251 target,
1252 phase_index,
1253 completed_updates_index,
1254 predecessor: SemanticPublishedIdentity {
1255 instance,
1256 word,
1257 logical_digest,
1258 state_digest,
1259 },
1260 model_generation,
1261 completed_updates,
1262 recipe_digest,
1263 recipe,
1264 admission,
1265 });
1266 }
1267 Ok(history)
1268}
1269
1270pub(super) fn validate_history(
1271 material: &PublicationMaterial,
1272) -> Result<(), SemanticTransitionError> {
1273 let mut previous: Option<&SemanticLearningPhaseRecord> = None;
1274 for record in &material.learning_phases {
1275 let keys = record
1276 .recipe
1277 .iter()
1278 .map(SemanticLearningCopyReset::key)
1279 .collect::<Vec<_>>();
1280 let recipe = SemanticLearningPhaseTransition {
1281 source: record.source,
1282 target: record.target,
1283 phase_index: record.phase_index,
1284 completed_updates_index: record.completed_updates_index,
1285 recipe: record.recipe.clone(),
1286 admission: record.admission.clone(),
1287 };
1288 if record.admission.is_empty()
1289 || record.phase_index == record.completed_updates_index
1290 || keys.windows(2).any(|pair| pair[0] >= pair[1])
1291 || keys.iter().any(|(role, _)| !(18..=25).contains(role))
1292 || !record.recipe.contains(&SemanticLearningCopyReset::Phase {
1293 index: record.phase_index,
1294 })
1295 || !record
1296 .recipe
1297 .contains(&SemanticLearningCopyReset::Preserve {
1298 role: 23,
1299 index: record.completed_updates_index,
1300 })
1301 || record.recipe.iter().any(|operation| match operation {
1302 SemanticLearningCopyReset::Zero { role, .. } => !matches!(role, 21 | 24 | 25),
1303 SemanticLearningCopyReset::MasterFromEffective { effective_role, .. } => {
1304 !(18..=20).contains(effective_role)
1305 }
1306 SemanticLearningCopyReset::Phase { index } => *index != record.phase_index,
1307 SemanticLearningCopyReset::Preserve { .. } => false,
1308 })
1309 || record.completed_updates > i64::MAX as u64
1310 || record.model_generation == 0
1311 || record.model_generation > material.bank.header.model_generation
1312 || record.predecessor.instance == Identity256::default()
1313 || record.recipe_digest == Identity256::default()
1314 || record.recipe_digest != recipe.recipe_digest()
1315 || !matches!(
1316 (record.source, record.target),
1317 (
1318 SemanticLearningPhase::Alignment,
1319 SemanticLearningPhase::Fast
1320 ) | (
1321 SemanticLearningPhase::Fast,
1322 SemanticLearningPhase::Consolidation
1323 ) | (
1324 SemanticLearningPhase::Consolidation,
1325 SemanticLearningPhase::Fast
1326 )
1327 )
1328 || previous.is_none() && record.source != SemanticLearningPhase::Alignment
1329 || previous.is_some_and(|prior| {
1330 prior.target != record.source
1331 || prior.phase_index != record.phase_index
1332 || prior.completed_updates_index != record.completed_updates_index
1333 || prior.completed_updates > record.completed_updates
1334 || prior.model_generation > record.model_generation
1335 })
1336 {
1337 return Err(publication_input_error(
1338 "checkpoint learning-phase lineage is inconsistent",
1339 ));
1340 }
1341 previous = Some(record);
1342 }
1343 if let Some(record) = previous {
1344 if scalar_i64(material, (23, record.phase_index))? != record.target as i64
1345 || scalar_i64(material, (23, record.completed_updates_index))?
1346 < record.completed_updates as i64
1347 {
1348 return Err(publication_input_error(
1349 "selected learning values differ from their retained phase lineage",
1350 ));
1351 }
1352 }
1353 Ok(())
1354}