Precision-agnostic types and data structures for representing numbers, quantum states, and operators. More...

Data Structures

struct  Complex
 Represents one complex number. More...
 
struct  ComplexMatrix2
 Represents a 2x2 matrix of complex numbers. More...
 
struct  ComplexMatrix4
 Represents a 4x4 matrix of complex numbers. More...
 
struct  ComplexMatrixN
 Represents a general 2^N by 2^N matrix of complex numbers. More...
 
struct  DiagonalOp
 Represents a diagonal complex operator on the full Hilbert state of a Qureg. More...
 
struct  PauliHamil
 Represents a weighted sum of pauli products. More...
 
struct  QuESTEnv
 Information about the environment the program is running in. More...
 
struct  Qureg
 Represents a system of qubits. More...
 
struct  Vector
 Represents a 3-vector of real numbers. More...
 

Macros

#define fromComplex(comp)   qcomp(comp.real, comp.imag)
 
#define getStaticComplexMatrixN(numQubits, re, im)
 Creates a ComplexMatrixN struct which lives in the stack and so does not need freeing, but cannot be returned beyond the calling scope. More...
 
#define qcomp
 
#define qreal   double
 
#define QuEST_PREC   2
 
#define toComplex(scalar)   ((Complex) {.real = creal(scalar), .imag = cimag(scalar)})
 

Enumerations

enum  pauliOpType { PAULI_I =0, PAULI_X =1, PAULI_Y =2, PAULI_Z =3 }
 Codes for specifying Pauli operators. More...
 

Functions

Qureg createCloneQureg (Qureg qureg, QuESTEnv env)
 Create a new Qureg which is an exact clone of the passed qureg, which can be either a statevector or a density matrix. More...
 
ComplexMatrixN createComplexMatrixN (int numQubits)
 Create (dynamically) a square complex matrix which can be passed to the multi-qubit general unitary functions. More...
 
Qureg createDensityQureg (int numQubits, QuESTEnv env)
 Create a Qureg for qubits which are represented by a density matrix, and can be in mixed states. More...
 
DiagonalOp createDiagonalOp (int numQubits, QuESTEnv env)
 Creates a DiagonalOp representing a diagonal operator on the full Hilbert space of a Qureg. More...
 
PauliHamil createPauliHamil (int numQubits, int numSumTerms)
 Create a PauliHamil instance, which is a Hamiltonian expressed as a real-weighted sum of products of Pauli operators. More...
 
PauliHamil createPauliHamilFromFile (char *fn)
 Create a PauliHamil instance, a real-weighted sum of products of Pauli operators, populated with the data in filename fn. More...
 
QuESTEnv createQuESTEnv (void)
 Create the QuEST execution environment. More...
 
Qureg createQureg (int numQubits, QuESTEnv env)
 Create a Qureg object representing a set of qubits which will remain in a pure state. More...
 
void destroyComplexMatrixN (ComplexMatrixN matr)
 Destroy a ComplexMatrixN instance created with createComplexMatrixN() More...
 
void destroyDiagonalOp (DiagonalOp op, QuESTEnv env)
 Destroys a DiagonalOp created with createDiagonalOp(), freeing its memory. More...
 
void destroyPauliHamil (PauliHamil hamil)
 Destroy a PauliHamil instance, created with either createPauliHamil() or createPauliHamilFromFile(). More...
 
void destroyQuESTEnv (QuESTEnv env)
 Destroy the QuEST environment. More...
 
void destroyQureg (Qureg qureg, QuESTEnv env)
 Deallocate a Qureg object representing a set of qubits. More...
 
void initComplexMatrixN (ComplexMatrixN m, qreal real[][1<< m.numQubits], qreal imag[][1<< m.numQubits])
 Initialises a ComplexMatrixN instance to have the passed real and imag values. More...
 
void initDiagonalOp (DiagonalOp op, qreal *real, qreal *imag)
 Updates the entire DiagonalOp op with the given elements, of which there must be 2^op.numQubits. More...
 
void initPauliHamil (PauliHamil hamil, qreal *coeffs, enum pauliOpType *codes)
 Initialise a PauliHamil instance with the given term coefficients and Pauli codes (one for every qubit in every term). More...
 
void setDiagonalOpElems (DiagonalOp op, long long int startInd, qreal *real, qreal *imag, long long int numElems)
 Modifies a subset (starting at index startInd) of the elements in DiagonalOp op with the given elements, of which there are numElems. More...
 
void syncDiagonalOp (DiagonalOp op)
 Copy the elements in DiagonalOp op.real and op.imag to the persisent GPU memory. More...
 

Detailed Description

Precision-agnostic types and data structures for representing numbers, quantum states, and operators.

Macro Definition Documentation

◆ fromComplex

#define fromComplex (   comp)    qcomp(comp.real, comp.imag)

Converts a Complex struct to a qcomp native type

Author
Tyson Jones

◆ getStaticComplexMatrixN

#define getStaticComplexMatrixN (   numQubits,
  re,
  im 
)
Value:
numQubits, \
(qreal[1<<numQubits][1<<numQubits]) UNPACK_ARR re, \
(qreal[1<<numQubits][1<<numQubits]) UNPACK_ARR im, \
(double*[1<<numQubits]) {NULL}, (double*[1<<numQubits]) {NULL} \
)

