Skip to content

Commit 187e936

Browse files
Extended tests to 8-qubit edgecases (#808)
to (only partially) address #801
1 parent 750932d commit 187e936

6 files changed

Lines changed: 123 additions & 8 deletions

File tree

‎.github/workflows/compile.yml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ jobs:
4343
startsWith(matrix.os, 'macos' ) && 'MacOS' ||
4444
startsWith(matrix.os, 'windows') && 'Windows' ||
4545
'Unknown' }}
46-
${{ endsWith(matrix.os, 'intel') && '(Intel)' || '' }}
46+
${{ endsWith(matrix.os, '15-intel') && '(Intel 2015)' || '' }}
47+
${{ endsWith(matrix.os, '26-intel') && '(Intel 2026)' || '' }}
4748
[${{ matrix.precision }}]
4849
${{ matrix.omp == 'ON' && 'OMP' || '' }}
4950
${{ matrix.mpi == 'ON' && 'MPI' || '' }}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Some free, ad hoc tests which test only specific
2+
# functions in specific regimes, covering edge cases.
3+
# These are intended as a stop gap in the interim to
4+
# an improved test harness.
5+
#
6+
# We here test only CPU functions which have
7+
# template parameters (like NumCtrls or NumTargs)
8+
# which inform an optimised definition for MORE than
9+
# 5 involved qubits. For example, multiControlledSWAP
10+
# has optimised treatment of up to 5 control qubits,
11+
# while needing 2 target qubits; so requires a Qureg
12+
# of at least 8 qubits to trigger its unoptimised
13+
# version (receiving 6 control qubits and 2 targets).
14+
#
15+
# We alas do NOT here test the fully-unoptimised
16+
# versions of multi-ctrl multi-targ functions (such
17+
# as applyMultiControlledCompMatr), because that
18+
# requires reaching 6 ctrls + 6 targs, and ergo a
19+
# Qureg of at least 12 qubits. So large a Hilbert
20+
# space is alas too slow for the reference maths of
21+
# our unit tests. Curse ye, exponential!
22+
#
23+
# @author Tyson Jones
24+
25+
name: test (edge cases)
26+
27+
28+
on:
29+
push:
30+
branches:
31+
- main
32+
- devel
33+
pull_request:
34+
branches:
35+
- main
36+
- devel
37+
38+
39+
jobs:
40+
41+
# run only some non-parallelised v4 unit tests at default (double) precision
42+
serial-unit-test:
43+
name: >
44+
${{ startsWith(matrix.os, 'ubuntu' ) && 'Linux' ||
45+
startsWith(matrix.os, 'macos' ) && 'MacOS' ||
46+
startsWith(matrix.os, 'windows') && 'Windows' ||
47+
'Unknown' }}
48+
${{ endsWith(matrix.os, '26-intel') && '(Intel 2026)' || '' }}
49+
serial
50+
${{ matrix.bmi2 == 'ON' && '(BMI)' || '' }}
51+
edgecases
52+
53+
runs-on: ${{ matrix.os }}
54+
55+
strategy:
56+
# continue other jobs if any fail
57+
fail-fast: false
58+
59+
# we will compile QuEST with all precisions but no parallelisation (though with Intel BMI2)
60+
matrix:
61+
os: [ubuntu-latest, macos-latest, windows-latest, macos-26-intel]
62+
bmi2: [ON, OFF]
63+
64+
exclude:
65+
# cannot use BMI2 on non-Intel MacOS
66+
- bmi2: ON
67+
os: macos-latest
68+
69+
# constants; all tests are non-comprehensive for speed
70+
env:
71+
build_dir: "build"
72+
full_tests_regex: "Swap|CompMatr1|CompMatr2"
73+
partial_tests_regex: "^applyMultiStateControlled(CompMatr|DiagMatr|PauliStr|PauliGadget)$"
74+
QUEST_TEST_NUM_QUBITS_IN_QUREG: 8
75+
QUEST_TEST_MAX_NUM_QUBIT_PERMUTATIONS: 1
76+
QUEST_TEST_NUM_MIXED_DEPLOYMENT_REPETITIONS: 1
77+
78+
# perform the job
79+
steps:
80+
- name: Get QuEST
81+
uses: actions/checkout@main
82+
83+
# compile serial unit tests
84+
- name: Configure CMake
85+
run: >
86+
cmake -B ${{ env.build_dir }}
87+
-DQUEST_BUILD_TESTS=ON
88+
-DQUEST_ENABLE_OMP=OFF
89+
-DQUEST_ENABLE_BMI2=${{ matrix.bmi2 }}
90+
91+
# force 'Release' build (needed by MSVC to enable optimisations)
92+
- name: Compile
93+
run: cmake --build ${{ env.build_dir }} --config Release
94+
95+
# test statevector and density-matrix functions which exhibit
96+
# optimisations on up to 5 ctrl + 2 target qubits, with 8-qubit
97+
# Quregs, in order to test the non-optimised implementation
98+
- name: 8-qubit edge-cases (full coverage)
99+
run: ctest -C Release -R "${{ env.full_tests_regex }}"
100+
working-directory: ${{ env.build_dir }}
101+
102+
# it is our desire to test the "partial_tests" functions with
103+
# 12-qubit Quregs, so that NumCtrls=6 while NumTargs=6 could be
104+
# achieved, testing the fully unoptimised versions - but alas
105+
# it's intractable for our slow reference maths! So we instead
106+
# at least test the NumCtrls=6 and NumTargs=6 cases independently,
107+
# requiring only 7-qubit Quregs, but we use 8 for consistency
108+
# with above
109+
- name: 8-qubit edge-cases (partial coverage)
110+
run: ctest -C Release -R "${{ env.partial_tests_regex }}"
111+
working-directory: ${{ env.build_dir }}

