use crate::{self as cubecl};
use alloc::vec::Vec;
use cubecl::prelude::*;
use cubecl_runtime::runtime::Runtime;
pub const LANES: usize = 4;
#[cube(launch_unchecked)]
pub fn kernel_product_over<F: Float>(input: &[F], output: &mut [F]) {
output[0] = input[0] * input[1] / input[2];
}
#[cube(launch_unchecked)]
pub fn kernel_product_over_through_a_let<F: Float>(input: &[F], output: &mut [F]) {
let product = input[0] * input[1];
output[0] = product / input[2];
}
#[allow(unused_mut)]
#[cube(launch_unchecked)]
pub fn kernel_product_over_through_a_let_mut<F: Float>(input: &[F], output: &mut [F]) {
let mut product = input[0] * input[1];
output[0] = product / input[2];
}
#[cube(launch_unchecked)]
pub fn kernel_product_on_a_branch<F: Float>(input: &[F], output: &mut [F], take: u32) {
let mut x = input[0];
if take == 1 {
x = input[0] * input[1];
}
output[0] = x;
output[1] = x / input[2];
}
#[cube(launch_unchecked)]
pub fn kernel_accumulate<F: Float>(input: &[F], output: &mut [F], steps: usize) {
let mut total = input[0];
for _ in 0..steps {
total += input[1];
}
output[0] = total;
}
#[cube(launch_unchecked)]
pub fn kernel_accumulate_vector<F: Float, N: Size>(
input: &[Vector<F, N>],
output: &mut [Vector<F, N>],
steps: usize,
) {
let mut total = input[0];
for _ in 0..steps {
total += input[1];
}
output[0] = total;
}
#[cube(launch_unchecked)]
pub fn kernel_accumulate_through_a_copy<F: Float>(input: &[F], output: &mut [F], steps: usize) {
let mut total = input[0];
for _ in 0..steps {
let carried = total;
total = carried + input[1];
}
output[0] = total;
}
pub fn test_products<R: Runtime, F: Float + CubeElement>(client: Client) {
let operands = [3.0, 4.0, 2.0];
assert_eq!(product_over::<F>(&client, operands), F::new(6.0));
assert_eq!(
product_over_through_a_let::<F>(&client, operands),
F::new(6.0)
);
assert_eq!(
product_over_through_a_let_mut::<F>(&client, operands),
F::new(6.0)
);
assert_eq!(product_on_a_branch::<F>(&client, operands), F::new(6.0));
}
const ACCUMULATION_STEPS: usize = 256;
pub fn test_accumulations<R: Runtime, F: Float + CubeElement>(client: Client) {
let steps = ACCUMULATION_STEPS;
let total = F::new(steps as f32);
assert_eq!(accumulated::<F>(&client, 0.0, 1.0, steps), total);
assert_eq!(
accumulated_through_a_copy::<F>(&client, 0.0, 1.0, steps),
total
);
assert_eq!(
accumulated_vector::<F>(&client, 0.0, 1.0, steps),
[total; LANES]
);
}
pub fn product_over<F: Float + CubeElement>(client: &Client, operands: [f32; 3]) -> F {
run(
client,
&operands.map(|x| F::new(x)),
1,
|input, output| unsafe {
kernel_product_over::launch_unchecked::<F>(
client,
CubeCount::new_single(),
CubeDim::new_1d(1),
input,
output,
)
},
)[0]
}
pub fn product_over_through_a_let<F: Float + CubeElement>(
client: &Client,
operands: [f32; 3],
) -> F {
run(
client,
&operands.map(|x| F::new(x)),
1,
|input, output| unsafe {
kernel_product_over_through_a_let::launch_unchecked::<F>(
client,
CubeCount::new_single(),
CubeDim::new_1d(1),
input,
output,
)
},
)[0]
}
pub fn product_over_through_a_let_mut<F: Float + CubeElement>(
client: &Client,
operands: [f32; 3],
) -> F {
run(
client,
&operands.map(|x| F::new(x)),
1,
|input, output| unsafe {
kernel_product_over_through_a_let_mut::launch_unchecked::<F>(
client,
CubeCount::new_single(),
CubeDim::new_1d(1),
input,
output,
)
},
)[0]
}
pub fn product_on_a_branch<F: Float + CubeElement>(client: &Client, operands: [f32; 3]) -> F {
run(
client,
&operands.map(|x| F::new(x)),
2,
|input, output| unsafe {
kernel_product_on_a_branch::launch_unchecked::<F>(
client,
CubeCount::new_single(),
CubeDim::new_1d(1),
input,
output,
1,
)
},
)[1]
}
pub fn accumulated<F: Float + CubeElement>(
client: &Client,
start: f32,
step: f32,
steps: usize,
) -> F {
run(
client,
&[F::new(start), F::new(step)],
1,
|input, output| unsafe {
kernel_accumulate::launch_unchecked::<F>(
client,
CubeCount::new_single(),
CubeDim::new_1d(1),
input,
output,
steps,
)
},
)[0]
}
pub fn accumulated_through_a_copy<F: Float + CubeElement>(
client: &Client,
start: f32,
step: f32,
steps: usize,
) -> F {
run(
client,
&[F::new(start), F::new(step)],
1,
|input, output| unsafe {
kernel_accumulate_through_a_copy::launch_unchecked::<F>(
client,
CubeCount::new_single(),
CubeDim::new_1d(1),
input,
output,
steps,
)
},
)[0]
}
pub fn accumulated_vector<F: Float + CubeElement>(
client: &Client,
start: f32,
step: f32,
steps: usize,
) -> [F; LANES] {
let mut input = [F::new(start); 2 * LANES];
input[LANES..].fill(F::new(step));
let output = run(client, &input, LANES, |input, output| unsafe {
kernel_accumulate_vector::launch_unchecked::<F>(
client,
CubeCount::new_single(),
CubeDim::new_1d(1),
LANES,
input,
output,
steps,
)
});
core::array::from_fn(|lane| output[lane])
}
fn run<F: CubeElement>(
client: &Client,
input: &[F],
outputs: usize,
launch: impl FnOnce(BufferArg, BufferArg),
) -> Vec<F> {
let input_handle = client.create_from_slice(F::as_bytes(input));
let output = client.empty(outputs * core::mem::size_of::<F>());
launch(
unsafe { BufferArg::from_raw_parts(input_handle, input.len()) },
unsafe { BufferArg::from_raw_parts(output.clone(), outputs) },
);
F::from_bytes(&client.read_one_unchecked(output)).to_vec()
}
#[allow(missing_docs)]
#[macro_export]
macro_rules! testgen_arithmetic_chains {
() => {
use super::*;
#[$crate::runtime_tests::test_log::test]
fn test_arithmetic_chain_products() {
let client = TestRuntime::client(&Default::default());
cubecl_core::runtime_tests::arithmetic_chains::test_products::<TestRuntime, FloatType>(
client,
);
}
#[$crate::runtime_tests::test_log::test]
fn test_arithmetic_chain_accumulations() {
let client = TestRuntime::client(&Default::default());
cubecl_core::runtime_tests::arithmetic_chains::test_accumulations::<
TestRuntime,
FloatType,
>(client);
}
};
}