Creates a ComplexMatrixN struct which lives in the stack and so does not need freeing, but cannot be returned beyond the calling scope.

That is, the .real and .imag arrays of the returned ComplexMatrixN live in the stack as opposed to that returned by createComplexMatrixN() (which live in the heap). Note the real and imag components must be wrapped in paranthesis, e.g.

ComplexMatrixN u = getStaticComplexMatrixN(1, ({{1,2},{3,4}}), ({{0}}));

Here is an example of an incorrect usage, since a 'local' ComplexMatrixN cannot leave the calling scope (otherwise inducing dangling pointers):

ComplexMatrixN getMyMatrix(void) {
    return getStaticComplexMatrixN(1, ({{1,2},{3,4}}), ({{0}}));
}

This function is actually a single-line anonymous macro, so can be safely invoked within arguments to other functions, e.g.

 multiQubitUnitary(
     qureg, (int[]) {0}, 1, 
     getStaticComplexMatrixN(1, ({{1,0},{0,1}}), ({{0}}))
 );

The returned ComplexMatrixN can be accessed and modified in the same way as that returned by createComplexMatrixN(), e.g.

 ComplexMatrixN u = getStaticComplexMatrixN(3, ({{0}}), ({{0}}));
 for (int i=0; i<8; i++)
     for (int j=0; j<8; j++)
         u.real[i][j] = .1;

Note that the first argument numQubits must be a literal.

This macro is only callable in C, since it invokes the function bindArraysToStackComplexMatrixN() which is only callable in C.

Author
Tyson Jones

Definition at line 3910 of file QuEST.h.

◆ qcomp

#define qcomp

A precision-agnostic operator-overloaded complex number type.
This is a complex analog of qreal and is of single, double or quad precision depending on the value of QuEST_PREC. It resolves to the native complex type provided by <complex.h> for both C99 and C++11, so can be used with operators. It can be constructed with qcomp(real, imag).

For example, in C,

qcomp x = 2 + 3i;
x -= 3.2*x;

and in C++,

qcomp x = qcomp(2, 3);
x -= 3*x;

Assuming QuEST_PREC=4, qcomp will be 'complex long double' in C and 'complex<long double>' in C++.

Can be converted to/from Complex, the struct accepted by the QuEST interface, using toComplex and fromComplex.

Authors
Randy Meyers and Dr. Thomas Plum (created C & C++ agnosticism)
Author
Tyson Jones (created precision agnosticism)

◆ qreal

#define qreal   double

A precision-agnostic floating point number, as determined by QuEST_PREC. Is a single, double or quad precision float when QuEST_PREC is 1, 2 or 4 respectively.

Author
Ania Brown
Tyson Jones (doc)

◆ QuEST_PREC

#define QuEST_PREC   2

Sets the precision of qreal and qcomp, and generally that of the state-vectors stored by QuEST. QuEST_PREC can be 1, 2 or 4 for single, double and quad precision - requires 4, 8 and 16 bytes per real & imag component per amplitude of the statevector respectively. This should be passed as a macro to the preprocessor during compilation, which overwrites the value explicitly defined in QuEST_precision.h. Note that quad precision is not compatible with most GPUs.

Author
Ania Brown
Tyson Jones (doc)

◆ toComplex

#define toComplex (   scalar)    ((Complex) {.real = creal(scalar), .imag = cimag(scalar)})

Creates a Complex struct, which can be passed to the QuEST API, from a qcomp

Author
Tyson Jones

Enumeration Type Documentation

◆ pauliOpType

Codes for specifying Pauli operators.

Author
Tyson Jones
Enumerator
PAULI_I 
PAULI_X 
PAULI_Y 
PAULI_Z 

Definition at line 96 of file QuEST.h.

96 {PAULI_I=0, PAULI_X=1, PAULI_Y=2, PAULI_Z=3};

Function Documentation

◆ createCloneQureg()

Qureg createCloneQureg ( Qureg  qureg,
QuESTEnv  env 
)

Create a new Qureg which is an exact clone of the passed qureg, which can be either a statevector or a density matrix.

That is, it will have the same dimensions as the passed qureg and begin in an identical quantum state. This must be destroyed by the user later with destroyQureg()

Returns
an object representing the set of qubits
Parameters
[in]quregan existing qureg to be cloned
[in]envobject representing the execution environment (local, multinode etc)
Author
Tyson Jones

Definition at line 64 of file QuEST.c.

64  {
65 
66  Qureg newQureg;
67  statevec_createQureg(&newQureg, qureg.numQubitsInStateVec, env);
68  newQureg.isDensityMatrix = qureg.isDensityMatrix;
71 
72  qasm_setup(&newQureg);
73  statevec_cloneQureg(newQureg, qureg);
74  return newQureg;
75 }

References Qureg::isDensityMatrix, Qureg::numQubitsInStateVec, Qureg::numQubitsRepresented, qasm_setup(), statevec_cloneQureg(), and statevec_createQureg().

Referenced by TEST_CASE().

◆ createComplexMatrixN()

ComplexMatrixN createComplexMatrixN ( int  numQubits)

Create (dynamically) a square complex matrix which can be passed to the multi-qubit general unitary functions.

