Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 25 additions & 21 deletions quest/src/core/accelerator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,36 @@
#include "quest/include/qureg.h"
#include "quest/include/matrices.h"

#include "quest/src/core/inliner.hpp"
#include "quest/src/core/lists.hpp"

#include <vector>


/*
* COMPILE-TIME VARIABLES
*
* used by cpu_subroutines.cpp and gpu_subroutines to attemptedly set
* a variable to a value known at compile-time (like a templated function's
* parameter), enabling compile-time optimisations of subsequent code which
* uses the variable such a loop unrolling. If the value is not known at
* compile-time (CompileTimeValue==-1 which indicates a templated function has
* been called with more qubits than it has been explicitly instantiated and
* optimised for), the runtime value is used, precluding optimisations.
*
* TODO: this trick is likely not presently working in (stoopid) MSVC compilers!
*/

template<int CompileTimeValue>
INLINE constexpr int accel_tryUseCompileTimeValue(int runtimeValue) {

if constexpr (CompileTimeValue == -1)
return runtimeValue;
else
return CompileTimeValue;
}


/*
* TEMPLATE INSTANTIATION MACROS
*
Expand Down Expand Up @@ -124,27 +149,6 @@
template returntype funcname <-1,numtargs, conj, haspower> args;



/*
* COMPILE-TIME VARIABLE MACROS
*
* used by cpu_subroutines.cpp and gpu_subroutines to attemptedly set
* a variable to a value known at compile-time (like a templated function's
* parameter), enabling compile-time optimisations of subsequent code which
* uses the variable such a loop unrolling. If the value is not known at
* compile-time (compileval==-1 which indicates a templated function has
* been called with more qubits than it has been explicitly instantiated and
* optimised for), the runtime value is used, precluding optimisations.
*/

#define SET_VAR_AT_COMPILE_TIME(type, name, compileval, runtimeval) \
type name; \
if constexpr (compileval == -1) \
name = runtimeval; \
else \
name = compileval;