‎.github/workflows/test_free.yml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ jobs:
3131
startsWith(matrix.os, 'macos' ) && 'MacOS' ||
3232
startsWith(matrix.os, 'windows') && 'Windows' ||
3333
'Unknown' }}
34-
${{ endsWith(matrix.os, 'intel') && '(Intel)' || '' }}
34+
${{ endsWith(matrix.os, '15-intel') && '(Intel 2015)' || '' }}
35+
${{ endsWith(matrix.os, '26-intel') && '(Intel 2026)' || '' }}
3536
[${{ matrix.precision }}]
3637
serial
3738
${{ matrix.bmi2 == 'ON' && '(BMI)' || '' }}

‎tests/utils/linalg.cpp‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include <algorithm>
1717
#include <vector>
18+
#include <limits>
1819

1920
using std::vector;
2021

@@ -93,14 +94,15 @@ qindex setBitsAt(qindex num, vector<int> inds, qindex bits) {
9394
}
9495

9596

96-
int getNumPermutations(int n, int k) {
97+
qindex getNumPermutations(int n, int k) {
9798
DEMAND( n >= k );
98-
DEMAND( n <= 11 ); // else int overflow
99+
100+
constexpr auto max = std::numeric_limits<qindex>::max();
99101

100102
// P(n, k) = n! / (n-k)!
101103
qindex p = 1;
102104
for (int t=n-k+1; t<=n; t++)
103-
p *= t;
105+
p *= (p < max / t)? t : 0; // set to 0 on overflow
104106

105107
return p;
106108
}

‎tests/utils/linalg.hpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@
2020
#include <vector>
2121
using std::vector;
2222

23-
int getNumPermutations(int n, int k);
2423
int getLog2(qindex);
2524
int getBitAt(qindex num, int ind);
2625
vector<int> getBits(qindex num, int numBits);
2726
qindex getBitsAt(qindex num, vector<int> inds);
2827
qindex setBitAt(qindex num, int ind, int bit);
2928
qindex setBitsAt(qindex num, vector<int> inds, qindex bits);
3029
qindex getPow2(int);
30+
qindex getNumPermutations(int n, int k); // =0 on overflow
3131

3232
qreal getSum(vector<qreal> vec);
3333
qcomp getSum(qvector);

‎tests/utils/lists.cpp‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,15 +264,15 @@ listpair GENERATE_CTRLS_AND_TARGS(int numQubits, int numCtrls, int numTargs) {
264264
DEMAND( numQubits >= numCtrls + numTargs );
265265

266266
// impose a limit on the number of {ctrls,targs} to generate (max-int if none set)
267-
int numPerms = getNumPermutations(numQubits, numCtrls + numTargs);
267+
auto numPerms = getNumPermutations(numQubits, numCtrls + numTargs); // 0 when overflowed
268268
int maxPerms = getMaxNumTestedQubitPermutations();
269269
if (maxPerms == 0)
270270
maxPerms = std::numeric_limits<int>::max();
271271

272272
// if all permutations are permitted, determinstically generate each in turn.
273273
// note this wastefully generates all orderings of ctrl qubits, despite that
274274
// this has no effect on all API operations, but we carefully check anyway!
275-
if (numPerms < maxPerms)
275+
if (numPerms != 0 && numPerms < maxPerms)
276276
return GENERATE_COPY( disjointsublists(range(0,numQubits), numCtrls, numTargs) );
277277

278278
// otherwise generate as many random {ctrls,targs} as permitted

0 commit comments

Comments
 (0)