use candle_core::Device;
use candle_core::Tensor;
use super::*;
use crate::models::Encoder;
struct StubEncoder(usize);
impl Encoder for StubEncoder {
fn forward(&self, input: &EncoderInput) -> Result<Tensor> {
let (batch, seq) = input.input_ids.dims2()?;
Ok(Tensor::zeros(
(batch, seq, self.0),
DType::F32,
input.input_ids.device(),
)?)
}
fn hidden_size(&self) -> usize {
self.0
}
fn max_position_embeddings(&self) -> usize {
16
}
fn weights_dtype(&self) -> DType {
DType::F32
}
}
#[test]
fn a_matrix_large_enough_to_collide_with_the_hashed_range_is_refused() {
let words: Vec<String> = (0..32_000).map(|i| format!("word{i}")).collect();
let word_refs: Vec<&str> = words.iter().map(String::as_str).collect();
let matrix = Tensor::zeros((32_001, 2, 4), DType::F32, &Device::Cpu).unwrap();
match MiniCoilSparse::new(
Box::new(StubEncoder(2)),
matrix,
vocab(&word_refs),
stopwords(&[]),
token_text(&["cat"]),
) {
Err(err) => assert!(
err.to_string().contains("128000"),
"expected the compiled-in SHIFT boundary in the error, got: {err}"
),
Ok(_) => panic!("a matrix this large must be refused"),
}
}
#[test]
fn a_matrix_exactly_at_the_collision_boundary_is_accepted() {
let words: Vec<String> = (0..31_999).map(|i| format!("word{i}")).collect();
let word_refs: Vec<&str> = words.iter().map(String::as_str).collect();
let matrix = Tensor::zeros((32_000, 2, 4), DType::F32, &Device::Cpu).unwrap();
assert!(
MiniCoilSparse::new(
Box::new(StubEncoder(2)),
matrix,
vocab(&word_refs),
stopwords(&[]),
token_text(&["cat"]),
)
.is_ok()
);
}
#[test]
fn a_hidden_width_disagreeing_with_the_matrix_is_refused() {
let matrix = Tensor::from_vec(vec![0f32; 2 * 3 * 4], (2, 3, 4), &Device::Cpu).unwrap();
let model = MiniCoilSparse::new(
Box::new(StubEncoder(2)),
matrix,
vocab(&["cat"]),
stopwords(&[]),
token_text(&["cat"]),
)
.unwrap();
let input_ids = Tensor::from_vec(vec![0u32], (1, 1), &Device::Cpu).unwrap();
let attention_mask = Tensor::from_vec(vec![1u32], (1, 1), &Device::Cpu).unwrap();
let token_type_ids = input_ids.zeros_like().unwrap();
let input = EncoderInput {
input_ids: &input_ids,
attention_mask: &attention_mask,
token_type_ids: &token_type_ids,
lengths: &[1],
};
let err = model.forward(&input).unwrap_err();
let message = err.to_string();
assert!(
message.contains('2') && message.contains('3'),
"expected both widths named in the error, got: {message}"
);
}