The matrix will have dimensions (2^numQubits) by (2^numQubits), and all elements of .real and .imag are initialised to zero. The ComplexMatrixN must eventually be freed using destroyComplexMatrixN(). Like ComplexMatrix2 and ComplexMatrix4, the returned ComplexMatrixN is safe to return from functions.

One can instead use getStaticComplexMatrixN() to create a ComplexMatrixN struct in the stack (which doesn't need to be later destroyed).

Parameters
[in]numQubitsthe number of qubits of which the returned ComplexMatrixN will correspond
Returns
a dynamic ComplexMatrixN struct, that is one where the .real and .imag fields are arrays kept in the heap and must be later destroyed.
Author
Tyson Jones

Definition at line 1099 of file QuEST.c.

1099  {
1100  validateNumQubitsInMatrix(numQubits, __func__);
1101 
1102  int numRows = 1 << numQubits;
1103 
1104  ComplexMatrixN m = {
1105  .numQubits = numQubits,
1106  .real = malloc(numRows * sizeof *m.real),
1107  .imag = malloc(numRows * sizeof *m.imag)};
1108 
1109  for (int n=0; n < 1<<numQubits; n++) {
1110  m.real[n] = calloc(numRows, sizeof **m.real);
1111  m.imag[n] = calloc(numRows, sizeof **m.imag);
1112  }
1113 
1114  // error if the ComplexMatrixN was not successfully malloc'ds
1115  validateMatrixInit(m, __func__);
1116 
1117  return m;
1118  }

References ComplexMatrixN::imag, ComplexMatrixN::numQubits, ComplexMatrixN::real, validateMatrixInit(), and validateNumQubitsInMatrix().

Referenced by densmatr_mixMultiQubitKrausMap(), and TEST_CASE().

◆ createDensityQureg()

Qureg createDensityQureg ( int  numQubits,
QuESTEnv  env 
)

Create a Qureg for qubits which are represented by a density matrix, and can be in mixed states.

Allocates space for a density matrix of probability amplitudes, including space for temporary values to be copied from one other chunk if running the distributed version. Define properties related to the size of the set of qubits. initZeroState is automatically called allocation, so that the density qureg begins in the zero state |0><0|.

Returns
an object representing the set of qubits
Parameters
[in]numQubitsnumber of qubits in the system
[in]envobject representing the execution environment (local, multinode etc)
Exceptions
invalidQuESTInputErrorif numQubits <= 0, or if numQubits is so large that the number of amplitudes cannot fit in a long long int type, or if in distributed mode, there are more nodes than elements in the would-be density-matrix
Author
Tyson Jones

Definition at line 50 of file QuEST.c.

50  {
51  validateNumQubitsInQureg(2*numQubits, env.numRanks, __func__);
52 
53  Qureg qureg;
54  statevec_createQureg(&qureg, 2*numQubits, env);
55  qureg.isDensityMatrix = 1;
56  qureg.numQubitsRepresented = numQubits;
57  qureg.numQubitsInStateVec = 2*numQubits;
58 
59  qasm_setup(&qureg);
60  initZeroState(qureg); // safe call to public function
61  return qureg;
62 }

References initZeroState(), Qureg::isDensityMatrix, Qureg::numQubitsInStateVec, Qureg::numQubitsRepresented, QuESTEnv::numRanks, qasm_setup(), statevec_createQureg(), and validateNumQubitsInQureg().

Referenced by TEST_CASE().

◆ createDiagonalOp()

DiagonalOp createDiagonalOp ( int  numQubits,
QuESTEnv  env 
)

Creates a DiagonalOp representing a diagonal operator on the full Hilbert space of a Qureg.

This can only be applied to state-vectors or density matrices of an equal number of qubits, using applyDiagonalOp(). There is no requirement that the operator is unitary or Hermitian - any complex operator is allowed.

The operator is initialised to all zero.

This function allocates space for 2^numQubits complex amplitudes, which are initially zero. This is the same cost as a state-vector of equal size. The elements should be modified with setDiagonalOpElems(). This memory must later be freed with destroyDiagonalOp().

In GPU mode, this function also creates persistent memory on the GPU. Hence, if not using setDiagonalOpElems() and instead modifying operator.real and .imag directly, the user must call thereafter call syncDiagonalOp() to modify the operator stored in the GPU.

In distributed mode, the memory for the diagonal operator is spread evenly between the available nodes, such that each node contains only operator.numElemsPerChunk complex values. Users must therefore exercise care in modifying .real and .imag directly, and should instead use initDiagonalOp(). E.g. the following is valid code when when distributed between TWO nodes:

// create {1,2,3,4,5,6,7,8, 9,10,11,12,13,14,15,16}
DiagonalOp op = createDiagonalOp(4, env); // 16 amplitudes total
for (int i=0; i<8; i++) {
    if (env.rank == 0)
        op.real[i] = (i+1);
    if (env.rank == 1)
        op.real[i] = (i+1+8);
}
Returns
a DiagonalOp instance, with 2^n-length .real and .imag arrays, initialised to zero
Parameters
[in]numQubitsnumber of qubit, informing the dimension of the operator.
[in]envobject representing the execution environment (local, multinode etc)
Exceptions
invalidQuESTInputErrorif numQubits <= 0, or if numQubits is so large that the number of elements cannot fit in a long long int type, or if in distributed mode, there are more nodes than elements in the operator
Author
Tyson Jones

Definition at line 1267 of file QuEST.c.

1267  {
1268  validateNumQubitsInDiagOp(numQubits, env.numRanks, __func__);
1269 
1270  return agnostic_createDiagonalOp(numQubits, env);
1271 }

References agnostic_createDiagonalOp(), QuESTEnv::numRanks, and validateNumQubitsInDiagOp().

Referenced by TEST_CASE().

◆ createPauliHamil()

PauliHamil createPauliHamil ( int  numQubits,
int  numSumTerms 
)

Create a PauliHamil instance, which is a Hamiltonian expressed as a real-weighted sum of products of Pauli operators.

This is merely an encapsulation of the multiple parameters of functions like applyPauliSum().

The Pauli operators (PauliHamil.pauliCodes) are all initialised to identity (PAULI_I), but the coefficients (PauliHamil.termCoeffs) are not initialised. The Hamiltonian can be used (e.g. in applyPauliHamil() and applyTrotterCircuit()) with Qureg instances of the same number of qubits.

The returned dynamic PauliHamil instance must later be freed via destroyPauliHamil().

Parameters
[in]numQubitsthe number of qubits on which this Hamiltonian acts
[in]numSumTermsthe number of weighted terms in the sum, or the number of Pauli products
Returns
a dynamic PauliHamil struct, with fields pauliCodes and termCoeffs stored in the heap
Exceptions
invalidQuESTInputErrorif numQubits <= 0, or numSumTerms <= 0.
Author
Tyson Jones

Definition at line 1147 of file QuEST.c.

1147  {
1148  validateHamilParams(numQubits, numSumTerms, __func__);
1149 
1150  PauliHamil h;
1151  h.numQubits = numQubits;
1152  h.numSumTerms = numSumTerms;
1153  h.termCoeffs = malloc(numSumTerms * sizeof *h.termCoeffs);
1154  h.pauliCodes = malloc(numQubits*numSumTerms * sizeof *h.pauliCodes);
1155 
1156  // initialise pauli codes to identity
1157  for (int i=0; i<numQubits*numSumTerms; i++)
1158  h.pauliCodes[i] = PAULI_I;
1159 
1160  return h;
1161 }

References PauliHamil::numQubits, PauliHamil::numSumTerms, PAULI_I, PauliHamil::pauliCodes, PauliHamil::termCoeffs, and validateHamilParams().

Referenced by createPauliHamilFromFile(), and TEST_CASE().

◆ createPauliHamilFromFile()

PauliHamil createPauliHamilFromFile ( char *  fn)

Create a PauliHamil instance, a real-weighted sum of products of Pauli operators, populated with the data in filename fn.

Each line in the plaintext file is interpreted as a separate product of Pauli operators in the sum, and is a space-separated list with format

c p1 p2 p3 ... pN

where c is the real coefficient of the term, and p1 ... pN are numbers 0, 1, 2, 3 to indicate identity, pauliX, pauliY and pauliZ operators respectively, acting on qubits 0 through N-1 (all qubits). For example, the file containing

0.31 1 0 1 2
-0.2 3 2 0 0

encodes a two-term four-qubit Hamiltonian $ 0.31 X_0 X_2 Y_3 -0.2 Z_0 Y_1 $.

The number of qubits and terms are inferred from the file. The created Hamiltonian can be used just like one created via createPauliHamil().

The returned dynamic PauliHamil instance must later be freed via destroyPauliHamil().

Parameters
[in]fnfilename of the plaintext file specifying the pauli operators and coefficients
Returns
a dynamic PauliHamil struct, with fields pauliCodes and termCoeffs stored in the heap
Exceptions
invalidQuESTInputErrorif the file cannot be read, or is not correctly formatted
Author
Tyson Jones

Definition at line 1169 of file QuEST.c.

1169  {
1170 
1171  /* The validation in this function must close the file handle and free
1172  * allocated memory before raising an error (whether that's a C exit, or
1173  * an overriden C++ exception).
1174  */
1175 
1176  FILE* file = fopen(fn, "r");
1177  int success = (file != NULL);
1178  validateFileOpened(success, fn, __func__);
1179 
1180  /* file format: coeff {term} \n where {term} is #numQubits values of
1181  * 0 1 2 3 signifying I X Y Z acting on that qubit index
1182  */
1183 
1184  // count the number of qubits (ignore trailing whitespace)
1185  int numQubits = -1; // to exclude coeff at start
1186  char ch = getc(file);
1187  char prev = '0'; // anything not space
1188  while (ch != '\n' && ch != EOF) {
1189  if (ch == ' ' && prev != ' ') // skip multiple spaces
1190  numQubits++;
1191  prev = ch;
1192  ch = getc(file);
1193  }
1194  // edge-case: if we hit EOF/newline without a space
1195  if (prev != ' ')
1196  numQubits++;
1197 
1198  /* TODO:
1199  * The below code may break on Windows where newlines are multiple characters
1200  */
1201 
1202  // count the number of terms (being cautious of trailing newlines)
1203  int numTerms = 0;
1204  prev = '\n';
1205  rewind(file);
1206  while ((ch=getc(file)) != EOF) {
1207  if (ch == '\n' && prev != '\n')
1208  numTerms++;
1209  prev = ch;
1210  }
1211  // edge-case: if we hit EOF without a newline, count that line
1212  if (prev != '\n')
1213  numTerms++;
1214 
1215  // validate the inferred number of terms and qubits (closes file if error)
1216  validateHamilFileParams(numQubits, numTerms, file, fn, __func__);
1217 
1218  // allocate space for PauliHamil data
1219  PauliHamil h = createPauliHamil(numQubits, numTerms);
1220 
1221  // specifier for a qreal number then a space
1222  char strSpec[50];
1223  strcpy(strSpec, REAL_SPECIFIER);
1224  strcat(strSpec, " ");
1225 
1226  // collect coefficients and terms
1227  rewind(file);
1228  for (int t=0; t<numTerms; t++) {
1229 
1230  // record coefficient, and validate (closes file and frees h if error)
1231  success = fscanf(file, strSpec, &(h.termCoeffs[t])) == 1;
1232  validateHamilFileCoeffParsed(success, h, file, fn, __func__);
1233 
1234  // record Pauli operations, and validate (closes file and frees h if error)
1235  for (int q=0; q<numQubits; q++) {
1236  int i = t*numQubits + q;
1237 
1238  // verbose, to avoid type warnings
1239  int code;
1240  success = fscanf(file, "%d ", &code) == 1;
1241  h.pauliCodes[i] = (enum pauliOpType) code;
1242  validateHamilFilePauliParsed(success, h, file, fn, __func__);
1243  validateHamilFilePauliCode(h.pauliCodes[i], h, file, fn, __func__);
1244  }
1245 
1246  // the trailing newline is magically eaten
1247  }
1248 
1249  fclose(file);
1250  return h;
1251 }

References createPauliHamil(), PauliHamil::pauliCodes, PauliHamil::termCoeffs, validateFileOpened(), validateHamilFileCoeffParsed(), validateHamilFileParams(), validateHamilFilePauliCode(), and validateHamilFilePauliParsed().

Referenced by TEST_CASE().

◆ createQuESTEnv()

QuESTEnv createQuESTEnv ( void  )

Create the QuEST execution environment.

This should be called only once, and the environment should be freed with destroyQuESTEnv at the end of the user's code. If something needs to be done to set up the execution environment, such as initializing MPI when running in distributed mode, it is handled here.

Returns
object representing the execution environment. A single instance is used for each program
Author
Ania Brown

Definition at line 129 of file QuEST_cpu_distributed.c.

129  {
130 
131  QuESTEnv env;
132 
133  // init MPI environment
134  int rank, numRanks, initialized;
135  MPI_Initialized(&initialized);
136  if (!initialized){
137  MPI_Init(NULL, NULL);
138  MPI_Comm_size(MPI_COMM_WORLD, &numRanks);
139  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
140 
141  env.rank=rank;
142  env.numRanks=numRanks;
143 
144  } else {
145 
146  printf("ERROR: Trying to initialize QuESTEnv multiple times. Ignoring...\n");
147 
148  // ensure env is initialised anyway, so the compiler is happy
149  MPI_Comm_size(MPI_COMM_WORLD, &numRanks);
150  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
151  env.rank=rank;
152  env.numRanks=numRanks;
153  }
154 
155  validateNumRanks(env.numRanks, __func__);
156 
158 
159  return env;
160 }

References GPUExists(), QuESTEnv::numRanks, QuESTEnv::rank, seedQuESTDefault(), and validateNumRanks().

Referenced by main().

◆ createQureg()

Qureg createQureg ( int  numQubits,
QuESTEnv  env 
)

Create a Qureg object representing a set of qubits which will remain in a pure state.

Allocate space for state vector of probability amplitudes, including space for temporary values to be copied from one other chunk if running the distributed version. Define properties related to the size of the set of qubits. The qubits are initialised in the zero state (i.e. initZeroState is automatically called)

Returns
an object representing the set of qubits
Parameters
[in]numQubitsnumber of qubits in the system
[in]envobject representing the execution environment (local, multinode etc)
Exceptions
invalidQuESTInputErrorif numQubits <= 0, or if numQubits is so large that the number of amplitudes cannot fit in a long long int type, or if in distributed mode, there are more nodes than elements in the would-be state-vector
Author
Ania Brown

Definition at line 36 of file QuEST.c.

36  {
37  validateNumQubitsInQureg(numQubits, env.numRanks, __func__);
38 
39  Qureg qureg;
40  statevec_createQureg(&qureg, numQubits, env);
41  qureg.isDensityMatrix = 0;
42  qureg.numQubitsRepresented = numQubits;
43  qureg.numQubitsInStateVec = numQubits;
44 
45  qasm_setup(&qureg);
46  initZeroState(qureg); // safe call to public function
47  return qureg;
48 }

References initZeroState(), Qureg::isDensityMatrix, Qureg::numQubitsInStateVec, Qureg::numQubitsRepresented, QuESTEnv::numRanks, qasm_setup(), statevec_createQureg(), and validateNumQubitsInQureg().

Referenced by TEST_CASE().

◆ destroyComplexMatrixN()

void destroyComplexMatrixN ( ComplexMatrixN  matr)

Destroy a ComplexMatrixN instance created with createComplexMatrixN()

It is invalid to attempt to destroy a matrix created with getStaticComplexMatrixN().

Parameters
[in]matrthe dynamic matrix (created with createComplexMatrixN()) to deallocate
Exceptions
invalidQuESTInputErrorif matr was not yet allocated.
malloc_errorif matr was static (created with getStaticComplexMatrixN())
Author
Tyson Jones

Definition at line 1120 of file QuEST.c.

1120  {
1121  /* this checks m.real/imag != NULL, which is only ever set when the mallocs
1122  * in createComplexMatrixN fail, which already prompts an error. Hence
1123  * this check if useless
1124  */
1125  validateMatrixInit(m, __func__);
1126 
1127  int numRows = 1 << m.numQubits;
1128  for (int r=0; r < numRows; r++) {
1129  free(m.real[r]);
1130  free(m.imag[r]);
1131  }
1132  free(m.real);
1133  free(m.imag);
1134 }

References ComplexMatrixN::imag, ComplexMatrixN::numQubits, ComplexMatrixN::real, and validateMatrixInit().

Referenced by densmatr_mixMultiQubitKrausMap(), and TEST_CASE().

◆ destroyDiagonalOp()

void destroyDiagonalOp ( DiagonalOp  op,
QuESTEnv  env 
)

Destroys a DiagonalOp created with createDiagonalOp(), freeing its memory.

Parameters
[in]opthe diagonal operator to destroy
[in]envobject representing the execution environment (local, multinode etc)
Exceptions
invalidQuESTInputErrorif op was not created
Author
Tyson Jones

Definition at line 1273 of file QuEST.c.

1273  {
1274  // env accepted for API consistency
1275  validateDiagOpInit(op, __func__);
1276 
1278 }

References agnostic_destroyDiagonalOp(), and validateDiagOpInit().

Referenced by TEST_CASE().

◆ destroyPauliHamil()

void destroyPauliHamil ( PauliHamil  hamil)

Destroy a PauliHamil instance, created with either createPauliHamil() or createPauliHamilFromFile().

Parameters
[in]hamila dynamic PauliHamil instantiation
Author
Tyson Jones

Definition at line 1163 of file QuEST.c.

1163  {
1164 
1165  free(h.termCoeffs);
1166  free(h.pauliCodes);
1167 }

References PauliHamil::pauliCodes, and PauliHamil::termCoeffs.

Referenced by TEST_CASE(), validateHamilFileCoeffParsed(), validateHamilFilePauliCode(), and validateHamilFilePauliParsed().

◆ destroyQuESTEnv()

void destroyQuESTEnv ( QuESTEnv  env)

Destroy the QuEST environment.

If something needs to be done to clean up the execution environment, such as finalizing MPI when running in distributed mode, it is handled here

Parameters
[in]envobject representing the execution environment. A single instance is used for each program
Author
Ania Brown

Definition at line 172 of file QuEST_cpu_distributed.c.

172  {
173  int finalized;
174  MPI_Finalized(&finalized);
175  if (!finalized) MPI_Finalize();
176  else printf("ERROR: Trying to close QuESTEnv multiple times. Ignoring\n");
177 }

Referenced by main().

◆ destroyQureg()

void destroyQureg ( Qureg  qureg,
QuESTEnv  env 
)

Deallocate a Qureg object representing a set of qubits.

Free memory allocated to state vector of probability amplitudes, including temporary vector for values copied from another chunk if running the distributed version.

Parameters
[in,out]quregobject to be deallocated
[in]envobject representing the execution environment (local, multinode etc)
Author
Ania Brown

Definition at line 77 of file QuEST.c.

77  {
78  statevec_destroyQureg(qureg, env);
79  qasm_free(qureg);
80 }

References qasm_free(), and statevec_destroyQureg().

Referenced by TEST_CASE().

◆ initComplexMatrixN()

void initComplexMatrixN ( ComplexMatrixN  m,
qreal  real[][1<< m.numQubits],
qreal  imag[][1<< m.numQubits] 
)

Initialises a ComplexMatrixN instance to have the passed real and imag values.

This allows succint population of any-sized ComplexMatrixN, e.g. through 2D arrays:

ComplexMatrixN m = createComplexMatrixN(3);
initComplexMatrixN(m, 
    (qreal[8][8]) {{1,2,3,4,5,6,7,8}, {0}},
    (qreal[8][8]) {{0}});

m can be created by either createComplexMatrixN() or getStaticComplexMatrixN().

This function is only callable in C, since C++ signatures cannot contain variable-length 2D arrays

Parameters
[in]mthe matrix to initialise
[in]realmatrix of real values; can be 2D array of array of pointers
[in]imagmatrix of imaginary values; can be 2D array of array of pointers
Exceptions
invalidQuESTInputErrorif m has not been allocated (e.g. with createComplexMatrixN())
Author
Tyson Jones

Definition at line 1136 of file QuEST.c.

1136  {
1137  validateMatrixInit(m, __func__);
1138 
1139  int dim = 1 << m.numQubits;
1140  for (int i=0; i<dim; i++)
1141  for (int j=0; j<dim; j++) {
1142  m.real[i][j] = re[i][j];
1143  m.imag[i][j] = im[i][j];
1144  }
1145 }

References ComplexMatrixN::imag, ComplexMatrixN::numQubits, ComplexMatrixN::real, and validateMatrixInit().

◆ initDiagonalOp()

void initDiagonalOp ( DiagonalOp  op,
qreal real,
qreal imag 
)

Updates the entire DiagonalOp op with the given elements, of which there must be 2^op.numQubits.

In GPU mode, this updates both the persistent GPU memory, and the arrays op.real and op.imag

In distributed mode, this function assumes real and imag exist fully on every node.

Parameters
[in,out]opthe diagonal operator to modify
[in]realthe real components of the full set of new elements
[in]imagthe imaginary components of the full set of new elements
Exceptions
invalidQuESTInputErrorif op was not created
Author
Tyson Jones

Definition at line 1286 of file QuEST.c.

1286  {
1287  validateDiagOpInit(op, __func__);
1288 
1289  agnostic_setDiagonalOpElems(op, 0, real, imag, 1LL << op.numQubits);
1290 }

References agnostic_setDiagonalOpElems(), DiagonalOp::numQubits, and validateDiagOpInit().

Referenced by TEST_CASE().

◆ initPauliHamil()

void initPauliHamil ( PauliHamil  hamil,
qreal coeffs,
enum pauliOpType codes 
)

Initialise a PauliHamil instance with the given term coefficients and Pauli codes (one for every qubit in every term).

coeffs and codes encode a weighted sum of Pauli operators, with the same format as other QuEST functions (like calcExpecPauliSum()).

hamil must be already created with createPauliHamil(), or createPauliHamilFromFile())

