cubek-pool 0.3.0

CubeK: Pool
mod adaptive_avg_pool;
mod avg_pool;
mod max_pool;

pub use adaptive_avg_pool::run_adaptive_avg_pool_backward;
pub use avg_pool::run_avg_pool_backward;
pub use max_pool::run_max_pool_backward;

use super::{f32_elem_type, i32_elem_type, make_random_f32_host, make_zero_handle};
use crate::definition::{PoolBackwardProblem, PoolMode};
use crate::eval::cpu_reference::{cpu_reference_max_pool_indices, cpu_reference_pool_backward};
use crate::{pool2d_backward, pool2d_with_indices, pool2d_with_indices_backward};
use cubecl::{
    client::Client,
    zspace::{Shape, Strides},
};
use cubek_test_utils::{
    ExecutionOutcome, HostData, HostDataType, HostDataVec, Progress, launch_and_capture_outcome,
};

pub fn strategy_result(
    client: Client,
    problem: PoolBackwardProblem<2>,
    seed: u64,
) -> Result<HostData, String> {
    if matches!(&problem.mode, PoolMode::Max(_)) && !problem.with_indices {
        return Err("max pool backward requires indices".to_string());
    }

    let dtype = f32_elem_type();
    let indices_dtype = i32_elem_type();
    let out_grad_shape = problem.out_grad_shape.to_vec();
    let input_shape = problem.input_shape().to_vec();

    let (input_handle, _input_host) = make_random_f32_host(&client, input_shape.clone(), seed);
    let (out_grad_handle, _out_grad_host) =
        make_random_f32_host(&client, out_grad_shape.clone(), seed);
    let input_grad_handle = make_zero_handle(&client, input_shape, dtype);

    let indices_handle = if problem.with_indices {
        let output_handle = make_zero_handle(&client, out_grad_shape.clone(), dtype);
        let indices_handle = make_zero_handle(&client, out_grad_shape.clone(), indices_dtype);

        let forward_outcome = launch_and_capture_outcome(
            &client,
            &[&output_handle.handle, &indices_handle.handle],
            |c| {
                pool2d_with_indices(
                    c,
                    input_handle.clone().binding(),
                    output_handle.clone().binding(),
                    indices_handle.clone().binding(),
                    problem.mode.clone(),
                    dtype,
                )
                .into()
            },
        );

        match forward_outcome {
            ExecutionOutcome::CompileError(e) => return Err(format!("compile error: {e}")),
            ExecutionOutcome::Executed => Some(indices_handle),
        }
    } else {
        None
    };

    let outcome = launch_and_capture_outcome(&client, &[&input_grad_handle.handle], |c| {
        if let Some(indices) = &indices_handle {
            pool2d_with_indices_backward(
                c,
                input_handle.clone().binding(),
                out_grad_handle.clone().binding(),
                indices.clone().binding(),
                input_grad_handle.clone().binding(),
                problem.mode.clone(),
                dtype,
                indices_dtype,
            )
            .into()
        } else {
            pool2d_backward(
                c,
                input_handle.clone().binding(),
                out_grad_handle.clone().binding(),
                input_grad_handle.clone().binding(),
                problem.mode.clone(),
                dtype,
            )
            .into()
        }
    });

    match outcome {
        ExecutionOutcome::CompileError(e) => Err(format!("compile error: {e}")),
        ExecutionOutcome::Executed => Ok(HostData::from_tensor_handle(
            &client,
            input_grad_handle,
            HostDataType::F32,
        )),
    }
}

pub fn cpu_reference_result(
    client: Client,
    problem: PoolBackwardProblem<2>,
    seed: u64,
    progress: Option<&Progress>,
) -> Result<HostData, String> {
    if matches!(&problem.mode, PoolMode::Max(_)) && !problem.with_indices {
        return Err("max pool backward requires indices".to_string());
    }

    let out_grad_shape = problem.out_grad_shape.to_vec();
    let input_shape = problem.input_shape().to_vec();

    if let Some(p) = progress {
        let total: usize = input_shape.iter().product();
        p.set_total(total as u64);
    }

    let (_input_handle, input_host) = make_random_f32_host(&client, input_shape, seed);
    let (_out_grad_handle, out_grad_host) =
        make_random_f32_host(&client, out_grad_shape.clone(), seed);

    let indices_host = if problem.with_indices {
        match &problem.mode {
            PoolMode::Max(opts) => {
                cpu_reference_max_pool_indices(&input_host, opts, &out_grad_shape)
            }
            _ => return Err("pool backward indices only supported for max".to_string()),
        }
    } else {
        let empty_dims: Vec<usize> = vec![];

        HostData {
            data: HostDataVec::I32(Vec::new()),
            shape: Shape::from(empty_dims),
            strides: Strides::new(&[] as &[usize]),
        }
    };

    Ok(cpu_reference_pool_backward(
        &out_grad_host,
        &indices_host,
        problem,
    ))
}