Repository: claritychallenge/clarity (language: python)

Issue:
81-unit-tests-for-data-module part 1
Working towards completing the unit-tests for the data module, issue #81 

Will split this over a number of PRs and will be doing a bit of test-driven refactoring along the way.
Inconsistent logic with parameter shift in evaluator/haspi/eb.CenterFreq
The EarModel in evaluator/haspi/eb computes the center frequency using shift = 0.02

https://github.com/claritychallenge/clarity/blob/94e39d43a304f2b13e33278451fe17c1860f4a2f/clarity/evaluator/haspi/eb.py#L70-L75

However, the function CenterFreq in evaluator/haspi/eb forces the parameter to be None to keep consistency with Matlab

https://github.com/claritychallenge/clarity/blob/94e39d43a304f2b13e33278451fe17c1860f4a2f/clarity/evaluator/haspi/eb.py#L180-L233

What is the point of setting shift in center freq if it is never used? 
Setting the value in the Ear Model gives the impression that a shift is applied, won't that confuse people while reading the code? 

What would be the best way to clean this part of the code?
1- Changing shift to None in EarModel?
2- Removing shift parameter from CenterFreq?
3- Removing the `shift=None` statement in CenterFreq?



Consistent use of pytest.approx in tests
We have a lot of tests that compare floating point results and are hard to reproduce on different platforms.

I think we should confine these to tests/regression and use pytest.approx in all other test modules.

This will mean that we can have a set of fast tests that are not fussy about CPU architecture that can run with,

pytest --ignore tests/regression

And we can keep the super-sensitive longer regression tests which run with GitHub actions when we merge. 

This will make it easier to test during development,  (e.g. when developing on a mac, the test suite is currently throwing lots of errors due to differences in the 8th or 9th significant fugue)



Discussion / hints:

Presumably there may be instances where it is desirable to have a shift applied.

If that is the case then my approach would be to add a `shift` parameter to the `EarModel` function...

```python
def EarModel(x, xsamp, y, ysamp, HL, itype, Level1, shift: float=None):
```

