#![allow(missing_docs)]
use crate::multivector::{
exact_maxsim, MockMultiVectorEmbedder, MultiVectorEmbedder, MultiVectorEmbedding,
ResidualCodec, WarpIndex, WarpIndexConfig, WarpSearchConfig,
};
use crate::{Chunk, DocumentId};
use std::time::Instant;
#[derive(Debug)]
pub struct FalsificationReport {
pub experimentum_crucis: ConjectureResult,
pub conjecture_1_compression: ConjectureResult,
pub conjecture_2_pruning: ConjectureResult,
pub conjecture_3_scaling: ConjectureResult,
pub overall_verdict: Verdict,
}
#[derive(Debug, Clone)]
pub struct ConjectureResult {
pub name: String,
pub hypothesis: String,
pub threshold: String,
pub observed_value: String,
pub verdict: Verdict,
pub details: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Verdict {
Corroborated,
Falsified,
}
impl std::fmt::Display for Verdict {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Verdict::Corroborated => write!(f, "CORROBORATED"),
Verdict::Falsified => write!(f, "FALSIFIED"),
}
}
}
impl FalsificationReport {
pub fn print_report(&self) {
println!("\n======== WARP FALSIFICATION REPORT ========");
println!();
self.print_conjecture(&self.experimentum_crucis);
self.print_conjecture(&self.conjecture_1_compression);
self.print_conjecture(&self.conjecture_2_pruning);
self.print_conjecture(&self.conjecture_3_scaling);
println!("======== FINAL VERDICT ========");
println!("Overall: {}", self.overall_verdict);
if self.overall_verdict == Verdict::Falsified {
println!("\n*** STOP THE LINE (Jidoka) ***");
println!("The current implementation has been FALSIFIED.");
println!("Do NOT patch. Reformulate the theory.");
}
}
fn print_conjecture(&self, result: &ConjectureResult) {
println!("--- {} ---", result.name);
println!("Hypothesis: {}", result.hypothesis);
println!("Threshold: {}", result.threshold);
println!("Observed: {}", result.observed_value);
println!("Verdict: {}", result.verdict);
println!("Details: {}", result.details);
println!();
}
}
fn error_to_falsified(name: &str, e: crate::Error) -> ConjectureResult {
ConjectureResult {
name: name.to_string(),
hypothesis: String::new(),
threshold: String::new(),
observed_value: format!("Error: {e}"),
verdict: Verdict::Falsified,
details: format!("Unexpected error during falsification: {e}"),
}
}
fn overall_verdict(results: &[&ConjectureResult]) -> Verdict {
let any_falsified = results.iter().any(|r| r.verdict == Verdict::Falsified);
if any_falsified {
Verdict::Falsified
} else {
Verdict::Corroborated
}
}
pub fn execute_falsification_plan() -> FalsificationReport {
let experimentum =
test_experimentum_crucis().unwrap_or_else(|e| error_to_falsified("Experimentum Crucis", e));
let compression = test_conjecture_1_compression()
.unwrap_or_else(|e| error_to_falsified("Conjecture 1: Compression", e));
let pruning = test_conjecture_2_pruning()
.unwrap_or_else(|e| error_to_falsified("Conjecture 2: Pruning", e));
let scaling = test_conjecture_3_scaling()
.unwrap_or_else(|e| error_to_falsified("Conjecture 3: Scaling", e));
let overall = overall_verdict(&[&experimentum, &compression, &pruning, &scaling]);
FalsificationReport {
experimentum_crucis: experimentum,
conjecture_1_compression: compression,
conjecture_2_pruning: pruning,
conjecture_3_scaling: scaling,
overall_verdict: overall,
}
}
fn test_experimentum_crucis() -> crate::Result<ConjectureResult> {
let name = "Experimentum Crucis (Hard Negatives)".to_string();
let hypothesis =
"Token-level interaction captures nuances that single-vector misses".to_string();
let threshold = "WARP MRR@10 delta >= 15% over single-vector".to_string();
let embedder = MockMultiVectorEmbedder::new(32, 256);
let hard_negative_pairs = vec![
(
"The quick brown cat is sitting comfortably on the soft mat today",
"The quick brown cat is definitely NOT sitting on the soft mat today",
),
(
"Machine learning algorithms significantly improve model accuracy and performance metrics",
"Machine learning algorithms do not significantly improve model accuracy and performance",
),
(
"The controlled scientific experiment succeeded beyond our initial expectations completely",
"The controlled scientific experiment failed miserably beyond our initial expectations",
),
(
"All registered users have full access to the secure system features",
"No registered users have any access to the secure system features",
),
(
"The credit card payment was successfully processed through the gateway",
"The credit card payment was unfortunately rejected by the gateway",
),
(
"The optimization algorithm converges quickly toward the global minimum solution",
"The optimization algorithm never converges toward the global minimum solution",
),
(
"The ambient temperature is steadily rising throughout the experimental period",
"The ambient temperature is rapidly falling throughout the experimental period",
),
(
"The encrypted network connection is completely secure against external attacks",
"The encrypted network connection is dangerously compromised by external attacks",
),
(
"The database query returned all matching records from the main table",
"The database query returned no matching records from the main table",
),
(
"The software test cases passed successfully without any critical errors",
"The software test cases failed completely with many critical errors",
),
];
let config = WarpIndexConfig::new(2, 4, 32).with_kmeans_iterations(10);
let mut index = WarpIndex::new(config);
let mut all_texts: Vec<String> = Vec::new();
for (pos, neg) in &hard_negative_pairs {
all_texts.push(pos.to_string());
all_texts.push(neg.to_string());
}
let training_embeddings: Vec<MultiVectorEmbedding> =
all_texts.iter().map(|t| embedder.embed_tokens(t)).collect::<crate::Result<Vec<_>>>()?;
if let Err(e) = index.train(&training_embeddings) {
return Ok(ConjectureResult {
name,
hypothesis,
threshold,
observed_value: format!("Training failed: {e}"),
verdict: Verdict::Falsified,
details: "Could not train index".to_string(),
});
}
for text in all_texts.iter() {
let chunk = Chunk::new(DocumentId::new(), text.clone(), 0, text.len());
let embedding = embedder.embed_tokens(text)?;
index.insert(chunk, embedding)?;
}
index.build()?;
let num_queries = hard_negative_pairs.len();
let (warp_mrr, single_mrr) =
compute_warp_vs_single_mrr(&hard_negative_pairs, &embedder, &index, &all_texts)?;
let delta_percent =
if single_mrr > 0.0 { ((warp_mrr - single_mrr) / single_mrr) * 100.0 } else { 100.0 };
let observed = format!(
"WARP MRR@10={:.4}, Single-Vector MRR@10={:.4}, Delta={:.2}%",
warp_mrr, single_mrr, delta_percent
);
let verdict = if delta_percent >= 15.0 { Verdict::Corroborated } else { Verdict::Falsified };
let details = format!(
"Tested {} hard negative pairs. WARP {} the 15% improvement threshold.",
num_queries,
if verdict == Verdict::Corroborated { "met" } else { "failed to meet" }
);
Ok(ConjectureResult { name, hypothesis, threshold, observed_value: observed, verdict, details })
}
fn compute_warp_vs_single_mrr(
hard_negative_pairs: &[(&str, &str)],
embedder: &MockMultiVectorEmbedder,
index: &WarpIndex,
all_texts: &[String],
) -> crate::Result<(f64, f64)> {
let mut warp_mrr_sum = 0.0;
let mut single_vector_mrr_sum = 0.0;
let num_queries = hard_negative_pairs.len();
for (query_idx, (positive, _negative)) in hard_negative_pairs.iter().enumerate() {
let query_embedding = embedder.embed_tokens(positive)?;
let search_config = WarpSearchConfig::with_k(10);
let warp_results = index.search(&query_embedding, &search_config)?;
let warp_rank = warp_results
.iter()
.position(|(chunk_id, _)| {
index.get_chunk(chunk_id).map(|c| c.content == *positive).unwrap_or(false)
})
.map(|r| r + 1);
if let Some(rank) = warp_rank {
warp_mrr_sum += 1.0 / rank as f64;
}
let positive_doc_idx = query_idx * 2;
let single_rank =
compute_single_vector_rank(embedder, &query_embedding, all_texts, positive_doc_idx)?;
if let Some(rank) = single_rank {
single_vector_mrr_sum += 1.0 / rank as f64;
}
}
Ok((warp_mrr_sum / num_queries as f64, single_vector_mrr_sum / num_queries as f64))
}
fn compute_single_vector_rank(
embedder: &MockMultiVectorEmbedder,
query_embedding: &MultiVectorEmbedding,
all_texts: &[String],
target_idx: usize,
) -> crate::Result<Option<usize>> {
let query_avg = average_embedding(query_embedding);
let mut scores: Vec<(usize, f64)> = all_texts
.iter()
.enumerate()
.map(|(i, text)| {
let doc_emb = embedder.embed_tokens(text)?;
let doc_avg = average_embedding(&doc_emb);
let score = cosine_similarity(&query_avg, &doc_avg);
Ok((i, score))
})
.collect::<crate::Result<Vec<_>>>()?;
scores.sort_by(|a, b| b.1.total_cmp(&a.1));
Ok(scores.iter().position(|(i, _)| *i == target_idx).map(|r| r + 1))
}
fn test_conjecture_1_compression() -> crate::Result<ConjectureResult> {
let name = "Conjecture 1: Compression Preserves Score Ordering".to_string();
let hypothesis =
"Residual quantization preserves relative ordering of MaxSim scores".to_string();
let threshold = "Kendall's tau >= 0.90".to_string();
let embedder = MockMultiVectorEmbedder::new(32, 128);
let documents: Vec<String> = (0..50)
.map(|i| {
format!(
"Document number {} comprehensively discusses topic {} with various {} and {} concepts and ideas",
i,
i % 10,
if i % 2 == 0 { "scientific" } else { "technical" },
if i % 3 == 0 {
"advanced theoretical"
} else {
"fundamental practical"
}
)
})
.collect();
let doc_embeddings: Vec<MultiVectorEmbedding> =
documents.iter().map(|d| embedder.embed_tokens(d)).collect::<crate::Result<Vec<_>>>()?;
let all_tokens: Vec<f32> =
doc_embeddings.iter().flat_map(|e| e.as_slice().iter().copied()).collect();
let codec = match ResidualCodec::train(&all_tokens, 32, 8, 4, 20) {
Ok(c) => c,
Err(e) => {
return Ok(ConjectureResult {
name,
hypothesis,
threshold,
observed_value: format!("Codec training failed: {e}"),
verdict: Verdict::Falsified,
details: "Could not train codec".to_string(),
})
}
};
let queries: Vec<String> = vec![
"scientific concepts in documents".to_string(),
"technical advanced topics".to_string(),
"fundamental discussion of ideas".to_string(),
];
let query_embeddings: Vec<MultiVectorEmbedding> =
queries.iter().map(|q| embedder.embed_tokens(q)).collect::<crate::Result<Vec<_>>>()?;
let mut exact_scores: Vec<f64> = Vec::new();
let mut approx_scores: Vec<f64> = Vec::new();
for query_emb in &query_embeddings {
for doc_emb in &doc_embeddings {
let exact = exact_maxsim(query_emb, doc_emb);
exact_scores.push(exact as f64);
let approx = compute_quantized_maxsim(&codec, query_emb, doc_emb);
approx_scores.push(approx);
}
}
let tau = kendalls_tau(&exact_scores, &approx_scores);
let observed = format!("Kendall's tau = {:.4}", tau);
let verdict = if tau >= 0.90 { Verdict::Corroborated } else { Verdict::Falsified };
let details = format!(
"Computed rank correlation over {} score pairs. Tau {} threshold.",
exact_scores.len(),
if verdict == Verdict::Corroborated { "meets" } else { "below" }
);
Ok(ConjectureResult { name, hypothesis, threshold, observed_value: observed, verdict, details })
}
fn test_conjecture_2_pruning() -> crate::Result<ConjectureResult> {
let name = "Conjecture 2: Centroid Pruning Recall".to_string();
let hypothesis =
"Top-nprobe centroids contain relevant tokens for accurate retrieval".to_string();
let threshold = "recall@10 (nprobe=4) vs exhaustive >= 95%".to_string();
let embedder = MockMultiVectorEmbedder::new(32, 64);
let documents: Vec<String> = (0..100)
.map(|i| {
format!(
"Document number {} contains detailed information about topic {} in category {} covering various aspects",
i,
i % 10,
i % 5
)
})
.collect();
let config = WarpIndexConfig::new(2, 4, 32).with_kmeans_iterations(10);
let mut index = WarpIndex::new(config);
let embeddings: Vec<MultiVectorEmbedding> =
documents.iter().map(|d| embedder.embed_tokens(d)).collect::<crate::Result<Vec<_>>>()?;
if let Err(e) = index.train(&embeddings) {
return Ok(ConjectureResult {
name,
hypothesis,
threshold,
observed_value: format!("Training failed: {e}"),
verdict: Verdict::Falsified,
details: "Could not train index".to_string(),
});
}
for (i, doc) in documents.iter().enumerate() {
let chunk = Chunk::new(DocumentId::new(), doc.clone(), 0, doc.len());
index.insert(chunk, embeddings[i].clone())?;
}
index.build()?;
let queries =
vec!["information about topic 5", "document in category 2", "number contains information"];
let mut total_recall = 0.0;
let num_queries = queries.len();
for query in &queries {
let query_emb = embedder.embed_tokens(query)?;
let exhaustive_config = WarpSearchConfig::with_k(10).nprobe(8).bound(1000);
let exhaustive_results = index.search(&query_emb, &exhaustive_config)?;
let exhaustive_ids: std::collections::HashSet<_> =
exhaustive_results.iter().map(|(id, _)| id.clone()).collect();
let pruned_config = WarpSearchConfig::with_k(10).nprobe(4).bound(128);
let pruned_results = index.search(&query_emb, &pruned_config)?;
let pruned_ids: std::collections::HashSet<_> =
pruned_results.iter().map(|(id, _)| id.clone()).collect();
let intersection = pruned_ids.intersection(&exhaustive_ids).count();
let recall = if exhaustive_ids.is_empty() {
1.0
} else {
intersection as f64 / exhaustive_ids.len() as f64
};
total_recall += recall;
}
let avg_recall = total_recall / num_queries as f64;
let observed = format!("recall@10 = {:.4} ({:.2}%)", avg_recall, avg_recall * 100.0);
let verdict = if avg_recall >= 0.95 { Verdict::Corroborated } else { Verdict::Falsified };
let details = format!(
"Tested {} queries. Recall {} 95% threshold.",
num_queries,
if verdict == Verdict::Corroborated { "meets" } else { "below" }
);
Ok(ConjectureResult { name, hypothesis, threshold, observed_value: observed, verdict, details })
}
fn test_conjecture_3_scaling() -> crate::Result<ConjectureResult> {
let name = "Conjecture 3: Scaling Laws".to_string();
let hypothesis = "Memory scales with N*T*bits, latency scales with nprobe not N".to_string();
let threshold = "Memory < theoretical*1.2, latency O(nprobe) not O(N)".to_string();
let embedder = MockMultiVectorEmbedder::new(32, 64);
let corpus_sizes = [500, 1000, 2000];
let mut memory_results: Vec<(usize, usize)> = Vec::new();
let mut latency_results: Vec<(usize, f64)> = Vec::new();
for &n in &corpus_sizes {
let documents: Vec<String> = (0..n)
.map(|i| {
format!(
"Document {} about topic {} in field {} with additional context and details",
i,
i % 50,
i % 10
)
})
.collect();
let num_centroids = 8.max(n / 100);
let config = WarpIndexConfig::new(2, num_centroids, 32).with_kmeans_iterations(5);
let mut index = WarpIndex::new(config);
let embeddings: Vec<MultiVectorEmbedding> = documents
.iter()
.map(|d| embedder.embed_tokens(d))
.collect::<crate::Result<Vec<_>>>()?;
if index.train(&embeddings).is_err() {
continue;
}
for (i, doc) in documents.iter().enumerate() {
let chunk = Chunk::new(DocumentId::new(), doc.clone(), 0, doc.len());
let _ = index.insert(chunk, embeddings[i].clone());
}
let _ = index.build();
let memory = index.memory_usage();
memory_results.push((n, memory));
let query_emb = embedder.embed_tokens("topic document field")?;
let search_config = WarpSearchConfig::with_k(10).nprobe(4);
let start = Instant::now();
let num_queries = 50;
for _ in 0..num_queries {
let _ = index.search(&query_emb, &search_config);
}
let elapsed = start.elapsed();
let avg_latency_us = elapsed.as_micros() as f64 / num_queries as f64;
latency_results.push((n, avg_latency_us));
}
let memory_ok = if memory_results.len() >= 2 {
let (n1, m1) = memory_results[0];
let (n2, m2) = memory_results[1];
let ratio = m2 as f64 / m1 as f64;
let expected_ratio = n2 as f64 / n1 as f64;
(ratio / expected_ratio).abs() < 1.5 && (ratio / expected_ratio).abs() > 0.5
} else {
true
};
let latency_ok = if latency_results.len() >= 2 {
let (n1, l1) = latency_results[0];
let (n2, l2) = latency_results[1];
let latency_ratio = l2 / l1;
let n_ratio = n2 as f64 / n1 as f64;
latency_ratio < n_ratio * 0.8 } else {
true
};
let observed = format!(
"Memory: {:?}, Latency: {:?}",
memory_results
.iter()
.map(|(n, m)| format!("N={}: {}KB", n, m / 1024))
.collect::<Vec<_>>()
.join(", "),
latency_results
.iter()
.map(|(n, l)| format!("N={}: {:.0}us", n, l))
.collect::<Vec<_>>()
.join(", ")
);
let verdict = if memory_ok && latency_ok { Verdict::Corroborated } else { Verdict::Falsified };
let details = format!(
"Memory scaling: {}, Latency scaling: {}",
if memory_ok { "OK" } else { "FAILED" },
if latency_ok { "sub-linear (OK)" } else { "linear with N (FAILED)" }
);
Ok(ConjectureResult { name, hypothesis, threshold, observed_value: observed, verdict, details })
}
fn sum_token_vectors(mv: &MultiVectorEmbedding) -> Vec<f32> {
let mut acc = vec![0.0f32; mv.dim()];
for token in mv.tokens() {
for (i, &v) in token.iter().enumerate() {
acc[i] += v;
}
}
acc
}
fn scale_vector(vec: &mut [f32], divisor: f32) {
for v in vec.iter_mut() {
*v /= divisor;
}
}
fn normalize_vector(vec: &mut [f32]) {
let norm: f32 = vec.iter().map(|x| x * x).sum::<f32>().sqrt();
scale_vector_if_nonzero(vec, norm);
}
fn scale_vector_if_nonzero(vec: &mut [f32], divisor: f32) {
if divisor > 0.0 {
scale_vector(vec, divisor);
}
}
fn average_embedding(mv: &MultiVectorEmbedding) -> Vec<f32> {
if mv.num_tokens() == 0 {
return vec![0.0; mv.dim()];
}
let mut avg = sum_token_vectors(mv);
scale_vector(&mut avg, mv.num_tokens() as f32);
normalize_vector(&mut avg);
avg
}
fn dot_product_f64(a: &[f32], b: &[f32]) -> f64 {
a.iter().zip(b.iter()).map(|(x, y)| (*x as f64) * (*y as f64)).sum()
}
fn l2_norm_f64(v: &[f32]) -> f64 {
v.iter().map(|x| (*x as f64) * (*x as f64)).sum::<f64>().sqrt()
}
fn safe_div(numerator: f64, denominator: f64) -> f64 {
if denominator > 0.0 {
numerator / denominator
} else {
0.0
}
}
fn cosine_similarity(a: &[f32], b: &[f32]) -> f64 {
let dot = dot_product_f64(a, b);
let denom = l2_norm_f64(a) * l2_norm_f64(b);
safe_div(dot, denom)
}
fn quantized_token_score(codec: &ResidualCodec, query_token: &[f32], doc_token: &[f32]) -> f32 {
let (centroid_id, residual) = codec.compress(doc_token);
let centroid_score = codec.centroid_score(query_token, centroid_id);
codec.decompress_score(query_token, centroid_id, centroid_score, &residual)
}
fn max_quantized_score_for_query_token(
codec: &ResidualCodec,
query_token: &[f32],
doc: &MultiVectorEmbedding,
) -> f32 {
doc.tokens()
.map(|doc_token| quantized_token_score(codec, query_token, doc_token))
.fold(f32::NEG_INFINITY, f32::max)
}
fn accumulate_finite(total: f64, score: f32) -> f64 {
if score > f32::NEG_INFINITY {
total + score as f64
} else {
total
}
}
fn compute_quantized_maxsim(
codec: &ResidualCodec,
query: &MultiVectorEmbedding,
doc: &MultiVectorEmbedding,
) -> f64 {
query
.tokens()
.map(|qt| max_quantized_score_for_query_token(codec, qt, doc))
.fold(0.0, accumulate_finite)
}
fn classify_pair(x_diff: f64, y_diff: f64) -> i64 {
let product = x_diff * y_diff;
product.signum() as i64
}
fn count_concordant_discordant(x: &[f64], y: &[f64]) -> (i64, i64) {
let mut concordant = 0i64;
let mut discordant = 0i64;
for i in 0..x.len() {
for j in (i + 1)..x.len() {
let sign = classify_pair(x[i] - x[j], y[i] - y[j]);
concordant += i64::from(sign > 0);
discordant += i64::from(sign < 0);
}
}
(concordant, discordant)
}
fn kendalls_tau(x: &[f64], y: &[f64]) -> f64 {
if x.len() < 2 {
return 1.0;
}
let (concordant, discordant) = count_concordant_discordant(x, y);
let total = concordant + discordant;
safe_div_i64(concordant - discordant, total)
}
fn safe_div_i64(numerator: i64, denominator: i64) -> f64 {
if denominator == 0 {
1.0
} else {
numerator as f64 / denominator as f64
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_kendalls_tau_perfect_correlation() {
let x = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let y = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let tau = kendalls_tau(&x, &y);
assert!((tau - 1.0).abs() < 0.001);
}
#[test]
fn test_kendalls_tau_perfect_inverse() {
let x = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let y = vec![5.0, 4.0, 3.0, 2.0, 1.0];
let tau = kendalls_tau(&x, &y);
assert!((tau - (-1.0)).abs() < 0.001);
}
#[test]
fn test_cosine_similarity_identical() {
let a = vec![1.0, 0.0, 0.0];
let b = vec![1.0, 0.0, 0.0];
let sim = cosine_similarity(&a, &b);
assert!((sim - 1.0).abs() < 0.001);
}
#[test]
fn test_cosine_similarity_orthogonal() {
let a = vec![1.0, 0.0, 0.0];
let b = vec![0.0, 1.0, 0.0];
let sim = cosine_similarity(&a, &b);
assert!(sim.abs() < 0.001);
}
#[test]
fn test_falsification_plan() {
let report = execute_falsification_plan();
println!("\n");
report.print_report();
println!("\nTest completed. Overall verdict: {}", report.overall_verdict);
}
#[test]
fn test_experimentum_crucis_standalone() {
let result = test_experimentum_crucis().unwrap();
println!("\nExperimentum Crucis Result:");
println!(" Observed: {}", result.observed_value);
println!(" Verdict: {}", result.verdict);
}
#[test]
fn test_compression_conjecture_standalone() {
let result = test_conjecture_1_compression().unwrap();
println!("\nCompression Conjecture Result:");
println!(" Observed: {}", result.observed_value);
println!(" Verdict: {}", result.verdict);
}
#[test]
fn test_pruning_conjecture_standalone() {
let result = test_conjecture_2_pruning().unwrap();
println!("\nPruning Conjecture Result:");
println!(" Observed: {}", result.observed_value);
println!(" Verdict: {}", result.verdict);
}
#[test]
fn test_scaling_conjecture_standalone() {
let result = test_conjecture_3_scaling().unwrap();
println!("\nScaling Conjecture Result:");
println!(" Observed: {}", result.observed_value);
println!(" Verdict: {}", result.verdict);
}
}