candding 0.1.0

Pure candle embeddings for Rust with no ONNX: dense, sparse, multi-vector and rerank models today, image models on the roadmap
Documentation
use super::*;

/// `head * 100 + i * 10 + j`, so a transposition or a head-order mistake shows in the numbers, not only the shape.
fn ramp_bias(heads: usize, seq: usize) -> Tensor {
  let mut values = Vec::with_capacity(heads * seq * seq);
  for h in 0..heads {
    for i in 0..seq {
      for j in 0..seq {
        values.push((h * 100 + i * 10 + j) as f32);
      }
    }
  }
  Tensor::from_vec(values, (1, heads, seq, seq), &Device::Cpu).unwrap()
}

fn floor_in(dtype: DType) -> f32 {
  Tensor::new(&[mask_min(dtype) as f32], &Device::Cpu)
    .unwrap()
    .to_dtype(dtype)
    .unwrap()
    .to_dtype(DType::F32)
    .unwrap()
    .to_vec1::<f32>()
    .unwrap()[0]
}

#[test]
fn padding_with_bias_keeps_the_bias_and_floors_the_padded_keys() {
  let attention_mask = Tensor::new(&[[1u32, 1, 0], [1, 1, 1]], &Device::Cpu).unwrap();
  let bias = ramp_bias(2, 3);
  let mask =
    AttentionMask::padding_with_bias(&attention_mask, &[2, 3], &bias, 2, 64, DType::F32).unwrap();
  let additive = mask
    .additive()
    .expect("padding_with_bias never returns none()");
  assert_eq!(additive.dims(), &[2, 2, 3, 3]);
  assert!(!mask.is_causal());
  let values = additive.flatten_all().unwrap().to_vec1::<f32>().unwrap();
  let at = |b: usize, h: usize, i: usize, j: usize| values[((b * 2 + h) * 3 + i) * 3 + j];
  let floor = floor_in(DType::F32);
  for h in 0..2 {
    for i in 0..3 {
      for j in 0..3 {
        let expected = (h * 100 + i * 10 + j) as f32;
        assert_eq!(at(1, h, i, j), expected, "row 1 ({h}, {i}, {j})");
        let expected0 = if j == 2 { floor } else { expected };
        assert_eq!(at(0, h, i, j), expected0, "row 0 ({h}, {i}, {j})");
      }
    }
  }
}

#[test]
fn padding_with_bias_keeps_one_row_when_nothing_is_padded() {
  let attention_mask = Tensor::new(&[[1u32, 1], [1, 1]], &Device::Cpu).unwrap();
  let bias = ramp_bias(3, 2);
  let mask =
    AttentionMask::padding_with_bias(&attention_mask, &[2, 2], &bias, 3, 64, DType::F32).unwrap();
  let additive = mask
    .additive()
    .expect("padding_with_bias never returns none()");
  assert_eq!(additive.dims(), &[1, 3, 2, 2]);
  assert_eq!(
    additive.flatten_all().unwrap().to_vec1::<f32>().unwrap(),
    bias.flatten_all().unwrap().to_vec1::<f32>().unwrap()
  );
  assert!(!mask.is_causal());
}

/// candle bails with `Mask type {mask_type:?} must match q type {itype:?}` otherwise.
#[test]
fn padding_with_bias_casts_once_at_the_end_in_every_dtype() {
  let attention_mask = Tensor::new(&[[1u32, 0]], &Device::Cpu).unwrap();
  let bias = ramp_bias(1, 2);
  for dtype in [DType::F32, DType::F16, DType::BF16] {
    let mask =
      AttentionMask::padding_with_bias(&attention_mask, &[1], &bias, 1, 64, dtype).unwrap();
    let additive = mask
      .additive()
      .expect("padding_with_bias never returns none()");
    assert_eq!(additive.dtype(), dtype, "{dtype:?}");
    let values = additive
      .to_dtype(DType::F32)
      .unwrap()
      .flatten_all()
      .unwrap()
      .to_vec1::<f32>()
      .unwrap();
    assert_eq!(values[0], 0.0, "{dtype:?} real key, bias 0");
    assert_eq!(values[1], floor_in(dtype), "{dtype:?} padded key");
    assert_eq!(values[2], 10.0, "{dtype:?} real key, bias 10");
    assert_eq!(values[3], floor_in(dtype), "{dtype:?} padded key");
  }
}

/// Past a magnitude of about sixteen, adding the bias to the F16 floor gives -inf, so a padded key must be selected.
#[test]
fn a_large_negative_bias_never_pushes_a_padded_key_past_the_floor() {
  let attention_mask = Tensor::new(&[[1u32, 0]], &Device::Cpu).unwrap();
  let bias = Tensor::from_vec(
    vec![-24.0f32, -24.0, -24.0, -24.0],
    (1, 1, 2, 2),
    &Device::Cpu,
  )
  .unwrap();
  for dtype in [DType::F16, DType::BF16] {
    let mask =
      AttentionMask::padding_with_bias(&attention_mask, &[1], &bias, 1, 64, dtype).unwrap();
    let values = mask
      .additive()
      .unwrap()
      .to_dtype(DType::F32)
      .unwrap()
      .flatten_all()
      .unwrap()
      .to_vec1::<f32>()
      .unwrap();
    assert!(
      values.iter().all(|v| v.is_finite()),
      "{dtype:?}: {values:?}"
    );
    assert_eq!(values[1], floor_in(dtype), "{dtype:?} padded key");
    assert!(
      (values[0] + 24.0).abs() < 0.2,
      "{dtype:?} real key kept the bias: {}",
      values[0]
    );
  }
}