libm-test: Make extensive an attribute rather than a test type

Currently we run logspace tests for extensive tests, but there isn't any
reason we couldn't also run more kinds of tests more extensively (e.g.
more edge cases, combine edge cases with logspace for multi-input
functions, etc). As a first step toward making this possible, make
`extensive` a new field in `CheckCtx`, and rename `QuickSpaced` to
`Spaced`.
This commit is contained in:
Trevor Gross 2025-05-29 20:53:48 +00:00
parent 9f84e9904f
commit 8edaa6e5c8
6 changed files with 60 additions and 25 deletions

View file

@ -23,7 +23,7 @@ macro_rules! icount_benches {
let mut ctx = CheckCtx::new(
Op::IDENTIFIER,
CheckBasis::None,
GeneratorKind::QuickSpaced
GeneratorKind::Spaced
);
ctx.override_iterations(BENCH_ITER_ITEMS);
let ret = spaced::get_test_cases::<Op>(&ctx).0.collect::<Vec<_>>();

View file

@ -55,7 +55,7 @@ where
Op: MathOp<FTy = f32, RustArgs = (f32,)>,
Op::RustArgs: SpacedInput<Op>,
{
let mut ctx = CheckCtx::new(Op::IDENTIFIER, CheckBasis::Mpfr, GeneratorKind::QuickSpaced);
let mut ctx = CheckCtx::new(Op::IDENTIFIER, CheckBasis::Mpfr, GeneratorKind::Spaced);
plot_one_generator(
out_dir,
&ctx,

View file

@ -22,13 +22,38 @@ static EXTENSIVE_ITER_OVERRIDE: LazyLock<Option<u64>> = LazyLock::new(|| {
/// Specific tests that need to have a reduced amount of iterations to complete in a reasonable
/// amount of time.
///
/// Contains the itentifier+generator combo to match on, plus the factor to reduce by.
const EXTEMELY_SLOW_TESTS: &[(Identifier, GeneratorKind, u64)] = &[
(Identifier::Fmodf128, GeneratorKind::QuickSpaced, 50),
(Identifier::Fmodf128, GeneratorKind::Extensive, 50),
const EXTREMELY_SLOW_TESTS: &[SlowTest] = &[
SlowTest {
ident: Identifier::Fmodf128,
gen_kind: GeneratorKind::Spaced,
extensive: false,
reduce_factor: 50,
},
SlowTest {
ident: Identifier::Fmodf128,
gen_kind: GeneratorKind::Spaced,
extensive: true,
reduce_factor: 50,
},
];
/// A pattern to match a `CheckCtx`, plus a factor to reduce by.
struct SlowTest {
ident: Identifier,
gen_kind: GeneratorKind,
extensive: bool,
reduce_factor: u64,
}
impl SlowTest {
/// True if the test in `CheckCtx` should be reduced by `reduce_factor`.
fn matches_ctx(&self, ctx: &CheckCtx) -> bool {
self.ident == ctx.fn_ident
&& self.gen_kind == ctx.gen_kind
&& self.extensive == ctx.extensive
}
}
/// Maximum number of iterations to run for a single routine.
///
/// The default value of one greater than `u32::MAX` allows testing single-argument `f32` routines
@ -54,6 +79,7 @@ pub struct CheckCtx {
/// Source of truth for tests.
pub basis: CheckBasis,
pub gen_kind: GeneratorKind,
pub extensive: bool,
/// If specified, this value will override the value returned by [`iteration_count`].
pub override_iterations: Option<u64>,
}
@ -69,12 +95,19 @@ impl CheckCtx {
base_name_str: fn_ident.base_name().as_str(),
basis,
gen_kind,
extensive: false,
override_iterations: None,
};
ret.ulp = crate::default_ulp(&ret);
ret
}
/// Configure that this is an extensive test.
pub fn extensive(mut self, extensive: bool) -> Self {
self.extensive = extensive;
self
}
/// The number of input arguments for this function.
pub fn input_count(&self) -> usize {
self.fn_ident.math_op().rust_sig.args.len()
@ -100,14 +133,17 @@ pub enum CheckBasis {
/// and quantity.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum GeneratorKind {
/// Extremes, zeros, nonstandard numbers, etc.
EdgeCases,
Extensive,
QuickSpaced,
/// Spaced by logarithm (floats) or linear (integers).
Spaced,
/// Test inputs from an RNG.
Random,
/// A provided test case list.
List,
}
/// A list of all functions that should get extensive tests.
/// A list of all functions that should get extensive tests, as configured by environment variable.
///
/// This also supports the special test name `all` to run all tests, as well as `all_f16`,
/// `all_f32`, `all_f64`, and `all_f128` to run all tests for a specific float type.
@ -216,17 +252,17 @@ pub fn iteration_count(ctx: &CheckCtx, argnum: usize) -> u64 {
let random_iter_count = domain_iter_count / 100;
let mut total_iterations = match ctx.gen_kind {
GeneratorKind::QuickSpaced => domain_iter_count,
GeneratorKind::Spaced if ctx.extensive => extensive_max_iterations(),
GeneratorKind::Spaced => domain_iter_count,
GeneratorKind::Random => random_iter_count,
GeneratorKind::Extensive => extensive_max_iterations(),
GeneratorKind::EdgeCases | GeneratorKind::List => {
unimplemented!("shoudn't need `iteration_count` for {:?}", ctx.gen_kind)
}
};
// Larger float types get more iterations.
if t_env.large_float_ty && ctx.gen_kind != GeneratorKind::Extensive {
if ctx.gen_kind == GeneratorKind::Extensive {
if t_env.large_float_ty {
if ctx.extensive {
// Extensive already has a pretty high test count.
total_iterations *= 2;
} else {
@ -244,13 +280,13 @@ pub fn iteration_count(ctx: &CheckCtx, argnum: usize) -> u64 {
}
// Some tests are significantly slower than others and need to be further reduced.
if let Some((_id, _gen, scale)) = EXTEMELY_SLOW_TESTS
if let Some(slow) = EXTREMELY_SLOW_TESTS
.iter()
.find(|(id, generator, _scale)| *id == ctx.fn_ident && *generator == ctx.gen_kind)
.find(|slow| slow.matches_ctx(ctx))
{
// However, do not override if the extensive iteration count has been manually set.
if !(ctx.gen_kind == GeneratorKind::Extensive && EXTENSIVE_ITER_OVERRIDE.is_some()) {
total_iterations /= scale;
if !(ctx.extensive && EXTENSIVE_ITER_OVERRIDE.is_some()) {
total_iterations /= slow.reduce_factor;
}
}
@ -279,7 +315,7 @@ pub fn iteration_count(ctx: &CheckCtx, argnum: usize) -> u64 {
let total = ntests.pow(t_env.input_count.try_into().unwrap());
let seed_msg = match ctx.gen_kind {
GeneratorKind::QuickSpaced | GeneratorKind::Extensive => String::new(),
GeneratorKind::Spaced => String::new(),
GeneratorKind::Random => {
format!(
" using `{SEED_ENV}={}`",
@ -327,8 +363,8 @@ pub fn int_range(ctx: &CheckCtx, argnum: usize) -> RangeInclusive<i32> {
let extensive_range = (-0xfff)..=0xfffff;
match ctx.gen_kind {
GeneratorKind::Extensive => extensive_range,
GeneratorKind::QuickSpaced | GeneratorKind::Random => non_extensive_range,
_ if ctx.extensive => extensive_range,
GeneratorKind::Spaced | GeneratorKind::Random => non_extensive_range,
GeneratorKind::EdgeCases => extensive_range,
GeneratorKind::List => unimplemented!("shoudn't need range for {:?}", ctx.gen_kind),
}

View file

@ -65,7 +65,7 @@ macro_rules! musl_tests {
$(#[$attr])*
fn [< musl_quickspace_ $fn_name >]() {
type Op = libm_test::op::$fn_name::Routine;
let ctx = CheckCtx::new(Op::IDENTIFIER, BASIS, GeneratorKind::QuickSpaced);
let ctx = CheckCtx::new(Op::IDENTIFIER, BASIS, GeneratorKind::Spaced);
let cases = spaced::get_test_cases::<Op>(&ctx).0;
musl_runner::<Op>(&ctx, cases, musl_math_sys::$fn_name);
}

View file

@ -55,7 +55,7 @@ macro_rules! mp_tests {
$(#[$attr])*
fn [< mp_quickspace_ $fn_name >]() {
type Op = libm_test::op::$fn_name::Routine;
let ctx = CheckCtx::new(Op::IDENTIFIER, BASIS, GeneratorKind::QuickSpaced);
let ctx = CheckCtx::new(Op::IDENTIFIER, BASIS, GeneratorKind::Spaced);
let cases = spaced::get_test_cases::<Op>(&ctx).0;
mp_runner::<Op>(&ctx, cases);
}

View file

@ -17,7 +17,6 @@ use rayon::prelude::*;
use spaced::SpacedInput;
const BASIS: CheckBasis = CheckBasis::Mpfr;
const GEN_KIND: GeneratorKind = GeneratorKind::Extensive;
/// Run the extensive test suite.
pub fn run() {
@ -77,7 +76,7 @@ where
Op::RustArgs: SpacedInput<Op> + Send,
{
let test_name = format!("mp_extensive_{}", Op::NAME);
let ctx = CheckCtx::new(Op::IDENTIFIER, BASIS, GEN_KIND);
let ctx = CheckCtx::new(Op::IDENTIFIER, BASIS, GeneratorKind::Spaced).extensive(true);
let skip = skip_extensive_test(&ctx);
let runner = move || {