Spaces:
Running
Running
"""Tests for FTIR preprocessing functionality.""" | |
import pytest | |
import numpy as np | |
from utils.preprocessing import ( | |
preprocess_spectrum, | |
validate_spectrum_range, | |
get_modality_info, | |
MODALITY_RANGES, | |
MODALITY_PARAMS, | |
) | |
def test_modality_ranges(): | |
"""Test that modality ranges are correctly defined.""" | |
assert "raman" in MODALITY_RANGES | |
assert "ftir" in MODALITY_RANGES | |
raman_range = MODALITY_RANGES["raman"] | |
ftir_range = MODALITY_RANGES["ftir"] | |
assert raman_range[0] < raman_range[1] # Valid range | |
assert ftir_range[0] < ftir_range[1] # Valid range | |
assert ftir_range[0] >= 400 # FTIR starts at 400 cm⁻¹ | |
assert ftir_range[1] <= 4000 # FTIR ends at 4000 cm⁻¹ | |
def test_validate_spectrum_range(): | |
"""Test spectrum range validation for different modalities.""" | |
# Test Raman range validation | |
raman_x = np.linspace(300, 3500, 100) # Typical Raman range | |
assert validate_spectrum_range(raman_x, "raman") == True | |
# Test FTIR range validation | |
ftir_x = np.linspace(500, 3800, 100) # Typical FTIR range | |
assert validate_spectrum_range(ftir_x, "ftir") == True | |
# Test out-of-range data | |
out_of_range_x = np.linspace(50, 150, 100) # Too low for either | |
assert validate_spectrum_range(out_of_range_x, "raman") == False | |
assert validate_spectrum_range(out_of_range_x, "ftir") == False | |
def test_ftir_preprocessing(): | |
"""Test FTIR-specific preprocessing parameters.""" | |
# Generate synthetic FTIR spectrum | |
x = np.linspace(400, 4000, 200) # FTIR range | |
y = np.sin(x / 500) + 0.1 * np.random.randn(len(x)) + 2.0 # Synthetic absorbance | |
# Test FTIR preprocessing | |
x_proc, y_proc = preprocess_spectrum(x, y, modality="ftir", target_len=500) | |
assert x_proc.shape == (500,) | |
assert y_proc.shape == (500,) | |
assert np.all(np.diff(x_proc) > 0) # Monotonic increasing | |
assert np.min(y_proc) >= 0.0 # Normalized to [0, 1] | |
assert np.max(y_proc) <= 1.0 | |
def test_raman_preprocessing(): | |
"""Test Raman-specific preprocessing parameters.""" | |
# Generate synthetic Raman spectrum | |
x = np.linspace(200, 3500, 200) # Raman range | |
y = np.exp(-(((x - 1500) / 200) ** 2)) + 0.05 * np.random.randn( | |
len(x) | |
) # Gaussian peak | |
# Test Raman preprocessing | |
x_proc, y_proc = preprocess_spectrum(x, y, modality="raman", target_len=500) | |
assert x_proc.shape == (500,) | |
assert y_proc.shape == (500,) | |
assert np.all(np.diff(x_proc) > 0) # Monotonic increasing | |
assert np.min(y_proc) >= 0.0 # Normalized to [0, 1] | |
assert np.max(y_proc) <= 1.0 | |
def test_modality_specific_parameters(): | |
"""Test that different modalities use different default parameters.""" | |
x = np.linspace(400, 4000, 200) | |
y = np.sin(x / 500) + 1.0 | |
# Test that FTIR uses different window length than Raman | |
ftir_params = MODALITY_PARAMS["ftir"] | |
raman_params = MODALITY_PARAMS["raman"] | |
assert ftir_params["smooth_window"] != raman_params["smooth_window"] | |
# Preprocess with both modalities (should use different parameters) | |
x_raman, y_raman = preprocess_spectrum(x, y, modality="raman") | |
x_ftir, y_ftir = preprocess_spectrum(x, y, modality="ftir") | |
# Results should be slightly different due to different parameters | |
assert not np.allclose(y_raman, y_ftir, rtol=1e-10) | |
def test_get_modality_info(): | |
"""Test modality information retrieval.""" | |
raman_info = get_modality_info("raman") | |
ftir_info = get_modality_info("ftir") | |
assert "range" in raman_info | |
assert "params" in raman_info | |
assert "range" in ftir_info | |
assert "params" in ftir_info | |
# Check that ranges match expected values | |
assert raman_info["range"] == MODALITY_RANGES["raman"] | |
assert ftir_info["range"] == MODALITY_RANGES["ftir"] | |
# Check that parameters are present | |
assert "baseline_degree" in raman_info["params"] | |
assert "smooth_window" in ftir_info["params"] | |
def test_invalid_modality(): | |
"""Test handling of invalid modality.""" | |
x = np.linspace(1000, 2000, 100) | |
y = np.sin(x / 100) | |
with pytest.raises(ValueError, match="Unsupported modality"): | |
preprocess_spectrum(x, y, modality="invalid") | |
with pytest.raises(ValueError, match="Unknown modality"): | |
validate_spectrum_range(x, "invalid") | |
with pytest.raises(ValueError, match="Unknown modality"): | |
get_modality_info("invalid") | |
def test_modality_parameter_override(): | |
"""Test that modality defaults can be overridden.""" | |
x = np.linspace(400, 4000, 100) | |
y = np.sin(x / 500) + 1.0 | |
# Override FTIR default window length | |
custom_window = 21 # Different from FTIR default (13) | |
x_proc, y_proc = preprocess_spectrum( | |
x, y, modality="ftir", window_length=custom_window | |
) | |
assert x_proc.shape[0] > 0 | |
assert y_proc.shape[0] > 0 | |
def test_range_validation_warning(): | |
"""Test that range validation warnings work correctly.""" | |
# Create spectrum outside typical FTIR range | |
x_bad = np.linspace(100, 300, 50) # Too low for FTIR | |
y_bad = np.ones_like(x_bad) | |
# Should still process but with validation disabled | |
x_proc, y_proc = preprocess_spectrum( | |
x_bad, y_bad, modality="ftir", validate_range=False # Disable validation | |
) | |
assert len(x_proc) > 0 | |
assert len(y_proc) > 0 | |
def test_backwards_compatibility(): | |
"""Test that old preprocessing calls still work (defaults to Raman).""" | |
x = np.linspace(1000, 2000, 100) | |
y = np.sin(x / 100) | |
# Old style call (should default to Raman) | |
x_old, y_old = preprocess_spectrum(x, y) | |
# New style call with explicit Raman | |
x_new, y_new = preprocess_spectrum(x, y, modality="raman") | |
# Should be identical | |
np.testing.assert_array_equal(x_old, x_new) | |
np.testing.assert_array_equal(y_old, y_new) | |
if __name__ == "__main__": | |
pytest.main([__file__]) | |