gradcheck 0.1.0

Finite-difference gradient checking for Rust ML frameworks. Verifies an autodiff engine against an independent numerical oracle, with a negative control that must fail.
// gradcheck — finite-difference gradient checking for Rust ML frameworks.
// Copyright (c) 2026 Henos D <henosd19@gmail.com> (GitHub: @4ktLuffy)
// Repository: https://github.com/4ktLuffy/gradcheck
// SPDX-License-Identifier: MIT OR Apache-2.0

//! Step 1 — positive control for the v4 comparator on the FINITE-DIFFERENCE path.
//!
//! The differential path was checked separately by re-adjudicating the recorded corpus.
//! This checks the other oracle: does `gradcheck` itself, with the v4 three-way rule,
//! still REJECT a defect that is known to be present?
//!
//! The defect used is the `avg_pool1d` backward bug ([burn#5308]), chosen because it is the
//! only known defect that reproduces against the *published* crate this test links to.
//! (burn#5304 exists only against burn `main`'s pinned cubecl revision, so it cannot serve
//! as a control here — the published build is genuinely clean and a `Pass` there is
//! correct, not a miss.)
//!
//! Expected on `burn-cpu`:   even channel counts REJECTED.
//! Expected on `burn-ndarray`: every channel count certified.
//!
//! ```text
//! cargo test --features burn-ndarray --test fd_positive_control --release -- --nocapture
//! cargo test --features burn-cpu     --test fd_positive_control --release -- --nocapture
//! ```
//!
//! [burn#5308]: https://github.com/tracel-ai/burn/issues/5308
#![cfg(feature = "burn")]

use burn_tensor::module::avg_pool1d;
use gradcheck::{adapters::burn::Burn, gradcheck, Config, Verdict};

#[test]
fn v4_still_rejects_the_pooling_defect() {
    let cfg = Config::f32_defaults();
    let mut rejected = vec![];
    let mut certified = vec![];
    let mut abstained = vec![];

    for c in [1usize, 2, 3, 4] {
        let len = 6usize;
        let n = c * len;
        // Deterministic, well away from kinks; gradients here are 1/3 and 2/3, comfortably
        // above the certification floor, so abstention would itself be a finding.
        let data: Vec<f64> = (0..n).map(|i| (i as f64) * 0.1 + 0.5).collect();

        let r = gradcheck::<Burn<3>, _>(
            &format!("avg_pool1d_c{c}"),
            &data,
            &[1, c, len],
            |t| avg_pool1d(t, 3, 2, 1, true, false),
            &cfg,
        );

        let (checked, total) = r.checked_fraction();
        println!(
            "FDPC c={c} {:?} checked={checked}/{total} worst_rel={:.3e}",
            r.verdict, r.worst_rel_error
        );

        match r.verdict {
            Verdict::Mismatch => rejected.push(c),
            Verdict::Pass => certified.push(c),
            _ => abstained.push(c),
        }
    }

    println!("FDPC rejected={rejected:?} certified={certified:?} abstained={abstained:?}");

    // The gradients in this configuration are ~0.33 and ~0.67 — far above the floor — so
    // every case must be adjudicated. An abstention here would mean the comparator is
    // mis-tolerated, not that the data is ambiguous.
    assert!(
        abstained.is_empty(),
        "no case should abstain at these magnitudes; abstained at {abstained:?}"
    );

    // Backend-specific expectation. On ndarray everything is correct; on cpu the even
    // channel counts must be rejected, or v4 has lost detection on the FD path.
    if cfg!(feature = "burn-cpu") {
        assert!(
            rejected.contains(&2) && rejected.contains(&4),
            "DETECTION REGRESSION: v4 failed to reject the known pooling defect at c=2,4. \
             rejected={rejected:?} certified={certified:?}"
        );
        assert!(
            certified.contains(&1) && certified.contains(&3),
            "odd channel counts are correct and must certify; certified={certified:?}"
        );
    } else {
        assert_eq!(
            rejected,
            Vec::<usize>::new(),
            "this backend is known-correct here; nothing should be rejected"
        );
        assert_eq!(certified.len(), 4, "all four channel counts should certify");
    }
}