sim-lib-interference-solve 0.1.0

Deterministic CPU f64 reference solving for coherent scalar wave fields.
Documentation
use sim_lib_interference_core::{SamplingCertificate, SamplingThresholds, SamplingVerdict};

use super::{AnalysisError, Extremum, ExtremumKind, analyze_fringes};
use crate::{
    HostPhasorField, LossClass, Observable, ReductionRule, ScalarSample, project, reduce_for_view,
};

fn certificate() -> SamplingCertificate {
    SamplingCertificate {
        thresholds: SamplingThresholds::default(),
        wavelength_m: 2.0,
        samples_per_wavelength_u: 16.0,
        samples_per_wavelength_v: 12.0,
        samples_per_power_fringe_u: 8.0,
        samples_per_power_fringe_v: 6.0,
        nearest_point_source_distance_m: None,
        max_envelope_fraction_per_cell: 0.0,
        verdict: SamplingVerdict::Resolved,
    }
}

#[test]
fn reports_stats_strict_moore_extrema_and_contrast_in_row_major_order() {
    let field = HostPhasorField::from_test_components(
        3,
        3,
        vec![2.0, 3.0, 2.0, 3.0, 0.0, 3.0, 2.0, 3.0, 4.0],
        vec![0.0; 9],
    );
    let projection = project(&field, certificate(), Observable::Amplitude, 0.0).unwrap();
    let report = analyze_fringes(&projection, 1.0e-12).unwrap();

    assert_eq!(report.sampling, certificate());
    assert_eq!(report.projection.observable(), Observable::Amplitude);
    assert_eq!(report.projection.rule(), ReductionRule::Detail);
    assert_eq!(report.projection.loss_class(), LossClass::Lossless);
    assert_eq!(report.projection.target_dimensions().rows(), 3);
    assert_eq!(report.projection.target_dimensions().columns(), 3);
    assert_eq!(report.stats.count, 9);
    assert_eq!(report.stats.minimum, 0.0);
    assert_eq!(report.stats.maximum, 4.0);
    assert!((report.stats.mean - 22.0 / 9.0).abs() < 1.0e-15);
    assert!(report.stats.population_variance > 0.0);
    assert_eq!(
        report.extrema,
        vec![
            Extremum {
                row: 1,
                column: 1,
                value: 0.0,
                kind: ExtremumKind::NodeCandidate,
            },
            Extremum {
                row: 2,
                column: 2,
                value: 4.0,
                kind: ExtremumKind::AntinodeCandidate,
            },
        ]
    );
    assert_eq!(report.michelson_contrast, Some(1.0));
}

#[test]
fn plateaus_have_no_arbitrary_extrema_and_dark_fields_have_no_contrast() {
    let field = HostPhasorField::from_test_components(2, 2, vec![0.25; 4], vec![0.0; 4]);
    let projection = project(&field, certificate(), Observable::Amplitude, 0.0).unwrap();
    let report = analyze_fringes(&projection, 0.25).unwrap();

    assert!(report.extrema.is_empty());
    assert_eq!(report.michelson_contrast, None);
    assert_eq!(report.stats.population_variance, 0.0);
}

#[test]
fn magnitude_squared_uses_amplitude_floor_and_preserves_detector_identity() {
    let field = HostPhasorField::from_test_components(2, 2, vec![0.1, 0.2, 0.3, 0.4], vec![0.0; 4]);
    let projection = reduce_for_view(
        &field,
        certificate(),
        Observable::MagnitudeSquared,
        0.0,
        1,
        1,
        ReductionRule::DetectorMagnitudeSquaredAreaMean,
    )
    .unwrap();
    let report = analyze_fringes(&projection, 1.0).unwrap();

    assert_eq!(report.michelson_contrast, None);
    assert_eq!(
        report.projection.rule(),
        ReductionRule::DetectorMagnitudeSquaredAreaMean
    );
    assert_eq!(report.projection.source_dimensions().rows(), 2);
    assert_eq!(report.projection.target_dimensions().rows(), 1);
}

#[test]
fn non_amplitude_projection_and_invalid_floor_fail_closed() {
    let field = HostPhasorField::from_test_components(1, 2, vec![1.0, 2.0], vec![0.0; 2]);
    let projection = project(&field, certificate(), Observable::Real, 0.0).unwrap();

    assert_eq!(
        analyze_fringes(&projection, 0.0),
        Err(AnalysisError::IncompatibleObservable {
            observable: Observable::Real,
        })
    );
    assert!(matches!(
        analyze_fringes(&projection, f64::NAN),
        Err(AnalysisError::InvalidAmplitudeFloor { value }) if value.is_nan()
    ));
    assert!(!projection.samples().contains(&ScalarSample::Masked));
}