spirv tests: no longer test against the legacy simulation:

test the CPU impl against the GPU impl.

it's a different class of test. but it provides some value yet and most
importantly, allows us to strip out the legacy simulation code without
losing *too much* test coverage.
This commit is contained in:
2022-08-23 20:05:30 -07:00
parent e7ed46bb89
commit 1891a72df3

View File

@@ -273,7 +273,6 @@ mod test {
use crate::mat::{self, AnisomorphicConductor};
use crate::meas::Energy;
use crate::real::{R32, ToFloat as _};
use crate::sim::legacy::{self, SimState};
use crate::stim::{
self,
ModulatedVectorField,
@@ -300,7 +299,9 @@ mod test {
m.cast()
}
fn assert_simstate_eq<B: SimBackend<R32, FullyGenericMaterial<R32>> + Send + Sync>(real_state: &SpirvSim<R32, FullyGenericMaterial<R32>, B>, ref_state: &SimState) {
fn assert_simstate_eq<S0: AbstractSim<Real=R32>, S1: AbstractSim<Real=R32>>(
real_state: &S0, ref_state: &S1
) {
let rel_threshold = 1e-2f32.to_r32();
let mean_e = mean_magnitude_e(ref_state);
let mean_h = mean_magnitude_h(ref_state);
@@ -387,7 +388,7 @@ mod test {
mod backend_agnostic {
use super::*;
use crate::stim::{Fields, NoopStimulus, RngStimulus};
use crate::stim::RngStimulus;
pub fn do_smoke_small<B: SimBackend<R32, FullyGenericMaterial<R32>> + Send + Sync + Default>() {
let mut state = SpirvSim::new_with_backend(Index::new(8, 8, 8), 1e-3, B::default());
state.step();
@@ -428,139 +429,6 @@ mod test {
state.step();
}
fn test_same_explicit<B: SimBackend<R32, FullyGenericMaterial<R32>> + Send + Sync + Default>(
mut ref_state: SimState,
mut dut_state: SpirvSim<R32, FullyGenericMaterial<R32>, B>,
step_iters: u64,
steps_per_iter: u32
) {
for _ in 0..step_iters {
ref_state.step_multiple(steps_per_iter, &NoopStimulus);
dut_state.step_multiple(steps_per_iter, &NoopStimulus);
assert_simstate_eq(&dut_state, &ref_state);
}
}
fn test_same<B: SimBackend<R32, FullyGenericMaterial<R32>> + Send + Sync + Default>(seed: u64, step_iters: u64, steps_per_iter: u32, size: Index) {
let mut cpu_state = SimState::new(size, 1e-3);
cpu_state.apply_stimulus(&RngStimulus::new(seed));
let mut spirv_state = SpirvSim::new_with_backend(size, 1e-3, B::default());
spirv_state.apply_stimulus(&RngStimulus::new(seed));
test_same_explicit(cpu_state, spirv_state, step_iters, steps_per_iter);
}
pub fn do_same_no_step_no_stim<B: SimBackend<R32, FullyGenericMaterial<R32>> + Send + Sync + Default>() {
let ref_state = SimState::new(Index::new(8, 8, 8), 1e-3);
let dut_state = SpirvSim::new_with_backend(Index::new(8, 8, 8), 1e-3, B::default());
test_same_explicit(ref_state, dut_state, 1, 1);
}
pub fn do_same_1_step<B: SimBackend<R32, FullyGenericMaterial<R32>> + Send + Sync + Default>() {
test_same::<B>(0x1234, 1, 1, Index::new(4, 4, 4));
}
pub fn do_same_1000_step<B: SimBackend<R32, FullyGenericMaterial<R32>> + Send + Sync + Default>() {
test_same::<B>(0x1234, 1000, 1, Index::new(4, 4, 4));
}
pub fn do_same_not_multiple_of_4<B: SimBackend<R32, FullyGenericMaterial<R32>> + Send + Sync + Default>() {
test_same::<B>(0x1234, 100, 1, Index::new(3, 2, 5));
}
pub fn do_same_100_steps_larger<B: SimBackend<R32, FullyGenericMaterial<R32>> + Send + Sync + Default>() {
test_same::<B>(0x1234, 100, 1, Index::new(24, 20, 44));
}
pub fn do_same_100_steps_of_10<B: SimBackend<R32, FullyGenericMaterial<R32>> + Send + Sync + Default>() {
test_same::<B>(0x1234, 100, 10, Index::new(24, 20, 44));
}
fn test_same_conductor<B: SimBackend<R32, FullyGenericMaterial<R32>> + Send + Sync + Default>(
seed: u64, step_iters: u64, steps_per_iter: u32, size: Index
) {
use rand::{Rng as _, SeedableRng as _};
let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
let mut ref_state = SimState::new(size, 1e-3);
let mut dut_state = SpirvSim::new_with_backend(size, 1e-3, B::default());
for z in 0..size.z() {
for y in 0..size.y() {
for x in 0..size.x() {
let cond = Vec3::new(
rng.gen_range(0.0..1e6),
rng.gen_range(0.0..1e6),
rng.gen_range(0.0..1e6),
);
ref_state.put_material(Index::new(x, y, z), AnisomorphicConductor::new(cond.cast()));
dut_state.put_material(Index::new(x, y, z), AnisomorphicConductor::new(cond.cast()));
}
}
}
ref_state.apply_stimulus(&RngStimulus::new(seed));
dut_state.apply_stimulus(&RngStimulus::new(seed));
test_same_explicit(ref_state, dut_state, step_iters, steps_per_iter);
}
pub fn do_conductor_1_step<B: SimBackend<R32, FullyGenericMaterial<R32>> + Send + Sync + Default>() {
test_same_conductor::<B>(0x1234, 1, 1, Index::new(4, 4, 4));
}
pub fn do_conductor_many_steps_larger<B: SimBackend<R32, FullyGenericMaterial<R32>> + Send + Sync + Default>() {
test_same_conductor::<B>(0x1234, 100, 10, Index::new(96, 16, 8));
}
fn test_same_mb_ferromagnet<B: SimBackend<R32, FullyGenericMaterial<R32>> + Send + Sync + Default>(
seed: u64, step_iters: u64, steps_per_iter: u32, size: Index
) {
use rand::{Rng as _, SeedableRng as _};
let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
let mut ref_state = SimState::new(size, 1e-3);
let mut dut_state = SpirvSim::new_with_backend(size, 1e-3, B::default());
for z in 0..size.z() {
for y in 0..size.y() {
for x in 0..size.x() {
let b_start = rng.gen_range(-1e-4..1e-4);
// if b_start is negative, we need the endpoint to be > |b_start|:
let b_range = (-2.0f32*b_start).max(0.0) + rng.gen_range(1e-5..1e-2);
// It would be weird for an increase in B to trigger a DECREASE in H.
// We have mu0(M+H) = B, so
// mu0(dM/dB = dH/dB) = 1
// -> dH/dB = mu0^-1 - dM/dB > 0
let slope = rng.gen_range(0.0..0.9*f32::mu0_inv());
let b_end = b_start + b_range;
let m_max = b_range * slope;
ref_state.put_material(Index::new(x, y, z), legacy::mat::MBPgram::new(
b_start.cast(), b_end.cast(), m_max.cast()
));
dut_state.put_material(Index::new(x, y, z), coremem_cross::mat::MBPgram::new(
b_start.cast(), b_end.cast(), m_max.cast()
));
}
}
}
// XXX H stimulus on the ferromagnet behaves differently on the CPU than GPU
// the CPU implementation is the more suspect one here (it doesn't consider material
// parameters).
ref_state.apply_stimulus(&RngStimulus::new_e(seed));
dut_state.apply_stimulus(&RngStimulus::new_e(seed));
test_same_explicit(ref_state, dut_state, step_iters, steps_per_iter);
}
pub fn do_mb_ferromagnet_1_step<B: SimBackend<R32, FullyGenericMaterial<R32>> + Send + Sync + Default>() {
test_same_mb_ferromagnet::<B>(0x1234, 1, 1, Index::new(4, 4, 4));
}
pub fn do_mb_ferromagnet_100_steps_larger<B: SimBackend<R32, FullyGenericMaterial<R32>> + Send + Sync + Default>() {
test_same_mb_ferromagnet::<B>(0x1234, 10, 10, Index::new(96, 16, 8));
}
// XXX these tests probably failed because they were allowing negative mu_r values
// #[test]
// fn mb_ferromagnet_diff_repro() {
@@ -639,18 +507,6 @@ mod test {
pub fn do_mh_ferromagnet_smoke_100_steps_larger<B: SimBackend<R32, FullyGenericMaterial<R32>> + Send + Sync + Default>() {
test_smoke_mh_ferromagnet::<B>(0x1234, 100, Index::new(328, 252, 160));
}
pub fn do_step_multiple_with_stim<B: SimBackend<R32, FullyGenericMaterial<R32>> + Send + Sync + Default>() {
let size = Index::new(4, 12, 8);
let mut ref_state = SimState::new(size, 1e-3);
let mut dut_state = SpirvSim::new_with_backend(size, 1e-3, B::default());
let stim = Fields::new_e(Vec3::new(1.0e15, 2.0e15, -3.0e15).cast::<R32>());
for _ in 0..5 {
ref_state.step_multiple(100, &stim);
dut_state.step_multiple(100, &stim);
assert_simstate_eq(&dut_state, &ref_state);
}
}
}
macro_rules! test_backend {
@@ -791,46 +647,6 @@ mod test {
do_smoke_not_multiple_of_4::<$backend>();
}
#[test]
fn same_no_step_no_stim() {
do_same_no_step_no_stim::<$backend>();
}
#[test]
fn same_1_step() {
do_same_1_step::<$backend>();
}
#[test]
fn same_1000_step() {
do_same_1000_step::<$backend>();
}
#[test]
fn same_not_multiple_of_4() {
do_same_not_multiple_of_4::<$backend>();
}
#[test]
fn same_100_steps_larger() {
do_same_100_steps_larger::<$backend>();
}
#[test]
fn same_100_steps_of_10() {
do_same_100_steps_of_10::<$backend>();
}
#[test]
fn conductor_1_step() {
do_conductor_1_step::<$backend>();
}
#[test]
fn conductor_many_steps_larger() {
do_conductor_many_steps_larger::<$backend>();
}
#[test]
fn mb_ferromagnet_1_step() {
do_mb_ferromagnet_1_step::<$backend>();
}
#[test]
fn mb_ferromagnet_100_steps_larger() {
do_mb_ferromagnet_100_steps_larger::<$backend>();
}
#[test]
fn mh_ferromagnet_smoke_1_step() {
do_mh_ferromagnet_smoke_1_step::<$backend>();
}
@@ -838,14 +654,171 @@ mod test {
fn mh_ferromagnet_smoke_100_steps_larger() {
do_mh_ferromagnet_smoke_100_steps_larger::<$backend>();
}
#[test]
fn step_multiple_with_stim() {
do_step_multiple_with_stim::<$backend>();
}
}
}
}
test_backend!(cpu, CpuBackend);
test_backend!(wgpu, WgpuBackend);
/// sanity checks that cpu and wgpu backends give *similar* results
mod same {
use super::*;
use crate::stim::{NoopStimulus, RngStimulus};
fn test_same_explicit(
mut cpu_state: SpirvSim<R32, FullyGenericMaterial<R32>, CpuBackend>,
mut wgpu_state: SpirvSim<R32, FullyGenericMaterial<R32>, WgpuBackend>,
step_iters: u64,
steps_per_iter: u32
) {
for _ in 0..step_iters {
cpu_state.step_multiple(steps_per_iter, &NoopStimulus);
wgpu_state.step_multiple(steps_per_iter, &NoopStimulus);
assert_simstate_eq(&wgpu_state, &cpu_state);
}
}
fn test_same(seed: u64, step_iters: u64, steps_per_iter: u32, size: Index) {
let mut cpu_state = SpirvSim::new_with_backend(size, 1e-3, CpuBackend::default());
cpu_state.apply_stimulus(&RngStimulus::new(seed));
let mut wgpu_state = SpirvSim::new_with_backend(size, 1e-3, WgpuBackend::default());
wgpu_state.apply_stimulus(&RngStimulus::new(seed));
test_same_explicit(cpu_state, wgpu_state, step_iters, steps_per_iter);
}
#[test]
fn same_no_step_no_stim() {
let cpu_state = SpirvSim::new_with_backend(Index::new(8, 8, 8), 1e-3, CpuBackend::default());
let wgpu_state = SpirvSim::new_with_backend(Index::new(8, 8, 8), 1e-3, WgpuBackend::default());
test_same_explicit(cpu_state, wgpu_state, 1, 1);
}
#[test]
fn do_same_1_step() {
test_same(0x1234, 1, 1, Index::new(4, 4, 4));
}
#[test]
fn same_1000_step() {
test_same(0x1234, 1000, 1, Index::new(4, 4, 4));
}
#[test]
fn same_not_multiple_of_4() {
test_same(0x1234, 100, 1, Index::new(3, 2, 5));
}
#[test]
fn same_100_steps_larger() {
test_same(0x1234, 100, 1, Index::new(24, 20, 44));
}
#[test]
fn same_100_steps_of_10() {
test_same(0x1234, 100, 10, Index::new(24, 20, 44));
}
fn test_same_conductor(
seed: u64, step_iters: u64, steps_per_iter: u32, size: Index
) {
use rand::{Rng as _, SeedableRng as _};
let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
let mut ref_state = SpirvSim::new_with_backend(size, 1e-3, CpuBackend::default());
let mut dut_state = SpirvSim::new_with_backend(size, 1e-3, WgpuBackend::default());
for z in 0..size.z() {
for y in 0..size.y() {
for x in 0..size.x() {
let cond = Vec3::new(
rng.gen_range(0.0..1e6),
rng.gen_range(0.0..1e6),
rng.gen_range(0.0..1e6),
);
ref_state.put_material(Index::new(x, y, z), AnisomorphicConductor::new(cond.cast()));
dut_state.put_material(Index::new(x, y, z), AnisomorphicConductor::new(cond.cast()));
}
}
}
// TODO: we should apply this on every frame?
ref_state.apply_stimulus(&RngStimulus::new(seed));
dut_state.apply_stimulus(&RngStimulus::new(seed));
test_same_explicit(ref_state, dut_state, step_iters, steps_per_iter);
}
#[test]
fn conductor_1_step() {
test_same_conductor(0x1234, 1, 1, Index::new(4, 4, 4));
}
#[test]
fn conductor_many_steps_larger() {
test_same_conductor(0x1234, 100, 10, Index::new(96, 16, 8));
}
fn test_same_mb_ferromagnet(
seed: u64, step_iters: u64, steps_per_iter: u32, size: Index
) {
use rand::{Rng as _, SeedableRng as _};
let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
let mut ref_state = SpirvSim::new_with_backend(size, 1e-3, CpuBackend::default());
let mut dut_state = SpirvSim::new_with_backend(size, 1e-3, WgpuBackend::default());
for z in 0..size.z() {
for y in 0..size.y() {
for x in 0..size.x() {
let b_start = rng.gen_range(-1e-4..1e-4);
// if b_start is negative, we need the endpoint to be > |b_start|:
let b_range = (-2.0f32*b_start).max(0.0) + rng.gen_range(1e-5..1e-2);
// It would be weird for an increase in B to trigger a DECREASE in H.
// We have mu0(M+H) = B, so
// mu0(dM/dB = dH/dB) = 1
// -> dH/dB = mu0^-1 - dM/dB > 0
let slope = rng.gen_range(0.0..0.9*f32::mu0_inv());
let b_end = b_start + b_range;
let m_max = b_range * slope;
ref_state.put_material(Index::new(x, y, z), coremem_cross::mat::MBPgram::new(
b_start.cast(), b_end.cast(), m_max.cast()
));
dut_state.put_material(Index::new(x, y, z), coremem_cross::mat::MBPgram::new(
b_start.cast(), b_end.cast(), m_max.cast()
));
}
}
}
// TODO: use new (not new_e)
ref_state.apply_stimulus(&RngStimulus::new_e(seed));
dut_state.apply_stimulus(&RngStimulus::new_e(seed));
test_same_explicit(ref_state, dut_state, step_iters, steps_per_iter);
}
#[test]
fn mb_ferromagnet_1_step() {
test_same_mb_ferromagnet(0x1234, 1, 1, Index::new(4, 4, 4));
}
#[test]
fn mb_ferromagnet_100_steps_larger() {
test_same_mb_ferromagnet(0x1234, 10, 10, Index::new(96, 16, 8));
}
#[test]
fn step_multiple_with_stim() {
let size = Index::new(4, 12, 8);
let mut ref_state: SpirvSim<R32, FullyGenericMaterial<R32>, CpuBackend> =
SpirvSim::new(size, 1e-3);
let mut dut_state: SpirvSim<R32, FullyGenericMaterial<R32>, WgpuBackend> =
SpirvSim::new(size, 1e-3);
let stim = stim::Fields::new_e(Vec3::new(1.0e15, 2.0e15, -3.0e15).cast::<R32>());
for _ in 0..5 {
ref_state.step_multiple(100, &stim);
dut_state.step_multiple(100, &stim);
assert_simstate_eq(&dut_state, &ref_state);
}
}
}
}