pub mod bit_stable_pref;
pub mod precision_floor;
pub mod strided_input_pref;
pub use bit_stable_pref::BitStablePreferenceFilter;
pub use precision_floor::{PrecisionFloorFilter, PrecisionRequirement};
pub use strided_input_pref::StridedInputPreferenceFilter;
use super::filter::AlternativeFilter;
pub fn default_chain(
precision_requirement: PrecisionRequirement,
) -> Vec<Box<dyn AlternativeFilter>> {
vec![
Box::new(PrecisionFloorFilter::new(precision_requirement)),
Box::new(StridedInputPreferenceFilter::default()),
Box::new(BitStablePreferenceFilter::default()),
]
}
#[cfg(test)]
mod tests {
use super::*;
use crate::fused::PrecisionGuarantee;
use crate::kernel::{KernelCaps, OpParams};
use crate::ranker::alternative_set::AlternativeSet;
use crate::ranker::candidate::Candidate;
use crate::ranker::chain::apply_filter_chain;
use crate::ranker::filter::FilterContext;
use fuel_ir::dispatch::OpKind;
use fuel_ir::probe::BackendId;
use fuel_ir::{DType, DeviceLocation, Layout, Result, Shape};
use fuel_memory::Storage;
use std::sync::{Arc, RwLock};
fn noop(
_i: &[Arc<RwLock<Storage>>],
_o: &mut [Arc<RwLock<Storage>>],
_l: &[Layout],
_p: &OpParams,
) -> Result<()> {
Ok(())
}
fn candidate(bit_stable: bool, strided_caps: bool) -> Candidate {
Candidate {
kernel: noop,
caps: if strided_caps {
KernelCaps::strided_input()
} else {
KernelCaps::empty()
},
backend: BackendId::Cpu,
device: DeviceLocation::Cpu,
precision: PrecisionGuarantee {
bit_stable_on_same_hardware: bit_stable,
..PrecisionGuarantee::PRIMITIVE_DETERMINISTIC_CPU
},
static_cost: Default::default(),
inbound_transfer_ns: 0,
op_params: OpParams::None,
coupling: Vec::new(),
kernel_source: "",
kernel_revision_hash: 0,
}
}
fn ctx<'a>() -> FilterContext<'a> {
static LAYOUTS: std::sync::OnceLock<Vec<Layout>> = std::sync::OnceLock::new();
let layouts = LAYOUTS.get_or_init(|| vec![Layout::contiguous(Shape::from(vec![4]))]);
FilterContext::new(OpKind::AddElementwise, &[DType::F32], layouts)
}
#[test]
fn default_chain_unconstrained_passes_everything() {
let chain = default_chain(PrecisionRequirement::default());
let mut set = AlternativeSet::from_candidates(vec![
candidate(false, false),
candidate(true, false),
candidate(false, true),
]);
apply_filter_chain(&mut set, &chain, &ctx()).expect("chain");
assert_eq!(set.len(), 1);
assert!(set.winner().unwrap().precision.bit_stable_on_same_hardware);
}
#[test]
fn default_chain_bit_stable_required_drops_unstable() {
let chain = default_chain(PrecisionRequirement::BIT_STABLE);
let mut set =
AlternativeSet::from_candidates(vec![candidate(false, false), candidate(true, false)]);
apply_filter_chain(&mut set, &chain, &ctx()).expect("chain");
assert_eq!(set.len(), 1);
assert!(set.winner().unwrap().precision.bit_stable_on_same_hardware);
}
#[test]
fn default_chain_no_bit_stable_falls_back() {
let chain = default_chain(PrecisionRequirement::BIT_STABLE);
let mut set =
AlternativeSet::from_candidates(vec![candidate(false, false), candidate(false, true)]);
let err = apply_filter_chain(&mut set, &chain, &ctx()).unwrap_err();
match err {
fuel_ir::Error::FilterRejected { filter, .. } => {
assert_eq!(filter, "precision-floor");
}
other => panic!("expected FilterRejected, got {other:?}"),
}
}
#[test]
fn default_chain_unconstrained_no_bit_stable_keeps_all() {
let chain = default_chain(PrecisionRequirement::default());
let mut set =
AlternativeSet::from_candidates(vec![candidate(false, false), candidate(false, true)]);
apply_filter_chain(&mut set, &chain, &ctx()).expect("chain");
assert_eq!(set.len(), 2, "no bit-stable + soft pref → no narrowing");
}
}