Streamline the way that test iteration count is determined
Currently, tests use a handful of constants to determine how many iterations to perform: `NTESTS`, `AROUND`, and `MAX_CHECK_POINTS`. This configuration is not very straightforward to adjust and needs to be repeated everywhere it is used. Replace this with new functions in the `run_cfg` module that determine iteration counts in a more reusable and documented way. This only updates `edge_cases` and `domain_logspace`, `random` is refactored in a later commit.
This commit is contained in:
@@ -6,41 +6,26 @@ use libm::support::{IntTy, MinInt};
|
||||
|
||||
use crate::domain::HasDomain;
|
||||
use crate::op::OpITy;
|
||||
use crate::run_cfg::{GeneratorKind, iteration_count};
|
||||
use crate::{CheckCtx, MathOp, logspace};
|
||||
|
||||
/// Number of tests to run.
|
||||
// FIXME(ntests): replace this with a more logical algorithm
|
||||
const NTESTS: usize = {
|
||||
if cfg!(optimizations_enabled) {
|
||||
if crate::emulated()
|
||||
|| !cfg!(target_pointer_width = "64")
|
||||
|| cfg!(all(target_arch = "x86_64", target_vendor = "apple"))
|
||||
{
|
||||
// Tests are pretty slow on non-64-bit targets, x86 MacOS, and targets that run
|
||||
// in QEMU.
|
||||
100_000
|
||||
} else {
|
||||
5_000_000
|
||||
}
|
||||
} else {
|
||||
// Without optimizations just run a quick check
|
||||
800
|
||||
}
|
||||
};
|
||||
|
||||
/// Create a range of logarithmically spaced inputs within a function's domain.
|
||||
///
|
||||
/// This allows us to get reasonably thorough coverage without wasting time on values that are
|
||||
/// NaN or out of range. Random tests will still cover values that are excluded here.
|
||||
pub fn get_test_cases<Op>(_ctx: &CheckCtx) -> impl Iterator<Item = (Op::FTy,)>
|
||||
pub fn get_test_cases<Op>(ctx: &CheckCtx) -> impl Iterator<Item = (Op::FTy,)>
|
||||
where
|
||||
Op: MathOp + HasDomain<Op::FTy>,
|
||||
IntTy<Op::FTy>: TryFrom<usize>,
|
||||
IntTy<Op::FTy>: TryFrom<u64>,
|
||||
RangeInclusive<IntTy<Op::FTy>>: Iterator,
|
||||
{
|
||||
let domain = Op::DOMAIN;
|
||||
let ntests = iteration_count(ctx, GeneratorKind::Domain, 0);
|
||||
|
||||
// We generate logspaced inputs within a specific range, excluding values that are out of
|
||||
// range in order to make iterations useful (random tests still cover the full range).
|
||||
let start = domain.range_start();
|
||||
let end = domain.range_end();
|
||||
let steps = OpITy::<Op>::try_from(NTESTS).unwrap_or(OpITy::<Op>::MAX);
|
||||
let steps = OpITy::<Op>::try_from(ntests).unwrap_or(OpITy::<Op>::MAX);
|
||||
logspace(start, end, steps).map(|v| (v,))
|
||||
}
|
||||
|
||||
@@ -3,18 +3,11 @@
|
||||
use libm::support::Float;
|
||||
|
||||
use crate::domain::HasDomain;
|
||||
use crate::run_cfg::{check_near_count, check_point_count};
|
||||
use crate::{CheckCtx, FloatExt, MathOp};
|
||||
|
||||
/// Number of values near an interesting point to check.
|
||||
// FIXME(ntests): replace this with a more logical algorithm
|
||||
const AROUND: usize = 100;
|
||||
|
||||
/// Functions have infinite asymptotes, limit how many we check.
|
||||
// FIXME(ntests): replace this with a more logical algorithm
|
||||
const MAX_CHECK_POINTS: usize = 10;
|
||||
|
||||
/// Create a list of values around interesting points (infinities, zeroes, NaNs).
|
||||
pub fn get_test_cases<Op, F>(_ctx: &CheckCtx) -> impl Iterator<Item = (F,)>
|
||||
pub fn get_test_cases<Op, F>(ctx: &CheckCtx) -> impl Iterator<Item = (F,)>
|
||||
where
|
||||
Op: MathOp<FTy = F> + HasDomain<F>,
|
||||
F: Float,
|
||||
@@ -25,23 +18,26 @@ where
|
||||
let domain_start = domain.range_start();
|
||||
let domain_end = domain.range_end();
|
||||
|
||||
let check_points = check_point_count(ctx);
|
||||
let near_points = check_near_count(ctx);
|
||||
|
||||
// Check near some notable constants
|
||||
count_up(F::ONE, values);
|
||||
count_up(F::ZERO, values);
|
||||
count_up(F::NEG_ONE, values);
|
||||
count_down(F::ONE, values);
|
||||
count_down(F::ZERO, values);
|
||||
count_down(F::NEG_ONE, values);
|
||||
count_up(F::ONE, near_points, values);
|
||||
count_up(F::ZERO, near_points, values);
|
||||
count_up(F::NEG_ONE, near_points, values);
|
||||
count_down(F::ONE, near_points, values);
|
||||
count_down(F::ZERO, near_points, values);
|
||||
count_down(F::NEG_ONE, near_points, values);
|
||||
values.push(F::NEG_ZERO);
|
||||
|
||||
// Check values near the extremes
|
||||
count_up(F::NEG_INFINITY, values);
|
||||
count_down(F::INFINITY, values);
|
||||
count_down(domain_end, values);
|
||||
count_up(domain_start, values);
|
||||
count_down(domain_start, values);
|
||||
count_up(domain_end, values);
|
||||
count_down(domain_end, values);
|
||||
count_up(F::NEG_INFINITY, near_points, values);
|
||||
count_down(F::INFINITY, near_points, values);
|
||||
count_down(domain_end, near_points, values);
|
||||
count_up(domain_start, near_points, values);
|
||||
count_down(domain_start, near_points, values);
|
||||
count_up(domain_end, near_points, values);
|
||||
count_down(domain_end, near_points, values);
|
||||
|
||||
// Check some special values that aren't included in the above ranges
|
||||
values.push(F::NAN);
|
||||
@@ -50,9 +46,9 @@ where
|
||||
// Check around asymptotes
|
||||
if let Some(f) = domain.check_points {
|
||||
let iter = f();
|
||||
for x in iter.take(MAX_CHECK_POINTS) {
|
||||
count_up(x, values);
|
||||
count_down(x, values);
|
||||
for x in iter.take(check_points) {
|
||||
count_up(x, near_points, values);
|
||||
count_down(x, near_points, values);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,11 +61,11 @@ where
|
||||
|
||||
/// Add `AROUND` values starting at and including `x` and counting up. Uses the smallest possible
|
||||
/// increments (1 ULP).
|
||||
fn count_up<F: Float>(mut x: F, values: &mut Vec<F>) {
|
||||
fn count_up<F: Float>(mut x: F, points: u64, values: &mut Vec<F>) {
|
||||
assert!(!x.is_nan());
|
||||
|
||||
let mut count = 0;
|
||||
while x < F::INFINITY && count < AROUND {
|
||||
while x < F::INFINITY && count < points {
|
||||
values.push(x);
|
||||
x = x.next_up();
|
||||
count += 1;
|
||||
@@ -78,11 +74,11 @@ fn count_up<F: Float>(mut x: F, values: &mut Vec<F>) {
|
||||
|
||||
/// Add `AROUND` values starting at and including `x` and counting down. Uses the smallest possible
|
||||
/// increments (1 ULP).
|
||||
fn count_down<F: Float>(mut x: F, values: &mut Vec<F>) {
|
||||
fn count_down<F: Float>(mut x: F, points: u64, values: &mut Vec<F>) {
|
||||
assert!(!x.is_nan());
|
||||
|
||||
let mut count = 0;
|
||||
while x > F::NEG_INFINITY && count < AROUND {
|
||||
while x > F::NEG_INFINITY && count < points {
|
||||
values.push(x);
|
||||
x = x.next_down();
|
||||
count += 1;
|
||||
|
||||
@@ -12,6 +12,7 @@ use crate::{BaseName, CheckCtx, GenerateInput};
|
||||
const SEED: [u8; 32] = *b"3.141592653589793238462643383279";
|
||||
|
||||
/// Number of tests to run.
|
||||
// FIXME(ntests): clean this up when possible
|
||||
const NTESTS: usize = {
|
||||
if cfg!(optimizations_enabled) {
|
||||
if crate::emulated()
|
||||
|
||||
Reference in New Issue
Block a user