1use std::sync::Arc;
7
8use cubecl::frontend::{AsMutExpand, AsRefExpand, CubeDebug, ExpandTypeClone, IntoExpand, IntoMut};
9use cubecl::ir::{ExpandValue, Scope};
10use cubecl::prelude::*;
11use cubecl::quant::scheme::QuantValue;
12use cubecl::unexpanded;
13use cubecl_std::tensor::layout::Coords2d;
14
15use crate::stage::pipeline::payload::base::{StageOperand, StageSpec, stage_one};
16use crate::*;
17
18#[derive(Clone)]
22pub struct Factor;
23
24#[derive(Clone, Default)]
25pub struct FactorExpand {
26 scale: Option<Arc<TileExpand>>,
28}
29
30impl Factor {
31 pub(crate) fn none() -> Factor {
33 unexpanded!()
34 }
35
36 pub(crate) fn at(&self, _step: &Step) -> Factor {
38 unexpanded!()
39 }
40
41 #[allow(dead_code)] pub(crate) fn placed_at(&self, _axis: Axis, _coord: u32) -> Factor {
45 unexpanded!()
46 }
47
48 #[allow(dead_code)] pub(crate) fn run_within(&self, _axis: Axis, _from: u32, _until: u32) -> Factor {
52 unexpanded!()
53 }
54
55 pub(crate) fn varies_along(&self, _axis: Axis) -> bool {
57 unexpanded!()
58 }
59
60 pub(crate) fn at_coords(&self, _coords: &Coords<u32>, _values: Space, _guard: Guard) -> f32 {
64 unexpanded!()
65 }
66
67 pub(crate) fn whole(&self) -> f32 {
70 unexpanded!()
71 }
72
73 pub(crate) fn scaled(&self) -> comptime_type!(bool) {
75 unexpanded!()
76 }
77
78 pub(crate) fn holds_words(&self) -> comptime_type!(bool) {
80 unexpanded!()
81 }
82
83 pub(crate) fn innermost_scale(&self) -> Tile {
86 unexpanded!()
87 }
88
89 pub(crate) fn coarse(&self) -> f32 {
91 unexpanded!()
92 }
93
94 pub(crate) fn depth(&self) -> comptime_type!(usize) {
96 unexpanded!()
97 }
98
99 pub(crate) fn inner_stored(&self) -> comptime_type!(Option<Packing>) {
101 unexpanded!()
102 }
103
104 pub(crate) fn word_at(&self, _coords: &Coords<u32>, _values: Space) -> u32 {
106 unexpanded!()
107 }
108
109 #[allow(dead_code)] pub(crate) fn write_at<N: Size>(
115 &self,
116 _coords: &Coords<u32>,
117 _values: Space,
118 _scales: Vector<f32, N>,
119 ) {
120 unexpanded!()
121 }
122
123 #[allow(dead_code)] #[allow(clippy::extra_unused_type_parameters)] pub(crate) fn calibrate_from<F: Float>(&self, _src: &Tile, _coarse: f32, _value: QuantValue) {
132 unexpanded!()
133 }
134
135 pub(crate) fn __expand_none(_scope: &Scope) -> FactorExpand {
136 FactorExpand::default()
137 }
138}
139
140#[allow(clippy::arc_with_non_send_sync)]
142impl FactorExpand {
143 pub(crate) fn of(scale: &TileExpand) -> Self {
145 FactorExpand {
146 scale: Some(Arc::new(scale.clone())),
147 }
148 }
149
150 pub(crate) fn innermost_tile(&self) -> Option<TileExpand> {
152 self.scale.as_deref().map(|scale| {
153 let mut alone = scale.clone();
154 alone.factor = FactorExpand::default();
155 alone
156 })
157 }
158
159 fn chain(&self) -> impl Iterator<Item = &TileExpand> {
161 std::iter::successors(self.scale.as_deref(), |scale| scale.factor.scale.as_deref())
162 }
163
164 fn varies_outside(scope: &Scope, scale: &TileExpand, space: &Space) -> bool {
166 let projection = scale.clone().__expand_projection_method(scope);
167 scale
168 .place
169 .space
170 .axes()
171 .any(|axis| projection.addresses(axis) && !space.contains(axis))
172 }
173
174 pub(crate) fn split_on(&self, scope: &Scope, space: &Space) -> (FactorExpand, FactorExpand) {
178 let Some(scale) = self.scale.as_deref() else {
179 return (FactorExpand::default(), FactorExpand::default());
180 };
181 if !Self::varies_outside(scope, scale, space) {
182 return (FactorExpand::default(), self.clone());
183 }
184 let (kept, taken) = scale.factor.split_on(scope, space);
185 let mut inner = scale.clone();
186 inner.factor = kept;
187 (FactorExpand::of(&inner), taken)
188 }
189
190 pub(crate) fn and(&self, coarser: FactorExpand) -> FactorExpand {
192 match self.scale.as_deref() {
193 None => coarser,
194 Some(scale) => {
195 let mut outer = scale.clone();
196 outer.factor = scale.factor.and(coarser);
197 FactorExpand::of(&outer)
198 }
199 }
200 }
201
202 pub(crate) fn scaled(&self) -> bool {
204 self.scale.is_some()
205 }
206
207 pub(crate) fn depth(&self) -> usize {
209 self.chain().count()
210 }
211
212 pub(crate) fn innermost(&self) -> FactorExpand {
214 match self.innermost_tile() {
215 Some(scale) => FactorExpand::of(&scale),
216 None => FactorExpand::default(),
217 }
218 }
219
220 pub(crate) fn coarse(&self, scope: &Scope) -> NativeExpand<f32> {
222 let mut scaled: NativeExpand<f32> =
223 ExpandValue::constant(1u64.into(), f32::elem_type(scope)).into();
224 for scale in self.chain().skip(1) {
225 let origin = origin_of(scope, &scale.place.space);
226 let one = scale_for(scope, scale, &origin, &scale.place.space, Guard::Checked);
227 scaled = MulExpand::__expand_mul_method(scaled, scope, one);
228 }
229 scaled
230 }
231
232 pub(crate) fn __expand_at_method(&self, scope: &Scope, step: &StepExpand) -> FactorExpand {
233 match self.scale.as_deref() {
234 Some(scale) => FactorExpand::of(&scale.clone().__expand_at_step_method(scope, step)),
236 None => FactorExpand::default(),
237 }
238 }
239
240 pub(crate) fn __expand_placed_at_method(
241 &self,
242 scope: &Scope,
243 axis: Axis,
244 coord: NativeExpand<u32>,
245 ) -> FactorExpand {
246 self.spanning(axis, |scale| {
247 scale.__expand_placed_at_method(scope, axis, coord)
248 })
249 }
250
251 pub(crate) fn __expand_run_within_method(
252 &self,
253 scope: &Scope,
254 axis: Axis,
255 from: NativeExpand<u32>,
256 until: NativeExpand<u32>,
257 ) -> FactorExpand {
258 self.spanning(axis, |scale| {
259 scale.__expand_run_within_method(scope, axis, from, until)
260 })
261 }
262
263 pub(crate) fn spanning(
266 &self,
267 axis: Axis,
268 place: impl Fn(&TileExpand) -> TileExpand,
269 ) -> FactorExpand {
270 match self.scale.as_deref() {
271 None => FactorExpand::default(),
272 Some(scale) if scale.place.space.contains(axis) => FactorExpand::of(&place(scale)),
273 Some(scale) => {
274 let mut kept = scale.clone();
275 kept.factor = scale.factor.spanning(axis, place);
276 FactorExpand::of(&kept)
277 }
278 }
279 }
280
281 pub(crate) fn __expand_at_coords_method(
282 &self,
283 scope: &Scope,
284 coords: &CoordsExpand<u32>,
285 values: Space,
286 guard: Guard,
287 ) -> NativeExpand<f32> {
288 let mut product: Option<NativeExpand<f32>> = None;
289 for scale in self.chain() {
290 assert!(
291 !holds_words(scope, scale),
292 "Factor: these scales are words of four e4m3 block scales, which only the \
293 block-scaled instruction reads; widened one at a time they mean nothing"
294 );
295 let one = scale_for(scope, scale, coords, &values, guard);
296 product = Some(match product {
297 None => one,
298 Some(product) => MulExpand::__expand_mul_method(product, scope, one),
299 });
300 }
301 product.unwrap_or_else(|| ExpandValue::constant(1u64.into(), f32::elem_type(scope)).into())
302 }
303
304 pub(crate) fn __expand_whole_method(&self, scope: &Scope) -> NativeExpand<f32> {
305 let mut product: NativeExpand<f32> =
306 ExpandValue::constant(1u64.into(), f32::elem_type(scope)).into();
307 for scale in self.chain() {
308 let projection = scale.clone().__expand_projection_method(scope);
309 assert!(
310 scale
311 .place
312 .space
313 .axes()
314 .all(|axis| !projection.addresses(axis)),
315 "Factor::whole: a scale that varies across the output is applied where the output \
316 is written through memory, not on the sum's fragments"
317 );
318 let origin = origin_of(scope, &scale.place.space);
319 let one = scale_for(scope, scale, &origin, &scale.place.space, Guard::Checked);
320 product = MulExpand::__expand_mul_method(product, scope, one);
321 }
322 product
323 }
324
325 pub(crate) fn __expand_innermost_scale_method(&self, _scope: &Scope) -> TileExpand {
326 self.innermost_tile()
327 .expect("Tile::quantize_from: the values are written under the block scales they carry")
328 }
329
330 pub(crate) fn __expand_coarse_method(&self, scope: &Scope) -> NativeExpand<f32> {
331 self.coarse(scope)
332 }
333
334 pub(crate) fn __expand_depth_method(&self, _scope: &Scope) -> usize {
335 self.depth()
336 }
337
338 pub(crate) fn __expand_inner_stored_method(&self, scope: &Scope) -> Option<Packing> {
339 self.scale
340 .as_deref()
341 .map(|scale| scale.clone().__expand_packing_method(scope))
342 }
343
344 pub(crate) fn __expand_scaled_method(&self, _scope: &Scope) -> bool {
345 self.scaled()
346 }
347
348 pub(crate) fn __expand_holds_words_method(&self, scope: &Scope) -> bool {
349 self.staged_beside(scope)
350 }
351
352 pub(crate) fn staged_beside(&self, scope: &Scope) -> bool {
356 self.scale
357 .as_deref()
358 .is_some_and(|scale| holds_words(scope, scale))
359 }
360
361 fn beside(&self) -> &TileExpand {
363 let scale = self
364 .scale
365 .as_deref()
366 .expect("Factor: these values carry no scale to stage beside them");
367 assert!(
368 !scale.factor.scaled(),
369 "Factor: values under words of block scales carry no other scale, since the \
370 block-scaled instruction that reads them holds one; take the coarser one out for the \
371 sum (`Tile::factor`)"
372 );
373 scale
374 }
375
376 pub(crate) fn staged(&self, scope: &Scope, spec: &StageSpec) -> FactorExpand {
379 let spec = StageSpec {
380 storage: StageStorage::Strided,
381 width: None,
382 ..spec.clone()
383 };
384 FactorExpand::of(&stage_one::expand(scope, self.beside(), spec))
385 }
386
387 pub(crate) fn fill_from(&self, scope: &Scope, src: &FactorExpand, meeting: &MeetingExpand) {
389 meeting.__expand_fill_method(scope, &mut self.beside().clone(), src.beside());
390 }
391
392 pub(crate) fn stage_operands(&self, scope: &Scope) -> Vec<StageOperand> {
394 let scale = self.beside();
395 vec![StageOperand {
396 delivery: scale.clone().__expand_delivery_method(scope),
397 space: scale.place.space.clone(),
398 keys: Vec::new(),
399 }]
400 }
401
402 pub(crate) fn free_stage(&self, scope: &Scope) {
404 self.beside().clone().__expand_free_stage_method(scope);
405 }
406
407 pub(crate) fn __expand_word_at_method(
408 &self,
409 scope: &Scope,
410 coords: &CoordsExpand<u32>,
411 values: Space,
412 ) -> NativeExpand<u32> {
413 let scale = self
414 .scale
415 .as_deref()
416 .filter(|scale| holds_words(scope, scale))
417 .expect("Factor::word_at: the innermost scales are no words of block scales");
418 scale
419 .clone()
420 .__expand_scale_for_method::<u32>(scope, coords, values, Guard::Checked)
421 }
422
423 pub(crate) fn __expand_write_at_method<N: Size>(
424 &self,
425 scope: &Scope,
426 coords: &CoordsExpand<u32>,
427 values: Space,
428 scales: NativeExpand<Vector<f32, N>>,
429 ) {
430 let mut scale = self
431 .innermost_tile()
432 .expect("Factor::write_at: these values carry no scale to write");
433 scale.__expand_write_scales_for_method::<N>(scope, coords, values, scales);
434 }
435
436 pub(crate) fn __expand_calibrate_from_method<F: Float>(
437 &self,
438 scope: &Scope,
439 src: &TileExpand,
440 coarse: NativeExpand<f32>,
441 value: QuantValue,
442 ) {
443 let mut scale = self
444 .innermost_tile()
445 .expect("Factor::calibrate_from: these values carry no scale to calibrate");
446 scale.__expand_calibrate_method::<F>(scope, src, coarse, value, self.depth() > 1);
447 }
448}
449
450#[cube]
451impl Tile {
452 #[allow(dead_code)] pub(crate) fn write_scales_for<N: Size>(
457 &mut self,
458 coords: &Coords<u32>,
459 #[comptime] values: Space,
460 scales: Vector<f32, N>,
461 ) {
462 let own = self.scale_coords(coords, values);
463 self.write_scales::<N>(&own, scales);
464 }
465
466 pub(crate) fn write_scales<N: Size>(&mut self, start: &Coords<u32>, scales: Vector<f32, N>) {
469 let space = comptime!(self.place.space.clone());
470 let load = self.vector_tile();
471 let n = N::value();
472 comptime!(assert!(
473 load.values() == n,
474 "Factor::write_at: a load of these scales holds {} of them, not {n}",
475 load.values()
476 ));
477 let at = load.index(start, &space);
478 let packing = self.packing();
479 match comptime!(packing) {
480 Packing::Plain => {
481 let define!(Stored) = self.element;
482 let mut out = self.nd_mut::<Stored, N>();
483 out.write(at, Vector::<Stored, N>::cast_from(scales));
484 }
485 Packing::Packed { field } => {
486 let size!(WP) = comptime!(packing.physical(n));
487 let mut words = self.nd_words_mut::<WP>();
488 words.write(at, pack_line::<WP, N>(scales, field));
489 }
490 }
491 }
492}
493
494fn holds_words(scope: &Scope, scale: &TileExpand) -> bool {
500 scale.element == u32::elem_type(scope) && scale.__expand_packing_method(scope) == Packing::Plain
501}
502
503fn scale_for(
506 scope: &Scope,
507 scale: &TileExpand,
508 coords: &CoordsExpand<u32>,
509 values: &Space,
510 guard: Guard,
511) -> NativeExpand<f32> {
512 scale
513 .clone()
514 .__expand_scale_for_method::<f32>(scope, coords, values.clone(), guard)
515}
516
517fn origin_of(scope: &Scope, space: &Space) -> CoordsExpand<u32> {
519 let mut origin = Coords::<u32>::__expand_new(scope);
520 for _ in 0..space.rank() {
521 let zero: NativeExpand<u32> =
522 ExpandValue::constant(0u64.into(), u32::elem_type(scope)).into();
523 origin.__expand_push_method(scope, zero);
524 }
525 origin
526}
527
528#[derive(CubeType, Clone)]
530#[expand(derive(Clone))]
531pub(crate) struct FactorReader {
532 pub(crate) inner: Factor,
534 pub(crate) coarse: f32,
536 #[cube(comptime)]
538 pub(crate) values: Space,
539 #[cube(comptime)]
540 pub(crate) axes: MatrixAxes,
541 #[cube(comptime)]
542 pub(crate) vector_size: usize,
543 #[cube(comptime)]
545 pub(crate) scaled: bool,
546 pub(crate) matrix: usize,
548}
549
550#[cube]
551impl FactorReader {
552 pub(crate) fn apply<E: Numeric, V: Size>(
554 &self,
555 value: Vector<E, V>,
556 pos: Coords2d,
557 ) -> Vector<E, V> {
558 if comptime!(self.scaled) {
559 let (row, col) = pos;
560 let coords = TileMatrix::value_coords(
561 row,
562 col,
563 self.matrix,
564 comptime!(&self.values),
565 comptime!(self.axes),
566 comptime!(self.vector_size),
567 );
568 self.apply_at::<E, V>(value, &coords)
569 } else {
570 value
571 }
572 }
573
574 pub(crate) fn apply_at<E: Numeric, V: Size>(
576 &self,
577 value: Vector<E, V>,
578 coords: &Coords<u32>,
579 ) -> Vector<E, V> {
580 if comptime!(self.scaled) {
581 let scale = self.inner.at_coords(
582 coords,
583 comptime!(self.values.clone()),
584 comptime!(Guard::Checked),
585 );
586 let scaled = Vector::<f32, V>::cast_from(value)
590 * Vector::<f32, V>::cast_from(scale * self.coarse);
591 Vector::<E, V>::cast_from(scaled)
592 } else {
593 value
594 }
595 }
596}
597
598impl FactorExpand {
599 pub(crate) fn __expand_varies_along_method(&self, scope: &Scope, axis: Axis) -> bool {
600 self.scale.as_deref().is_some_and(|scale| {
601 scale
602 .clone()
603 .__expand_projection_method(scope)
604 .addresses(axis)
605 })
606 }
607}
608
609impl CubeType for Factor {
610 type ExpandType = FactorExpand;
611}
612impl IntoExpand for FactorExpand {
613 type Expand = Self;
614 fn into_expand(self, _: &Scope) -> Self {
615 self
616 }
617}
618impl ExpandTypeClone for FactorExpand {
619 fn clone_unchecked(&self) -> Self {
620 self.clone()
621 }
622}
623impl IntoMut for FactorExpand {
624 fn into_mut(self, _: &Scope) -> Self {
625 self
626 }
627}
628impl CubeDebug for FactorExpand {}
629impl AsRefExpand for FactorExpand {
630 fn __expand_ref_method(&self, _: &Scope) -> &Self {
631 self
632 }
633}
634impl AsMutExpand for FactorExpand {
635 fn __expand_ref_mut_method(&mut self, _: &Scope) -> &mut Self {
636 self
637 }
638}