...line [72](https://github.com/claritychallenge/clarity/blob/94e39d43a304f2b13e33278451fe17c1860f4a2f/clarity/evaluator/haspi/eb.py#L72) where `shift = 0.02` can then be removed and whatever value `EarModel(shift = XX)` is called with will get passed in the next line [73](https://github.com/claritychallenge/clarity/blob/94e39d43a304f2b13e33278451fe17c1860f4a2f/clarity/evaluator/haspi/eb.py#L73) to the `CenterFreq()` function (i.e. `cfreq1 = CenterFreq(nchan, shift)`).

In the `CenterFreq()` function definition the default is already `shift=None` and so if the above change is made to `EarModel()` then the value will cascade through and the current line [208](https://github.com/claritychallenge/clarity/blob/94e39d43a304f2b13e33278451fe17c1860f4a2f/clarity/evaluator/haspi/eb.py#L208) can be removed.

That way things should just cascade through, with the default being `None`.

I hope that makes sense, and apologies I can't comment on what the value should actually be.
Had to do a bit of digging in the original MATLAB code to fully understand this (I've added links to the MATLAB are below)

The issue here is that there is a bug in the original HASPI MATLAB implementation that we were translating to Python. But the bug has been 'baked into' HASPI and so can't simply be fixed at this stage. To explain:

At different points in the code, the CenterFreq function is sometimes called with no shift and sometimes with a shift of 0.02, but... because of a bug the shift is never actually applied. The "shift=None" statement that Zuza has added is basically replicating the bug. 

Zuza's comment that "the current code was trained with this bug" refers, I think, to the fact that HASPI has a trained neural network stage which has hard-coded weights. The weights were are using are the  ones trained by Kates' lab (i.e. to fit to experimental data) .... but the training was, presumably, inadvertently done with this bug in place. So it's a case where 'fixing' the bug will break HASPI.

I think the best solution would be a combination of Gerardo's 1 and 3.
-   We remove the shift=None so that CentrreFreq will work as advertised.  
-  We change the default of EarModel to "shift=None" so that EarModel behaviour doesn't change
- Where the EarModel's are constructed in the top-level HASPI, HAAQI code etc, we have a commented out "shift=0.02" replaced with "shift=None" and a comment to explain we are do this to replicate the bug in the original. i.e. "shift=0.02" is the theoretically justified value but "shift=None" is what has been (effectively) used in all previous HASPI implementations.

This will allow CentreFreq and EarModel to work with their intended behaviours, it will leave HASPI and HAAQI results unchanged and will more clearly communicate the issue.

For reference:

https://github.com/claritychallenge/clarity_dev/blob/development/dev/HASPI_dev/MATLAB/HASPIv2_HASQIv2_HAAQIv1/eb_CenterFreq.m   (the bug is 'nargin>3' I believe that should be 2 or 1 -- can't remember has MATLAB counts arguments)

https://github.com/claritychallenge/clarity_dev/blob/development/dev/HASPI_dev/MATLAB/HASPIv2_HASQIv2_HAAQIv1/eb_EarModel.m  (this is the function that thinks it is calling CentreFreq with a shift of 0.02)

Incidentally, I don't really understand the rationale for the shift but my understanding is that it is a 2% correction to filter centre frequencies -- hard to imagine that it makes much difference. I wonder if Jim Kates is aware of the error?)




@jonbarker68 , thank you so much for the clarification.
Let's wait for the #85 to be merged before doing changes in haspi.eb
Just to play safe




Fix patch:
diff --git a/.vscode/settings.json b/.vscode/settings.json
index dcfc2048b..1dfd9751b 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -5,8 +5,15 @@
         "hasqi"
     ],
     "python.testing.pytestArgs": [
-        "tests"
+        "tests/",
+        "--cov",
+        "--cov-branch",
+        "--cov-report",
+        "html",
     ],
     "python.testing.unittestEnabled": false,
-    "python.testing.pytestEnabled": true
+    "python.testing.pytestEnabled": true,
+    "githubPullRequests.ignoredPullRequestBranches": [
+        "main"
+    ],
 }
\ No newline at end of file
diff --git a/clarity/data/HOA_tools_cec2.py b/clarity/data/HOA_tools_cec2.py
index 9ff0add33..83f61c16b 100644
--- a/clarity/data/HOA_tools_cec2.py
+++ b/clarity/data/HOA_tools_cec2.py
@@ -2,10 +2,12 @@
 from __future__ import annotations
 
 import logging
+from typing import Any
 
 import numpy as np
 from numba import njit
 from numba.typed import List as TypedList  # pylint: disable=no-name-in-module
+from numpy import ndarray
 from scipy.signal import convolve
 from scipy.spatial.transform import Rotation as R
 from scipy.special import comb
@@ -56,7 +58,7 @@ def convert_a_to_b_format(
 
 # Code for generation ambisonic rotation matrices
 @njit
-def compute_rotation_matrix(n: int, foa_rotmat: np.ndarray) -> np.ndarray:
+def compute_rotation_matrix(n: int, foa_rotmat: ndarray) -> ndarray:
     """Generate a rotation matrix to rotate HOA soundfield.
 
     Based on [1]_ and [2]_. Operates on HOA of a given order rotates by azimuth theta
@@ -92,7 +94,6 @@ def compute_rotation_matrix(n: int, foa_rotmat: np.ndarray) -> np.ndarray:
 
     sub_matrices = [np.eye(i * 2 + 1) for i in np.arange(n + 1)]
     sub_matrices[1] = foa_rotmat
-
     typed_sub_matrices = TypedList()
     for x in sub_matrices:
         typed_sub_matrices.append(x)
@@ -126,7 +127,13 @@ def centred_element(reference: np.ndarray, row: int, col: int):
 
 
 @njit
-def P(i, a, b, order, rotation_matrices):
+def P(
+    i: int,
+    a: int,
+    b: int,
+    order: int,
+    rotation_matrices: TypedList,
+) -> TypedList:
     """P function for rotation matrix calculation.
 
     Args:
@@ -165,7 +172,12 @@ def P(i, a, b, order, rotation_matrices):
 
 
 @njit
-def U(degree, n, order, rotation_matrices):
+def U(
+    degree: int,
+    n: int,
+    order: int,
+    rotation_matrices: TypedList,
+) -> TypedList:
     """U coefficient initialiser for rotation matrix calculation.
 
     Args:
@@ -182,7 +194,12 @@ def U(degree, n, order, rotation_matrices):
 
 
 @njit
-def V(degree, n, order, rotation_matrices):
+def V(
+    degree: int,
+    n: int,
+    order: int,
+    rotation_matrices: TypedList,
+) -> TypedList:
     """V coefficient initialiser for rotation matrix calculation.
 
     Args:
@@ -194,7 +211,7 @@ def V(degree, n, order, rotation_matrices):
     Returns:
         float: V value
     """
-    d = 0
+    d = 0.0
     if degree == 0:
         v_coeff = P(1, 1, n, order, rotation_matrices) + P(
             -1, -1, n, order, rotation_matrices
@@ -215,7 +232,7 @@ def V(degree, n, order, rotation_matrices):
 
 
 @njit
-def W(degree, n, order, rotation_matrices):
+def W(degree: int, n: int, order: int, rotation_matrices: TypedList) -> TypedList:
     """W coefficient initialiser for rotation matrix calculation.
 
     Args:
@@ -285,7 +302,7 @@ def compute_UVW_coefficients(degree, n, order):
 
 
 @njit
-def compute_band_rotation(order, rotation_matrices, output):
+def compute_band_rotation(order: int, rotation_matrices: TypedList, output):
     """Compute submatrix for rotation matrix.
 
     Args:
@@ -297,7 +314,6 @@ def compute_band_rotation(order, rotation_matrices, output):
         matrix: rotation submatrix
     """
     # print(f'entering band rotation with l = {el}')
-
     for row, m in enumerate(np.arange(-order, order + 1, 1)):
         for col, n in enumerate(np.arange(-order, order + 1, 1)):
             u, v, w = compute_UVW_coefficients(m, n, order)
@@ -360,7 +376,7 @@ def __init__(self, order, resolution):
             foa_rotmat = np.linalg.inv(foa_rotmat)
             self.rotmat[i, :, :] = compute_rotation_matrix(order, foa_rotmat)
 
-    def rotate(self, signal, rotation_vector):
+    def rotate(self, signal: ndarray, rotation_vector: ndarray) -> ndarray:
         """Apply rotation to HOA signals using precomputed rotation matrices.
 
         Args:
@@ -389,7 +405,11 @@ def rotate(self, signal, rotation_vector):
         return signal
 
 
-def binaural_mixdown(ambisonic_signals, hrir, hrir_metadata):
+def binaural_mixdown(
+    ambisonic_signals: ndarray,
+    hrir: dict[str, Any],
+    hrir_metadata: dict[str, Any],
+) -> ndarray:
     """Perform binaural mixdown of ambisonic signals.
 
     Args:
@@ -425,8 +445,8 @@ def binaural_mixdown(ambisonic_signals, hrir, hrir_metadata):
 
 
 def ambisonic_convolve(
-    signal: np.ndarray, hoa_impulse_responses: np.ndarray, order: int
-) -> np.ndarray:
+    signal: ndarray, hoa_impulse_responses: ndarray, order: int
+) -> ndarray:
     """Convolve HOA Impulse Responses with signals.
 
     Args:
@@ -438,6 +458,11 @@ def ambisonic_convolve(
         np.ndarray[samples, channels]: the convolved signal
     """
     n = (order + 1) ** 2
+    if n > hoa_impulse_responses.shape[1]:
+        raise ValueError(
+            f"Number of channels in impulse response ({hoa_impulse_responses.shape[1]})"
+            f" must be >= number of channels required for order {order} ({n})"
+        )
     return np.array(
         [
             convolve(impulse_response, signal)
@@ -446,7 +471,7 @@ def ambisonic_convolve(
     ).T
 
 
-def compute_rms(input_signal: np.ndarray, axis: int = 0) -> float:
+def compute_rms(input_signal: ndarray, axis: int = 0) -> ndarray:
     """Compute rms values along a given axis.
     Args:
         input_signal (np.ndarray): Input signal
@@ -458,7 +483,7 @@ def compute_rms(input_signal: np.ndarray, axis: int = 0) -> float:
     return np.sqrt(np.mean(input_signal**2, axis=axis))
 
 
-def equalise_rms_levels(inputs: list[np.ndarray]) -> list[np.ndarray]:
+def equalise_rms_levels(inputs: list[ndarray]) -> list[ndarray]:
     """Equalise RMS levels.
 
     Args:
@@ -473,7 +498,7 @@ def equalise_rms_levels(inputs: list[np.ndarray]) -> list[np.ndarray]:
     return outputs
 
 
-def dB_to_gain(x: float) -> float:
+def dB_to_gain(x: int | float) -> float:
     """Convert dB to gain.
 
     Args:
@@ -486,8 +511,8 @@ def dB_to_gain(x: float) -> float:
 
 
 def smoothstep(
-    x: np.ndarray, x_min: float = 0, x_max: float = 1, N: int = 1
-) -> np.ndarray:
+    x: ndarray, x_min: float = 0.0, x_max: float = 1.0, N: int = 1
+) -> ndarray:
     """Apply the smoothstep function.
 
     Args:
@@ -512,7 +537,7 @@ def smoothstep(
 
 def rotation_control_vector(
     array_length: int, start_index: int, end_index: int, smoothness: int = 1
-) -> np.ndarray:
+) -> ndarray:
     """Generate mapped rotation control vector for values of theta.
 
     Args:
@@ -541,7 +566,7 @@ def compute_rotation_vector(
     signal_length: int,
     start_idx: int,
     end_idx: int,
-) -> np.ndarray:
+) -> ndarray:
     """Compute the rotation vector.
 
     Args:
diff --git a/clarity/evaluator/haspi/eb.py b/clarity/evaluator/haspi/eb.py
index 3e7571d92..34872be45 100644
--- a/clarity/evaluator/haspi/eb.py
+++ b/clarity/evaluator/haspi/eb.py
@@ -29,7 +29,7 @@ def ear_model(
     level1,
     nchan=32,
     m_delay=1,
-    shift=0.02,
+    shift=None,
 ):
     """
     Function that implements a cochlear model that includes the middle ear,
@@ -117,11 +117,10 @@ def ear_model(
 
     # Parameters for the control filter bank
     hl_max = [100, 100, 100, 100, 100, 100]
-    _center_freq_control = center_frequency(
-        nchan, shift
-    )  # Center frequencies for the control
-    _, bandwidth_1, _, _, _ = loss_parameters(hl_max, _center_freq_control)
+    # Compute center frequencies for the control
+    _center_freq_control = center_frequency(nchan, shift)
     # Maximum BW for the control
+    _, bandwidth_1, _, _, _ = loss_parameters(hl_max, _center_freq_control)
 
     # Input signal adjustments
     # Convert the signals to 24 kHz sampling rate.
@@ -357,9 +356,6 @@ def center_frequency(
     Translated from MATLAB to Python by Zuzanna Podwinska, March 2022.
     """
 
-    # In the Matlab code, the loop below never evaluates
-    # (but the current code was trained with this bug)
-    shift = None  # This is to keep consistency with MATLAB code
     if shift is not None:
         k = 1
         A = 165.4  # pylint: disable=invalid-name
diff --git a/clarity/evaluator/haspi/haspi.py b/clarity/evaluator/haspi/haspi.py
index e20b05f62..79f9e488a 100644
--- a/clarity/evaluator/haspi/haspi.py
+++ b/clarity/evaluator/haspi/haspi.py
@@ -30,6 +30,13 @@ def haspi_v2(  # pylint: disable=too-many-arguments too-many-locals
     version of HASPI uses a modulation filterbank followed by an ensemble of
     neural networks to compute the estimated intelligibility.
 
+    **NB** - The original HASPI model derivation included a bug which meant that
+    although the 'shift' parameter used in band centre frequency calculations was set to
+    '0.02' it was never actually applied. To replicate this behaviour ear_model is
+    called with 'shift' set to None.  For discussion please refer to the discussion in
+    `Issue #105 <https://github.com/claritychallenge/clarity/issues/105>`
+    for further details.
+
     Args:
         reference (np.ndarray): Clear input reference speech signal with no noise or
             distortion. If a hearing loss is specified, no amplification should be
@@ -68,6 +75,8 @@ def haspi_v2(  # pylint: disable=too-many-arguments too-many-locals
         hearing_loss,
         itype,
         level1,
+        # shift=0.02 # See comment in docstring
+        shift=None,
     )
 
     # Envelope modulation features
diff --git a/tests/recipes/cad1/task1/__init__.py b/recipes/cad1/task1/__init__.py
similarity index 100%
rename from tests/recipes/cad1/task1/__init__.py
rename to recipes/cad1/task1/__init__.py
diff --git a/tests/recipes/cad1/task2/__init__.py b/recipes/cad1/task2/__init__.py
similarity index 100%
rename from tests/recipes/cad1/task2/__init__.py
rename to recipes/cad1/task2/__init__.py


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
index 2aacd20da..9149d521f 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,4 +1,6 @@
 """Fixtures for testing."""
+from __future__ import annotations
+
 from pathlib import Path
 
 import numpy as np
@@ -12,6 +14,32 @@
 
 
 @pytest.fixture
-def random_matrix() -> np.ndarray:
-    """Generate a random matrix for use in tests."""
-    return np.asarray(rng.random((100, 100)))
+def make_random_matrix():
+    """Generate a random matrix for use in tests.
+
+    The fixture returns a function that can be called to generate a random matrix
+    >>> def my_test(random_matrix):
+    >>>     matrix = random_matrix(seed=1234)
+    or use the global seed
+    >>>     matrix = random_matrix()
+    """
+
+    def _random_matrix(seed: int | None = None, size=(100, 100)) -> np.ndarray:
+        if seed is not None:
+            # Seed is supplied so use a generator with that seed...
+            rng_to_use = np.random.default_rng(seed)
+        else:
+            # ... else use the global generator
+            rng_to_use = rng
+        return np.asarray(rng_to_use.random(size))
+
+    return _random_matrix
+
+
+def pytest_configure() -> None:
+    """Configure custom variables for pytest.
+
+    **NB**: pytest automatically calls this hook when the conftest is loaded.
+    """
+    pytest.abs_tolerance = 1e-7
+    pytest.rel_tolerance = 1e-7
diff --git a/tests/data/test_HOA_tools_cec2.py b/tests/data/test_HOA_tools_cec2.py
index e1a5b6983..cb5c88c92 100644
--- a/tests/data/test_HOA_tools_cec2.py
+++ b/tests/data/test_HOA_tools_cec2.py
@@ -1,48 +1,140 @@
 """Tests for the data.HOA_tools_cec2 module."""
 import numpy as np
 import pytest
+from numba.typed import List as TypedList  # pylint: disable=no-name-in-module
 
 # HOARotator,; ambisonic_convolve,; binaural_mixdown,;;;
 # compute_band_rotation,;; compute_rotation_matrix,;  dot,; dot,
 from clarity.data.HOA_tools_cec2 import (
+    HOARotator,
     P,
     U,
     V,
     W,
+    ambisonic_convolve,
+    binaural_mixdown,
     centred_element,
+    compute_band_rotation,
     compute_rms,
+    compute_rotation_matrix,
     compute_rotation_vector,
     compute_UVW_coefficients,
+    convert_a_to_b_format,
     dB_to_gain,
+    dot,
     equalise_rms_levels,
     rotation_control_vector,
     smoothstep,
 )
 
 
+def test_convert_a_to_b_format() -> None:
+    """Test for convert_a_to_b_format() function."""
+    np.random.seed(1234)
+    signal = np.random.random((4, 100))
+    result = convert_a_to_b_format(
+        signal[0, :], signal[1, :], signal[2, :], signal[3, :]
+    )
+    assert result.shape == (4, 100)
+    assert np.sum(np.abs(signal)) == pytest.approx(208.78666127520782)
+
+
+def test_convert_a_to_b_format_index_error() -> None:
+    """Test convert_a_to_b_format() raises IndexError if inputs not same length."""
+    with pytest.raises(ValueError):
+        convert_a_to_b_format(np.zeros(100), np.zeros(100), np.zeros(100), np.zeros(80))
+
+
 def test_compute_rotation_matrix() -> None:
     """Test for compute_rotation_matrix() function."""
-    assert True
+    order = 2
+    rot_mat_dim = (order + 1) ** 2
+
+    # Test for identity matrix
+    rot_mat = compute_rotation_matrix(order, np.eye(3))
+    assert rot_mat == pytest.approx(np.eye(rot_mat_dim))
+
+    # Test for 90 degree rotation about x-axis
+    rot_mat = compute_rotation_matrix(
+        2, np.array([[0.0, 0.0, -1.0], [0.0, 1.0, 0.0], [1.0, 0.0, 0.0]])
+    )
+    assert rot_mat.shape == (rot_mat_dim, rot_mat_dim)
+    # For this matrix, all values should be either 0, 1 or -1
+    assert np.all(
+        np.isclose(rot_mat, 0.0) + np.isclose(rot_mat, 1.0) + np.isclose(rot_mat, -1.0)
+    )
+
+
+def test_hoa_rotator_construction() -> None:
+    """Test for HOARotator class construction."""
+
+    # Testing 2nd order matrix with a 90 degree rotation step
+    hoa_rotator = HOARotator(2, 90)
+    rot_mat = hoa_rotator.rotmat
+    assert rot_mat.shape == (4, 9, 9)
+    rot90 = np.array([[0.0, 0.0, -1.0], [0.0, 1.0, 0.0], [1.0, 0.0, 0.0]])
+    assert rot_mat[1, :, :] == pytest.approx(compute_rotation_matrix(2, rot90))
+    # For multiples of 90 degrees all entries should be either 0, 1 or -1
+    assert np.all(
+        np.isclose(rot_mat, 0.0) + np.isclose(rot_mat, 1.0) + np.isclose(rot_mat, -1.0)
+    )
+
+
+def test_hoa_rotate_no_rotation() -> None:
+    """Test the HOARotator rotate method"""
+
+    # 1st order ambinsonic signal, ie 4 channels
+    np.random.seed(1234)
+    signal = np.random.random((100, 4))
+    # equal length signal specify rotation for each sample
+    rotations = np.zeros(100)
+    # Apply the rotation
+    hoa_rotator = HOARotator(1, 90)
+    result = hoa_rotator.rotate(signal, rotations)
+    assert result.shape == (100, 4)
+    assert result == pytest.approx(signal)
 
 
 @pytest.mark.parametrize(
-    "row, col, expected",
+    "seed, expected",
     [
-        (1, 1, 0.8840922122960315),
-        (45, 54, 0.2378095805520779),
-        (18, 91, 0.39897595320190293),
+        (1234, 34.9827354617758),
+        (1235, 37.63083581519224),
+        (1236, 35.83739821236641),
+    ],
+)
+def test_hoa_rotate(seed: int, expected: float) -> None:
+    """Test the HOARotator rotate method"""
+
+    # 1st order ambinsonic signal, ie 4 channels
+    np.random.seed(seed)
+    signal = np.random.random((20, 4))
+    rotations = np.random.random(20)
+    hoa_rotator = HOARotator(1, 90)
+    result = hoa_rotator.rotate(signal, rotations)
+    assert np.sum(np.abs(result)) == pytest.approx(expected)
+
+
+@pytest.mark.parametrize(
+    "seed, row, col, expected",
+    [
+        (1234, 1, 1, 0.740996817336437),
+        (1235, 45, 24, 0.2617612929455504),
+        (1236, 18, 47, 0.2706397841499083),
     ],
 )
 def test_centred_element(
-    random_matrix: np.ndarray, row: int, col: int, expected: float
+    make_random_matrix, seed: int, row: int, col: int, expected: float
 ) -> None:
     """Test for centered_element() function."""
-    assert centred_element(random_matrix, row, col) == expected
+    random_matrix = make_random_matrix(seed)
+    assert centred_element(random_matrix, row, col) == pytest.approx(expected)
 
 
-def test_centred_element_index_error(random_matrix: np.ndarray) -> None:
+def test_centred_element_index_error(make_random_matrix) -> None:
     """Test that centered_element() raises an IndexError if centering is outside
     of matrix dimensions."""
+    random_matrix = make_random_matrix()
     with pytest.raises(IndexError):
         centred_element(random_matrix, row=101, col=2)
     with pytest.raises(IndexError):
@@ -50,107 +142,110 @@ def test_centred_element_index_error(random_matrix: np.ndarray) -> None:
 
 
 @pytest.mark.parametrize(
-    "i, a, b, order, expected",
+    "i, a, b, order, seed, expected",
     [
-        (1, 1, 2, 2, -0.3475073926260553),
-        (1, 1, 2, -2, 0.6814353682034401),
-        (1, 1, 2, 2, -0.7185822065660934),
+        (1, 1, 2, 2, 1234, -0.20056163713681976),
+        (1, 1, 2, -2, 1235, 0.1767992365183524),
+        (1, 1, 2, 2, 1236, -0.5552636713315029),
     ],
 )
 def test_P(
-    i: int, a: int, b: int, order: int, random_matrix: np.ndarray, expected: float
+    i: int, a: int, b: int, order: int, make_random_matrix, seed: int, expected: float
 ) -> None:
     """Test for P() function."""
-    assert (
-        P(
-            i,
-            a,
-            b,
-            order,
-            rotation_matrices=[random_matrix, random_matrix, random_matrix],
-        )
-        == expected
-    )
-
-
-def test_P_index_error(random_matrix: np.ndarray) -> None:
+    r1_matrix = make_random_matrix(seed)
+    r2_matrix = make_random_matrix(seed + 10)  # Different seed to get different matrix
+    r3_matrix = make_random_matrix(seed + 20)  # ditto
+    assert P(
+        i,
+        a,
+        b,
+        order,
+        rotation_matrices=TypedList([r1_matrix, r2_matrix, r3_matrix]),
+    ) == pytest.approx(expected)
+
+
+def test_P_index_error(make_random_matrix) -> None:
     """Test P() raises IndexError if invalid indices to r are provided."""
+    random_matrix = make_random_matrix()
+    rotation_matrices = TypedList([random_matrix])
     with pytest.raises(IndexError):
-        assert P(i=1, a=5, b=1, order=1, rotation_matrices=[random_matrix])
+        assert P(i=1, a=5, b=1, order=1, rotation_matrices=rotation_matrices)
     with pytest.raises(IndexError):
-        assert P(i=1, a=5, b=1, order=-1, rotation_matrices=[random_matrix])
+        assert P(i=1, a=5, b=1, order=-1, rotation_matrices=rotation_matrices)
     with pytest.raises(IndexError):
-        assert P(i=1, a=5, b=4, order=3, rotation_matrices=[random_matrix])
+        assert P(i=1, a=5, b=4, order=3, rotation_matrices=rotation_matrices)
 
 
 @pytest.mark.parametrize(
-    "degree, n, order, expected",
+    "degree, n, order, seed, expected",
     [
-        (1, 2, 2, 0.8066335578037782),
-        (1, 2, -2, 0.647932971103906),
-        (1, 2, 2, 0.504340149676519),
+        (1, 2, 2, 2234, -0.08645029499482301),
+        (1, 2, -2, 2235, 0.1550159595919902),
+        (1, 2, 2, 2236, -0.6545704498040456),
     ],
 )
 def test_U(
-    degree: int, n: int, order: int, random_matrix: np.ndarray, expected: float
+    degree: int, n: int, order: int, make_random_matrix, seed: int, expected: float
 ) -> None:
     """Test for U function."""
-    assert (
-        U(
-            degree,
-            n,
-            order,
-            rotation_matrices=[random_matrix, random_matrix, random_matrix],
-        )
-        == expected
-    )
+    r1_matrix = make_random_matrix(seed)
+    r2_matrix = make_random_matrix(seed + 10)  # Different seed to get different matrix
+    r3_matrix = make_random_matrix(seed + 20)  # ditto
+    assert U(
+        degree,
+        n,
+        order,
+        rotation_matrices=TypedList([r1_matrix, r2_matrix, r3_matrix]),
+    ) == pytest.approx(expected)
 
 
 @pytest.mark.parametrize(
-    "degree, n, order, expected",
+    "degree, n, order, seed, expected",
     [
-        (0, 2, 2, -0.02607000059684761),
-        (1, 2, 2, -0.8041211269599031),
-        (1, 2, -2, 0.889239093568916),
-        (-1, 2, -2, 0.5668224726503112),
+        (0, 2, 2, 3234, 0.6475456062624654),
+        (1, 2, 2, 3235, -0.009790830648337613),
+        (1, 2, -2, 3236, 1.3180460765445767),
+        (-1, 2, -2, 3237, 0.44136069630527924),
     ],
 )
 def test_V(
-    degree: int, n: int, order: int, random_matrix: np.ndarray, expected: float
+    degree: int, n: int, order: int, make_random_matrix, seed: int, expected: float
 ) -> None:
     """Test for V() function."""
-    assert (
-        V(
-            degree,
-            n,
-            order,
-            rotation_matrices=[random_matrix, random_matrix, random_matrix],
-        )
-        == expected
-    )
+    r1_matrix = make_random_matrix(seed)
+    r2_matrix = make_random_matrix(seed + 10)  # Different seed to get different matrix
+    r3_matrix = make_random_matrix(seed + 20)  # ditto
+    assert V(
+        degree,
+        n,
+        order,
+        rotation_matrices=TypedList([r1_matrix, r2_matrix, r3_matrix]),
+    ) == pytest.approx(expected)
 
 
 @pytest.mark.parametrize(
-    "degree, n, order, expected",
+    "degree, n, order, seed, expected",
     [
-        (0, 2, 2, 0.0),
-        (1, 2, 2, -0.18322112315448064),
-        (-1, 2, 2, -0.3501066433176404),
+        (0, 2, 2, None, 0.0),  # No seed, as should result in 0.0 for all matrices
+        (1, 2, 2, 4235, 0.6708751988390329),
+        (-1, 2, 2, 4236, 0.38743574623364374),
     ],
 )
 def test_W(
-    degree: int, n: int, order: int, random_matrix: np.ndarray, expected: float
+    degree: int, n: int, order: int, make_random_matrix, seed: int, expected: float
 ) -> None:
     """Test for W() function."""
-    assert (
-        W(
-            degree,
-            n,
-            order,
-            rotation_matrices=[random_matrix, random_matrix, random_matrix],
-        )
-        == expected
-    )
+    r1_matrix = make_random_matrix(seed)
+    r2_matrix = make_random_matrix(seed + 10 if seed is not None else None)
+    r3_matrix = make_random_matrix(seed + 10 if seed is not None else None)  # ditto
+
+    assert W(
+        degree,
+        n,
+        order,
+        rotation_matrices=TypedList([r1_matrix, r2_matrix, r3_matrix]),
+    ) == pytest.approx(expected)
 
 
 @pytest.mark.parametrize(
@@ -167,7 +262,7 @@ def test_compute_UVW_coefficients(
     degree: int, n: int, order: int, expected: float
 ) -> None:
     """Test for computer_UVW_coefficients() function."""
-    assert compute_UVW_coefficients(degree, n, order) == expected
+    assert compute_UVW_coefficients(degree, n, order) == pytest.approx(expected)
 
 
 def test_compute_UVW_coefficients_zero_division_error() -> None:
@@ -176,90 +271,133 @@ def test_compute_UVW_coefficients_zero_division_error() -> None:
         compute_UVW_coefficients(degree=-1, n=2, order=-2)
 
 
-# TODO : Not yet working, I don't understand how to get `output` passed in correctly
-# @pytest.mark.parametrize(
-#     "el, output, expected",
-#     [
-#         (2, np.asarray([0, 0]), np.asarray([[1, 2], [3, 4]])),
-#     ],
-# )
-# def test_compute_band_rotation(el: int, output: np.ndarray,
-#   random_matrix: np.ndarray, expected: np.ndarray) -> None:
-#     """Test for compute_band_rotation() function."""
-#     np.testing.assert_array_equal(compute_band_rotation(el,
-# [random_matrix, random_matrix], output), expected)
-
-
-# TODO : Not working yet, results in a typing error???
-# @pytest.mark.parametrize(
-#     "A, B, expected",
-#     [
-#         (np.asarray([[1, 2], [3, 4]]), np.asarray([[4, 3], [2, 1]]),
-#               np.asarray([[1, 2], [3, 4]])),
-#         (np.asarray([[5, 6], [7, 8]]), np.asarray([[8, 7], [6, 5]]),
-#               np.asarray([[1, 2], [3, 4]])),
-#     ],
-# )
-# def test_dot(A: np.ndarray, B: np.ndarray, expected: np.ndarray) -> None:
-#     """Test for dot() function."""
-#     np.testing.assert_array_equal(dot(A, B), expected)
-
-
-def test_HOARotator() -> None:
-    """Test for test_HOARotator class."""
-    assert True
+def test_compute_band_rotation() -> None:
+    """Test for compute_band_rotation() function."""
+
+    # This is not a very useful test as it is just one special case
+    # but this function is tested implicitly in other tests
+    order = 2
+    rot_mats = TypedList([np.eye(1), np.eye(3), np.eye(5)])
+    output = np.eye(9)
+    new_output, new_rotation_matrices = compute_band_rotation(order, rot_mats, output)
+    # output and rotation matrices should be unchanged
+    assert new_output.shape == (9, 9)
+    assert new_output == pytest.approx(np.eye(9))
+    for new_rot_mat, expected_rot_mat in zip(new_rotation_matrices, rot_mats):
+        assert new_rot_mat == pytest.approx(expected_rot_mat)
+
+
+@pytest.mark.parametrize(
+    "A, B, expected",
+    [
+        (
+            np.asarray([[1.0, 2.0], [3.0, 4.0]]),
+            np.asarray([[4.0, 3.0], [2.0, 1.0]]),
+            np.asarray([[8.0, 5.0], [20.0, 13.0]]),
+        ),
+        (
+            np.asarray([[5.0, 6.0], [7.0, 8.0]]),
+            np.asarray([[8.0, 7.0], [6.0, 5.0]]),
+            np.asarray([[76.0, 65.0], [104.0, 89.0]]),
+        ),
+    ],
+)
+def test_dot(A: np.ndarray, B: np.ndarray, expected: np.ndarray) -> None:
+    """Test for dot() function."""
+    result = dot(A, B)
+    assert result == pytest.approx(expected)
 
 
 # TODO : need examples of hrir_metadata dictionary to be able to write a tests for this
 def test_binaural_mixdown() -> None:
     """Test for binaural_mixdown() function."""
-    assert True
+    np.random.seed(1234)
+    hrir = {"M_data": np.random.random((100, 6, 2))}  # 6 random hrir filters
+    hrir_filters = [0, 2, 3, 5]  # 4 channels selected of possible 6
+    n_hrir_filters = len(hrir_filters)
+    hrir_metadata = {
+        "selected_channels": hrir_filters,
+        "matrix": np.eye(n_hrir_filters),  # no rotation
+    }
+    signals = np.random.random((100, n_hrir_filters))
+
+    result = binaural_mixdown(signals, hrir, hrir_metadata)
+    assert result.shape == (199, 2)
+    assert np.sum(np.abs(result)) == pytest.approx(19647.32682819124)
 
 
 def test_ambisonic_convolve() -> None:
     """Test for ambisonic_convolve() function."""
-    assert True
+    np.random.seed(1234)
+    signal = np.random.randn(100)
+    hoa_irs = np.random.random((100, 64))
+    order = 2
+    result = ambisonic_convolve(signal, hoa_irs, order=order)
+    # channel
+    assert result.shape == (199, (order + 1) ** 2)
+    assert np.sum(np.abs(result)) == pytest.approx(3359.04412270433)
+
+
+def test_ambisonic_convolve_error() -> None:
+    """Test for ambisonic_convolve() function."""
+    signal = np.random.randn(100)
+    hoa_irs = np.random.random((100, 64))
+    order = 10  # too high for number of hoa_ir channels
+    with pytest.raises(ValueError):
+        ambisonic_convolve(signal, hoa_irs, order=order)
 
 
 @pytest.mark.parametrize(
     "array, axis, expected",
     [
         (
-            np.asarray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]),
+            np.asarray([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]),
             0,
             [
-                np.sqrt(np.mean([1**2, 4**2, 7**2])),
-                np.sqrt(np.mean([2**2, 5**2, 8**2])),
-                np.sqrt(np.mean([3**2, 6**2, 9**2])),
+                np.sqrt(np.mean([1.0**2, 4.0**2, 7.0**2])),
+                np.sqrt(np.mean([2.0**2, 5.0**2, 8.0**2])),
+                np.sqrt(np.mean([3.0**2, 6.0**2, 9.0**2])),
             ],
         ),
         (
-            np.asarray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]),
+            np.asarray([[1.0, 2.0, 3.0], [4, 5.0, 6.0], [7.0, 8.0, 9.0]]),
             1,
             [
-                np.sqrt(np.mean([1**2, 2**2, 3**2])),
-                np.sqrt(np.mean([4**2, 5**2, 6**2])),
-                np.sqrt(np.mean([7**2, 8**2, 9**2])),
+                np.sqrt(np.mean([1.0**2, 2.0**2, 3.0**2])),
+                np.sqrt(np.mean([4.0**2, 5.0**2, 6.0**2])),
+                np.sqrt(np.mean([7.0**2, 8.0**2, 9.0**2])),
             ],
         ),
     ],
 )
 def test_compute_rms(array: np.ndarray, axis: int, expected: float) -> None:
     """Test for compute_rms() function along both axes."""
-    np.testing.assert_array_equal(compute_rms(input_signal=array, axis=axis), expected)
+    result = compute_rms(input_signal=array, axis=axis)
+    assert result == pytest.approx(expected)
 
 
-def test_equalise_rms_levels(random_matrix: np.ndarray) -> None:
+def test_equalise_rms_levels() -> None:
     """Test for equalise_rms_levels() function."""
-    assert equalise_rms_levels(inputs=[random_matrix, random_matrix])
+    np.random.seed(1234)
+    signal_1 = np.random.random((100, 2)) * 100
+    signal_2 = np.random.random((100, 2)) * 200
+    signal_3 = np.random.random((100, 2)) * 200
+    results = equalise_rms_levels(inputs=[signal_1, signal_2, signal_3])
+    # Check all have same level after equalisation
+    rms_1 = compute_rms(results[0], axis=0)
+    rms_2 = compute_rms(results[1], axis=0)
+    rms_3 = compute_rms(results[2], axis=0)
+    assert rms_1[0] == pytest.approx(rms_3[0])
+    assert rms_2[0] == pytest.approx(rms_3[0])
 
 
 @pytest.mark.parametrize(
     "db, gain", [(10, 3.1622776601683795), (20, 10.0), (0.045, 1.0051942600951387)]
 )
 def test_dB_to_gain(db: float, gain: float) -> None:
-    """Test for XX function."""
-    assert dB_to_gain(db) == gain
+    """Test for test_dB_to_gain function."""
+    result = dB_to_gain(db)
+    assert result == pytest.approx(gain)
 
 
 @pytest.mark.parametrize(
@@ -267,29 +405,29 @@ def test_dB_to_gain(db: float, gain: float) -> None:
     [
         (
             np.asarray([100, 200, 300, 400]),
-            10,
-            300,
+            10.0,
+            300.0,
             1,
-            np.asarray([0.229161, 0.725286, 1.0, 1.0]),
+            np.asarray([0.22916068719504698, 0.725286, 1.0, 1.0]),
         ),
         (
             np.asarray([10, 200, 300, 4000]),
-            10,
-            300,
+            10.0,
+            300.0,
             1,
             np.asarray([0.0, 0.725286, 1.0, 1.0]),
         ),
         (
             np.asarray([10, -200, 300, 4000]),
-            -200,
-            300,
+            -200.0,
+            300.0,
             1,
             np.asarray([0.381024, 0.0, 1.0, 1.0]),
         ),
         (
             np.asarray([10, -50, 300, 4000]),
-            -200,
-            300,
+            -200.0,
+            300.0,
             1,
             np.asarray([0.381024, 0.216, 1.0, 1.0]),
         ),
@@ -299,7 +437,8 @@ def test_smoothstep(
     x: np.ndarray, x_min: float, x_max: float, N: int, expected
 ) -> None:
     """Test for smoothstep() function."""
-    np.testing.assert_array_almost_equal(smoothstep(x, x_min, x_max, N), expected)
+    result = smoothstep(x, x_min, x_max, N)
+    assert result == pytest.approx(expected)
 
 
 @pytest.mark.parametrize(
@@ -318,9 +457,9 @@ def test_rotation_control_vector(
     array_length: int, start_idx: int, end_idx: int, expected: np.ndarray
 ) -> None:
     """Test for rotation_control_vector() function."""
-    np.testing.assert_array_equal(
-        rotation_control_vector(array_length, start_idx, end_idx), expected
-    )
+
+    result = rotation_control_vector(array_length, start_idx, end_idx)
+    assert result == pytest.approx(expected)
 
 
 @pytest.mark.parametrize(
diff --git a/tests/data/test_utils.py b/tests/data/test_utils.py
index ed8eeea83..78bffb82c 100644
--- a/tests/data/test_utils.py
+++ b/tests/data/test_utils.py
@@ -46,7 +46,9 @@ def test_better_ear_speechweighted_snr(
     """Test of better_ear_speechweighted_snr()."""
     better_ear_signal_noise_ratio = better_ear_speechweighted_snr(target, noise)
     assert isinstance(better_ear_signal_noise_ratio, float)
-    assert better_ear_signal_noise_ratio == expected
+    assert better_ear_signal_noise_ratio == pytest.approx(
+        expected, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_better_ear_speechweighted_snr_noise_wrong_shape_index_error() -> None:
@@ -79,7 +81,9 @@ def test_speechweighted_snr(target: np.array, noise: np.array, expected: float)
     """Test of speechweighted_snr()."""
     signal_noise_ratio = speechweighted_snr(target, noise)
     assert isinstance(signal_noise_ratio, float)
-    assert signal_noise_ratio == expected
+    assert signal_noise_ratio == pytest.approx(
+        expected, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_speechweighted_snr_unequal_arrays_value_error() -> None:
diff --git a/tests/enhancer/test_nalr.py b/tests/enhancer/test_nalr.py
index 8c2e3ffff..4da3099bc 100644
--- a/tests/enhancer/test_nalr.py
+++ b/tests/enhancer/test_nalr.py
@@ -25,4 +25,6 @@
 def test_nalr(audiogram: np.ndarray, cfs: np.ndarray, expected: float) -> None:
     enhancer = NALR(**cfg_nalr)
     nalr_fir, _ = enhancer.build(audiogram, cfs)
-    assert nalr_fir[0] == expected
+    assert nalr_fir[0] == pytest.approx(
+        expected, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
diff --git a/tests/evaluator/haaqi/test_haaqi.py b/tests/evaluator/haaqi/test_haaqi.py
index 2a58dffca..323eedd0e 100644
--- a/tests/evaluator/haaqi/test_haaqi.py
+++ b/tests/evaluator/haaqi/test_haaqi.py
@@ -19,7 +19,9 @@ def test_haaqi_v1() -> None:
     score, _, _, _ = haaqi_v1(
         x, sample_rate, y, sample_rate, hearing_loss, equalisation_mode, level1
     )
-    assert score == pytest.approx(0.111290948, rel=1e-7)
+    assert score == pytest.approx(
+        0.111290948, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_compute_haaqi():
@@ -43,4 +45,6 @@ def test_compute_haaqi():
     )
 
     # Check that the score is a float between 0 and 1
-    assert score == pytest.approx(0.113759275, rel=1e-7)
+    assert score == pytest.approx(
+        0.113759275, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
diff --git a/tests/evaluator/haspi/test_eb.py b/tests/evaluator/haspi/test_eb.py
index e8b114b0d..389a23d60 100644
--- a/tests/evaluator/haspi/test_eb.py
+++ b/tests/evaluator/haspi/test_eb.py
@@ -64,12 +64,24 @@ def test_ear_model():
 
     # check values
     assert freq_sample == 24000
-    assert np.sum(np.abs(ref_db)) == pytest.approx(102596.63767028379)
-    assert np.sum(np.abs(proc_db)) == pytest.approx(4145.3884196835625)
-    assert np.sum(np.abs(ref_bm)) == pytest.approx(65517.72934742906)
-    assert np.sum(np.abs(proc_bm)) == pytest.approx(2366.401656815131)
-    assert np.sum(np.abs(ref_sl)) == pytest.approx(291.3527365691821)
-    assert np.sum(np.abs(proc_sl)) == pytest.approx(13.655317152968216)
+    assert np.sum(np.abs(ref_db)) == pytest.approx(
+        102596.63767028379, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.sum(np.abs(proc_db)) == pytest.approx(
+        4145.3884196835625, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.sum(np.abs(ref_bm)) == pytest.approx(
+        65517.72934742906, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.sum(np.abs(proc_bm)) == pytest.approx(
+        2366.401656815131, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.sum(np.abs(ref_sl)) == pytest.approx(
+        291.3527365691821, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.sum(np.abs(proc_sl)) == pytest.approx(
+        13.655317152968216, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_center_frequency():
@@ -84,7 +96,9 @@ def test_center_frequency():
         min_bw=24.7,
     )
     assert center_freq.shape == (10,)
-    assert np.sum(center_freq) == pytest.approx(23935.19626226296)
+    assert np.sum(center_freq) == pytest.approx(
+        23935.19626226296, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_loss_parameters():
@@ -109,11 +123,21 @@ def test_loss_parameters():
     assert compression_ratio.shape == (6,)
     assert annenuated_ihc.shape == (6,)
     # check values
-    assert np.sum(attenuated_ohc) == pytest.approx(220.39149328167292)
-    assert np.sum(bandwith) == pytest.approx(15.041134665207498)
-    assert np.sum(low_knee) == pytest.approx(400.3914932816729)
-    assert np.sum(compression_ratio) == pytest.approx(6.0)
-    assert np.sum(annenuated_ihc) == pytest.approx(129.6085067183270)
+    assert np.sum(attenuated_ohc) == pytest.approx(
+        220.39149328167292, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.sum(bandwith) == pytest.approx(
+        15.041134665207498, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.sum(low_knee) == pytest.approx(
+        400.3914932816729, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.sum(compression_ratio) == pytest.approx(
+        6.0, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.sum(annenuated_ihc) == pytest.approx(
+        129.6085067183270, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_resample():
@@ -129,7 +153,9 @@ def test_resample():
     # check shapes
     assert len(ref_signal_24) == len(reference_signal) * 24000 / reference_freq
     # check values
-    assert np.sum(np.abs(ref_signal_24)) == pytest.approx(604.1522707137393)
+    assert np.sum(np.abs(ref_signal_24)) == pytest.approx(
+        604.1522707137393, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
     assert freq_sample_hz == 24000
 
 
@@ -146,8 +172,12 @@ def test_input_align():
 
     assert ref.shape == (600,)
     assert proc.shape == (600,)
-    assert np.sum(np.abs(ref)) == pytest.approx(29892.167176853407)
-    assert np.sum(np.abs(proc)) == pytest.approx(27199.009291096496)
+    assert np.sum(np.abs(ref)) == pytest.approx(
+        29892.167176853407, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.sum(np.abs(proc)) == pytest.approx(
+        27199.009291096496, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_middle_ear():
@@ -158,7 +188,9 @@ def test_middle_ear():
     filtered_signal = middle_ear(reference_signal, 24000)
 
     assert filtered_signal.shape == (600,)
-    assert np.sum(np.abs(filtered_signal)) == pytest.approx(9241.220369749171)
+    assert np.sum(np.abs(filtered_signal)) == pytest.approx(
+        9241.220369749171, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_gammatone_basilar_membrane():
@@ -190,10 +222,18 @@ def test_gammatone_basilar_membrane():
     assert processed_envelope.shape == (600,)
     assert processed_basilar_membrane.shape == (600,)
     # check values
-    assert np.sum(np.abs(reference_envelope)) == pytest.approx(3605.427313705984)
-    assert np.sum(np.abs(reference_basilar_membrane)) == pytest.approx(2288.3557465)
-    assert np.sum(np.abs(processed_envelope)) == pytest.approx(4426.111706599469)
-    assert np.sum(np.abs(processed_basilar_membrane)) == pytest.approx(2804.93743475)
+    assert np.sum(np.abs(reference_envelope)) == pytest.approx(
+        3605.427313705984, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.sum(np.abs(reference_basilar_membrane)) == pytest.approx(
+        2288.3557465, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.sum(np.abs(processed_envelope)) == pytest.approx(
+        4426.111706599469, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.sum(np.abs(processed_basilar_membrane)) == pytest.approx(
+        2804.93743475, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_gammatone_bandwidth_demodulation():
@@ -207,8 +247,12 @@ def test_gammatone_bandwidth_demodulation():
     )
     assert centre_freq_sin.shape == (100,)
     assert centre_freq_cos.shape == (100,)
-    assert np.sum(centre_freq_sin) == pytest.approx(-0.3791946274493412)
-    assert np.sum(centre_freq_cos) == pytest.approx(-0.39460748051808026)
+    assert np.sum(centre_freq_sin) == pytest.approx(
+        -0.3791946274493412, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.sum(centre_freq_cos) == pytest.approx(
+        -0.39460748051808026, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 @pytest.mark.parametrize(
@@ -227,7 +271,9 @@ def test_bandwidth_adjust(scale, bw_min, bw_max, expected):
         bandwidth_max=bw_max,
         level1=1,
     )
-    assert bw_adjusted == pytest.approx(expected)
+    assert bw_adjusted == pytest.approx(
+        expected, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_env_compress_basilar_membrane():
@@ -253,9 +299,11 @@ def test_env_compress_basilar_membrane():
     assert compressed_signal.shape == (600,)
     assert compressed_basilar_membrane.shape == (600,)
     # check values
-    assert np.mean(np.abs(compressed_signal)) == pytest.approx(15486012153068.807)
+    assert np.mean(np.abs(compressed_signal)) == pytest.approx(
+        15486012153068.807, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
     assert np.mean(np.abs(compressed_basilar_membrane)) == pytest.approx(
-        15415471156.59357
+        15415471156.59357, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
     )
 
 
@@ -280,9 +328,13 @@ def test_envelope_align():
 
     # check shapes and values
     assert aligned_output.shape == (600,)
-    assert np.sum(np.abs(aligned_output)) == pytest.approx(299.1891022020615)
+    assert np.sum(np.abs(aligned_output)) == pytest.approx(
+        299.1891022020615, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
     # Check output is now aligned with the reference
-    assert aligned_output[100] == pytest.approx(scale * reference[100])
+    assert aligned_output[100] == pytest.approx(
+        scale * reference[100], rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 # envelope_sl
@@ -304,8 +356,12 @@ def test_envelope_sl():
 
     assert reference.shape == (600,)
     assert basilar_membrane.shape == (600,)
-    assert np.sum(np.abs(reference)) == pytest.approx(42746.12859151134)
-    assert np.sum(np.abs(basilar_membrane)) == pytest.approx(98.97646233693762)
+    assert np.sum(np.abs(reference)) == pytest.approx(
+        42746.12859151134, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.sum(np.abs(basilar_membrane)) == pytest.approx(
+        98.97646233693762, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 # inner_hair_cell_adaptation
@@ -324,8 +380,12 @@ def test_inner_hair_cell_adaptation():
     # check shapes and values
     assert output_db.shape == (600,)
     assert output_basilar_membrane.shape == (600,)
-    assert np.sum(np.abs(output_db)) == pytest.approx(298.9359292744365)
-    assert np.sum(np.abs(output_basilar_membrane)) == pytest.approx(0.2963082865723811)
+    assert np.sum(np.abs(output_db)) == pytest.approx(
+        298.9359292744365, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.sum(np.abs(output_basilar_membrane)) == pytest.approx(
+        0.2963082865723811, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 # basilar_membrane_add_noise
@@ -342,7 +402,9 @@ def test_basilar_membrane_add_noise():
 
     # check shapes and values and that signal has changed
     assert noisy_reference.shape == (600,)
-    assert np.sum(np.abs(noisy_reference)) == pytest.approx(298.919051930547)
+    assert np.sum(np.abs(noisy_reference)) == pytest.approx(
+        298.919051930547, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
     assert not np.allclose(noisy_reference, ref)
 
     # Check that adding on nearly 0 noise (-100 db) doesn't change the signal
@@ -371,7 +433,9 @@ def test_group_delay_compensate():
 
     # check shapes and values
     assert processed.shape == (4, 600)
-    assert np.sum(np.abs(processed)) == pytest.approx(1193.8088344682358)
+    assert np.sum(np.abs(processed)) == pytest.approx(
+        1193.8088344682358, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_convert_rms_to_sl():
@@ -396,7 +460,9 @@ def test_convert_rms_to_sl():
 
     # check shapes and values
     assert ref_db.shape == (600,)
-    assert np.sum(np.abs(ref_db)) == pytest.approx(34746.74406262155)
+    assert np.sum(np.abs(ref_db)) == pytest.approx(
+        34746.74406262155, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_env_smooth():
@@ -415,7 +481,9 @@ def test_env_smooth():
     # check shapes and values
     expected_length = 2 * sig_len / (sample_freq * segment_size / 1000)
     assert smooth.shape == (4, expected_length)
-    assert np.sum(np.abs(smooth)) == pytest.approx(100.7397658862719)
+    assert np.sum(np.abs(smooth)) == pytest.approx(
+        100.7397658862719, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_mel_cepstrum_correlation():
@@ -449,11 +517,15 @@ def test_mel_cepstrum_correlation():
 
     # check shapes and values
     assert np.array(individual_cepstral_correlation).shape == (6,)
-    assert ave_cepstral_correlation == pytest.approx(0.04195483905166838)
+    assert ave_cepstral_correlation == pytest.approx(
+        0.04195483905166838, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
     assert individual_cepstral_correlation == pytest.approx(
         np.array(
             [0.04582154, 0.04306849, 0.02364514, 0.07634693, 0.02364514, 0.04306849]
-        )
+        ),
+        rel=pytest.rel_tolerance,
+        abs=pytest.abs_tolerance,
     )
 
 
@@ -477,9 +549,15 @@ def test_melcor9():
 
     assert mel_cep_mod.shape == (8,)
 
-    assert mel_cep_ave == pytest.approx(0.07847183114015181)
-    assert mel_cep_low == pytest.approx(0.08244466051783299)
-    assert mel_cep_high == pytest.approx(0.07449900176247062)
+    assert mel_cep_ave == pytest.approx(
+        0.07847183114015181, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert mel_cep_low == pytest.approx(
+        0.08244466051783299, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert mel_cep_high == pytest.approx(
+        0.07449900176247062, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
     assert mel_cep_mod == pytest.approx(
         np.array(
             [
@@ -492,7 +570,9 @@ def test_melcor9():
                 0.06127608591547042,
                 0.009358303500341338,
             ]
-        )
+        ),
+        rel=pytest.rel_tolerance,
+        abs=pytest.abs_tolerance,
     )
 
 
@@ -514,11 +594,19 @@ def test_melcor9_equal_input():
 
     assert mel_cep_mod.shape == (8,)
 
-    assert mel_cep_ave == pytest.approx(1.0)
-    assert mel_cep_low == pytest.approx(1.0)
-    assert mel_cep_high == pytest.approx(1.0)
+    assert mel_cep_ave == pytest.approx(
+        1.0, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert mel_cep_low == pytest.approx(
+        1.0, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert mel_cep_high == pytest.approx(
+        1.0, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
     assert mel_cep_mod == pytest.approx(
-        np.array([1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0])
+        np.array([1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]),
+        rel=pytest.rel_tolerance,
+        abs=pytest.abs_tolerance,
     )
 
 
@@ -544,7 +632,9 @@ def test_melcor9_crosscovmatrix():
     )
 
     assert cross_cov_matrix.shape == (n_modulations, n_basis)
-    assert np.mean(cross_cov_matrix) == pytest.approx(0.9981200876213588)
+    assert np.mean(cross_cov_matrix) == pytest.approx(
+        0.9981200876213588, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_spectrum_diff():
@@ -564,13 +654,19 @@ def test_spectrum_diff():
 
     # Check values
     assert dloud == pytest.approx(
-        np.array([0.037373053253378426, 7.671407491067586e-05, 4.762048775653672e-05])
+        np.array([0.037373053253378426, 7.671407491067586e-05, 4.762048775653672e-05]),
+        rel=pytest.rel_tolerance,
+        abs=pytest.abs_tolerance,
     )
     assert dnorm == pytest.approx(
-        np.array([44.84779838405483, 0.09203844008264382, 0.0570982338215042])
+        np.array([44.84779838405483, 0.09203844008264382, 0.0570982338215042]),
+        rel=pytest.rel_tolerance,
+        abs=pytest.abs_tolerance,
     )
     assert dslope == pytest.approx(
-        np.array([0.03918063966807504, 0.00010878329523853699, 8.006096597686129e-05])
+        np.array([0.03918063966807504, 0.00010878329523853699, 8.006096597686129e-05]),
+        rel=pytest.rel_tolerance,
+        abs=pytest.abs_tolerance,
     )
 
 
@@ -597,9 +693,15 @@ def test_bm_covary_ok():
     assert proc_mean_square.shape == (4, 12)
 
     # Check values
-    assert np.sum(np.abs(signal_cross_cov)) == pytest.approx(46.43935095481214)
-    assert np.sum(np.abs(ref_mean_square)) == pytest.approx(16.65809872708418)
-    assert np.sum(np.abs(proc_mean_square)) == pytest.approx(26.110255782462076)
+    assert np.sum(np.abs(signal_cross_cov)) == pytest.approx(
+        46.43935095481214, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.sum(np.abs(ref_mean_square)) == pytest.approx(
+        16.65809872708418, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.sum(np.abs(proc_mean_square)) == pytest.approx(
+        26.110255782462076, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_bm_covary_error():
@@ -639,8 +741,12 @@ def test_ave_covary2():
 
     assert len(ihc_sync_covariance) == 6
 
-    assert ave_covariance == pytest.approx(0.5129961720524688)
-    assert np.sum(ihc_sync_covariance) == pytest.approx(3.057984614887033)
+    assert ave_covariance == pytest.approx(
+        0.5129961720524688, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.sum(ihc_sync_covariance) == pytest.approx(
+        3.057984614887033, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
     assert ihc_sync_covariance == pytest.approx(
         [
@@ -650,5 +756,7 @@ def test_ave_covary2():
             0.511743935480639,
             0.5124431322756856,
             0.5126029271248499,
-        ]
+        ],
+        rel=pytest.rel_tolerance,
+        abs=pytest.abs_tolerance,
     )
diff --git a/tests/evaluator/haspi/test_ebm.py b/tests/evaluator/haspi/test_ebm.py
index 92c2e78b2..3e62c9b62 100644
--- a/tests/evaluator/haspi/test_ebm.py
+++ b/tests/evaluator/haspi/test_ebm.py
@@ -20,8 +20,12 @@ def test_env_filter_ok() -> None:
         freq_sub_sample=4000,
         freq_samp=16000,
     )
-    assert reference_env == pytest.approx(48.84481105)
-    assert processed_env == pytest.approx(32.32800458)
+    assert reference_env == pytest.approx(
+        48.84481105, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert processed_env == pytest.approx(
+        32.32800458, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 @pytest.mark.parametrize(
@@ -84,8 +88,12 @@ def test_cepstral_correlation_coef_ok() -> None:
     )
     assert ref_cep.shape == (72, n_basis)
     assert proc_cep.shape == (72, n_basis)
-    assert np.mean(np.abs(ref_cep)) == pytest.approx(82.4395877477838)
-    assert np.mean(np.abs(proc_cep)) == pytest.approx(110.74820232861624)
+    assert np.mean(np.abs(ref_cep)) == pytest.approx(
+        82.4395877477838, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.mean(np.abs(proc_cep)) == pytest.approx(
+        110.74820232861624, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_cepstral_correlation_coef_error() -> None:
@@ -141,8 +149,12 @@ def test_fir_modulation_filter() -> None:
     assert out_center_freqs.shape == in_center_freqs.shape
     # Check return value have expected values
     assert np.allclose(in_center_freqs, out_center_freqs)
-    assert np.mean(np.abs(ref_mod)) == pytest.approx(14.477262943878237)
-    assert np.mean(np.abs(proc_mod)) == pytest.approx(13.85669526424176)
+    assert np.mean(np.abs(ref_mod)) == pytest.approx(
+        14.477262943878237, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.mean(np.abs(proc_mod)) == pytest.approx(
+        13.85669526424176, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_modulation_cross_correlation() -> None:
@@ -168,4 +180,6 @@ def test_modulation_cross_correlation() -> None:
         reference_modulation=ref_mod, processed_modulation=proc_mod
     )
     assert cross_corr.shape == (n_mod_filters,)
-    assert np.sum(cross_corr) == pytest.approx(0.4414165879317118)
+    assert np.sum(cross_corr) == pytest.approx(
+        0.4414165879317118, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
diff --git a/tests/evaluator/haspi/test_haspi.py b/tests/evaluator/haspi/test_haspi.py
index 5e8dce1fd..60c12b4a1 100644
--- a/tests/evaluator/haspi/test_haspi.py
+++ b/tests/evaluator/haspi/test_haspi.py
@@ -16,7 +16,9 @@ def test_haspi_v2() -> None:
     level1 = 65
 
     score, _ = haspi_v2(x, sample_rate, y + x, sample_rate, hearing_loss, level1)
-    assert score == pytest.approx(0.043808448934532965, rel=1e-7)
+    assert score == pytest.approx(
+        0.043808448934532965, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_haspi_v2_better_ear() -> None:
@@ -45,4 +47,6 @@ def test_haspi_v2_better_ear() -> None:
         level=100,
     )
 
-    assert score == pytest.approx(0.839975335323691)
+    assert score == pytest.approx(
+        0.839975335323691, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
diff --git a/tests/evaluator/haspi/test_ip.py b/tests/evaluator/haspi/test_ip.py
index e13885e8e..2217521c9 100644
--- a/tests/evaluator/haspi/test_ip.py
+++ b/tests/evaluator/haspi/test_ip.py
@@ -43,8 +43,12 @@ def test_get_neural_net() -> None:
     assert len(weights_out) == len(weights_hidden)
     # Check the specific values of the weights are correct
     assert normalization_factor == 0.9508
-    assert np.sum(weights_out) == pytest.approx(-33.8515)
-    assert np.sum(weights_hidden) == pytest.approx(-670.8200)
+    assert np.sum(weights_out) == pytest.approx(
+        -33.8515, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.sum(weights_hidden) == pytest.approx(
+        -670.82009999, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_nn_feed_forward_ensemble() -> None:
@@ -63,7 +67,9 @@ def test_nn_feed_forward_ensemble() -> None:
 
     # Check that the output is correct
     x = nn_feed_forward_ensemble(data, neural_net_params, weights_hidden, weights_out)
-    assert x == pytest.approx(0.95078679)
+    assert x == pytest.approx(
+        0.95078679, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_nn_feed_forward() -> None:
@@ -90,5 +96,9 @@ def test_nn_feed_forward() -> None:
     assert hidden[0] == pytest.approx(1.0)
     assert output[0] == pytest.approx(1.0)
     # Check that the mean of the hidden and output layers are correct
-    assert np.mean(hidden) == pytest.approx(0.599999906437356)
-    assert np.mean(output) == pytest.approx(0.982229820658455)
+    assert np.mean(hidden) == pytest.approx(
+        0.599999906437356, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.mean(output) == pytest.approx(
+        0.982229820658455, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
diff --git a/tests/evaluator/hasqi/test_hasqi.py b/tests/evaluator/hasqi/test_hasqi.py
index 6d5eb7ec2..2a29e799d 100644
--- a/tests/evaluator/hasqi/test_hasqi.py
+++ b/tests/evaluator/hasqi/test_hasqi.py
@@ -19,7 +19,9 @@ def test_hasqi_v2() -> None:
     score, _, _, _ = hasqi_v2(
         x, sample_rate, y, sample_rate, hearing_loss, equalisation_mode, level1
     )
-    assert score == pytest.approx(0.002525809, rel=1e-7)
+    assert score == pytest.approx(
+        0.002525809, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_hasqi_v2_better_ear() -> None:
@@ -49,4 +51,6 @@ def test_hasqi_v2_better_ear() -> None:
         audiogram_freq=None,
     )
 
-    assert score == pytest.approx(0.1256893032667640)
+    assert score == pytest.approx(
+        0.1256893032667640, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
diff --git a/tests/evaluator/mbstoi/test_mbstoi.py b/tests/evaluator/mbstoi/test_mbstoi.py
index 6a2c7fcf1..7b1e08a60 100644
--- a/tests/evaluator/mbstoi/test_mbstoi.py
+++ b/tests/evaluator/mbstoi/test_mbstoi.py
@@ -33,4 +33,6 @@ def test_mbstoi() -> None:
         dyn_range=60,
     )
 
-    assert mbstoi_val == pytest.approx(0.9061193314307591)
+    assert mbstoi_val == pytest.approx(
+        0.9061193314307591, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
diff --git a/tests/evaluator/mbstoi/test_mbstoi_utils.py b/tests/evaluator/mbstoi/test_mbstoi_utils.py
index daf782fbd..99ec01c78 100644
--- a/tests/evaluator/mbstoi/test_mbstoi_utils.py
+++ b/tests/evaluator/mbstoi/test_mbstoi_utils.py
@@ -63,8 +63,12 @@ def test_equalisation_cancellation() -> None:
     assert p_ec_m.shape == (n_gamma_bands, n_gammas)
 
     # check values
-    assert np.sum(intermediate_grid) == pytest.approx(277.84700307853785)
-    assert np.sum(p_ec_m) == pytest.approx(73.14293826967146)
+    assert np.sum(intermediate_grid) == pytest.approx(
+        277.84700307853785, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.sum(p_ec_m) == pytest.approx(
+        73.14293826967146, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_firstpartfunc() -> None:
@@ -83,7 +87,9 @@ def test_firstpartfunc() -> None:
         epsexp=np.random.random(size=(1, n_gammas)),
     )
     assert result.shape == (n_taus, n_gammas)
-    assert np.sum(result) == pytest.approx(405590.0256254587)
+    assert np.sum(result) == pytest.approx(
+        405590.0256254587, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_secondpartfunc() -> None:
@@ -103,7 +109,9 @@ def test_secondpartfunc() -> None:
         gammas=np.random.random(size=(1, n_gammas)),
     )
     assert result.shape == (n_taus, n_gammas)
-    assert np.sum(result) == pytest.approx(127394.91897484378)
+    assert np.sum(result) == pytest.approx(
+        127394.91897484378, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_thirdpartfunc() -> None:
@@ -123,7 +131,9 @@ def test_thirdpartfunc() -> None:
         gammas=np.random.random(size=(1, n_gammas)),
     )
     assert result.shape == (n_taus, n_gammas)
-    assert np.sum(result) == pytest.approx(9483.482148264484)
+    assert np.sum(result) == pytest.approx(
+        9483.482148264484, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_fourthpartfunc() -> None:
@@ -141,7 +151,9 @@ def test_fourthpartfunc() -> None:
         deltexp=np.random.random(size=(1, n_taus)),
     )
     assert result.shape == (n_taus, n_gammas)
-    assert np.sum(result) == pytest.approx(75883.96658627654)
+    assert np.sum(result) == pytest.approx(
+        75883.96658627654, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_stft() -> None:
@@ -153,7 +165,9 @@ def test_stft() -> None:
     stft_out = stft(signal, win_size=128, fft_size=fft_size)
 
     assert stft_out.shape == (8, fft_size)
-    assert np.sum(stft_out) == pytest.approx(0.07977179832430759)
+    assert np.sum(stft_out) == pytest.approx(
+        0.07977179832430759, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_remove_silent_frames() -> None:
@@ -178,11 +192,21 @@ def test_remove_silent_frames() -> None:
         hop=32,
     )
 
-    assert np.sum(xl_sil[0:300]) == pytest.approx(0.0)
-    assert np.sum(xl_sil) == pytest.approx(128.15949932062742)
-    assert np.sum(xr_sil) == pytest.approx(272.06229083012374)
-    assert np.sum(yl_sil) == pytest.approx(413.65823132789836)
-    assert np.sum(yr_sil) == pytest.approx(551.1857227061379)
+    assert np.sum(xl_sil[0:300]) == pytest.approx(
+        0.0, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.sum(xl_sil) == pytest.approx(
+        128.15949932062742, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.sum(xr_sil) == pytest.approx(
+        272.06229083012374, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.sum(yl_sil) == pytest.approx(
+        413.65823132789836, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert np.sum(yr_sil) == pytest.approx(
+        551.1857227061379, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_thirdoct() -> None:
@@ -195,15 +219,25 @@ def test_thirdoct() -> None:
         freq_high,
     ) = thirdoct(frequency_sampling=8000, nfft=512, num_bands=3, min_freq=100)
 
-    assert freq_low == pytest.approx([93.75, 109.375, 140.625])
-    assert freq_high == pytest.approx([109.375, 140.625, 171.875])
+    assert freq_low == pytest.approx(
+        [93.75, 109.375, 140.625], rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
+    assert freq_high == pytest.approx(
+        [109.375, 140.625, 171.875], rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
     assert centre_frequencies == pytest.approx(
-        np.array([[100.0, 125.99210499, 158.7401052]])
+        np.array([[100.0, 125.99210499, 158.7401052]]),
+        rel=pytest.rel_tolerance,
+        abs=pytest.abs_tolerance,
     )
     assert octave_band_matrix.shape == (3, 257)
-    assert np.sum(octave_band_matrix) == pytest.approx(5.0)
+    assert np.sum(octave_band_matrix) == pytest.approx(
+        5.0, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
     assert frequency_band_edges_indices == pytest.approx(
-        np.array([[7, 7], [8, 9], [10, 11]])
+        np.array([[7, 7], [8, 9], [10, 11]]),
+        rel=pytest.rel_tolerance,
+        abs=pytest.abs_tolerance,
     )
 
 
diff --git a/tests/evaluator/msbg/test_smearing.py b/tests/evaluator/msbg/test_smearing.py
index 4f20380ab..490d4d2ff 100644
--- a/tests/evaluator/msbg/test_smearing.py
+++ b/tests/evaluator/msbg/test_smearing.py
@@ -35,7 +35,9 @@ def test_smear_valid_input():
     input_signal = np.random.rand(10000)
     output_signal = Smearer(r_lower, r_upper, sample_freq).smear(input_signal)
     assert output_signal.shape == (10240,)
-    assert np.sum(np.abs(output_signal)) == pytest.approx(5066.986397433977)
+    assert np.sum(np.abs(output_signal)) == pytest.approx(
+        5066.986397433977, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 def test_make_smear_mat3_valid_input():
diff --git a/tests/recipes/cad1/task1/test_enhance.py b/tests/recipes/cad1/task1/test_enhance_task1.py
similarity index 100%
rename from tests/recipes/cad1/task1/test_enhance.py
rename to tests/recipes/cad1/task1/test_enhance_task1.py
diff --git a/tests/recipes/cad1/task1/test_evaluate.py b/tests/recipes/cad1/task1/test_evaluate.py
index d0bf9b48e..422a4ad04 100644
--- a/tests/recipes/cad1/task1/test_evaluate.py
+++ b/tests/recipes/cad1/task1/test_evaluate.py
@@ -169,7 +169,9 @@ def test_evaluate_song_listener(
     # Check the outputs
     # Combined score
     assert isinstance(combined_score, float)
-    assert combined_score == pytest.approx(0.144920571533222, rel=1e-7)
+    assert combined_score == pytest.approx(
+        0.144920571533222, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
     # Per instrument score
     assert isinstance(per_instrument_score, dict)
@@ -177,5 +179,7 @@ def test_evaluate_song_listener(
         assert instrument in per_instrument_score
         assert isinstance(per_instrument_score[instrument], float)
         assert per_instrument_score[instrument] == pytest.approx(
-            expected_results[instrument], rel=1e-7
+            expected_results[instrument],
+            rel=pytest.rel_tolerance,
+            abs=pytest.abs_tolerance,
         )
diff --git a/tests/recipes/cad1/task2/test_audio_manager.py b/tests/recipes/cad1/task2/test_audio_manager.py
index e78b4aa0f..05e7537d6 100644
--- a/tests/recipes/cad1/task2/test_audio_manager.py
+++ b/tests/recipes/cad1/task2/test_audio_manager.py
@@ -1,9 +1,9 @@
 """Test for AudioManager module"""
 # pylint: disable=import-error
-
 from pathlib import Path
 
 import numpy as np
+import pytest
 from scipy.io import wavfile
 
 from recipes.cad1.task2.baseline.audio_manager import AudioManager
@@ -30,4 +30,6 @@ def test_save_audios(tmp_path):
 
     # Check if audio data was saved correctly
     sample_rate, _ = wavfile.read(audio_file)
-    assert sample_rate == audio_manager.sample_rate
+    assert sample_rate == pytest.approx(
+        audio_manager.sample_rate, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
diff --git a/tests/recipes/cad1/task2/test_enhance.py b/tests/recipes/cad1/task2/test_enhance_task2.py
similarity index 100%
rename from tests/recipes/cad1/task2/test_enhance.py
rename to tests/recipes/cad1/task2/test_enhance_task2.py
diff --git a/tests/recipes/cpc2/__init__.py b/tests/recipes/cpc2/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/tests/recipes/cpc2/test_evaluate.py b/tests/recipes/cpc2/test_evaluate_cpc2.py
similarity index 88%
rename from tests/recipes/cpc2/test_evaluate.py
rename to tests/recipes/cpc2/test_evaluate_cpc2.py
index dc31d2765..cb6414ec2 100644
--- a/tests/recipes/cpc2/test_evaluate.py
+++ b/tests/recipes/cpc2/test_evaluate_cpc2.py
@@ -19,7 +19,9 @@
 )
 def test_rmse_score_ok(x, y, expected):
     """Test the function rmse_score valid inputs"""
-    assert rmse_score(np.array(x), np.array(y)) == pytest.approx(expected)
+    assert rmse_score(np.array(x), np.array(y)) == pytest.approx(
+        expected, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 @pytest.mark.parametrize(
@@ -38,7 +40,9 @@ def test_rmse_score_error(x, y, expected):
 )
 def test_ncc_score_ok(x, y, expected):
     """Test the function ncc_score valid inputs"""
-    assert ncc_score(np.array(x), np.array(y)) == pytest.approx(expected)
+    assert ncc_score(np.array(x), np.array(y)) == pytest.approx(
+        expected, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 @pytest.mark.parametrize(
@@ -56,7 +60,9 @@ def test_ncc_score_error(x, y, expected):
 )
 def test_kt_score_ok(x, y, expected):
     """Test the function kt_score valid inputs"""
-    assert kt_score(np.array(x), np.array(y)) == pytest.approx(expected)
+    assert kt_score(np.array(x), np.array(y)) == pytest.approx(
+        expected, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 @pytest.mark.parametrize(
@@ -82,7 +88,9 @@ def test_kt_score_error(x, y, expected):
 )
 def test_std_err_ok(x, y, expected):
     """Test the function std_err valid inputs"""
-    assert std_err(np.array(x), np.array(y)) == pytest.approx(expected)
+    assert std_err(np.array(x), np.array(y)) == pytest.approx(
+        expected, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
 
 
 @pytest.mark.parametrize(
diff --git a/tests/recipes/cpc2/test_predict.py b/tests/recipes/cpc2/test_predict.py
index a98ba9756..9c840e72c 100644
--- a/tests/recipes/cpc2/test_predict.py
+++ b/tests/recipes/cpc2/test_predict.py
@@ -24,7 +24,7 @@ def test_logistic_model_symmetry(model: LogisticModel, value):
     """Test the LogisticModel is symmetric."""
     symmetric_value = 4 - value
     assert model.predict(value) + model.predict(symmetric_value) == pytest.approx(
-        100.0, abs=1e-3
+        100.0, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
     )
 
 
@@ -38,9 +38,13 @@ def test_logistic_model_extremes(model, value):
     """Test the LogisticModel class ."""
     # logistic_model must asymptote to 0 and 100 for extreme values
     if value > 10:
-        assert model.predict(value) == pytest.approx(100)
+        assert model.predict(value) == pytest.approx(
+            100, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+        )
     elif value < -10:
-        assert model.predict(value) == pytest.approx(0)
+        assert model.predict(value) == pytest.approx(
+            0, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+        )
 
 
 @pytest.mark.parametrize(
@@ -68,4 +72,6 @@ def test_make_disjoint_train_set_empty(data_1, data_2, expected):
     test_df1 = pd.DataFrame([data_1])
     test_df2 = pd.DataFrame([data_2])
     disjoint = make_disjoint_train_set(test_df1, test_df2)
-    assert disjoint.shape[0] == expected
+    assert disjoint.shape[0] == pytest.approx(
+        expected, rel=pytest.rel_tolerance, abs=pytest.abs_tolerance
+    )
diff --git a/tests/regression/test_data_HOA_tools_cec2.py b/tests/regression/test_data_HOA_tools_cec2.py
index c9a90f56d..57ff9c2bf 100644
--- a/tests/regression/test_data_HOA_tools_cec2.py
+++ b/tests/regression/test_data_HOA_tools_cec2.py
@@ -1,5 +1,5 @@
 import numpy as np
-from numba.typed import List  # pylint: disable=no-name-in-module
+from numba.typed import List as TypedList  # pylint: disable=no-name-in-module
 from scipy.spatial.transform import Rotation as R
 
 from clarity.data import HOA_tools_cec2 as hoa
@@ -24,7 +24,7 @@ def test_P(regtest):
     theta = 45
     order = 2
     foa = R.from_euler("y", theta, degrees=True).as_matrix()
-    rotmats = [foa, hoa.compute_rotation_matrix(order, foa)]
+    rotmats = TypedList([foa, hoa.compute_rotation_matrix(order, foa)])
     p = hoa.P.py_func(0, 1, 0, order, rotmats)
     regtest.write(f"P value {p:0.3f}\n")
 
@@ -35,7 +35,7 @@ def test_U(regtest):
     theta = 45
     order = 2
     foa = R.from_euler("y", theta, degrees=True).as_matrix()
-    rotmats = List([foa, hoa.compute_rotation_matrix(order, foa)])
+    rotmats = TypedList([foa, hoa.compute_rotation_matrix(order, foa)])
     u = hoa.U.py_func(m, n, order, rotmats)
     regtest.write(f"U value {u:0.3f}\n")
 
@@ -45,7 +45,7 @@ def test_V(regtest):
     theta = 45
     order = 2
     foa = R.from_euler("y", theta, degrees=True).as_matrix()
-    rotmats = List([foa, hoa.compute_rotation_matrix(order, foa)])
+    rotmats = TypedList([foa, hoa.compute_rotation_matrix(order, foa)])
     for i in range(3):
         v = hoa.V.py_func(i - 1, n, order, rotmats)
         regtest.write(f"V[{i}] value {v:0.3f}\n")
@@ -56,7 +56,7 @@ def test_W(regtest):
     theta = 45
     order = 2
     foa = R.from_euler("y", theta, degrees=True).as_matrix()
-    rotmats = List([foa, hoa.compute_rotation_matrix(order, foa)])
+    rotmats = TypedList([foa, hoa.compute_rotation_matrix(order, foa)])
     for i in range(3):
         w = hoa.W.py_func(i - 1, n, order, rotmats)
         regtest.write(f"W[{i}] value {w:0.3f}\n")
@@ -90,6 +90,6 @@ def test_compute_band_rotation(regtest):
     if n > 1:
         for i in np.arange(2, n + 1):
             rot_mat, typed_sub_matrices = hoa.compute_band_rotation.py_func(
-                i, typed_sub_matrices, rot_mat
+                i, TypedList(typed_sub_matrices), rot_mat
             )
     regtest.write(f"Band rotations {rot_mat}, {sub_matrices}\n")