csrk 1.1.4

Sparse Gaussian Process regression with compactly supported radial kernels
Documentation
#[test]
#[ignore] // remove ignore when you actually want to benchmark
fn timing_kernel_build_naive_vs_hashed() {
    use std::time::Instant;
    use csrk::{GP,PCG64Stream};
    use ndarray::{arr1, Array2};

    // ---- build a 2D grid of training points ----
    let nx = 150;
    let ny = 150;
    let h = 0.2; // spacing < 1.0 so neighbors fall inside support

    let mut x_train_vec: Vec<Vec<f64>> = Vec::new();
    for i in 0..nx {
        for j in 0..ny {
            x_train_vec.push(vec![i as f64 * h, j as f64 * h]);
        }
    }
    // Flatten for array construction
    let x_train_flat: Vec<f64> = x_train_vec.iter()
        .flat_map(|row| row.iter().copied())
        .collect();

    let nsample = x_train_vec.len();
    let ndim = 2;

    println!("points: {}", nsample);
    let x_train = Array2::from_shape_vec((nsample, ndim), x_train_flat).unwrap();

    // dummy data (kernel build only depends on geometry + errors)
    let y_train = arr1(&vec![0.0; nsample]);
    let y_err = arr1(&vec![0.0; nsample]);
    let whitenoise = 1e-10;
    // kernel parameters (pick one representative case)
    let msq_order = 2;

    // =======================================================================
    // ================= Realization Benchmark ===============================
    // =======================================================================
    // build and train full GP
    let scale = arr1(&[1.0, 1.0]);
    let t2 = Instant::now();
    let gp = GP::train(
        x_train.view(),
        y_train.view(),
        y_err.view(),
        scale.view(),
        whitenoise,
        msq_order,
    );
    let t_train = t2.elapsed();
    println!("gp train    : {:?}", t_train);
    // Generate evaluation points
    let mut x_eval_vec = Vec::new();
    for i in 0..nx {
        for j in 0..ny {
            x_eval_vec.push(
                vec![
                    i as f64 * h + 0.05,
                    j as f64 * h + 0.05
                ]
            );
        }
    }
    // Flatten for array construction
    let x_eval_flat: Vec<f64> = x_eval_vec.iter()
        .flat_map(|row| row.iter().copied())
        .collect();
    let neval = x_eval_vec.len();
    let x_eval = Array2::from_shape_vec((neval,ndim),
        x_eval_flat).unwrap();
    println!("eval points : {:?}", neval);
    // Predict mean
    let t3 = Instant::now();
    let mu = gp.predict_mean(x_eval.view());
    let t_mean = t3.elapsed();
    for x in mu.iter() {
        assert!(x.is_finite());
    }
    println!("predict mean: {:?}", t_mean);
    // Realization timing
    let nreal = 200;
    let mut rng = PCG64Stream::new(123456, 987654);
    let t4 = Instant::now();
    let mut acc = 0.0;
    for _ in 0..nreal {
        let r = gp.draw_realization(&mut rng);
        for i in 0..neval {
            acc += r.value(x_eval.row(i))
        }
    }
    let t_real = t4.elapsed();
    println!("eval reals  : {:?}", t_real);
    let total_evals = nreal * neval;
    println!("realizations: {}", nreal);
    println!("total evals: {}", total_evals);
    println!("realization generation time: {:?}", t_real);
    println!("  throughput: {:.3e} evals/sec",
        (total_evals as f64) / t_real.as_secs_f64()
    );
    assert!(acc.is_finite());
}

