From 6283c6ea783d8fbae8697ddc755ed0b456015a07 Mon Sep 17 00:00:00 2001 From: Vasco Ferreira Date: Tue, 6 Jan 2026 15:07:49 +0000 Subject: [PATCH 1/4] feat: add QFT inverse and doc --- quest/include/operations.h | 72 ++++++++++++++++++++++++++++++++---- quest/src/api/operations.cpp | 45 +++++++++++++++------- 2 files changed, 96 insertions(+), 21 deletions(-) diff --git a/quest/include/operations.h b/quest/include/operations.h index b138f200..7b605c95 100644 --- a/quest/include/operations.h +++ b/quest/include/operations.h @@ -25,6 +25,8 @@ #include "quest/include/matrices.h" #include "quest/include/channels.h" +#include + #ifdef __cplusplus #include #endif @@ -2406,14 +2408,70 @@ extern "C" { #endif -/// @notyetdoced -/// @notyetvalidated -void applyQuantumFourierTransform(Qureg qureg, int* targets, int numTargets); +/** @notyetdoced + * @notyetvalidated + * + * Applies the Quantum Fourier Transform to the specified @p targets of @p qureg. + * Alternatively, applies the Inverse Quantum Fourier Transform according to @p inverse. + * + * @formulae + * + * Let @f$ N @f$ = @p numTargets, the @f$ N @f$ qubit Quantum Fourier Transform maps each + * computational basis state belonging to the targeted qubits, @f$ \ket{j} @f$, according to + * @f[ + \ket{j} \rightarrow \frac{1}{\sqrt{2^N}} \sum_{k=0}^{2^N-1} e^{2 \pi i j k / 2^N} \ket{k}. + * @f] + * Similarly the Inverse Quantum Fourier Transform maps each basis state like + * @f[ + \ket{j} \rightarrow \frac{1}{\sqrt{2^N}} \sum_{k=0}^{2^N-1} e^{-2 \pi i j k / 2^N} \ket{k}. + * @f] + * + * @param[in,out] qureg the state to modify. + * @param[in] targets the indices of the target qubits. + * @param[in] numTargets the length of list @p targets + * @param[in] inverse whether to apply the inverse QFT or forward QFT + * @throws @validationerror + * - if @p qureg is uninitialised. +* - if @p targets are invalid qubit indices. + * - if @p numTargets < 1. + * @see + * - applyFullQuantumFourierTransform() + * @author Vasco Ferreira + */ +void applyQuantumFourierTransform(Qureg qureg, int* targets, int numTargets, bool inverse); -/// @notyetdoced -/// @notyetvalidated -void applyFullQuantumFourierTransform(Qureg qureg); +/** @notyetdoced + * @notyetvalidated + * + * Applies the Quantum Fourier Transform to each qubit in @p qureg. Alternatively, + * applies the Inverse Quantum Fourier Transform according to @p inverse. + * + * @formulae + * + * The Quantum Fourier Transform maps each computational basis state @f$ \ket{j} @f$ + * in an @f$ N @f$ qubit @p qureg according to + * @f[ + \ket{j} \rightarrow \frac{1}{\sqrt{2^N}} \sum_{k=0}^{2^N-1} e^{2 \pi i j k / 2^N} \ket{k}. + * @f] + * Similarly the Inverse Quantum Fourier Transform maps each basis state like + * @f[ + \ket{j} \rightarrow \frac{1}{\sqrt{2^N}} \sum_{k=0}^{2^N-1} e^{-2 \pi i j k / 2^N} \ket{k}. + * @f] + * + * @equivalences + * + * - This function wraps applyQuantumFourierTransform() with all the qubits in the @p qureg as @p targets. + * + * @param[in,out] qureg the state to modify. + * @param[in] inverse whether to apply the inverse QFT or forward QFT + * @throws @validationerror + * - if @p qureg is uninitialised. + * @see + * - applyQuantumFourierTransform() + * @author Vasco Ferreira + */ +void applyFullQuantumFourierTransform(Qureg qureg, bool inverse); // end de-mangler @@ -2429,7 +2487,7 @@ void applyFullQuantumFourierTransform(Qureg qureg); /// @notyetdoced /// @cppvectoroverload /// @see applyQuantumFourierTransform() -void applyQuantumFourierTransform(Qureg qureg, std::vector targets); +void applyQuantumFourierTransform(Qureg qureg, std::vector targets, bool inverse); #endif // __cplusplus diff --git a/quest/src/api/operations.cpp b/quest/src/api/operations.cpp index e458605a..979e3ac0 100644 --- a/quest/src/api/operations.cpp +++ b/quest/src/api/operations.cpp @@ -1736,7 +1736,7 @@ qreal applyForcedMultiQubitMeasurement(Qureg qureg, vector qubits, vector=0; n--) { - applyHadamard(qureg, targets[n]); - for (int m=0; m=0; n--) { + applyHadamard(qureg, targets[n]); + + for (int m=0; m targets) { +void applyQuantumFourierTransform(Qureg qureg, vector targets, bool inverse) { - applyQuantumFourierTransform(qureg, targets.data(), targets.size()); + applyQuantumFourierTransform(qureg, targets.data(), targets.size(), inverse); } From 7ec91b9e86b1023e6f5521eb66a77e91c9a37149 Mon Sep 17 00:00:00 2001 From: Vasco Ferreira Date: Tue, 6 Jan 2026 15:11:13 +0000 Subject: [PATCH 2/4] test: add inverse to DFT --- tests/utils/linalg.cpp | 8 ++++---- tests/utils/linalg.hpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/utils/linalg.cpp b/tests/utils/linalg.cpp index c7477521..e8285cdf 100644 --- a/tests/utils/linalg.cpp +++ b/tests/utils/linalg.cpp @@ -157,7 +157,7 @@ qvector getNormalised(qvector vec) { } -qvector getDisceteFourierTransform(qvector in) { +qvector getDiscreteFourierTransform(qvector in, bool inverse) { DEMAND( in.size() > 0 ); size_t dim = in.size(); @@ -166,7 +166,7 @@ qvector getDisceteFourierTransform(qvector in) { // PI must be accurate here qreal pi = 3.14159265358979323846; qreal a = 1 / std::sqrt(dim); - qreal b = 2 * pi / dim; + qreal b = (inverse ? -1 : 1) * 2 * pi / dim; for (size_t x=0; x targs) { +qvector getDiscreteFourierTransform(qvector in, vector targs, bool inverse) { DEMAND( in.size() > 0 ); size_t dim = in.size(); @@ -185,7 +185,7 @@ qvector getDisceteFourierTransform(qvector in, vector targs) { qindex len = getPow2(targs.size()); qreal pi = 3.14159265358979323846; qreal a = 1 / std::sqrt(len); - qreal b = 2 * pi / len; + qreal b = (inverse ? -1 : 1) * 2 * pi / len; for (size_t i=0; i vec); qcomp getSum(qvector); qvector getNormalised(qvector); -qvector getDisceteFourierTransform(qvector); -qvector getDisceteFourierTransform(qvector in, vector targs); +qvector getDiscreteFourierTransform(qvector in, bool inverse); +qvector getDiscreteFourierTransform(qvector in, vector targs, bool inverse); qcomp getInnerProduct(qvector bra, qvector ket); qmatrix getOuterProduct(qvector ket, qvector bra); From a0d7f94d48efdd50fb03a8a2749b2b0a30f492ef Mon Sep 17 00:00:00 2001 From: Vasco Ferreira Date: Tue, 6 Jan 2026 15:11:57 +0000 Subject: [PATCH 3/4] test: add inverse QFT tests --- tests/unit/operations.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/tests/unit/operations.cpp b/tests/unit/operations.cpp index 654bc9c2..a0470f8c 100644 --- a/tests/unit/operations.cpp +++ b/tests/unit/operations.cpp @@ -1372,16 +1372,18 @@ TEST_CASE( "applyQuantumFourierTransform", TEST_CATEGORY_OPS ) { int numTargs = GENERATE_COPY( range(1,numQubits+1) ); auto targs = GENERATE_TARGS( numQubits, numTargs ); + bool inverse = GENERATE(false, true); CAPTURE( targs ); SECTION( LABEL_STATEVEC ) { auto testFunc = [&](Qureg qureg, qvector& ref) { - applyQuantumFourierTransform(qureg, targs.data(), targs.size()); - ref = getDisceteFourierTransform(ref, targs); + applyQuantumFourierTransform(qureg, targs.data(), targs.size(), inverse); + ref = getDiscreteFourierTransform(ref, targs, inverse); }; + CAPTURE(inverse); TEST_ON_CACHED_QUREGS(statevecQuregs, statevecRef, testFunc); } @@ -1395,15 +1397,16 @@ TEST_CASE( "applyQuantumFourierTransform", TEST_CATEGORY_OPS ) { // overwrite the Qureg debug state set by caller to above mixture setQuregToReference(qureg, getMixture(states, probs)); - applyQuantumFourierTransform(qureg, targs.data(), targs.size()); + applyQuantumFourierTransform(qureg, targs.data(), targs.size(), inverse); ref = getZeroMatrix(ref.size()); for (size_t i=0; i Date: Tue, 6 Jan 2026 15:12:48 +0000 Subject: [PATCH 4/4] docs: update QFT in examples --- README.md | 2 +- docs/tutorial.md | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9eb85bfd..f1dd5e87 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ initRandomPureState(qureg); applyHadamard(qureg, 0); applyControlledRotateX(qureg, 0, 1, angle); -applyFullQuantumFourierTransform(qureg); +applyFullQuantumFourierTransform(qureg, inverse); reportQureg(qureg); diff --git a/docs/tutorial.md b/docs/tutorial.md index a006a3fc..b3e99706 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -626,7 +626,8 @@ applyCompMatr1(qureg, 0, m); QuEST includes a few convenience functions for effecting [QFT](https://quest-kit.github.io/QuEST/group__op__qft.html) and [Trotter](https://quest-kit.github.io/QuEST/group__op__paulistrsum.html) circuits. ```cpp -applyQuantumFourierTransform(qureg, targets, 3); +bool inverse = false; +applyQuantumFourierTransform(qureg, targets, 3, inverse); qreal time = .3; int order = 4; @@ -865,4 +866,4 @@ This is important because it ensures: > [!CAUTION] > After calling `finalizeQuESTEnv()`, MPI will close and if being accessed directly by the user, will enter an undefined state. Subsequent calls to MPI routines may return gibberish, and distributed machines will lose their ability to communicate. It is recommended to call `finalizeQuESTEnv()` immediately before exiting. -You are now a QuEST expert 🎉 though there are _many_ more functions in the [API](https://quest-kit.github.io/QuEST/group__api.html) not covered here. Go forth and simulate! \ No newline at end of file +You are now a QuEST expert 🎉 though there are _many_ more functions in the [API](https://quest-kit.github.io/QuEST/group__api.html) not covered here. Go forth and simulate!