Parameters
[in,out]hamilan already created PauliHamil instance to be modified
[in]coeffsa length-hamil.numSumTerms array of coefficients
[in]codesa length-hamil.numSumTerms*hamil.numQubits array of Pauli codes
Exceptions
invalidQuESTInputErrorif hamil has invalid parameters (numQubits <= 0, numSumTerms <= 0), or if any code in codes is not a valid Pauli code.
Author
Tyson Jones

Definition at line 1253 of file QuEST.c.

1253  {
1254  validateHamilParams(hamil.numQubits, hamil.numSumTerms, __func__);
1255  validatePauliCodes(codes, hamil.numSumTerms*hamil.numQubits, __func__);
1256 
1257  int i=0;
1258  for (int t=0; t<hamil.numSumTerms; t++) {
1259  hamil.termCoeffs[t] = coeffs[t];
1260  for (int q=0; q<hamil.numQubits; q++) {
1261  hamil.pauliCodes[i] = codes[i];
1262  i++;
1263  }
1264  }
1265 }

References PauliHamil::numQubits, PauliHamil::numSumTerms, PauliHamil::pauliCodes, PauliHamil::termCoeffs, validateHamilParams(), and validatePauliCodes().

Referenced by TEST_CASE().

◆ setDiagonalOpElems()

