candle-einops 0.2.0

Compile-time einops and einsum tensor operations for Candle
Documentation
use std::panic::{AssertUnwindSafe, catch_unwind};

use candle_core::{Device, Result, Tensor};
use candle_einops::einops;

#[test]
fn zero_decomposition_factor_returns_an_error() -> Result<()> {
    let input = Tensor::arange(0u32, 6, &Device::Cpu)?;

    let outcome = catch_unwind(AssertUnwindSafe(|| {
        einops!("(axis factor:0) -> axis factor", &input)
    }));

    assert!(outcome.is_ok(), "zero factor must not panic");
    let error = outcome.unwrap().unwrap_err();
    assert!(error.to_string().contains("must be non-zero"));
    Ok(())
}

#[test]
fn non_divisible_decomposition_returns_a_specific_error() -> Result<()> {
    let input = Tensor::arange(0u32, 5, &Device::Cpu)?;

    let error = einops!("(axis factor:2) -> axis factor", &input).unwrap_err();

    assert!(error.to_string().contains("not divisible"));
    Ok(())
}

#[test]
fn overflowing_runtime_factor_product_returns_an_error() -> Result<()> {
    let input = Tensor::new(&[1u32], &Device::Cpu)?;
    let large = usize::MAX;
    let two = 2usize;

    let outcome = catch_unwind(AssertUnwindSafe(|| {
        einops!("(axis {large} {two}) -> axis {large} {two}", &input)
    }));

    assert!(outcome.is_ok(), "factor overflow must not panic");
    let error = outcome.unwrap().unwrap_err();
    assert!(error.to_string().contains("factor product overflows usize"));
    Ok(())
}

#[test]
fn valid_inferred_decomposition_is_unchanged() -> Result<()> {
    let input = Tensor::arange(0u32, 12, &Device::Cpu)?;

    let output = einops!("(row column:3) -> row column", &input)?;

    assert_eq!(output.dims(), &[4, 3]);
    assert_eq!(
        output.to_vec2::<u32>()?,
        &[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]
    );
    Ok(())
}