From fe7406f87139f4b4316b916895d0f8b9dd6d3c46 Mon Sep 17 00:00:00 2001 From: GeorgWa Date: Fri, 12 Jul 2024 12:07:24 +0200 Subject: [PATCH 1/4] e2e bounds check --- .github/workflows/e2e_testing.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/e2e_testing.yml b/.github/workflows/e2e_testing.yml index f43f8ae4..f34ca61e 100644 --- a/.github/workflows/e2e_testing.yml +++ b/.github/workflows/e2e_testing.yml @@ -21,6 +21,9 @@ jobs: BRANCH_NAME: ${{ github.head_ref || github.ref_name }} NEPTUNE_API_TOKEN: ${{ secrets.NEPTUNE_E2E_TOKEN }} NEPTUNE_PROJECT_NAME: "MannLabs/alphaDIA-e2e-tests" + NUMBA_BOUNDSCHECK: 1 + NUMBA_DEVELOPER_MODE: 1 + NUMBA_FULL_TRACEBACKS: 1 steps: - uses: actions/checkout@v4 - name: Conda info From cf83e3d8d2242ced24bc27bc89a86282b2985947 Mon Sep 17 00:00:00 2001 From: GeorgWa Date: Sat, 13 Jul 2024 12:00:05 +0200 Subject: [PATCH 2/4] fixed out of bounds bug --- alphadia/features.py | 79 +++++++++++++++++++++++++++++-- alphadia/plexscoring.py | 3 ++ tests/unit_tests/conftest.py | 5 +- tests/unit_tests/test_features.py | 79 +++++++++++++++++++++++++++++++ 4 files changed, 161 insertions(+), 5 deletions(-) create mode 100644 tests/unit_tests/test_features.py diff --git a/alphadia/features.py b/alphadia/features.py index 14d2a60d..50e69a60 100644 --- a/alphadia/features.py +++ b/alphadia/features.py @@ -352,8 +352,19 @@ def center_envelope(x): ) * 0.5 -@nb.njit -def center_envelope_1d(x): +@nb.njit(inline="always") +def _odd_center_envelope(x: np.ndarray): + """ + Applies an interference correction envelope to a collection of odd-length 1D arrays. + Numba function which operates in place. + + Parameters + ---------- + x: np.ndarray + Array of shape (a, b) where a is the number of arrays and b is the length of each array. + It is mandatory that dimension b is odd. + + """ center_index = x.shape[1] // 2 for a0 in range(x.shape[0]): @@ -362,16 +373,76 @@ def center_envelope_1d(x): for i in range(1, center_index + 1): x[a0, center_index - i] = min(left_intensity, x[a0, center_index - i]) + left_intensity = ( - x[a0, center_index - i] + x[a0, center_index - i - 1] + x[a0, center_index - i] + x[a0, center_index - i + 1] ) * 0.5 x[a0, center_index + i] = min(right_intensity, x[a0, center_index + i]) right_intensity = ( - x[a0, center_index + i] + x[a0, center_index + i + 1] + x[a0, center_index + i] + x[a0, center_index + i - 1] ) * 0.5 +@nb.njit(inline="always") +def _even_center_envelope(x: np.ndarray): + """ + Applies an interference correction envelope to a collection of even-length 1D arrays. + Numba function which operates in place. + + Parameters + ---------- + x: np.ndarray + Array of shape (a, b) where a is the number of arrays and b is the length of each array. + It is mandatory that dimension b is even. + + """ + center_index_right = x.shape[1] // 2 + center_index_left = center_index_right - 1 + + for a0 in range(x.shape[0]): + left_intensity = x[a0, center_index_left] + right_intensity = x[a0, center_index_right] + + for i in range(1, center_index_left + 1): + x[a0, center_index_left - i] = min( + left_intensity, x[a0, center_index_left - i] + ) + + left_intensity = ( + x[a0, center_index_left - i] + x[a0, center_index_left - i + 1] + ) * 0.5 + + x[a0, center_index_right + i] = min( + right_intensity, x[a0, center_index_right + i] + ) + right_intensity = ( + x[a0, center_index_right + i] + x[a0, center_index_right + i - 1] + ) * 0.5 + + +@nb.njit +def center_envelope_1d(x: np.ndarray): + """ + Applies an interference correction envelope to a collection of 1D arrays. + Numba function which operates in place. + + Parameters + ---------- + x: np.ndarray + Array of shape (a, b) where a is the number of arrays and b is the length of each array. + It is mandatory that dimension b is odd. + + """ + + is_even = x.shape[1] % 2 == 0 + + if is_even: + _even_center_envelope(x) + else: + _odd_center_envelope(x) + + @nb.njit def weighted_mean_a1(array, weight_mask): """ diff --git a/alphadia/plexscoring.py b/alphadia/plexscoring.py index f4a10810..83d82dd9 100644 --- a/alphadia/plexscoring.py +++ b/alphadia/plexscoring.py @@ -1895,6 +1895,9 @@ def __call__( ) thread_count = 1 if debug else thread_count + if debug: + logger.info("Debug mode enabled. Processing only 10 elution groups") + alphatims.utils.set_threads(thread_count) _executor( range(iterator_len), diff --git a/tests/unit_tests/conftest.py b/tests/unit_tests/conftest.py index 17201d33..3fa6b592 100644 --- a/tests/unit_tests/conftest.py +++ b/tests/unit_tests/conftest.py @@ -188,7 +188,10 @@ def pytest_configure(config): ] pytest.test_data[raw_folder] = raw_files - # important to supress matplotlib output + # set numba environment variables + os.environ["NUMBA_BOUNDSCHECK"] = "1" + os.environ["NUMBA_DEVELOPER_MODE"] = "1" + os.environ["NUMBA_FULL_TRACEBACKS"] = "1" def random_tempfolder(): diff --git a/tests/unit_tests/test_features.py b/tests/unit_tests/test_features.py new file mode 100644 index 00000000..345e3e73 --- /dev/null +++ b/tests/unit_tests/test_features.py @@ -0,0 +1,79 @@ +import numpy as np +import pytest + +from alphadia.features import center_envelope_1d + + +@pytest.mark.parametrize( + "input_array, expected_output", + [ + ( + np.array([[1, 1, 1, 1, 1, 1, 1]], dtype=np.float64), + np.array( + [ + [1, 1, 1, 1, 1, 1, 1], + ], + dtype=np.float64, + ), + ), + ( + np.array([[100, 10, 1, 1, 1, 10, 100]], dtype=np.float64), + np.array([[1, 1, 1, 1, 1, 1, 1]], dtype=np.float64), + ), + ( + np.array([[100, 0, 0, 1, 0, 0, 100]], dtype=np.float64), + np.array([[0, 0, 0, 1, 0, 0, 0]], dtype=np.float64), + ), + ( + np.array([[1, 1, 1, 1, 1, 1, 1, 1]], dtype=np.float64), + np.array( + [ + [1, 1, 1, 1, 1, 1, 1, 1], + ], + dtype=np.float64, + ), + ), + ( + np.array([[100, 10, 1, 1, 1, 1, 10, 100]], dtype=np.float64), + np.array([[1, 1, 1, 1, 1, 1, 1, 1]], dtype=np.float64), + ), + ( + np.array([[100, 0, 0, 1, 1, 0, 0, 100]], dtype=np.float64), + np.array([[0, 0, 0, 1, 1, 0, 0, 0]], dtype=np.float64), + ), + ], +) +def test_center_envelope_1d_simple(input_array, expected_output): + # given + + # when + center_envelope_1d(input_array) + + # then + np.testing.assert_array_almost_equal(input_array, expected_output) + + +def test_center_envelope_1d_multiple_rows(): + # given + shape = (10, 11) + + input_array = np.random.rand(*shape) + output_array = input_array.copy() + # when + center_envelope_1d(input_array) + + # then + assert output_array.shape == input_array.shape + assert np.all(input_array[:, 0] <= output_array[:, 0]) + assert np.all(input_array[:, -1] <= output_array[:, -1]) + + +def test_center_envelope_1d_empty_array(): + # given + x = np.array([[]], dtype=np.float64) + + # when + center_envelope_1d(x) + + # then + assert x.shape == (1, 0) From 53009ac0913d5dd9981e8322e3e66ff349fb8a9f Mon Sep 17 00:00:00 2001 From: GeorgWa Date: Mon, 15 Jul 2024 10:45:58 +0200 Subject: [PATCH 3/4] more concise logic --- alphadia/plexscoring.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/alphadia/plexscoring.py b/alphadia/plexscoring.py index 83d82dd9..82187ce4 100644 --- a/alphadia/plexscoring.py +++ b/alphadia/plexscoring.py @@ -1889,14 +1889,12 @@ def __call__( n_candidates = score_group_container.get_candidate_count() psm_proto_df = OuptutPsmDF(n_candidates, self.config.top_k_fragments) - # if debug mode, only iterate over 10 elution groups - iterator_len = ( - min(10, len(score_group_container)) if debug else len(score_group_container) - ) - thread_count = 1 if debug else thread_count + iterator_len = len(score_group_container) if debug: logger.info("Debug mode enabled. Processing only 10 elution groups") + thread_count = 1 + iterator_len = min(10, iterator_len) alphatims.utils.set_threads(thread_count) _executor( From 12e021bc4bcef6f0196e84f48a69126bc02d1229 Mon Sep 17 00:00:00 2001 From: GeorgWa Date: Mon, 15 Jul 2024 10:46:05 +0200 Subject: [PATCH 4/4] fix --- alphadia/plexscoring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alphadia/plexscoring.py b/alphadia/plexscoring.py index 82187ce4..af2e16f4 100644 --- a/alphadia/plexscoring.py +++ b/alphadia/plexscoring.py @@ -1892,7 +1892,7 @@ def __call__( iterator_len = len(score_group_container) if debug: - logger.info("Debug mode enabled. Processing only 10 elution groups") + logger.info("Debug mode enabled. Processing only the first 10 score groups") thread_count = 1 iterator_len = min(10, iterator_len)