use ruda_core::device::Device;
use ruda_kernel::dsl as kernel_dsl;
use ruda_kernel::dsl::{prelude::*, calculate_ruda_count_elemwise};
use ruda_kernel::library::tensor::layout::linear::LinearView;
use ruda_kernel::tensor::{RudaTensor, allocation::empty_device_dtype, element::TensorElement, layout::address_type};
use ruda_core::tensor::{DType, Shape};
use crate::collective::{RudaSum, RudaSumLaunch, RudaKeyEqual, RudaKeyEqualExpand};
use super::{RudaPrimitiveError, check_type, empty_like, scan};
#[ruda]
pub trait RudaPredicate<T: RudaType>: RudaType {
fn test(&self, value: T) -> bool;
}
pub struct RudaSelection<R: Runtime> {
pub values: RudaTensor<R>,
pub count: RudaTensor<R>,
}
#[ruda(launch_unchecked, explicit_define, address_type = "dynamic")]
fn predicate_flags<T: Numeric, P: RudaPredicate<T> + LaunchArg>(
input: &LinearView<T>, flags: &mut LinearView<u64, ReadWrite>, predicate: &P,
) {
let index = ABSOLUTE_POS;
if index < input.shape() { flags[index] = u64::cast_from(predicate.test(input[index])); }
}
#[ruda(launch_unchecked, explicit_define, address_type = "dynamic")]
fn convert_flags<F: Numeric>(input: &LinearView<F>, flags: &mut LinearView<u64, ReadWrite>) {
let index = ABSOLUTE_POS;
if index < input.shape() { flags[index] = u64::cast_from(input[index] != F::from_int(0)); }
}
#[ruda(launch_unchecked, explicit_define, address_type = "dynamic")]
fn unique_flags<T: Numeric, E: RudaKeyEqual<T> + LaunchArg>(
input: &LinearView<T>, flags: &mut LinearView<u64, ReadWrite>, equal: &E,
) {
let index = ABSOLUTE_POS;
if index >= input.shape() { terminate!(); }
let mut head = true;
if index > 0 { head = !equal.equal(input[index - 1], input[index]); }
flags[index] = u64::cast_from(head);
}
#[ruda(launch_unchecked, explicit_define, address_type = "dynamic")]
fn scatter<T: Numeric>(
input: &LinearView<T>, flags: &LinearView<u64>, prefixes: &LinearView<u64>,
output: &mut LinearView<T, ReadWrite>, count: &mut LinearView<u64, ReadWrite>,
#[comptime] partition: bool,
) {
let index = ABSOLUTE_POS;
if index == 0 {
let mut total = 0u64;
if input.shape() > 0 { total = prefixes[input.shape() - 1]; }
count[0] = total;
}
if index < input.shape() {
let prefix = prefixes[index] as usize;
if flags[index] != 0 {
output[prefix - 1] = input[index];
} else {
if partition {
output[input.shape() - 1 - (index - prefix)] = input[index];
}
}
}
}
pub(crate) fn compact<R: Runtime, T: TensorElement>(
input: &RudaTensor<R>, flags: &RudaTensor<R>, threads: u32, partition: bool,
) -> Result<RudaSelection<R>, RudaPrimitiveError> {
let prefixes = scan::inclusive_scan::<R, u64, RudaSum>(flags, RudaSumLaunch::new(), threads)?;
let values = empty_like(input);
let count = empty_device_dtype(input.client.clone(), input.device.clone(), Shape::new([1]), DType::U64);
let work = input.meta.num_elements().max(1);
let dim = RudaDim::new(input.client.properties(), work);
let grid = calculate_ruda_count_elemwise(&input.client, work, dim);
unsafe {
scatter::launch_unchecked::<T, R>(
&input.client, grid, dim, address_type!(input, flags, prefixes, values, count),
input.clone().into_linear_view(), flags.clone().into_linear_view(), prefixes.into_linear_view(),
values.clone().into_linear_view(), count.clone().into_linear_view(), partition,
);
}
Ok(RudaSelection { values, count })
}
pub fn flagged<R: Runtime, T: TensorElement, F: TensorElement>(
input: &RudaTensor<R>, flags: &RudaTensor<R>, threads: u32, partition: bool,
) -> Result<RudaSelection<R>, RudaPrimitiveError> {
check_type::<R, T>(input)?;
check_type::<R, F>(flags)?;
let size = input.meta.num_elements();
if size != flags.meta.num_elements() { return Err(RudaPrimitiveError::Length); }
if input.device.to_id() != flags.device.to_id() { return Err(RudaPrimitiveError::Device); }
let normalized = empty_device_dtype(input.client.clone(), input.device.clone(), Shape::new([size]), DType::U64);
if size > 0 {
let dim = RudaDim::new(input.client.properties(), size);
let grid = calculate_ruda_count_elemwise(&input.client, size, dim);
unsafe {
convert_flags::launch_unchecked::<F, R>(
&input.client, grid, dim, address_type!(flags, normalized),
flags.clone().into_linear_view(), normalized.clone().into_linear_view(),
);
}
}
compact::<R, T>(input, &normalized, threads, partition)
}
pub fn select_if<R, T, P>(input: &RudaTensor<R>, predicate: P::RuntimeArg<R>, threads: u32, partition: bool) -> Result<RudaSelection<R>, RudaPrimitiveError>
where R: Runtime, T: TensorElement, P: RudaPredicate<T> + LaunchArg,
{
check_type::<R, T>(input)?;
let size = input.meta.num_elements();
let flags = empty_device_dtype(input.client.clone(), input.device.clone(), Shape::new([size]), DType::U64);
if size > 0 {
let dim = RudaDim::new(input.client.properties(), size);
let grid = calculate_ruda_count_elemwise(&input.client, size, dim);
unsafe {
predicate_flags::launch_unchecked::<T, P, R>(
&input.client, grid, dim, address_type!(input, flags),
input.clone().into_linear_view(), flags.clone().into_linear_view(), predicate,
);
}
}
compact::<R, T>(input, &flags, threads, partition)
}
pub fn unique<R, T, E>(input: &RudaTensor<R>, equal: E::RuntimeArg<R>, threads: u32) -> Result<RudaSelection<R>, RudaPrimitiveError>
where R: Runtime, T: TensorElement, E: RudaKeyEqual<T> + LaunchArg,
{
check_type::<R, T>(input)?;
let size = input.meta.num_elements();
let flags = empty_device_dtype(input.client.clone(), input.device.clone(), Shape::new([size]), DType::U64);
if size > 0 {
let dim = RudaDim::new(input.client.properties(), size);
let grid = calculate_ruda_count_elemwise(&input.client, size, dim);
unsafe {
unique_flags::launch_unchecked::<T, E, R>(
&input.client, grid, dim, address_type!(input, flags),
input.clone().into_linear_view(), flags.clone().into_linear_view(), equal,
);
}
}
compact::<R, T>(input, &flags, threads, false)
}
pub struct RudaPairSelection<R: Runtime> {
pub keys: RudaTensor<R>,
pub values: RudaTensor<R>,
pub count: RudaTensor<R>,
}
#[ruda(launch_unchecked, explicit_define, address_type = "dynamic")]
fn scatter_unique_pairs<K: Numeric, V: Numeric>(
keys: &LinearView<K>, values: &LinearView<V>, flags: &LinearView<u64>, prefixes: &LinearView<u64>,
output_keys: &mut LinearView<K, ReadWrite>, output_values: &mut LinearView<V, ReadWrite>, count: &mut LinearView<u64, ReadWrite>,
) {
let index = ABSOLUTE_POS;
if index == 0 {
let mut total = 0u64;
if keys.shape() > 0 { total = prefixes[keys.shape() - 1]; }
count[0] = total;
}
if index < keys.shape() {
if flags[index] != 0 {
let rank = prefixes[index] as usize - 1;
output_keys[rank] = keys[index];
output_values[rank] = values[index];
}
}
}
pub fn unique_by_key<R, K, V, E>(
keys: &RudaTensor<R>, values: &RudaTensor<R>, equal: E::RuntimeArg<R>, threads: u32,
) -> Result<RudaPairSelection<R>, RudaPrimitiveError>
where R: Runtime, K: TensorElement, V: TensorElement, E: RudaKeyEqual<K> + LaunchArg,
{
check_type::<R, K>(keys)?;
check_type::<R, V>(values)?;
let size = keys.meta.num_elements();
if size != values.meta.num_elements() { return Err(RudaPrimitiveError::Length); }
if keys.device.to_id() != values.device.to_id() { return Err(RudaPrimitiveError::Device); }
let flags = empty_device_dtype(keys.client.clone(), keys.device.clone(), Shape::new([size]), DType::U64);
let dim = RudaDim::new(keys.client.properties(), size.max(1));
if size > 0 {
let grid = calculate_ruda_count_elemwise(&keys.client, size, dim);
unsafe {
unique_flags::launch_unchecked::<K, E, R>(&keys.client, grid, dim, address_type!(keys, flags),
keys.clone().into_linear_view(), flags.clone().into_linear_view(), equal);
}
}
let prefixes = scan::inclusive_scan::<R, u64, RudaSum>(&flags, RudaSumLaunch::new(), threads)?;
let output_keys = empty_like(keys);
let output_values = empty_like(values);
let count = empty_device_dtype(keys.client.clone(), keys.device.clone(), Shape::new([1]), DType::U64);
let grid = calculate_ruda_count_elemwise(&keys.client, size.max(1), dim);
unsafe {
scatter_unique_pairs::launch_unchecked::<K, V, R>(
&keys.client, grid, dim, address_type!(keys, values, flags, prefixes, output_keys, output_values, count),
keys.clone().into_linear_view(), values.clone().into_linear_view(), flags.into_linear_view(), prefixes.into_linear_view(),
output_keys.clone().into_linear_view(), output_values.clone().into_linear_view(), count.clone().into_linear_view(),
);
}
Ok(RudaPairSelection { keys: output_keys, values: output_values, count })
}
pub fn flagged_if<R, T, F, P>(
input: &RudaTensor<R>, flags: &RudaTensor<R>, predicate: P::RuntimeArg<R>, threads: u32,
) -> Result<RudaSelection<R>, RudaPrimitiveError>
where R: Runtime, T: TensorElement, F: TensorElement, P: RudaPredicate<F> + LaunchArg,
{
check_type::<R, T>(input)?;
check_type::<R, F>(flags)?;
let size = input.meta.num_elements();
if size != flags.meta.num_elements() { return Err(RudaPrimitiveError::Length); }
if input.device.to_id() != flags.device.to_id() { return Err(RudaPrimitiveError::Device); }
let normalized = empty_device_dtype(input.client.clone(), input.device.clone(), Shape::new([size]), DType::U64);
if size > 0 {
let dim = RudaDim::new(input.client.properties(), size);
let grid = calculate_ruda_count_elemwise(&input.client, size, dim);
unsafe {
predicate_flags::launch_unchecked::<F, P, R>(&input.client, grid, dim, address_type!(flags, normalized),
flags.clone().into_linear_view(), normalized.clone().into_linear_view(), predicate);
}
}
compact::<R, T>(input, &normalized, threads, false)
}
#[ruda(launch_unchecked, explicit_define, address_type = "dynamic")]
fn write_selection<T: Numeric>(values: &LinearView<T>, count: &LinearView<u64>, output: &mut LinearView<T, ReadWrite>) {
let index = ABSOLUTE_POS;
if index < output.shape() {
if (index as u64) < count[0] { output[index] = values[index]; }
}
}
pub fn write_into<R: Runtime, T: TensorElement>(
selection: RudaSelection<R>, output: &RudaTensor<R>,
) -> Result<RudaTensor<R>, RudaPrimitiveError> {
check_type::<R, T>(&selection.values)?;
check_type::<R, T>(output)?;
check_type::<R, u64>(&selection.count)?;
if selection.count.meta.num_elements() != 1 || output.meta.num_elements() < selection.values.meta.num_elements() {
return Err(RudaPrimitiveError::Length);
}
if output.device.to_id() != selection.values.device.to_id() || output.device.to_id() != selection.count.device.to_id() { return Err(RudaPrimitiveError::Device); }
let size = output.meta.num_elements();
if size > 0 {
let dim = RudaDim::new(output.client.properties(), size);
let grid = calculate_ruda_count_elemwise(&output.client, size, dim);
unsafe {
write_selection::launch_unchecked::<T, R>(&output.client, grid, dim, address_type!((selection.values), (selection.count), output),
selection.values.into_linear_view(), selection.count.clone().into_linear_view(), output.clone().into_linear_view());
}
}
Ok(selection.count)
}
pub fn select_if_in_place<R, T, P>(input: &RudaTensor<R>, predicate: P::RuntimeArg<R>, threads: u32) -> Result<RudaTensor<R>, RudaPrimitiveError>
where R: Runtime, T: TensorElement, P: RudaPredicate<T> + LaunchArg,
{
let selected = select_if::<R, T, P>(input, predicate, threads, false)?;
write_into::<R, T>(selected, input)
}
pub fn flagged_if_in_place<R, T, F, P>(
input: &RudaTensor<R>, flags: &RudaTensor<R>, predicate: P::RuntimeArg<R>, threads: u32,
) -> Result<RudaTensor<R>, RudaPrimitiveError>
where R: Runtime, T: TensorElement, F: TensorElement, P: RudaPredicate<F> + LaunchArg,
{
let selected = flagged_if::<R, T, F, P>(input, flags, predicate, threads)?;
write_into::<R, T>(selected, input)
}
pub fn flagged_in_place<R: Runtime, T: TensorElement, F: TensorElement>(
input: &RudaTensor<R>, flags: &RudaTensor<R>, threads: u32,
) -> Result<RudaTensor<R>, RudaPrimitiveError> {
let selected = flagged::<R, T, F>(input, flags, threads, false)?;
write_into::<R, T>(selected, input)
}
pub fn unique_in_place<R, T, E>(input: &RudaTensor<R>, equal: E::RuntimeArg<R>, threads: u32) -> Result<RudaTensor<R>, RudaPrimitiveError>
where R: Runtime, T: TensorElement, E: RudaKeyEqual<T> + LaunchArg,
{
let selected = unique::<R, T, E>(input, equal, threads)?;
write_into::<R, T>(selected, input)
}