void setDiagonalOpElems ( DiagonalOp  op,
long long int  startInd,
qreal real,
qreal imag,
long long int  numElems 
)

Modifies a subset (starting at index startInd) of the elements in DiagonalOp op with the given elements, of which there are numElems.

In GPU mode, this updates both the persistent GPU memory, and the arrays op.real and op.imag

In distributed mode, this function assumes the subset real and imag exist (at least) on the node containing the ultimately updated elements. For example, below is the correct way to modify the full 8 elements of op when split between 2 nodes.

DiagonalOp op = createDiagonalOp(3, env);

qreal re[] = {1,2,3,4};
qreal im[] = {1,2,3,4};
setDiagonalOpElems(op, 0, re, im, 4);

// modify re and im to the next set of elements 

setDiagonalOpElems(op, 4, re, im, 4);

In this way, one can avoid a single node containing all new elements which might not fit. If more elements are passed than exist on an individual node, each node merely ignores the additional elements.

Parameters
[in,out]opthe diagonal operator to modify the elements of
[in]startIndthe starting index (globally) of the subset of elements to modify
[in]realthe real components of the new elements
[in]imagthe imaginary components of the new elements
[in]numElemsthe number of new elements (the length of real and imag)
Exceptions
invalidQuESTInputErrorif op was not created, or if startInd is an invalid index, or if numElems is an invalid number of elements, or if there less than numElems elements in the operator after startInd.
Author
Tyson Jones

