Skip to content

Commit ee85737

Browse files
added QFT (QuEST-Kit#291)
* added `applyQFT()` * added `applyFullQFT()`
1 parent 29cfd64 commit ee85737

File tree

7 files changed

+835
-9
lines changed

7 files changed

+835
-9
lines changed

QuEST/include/QuEST.h

+210
Original file line numberDiff line numberDiff line change
@@ -6325,6 +6325,216 @@ void applyParamNamedPhaseFunc(Qureg qureg, int* qubits, int* numQubitsPerReg, in
63256325
*/
63266326
void applyParamNamedPhaseFuncOverrides(Qureg qureg, int* qubits, int* numQubitsPerReg, int numRegs, enum bitEncoding encoding, enum phaseFunc functionNameCode, qreal* params, int numParams, long long int* overrideInds, qreal* overridePhases, int numOverrides);
63276327

6328+
/** Applies the quantum Fourier transform (QFT) to the entirety of \p qureg.
6329+
* The effected unitary circuit (shown here for 4 qubits, bottom qubit is <b>0</b>) resembles
6330+
\f[
6331+
\begin{tikzpicture}[scale=.5]
6332+
\draw (-2, 5) -- (23, 5);
6333+
\draw (-2, 3) -- (23, 3);
6334+
\draw (-2, 1) -- (23, 1);
6335+
\draw (-2, -1) -- (23, -1);
6336+
6337+
\draw[fill=white] (-1, 4) -- (-1, 6) -- (1, 6) -- (1,4) -- cycle;
6338+
\node[draw=none] at (0, 5) {H};
6339+
6340+
\draw(2, 5) -- (2, 3);
6341+
\draw[fill=black] (2, 5) circle (.2);
6342+
\draw[fill=black] (2, 3) circle (.2);
6343+
\draw(4, 5) -- (4, 1);
6344+
\draw[fill=black] (4, 5) circle (.2);
6345+
\draw[fill=black] (4, 1) circle (.2);
6346+
\draw(6, 5) -- (6, -1);
6347+
\draw[fill=black] (6, 5) circle (.2);
6348+
\draw[fill=black] (6, -1) circle (.2);
6349+
6350+
\draw[fill=white] (-1+8, 4-2) -- (-1+8, 6-2) -- (1+8, 6-2) -- (1+8,4-2) -- cycle;
6351+
\node[draw=none] at (8, 5-2) {H};
6352+
6353+
\draw(10, 5-2) -- (10, 3-2);
6354+
\draw[fill=black] (10, 5-2) circle (.2);
6355+
\draw[fill=black] (10, 3-2) circle (.2);
6356+
\draw(12, 5-2) -- (12, 3-4);
6357+
\draw[fill=black] (12, 5-2) circle (.2);
6358+
\draw[fill=black] (12, 3-4) circle (.2);
6359+
6360+
\draw[fill=white] (-1+8+6, 4-4) -- (-1+8+6, 6-4) -- (1+8+6, 6-4) -- (1+8+6,4-4) -- cycle;
6361+
\node[draw=none] at (8+6, 5-4) {H};
6362+
6363+
\draw(16, 5-2-2) -- (16, 3-4);
6364+
\draw[fill=black] (16, 5-2-2) circle (.2);
6365+
\draw[fill=black] (16, 3-4) circle (.2);
6366+
6367+
\draw[fill=white] (-1+8+6+4, 4-4-2) -- (-1+8+6+4, 6-4-2) -- (1+8+6+4, 6-4-2) -- (1+8+6+4,4-4-2) -- cycle;
6368+
\node[draw=none] at (8+6+4, 5-4-2) {H};
6369+
6370+
\draw (20, 5) -- (20, -1);
6371+
\draw (20 - .35, 5 + .35) -- (20 + .35, 5 - .35);
6372+
\draw (20 - .35, 5 - .35) -- (20 + .35, 5 + .35);
6373+
\draw (20 - .35, -1 + .35) -- (20 + .35, -1 - .35);
6374+
\draw (20 - .35, -1 - .35) -- (20 + .35, -1 + .35);
6375+
\draw (22, 3) -- (22, 1);
6376+
\draw (22 - .35, 3 + .35) -- (22 + .35, 3 - .35);
6377+
\draw (22 - .35, 3 - .35) -- (22 + .35, 3 + .35);
6378+
\draw (22 - .35, 1 + .35) -- (22 + .35, 1 - .35);
6379+
\draw (22 - .35, 1 - .35) -- (22 + .35, 1 + .35);
6380+
\end{tikzpicture}
6381+
\f]
6382+
* though is performed more efficiently.
6383+
*
6384+
* - If \p qureg is a state-vector, the output amplitudes are the discrete Fourier
6385+
* transform (DFT) of the input amplitudes, in the exact ordering. This is true
6386+
* even if \p qureg is unnormalised. \n
6387+
* Precisely,
6388+
* \f[
6389+
* \text{QFT} \, \left( \sum\limits_{x=0}^{2^N-1} \alpha_x |x\rangle \right)
6390+
* =
6391+
* \frac{1}{\sqrt{2^N}}
6392+
* \sum\limits_{x=0}^{2^N-1} \left(
6393+
* \sum\limits_{y=0}^{2^N-1} e^{2 \pi \, i \, x \, y / 2^N} \; \alpha_y
6394+
* \right) |x\rangle
6395+
* \f]
6396+
*
6397+
* - If \p qureg is a density matrix \f$\rho\f$, it will be changed under the unitary action
6398+
* of the QFT. This can be imagined as each mixed state-vector undergoing the DFT
6399+
* on its amplitudes. This is true even if \p qureg is unnormalised.
6400+
* \f[
6401+
* \rho \; \rightarrow \; \text{QFT} \; \rho \; \text{QFT}^{\dagger}
6402+
* \f]
6403+
*
6404+
* > This function merges contiguous controlled-phase gates into single invocations
6405+
* > of applyNamedPhaseFunc(), and hence is significantly faster than performing
6406+
* > the QFT circuit directly.
6407+
*
6408+
* > Furthermore, in distributed mode, this function requires only \f$\log_2(\text{\#nodes})\f$
6409+
* > rounds of pair-wise
6410+
* > communication, and hence is exponentially faster than directly performing the
6411+
* > DFT on the amplitudes of \p qureg.
6412+
*
6413+
* @see
6414+
* - applyQFT() to apply the QFT to a sub-register of \p qureg.
6415+
*
6416+
* @ingroup operator
6417+
* @param[in,out] qureg a state-vector or density matrix to modify
6418+
* @author Tyson Jones
6419+
*/
6420+
void applyFullQFT(Qureg qureg);
6421+
6422+
/** Applies the quantum Fourier transform (QFT) to a specific subset of qubits
6423+
* of the register \p qureg.
6424+
*
6425+
* The order of \p qubits affects the ultimate unitary.
6426+
* The canonical full-state QFT (applyFullQFT()) is achieved by targeting every
6427+
* qubit in increasing order.
6428+
*
6429+
* The effected unitary circuit (shown here for \p numQubits <b>= 4</b>) resembles
6430+
* \f[
6431+
\begin{tikzpicture}[scale=.5]
6432+
\draw (-2, 5) -- (23, 5); \node[draw=none] at (-4,5) {qubits[3]};
6433+
\draw (-2, 3) -- (23, 3); \node[draw=none] at (-4,3) {qubits[2]};
6434+
\draw (-2, 1) -- (23, 1); \node[draw=none] at (-4,1) {qubits[1]};
6435+
\draw (-2, -1) -- (23, -1); \node[draw=none] at (-4,-1) {qubits[0]};
6436+
6437+
\draw[fill=white] (-1, 4) -- (-1, 6) -- (1, 6) -- (1,4) -- cycle;
6438+
\node[draw=none] at (0, 5) {H};
6439+
6440+
\draw(2, 5) -- (2, 3);
6441+
\draw[fill=black] (2, 5) circle (.2);
6442+
\draw[fill=black] (2, 3) circle (.2);
6443+
\draw(4, 5) -- (4, 1);
6444+
\draw[fill=black] (4, 5) circle (.2);
6445+
\draw[fill=black] (4, 1) circle (.2);
6446+
\draw(6, 5) -- (6, -1);
6447+
\draw[fill=black] (6, 5) circle (.2);
6448+
\draw[fill=black] (6, -1) circle (.2);
6449+
6450+
\draw[fill=white] (-1+8, 4-2) -- (-1+8, 6-2) -- (1+8, 6-2) -- (1+8,4-2) -- cycle;
6451+
\node[draw=none] at (8, 5-2) {H};
6452+
6453+
\draw(10, 5-2) -- (10, 3-2);
6454+
\draw[fill=black] (10, 5-2) circle (.2);
6455+
\draw[fill=black] (10, 3-2) circle (.2);
6456+
\draw(12, 5-2) -- (12, 3-4);
6457+
\draw[fill=black] (12, 5-2) circle (.2);
6458+
\draw[fill=black] (12, 3-4) circle (.2);
6459+
6460+
\draw[fill=white] (-1+8+6, 4-4) -- (-1+8+6, 6-4) -- (1+8+6, 6-4) -- (1+8+6,4-4) -- cycle;
6461+
\node[draw=none] at (8+6, 5-4) {H};
6462+
6463+
\draw(16, 5-2-2) -- (16, 3-4);
6464+
\draw[fill=black] (16, 5-2-2) circle (.2);
6465+
\draw[fill=black] (16, 3-4) circle (.2);
6466+
6467+
\draw[fill=white] (-1+8+6+4, 4-4-2) -- (-1+8+6+4, 6-4-2) -- (1+8+6+4, 6-4-2) -- (1+8+6+4,4-4-2) -- cycle;
6468+
\node[draw=none] at (8+6+4, 5-4-2) {H};
6469+
6470+
\draw (20, 5) -- (20, -1);
6471+
\draw (20 - .35, 5 + .35) -- (20 + .35, 5 - .35);
6472+
\draw (20 - .35, 5 - .35) -- (20 + .35, 5 + .35);
6473+
\draw (20 - .35, -1 + .35) -- (20 + .35, -1 - .35);
6474+
\draw (20 - .35, -1 - .35) -- (20 + .35, -1 + .35);
6475+
\draw (22, 3) -- (22, 1);
6476+
\draw (22 - .35, 3 + .35) -- (22 + .35, 3 - .35);
6477+
\draw (22 - .35, 3 - .35) -- (22 + .35, 3 + .35);
6478+
\draw (22 - .35, 1 + .35) -- (22 + .35, 1 - .35);
6479+
\draw (22 - .35, 1 - .35) -- (22 + .35, 1 + .35);
6480+
\end{tikzpicture}
6481+
* \f]
6482+
* though is performed more efficiently.
6483+
*
6484+
* - If \p qureg is a state-vector, the output amplitudes are a kronecker product of
6485+
* the discrete Fourier transform (DFT) acting upon the targeted amplitudes, and the
6486+
* remaining. \n
6487+
* Precisely,
6488+
* - let \f$|x,r\rangle\f$ represent a computational basis state where
6489+
* \f$x\f$ is the binary value of the targeted qubits, and \f$r\f$ is the binary value
6490+
* of the remaining qubits.
6491+
* - let \f$|x_j,r_j\rangle\f$ be the \f$j\text{th}\f$ such state.
6492+
* - let \f$n =\f$ \p numQubits, and \f$N =\f$ `qureg.numQubitsRepresented`.\n
6493+
* Then, this function effects
6494+
* \f[
6495+
* (\text{QFT}\otimes 1) \, \left( \sum\limits_{j=0}^{2^N-1} \alpha_j \, |x_j,r_j\rangle \right)
6496+
* =
6497+
* \frac{1}{\sqrt{2^n}}
6498+
* \sum\limits_{j=0}^{2^N-1} \alpha_j \left(
6499+
* \sum\limits_{y=0}^{2^n-1} e^{2 \pi \, i \, x_j \, y / 2^N} \;
6500+
* |y,r_j \rangle
6501+
* \right)
6502+
* \f]
6503+
*
6504+
* - If \p qureg is a density matrix \f$\rho\f$, it will be changed under the unitary action
6505+
* of the QFT. This can be imagined as each mixed state-vector undergoing the DFT
6506+
* on its amplitudes. This is true even if \p qureg is unnormalised.
6507+
* \f[
6508+
* \rho \; \rightarrow \; \text{QFT} \; \rho \; \text{QFT}^{\dagger}
6509+
* \f]
6510+
*
6511+
* > This function merges contiguous controlled-phase gates into single invocations
6512+
* > of applyNamedPhaseFunc(), and hence is significantly faster than performing
6513+
* > the QFT circuit directly.
6514+
*
6515+
* > Furthermore, in distributed mode, this function requires only \f$\log_2(\text{\#nodes})\f$
6516+
* > rounds of pair-wise
6517+
* > communication, and hence is exponentially faster than directly performing the
6518+
* > DFT on the amplitudes of \p qureg.
6519+
*
6520+
* @see
6521+
* - applyFullQFT() to apply the QFT to the entirety of \p qureg.
6522+
*
6523+
* @ingroup operator
6524+
* @param[in,out] qureg a state-vector or density matrix to modify
6525+
* @param[in] qubits a list of the qubits to operate the QFT upon
6526+
* @param[in] numQubits the length of list \p qubits
6527+
* @throws invalidQuESTInputError()
6528+
* - if any qubit in \p qubits is invalid, i.e. outside <b>[0, </b>`qureg.numQubitsRepresented`<b>)</b>
6529+
* - if \p qubits contain any repetitions
6530+
* - if \p numQubits <b>< 1</b>
6531+
* - if \p numQubits <b>></b>`qureg.numQubitsRepresented`
6532+
* @throws segmentation-fault
6533+
* - if \p qubits contains fewer elements than \p numQubits
6534+
* @author Tyson Jones
6535+
*/
6536+
void applyQFT(Qureg qureg, int* qubits, int numQubits);
6537+
63286538
// end prevention of C++ name mangling
63296539
#ifdef __cplusplus
63306540
}

QuEST/src/QuEST.c

+22
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,28 @@ void applyParamNamedPhaseFuncOverrides(Qureg qureg, int* qubits, int* numQubitsP
864864
qasm_recordNamedPhaseFunc(qureg, qubits, numQubitsPerReg, numRegs, encoding, functionNameCode, params, numParams, overrideInds, overridePhases, numOverrides);
865865
}
866866

867+
void applyQFT(Qureg qureg, int* qubits, int numQubits) {
868+
validateMultiTargets(qureg, qubits, numQubits, __func__);
869+
870+
qasm_recordComment(qureg, "Beginning of QFT circuit");
871+
872+
agnostic_applyQFT(qureg, qubits, numQubits);
873+
874+
qasm_recordComment(qureg, "End of QFT circuit");
875+
}
876+
877+
void applyFullQFT(Qureg qureg) {
878+
879+
qasm_recordComment(qureg, "Beginning of QFT circuit");
880+
881+
int qubits[qureg.numQubitsRepresented];
882+
for (int i=0; i<qureg.numQubitsRepresented; i++)
883+
qubits[i] = i;
884+
agnostic_applyQFT(qureg, qubits, qureg.numQubitsRepresented);
885+
886+
qasm_recordComment(qureg, "End of QFT circuit");
887+
}
888+
867889

868890

869891
/*

QuEST/src/QuEST_common.c

+70
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@
3636
# include <stdlib.h>
3737

3838

39+
// expose PI on GPU build
40+
#ifndef M_PI
41+
#define M_PI 3.1415926535897932384626433832795028841971
42+
#endif
43+
44+
3945
#ifdef __cplusplus
4046
extern "C" {
4147
#endif
@@ -827,6 +833,70 @@ void agnostic_applyTrotterCircuit(Qureg qureg, PauliHamil hamil, qreal time, int
827833
applySymmetrizedTrotterCircuit(qureg, hamil, time/reps, order);
828834
}
829835

836+
void agnostic_applyQFT(Qureg qureg, int* qubits, int numQubits) {
837+
838+
int densShift = qureg.numQubitsRepresented;
839+
840+
// start with top/left-most qubit, work down
841+
for (int q=numQubits-1; q >= 0; q--) {
842+
843+
// H
844+
statevec_hadamard(qureg, qubits[q]);
845+
if (qureg.isDensityMatrix)
846+
statevec_hadamard(qureg, qubits[q] + densShift);
847+
qasm_recordGate(qureg, GATE_HADAMARD, qubits[q]);
848+
849+
if (q == 0)
850+
break;
851+
852+
// succession of C-phases, control on qubits[q], targeting each of
853+
// qubits[q-1], qubits[q-1], ... qubits[0]. This can be expressed by
854+
// a single invocation of applyNamedPhaseFunc product
855+
856+
int numRegs = 2;
857+
int numQubitsPerReg[2] = {q, 1};
858+
int regs[q+1];
859+
for (int i=0; i<q+1; i++)
860+
regs[i] = qubits[i]; // qubits[q] is in own register
861+
862+
int numParams = 1;
863+
qreal params[1] = { M_PI / (1 << q) };
864+
865+
int conj = 0;
866+
statevec_applyParamNamedPhaseFuncOverrides(
867+
qureg, regs, numQubitsPerReg, numRegs,
868+
UNSIGNED, SCALED_PRODUCT, params, numParams,
869+
NULL, NULL, 0,
870+
conj);
871+
if (qureg.isDensityMatrix) {
872+
conj = 1;
873+
shiftSubregIndices(regs, numQubitsPerReg, numRegs, densShift);
874+
statevec_applyParamNamedPhaseFuncOverrides(
875+
qureg, regs, numQubitsPerReg, numRegs,
876+
UNSIGNED, SCALED_PRODUCT, params, numParams,
877+
NULL, NULL, 0,
878+
conj);
879+
shiftSubregIndices(regs, numQubitsPerReg, numRegs, - densShift);
880+
}
881+
qasm_recordNamedPhaseFunc(
882+
qureg, regs, numQubitsPerReg, numRegs,
883+
UNSIGNED, SCALED_PRODUCT, params, numParams,
884+
NULL, NULL, 0);
885+
}
886+
887+
// final swaps
888+
for (int i=0; i<(numQubits/2); i++) {
889+
890+
int qb1 = qubits[i];
891+
int qb2 = qubits[numQubits-i-1];
892+
893+
statevec_swapQubitAmps(qureg, qb1, qb2);
894+
if (qureg.isDensityMatrix)
895+
statevec_swapQubitAmps(qureg, qb1 + densShift, qb2 + densShift);
896+
qasm_recordControlledGate(qureg, GATE_SWAP, qb1, qb2);
897+
}
898+
}
899+
830900
#ifdef __cplusplus
831901
}
832902
#endif

QuEST/src/QuEST_internal.h

+2
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,8 @@ void statevec_applyParamNamedPhaseFuncOverrides(Qureg qureg, int* qubits, int* n
279279

280280
void agnostic_applyTrotterCircuit(Qureg qureg, PauliHamil hamil, qreal time, int order, int reps);
281281

282+
void agnostic_applyQFT(Qureg qureg, int* qubits, int numQubits);
283+
282284
DiagonalOp agnostic_createDiagonalOp(int numQubits, QuESTEnv env);
283285

284286
void agnostic_destroyDiagonalOp(DiagonalOp op);

0 commit comments

Comments
 (0)