use std::marker::PhantomData;
use cubecl::prelude::*;
use crate::*;
#[derive(CubeType, Clone)]
pub struct Decoded<O: Float, W: Size> {
window: Tile,
#[cube(comptime)]
line: PhantomData<(O, W)>,
}
#[cube]
impl<O: Float, W: Size> Decoded<O, W> {
pub fn read(&self, row: usize, line: usize) -> Vector<O, W> {
self.read_under(row, line, comptime!(Guard::Checked))
}
pub fn read_unchecked(&self, row: usize, line: usize) -> Vector<O, W> {
let provable = self.window.guard_provable();
comptime!(assert!(
provable,
"Decoded::read_unchecked: a clamped window's reads are clamped, never untested"
));
self.read_under(row, line, comptime!(Guard::Proved))
}
fn read_under(&self, row: usize, line: usize, #[comptime] guard: Guard) -> Vector<O, W> {
let packing = self.window.packing();
match comptime!(packing) {
Packing::Plain => read_plain_line::<O, W>(&self.window, row, line, guard),
Packing::Packed { .. } => read_packed_line::<O, W>(&self.window, row, line, guard),
}
}
}
#[cube]
impl Tile {
pub fn decoded<O: Float, W: Size>(&self) -> Decoded<O, W> {
let (packing, scaled) = (self.packing(), self.factor.scaled());
comptime!(assert!(
packing != Packing::Plain || !scaled,
"Tile::decoded: plain values under scales are decoded by a copy (`dequantize_from`)"
));
Decoded::<O, W> {
window: self.clone(),
line: PhantomData,
}
}
}
#[cube]
fn read_plain_line<O: Float, W: Size>(
window: &Tile,
row: usize,
line: usize,
#[comptime] guard: Guard,
) -> Vector<O, W> {
let space = comptime!(window.place.space.clone());
let lines = comptime!(space.extent_at(space.rank() - 1)) / W::value();
let values = window.flat::<O, W>();
match comptime!(guard) {
Guard::Checked => values.read(row * lines + line),
Guard::Proved => values.read_unchecked(row * lines + line),
}
}
#[cube]
fn read_packed_line<O: Float, W: Size>(
window: &Tile,
row: usize,
line: usize,
#[comptime] guard: Guard,
) -> Vector<O, W> {
let space = comptime!(window.place.space.clone());
let rank = comptime!(space.rank());
let along = comptime!(space.axis_at(rank - 1));
let packing = window.packing();
let field = comptime!(match packing {
Packing::Packed { field } => field,
Packing::Plain => unreachable!("Tile::decoded serves plain values as they lie"),
});
let (bits, per_word) = comptime!((field.size_bits() as u32, field.per_word()));
let width = W::value();
let load = window.vector_tile();
comptime!(assert!(
load.extents() == [(along, per_word)],
"Tile::decoded: packed values are read a word a load, along {along:?}; this tile loads \
{:?}. Bind it one word wide",
load.extents()
));
let varies = window.factor.varies_along(comptime!(along));
comptime!(assert!(
width == 1 || !varies,
"Tile::decoded: a {width}-wide line runs along {along:?}, which the scales vary along, so \
one line lies under several scales"
));
let fields = comptime!(core::cmp::min(width, per_word));
let words_per_line = comptime!(width / fields);
let size!(NF) = fields;
let mem = window.mem("Tile::decoded");
let looks_up = mem.codebook.present();
let scaled = window.factor.scaled();
let words = window.nd_words::<Const<1>>(guard);
let rows = Coords::constant(comptime!(
(0..rank - 1)
.map(|p| space.extent_at(p))
.collect::<Vec<_>>()
));
let mut first = rows.unravel(row as u32);
let along_first = (line * width) as u32;
first.push(along_first);
let shift = (along_first % comptime!(per_word as u32)) * bits;
let mut values = Vector::<f32, W>::empty();
#[unroll]
for w in 0..words_per_line {
let mut at = Coords::<u32>::new();
#[unroll]
for p in 0..rank {
if comptime!(p == rank - 1) {
at.push(first.at(p) + comptime!((w * per_word) as u32));
} else {
at.push(first.at(p));
}
}
let mut word = words.read(load.index(&at, &space)).extract(0usize);
if comptime!(fields < per_word) {
word >>= shift;
}
let word = Vector::<u32, Const<1>>::new(word);
if comptime!(looks_up) {
let indices = unpack_line::<u32, Const<1>, NF>(word, comptime!(field));
#[unroll]
for j in 0..fields {
values.insert(w * fields + j, mem.codebook.entry(indices.extract(j)));
}
} else {
let fields_of_word = unpack_line::<f32, Const<1>, NF>(word, comptime!(field));
#[unroll]
for j in 0..fields {
values.insert(w * fields + j, fields_of_word.extract(j));
}
}
}
if comptime!(scaled) {
let scale = window
.factor
.at_coords(&first, comptime!(space.clone()), guard);
values *= Vector::<f32, W>::cast_from(scale);
}
Vector::<O, W>::cast_from(values)
}