use std::sync::Arc;
use cubecl::frontend::{AsMutExpand, AsRefExpand, CubeDebug, ExpandTypeClone, IntoExpand, IntoMut};
use cubecl::ir::{ExpandValue, Scope};
use cubecl::prelude::*;
use cubecl::quant::scheme::QuantValue;
use cubecl::unexpanded;
use cubecl_std::tensor::layout::Coords2d;
use crate::stage::pipeline::payload::base::{StageOperand, StageSpec, stage_one};
use crate::*;
#[derive(Clone)]
pub struct Factor;
#[derive(Clone, Default)]
pub struct FactorExpand {
scale: Option<Arc<TileExpand>>,
}
impl Factor {
pub(crate) fn none() -> Factor {
unexpanded!()
}
pub(crate) fn at(&self, _step: &Step) -> Factor {
unexpanded!()
}
#[allow(dead_code)] pub(crate) fn placed_at(&self, _axis: Axis, _coord: u32) -> Factor {
unexpanded!()
}
#[allow(dead_code)] pub(crate) fn run_within(&self, _axis: Axis, _from: u32, _until: u32) -> Factor {
unexpanded!()
}
pub(crate) fn varies_along(&self, _axis: Axis) -> bool {
unexpanded!()
}
pub(crate) fn at_coords(&self, _coords: &Coords<u32>, _values: Space, _guard: Guard) -> f32 {
unexpanded!()
}
pub(crate) fn whole(&self) -> f32 {
unexpanded!()
}
pub(crate) fn scaled(&self) -> comptime_type!(bool) {
unexpanded!()
}
pub(crate) fn holds_words(&self) -> comptime_type!(bool) {
unexpanded!()
}
pub(crate) fn innermost_scale(&self) -> Tile {
unexpanded!()
}
pub(crate) fn coarse(&self) -> f32 {
unexpanded!()
}
pub(crate) fn depth(&self) -> comptime_type!(usize) {
unexpanded!()
}
pub(crate) fn inner_stored(&self) -> comptime_type!(Option<Packing>) {
unexpanded!()
}
pub(crate) fn word_at(&self, _coords: &Coords<u32>, _values: Space) -> u32 {
unexpanded!()
}
#[allow(dead_code)] pub(crate) fn write_at<N: Size>(
&self,
_coords: &Coords<u32>,
_values: Space,
_scales: Vector<f32, N>,
) {
unexpanded!()
}
#[allow(dead_code)] #[allow(clippy::extra_unused_type_parameters)] pub(crate) fn calibrate_from<F: Float>(&self, _src: &Tile, _coarse: f32, _value: QuantValue) {
unexpanded!()
}
pub(crate) fn __expand_none(_scope: &Scope) -> FactorExpand {
FactorExpand::default()
}
}
#[allow(clippy::arc_with_non_send_sync)]
impl FactorExpand {
pub(crate) fn of(scale: &TileExpand) -> Self {
FactorExpand {
scale: Some(Arc::new(scale.clone())),
}
}
pub(crate) fn innermost_tile(&self) -> Option<TileExpand> {
self.scale.as_deref().map(|scale| {
let mut alone = scale.clone();
alone.factor = FactorExpand::default();
alone
})
}
fn chain(&self) -> impl Iterator<Item = &TileExpand> {
std::iter::successors(self.scale.as_deref(), |scale| scale.factor.scale.as_deref())
}
fn varies_outside(scope: &Scope, scale: &TileExpand, space: &Space) -> bool {
let projection = scale.clone().__expand_projection_method(scope);
scale
.place
.space
.axes()
.any(|axis| projection.addresses(axis) && !space.contains(axis))
}
pub(crate) fn split_on(&self, scope: &Scope, space: &Space) -> (FactorExpand, FactorExpand) {
let Some(scale) = self.scale.as_deref() else {
return (FactorExpand::default(), FactorExpand::default());
};
if !Self::varies_outside(scope, scale, space) {
return (FactorExpand::default(), self.clone());
}
let (kept, taken) = scale.factor.split_on(scope, space);
let mut inner = scale.clone();
inner.factor = kept;
(FactorExpand::of(&inner), taken)
}
pub(crate) fn and(&self, coarser: FactorExpand) -> FactorExpand {
match self.scale.as_deref() {
None => coarser,
Some(scale) => {
let mut outer = scale.clone();
outer.factor = scale.factor.and(coarser);
FactorExpand::of(&outer)
}
}
}
pub(crate) fn scaled(&self) -> bool {
self.scale.is_some()
}
pub(crate) fn depth(&self) -> usize {
self.chain().count()
}
pub(crate) fn innermost(&self) -> FactorExpand {
match self.innermost_tile() {
Some(scale) => FactorExpand::of(&scale),
None => FactorExpand::default(),
}
}
pub(crate) fn coarse(&self, scope: &Scope) -> NativeExpand<f32> {
let mut scaled: NativeExpand<f32> =
ExpandValue::constant(1u64.into(), f32::elem_type(scope)).into();
for scale in self.chain().skip(1) {
let origin = origin_of(scope, &scale.place.space);
let one = scale_for(scope, scale, &origin, &scale.place.space, Guard::Checked);
scaled = MulExpand::__expand_mul_method(scaled, scope, one);
}
scaled
}
pub(crate) fn __expand_at_method(&self, scope: &Scope, step: &StepExpand) -> FactorExpand {
match self.scale.as_deref() {
Some(scale) => FactorExpand::of(&scale.clone().__expand_at_step_method(scope, step)),
None => FactorExpand::default(),
}
}
pub(crate) fn __expand_placed_at_method(
&self,
scope: &Scope,
axis: Axis,
coord: NativeExpand<u32>,
) -> FactorExpand {
self.spanning(axis, |scale| {
scale.__expand_placed_at_method(scope, axis, coord)
})
}
pub(crate) fn __expand_run_within_method(
&self,
scope: &Scope,
axis: Axis,
from: NativeExpand<u32>,
until: NativeExpand<u32>,
) -> FactorExpand {
self.spanning(axis, |scale| {
scale.__expand_run_within_method(scope, axis, from, until)
})
}
pub(crate) fn spanning(
&self,
axis: Axis,
place: impl Fn(&TileExpand) -> TileExpand,
) -> FactorExpand {
match self.scale.as_deref() {
None => FactorExpand::default(),
Some(scale) if scale.place.space.contains(axis) => FactorExpand::of(&place(scale)),
Some(scale) => {
let mut kept = scale.clone();
kept.factor = scale.factor.spanning(axis, place);
FactorExpand::of(&kept)
}
}
}
pub(crate) fn __expand_at_coords_method(
&self,
scope: &Scope,
coords: &CoordsExpand<u32>,
values: Space,
guard: Guard,
) -> NativeExpand<f32> {
let mut product: Option<NativeExpand<f32>> = None;
for scale in self.chain() {
assert!(
!holds_words(scope, scale),
"Factor: these scales are words of four e4m3 block scales, which only the \
block-scaled instruction reads; widened one at a time they mean nothing"
);
let one = scale_for(scope, scale, coords, &values, guard);
product = Some(match product {
None => one,
Some(product) => MulExpand::__expand_mul_method(product, scope, one),
});
}
product.unwrap_or_else(|| ExpandValue::constant(1u64.into(), f32::elem_type(scope)).into())
}
pub(crate) fn __expand_whole_method(&self, scope: &Scope) -> NativeExpand<f32> {
let mut product: NativeExpand<f32> =
ExpandValue::constant(1u64.into(), f32::elem_type(scope)).into();
for scale in self.chain() {
let projection = scale.clone().__expand_projection_method(scope);
assert!(
scale
.place
.space
.axes()
.all(|axis| !projection.addresses(axis)),
"Factor::whole: a scale that varies across the output is applied where the output \
is written through memory, not on the sum's fragments"
);
let origin = origin_of(scope, &scale.place.space);
let one = scale_for(scope, scale, &origin, &scale.place.space, Guard::Checked);
product = MulExpand::__expand_mul_method(product, scope, one);
}
product
}
pub(crate) fn __expand_innermost_scale_method(&self, _scope: &Scope) -> TileExpand {
self.innermost_tile()
.expect("Tile::quantize_from: the values are written under the block scales they carry")
}
pub(crate) fn __expand_coarse_method(&self, scope: &Scope) -> NativeExpand<f32> {
self.coarse(scope)
}
pub(crate) fn __expand_depth_method(&self, _scope: &Scope) -> usize {
self.depth()
}
pub(crate) fn __expand_inner_stored_method(&self, scope: &Scope) -> Option<Packing> {
self.scale
.as_deref()
.map(|scale| scale.clone().__expand_packing_method(scope))
}
pub(crate) fn __expand_scaled_method(&self, _scope: &Scope) -> bool {
self.scaled()
}
pub(crate) fn __expand_holds_words_method(&self, scope: &Scope) -> bool {
self.staged_beside(scope)
}
pub(crate) fn staged_beside(&self, scope: &Scope) -> bool {
self.scale
.as_deref()
.is_some_and(|scale| holds_words(scope, scale))
}
fn beside(&self) -> &TileExpand {
let scale = self
.scale
.as_deref()
.expect("Factor: these values carry no scale to stage beside them");
assert!(
!scale.factor.scaled(),
"Factor: values under words of block scales carry no other scale, since the \
block-scaled instruction that reads them holds one; take the coarser one out for the \
sum (`Tile::factor`)"
);
scale
}
pub(crate) fn staged(&self, scope: &Scope, spec: &StageSpec) -> FactorExpand {
let spec = StageSpec {
storage: StageStorage::Strided,
width: None,
..spec.clone()
};
FactorExpand::of(&stage_one::expand(scope, self.beside(), spec))
}
pub(crate) fn fill_from(&self, scope: &Scope, src: &FactorExpand, meeting: &MeetingExpand) {
meeting.__expand_fill_method(scope, &mut self.beside().clone(), src.beside());
}
pub(crate) fn stage_operands(&self, scope: &Scope) -> Vec<StageOperand> {
let scale = self.beside();
vec![StageOperand {
delivery: scale.clone().__expand_delivery_method(scope),
space: scale.place.space.clone(),
keys: Vec::new(),
}]
}
pub(crate) fn free_stage(&self, scope: &Scope) {
self.beside().clone().__expand_free_stage_method(scope);
}
pub(crate) fn __expand_word_at_method(
&self,
scope: &Scope,
coords: &CoordsExpand<u32>,
values: Space,
) -> NativeExpand<u32> {
let scale = self
.scale
.as_deref()
.filter(|scale| holds_words(scope, scale))
.expect("Factor::word_at: the innermost scales are no words of block scales");
scale
.clone()
.__expand_scale_for_method::<u32>(scope, coords, values, Guard::Checked)
}
pub(crate) fn __expand_write_at_method<N: Size>(
&self,
scope: &Scope,
coords: &CoordsExpand<u32>,
values: Space,
scales: NativeExpand<Vector<f32, N>>,
) {
let mut scale = self
.innermost_tile()
.expect("Factor::write_at: these values carry no scale to write");
scale.__expand_write_scales_for_method::<N>(scope, coords, values, scales);
}
pub(crate) fn __expand_calibrate_from_method<F: Float>(
&self,
scope: &Scope,
src: &TileExpand,
coarse: NativeExpand<f32>,
value: QuantValue,
) {
let mut scale = self
.innermost_tile()
.expect("Factor::calibrate_from: these values carry no scale to calibrate");
scale.__expand_calibrate_method::<F>(scope, src, coarse, value, self.depth() > 1);
}
}
#[cube]
impl Tile {
#[allow(dead_code)] pub(crate) fn write_scales_for<N: Size>(
&mut self,
coords: &Coords<u32>,
#[comptime] values: Space,
scales: Vector<f32, N>,
) {
let own = self.scale_coords(coords, values);
self.write_scales::<N>(&own, scales);
}
pub(crate) fn write_scales<N: Size>(&mut self, start: &Coords<u32>, scales: Vector<f32, N>) {
let space = comptime!(self.place.space.clone());
let load = self.vector_tile();
let n = N::value();
comptime!(assert!(
load.values() == n,
"Factor::write_at: a load of these scales holds {} of them, not {n}",
load.values()
));
let at = load.index(start, &space);
let packing = self.packing();
match comptime!(packing) {
Packing::Plain => {
let define!(Stored) = self.element;
let mut out = self.nd_mut::<Stored, N>();
out.write(at, Vector::<Stored, N>::cast_from(scales));
}
Packing::Packed { field } => {
let size!(WP) = comptime!(packing.physical(n));
let mut words = self.nd_words_mut::<WP>();
words.write(at, pack_line::<WP, N>(scales, field));
}
}
}
}
fn holds_words(scope: &Scope, scale: &TileExpand) -> bool {
scale.element == u32::elem_type(scope) && scale.__expand_packing_method(scope) == Packing::Plain
}
fn scale_for(
scope: &Scope,
scale: &TileExpand,
coords: &CoordsExpand<u32>,
values: &Space,
guard: Guard,
) -> NativeExpand<f32> {
scale
.clone()
.__expand_scale_for_method::<f32>(scope, coords, values.clone(), guard)
}
fn origin_of(scope: &Scope, space: &Space) -> CoordsExpand<u32> {
let mut origin = Coords::<u32>::__expand_new(scope);
for _ in 0..space.rank() {
let zero: NativeExpand<u32> =
ExpandValue::constant(0u64.into(), u32::elem_type(scope)).into();
origin.__expand_push_method(scope, zero);
}
origin
}
#[derive(CubeType, Clone)]
#[expand(derive(Clone))]
pub(crate) struct FactorReader {
pub(crate) inner: Factor,
pub(crate) coarse: f32,
#[cube(comptime)]
pub(crate) values: Space,
#[cube(comptime)]
pub(crate) axes: MatrixAxes,
#[cube(comptime)]
pub(crate) vector_size: usize,
#[cube(comptime)]
pub(crate) scaled: bool,
pub(crate) matrix: usize,
}
#[cube]
impl FactorReader {
pub(crate) fn apply<E: Numeric, V: Size>(
&self,
value: Vector<E, V>,
pos: Coords2d,
) -> Vector<E, V> {
if comptime!(self.scaled) {
let (row, col) = pos;
let coords = TileMatrix::value_coords(
row,
col,
self.matrix,
comptime!(&self.values),
comptime!(self.axes),
comptime!(self.vector_size),
);
self.apply_at::<E, V>(value, &coords)
} else {
value
}
}
pub(crate) fn apply_at<E: Numeric, V: Size>(
&self,
value: Vector<E, V>,
coords: &Coords<u32>,
) -> Vector<E, V> {
if comptime!(self.scaled) {
let scale = self.inner.at_coords(
coords,
comptime!(self.values.clone()),
comptime!(Guard::Checked),
);
let scaled = Vector::<f32, V>::cast_from(value)
* Vector::<f32, V>::cast_from(scale * self.coarse);
Vector::<E, V>::cast_from(scaled)
} else {
value
}
}
}
impl FactorExpand {
pub(crate) fn __expand_varies_along_method(&self, scope: &Scope, axis: Axis) -> bool {
self.scale.as_deref().is_some_and(|scale| {
scale
.clone()
.__expand_projection_method(scope)
.addresses(axis)
})
}
}
impl CubeType for Factor {
type ExpandType = FactorExpand;
}
impl IntoExpand for FactorExpand {
type Expand = Self;
fn into_expand(self, _: &Scope) -> Self {
self
}
}
impl ExpandTypeClone for FactorExpand {
fn clone_unchecked(&self) -> Self {
self.clone()
}
}
impl IntoMut for FactorExpand {
fn into_mut(self, _: &Scope) -> Self {
self
}
}
impl CubeDebug for FactorExpand {}
impl AsRefExpand for FactorExpand {
fn __expand_ref_method(&self, _: &Scope) -> &Self {
self
}
}
impl AsMutExpand for FactorExpand {
fn __expand_ref_mut_method(&mut self, _: &Scope) -> &mut Self {
self
}
}