pub fn rrf_explain<I, L>(
lists: &[L],
retriever_ids: &[RetrieverId],
config: RrfConfig,
) -> Vec<FusedResult<I>>Expand description
RRF with explainability: returns full provenance for each result.
This variant preserves which retrievers contributed each document, their original ranks, and how much each source contributed to the final RRF score.
§Arguments
lists- Ranked lists from each retrieverretriever_ids- Identifiers for each retriever (must matchlists.len())config- RRF configuration
§Returns
Results sorted by fused score (descending), with full explanation metadata.
§Example
use rankops::explain::{rrf_explain, RetrieverId};
use rankops::RrfConfig;
let bm25 = vec![("d1", 12.5), ("d2", 11.0)];
let dense = vec![("d2", 0.9), ("d3", 0.8)];
let retrievers = vec![
RetrieverId::new("bm25"),
RetrieverId::new("dense"),
];
let explained = rrf_explain(
&[&bm25[..], &dense[..]],
&retrievers,
RrfConfig::default(),
);
// d2 appears in both lists at rank 1 and 0 respectively
let d2 = explained.iter().find(|r| r.id == "d2").unwrap();
assert_eq!(d2.explanation.sources.len(), 2);
assert_eq!(d2.explanation.consensus_score, 1.0); // in both lists
// Check contributions
let bm25_contrib = d2.explanation.sources.iter()
.find(|s| s.retriever_id == "bm25")
.unwrap();
assert_eq!(bm25_contrib.original_rank, Some(1));
assert!(bm25_contrib.contribution > 0.0);