From 9af8c4c1763a785b66d0674308b5e5f48c6d6e19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8D=C3=B1igo=20Ar=C3=A9jula=20A=C3=ADsa?= Date: Mon, 3 Aug 2026 16:04:32 +0200 Subject: [PATCH 1/4] added free distributed MPI test job via minidmr Credits: AccelCom @ Barcelona Supercomputing Center --- .github/workflows/test_free.yml | 76 +++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/.github/workflows/test_free.yml b/.github/workflows/test_free.yml index e0837bfde..6bed7275e 100644 --- a/.github/workflows/test_free.yml +++ b/.github/workflows/test_free.yml @@ -6,6 +6,8 @@ # and as such use the slower, rigorous tests. # # @author Tyson Jones +# @author Íñigo Aréjula Aísa +# Credits: AccelCom @ Barcelona Supercomputing Center name: test (free, serial) @@ -88,3 +90,77 @@ jobs: if: ${{ matrix.version == 3 }} run: ctest -j2 --output-on-failure --schedule-random working-directory: ${{ env.depr_dir }} + + + # run the v4 unit tests distributed over MPI, for free, using minidmr + # to simulate a multi-node Slurm cluster on the single free runner + distributed-minidmr-test: + name: Linux [2] MPI (minidmr, 4 nodes) unit v4 + + runs-on: ubuntu-latest + + # constants + env: + build_dir: "build" + minidmr_nodes: 4 + test_exec: tests/tests + + # tests will be non-comprehensive/faster, since minidmr's 4 simulated + # nodes share the runner's 2 real vCPUs + num_qubit_perms: 5 + test_all_deploys: 0 + + # perform the job + steps: + - name: Get QuEST + uses: actions/checkout@main + + - name: Install minidmr + run: | + curl -fsSL https://gitlab.bsc.es/accelcom/releases/dmr/tools/minidmr/-/raw/master/scripts/install.sh | bash -s -- --install-dir "$HOME/.local/bin" + echo "$HOME/.local/bin" >> $GITHUB_PATH + + # Docker is preinstalled and running on GitHub-hosted ubuntu-latest runners + - name: Start minidmr cluster + run: minidmr start --nodes ${{ env.minidmr_nodes }} + + # minidmr bind-mounts the checkout (the current working directory) + # into every container at the same path, so no copying is needed + - name: Configure and compile QuEST (inside minidmr controller) + run: | + minidmr exec -- bash -lc " \ + cd '$GITHUB_WORKSPACE' && \ + cmake -B ${{ env.build_dir }} \ + -DENABLE_TESTING=ON \ + -DFLOAT_PRECISION=2 \ + -DENABLE_MULTITHREADING=OFF \ + -DENABLE_DISTRIBUTION=ON \ + -DTEST_ALL_DEPLOYMENTS=${{ env.test_all_deploys }} \ + -DTEST_MAX_NUM_QUBIT_PERMUTATIONS=${{ env.num_qubit_perms }} && \ + cmake --build ${{ env.build_dir }} --target tests --parallel \ + " + + # env vars must be forwarded with -x or remote ranks silently use + # different test configuration + - name: Run distributed v4 tests (minidmr, 4 simulated nodes) + run: | + minidmr srun \ + -e TEST_MAX_NUM_QUBIT_PERMUTATIONS=${{ env.num_qubit_perms }} \ + -e TEST_ALL_DEPLOYMENTS=${{ env.test_all_deploys }} \ + -e GITHUB_WORKSPACE=$GITHUB_WORKSPACE \ + -- -N ${{ env.minidmr_nodes }} -n ${{ env.minidmr_nodes }} bash -c ' \ + if [ "$SLURM_PROCID" -eq 0 ]; then \ + NODELIST_WITH_COUNTS=$(scontrol show hostnames "$SLURM_JOB_NODELIST" | awk "{print \$1\":1\"}" | paste -sd, -); \ + mpirun --prtemca ras ^slurm --prtemca plm ^slurm \ + -x TEST_ALL_DEPLOYMENTS -x TEST_MAX_NUM_QUBIT_PERMUTATIONS \ + --host "$NODELIST_WITH_COUNTS" -np ${{ env.minidmr_nodes }} \ + "$GITHUB_WORKSPACE/${{ env.build_dir }}/${{ env.test_exec }}"; \ + exit $?; \ + else \ + exit 0; \ + fi \ + ' + + - name: Stop minidmr cluster + if: always() + run: minidmr stop From c784a7634d1fb95b5aa85d00fea4f9964733fa4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8D=C3=B1igo=20Ar=C3=A9jula=20A=C3=ADsa?= Date: Mon, 3 Aug 2026 16:04:32 +0200 Subject: [PATCH 2/4] added free MPI sanitisation test job via minidmr Credits: AccelCom @ Barcelona Supercomputing Center --- .github/workflows/audit.yml | 89 ++++++++++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index 583749df4..6e8a0f488 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -6,6 +6,8 @@ # # @author Tyson Jones # @author Fergus Cooper (v3 CI) +# @author Íñigo Aréjula Aísa +# Credits: AccelCom @ Barcelona Supercomputing Center name: audit @@ -67,7 +69,92 @@ jobs: run: ctest -j2 --output-on-failure --schedule-random -E "density evolution" working-directory: ${{ env.build_dir }} - + + # run address sanitiser over the distributed (comm/*) code, using + # minidmr for a free multi-node MPI environment + sanitisation-test-mpi: + name: address sanitisation (MPI, minidmr, 4 nodes) + runs-on: ubuntu-latest + + # constants + env: + build_dir: "build" + minidmr_nodes: 4 + test_exec: tests/tests + sanitiser_flags: -g -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=address -fsanitize-address-use-after-scope + + # tests will be non-comprehensive/faster, since minidmr's 4 simulated + # nodes share the runner's 2 real vCPUs + num_qubit_perms: 5 + test_all_deploys: 0 + + # LeakSanitizer flags OpenMPI's own startup leaks as noise; not a QuEST bug + asan_options: detect_leaks=0 + + # perform the job + steps: + - name: Get QuEST + uses: actions/checkout@main + + - name: Install minidmr + run: | + curl -fsSL https://gitlab.bsc.es/accelcom/releases/dmr/tools/minidmr/-/raw/master/scripts/install.sh | bash -s -- --install-dir "$HOME/.local/bin" + echo "$HOME/.local/bin" >> $GITHUB_PATH + + # Docker is preinstalled and running on GitHub-hosted ubuntu-latest runners + - name: Start minidmr cluster + run: minidmr start --nodes ${{ env.minidmr_nodes }} + + # the image's GCC doesn't ship libasan by default + - name: Install libasan in cluster + run: minidmr install libasan + + # minidmr bind-mounts the checkout into every container at the same + # path, so no copying is needed. ASAN_OPTIONS must be set here too, + # else ctest's test-discovery step hits the benign leak above + - name: Configure and compile QuEST with sanitiser (inside minidmr controller) + run: | + minidmr exec -e ASAN_OPTIONS=${{ env.asan_options }} -- bash -lc " \ + cd '$GITHUB_WORKSPACE' && \ + cmake -B ${{ env.build_dir }} \ + -DENABLE_TESTING=ON \ + -DFLOAT_PRECISION=2 \ + -DENABLE_MULTITHREADING=OFF \ + -DENABLE_DISTRIBUTION=ON \ + -DTEST_ALL_DEPLOYMENTS=${{ env.test_all_deploys }} \ + -DTEST_MAX_NUM_QUBIT_PERMUTATIONS=${{ env.num_qubit_perms }} \ + -DCMAKE_CXX_FLAGS='${{ env.sanitiser_flags }}' \ + -DCMAKE_EXE_LINKER_FLAGS='${{ env.sanitiser_flags }}' && \ + cmake --build ${{ env.build_dir }} --target tests --parallel \ + " + + # env vars must be forwarded with -x or remote ranks silently use + # different test configuration + - name: Run distributed v4 tests with active sanitiser (minidmr, 4 simulated nodes) + run: | + minidmr srun \ + -e TEST_MAX_NUM_QUBIT_PERMUTATIONS=${{ env.num_qubit_perms }} \ + -e TEST_ALL_DEPLOYMENTS=${{ env.test_all_deploys }} \ + -e ASAN_OPTIONS=${{ env.asan_options }} \ + -e GITHUB_WORKSPACE=$GITHUB_WORKSPACE \ + -- -N ${{ env.minidmr_nodes }} -n ${{ env.minidmr_nodes }} bash -c ' \ + if [ "$SLURM_PROCID" -eq 0 ]; then \ + NODELIST_WITH_COUNTS=$(scontrol show hostnames "$SLURM_JOB_NODELIST" | awk "{print \$1\":1\"}" | paste -sd, -); \ + mpirun --prtemca ras ^slurm --prtemca plm ^slurm \ + -x TEST_ALL_DEPLOYMENTS -x TEST_MAX_NUM_QUBIT_PERMUTATIONS -x ASAN_OPTIONS \ + --host "$NODELIST_WITH_COUNTS" -np ${{ env.minidmr_nodes }} \ + "$GITHUB_WORKSPACE/${{ env.build_dir }}/${{ env.test_exec }}"; \ + exit $?; \ + else \ + exit 0; \ + fi \ + ' + + - name: Stop minidmr cluster + if: always() + run: minidmr stop + + # run valgrind memory-leak-test: name: memory checks [${{ matrix.precision }}] From f681de8f5c8ad202d6616bcf3af8b00007123ca6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8D=C3=B1igo=20Ar=C3=A9jula=20A=C3=ADsa?= Date: Mon, 3 Aug 2026 16:04:32 +0200 Subject: [PATCH 3/4] added free MPI coverage test job via minidmr Credits: AccelCom @ Barcelona Supercomputing Center --- .github/workflows/audit.yml | 121 ++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index 6e8a0f488..aa2f94e44 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -280,3 +280,124 @@ jobs: # PR since they're not yet representative and will only # confuse incoming unitaryHACK contributors if: false + + + # run lcov over the distributed (comm/*) code, using minidmr for a free + # multi-node MPI environment + coverage-test-mpi: + name: code coverage (MPI, minidmr, 2 nodes) + runs-on: ubuntu-latest + + # constants + env: + build_dir: "build" + minidmr_nodes: 2 + test_exec: tests/tests + num_qubit_perms: 5 + test_all_deploys: 0 + min_coverage: 1 # % + tracefile: "coverage-mpi.info" + + # perform the job + steps: + - name: Get QuEST + uses: actions/checkout@main + + - name: Install minidmr + run: | + curl -fsSL https://gitlab.bsc.es/accelcom/releases/dmr/tools/minidmr/-/raw/master/scripts/install.sh | bash -s -- --install-dir "$HOME/.local/bin" + echo "$HOME/.local/bin" >> $GITHUB_PATH + + # Docker is preinstalled and running on GitHub-hosted ubuntu-latest runners + - name: Start minidmr cluster + run: minidmr start --nodes ${{ env.minidmr_nodes }} + + # minidmr bind-mounts the checkout (the current working directory) + # into every container at the same path, so no copying is needed. + # Release mode is required: coverage without optimisation is slow + # enough to time out the runner + - name: Configure and compile QuEST with coverage (inside minidmr controller) + run: | + minidmr exec -- bash -lc " \ + cd '$GITHUB_WORKSPACE' && \ + cmake -B ${{ env.build_dir }} \ + -DCMAKE_BUILD_TYPE=Release \ + -DENABLE_TESTING=ON \ + -DFLOAT_PRECISION=2 \ + -DENABLE_MULTITHREADING=OFF \ + -DENABLE_DISTRIBUTION=ON \ + -DTEST_ALL_DEPLOYMENTS=${{ env.test_all_deploys }} \ + -DTEST_MAX_NUM_QUBIT_PERMUTATIONS=${{ env.num_qubit_perms }} \ + -DCMAKE_CXX_FLAGS='--coverage' \ + -DCMAKE_EXE_LINKER_FLAGS='--coverage' && \ + cmake --build ${{ env.build_dir }} --target tests --parallel \ + " + + # env vars must be forwarded with -x or remote ranks silently use + # different test configuration + - name: Run distributed v4 tests with coverage (minidmr, 2 simulated nodes) + run: | + minidmr srun \ + -e TEST_MAX_NUM_QUBIT_PERMUTATIONS=${{ env.num_qubit_perms }} \ + -e TEST_ALL_DEPLOYMENTS=${{ env.test_all_deploys }} \ + -e GITHUB_WORKSPACE=$GITHUB_WORKSPACE \ + -- -N ${{ env.minidmr_nodes }} -n ${{ env.minidmr_nodes }} bash -c ' \ + if [ "$SLURM_PROCID" -eq 0 ]; then \ + NODELIST_WITH_COUNTS=$(scontrol show hostnames "$SLURM_JOB_NODELIST" | awk "{print \$1\":1\"}" | paste -sd, -); \ + mpirun --prtemca ras ^slurm --prtemca plm ^slurm \ + -x TEST_ALL_DEPLOYMENTS -x TEST_MAX_NUM_QUBIT_PERMUTATIONS \ + --host "$NODELIST_WITH_COUNTS" -np ${{ env.minidmr_nodes }} \ + "$GITHUB_WORKSPACE/${{ env.build_dir }}/${{ env.test_exec }}"; \ + exit $?; \ + else \ + exit 0; \ + fi \ + ' + + - name: Install lcov in cluster + run: minidmr install --controller-only lcov + + # the controller's gcov (matching the compiler that built the + # binaries) must read the .gcda files, so lcov runs inside minidmr + # rather than on the runner, which has a different GCC version + - name: Run LCOV (inside minidmr controller) + run: | + minidmr exec -- bash -lc " \ + cd '$GITHUB_WORKSPACE/${{ env.build_dir }}' && \ + lcov --directory . --capture --output-file ${{ env.tracefile }} --ignore-errors source \ + " + + # comm/* is deliberately kept (the point of this job); gpu/* is + # removed since no GPU hardware is available here + - name: Filter LCOV results (inside minidmr controller) + run: | + minidmr exec -- bash -lc " \ + cd '$GITHUB_WORKSPACE/${{ env.build_dir }}' && \ + lcov --remove ${{ env.tracefile }} '/usr/*' '*/tests/*' '*/_deps/*' '*/gpu/*' --output-file ${{ env.tracefile }} \ + " + + - name: Preview LCOV results + run: | + minidmr exec -- bash -lc " \ + cd '$GITHUB_WORKSPACE/${{ env.build_dir }}' && \ + lcov --list ${{ env.tracefile }} \ + " + + - name: Stop minidmr cluster + if: always() + run: minidmr stop + + # report coverage of remaining files in the triggering PR + - name: Report code coverage + uses: zgosalvez/github-actions-report-lcov@v4 + with: + artifact-name: code-coverage-report-mpi + update-comment: true + coverage-files: ./${{ env.build_dir }}/${{ env.tracefile }} + minimum-coverage: ${{ env.min_coverage }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + # temporarily DISABLING above reporting of LCOV results in + # PR since they're not yet representative and will only + # confuse incoming unitaryHACK contributors + if: false From 3ea87473d8a02ad602196b4ef79667b661283706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8D=C3=B1igo=20Ar=C3=A9jula=20A=C3=ADsa?= Date: Mon, 3 Aug 2026 16:04:46 +0200 Subject: [PATCH 4/4] switch sanitisation-test-mpi to selective LSAN suppressions Credits: AccelCom @ Barcelona Supercomputing Center --- .github/workflows/audit.yml | 14 +++++++------- .github/workflows/lsan-mpi.supp | 12 ++++++++++++ 2 files changed, 19 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/lsan-mpi.supp diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index aa2f94e44..d0ab596a6 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -88,8 +88,8 @@ jobs: num_qubit_perms: 5 test_all_deploys: 0 - # LeakSanitizer flags OpenMPI's own startup leaks as noise; not a QuEST bug - asan_options: detect_leaks=0 + # suppresses known OpenMPI/PMIx startup leaks, not QuEST bugs + lsan_supp: .github/workflows/lsan-mpi.supp # perform the job steps: @@ -110,11 +110,11 @@ jobs: run: minidmr install libasan # minidmr bind-mounts the checkout into every container at the same - # path, so no copying is needed. ASAN_OPTIONS must be set here too, - # else ctest's test-discovery step hits the benign leak above + # path, so no copying is needed. LSAN_OPTIONS must be set here too, + # since Catch2's CMake test-discovery runs the binary at build time - name: Configure and compile QuEST with sanitiser (inside minidmr controller) run: | - minidmr exec -e ASAN_OPTIONS=${{ env.asan_options }} -- bash -lc " \ + minidmr exec -e LSAN_OPTIONS="suppressions=$GITHUB_WORKSPACE/${{ env.lsan_supp }}" -- bash -lc " \ cd '$GITHUB_WORKSPACE' && \ cmake -B ${{ env.build_dir }} \ -DENABLE_TESTING=ON \ @@ -135,13 +135,13 @@ jobs: minidmr srun \ -e TEST_MAX_NUM_QUBIT_PERMUTATIONS=${{ env.num_qubit_perms }} \ -e TEST_ALL_DEPLOYMENTS=${{ env.test_all_deploys }} \ - -e ASAN_OPTIONS=${{ env.asan_options }} \ + -e LSAN_OPTIONS="suppressions=$GITHUB_WORKSPACE/${{ env.lsan_supp }}" \ -e GITHUB_WORKSPACE=$GITHUB_WORKSPACE \ -- -N ${{ env.minidmr_nodes }} -n ${{ env.minidmr_nodes }} bash -c ' \ if [ "$SLURM_PROCID" -eq 0 ]; then \ NODELIST_WITH_COUNTS=$(scontrol show hostnames "$SLURM_JOB_NODELIST" | awk "{print \$1\":1\"}" | paste -sd, -); \ mpirun --prtemca ras ^slurm --prtemca plm ^slurm \ - -x TEST_ALL_DEPLOYMENTS -x TEST_MAX_NUM_QUBIT_PERMUTATIONS -x ASAN_OPTIONS \ + -x TEST_ALL_DEPLOYMENTS -x TEST_MAX_NUM_QUBIT_PERMUTATIONS -x LSAN_OPTIONS \ --host "$NODELIST_WITH_COUNTS" -np ${{ env.minidmr_nodes }} \ "$GITHUB_WORKSPACE/${{ env.build_dir }}/${{ env.test_exec }}"; \ exit $?; \ diff --git a/.github/workflows/lsan-mpi.supp b/.github/workflows/lsan-mpi.supp new file mode 100644 index 000000000..335ab95f8 --- /dev/null +++ b/.github/workflows/lsan-mpi.supp @@ -0,0 +1,12 @@ +# known OpenMPI/PMIx startup leaks, not QuEST bugs +leak:avx_component_op_query +leak:ompi_op_base_op_select +leak:ompi_comm_init_mpi3 +leak:make_copy +leak:pmix_ptl_base_get_cmd_line +leak:register_variable +leak:opal_common_ucx_mca_var_register +leak:get_data +leak:PMIx_Value_create +leak:pmix_bfrops_base_value_load +leak:vasprintf