from pathlib import Path import sys import numpy as np import pytest sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from raptor_validation.output_comparison import compare_output_arrays def test_absolute_floor_does_not_accept_lost_small_output(): result = compare_output_arrays(np.zeros(2), np.array([1e-4, -2e-4]), atol=1e-3, rtol=1e-4) assert result.pointwise_passed assert result.relative_l2_error == pytest.approx(1) assert not result.passed @pytest.mark.parametrize("scale", [1e-200, 1e-4, 1, 1e200]) def test_small_relative_roundoff_is_independent_of_scale(scale): expected = np.array([1, -2, 3], dtype=np.float64) * scale result = compare_output_arrays(expected * (1 + 1e-6), expected, atol=1e-3, rtol=1e-4) assert result.passed assert result.relative_l2_error == pytest.approx(1e-6) def test_norm_agreement_cannot_hide_an_incorrect_small_element(): result = compare_output_arrays([1e6, .01], [1e6, 0.0], atol=1e-3, rtol=1e-4) assert result.relative_l2_error < 1e-4 assert not result.pointwise_passed assert not result.passed @pytest.mark.parametrize("actual,expected", [([np.inf], [1.0]), ([1.0], [np.inf]), ([np.inf], [np.inf]), ([np.nan], [np.nan])]) def test_nonfinite_outputs_never_pass(actual, expected): assert not compare_output_arrays(actual, expected, atol=1e-3, rtol=1e-4).passed @pytest.mark.parametrize("actual,expected", [([1.0], [[1.0]]), ([1.0], [])]) def test_broadcasting_cannot_hide_output_shape_errors(actual, expected): with pytest.raises(ValueError, match="shapes differ"): compare_output_arrays(actual, expected, atol=1e-3, rtol=1e-4) def test_zero_reference_retains_absolute_check(): result = compare_output_arrays([1e-6], [0.0], atol=1e-3, rtol=1e-4) assert result.passed and result.relative_l2_error is None assert not compare_output_arrays([.01], [0.0], atol=1e-3, rtol=1e-4).passed def test_zero_relative_tolerance_requests_absolute_only_check(): result = compare_output_arrays([0.0], [1e-4], atol=1e-3, rtol=0) assert result.passed def test_integer_outputs_require_exact_agreement_without_float_rounding(): expected = np.array([2**53], np.int64) result = compare_output_arrays(expected + 1, expected, atol=1e-3, rtol=1e-4) assert not result.passed and result.max_abs_diff == 1 def test_empty_outputs_match(): assert compare_output_arrays([], [], atol=1e-3, rtol=1e-4).passed @pytest.mark.parametrize("atol,rtol", [(-1, 0), (0, -1), (np.inf, 0), (0, np.nan)]) def test_invalid_tolerances_are_rejected(atol, rtol): with pytest.raises(ValueError, match="tolerances"): compare_output_arrays([0.0], [0.0], atol=atol, rtol=rtol) def test_complex_outputs_are_not_silently_cast_to_real(): with pytest.raises(ValueError, match="real numeric"): compare_output_arrays([1 + 2j], [1 + 0j], atol=1e-3, rtol=1e-4)