#[test]
#[ignore]
fn scaling_sweep_kernel_and_realizations() {
    use std::time::Instant;
    use csrk::kernel::{Kernel};
    use csrk::{GP,PCG64Stream};
    use csrk::spatial_hash::SpatialHash;
    use ndarray::{arr1, Array2, ArrayView1};

    let msq_order = 2;
    let whitenoise = 1e-10;
    let h = 0.2;

    println!(
        "(nx,ny),\tntrain,\tnnz,\tnnz/ntrain,\tbuild_s, train_s, mean_s, draw_s, eval_s, evals_per_sec"
    );

    // sweep grid sizes
    let sizes = [30, 50, 75, 100, 125, 150, 200];

    for &n in &sizes {
        let nx = n;
        let ny = n;

        // ---- build training grid ----
        let mut x_train_vec = Vec::new();
        for i in 0..nx {
            for j in 0..ny {
                x_train_vec.push(vec![i as f64 * h, j as f64 * h]);
            }
        }
        // Flatten for array construction
        let x_train_flat: Vec<f64> = x_train_vec.iter()
            .flat_map(|row| row.iter().copied())
            .collect();

        let ndim = 2;
        let nsample = x_train_vec.len();
        let x_train: Array2<f64> = Array2::from_shape_vec((nsample,ndim),
            x_train_flat).unwrap();

        let y_train = arr1(&vec![0.0; nsample]);
        let y_err = arr1(&vec![0.0; nsample]);
        let scale = arr1(&[1.0, 1.0]);

        let mykernel = Kernel::new(
            ndim,
            msq_order,
            scale.view(),
            whitenoise
        );
        let hash = SpatialHash::build(&x_train_vec, 1.0);

        // ---- kernel build ----
        let t0 = Instant::now();
        let k = mykernel.hashed_kernel_construction(
            x_train.view(),
            y_err.view(),
            &hash,
        );
        let build_t = t0.elapsed().as_secs_f64();
        let nnz = k.nnz();

        // ---- train GP----
        let t1 = Instant::now();
        let gp = GP::train(
            x_train.view(),
            y_train.view(),
            y_err.view(),
            scale.view(),
            whitenoise,
            msq_order,
        );

        let train_t = t1.elapsed().as_secs_f64();

        // ---- evaluation points ----
        let mut x_eval_vec = Vec::new();
        for i in 0..nx {
            for j in 0..ny {
                x_eval_vec.push(vec![i as f64 * h + 0.05, j as f64 * h + 0.05]);
            }
        }
        // Flatten for array construction
        let x_eval_flat: Vec<f64> = x_eval_vec.iter()
            .flat_map(|row| row.iter().copied())
            .collect();

        let neval = x_eval_vec.len();
        let x_eval = Array2::from_shape_vec((neval,ndim),
            x_eval_flat).unwrap();
        let nreal_eval = 5;

        // ---- mean timing ----
        let t2 = Instant::now();
        let mean = gp.predict_mean(x_eval.view());
        let mean_t = t2.elapsed().as_secs_f64();
        for mu in mean.iter() {
            assert!(mu.is_finite());
        }

        // ---- realization draw timing ----
        let mut rng = PCG64Stream::new(1234, 5678);

        let nreal_draw = 20;
        let t3 = Instant::now();
        let mut reals = Vec::new();
        for _ in 0..nreal_draw {
            reals.push(gp.draw_realization(&mut rng));
        }
        let draw_t = t3.elapsed().as_secs_f64() / nreal_draw as f64;

        // ---- realization eval timing ----
        let t4 = Instant::now();
        let mut acc = 0.0;
        for r in reals.iter().take(nreal_eval) {
            for x in &x_eval_vec {
                let x_view = ArrayView1::from(x.as_slice());
                acc += r.value(x_view);
            }
        }
        let eval_t = t4.elapsed().as_secs_f64();
        let evals_per_sec = (nreal_eval * neval) as f64 / eval_t;

        // ---- report ----
        println!(
            "({}, {}),\t{},\t{},  \t{:.4},\t{:.4},\t{:.4},\t{:.4},\t{:.4},\t{:.4}\t{:.3e}",
            nx,
            ny,
            nsample,
            nnz,
            nnz as f64 / nsample as f64,
            build_t,
            train_t,
            mean_t,
            draw_t,
            eval_t,
            evals_per_sec,
        );

        assert!(acc.is_finite());
    }
}