use super::*;
use windit::plan::WindowOptions;
use crate::embeddings::{
aggregate::EmaRenormalizedOptions,
embeddinggemma2::{
embedding::EMBEDDING_DIM,
text::{framing_of, framing_table},
tokenizer::image::fixture,
},
};
struct Lcg(u64);
impl Lcg {
fn next(&mut self) -> u64 {
self.0 = self
.0
.wrapping_mul(6_364_136_223_846_793_005)
.wrapping_add(1_442_695_040_888_963_407);
self.0 >> 33
}
fn below(&mut self, n: usize) -> usize {
(self.next() % n as u64) as usize
}
}
fn encode(
image: &TokenizerImage,
prompt: Prompt<'_>,
text: &str,
split: SplitSpecialTokens,
) -> (Vec<TokenId>, Vec<Range<usize>>) {
let (mut ids, mut spans) = (Vec::new(), Vec::new());
image
.encode_joint(
&prompt,
text,
split,
&mut TokenizerScratch::new(),
Encoded {
ids: &mut ids,
spans: Some(&mut spans),
},
)
.expect("encodes");
(ids, spans)
}
const ATOMS: [&str; 30] = [
"a", "b", "x", "y", "aa", "ab", "xy", " ", " ", " ", " ", " ", ">", "<", "/", "</", "> </",
":", "q", "<m>", "<m>!", "<|ima", "ge>", "<|image>", "<image|>", "é", "日", "\u{2581}", ". ",
"\n\n",
];
fn generated(rng: &mut Lcg, atoms: usize) -> String {
(0..atoms).map(|_| ATOMS[rng.below(ATOMS.len())]).collect()
}
fn range(rng: &mut Lcg, text: &str) -> (usize, usize) {
let bounds: Vec<usize> = text
.char_indices()
.map(|(at, _)| at)
.chain([text.len()])
.collect();
match rng.below(8) {
0 => (0, text.len()),
_ => {
let (i, j) = (rng.below(bounds.len()), rng.below(bounds.len()));
(bounds[i.min(j)], bounds[i.max(j)])
}
}
}
const PROMPTS: [Prompt<'static>; 11] = [
Prompt::Bare,
Prompt::Document(None),
Prompt::Document(Some("ab")),
Prompt::SearchQuery,
Prompt::Custom("task: legal clause retrieval | query:"),
Prompt::Custom("markup: <b>"),
Prompt::Custom("task: search result | query:"),
Prompt::Custom("look <|ima"),
Prompt::Custom("q <"),
Prompt::Custom("a "),
Prompt::Custom("<m>"),
];
const BOUNDARY: [(&str, &str); 5] = [
("task: legal clause retrieval | query:", "</task> hi"),
("q <", "/x a"),
("markup: <b>", " </b> bold"),
("task: search result | query:", " bar"),
("look <|ima", "ge> now"),
];
fn check_range(
image: &TokenizerImage,
measure: &JointMeasure<'_>,
prompt: Prompt<'_>,
split: SplitSpecialTokens,
text: &str,
(start, end): (usize, usize),
) {
let (ids, spans) = encode(image, prompt, &text[start..end], split);
let direct = ids.len();
let context = || format!("{prompt} ⧺ {:?} under {split}", &text[start..end]);
assert_eq!(
measure.measure_range(start, end).expect("measures"),
direct,
"{}",
context()
);
for limit in [direct.saturating_sub(1), direct, direct + 1, 0] {
assert_eq!(
measure.measure_within(&text[start..end], limit),
(direct <= limit).then_some(direct),
"{} within {limit}",
context()
);
}
if start < end {
let seam = prompt.pieces().concat().len();
let content = &spans[1..spans.len() - 1];
let first = content
.iter()
.position(|span| span.end > seam)
.expect("the content has a token");
assert!(content[first].start <= seam, "{}", context());
assert_eq!(content[first].start.max(seam) - seam, 0, "{}", context());
assert!(
content[..first].iter().all(|span| span.end <= seam),
"{}",
context()
);
}
}
#[test]
fn the_measure_is_the_joint_encoding() {
let (image, _file) = fixture::mini();
let mut rng = Lcg(0x7e8);
let mut compared = 0usize;
for _ in 0..120 {
let atoms = 4 + rng.below(40);
let text = generated(&mut rng, atoms);
for split in [SplitSpecialTokens::Recognize, SplitSpecialTokens::Split] {
for prompt in PROMPTS {
let measure = JointMeasure::new(&image, &text, prompt, split).expect("measures");
for _ in 0..6 {
let bounds = range(&mut rng, &text);
check_range(&image, &measure, prompt, split, &text, bounds);
compared += 1;
}
}
}
}
for (prefix, content) in BOUNDARY {
for split in [SplitSpecialTokens::Recognize, SplitSpecialTokens::Split] {
let text = format!("{content} aa xy. </b> {content}");
let prompt = Prompt::Custom(prefix);
let measure = JointMeasure::new(&image, &text, prompt, split).expect("measures");
for (end, _) in text.char_indices().skip(1) {
check_range(&image, &measure, prompt, split, &text, (0, end));
compared += 1;
}
check_range(&image, &measure, prompt, split, &text, (0, text.len()));
}
}
assert!(compared > 15_000, "{compared}");
}
#[test]
fn the_piece_table_cuts_at_word_boundaries_off_the_bridging_site() {
let (image, _file) = fixture::mini();
let seams = Seams::of(&image).expect("the seams");
assert!(seams.cuts);
assert_eq!(seams.reach, 1);
let text = "aa xy > </b ab";
let table = PieceTable::build(
&image,
text,
SplitSpecialTokens::Recognize,
&seams,
&mut TokenizerScratch::new(),
)
.expect("builds");
assert_eq!(table.cuts, [2, 5, 11]);
let (ids, spans) = encode(&image, Prompt::Bare, text, SplitSpecialTokens::Recognize);
for (cut, before) in table.cuts.iter().zip(&table.before) {
let counted = spans[1..ids.len() - 1]
.iter()
.filter(|span| span.start < *cut)
.count();
assert_eq!(*before, counted, "before {cut}");
}
}
#[test]
fn the_prompt_joins_at_its_last_piece() {
let (image, _file) = fixture::mini();
let seams = Seams::of(&image).expect("the seams");
let at = |prompt: &str| join_point(prompt, &seams);
assert_eq!(at("title: none | text: "), 19);
assert_eq!(at("task: legal clause retrieval | query:"), 30);
assert_eq!(at("q <"), 1);
assert_eq!(at("x > "), 1);
assert_eq!(at("y:"), 0);
assert_eq!(at(""), 0);
}
fn paragraphs(rng: &mut Lcg, count: usize) -> String {
const WORDS: [&str; 10] = ["aa", "ab", "xy", "b", "a", "x", "y", "é", "日日", "<m>"];
(0..count)
.map(|_| {
(0..1 + rng.below(4))
.map(|_| {
let words: Vec<&str> = (0..2 + rng.below(9))
.map(|_| WORDS[rng.below(WORDS.len())])
.collect();
format!("{}.", words.join(" "))
})
.collect::<Vec<_>>()
.join(" ")
})
.collect::<Vec<_>>()
.join("\n\n")
}
fn mini_framing(image: &TokenizerImage, prompt: Prompt<'_>, split: SplitSpecialTokens) -> Framing {
let mut scratch = TokenizerScratch::new();
let table = framing_table(image, &mut scratch).expect("frames");
framing_of(image, &table, prompt, split, &mut scratch).expect("frames")
}
#[test]
fn the_windows_cover_the_text_and_each_carries_the_prompt() {
let (image, _file) = fixture::mini();
let mut rng = Lcg(0x77);
let mut scratch = TokenizerScratch::new();
let mut multi = 0;
for case in 0..60 {
let count = 1 + rng.below(5);
let text = paragraphs(&mut rng, count);
let prompt = [
Prompt::Bare,
Prompt::Custom("q <"),
Prompt::Custom("markup: <b>"),
Prompt::Document(Some("ab")),
][case % 4];
for split in [SplitSpecialTokens::Recognize, SplitSpecialTokens::Split] {
let framing = mini_framing(&image, prompt, split);
for extra in [3, 7, 16] {
let window = framing.ids() + extra;
for hop in [window, window - 2] {
let geometry = LongTextOptions::from(WindowOptions::new(window).with_hop(hop));
let windows = plan(
&image,
&text,
prompt,
split,
&geometry,
framing,
&mut scratch,
)
.expect("plans");
assert!(!windows.is_empty());
multi += usize::from(windows.len() > 1);
let mut covered = 0;
for (index, planned) in windows.iter().enumerate() {
let chunk = text.get(planned.range.clone()).expect("char-aligned");
assert!(planned.range.start <= covered, "{prompt} {index}: a gap");
if hop == window && index > 0 {
assert_eq!(
planned.range.start, covered,
"{prompt} {index}: not a partition"
);
}
covered = covered.max(planned.range.end);
assert_eq!(
planned.ids,
encode(&image, prompt, chunk, split).0,
"{prompt}: window {index} ({chunk:?}) does not carry the prompt"
);
let lone = chunk.chars().count() == 1 || !chunk.chars().any(char::is_alphanumeric);
assert!(
planned.ids.len() <= window || lone,
"{prompt}: window {index} holds {} ids against {window}",
planned.ids.len()
);
assert!(planned.ids.len() <= MAX_TOKENS);
}
assert_eq!(windows[0].range.start, 0);
assert_eq!(covered, text.len());
}
}
}
}
assert!(multi > 100, "{multi}");
}
#[test]
fn a_text_that_fits_one_window_is_embeds_ids() {
let (image, _file) = fixture::mini();
let mut scratch = TokenizerScratch::new();
for prompt in PROMPTS {
for text in ["ab", "aa xy. b", "\n\n ", "</b> x", "ge> now"] {
let split = SplitSpecialTokens::Recognize;
let framing = mini_framing(&image, prompt, split);
let windows = plan(
&image,
text,
prompt,
split,
&LongTextOptions::new(),
framing,
&mut scratch,
)
.expect("plans");
assert_eq!(windows.len(), 1, "{prompt} ⧺ {text:?}");
assert_eq!(windows[0].range, 0..text.len());
assert_eq!(windows[0].ids, encode(&image, prompt, text, split).0);
}
}
}
#[test]
fn a_call_is_refused_by_name_in_order() {
let (image, _file) = fixture::mini();
let mut scratch = TokenizerScratch::new();
let split = SplitSpecialTokens::Recognize;
let custom = mini_framing(&image, Prompt::Custom("q <"), split);
assert!(custom.is_conservative());
assert_eq!(
custom.ids(),
encode(&image, Prompt::Bare, "q <", split).0.len()
);
let mut call = |text: &str, prompt: Prompt<'_>, geometry: LongTextOptions| {
let framing = mini_framing(&image, prompt, split);
plan(
&image,
text,
prompt,
split,
&geometry,
framing,
&mut scratch,
)
};
let window = |w: usize| LongTextOptions::from(WindowOptions::new(w));
match call("aa ab", Prompt::Bare, window(513).with_max_input_bytes(4)) {
Err(Error::InputTooLarge(e)) => assert_eq!((e.got(), e.max()), (5, 4)),
other => panic!("expected InputTooLarge, got {other:?}"),
}
match call("aa ab", Prompt::Bare, window(513)) {
Err(Error::WindowOverBudget(e)) => assert_eq!((e.window(), e.max()), (513, 512)),
other => panic!("expected WindowOverBudget, got {other:?}"),
}
assert!(matches!(
call("aa ab", Prompt::Bare, window(0)),
Err(Error::Windowing(_))
));
match call("aa ab", Prompt::Custom("q <"), window(custom.ids())) {
Err(Error::PromptFillsWindow(e)) => {
assert_eq!((e.window(), e.framing()), (custom.ids(), custom.ids()))
}
other => panic!("expected PromptFillsWindow, got {other:?}"),
}
assert!(matches!(
call("", Prompt::Bare, window(2)),
Err(Error::PromptFillsWindow(_))
));
assert!(matches!(
call("", Prompt::Bare, window(3)),
Err(Error::EmptyText)
));
let planned = call("aa ab", Prompt::Custom("q <"), window(custom.ids() + 1)).expect("plans");
assert_eq!(planned.last().map(|w| w.range.end), Some(5));
}
#[test]
fn one_character_past_the_models_512_is_refused_by_name() {
let (image, _file) = fixture::mini();
let mut scratch = TokenizerScratch::new();
let split = SplitSpecialTokens::Recognize;
let prefix = "b".repeat(509);
let prompt = Prompt::Custom(&prefix);
let framing = mini_framing(&image, prompt, split);
assert_eq!(framing.ids(), 511);
let text = "\u{1f600}";
assert_eq!(encode(&image, prompt, text, split).0.len(), 515);
match plan(
&image,
text,
prompt,
split,
&LongTextOptions::new(),
framing,
&mut scratch,
) {
Err(Error::PromptFillsWindow(e)) => assert_eq!((e.window(), e.framing()), (512, 511)),
other => panic!("expected PromptFillsWindow at 512, got {other:?}"),
}
let fits = plan(
&image,
"b",
prompt,
split,
&LongTextOptions::new(),
framing,
&mut scratch,
)
.expect("plans");
assert_eq!(fits.len(), 1);
assert_eq!(fits[0].ids.len(), 512);
}
fn vector(seed: u32) -> Embedding {
let mut row = [0f32; EMBEDDING_DIM];
for (i, v) in row.iter_mut().enumerate() {
*v = ((i as u32).wrapping_mul(2_654_435_761).wrapping_add(seed) % 1_000) as f32 / 1_000.0 - 0.5;
}
Embedding::from_output(&row).expect("a finite, non-zero row")
}
#[test]
fn one_window_folds_to_itself_bit_for_bit() {
let window = WindowEmbedding::new(0, 0..10, Span::new(0, 300, MAX_TOKENS), vector(7));
let folded = windit::aggregate::aggregate(
&windit::aggregate::CoverageWeightedMean,
&[Windowed::new(
window.embedding().clone(),
window.token_span(),
)],
)
.expect("folds");
assert_eq!(folded.as_slice(), window.embedding().as_slice());
for kind in [
AggregatePolicyKind::CoverageWeightedMean,
AggregatePolicyKind::MeanRenormalized,
AggregatePolicyKind::EmaRenormalized(EmaRenormalizedOptions::new(0.5)),
] {
let alone = fold_windows(vec![window.clone()], &kind).expect("folds");
assert_eq!(alone.as_slice(), window.embedding().as_slice(), "{kind}");
}
}
#[test]
fn the_fold_option_is_honoured() {
let windows: Vec<WindowEmbedding> = [(508, 1), (508, 2), (308, 3)]
.iter()
.scan(0, |offset, &(ids, seed)| {
let span = Span::new(*offset, ids, MAX_TOKENS);
*offset += ids;
Some(WindowEmbedding::new(
seed as usize - 1,
0..1,
span,
vector(seed),
))
})
.collect();
let kinds = [
AggregatePolicyKind::CoverageWeightedMean,
AggregatePolicyKind::MeanRenormalized,
AggregatePolicyKind::EmaRenormalized(EmaRenormalizedOptions::new(0.5)),
];
let mut folded = Vec::new();
for kind in kinds {
let windowed: Vec<Windowed<Embedding>> = windows
.iter()
.map(|w| Windowed::new(w.embedding().clone(), w.token_span()))
.collect();
let expected =
windit::aggregate::aggregate(kind.into_policy().as_ref(), &windowed).expect("folds");
let got = fold_windows(windows.clone(), &kind).expect("folds");
assert_eq!(got.as_slice(), expected.as_slice(), "{kind}");
folded.push(got);
}
assert_ne!(folded[0].as_slice(), folded[1].as_slice());
assert_ne!(folded[0].as_slice(), folded[2].as_slice());
}
fn staged_dir() -> std::path::PathBuf {
std::env::var_os("EMBEDDINGGEMMA2_TEST_MODELS").map_or_else(
|| {
std::path::PathBuf::from(concat!(
env!("CARGO_MANIFEST_DIR"),
"/../Models/embeddinggemma2"
))
},
std::path::PathBuf::from,
)
}
#[test]
#[ignore = "needs the staged EmbeddingGemma 2 tree (EMBEDDINGGEMMA2_TEST_MODELS)"]
fn the_staged_measure_is_the_joint_encoding() {
use crate::embeddings::embeddinggemma2::text::TextArtifacts;
let artifacts = TextArtifacts::open(staged_dir()).expect("the staged text part opens");
let image = artifacts.image();
let golden: serde_json::Value = serde_json::from_slice(
&std::fs::read(
std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests/embeddinggemma2/fixtures/goldens/text.json"),
)
.expect("the golden reads"),
)
.expect("parses");
let mut rng = Lcg(0x8a);
let mut compared = 0usize;
for item in golden["items"].as_array().expect("items") {
let source = item["source"].as_str().expect("source");
if source != "long" && source != "boundary" {
continue;
}
let prefix = item["prefix"].as_str().expect("prefix");
let text = item["text"].as_str().expect("text");
let prompt = if prefix.is_empty() {
Prompt::Bare
} else {
Prompt::Custom(prefix)
};
for split in [SplitSpecialTokens::Recognize, SplitSpecialTokens::Split] {
let measure = JointMeasure::new(image, text, prompt, split).expect("measures");
if source == "boundary" {
for (end, _) in text.char_indices().skip(1) {
check_range(image, &measure, prompt, split, text, (0, end));
compared += 1;
}
check_range(image, &measure, prompt, split, text, (0, text.len()));
} else {
for _ in 0..200 {
let bounds = range(&mut rng, text);
check_range(image, &measure, prompt, split, text, bounds);
compared += 1;
}
}
}
}
eprintln!("T8 (staged): {compared} ranges equal the direct joint encoding");
assert!(compared > 1_600, "{compared}");
}
#[test]
fn an_allocation_refused_while_measuring_is_answered_by_name() {
let (image, _file) = fixture::mini();
let text = "aa xy. b aa xy. b aa xy. b";
let measure =
JointMeasure::new(&image, text, Prompt::Bare, SplitSpecialTokens::Recognize).expect("measures");
let window = WindowOptions::new(64).with_max_windows(1);
let _refuse = alloc::inject::refuse(AllocationSite::TokenIds, 0);
match long_text::chunk_long(&measure, text, &window, MAX_TOKENS) {
Err(Error::Allocation(refused)) => assert_eq!(refused.site(), AllocationSite::TokenIds),
other => panic!("expected the measure's allocation by name, got {other:?}"),
}
}
#[test]
fn a_refused_chunk_list_is_answered_by_name() {
let (image, _file) = fixture::mini();
let text = "aa xy. b";
let measure =
JointMeasure::new(&image, text, Prompt::Bare, SplitSpecialTokens::Recognize).expect("measures");
let _refuse = alloc::inject::refuse(AllocationSite::WindowList, 0);
match long_text::chunk_long(&measure, text, &WindowOptions::new(64), MAX_TOKENS) {
Err(Error::Allocation(refused)) => assert_eq!(refused.site(), AllocationSite::WindowList),
other => panic!("expected the chunk list's allocation by name, got {other:?}"),
}
}