/*
* GETTERS
*/
Expand Down
60 changes: 27 additions & 33 deletions quest/src/cpu/cpu_subroutines.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ qindex cpu_statevec_packAmpsIntoBuffer(Qureg qureg, ConstList64 qubitInds, Const
auto qubitStateMask = util_getBitMask(qubitInds, qubitStates);

// use template param to compile-time unroll loop in insertBits()
SET_VAR_AT_COMPILE_TIME(int, numBits, NumQubits, qubitInds.size());
int numBits = accel_tryUseCompileTimeValue<NumQubits>(qubitInds.size());

#pragma omp parallel for if(qureg.isMultithreaded)
for (qindex n=0; n<numIts; n++) {
Expand Down Expand Up @@ -309,8 +309,7 @@ void cpu_statevec_anyCtrlSwap_subA(Qureg qureg, ConstList64 ctrls, ConstList64 c
auto qubitStateMask = util_getBitMask(ctrls, ctrlStates, {targ2, targ1}, {0, 1});

// use template param to compile-time unroll loop in insertBits()
SET_VAR_AT_COMPILE_TIME(int, numCtrlBits, NumCtrls, ctrls.size());
int numQubitBits = numCtrlBits + 2;
int numQubitBits = 2 + accel_tryUseCompileTimeValue<NumCtrls>(ctrls.size());

#pragma omp parallel for if(qureg.isMultithreaded)
for (qindex n=0; n<numIts; n++) {
Expand Down Expand Up @@ -344,7 +343,7 @@ void cpu_statevec_anyCtrlSwap_subB(Qureg qureg, ConstList64 ctrls, ConstList64 c
auto ctrlStateMask = util_getBitMask(ctrls, ctrlStates);

// use template param to compile-time unroll loop in insertBits()
SET_VAR_AT_COMPILE_TIME(int, numCtrlBits, NumCtrls, ctrls.size());
int numCtrlBits = accel_tryUseCompileTimeValue<NumCtrls>(ctrls.size());

#pragma omp parallel for if(qureg.isMultithreaded)
for (qindex n=0; n<numIts; n++) {
Expand Down Expand Up @@ -381,8 +380,7 @@ void cpu_statevec_anyCtrlSwap_subC(Qureg qureg, ConstList64 ctrls, ConstList64 c
auto qubitStateMask = util_getBitMask(ctrls, ctrlStates, {targ}, {targState});

// use template param to compile-time unroll loop in insertBits()
SET_VAR_AT_COMPILE_TIME(int, numCtrlBits, NumCtrls, ctrls.size());
int numQubitBits = numCtrlBits + 1;
int numQubitBits = 1 + accel_tryUseCompileTimeValue<NumCtrls>(ctrls.size());

#pragma omp parallel for if(qureg.isMultithreaded)
for (qindex n=0; n<numIts; n++) {
Expand Down Expand Up @@ -427,8 +425,7 @@ void cpu_statevec_anyCtrlOneTargDenseMatr_subA(Qureg qureg, ConstList64 ctrls, C
auto qubitStateMask = util_getBitMask(ctrls, ctrlStates, {targ}, {0});

// use template param to compile-time unroll loop in insertBits()
SET_VAR_AT_COMPILE_TIME(int, numCtrlBits, NumCtrls, ctrls.size());
int numQubitBits = numCtrlBits + 1;
int numQubitBits = 1 + accel_tryUseCompileTimeValue<NumCtrls>(ctrls.size());

#pragma omp parallel for if(qureg.isMultithreaded)
for (qindex n=0; n<numIts; n++) {
Expand Down Expand Up @@ -469,7 +466,7 @@ void cpu_statevec_anyCtrlOneTargDenseMatr_subB(Qureg qureg, ConstList64 ctrls, C
auto ctrlStateMask = util_getBitMask(ctrls, ctrlStates);

// use template param to compile-time unroll loop in insertBits()
SET_VAR_AT_COMPILE_TIME(int, numCtrlBits, NumCtrls, ctrls.size());
int numCtrlBits = accel_tryUseCompileTimeValue<NumCtrls>(ctrls.size());

#pragma omp parallel for if(qureg.isMultithreaded)
for (qindex n=0; n<numIts; n++) {
Expand Down Expand Up @@ -512,8 +509,7 @@ void cpu_statevec_anyCtrlTwoTargDenseMatr_sub(Qureg qureg, ConstList64 ctrls, Co
auto qubitStateMask = util_getBitMask(ctrls, ctrlStates, {targ1, targ2}, {0, 0});

// use template param to compile-time unroll loop in insertBits()
SET_VAR_AT_COMPILE_TIME(int, numCtrlBits, NumCtrls, ctrls.size());
int numQubitBits = numCtrlBits + 2;
int numQubitBits = 2 + accel_tryUseCompileTimeValue<NumCtrls>(ctrls.size());

#pragma omp parallel for if(qureg.isMultithreaded)
for (qindex n=0; n<numIts; n++) {
Expand Down Expand Up @@ -585,12 +581,10 @@ void cpu_statevec_anyCtrlAnyTargDenseMatr_sub(Qureg qureg, ConstList64 ctrls, Co
auto qubitStateMask = util_getBitMask(ctrls, ctrlStates, targs, util_getConstantList(0,targs.size()));

// attempt to use compile-time variables to automatically optimise/unroll dependent loops
SET_VAR_AT_COMPILE_TIME(int, numCtrlBits, NumCtrls, ctrls.size());
SET_VAR_AT_COMPILE_TIME(int, numTargBits, NumTargs, targs.size());

// compiler will infer these at compile-time if possible
int numCtrlBits = accel_tryUseCompileTimeValue<NumCtrls>(ctrls.size());
int numTargBits = accel_tryUseCompileTimeValue<NumTargs>(targs.size());
int numQubitBits = numCtrlBits + numTargBits;
qindex numTargAmps = powerOf2(numTargBits);
auto numTargAmps = powerOf2(numTargBits);

// create an explicit parallel region to avoid re-initialisation of vectors every iteration
#pragma omp parallel if(qureg.isMultithreaded)
Expand Down Expand Up @@ -678,7 +672,7 @@ void cpu_statevec_anyCtrlOneTargDiagMatr_sub(Qureg qureg, ConstList64 ctrls, Con
auto ctrlStateMask = util_getBitMask(ctrls, ctrlStates);

// use template params to compile-time unroll loops in insertBits()
SET_VAR_AT_COMPILE_TIME(int, numCtrlBits, NumCtrls, ctrls.size());
int numCtrlBits = accel_tryUseCompileTimeValue<NumCtrls>(ctrls.size());

#pragma omp parallel for if(qureg.isMultithreaded)
for (qindex n=0; n<numIts; n++) {
Expand Down Expand Up @@ -721,7 +715,7 @@ void cpu_statevec_anyCtrlTwoTargDiagMatr_sub(Qureg qureg, ConstList64 ctrls, Con
auto ctrlStateMask = util_getBitMask(ctrls, ctrlStates);

// use template params to compile-time unroll loops in insertBits()
SET_VAR_AT_COMPILE_TIME(int, numCtrlBits, NumCtrls, ctrls.size());
int numCtrlBits = accel_tryUseCompileTimeValue<NumCtrls>(ctrls.size());

#pragma omp parallel for if(qureg.isMultithreaded)
for (qindex n=0; n<numIts; n++) {
Expand Down Expand Up @@ -771,8 +765,8 @@ void cpu_statevec_anyCtrlAnyTargDiagMatr_sub(Qureg qureg, ConstList64 ctrls, Con
const bool areTargsSorted = util_isSorted(targs);

// use template params to compile-time unroll loops in insertBits() and getValueOfBits()
SET_VAR_AT_COMPILE_TIME(int, numCtrlBits, NumCtrls, ctrls.size());
SET_VAR_AT_COMPILE_TIME(int, numTargBits, NumTargs, targs.size());
int numCtrlBits = accel_tryUseCompileTimeValue<NumCtrls>(ctrls.size());
int numTargBits = accel_tryUseCompileTimeValue<NumTargs>(targs.size());

#pragma omp parallel for if(qureg.isMultithreaded)
for (qindex n=0; n<numIts; n++) {
Expand Down Expand Up @@ -957,7 +951,7 @@ INLINE void applyPauliUponAmpPair(
// also being compiled (through the tests)... Hours of my life forever lost!

// remind compiler when NumTargs is compile-time to unroll loop in setBits()
SET_VAR_AT_COMPILE_TIME(int, numTargBits, NumTargs, numXY);
int numTargBits = accel_tryUseCompileTimeValue<NumTargs>(numXY);

// iA = nth local index where targs have value m, iB = (last - nth) such index
qindex iA = setBits(i0, indXY, numTargBits, v);
Expand Down Expand Up @@ -1004,12 +998,12 @@ void cpu_statevector_anyCtrlPauliTensorOrGadget_subA(
auto maskYZ = util_getBitMask(util_getConcatenated(y, z));

// use template params to compile-time unroll loops in insertBits() and inner-loop below
SET_VAR_AT_COMPILE_TIME(int, numCtrlBits, NumCtrls, ctrls.size());
SET_VAR_AT_COMPILE_TIME(int, numTargBits, NumTargs, sortedTargsXY.size());
int numCtrlBits = accel_tryUseCompileTimeValue<NumCtrls>(ctrls.size());
int numTargBits = accel_tryUseCompileTimeValue<NumTargs>(sortedTargsXY.size());
int numQubitBits = numCtrlBits + numTargBits;

// each outer iteration handles all assignments of the target qubits, and each ctrl halves the outer iterations
qindex numOuterIts = qureg.numAmpsPerNode / powerOf2(numCtrlBits + numTargBits);
qindex numOuterIts = qureg.numAmpsPerNode / powerOf2(numQubitBits);

// each inner iteration modifies 2 amplitudes (may be compile-time sized)
qindex numInnerIts = powerOf2(numTargBits) / 2; // divides evenly
Expand Down Expand Up @@ -1088,7 +1082,7 @@ void cpu_statevector_anyCtrlPauliTensorOrGadget_subB(
auto maskYZ = util_getBitMask(util_getConcatenated(y, z));

// use template param to compile-time unroll loop in insertBits()
SET_VAR_AT_COMPILE_TIME(int, numCtrlBits, NumCtrls, ctrls.size());
int numCtrlBits = accel_tryUseCompileTimeValue<NumCtrls>(ctrls.size());

#pragma omp parallel for if(qureg.isMultithreaded)
for (qindex n=0; n<numIts; n++) {
Expand Down Expand Up @@ -1139,7 +1133,7 @@ void cpu_statevector_anyCtrlAnyTargZOrPhaseGadget_sub(
auto targIndMask = util_getBitMask(targs);

// use template param to compile-time unroll loop in insertBits()
SET_VAR_AT_COMPILE_TIME(int, numCtrlBits, NumCtrls, ctrls.size());
int numCtrlBits = accel_tryUseCompileTimeValue<NumCtrls>(ctrls.size());

#pragma omp parallel for if(qureg.isMultithreaded)
for (qindex n=0; n<numIts; n++) {
Expand Down Expand Up @@ -1173,7 +1167,7 @@ void cpu_statevec_setQuregToWeightedSum_sub(Qureg outQureg, vector<qcomp> coeffs
qindex numIts = outQureg.numAmpsPerNode;

// use template param to compile-time unroll inner loop below
SET_VAR_AT_COMPILE_TIME(int, numQuregs, NumQuregs, inQuregs.size());
int numQuregs = accel_tryUseCompileTimeValue<NumQuregs>(inQuregs.size());

#pragma omp parallel for if(outQureg.isMultithreaded)
for (qindex n=0; n<numIts; n++) {
Expand Down Expand Up @@ -1888,10 +1882,10 @@ void cpu_densmatr_partialTrace_sub(Qureg inQureg, Qureg outQureg, ConstList64 ta
auto allTargsSorted = util_getSorted(targs, pairTargs);

// use template param to compile-time unroll below loops
SET_VAR_AT_COMPILE_TIME(int, numTargPairs, NumTargs, targs.size());
int numTargPairs = accel_tryUseCompileTimeValue<NumTargs>(targs.size());

// may be inferred at compile-time
int numAllTargs = 2*numTargPairs;
int numAllTargs = 2 * numTargPairs;
qindex numInnerIts = powerOf2(numTargPairs);

/// @todo
Expand Down Expand Up @@ -2027,7 +2021,7 @@ qreal cpu_statevec_calcProbOfMultiQubitOutcome_sub(Qureg qureg, ConstList64 qubi
auto qubitStateMask = util_getBitMask(qubits, outcomes);

// use template param to compile-time unroll loop in insertBits()
SET_VAR_AT_COMPILE_TIME(int, numBits, NumQubits, qubits.size());
int numBits = accel_tryUseCompileTimeValue<NumQubits>(qubits.size());

#pragma omp parallel for reduction(+:prob) if(qureg.isMultithreaded)
for (qindex n=0; n<numIts; n++) {
Expand Down Expand Up @@ -2065,7 +2059,7 @@ qreal cpu_densmatr_calcProbOfMultiQubitOutcome_sub(Qureg qureg, ConstList64 qubi
auto qubitStateMask = util_getBitMask(qubits, outcomes);

// use template param to compile-time unroll loop in insertBits()
SET_VAR_AT_COMPILE_TIME(int, numBits, NumQubits, qubits.size());
int numBits = accel_tryUseCompileTimeValue<NumQubits>(qubits.size());

#pragma omp parallel for reduction(+:prob) if(qureg.isMultithreaded)
for (qindex n=0; n<numIts; n++) {
Expand Down Expand Up @@ -2099,7 +2093,7 @@ void cpu_statevec_calcProbsOfAllMultiQubitOutcomes_sub(qreal* outProbs, Qureg qu
const bool areQubitsSorted = util_isSorted(qubits);

// use template param to compile-time unroll loop in getValueOfBits()
SET_VAR_AT_COMPILE_TIME(int, numBits, NumQubits, qubits.size());
int numBits = accel_tryUseCompileTimeValue<NumQubits>(qubits.size());
qindex numOutcomes = powerOf2(numBits);

// decide whether to parallelise below amp-clearing, since outProbs ~ dim of a qureg
Expand Down Expand Up @@ -2146,7 +2140,7 @@ void cpu_densmatr_calcProbsOfAllMultiQubitOutcomes_sub(qreal* outProbs, Qureg qu
const bool areQubitsSorted = util_isSorted(qubits);

// use template param to compile-time unroll loop in getValueOfBits()
SET_VAR_AT_COMPILE_TIME(int, numBits, NumQubits, qubits.size());
int numBits = accel_tryUseCompileTimeValue<NumQubits>(qubits.size());
qindex numOutcomes = powerOf2(numBits);

// decide whether to parallelise below amp-clearing, since outProbs ~ dim of a qureg
Expand Down
Loading
Loading