From 6ebfa2b4064f32bd25656dfcdbf13159e3222bb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9rome=20Eertmans?= Date: Wed, 18 Sep 2024 13:04:08 +0200 Subject: [PATCH 1/6] chore(lib): warn about overflow and add Rust coverage --- .github/workflows/test.yml | 32 +++++++++++++++++- differt-core/src/rt/graph.rs | 52 +++++++++++++++++------------ differt-core/tests/rt/test_graph.py | 48 +++++++++++++++++++++++++- 3 files changed, 109 insertions(+), 23 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 21f6aaec..39fa0fb7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -69,7 +69,8 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - name: Setup Python + uses: actions/setup-python@v5 with: python-version: ${{ matrix.pyversion }} @@ -82,6 +83,35 @@ jobs: - name: Run Cargo test run: cargo test + cargo-tarpaulin: + runs-on: ubuntu-latest + container: + image: xd009642/tarpaulin:develop-nightly + options: --security-opt seccomp=unconfined + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install NumPy + run: pip install numpy + + - name: Run Cargo test + run: cargo test + + - name: Generate code coverage + run: cargo +nightly tarpaulin --verbose --all-features --workspace --timeout 120 --out xml + + - name: Upload to codecov.io + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.DIFFERT_CODECOV_TOKEN }} + fail_ci_if_error: true + python-benchmark: runs-on: ubuntu-latest permissions: diff --git a/differt-core/src/rt/graph.rs b/differt-core/src/rt/graph.rs index 0395716d..4adb7c6a 100644 --- a/differt-core/src/rt/graph.rs +++ b/differt-core/src/rt/graph.rs @@ -317,7 +317,7 @@ pub mod complete { let from_in_graph = from < num_nodes; let to_in_graph = to < num_nodes; - match (from_in_graph, to_in_graph) { + (match (from_in_graph, to_in_graph) { (true, true) => { //This solution was obtained thanks to user @ronno, see: // https://math.stackexchange.com/a/4874894/1297520. @@ -326,38 +326,48 @@ pub mod complete { let num_nodes_minus_1 = num_nodes.saturating_sub(1); if from != to { num_nodes_minus_1 - .saturating_pow(depth_minus_1) - .saturating_add_signed(if depth_minus_1 % 2 == 0 { - -1 - } else { - 1 + .checked_pow(depth_minus_1) + .and_then(|num| { + num.checked_add_signed(if depth_minus_1 % 2 == 0 { + -1 + } else { + 1 + }) }) - / num_nodes + .map(|num| num / num_nodes) } else { num_nodes_minus_1 - .saturating_pow(depth_minus_2) - .saturating_add_signed(if depth_minus_2 % 2 == 0 { - -1 - } else { - 1 + .checked_pow(depth_minus_2) + .and_then(|num| { + num.checked_add_signed(if depth_minus_2 % 2 == 0 { + -1 + } else { + 1 + }) }) - * num_nodes_minus_1 - / num_nodes + .map(|num| (num / num_nodes) * num_nodes_minus_1) } }, (false, false) => { // (num_nodes) * (num_nodes - 1)^(num_intermediate_nodes - 1) - (num_nodes).saturating_mul( - num_nodes - .saturating_sub(1) - .saturating_pow(num_intermediate_nodes.saturating_sub(1)), - ) + num_nodes + .saturating_sub(1) + .checked_pow(num_intermediate_nodes.saturating_sub(1)) + .and_then(|num| num_nodes.checked_mul(num)) }, _ => { // (num_nodes - 1)^num_intermediate_nodes - (num_nodes.saturating_sub(1)).saturating_pow(num_intermediate_nodes) + (num_nodes.saturating_sub(1)).checked_pow(num_intermediate_nodes) }, - } + }) + .unwrap_or_else(|| { + log::warn!( + "OverflowError: overflow occurred when computing the total number of \ + paths, defaulting to maximum value {}.", + usize::MAX + ); + usize::MAX + }) }, }; diff --git a/differt-core/tests/rt/test_graph.py b/differt-core/tests/rt/test_graph.py index b9c71b94..c0260327 100644 --- a/differt-core/tests/rt/test_graph.py +++ b/differt-core/tests/rt/test_graph.py @@ -83,6 +83,27 @@ def test_all_keyword_only_parameters(self) -> None: ): _ = graph.all_paths(0, 1, 0, True) # noqa: FBT003 + @pytest.mark.parametrize( + ("num_nodes", "depth"), + [ + (15, 2), + (25, 3), + (11, 4), + ], + ) + def test_all_paths_count_from_complete_graph( + self, + num_nodes: int, + depth: int, + ) -> None: + graph = CompleteGraph(num_nodes) + from_, to = num_nodes, num_nodes + 1 + paths = graph.all_paths(from_, to, depth + 2, include_from_and_to=False) + num_paths = sum(1 for _ in paths) + assert num_paths == num_nodes * (num_nodes - 1) ** (depth - 1) + array = graph.all_paths_array(from_, to, depth + 2, include_from_and_to=False) + assert array.shape == (num_paths, depth) + @pytest.mark.parametrize( ("num_nodes", "depth"), [ @@ -91,7 +112,7 @@ def test_all_keyword_only_parameters(self) -> None: (10, 3), ], ) - def test_all_paths_count_from_complete_graph( + def test_all_paths_count_from_di_graph( self, num_nodes: int, depth: int, @@ -103,3 +124,28 @@ def test_all_paths_count_from_complete_graph( assert num_paths == num_nodes * (num_nodes - 1) ** (depth - 1) array = graph.all_paths_array(from_, to, depth + 2, include_from_and_to=False) assert array.shape == (num_paths, depth) + + @pytest.mark.parametrize( + ("num_nodes", "depth"), + [ + (10, 100), + (50_000, 10), + ], + ) + def test_all_paths_count_from_complete_graph_overflow( + self, + num_nodes: int, + depth: int, + caplog: pytest.LogCaptureFixture, + ) -> None: + graph = CompleteGraph(num_nodes) + from_, to = num_nodes, num_nodes + 1 + _ = graph.all_paths(from_, to, depth + 2, include_from_and_to=False) + + for record in caplog.records: + assert record.levelname == "WARNING" + + assert ( + "OverflowError: overflow occurred when computing the total number of paths" + in caplog.text + ) From 78a1afae306bedec1d34232d7e2a5a74bd3ffbbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9rome=20Eertmans?= Date: Wed, 18 Sep 2024 13:14:29 +0200 Subject: [PATCH 2/6] fix(ci): setup Python inside container --- .github/workflows/test.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 39fa0fb7..08210139 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -93,16 +93,13 @@ jobs: uses: actions/checkout@v4 - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: '3.11' + run: | + apt update -y + apt install -y python3 - name: Install NumPy run: pip install numpy - - name: Run Cargo test - run: cargo test - - name: Generate code coverage run: cargo +nightly tarpaulin --verbose --all-features --workspace --timeout 120 --out xml From 7bb7da1dc32b9cd670762d8bbeeff341d5f95d86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9rome=20Eertmans?= Date: Wed, 18 Sep 2024 14:15:19 +0200 Subject: [PATCH 3/6] chore(ci): does it work? --- .github/workflows/test.yml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 08210139..3cf37a6f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -86,22 +86,19 @@ jobs: cargo-tarpaulin: runs-on: ubuntu-latest container: - image: xd009642/tarpaulin:develop-nightly + image: xd009642/tarpaulin options: --security-opt seccomp=unconfined steps: - name: Checkout repository uses: actions/checkout@v4 - name: Setup Python - run: | - apt update -y - apt install -y python3 - - - name: Install NumPy - run: pip install numpy + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.pyversion }} - name: Generate code coverage - run: cargo +nightly tarpaulin --verbose --all-features --workspace --timeout 120 --out xml + run: cargo tarpaulin --verbose --timeout 120 --out xml - name: Upload to codecov.io uses: codecov/codecov-action@v4 From 3096522bef6bc8ffbfe89017c8b63ba09cd06fd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9rome=20Eertmans?= Date: Wed, 18 Sep 2024 14:18:29 +0200 Subject: [PATCH 4/6] chore(ci): setup-python reads `.python-version` --- .github/workflows/lint.yml | 2 -- .github/workflows/publish.yml | 3 --- .github/workflows/test.yml | 5 ----- 3 files changed, 10 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 382b42c7..663d008c 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -22,8 +22,6 @@ jobs: - name: Install Python uses: actions/setup-python@v5 - with: - python-version: '3.11' - name: Install pre-commit run: pip install pre-commit diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 6b51c11a..1b819097 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -26,8 +26,6 @@ jobs: - name: Install Python uses: actions/setup-python@v5 - with: - python-version: '3.10' - name: Build wheels uses: PyO3/maturin-action@v1 @@ -57,7 +55,6 @@ jobs: - name: Install Python uses: actions/setup-python@v5 with: - python-version: '3.11' architecture: ${{ matrix.target }} - name: Build wheels diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3cf37a6f..65e84c2b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -94,8 +94,6 @@ jobs: - name: Setup Python uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.pyversion }} - name: Generate code coverage run: cargo tarpaulin --verbose --timeout 120 --out xml @@ -116,7 +114,6 @@ jobs: - uses: actions/setup-python@v5 with: - python-version: '3.11' cache: pip cache-dependency-path: uv.lock @@ -229,8 +226,6 @@ jobs: uses: actions/checkout@v4 - uses: actions/setup-python@v5 - with: - python-version: '3.11' - name: Install NumPy run: pip install numpy From df34f56143a59d1071a2617d84dedabdbe0b8000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9rome=20Eertmans?= Date: Wed, 18 Sep 2024 14:19:43 +0200 Subject: [PATCH 5/6] fix(ci): `.python-version` --- .python-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.python-version b/.python-version index b6d8b761..2c073331 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.11.8 +3.11 From d1673bf70ede218a86984cfa37c6f04f3c22cd7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9rome=20Eertmans?= Date: Wed, 18 Sep 2024 14:29:23 +0200 Subject: [PATCH 6/6] fix(ci): install cargo-tarpaulin manually --- .github/workflows/test.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 65e84c2b..81d6ca7d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -85,9 +85,6 @@ jobs: cargo-tarpaulin: runs-on: ubuntu-latest - container: - image: xd009642/tarpaulin - options: --security-opt seccomp=unconfined steps: - name: Checkout repository uses: actions/checkout@v4 @@ -95,6 +92,16 @@ jobs: - name: Setup Python uses: actions/setup-python@v5 + - name: Install NumPy + run: pip install numpy + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + + - uses: taiki-e/install-action@v2 + with: + tool: cargo-tarpaulin@0.31.2 + - name: Generate code coverage run: cargo tarpaulin --verbose --timeout 120 --out xml