Definition at line 1292 of file QuEST.c.

1292  {
1293  validateDiagOpInit(op, __func__);
1294  validateNumElems(op, startInd, numElems, __func__);
1295 
1296  agnostic_setDiagonalOpElems(op, startInd, real, imag, numElems);
1297 }

References agnostic_setDiagonalOpElems(), validateDiagOpInit(), and validateNumElems().

Referenced by TEST_CASE().

◆ syncDiagonalOp()

void syncDiagonalOp ( DiagonalOp  op)

Copy the elements in DiagonalOp op.real and op.imag to the persisent GPU memory.

This updates the GPU memory for op with any manual changes made to op.real and op.imag.

Note if users just modify the diagonal operator to values known a priori, they should instead use initDiagonalOp() or setDiagonalOpElems()

This function has no effect in other modes besides GPU mode.

Parameters
[in,out]opthe diagonal operator to synch to GPU
Exceptions
invalidQuESTInputErrorif op was not created
Author
Tyson Jones

Definition at line 1280 of file QuEST.c.

1280  {
1281  validateDiagOpInit(op, __func__);
1282 
1284 }

References agnostic_syncDiagonalOp(), and validateDiagOpInit().

Referenced by TEST_CASE().

void agnostic_destroyDiagonalOp(DiagonalOp op)
Definition: QuEST_cpu.c:1357
pauliOpType
Codes for specifying Pauli operators.
Definition: QuEST.h:96
@ PAULI_Z
Definition: QuEST.h:96
int rank
Definition: QuEST.h:244
void validateHamilFileCoeffParsed(int parsed, PauliHamil h, FILE *file, char *fn, const char *caller)
void validateHamilParams(int numQubits, int numTerms, const char *caller)
void qasm_free(Qureg qureg)
Definition: QuEST_qasm.c:500
@ PAULI_I
Definition: QuEST.h:96
void validateNumQubitsInQureg(int numQubits, int numRanks, const char *caller)
void validateHamilFilePauliParsed(int parsed, PauliHamil h, FILE *file, char *fn, const char *caller)
void agnostic_syncDiagonalOp(DiagonalOp op)
Definition: QuEST_cpu.c:1362
void statevec_destroyQureg(Qureg qureg, QuESTEnv env)
Definition: QuEST_cpu.c:1317
void validateHamilFilePauliCode(enum pauliOpType code, PauliHamil h, FILE *file, char *fn, const char *caller)
Information about the environment the program is running in.
Definition: QuEST.h:242
ComplexMatrixN bindArraysToStackComplexMatrixN(int numQubits, qreal re[][1<< numQubits], qreal im[][1<< numQubits], qreal **reStorage, qreal **imStorage)
Definition: QuEST_common.c:607
Represents a general 2^N by 2^N matrix of complex numbers.
Definition: QuEST.h:136
#define qreal
void validateMatrixInit(ComplexMatrixN matr, const char *caller)
void validateFileOpened(int opened, char *fn, const char *caller)
@ PAULI_X
Definition: QuEST.h:96
DiagonalOp agnostic_createDiagonalOp(int numQubits, QuESTEnv env)
Definition: QuEST_cpu.c:1335
int numQubitsInStateVec
Number of qubits in the state-vector - this is double the number represented for mixed states.
Definition: QuEST.h:210
qreal * termCoeffs
The coefficient of each Pauli product. This is a length numSumTerms array.
Definition: QuEST.h:164
#define qcomp
enum pauliOpType * pauliCodes
The Pauli operators acting on each qubit, flattened over every operator.
Definition: QuEST.h:162
void statevec_cloneQureg(Qureg targetQureg, Qureg copyQureg)
works for both statevectors and density matrices
Definition: QuEST_cpu.c:1506
int numRanks
Definition: QuEST.h:245
int numQubits
The number of qubits this operator can act on (informing its size)
Definition: QuEST.h:181
int numSumTerms
The number of terms in the weighted sum, or the number of Pauli products.
Definition: QuEST.h:166
@ PAULI_Y
Definition: QuEST.h:96
Represents a weighted sum of pauli products.
Definition: QuEST.h:158
void validateNumElems(DiagonalOp op, long long int startInd, long long int numElems, const char *caller)
qreal ** real
Definition: QuEST.h:139
Represents a system of qubits.
Definition: QuEST.h:203
void validateNumQubitsInMatrix(int numQubits, const char *caller)
qreal ** imag
Definition: QuEST.h:140
void validateDiagOpInit(DiagonalOp op, const char *caller)
void seedQuESTDefault()
Seed the Mersenne Twister used for random number generation in the QuEST environment with an example ...
int isDensityMatrix
Whether this instance is a density-state representation.
Definition: QuEST.h:206
int numQubits
Definition: QuEST.h:138
void validateHamilFileParams(int numQubits, int numTerms, FILE *file, char *fn, const char *caller)
int numQubits
The number of qubits for which this Hamiltonian is defined.
Definition: QuEST.h:168
int numQubitsRepresented
The number of qubits represented in either the state-vector or density matrix.
Definition: QuEST.h:208
void agnostic_setDiagonalOpElems(DiagonalOp op, long long int startInd, qreal *real, qreal *imag, long long int numElems)
Definition: QuEST_cpu.c:3842
void validateNumQubitsInDiagOp(int numQubits, int numRanks, const char *caller)
void validateNumRanks(int numRanks, const char *caller)
void initZeroState(Qureg qureg)
Initialise a set of qubits to the classical zero state .
Definition: QuEST.c:113
void qasm_setup(Qureg *qureg)
Definition: QuEST_qasm.c:60
void validatePauliCodes(enum pauliOpType *pauliCodes, int numPauliCodes, const char *caller)
void statevec_createQureg(Qureg *qureg, int numQubits, QuESTEnv env)
Definition: QuEST_cpu.c:1279
PauliHamil createPauliHamil(int numQubits, int numSumTerms)
Create a PauliHamil instance, which is a Hamiltonian expressed as a real-weighted sum of products of ...
Definition: QuEST.c:1147