libmir-metal 0.3.0

Metal inference backend for libmir
use super::*;

#[test]
fn creates_piecewise_rope_frequencies_on_the_gpu_stream() -> Result<()> {
    let stream = Stream::new_gpu()?;
    let frequencies = Array::piecewise_rope_frequencies(8, 10_000.0, 8.0, 1.0, 4.0, 8192, &stream)?;
    frequencies.async_eval()?;
    stream.synchronize()?;

    let values = frequencies.to_vec_f32()?;

    assert_eq!(values.len(), 4);
    assert!((values[0] - 1.0).abs() < 1.0e-6);
    assert!((values[1] - 10.0).abs() < 1.0e-5);
    assert!((values[2] - 100.0).abs() < 1.0e-4);
    assert!(values[3] > 4_000.0 && values[3] < 5_000.0);
    Ok(())
}

#[test]
fn creates_truncated_yarn_frequencies_on_the_gpu_stream() -> Result<()> {
    let stream = Stream::new_gpu()?;
    let frequencies =
        Array::yarn_rope_frequencies(128, 1_000_000.0, 4.0, 32.0, 1.0, 32768, &stream)?;
    frequencies.async_eval()?;
    stream.synchronize()?;

    let values = frequencies.to_vec_f32()?;
    let expected = yarn_reference(128, 1_000_000.0, 4.0, 32.0, 1.0, 32768.0)?;

    assert_eq!(values.len(), expected.len());
    for (actual, expected) in values.iter().zip(expected) {
        assert!((actual - expected).abs() <= expected.abs().max(1.0) * 2.0e-6);
    }
    Ok(())
}

fn yarn_reference(
    dimensions: usize,
    base: f32,
    factor: f32,
    beta_fast: f32,
    beta_slow: f32,
    original: f32,
) -> Result<Vec<f32>> {
    let dimensions_f32 = f32::from(u16::try_from(dimensions)?);
    let half = dimensions_f32 / 2.0;
    let low = (half * (original / (beta_fast * std::f32::consts::TAU)).ln() / base.ln())
        .floor()
        .max(0.0);
    let high = (half * (original / (beta_slow * std::f32::consts::TAU)).ln() / base.ln())
        .ceil()
        .min(dimensions_f32 - 1.0);
    (0..dimensions / 2)
        .map(|index| {
            let index = f32::from(u16::try_from(index)?);
            let frequency = base.powf(2.0 * index / dimensions_f32);
            let ramp = ((index - low) / (high - low)).clamp(0.0, 1.0);
            let mask = 1.0 - ramp;
            let inverse = frequency.recip();
            Ok((inverse / factor).mul_add(1.0 - mask, inverse * mask).recip())
        })
        .collect()
}