QuEST_cpu_distributed.c
Go to the documentation of this file.
1 // Distributed under MIT licence. See https://github.com/QuEST-Kit/QuEST/blob/master/LICENCE.txt for details
2 
12 # include "QuEST.h"
13 # include "QuEST_internal.h"
14 # include "QuEST_precision.h"
15 # include "QuEST_validation.h"
16 # include "mt19937ar.h"
17 
18 # include "QuEST_cpu_internal.h"
19 
20 # define _BSD_SOURCE
21 # include <unistd.h>
22 # include <mpi.h>
23 # include <stdlib.h>
24 # include <stdio.h>
25 # include <string.h> // for memcpy
26 # include <math.h>
27 # include <time.h>
28 # include <sys/types.h>
29 
30 # ifdef _OPENMP
31 # include <omp.h>
32 # endif
33 
34 
36 
37  Complex localInnerProd = statevec_calcInnerProductLocal(bra, ket);
38  if (bra.numChunks == 1)
39  return localInnerProd;
40 
41  qreal localReal = localInnerProd.real;
42  qreal localImag = localInnerProd.imag;
43  qreal globalReal, globalImag;
44  MPI_Allreduce(&localReal, &globalReal, 1, MPI_QuEST_REAL, MPI_SUM, MPI_COMM_WORLD);
45  MPI_Allreduce(&localImag, &globalImag, 1, MPI_QuEST_REAL, MPI_SUM, MPI_COMM_WORLD);
46 
47  Complex globalInnerProd;
48  globalInnerProd.real = globalReal;
49  globalInnerProd.imag = globalImag;
50  return globalInnerProd;
51 }
52 
54 
55  // computes the trace by summing every element ("diag") with global index (2^n + 1)i for i in [0, 2^n-1]
56 
57  // computes first local index containing a diagonal element
58  long long int diagSpacing = 1LL + (1LL << qureg.numQubitsRepresented);
59  long long int numPrevDiags = (qureg.chunkId>0)? 1+(qureg.chunkId*qureg.numAmpsPerChunk)/diagSpacing : 0;
60  long long int globalIndNextDiag = diagSpacing * numPrevDiags;
61  long long int localIndNextDiag = globalIndNextDiag % qureg.numAmpsPerChunk;
62  long long int index;
63 
64  qreal rankTotal = 0;
65  qreal y, t, c;
66  c = 0;
67 
68  // iterates every local diagonal
69  for (index=localIndNextDiag; index < qureg.numAmpsPerChunk; index += diagSpacing) {
70 
71  // Kahan summation - brackets are important
72  y = qureg.stateVec.real[index] - c;
73  t = rankTotal + y;
74  c = ( t - rankTotal ) - y;
75  rankTotal = t;
76  }
77 
78  // combine each node's sum of diagonals
79  qreal globalTotal;
80  if (qureg.numChunks > 1)
81  MPI_Allreduce(&rankTotal, &globalTotal, 1, MPI_QuEST_REAL, MPI_SUM, MPI_COMM_WORLD);
82  else
83  globalTotal = rankTotal;
84 
85  return globalTotal;
86 }
87 
89  // Implemented using Kahan summation for greater accuracy at a slight floating
90  // point operation overhead. For more details see https://en.wikipedia.org/wiki/Kahan_summation_algorithm
91  qreal pTotal=0;
92  qreal y, t, c;
93  qreal allRankTotals=0;
94  long long int index;
95  long long int numAmpsPerRank = qureg.numAmpsPerChunk;
96  c = 0.0;
97  for (index=0; index<numAmpsPerRank; index++){
98  // Perform pTotal+=qureg.stateVec.real[index]*qureg.stateVec.real[index]; by Kahan
99  y = qureg.stateVec.real[index]*qureg.stateVec.real[index] - c;
100  t = pTotal + y;
101  // Don't change the bracketing on the following line
102  c = ( t - pTotal ) - y;
103  pTotal = t;
104  // Perform pTotal+=qureg.stateVec.imag[index]*qureg.stateVec.imag[index]; by Kahan
105  y = qureg.stateVec.imag[index]*qureg.stateVec.imag[index] - c;
106  t = pTotal + y;
107  // Don't change the bracketing on the following line
108  c = ( t - pTotal ) - y;
109  pTotal = t;
110  }
111  if (qureg.numChunks>1)
112  MPI_Allreduce(&pTotal, &allRankTotals, 1, MPI_QuEST_REAL, MPI_SUM, MPI_COMM_WORLD);
113  else
114  allRankTotals=pTotal;
115 
116  return allRankTotals;
117 }
118 
119 
120 static int isChunkToSkipInFindPZero(int chunkId, long long int chunkSize, int measureQubit);
121 static int chunkIsUpper(int chunkId, long long int chunkSize, int targetQubit);
122 static int chunkIsUpperInOuterBlock(int chunkId, long long int chunkSize, int targetQubit, int numQubits);
123 static void getRotAngle(int chunkIsUpper, Complex *rot1, Complex *rot2, Complex alpha, Complex beta);
124 static int getChunkPairId(int chunkIsUpper, int chunkId, long long int chunkSize, int targetQubit);
125 static int getChunkOuterBlockPairId(int chunkIsUpper, int chunkId, long long int chunkSize, int targetQubit, int numQubits);
126 static int halfMatrixBlockFitsInChunk(long long int chunkSize, int targetQubit);
127 static int getChunkIdFromIndex(Qureg qureg, long long int index);
128 
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 }
161 
163  MPI_Barrier(MPI_COMM_WORLD);
164 }
165 
166 int syncQuESTSuccess(int successCode){
167  int totalSuccess;
168  MPI_Allreduce(&successCode, &totalSuccess, 1, MPI_INT, MPI_LAND, MPI_COMM_WORLD);
169  return totalSuccess;
170 }
171 
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 }
178 
180  if (env.rank==0){
181  printf("EXECUTION ENVIRONMENT:\n");
182  printf("Running distributed (MPI) version\n");
183  printf("Number of ranks is %d\n", env.numRanks);
184 # ifdef _OPENMP
185  printf("OpenMP enabled\n");
186  printf("Number of threads available is %d\n", omp_get_max_threads());
187 # else
188  printf("OpenMP disabled\n");
189 # endif
190  printf("Precision: size of qreal is %ld bytes\n", sizeof(qreal) );
191  }
192 }
193 
194 int getChunkIdFromIndex(Qureg qureg, long long int index){
195  return index/qureg.numAmpsPerChunk; // this is numAmpsPerChunk
196 }
197 
198 qreal statevec_getRealAmp(Qureg qureg, long long int index){
199  int chunkId = getChunkIdFromIndex(qureg, index);
200  qreal el;
201  if (qureg.chunkId==chunkId){
202  el = qureg.stateVec.real[index-chunkId*qureg.numAmpsPerChunk];
203  }
204  MPI_Bcast(&el, 1, MPI_QuEST_REAL, chunkId, MPI_COMM_WORLD);
205  return el;
206 }
207 
208 qreal statevec_getImagAmp(Qureg qureg, long long int index){
209  int chunkId = getChunkIdFromIndex(qureg, index);
210  qreal el;
211  if (qureg.chunkId==chunkId){
212  el = qureg.stateVec.imag[index-chunkId*qureg.numAmpsPerChunk];
213  }
214  MPI_Bcast(&el, 1, MPI_QuEST_REAL, chunkId, MPI_COMM_WORLD);
215  return el;
216 }
217 
226 static int chunkIsUpper(int chunkId, long long int chunkSize, int targetQubit)
228 {
229  long long int sizeHalfBlock = 1LL << (targetQubit);
230  long long int sizeBlock = sizeHalfBlock*2;
231  long long int posInBlock = (chunkId*chunkSize) % sizeBlock;
232  return posInBlock<sizeHalfBlock;
233 }
234 
236 static int chunkIsUpperInOuterBlock(int chunkId, long long int chunkSize, int targetQubit, int numQubits)
237 {
238  long long int sizeOuterHalfBlock = 1LL << (targetQubit+numQubits);
239  long long int sizeOuterBlock = sizeOuterHalfBlock*2;
240  long long int posInBlock = (chunkId*chunkSize) % sizeOuterBlock;
241  return posInBlock<sizeOuterHalfBlock;
242 }
243 
258 static void getRotAngle(int chunkIsUpper, Complex *rot1, Complex *rot2, Complex alpha, Complex beta)
259 {
260  if (chunkIsUpper){
261  *rot1=alpha;
262  rot2->real=-beta.real;
263  rot2->imag=-beta.imag;
264  } else {
265  *rot1=beta;
266  *rot2=alpha;
267  }
268 }
269 
284 {
285  if (chunkIsUpper){
286  *rot1=(Complex) {.real=u.real[0][0], .imag=u.imag[0][0]};
287  *rot2=(Complex) {.real=u.real[0][1], .imag=u.imag[0][1]};
288  } else {
289  *rot1=(Complex) {.real=u.real[1][0], .imag=u.imag[1][0]};
290  *rot2=(Complex) {.real=u.real[1][1], .imag=u.imag[1][1]};
291  }
292 }
293 
303 static int getChunkPairId(int chunkIsUpper, int chunkId, long long int chunkSize, int targetQubit)
304 {
305  long long int sizeHalfBlock = 1LL << (targetQubit);
306  int chunksPerHalfBlock = sizeHalfBlock/chunkSize;
307  if (chunkIsUpper){
308  return chunkId + chunksPerHalfBlock;
309  } else {
310  return chunkId - chunksPerHalfBlock;
311  }
312 }
313 
314 static int getChunkOuterBlockPairId(int chunkIsUpper, int chunkId, long long int chunkSize, int targetQubit, int numQubits)
315 {
316  long long int sizeOuterHalfBlock = 1LL << (targetQubit+numQubits);
317  int chunksPerOuterHalfBlock = sizeOuterHalfBlock/chunkSize;
318  if (chunkIsUpper){
319  return chunkId + chunksPerOuterHalfBlock;
320  } else {
321  return chunkId - chunksPerOuterHalfBlock;
322  }
323 }
324 
325 static int getChunkOuterBlockPairIdForPart3(int chunkIsUpperSmallerQubit, int chunkIsUpperBiggerQubit, int chunkId,
326  long long int chunkSize, int smallerQubit, int biggerQubit, int numQubits)
327 {
328  long long int sizeOuterHalfBlockBiggerQubit = 1LL << (biggerQubit+numQubits);
329  long long int sizeOuterHalfBlockSmallerQubit = 1LL << (smallerQubit+numQubits);
330  int chunksPerOuterHalfBlockSmallerQubit = sizeOuterHalfBlockSmallerQubit/chunkSize;
331  int chunksPerOuterHalfBlockBiggerQubit = sizeOuterHalfBlockBiggerQubit/chunkSize;
332  int rank;
333  if (chunkIsUpperBiggerQubit){
334  rank = chunkId + chunksPerOuterHalfBlockBiggerQubit;
335  } else {
336  rank = chunkId - chunksPerOuterHalfBlockBiggerQubit;
337  }
338 
339  if (chunkIsUpperSmallerQubit){
340  rank = rank + chunksPerOuterHalfBlockSmallerQubit;
341  } else {
342  rank = rank - chunksPerOuterHalfBlockSmallerQubit;
343  }
344 
345  return rank;
346 }
347 
355 static int halfMatrixBlockFitsInChunk(long long int chunkSize, int targetQubit)
357 {
358  long long int sizeHalfBlock = 1LL << (targetQubit);
359  if (chunkSize > sizeHalfBlock) return 1;
360  else return 0;
361 }
362 
363 static int densityMatrixBlockFitsInChunk(long long int chunkSize, int numQubits, int targetQubit) {
364  long long int sizeOuterHalfBlock = 1LL << (targetQubit+numQubits);
365  if (chunkSize > sizeOuterHalfBlock) return 1;
366  else return 0;
367 }
368 
372 
373  // Remember that for every amplitude that `vec` stores on the node,
374  // `matr` stores an entire column. Ergo there are always an integer
375  // number (in fact, a power of 2) number of `matr`s columns on each node.
376  // Since the total size of `vec` (between all nodes) is one column
377  // and each node stores (possibly) multiple columns (vec.numAmpsPerChunk as many),
378  // `vec` can be fit entirely inside a single node's matr.pairStateVec (with excess!)
379 
380  // copy this node's vec segment into this node's matr pairState (in the right spot)
381  long long int numLocalAmps = vec.numAmpsPerChunk;
382  long long int myOffset = vec.chunkId * numLocalAmps;
383  memcpy(&matr.pairStateVec.real[myOffset], vec.stateVec.real, numLocalAmps * sizeof(qreal));
384  memcpy(&matr.pairStateVec.imag[myOffset], vec.stateVec.imag, numLocalAmps * sizeof(qreal));
385 
386  // we now want to share this node's vec segment with other node, so that
387  // vec is cloned in every node's matr.pairStateVec
388 
389  // work out how many messages needed to send vec chunks (2GB limit)
390  long long int maxMsgSize = MPI_MAX_AMPS_IN_MSG;
391  if (numLocalAmps < maxMsgSize)
392  maxMsgSize = numLocalAmps;
393  // safely assume MPI_MAX... = 2^n, so division always exact:
394  int numMsgs = numLocalAmps / maxMsgSize;
395 
396  // every node gets a turn at being the broadcaster
397  for (int broadcaster=0; broadcaster < vec.numChunks; broadcaster++) {
398 
399  long long int otherOffset = broadcaster * numLocalAmps;
400 
401  // every node sends a slice of qureg's pairState to every other
402  for (int i=0; i< numMsgs; i++) {
403 
404  // by sending that slice in further slices (due to bandwidth limit)
405  MPI_Bcast(
406  &matr.pairStateVec.real[otherOffset + i*maxMsgSize],
407  maxMsgSize, MPI_QuEST_REAL, broadcaster, MPI_COMM_WORLD);
408  MPI_Bcast(
409  &matr.pairStateVec.imag[otherOffset + i*maxMsgSize],
410  maxMsgSize, MPI_QuEST_REAL, broadcaster, MPI_COMM_WORLD);
411  }
412  }
413 }
414 
416 
417  // set qureg's pairState is to be the full pureState (on every node)
418  copyVecIntoMatrixPairState(qureg, pureState);
419 
420  // collect calcFidelityLocal by every machine
421  qreal localSum = densmatr_calcFidelityLocal(qureg, pureState);
422 
423  // sum each localSum
424  qreal globalSum;
425  MPI_Allreduce(&localSum, &globalSum, 1, MPI_QuEST_REAL, MPI_SUM, MPI_COMM_WORLD);
426 
427  return globalSum;
428 }
429 
431 
433 
434  qreal globalSum;
435  MPI_Allreduce(&localSum, &globalSum, 1, MPI_QuEST_REAL, MPI_SUM, MPI_COMM_WORLD);
436 
437  qreal dist = sqrt(globalSum);
438  return dist;
439 }
440 
442 
443  qreal localSum = densmatr_calcInnerProductLocal(a, b);
444 
445  qreal globalSum;
446  MPI_Allreduce(&localSum, &globalSum, 1, MPI_QuEST_REAL, MPI_SUM, MPI_COMM_WORLD);
447 
448  qreal dist = globalSum;
449  return dist;
450 }
451 
452 void densmatr_initPureState(Qureg targetQureg, Qureg copyQureg) {
453 
454  if (targetQureg.numChunks==1){
455  // local version
456  // save pointers to qureg's pair state
457  qreal* quregPairRePtr = targetQureg.pairStateVec.real;
458  qreal* quregPairImPtr = targetQureg.pairStateVec.imag;
459 
460  // populate qureg pair state with pure state (by repointing)
461  targetQureg.pairStateVec.real = copyQureg.stateVec.real;
462  targetQureg.pairStateVec.imag = copyQureg.stateVec.imag;
463 
464  // populate density matrix via it's pairState
465  densmatr_initPureStateLocal(targetQureg, copyQureg);
466 
467  // restore pointers
468  targetQureg.pairStateVec.real = quregPairRePtr;
469  targetQureg.pairStateVec.imag = quregPairImPtr;
470  } else {
471  // set qureg's pairState is to be the full pure state (on every node)
472  copyVecIntoMatrixPairState(targetQureg, copyQureg);
473 
474  // update every density matrix chunk using pairState
475  densmatr_initPureStateLocal(targetQureg, copyQureg);
476  }
477 }
478 
479 
480 
481 void exchangeStateVectors(Qureg qureg, int pairRank){
482  // MPI send/receive vars
483  int TAG=100;
484  MPI_Status status;
485 
486  // Multiple messages are required as MPI uses int rather than long long int for count
487  // For openmpi, messages are further restricted to 2GB in size -- do this for all cases
488  // to be safe
489  long long int maxMessageCount = MPI_MAX_AMPS_IN_MSG;
490  if (qureg.numAmpsPerChunk < maxMessageCount)
491  maxMessageCount = qureg.numAmpsPerChunk;
492 
493  // safely assume MPI_MAX... = 2^n, so division always exact
494  int numMessages = qureg.numAmpsPerChunk/maxMessageCount;
495  int i;
496  long long int offset;
497  // send my state vector to pairRank's qureg.pairStateVec
498  // receive pairRank's state vector into qureg.pairStateVec
499  for (i=0; i<numMessages; i++){
500  offset = i*maxMessageCount;
501  MPI_Sendrecv(&qureg.stateVec.real[offset], maxMessageCount, MPI_QuEST_REAL, pairRank, TAG,
502  &qureg.pairStateVec.real[offset], maxMessageCount, MPI_QuEST_REAL,
503  pairRank, TAG, MPI_COMM_WORLD, &status);
504  //printf("rank: %d err: %d\n", qureg.rank, err);
505  MPI_Sendrecv(&qureg.stateVec.imag[offset], maxMessageCount, MPI_QuEST_REAL, pairRank, TAG,
506  &qureg.pairStateVec.imag[offset], maxMessageCount, MPI_QuEST_REAL,
507  pairRank, TAG, MPI_COMM_WORLD, &status);
508  }
509 }
510 
511 void exchangePairStateVectorHalves(Qureg qureg, int pairRank){
512  // MPI send/receive vars
513  int TAG=100;
514  MPI_Status status;
515  long long int numAmpsToSend = qureg.numAmpsPerChunk >> 1;
516 
517  // Multiple messages are required as MPI uses int rather than long long int for count
518  // For openmpi, messages are further restricted to 2GB in size -- do this for all cases
519  // to be safe
520  long long int maxMessageCount = MPI_MAX_AMPS_IN_MSG;
521  if (numAmpsToSend < maxMessageCount)
522  maxMessageCount = numAmpsToSend;
523 
524  // safely assume MPI_MAX... = 2^n, so division always exact
525  int numMessages = numAmpsToSend/maxMessageCount;
526  int i;
527  long long int offset;
528  // send the bottom half of my state vector to the top half of pairRank's qureg.pairStateVec
529  // receive pairRank's state vector into the top of qureg.pairStateVec
530  for (i=0; i<numMessages; i++){
531  offset = i*maxMessageCount;
532  MPI_Sendrecv(&qureg.pairStateVec.real[offset+numAmpsToSend], maxMessageCount,
533  MPI_QuEST_REAL, pairRank, TAG,
534  &qureg.pairStateVec.real[offset], maxMessageCount, MPI_QuEST_REAL,
535  pairRank, TAG, MPI_COMM_WORLD, &status);
536  //printf("rank: %d err: %d\n", qureg.rank, err);
537  MPI_Sendrecv(&qureg.pairStateVec.imag[offset+numAmpsToSend], maxMessageCount,
538  MPI_QuEST_REAL, pairRank, TAG,
539  &qureg.pairStateVec.imag[offset], maxMessageCount, MPI_QuEST_REAL,
540  pairRank, TAG, MPI_COMM_WORLD, &status);
541  }
542 }
543 
544 //TODO -- decide where this function should go. It is a preparation for MPI data transfer function
545 void compressPairVectorForSingleQubitDepolarise(Qureg qureg, const int targetQubit){
546  long long int sizeInnerBlock, sizeInnerHalfBlock;
547  long long int sizeOuterColumn, sizeOuterHalfColumn;
548  long long int thisInnerBlock, // current block
549  thisOuterColumn, // current column in density matrix
550  thisIndex, // current index in (density matrix representation) state vector
551  thisIndexInOuterColumn,
552  thisIndexInInnerBlock;
553 
554  int outerBit;
555 
556  long long int thisTask;
557  const long long int numTasks=qureg.numAmpsPerChunk>>1;
558 
559  // set dimensions
560  sizeInnerHalfBlock = 1LL << targetQubit;
561  sizeInnerBlock = 2LL * sizeInnerHalfBlock;
562  sizeOuterHalfColumn = 1LL << qureg.numQubitsRepresented;
563  sizeOuterColumn = 2LL * sizeOuterHalfColumn;
564 
565 # ifdef _OPENMP
566 # pragma omp parallel \
567  default (none) \
568  shared (sizeInnerBlock,sizeInnerHalfBlock,sizeOuterColumn,sizeOuterHalfColumn,qureg) \
569  private (thisTask,thisInnerBlock,thisOuterColumn,thisIndex,thisIndexInOuterColumn, \
570  thisIndexInInnerBlock,outerBit)
571 # endif
572  {
573 # ifdef _OPENMP
574 # pragma omp for schedule (static)
575 # endif
576  // thisTask iterates over half the elements in this process' chunk of the density matrix
577  // treat this as iterating over all columns, then iterating over half the values
578  // within one column.
579  // If this function has been called, this process' chunk contains half an
580  // outer block or less
581  for (thisTask=0; thisTask<numTasks; thisTask++) {
582  // we want to process all columns in the density matrix,
583  // updating the values for half of each column (one half of each inner block)
584  thisOuterColumn = thisTask / sizeOuterHalfColumn;
585  thisIndexInOuterColumn = thisTask&(sizeOuterHalfColumn-1); // thisTask % sizeOuterHalfColumn
586  thisInnerBlock = thisIndexInOuterColumn/sizeInnerHalfBlock;
587  // get index in state vector corresponding to upper inner block
588  thisIndexInInnerBlock = thisTask&(sizeInnerHalfBlock-1); // thisTask % sizeInnerHalfBlock
589  thisIndex = thisOuterColumn*sizeOuterColumn + thisInnerBlock*sizeInnerBlock
590  + thisIndexInInnerBlock;
591  // check if we are in the upper or lower half of an outer block
592  outerBit = extractBit(targetQubit, (thisIndex+qureg.numAmpsPerChunk*qureg.chunkId)>>qureg.numQubitsRepresented);
593  // if we are in the lower half of an outer block, shift to be in the lower half
594  // of the inner block as well (we want to dephase |0><0| and |1><1| only)
595  thisIndex += outerBit*(sizeInnerHalfBlock);
596 
597  // NOTE: at this point thisIndex should be the index of the element we want to
598  // dephase in the chunk of the state vector on this process, in the
599  // density matrix representation.
600  // thisTask is the index of the pair element in pairStateVec
601  // we will populate the second half of pairStateVec with this process'
602  // data to send
603 
604  qureg.pairStateVec.real[thisTask+numTasks] = qureg.stateVec.real[thisIndex];
605  qureg.pairStateVec.imag[thisTask+numTasks] = qureg.stateVec.imag[thisIndex];
606 
607  }
608  }
609 }
610 
611 void compressPairVectorForTwoQubitDepolarise(Qureg qureg, const int targetQubit,
612  const int qubit2) {
613 
614  long long int sizeInnerBlockQ1, sizeInnerHalfBlockQ1;
615  long long int sizeInnerBlockQ2, sizeInnerHalfBlockQ2, sizeInnerQuarterBlockQ2;
616  long long int sizeOuterColumn, sizeOuterQuarterColumn;
617  long long int
618  thisInnerBlockQ2,
619  thisOuterColumn, // current column in density matrix
620  thisIndex, // current index in (density matrix representation) state vector
621  thisIndexInOuterColumn,
622  thisIndexInInnerBlockQ1,
623  thisIndexInInnerBlockQ2,
624  thisInnerBlockQ1InInnerBlockQ2;
625  int outerBitQ1, outerBitQ2;
626 
627  long long int thisTask;
628  const long long int numTasks=qureg.numAmpsPerChunk>>2;
629 
630  // set dimensions
631  sizeInnerHalfBlockQ1 = 1LL << targetQubit;
632  sizeInnerHalfBlockQ2 = 1LL << qubit2;
633  sizeInnerQuarterBlockQ2 = sizeInnerHalfBlockQ2 >> 1;
634  sizeInnerBlockQ2 = sizeInnerHalfBlockQ2 << 1;
635  sizeInnerBlockQ1 = 2LL * sizeInnerHalfBlockQ1;
636  sizeOuterColumn = 1LL << qureg.numQubitsRepresented;
637  sizeOuterQuarterColumn = sizeOuterColumn >> 2;
638 
639 # ifdef _OPENMP
640 # pragma omp parallel \
641  default (none) \
642  shared (sizeInnerBlockQ1,sizeInnerHalfBlockQ1,sizeInnerQuarterBlockQ2,sizeInnerHalfBlockQ2,sizeInnerBlockQ2, \
643  sizeOuterColumn, \
644  sizeOuterQuarterColumn,qureg) \
645  private (thisTask,thisInnerBlockQ2,thisOuterColumn,thisIndex,thisIndexInOuterColumn, \
646  thisIndexInInnerBlockQ1,thisIndexInInnerBlockQ2,thisInnerBlockQ1InInnerBlockQ2,outerBitQ1,outerBitQ2)
647 # endif
648  {
649 # ifdef _OPENMP
650 # pragma omp for schedule (static)
651 # endif
652  // thisTask iterates over half the elements in this process' chunk of the density matrix
653  // treat this as iterating over all columns, then iterating over half the values
654  // within one column.
655  // If this function has been called, this process' chunk contains half an
656  // outer block or less
657  for (thisTask=0; thisTask<numTasks; thisTask++) {
658  // we want to process all columns in the density matrix,
659  // updating the values for half of each column (one half of each inner block)
660  thisOuterColumn = thisTask / sizeOuterQuarterColumn;
661  // thisTask % sizeOuterQuarterColumn
662  thisIndexInOuterColumn = thisTask&(sizeOuterQuarterColumn-1);
663  thisInnerBlockQ2 = thisIndexInOuterColumn / sizeInnerQuarterBlockQ2;
664  // thisTask % sizeInnerQuarterBlockQ2;
665  thisIndexInInnerBlockQ2 = thisTask&(sizeInnerQuarterBlockQ2-1);
666  thisInnerBlockQ1InInnerBlockQ2 = thisIndexInInnerBlockQ2 / sizeInnerHalfBlockQ1;
667  // thisTask % sizeInnerHalfBlockQ1;
668  thisIndexInInnerBlockQ1 = thisTask&(sizeInnerHalfBlockQ1-1);
669 
670  // get index in state vector corresponding to upper inner block
671  thisIndex = thisOuterColumn*sizeOuterColumn + thisInnerBlockQ2*sizeInnerBlockQ2
672  + thisInnerBlockQ1InInnerBlockQ2*sizeInnerBlockQ1 + thisIndexInInnerBlockQ1;
673 
674  // check if we are in the upper or lower half of an outer block for Q1
675  outerBitQ1 = extractBit(targetQubit, (thisIndex+qureg.numAmpsPerChunk*qureg.chunkId)>>qureg.numQubitsRepresented);
676  // if we are in the lower half of an outer block, shift to be in the lower half
677  // of the inner block as well (we want to dephase |0><0| and |1><1| only)
678  thisIndex += outerBitQ1*(sizeInnerHalfBlockQ1);
679 
680  // check if we are in the upper or lower half of an outer block for Q2
681  outerBitQ2 = extractBit(qubit2, (thisIndex+qureg.numAmpsPerChunk*qureg.chunkId)>>qureg.numQubitsRepresented);
682  // if we are in the lower half of an outer block, shift to be in the lower half
683  // of the inner block as well (we want to dephase |0><0| and |1><1| only)
684  thisIndex += outerBitQ2*(sizeInnerQuarterBlockQ2<<1);
685 
686  // NOTE: at this point thisIndex should be the index of the element we want to
687  // dephase in the chunk of the state vector on this process, in the
688  // density matrix representation.
689  // thisTask is the index of the pair element in pairStateVec
690 
691  // state[thisIndex] = (1-depolLevel)*state[thisIndex] + depolLevel*(state[thisIndex]
692  // + pair[thisTask])/2
693  qureg.pairStateVec.real[thisTask+numTasks*2] = qureg.stateVec.real[thisIndex];
694  qureg.pairStateVec.imag[thisTask+numTasks*2] = qureg.stateVec.imag[thisIndex];
695  }
696  }
697 }
698 
699 
700 void densmatr_mixDepolarising(Qureg qureg, const int targetQubit, qreal depolLevel) {
701  if (depolLevel == 0)
702  return;
703 
704  int rankIsUpper; // rank is in the upper half of an outer block
705  int pairRank; // rank of corresponding chunk
706 
707  int useLocalDataOnly = densityMatrixBlockFitsInChunk(qureg.numAmpsPerChunk,
708  qureg.numQubitsRepresented, targetQubit);
709 
710  if (useLocalDataOnly){
711  densmatr_mixDepolarisingLocal(qureg, targetQubit, depolLevel);
712  } else {
713  // pack data to send to my pair process into the first half of pairStateVec
714  compressPairVectorForSingleQubitDepolarise(qureg, targetQubit);
715 
716  rankIsUpper = chunkIsUpperInOuterBlock(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit,
717  qureg.numQubitsRepresented);
718  pairRank = getChunkOuterBlockPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk,
719  targetQubit, qureg.numQubitsRepresented);
720 
721  exchangePairStateVectorHalves(qureg, pairRank);
722  densmatr_mixDepolarisingDistributed(qureg, targetQubit, depolLevel);
723  }
724 
725 }
726 
727 void densmatr_mixDamping(Qureg qureg, const int targetQubit, qreal damping) {
728  if (damping == 0)
729  return;
730 
731  int rankIsUpper; // rank is in the upper half of an outer block
732  int pairRank; // rank of corresponding chunk
733 
734  int useLocalDataOnly = densityMatrixBlockFitsInChunk(qureg.numAmpsPerChunk,
735  qureg.numQubitsRepresented, targetQubit);
736 
737  if (useLocalDataOnly){
738  densmatr_mixDampingLocal(qureg, targetQubit, damping);
739  } else {
740  // pack data to send to my pair process into the first half of pairStateVec
741  compressPairVectorForSingleQubitDepolarise(qureg, targetQubit);
742 
743  rankIsUpper = chunkIsUpperInOuterBlock(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit,
744  qureg.numQubitsRepresented);
745  pairRank = getChunkOuterBlockPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk,
746  targetQubit, qureg.numQubitsRepresented);
747 
748  exchangePairStateVectorHalves(qureg, pairRank);
749  densmatr_mixDampingDistributed(qureg, targetQubit, damping);
750  }
751 
752 }
753 
754 void densmatr_mixTwoQubitDepolarising(Qureg qureg, int qubit1, int qubit2, qreal depolLevel){
755  if (depolLevel == 0)
756  return;
757  int rankIsUpperBiggerQubit, rankIsUpperSmallerQubit;
758  int pairRank; // rank of corresponding chunk
759  int biggerQubit, smallerQubit;
760 
761  densmatr_mixTwoQubitDephasing(qureg, qubit1, qubit2, depolLevel);
762 
763  qreal eta = 2/depolLevel;
764  qreal delta = eta - 1 - sqrt( (eta-1)*(eta-1) - 1 );
765  qreal gamma = 1+delta;
766  gamma = 1/(gamma*gamma*gamma);
767  const qreal GAMMA_PARTS_1_OR_2 = 1.0;
768  // TODO -- test delta too small
769  /*
770  if (fabs(4*delta*(1+delta)*gamma-depolLevel)>1e-5){
771  printf("Numerical error in delta; for small error rates try Taylor expansion.\n");
772  exit(1);
773  }
774  */
775 
776  biggerQubit = qubit1 > qubit2 ? qubit1 : qubit2;
777  smallerQubit = qubit1 < qubit2 ? qubit1 : qubit2;
778  int useLocalDataOnlyBigQubit, useLocalDataOnlySmallQubit;
779 
780  useLocalDataOnlyBigQubit = densityMatrixBlockFitsInChunk(qureg.numAmpsPerChunk,
781  qureg.numQubitsRepresented, biggerQubit);
782  if (useLocalDataOnlyBigQubit){
783  // does parts 1, 2 and 3 locally in one go
784  densmatr_mixTwoQubitDepolarisingLocal(qureg, qubit1, qubit2, delta, gamma);
785  } else {
786  useLocalDataOnlySmallQubit = densityMatrixBlockFitsInChunk(qureg.numAmpsPerChunk,
787  qureg.numQubitsRepresented, smallerQubit);
788  if (useLocalDataOnlySmallQubit){
789  // do part 1 locally
790  densmatr_mixTwoQubitDepolarisingLocalPart1(qureg, smallerQubit, biggerQubit, delta);
791 
792  // do parts 2 and 3 distributed (if part 2 is distributed part 3 is also distributed)
793  // part 2 will be distributed and the value of the small qubit won't matter
794  compressPairVectorForTwoQubitDepolarise(qureg, smallerQubit, biggerQubit);
795  rankIsUpperBiggerQubit = chunkIsUpperInOuterBlock(qureg.chunkId, qureg.numAmpsPerChunk, biggerQubit,
796  qureg.numQubitsRepresented);
797  pairRank = getChunkOuterBlockPairId(rankIsUpperBiggerQubit, qureg.chunkId, qureg.numAmpsPerChunk,
798  biggerQubit, qureg.numQubitsRepresented);
799 
800  exchangePairStateVectorHalves(qureg, pairRank);
801  densmatr_mixTwoQubitDepolarisingDistributed(qureg, smallerQubit, biggerQubit, delta, GAMMA_PARTS_1_OR_2);
802 
803  // part 3 will be distributed but involve rearranging for the smaller qubit
804  compressPairVectorForTwoQubitDepolarise(qureg, smallerQubit, biggerQubit);
805  rankIsUpperBiggerQubit = chunkIsUpperInOuterBlock(qureg.chunkId, qureg.numAmpsPerChunk, biggerQubit,
806  qureg.numQubitsRepresented);
807  pairRank = getChunkOuterBlockPairId(rankIsUpperBiggerQubit, qureg.chunkId, qureg.numAmpsPerChunk,
808  biggerQubit, qureg.numQubitsRepresented);
809 
810  exchangePairStateVectorHalves(qureg, pairRank);
811  densmatr_mixTwoQubitDepolarisingQ1LocalQ2DistributedPart3(qureg, smallerQubit, biggerQubit, delta, gamma);
812  } else {
813  // do part 1, 2 and 3 distributed
814  // part 1
815  compressPairVectorForTwoQubitDepolarise(qureg, smallerQubit, biggerQubit);
816  rankIsUpperSmallerQubit = chunkIsUpperInOuterBlock(qureg.chunkId, qureg.numAmpsPerChunk, smallerQubit,
817  qureg.numQubitsRepresented);
818  pairRank = getChunkOuterBlockPairId(rankIsUpperSmallerQubit, qureg.chunkId, qureg.numAmpsPerChunk,
819  smallerQubit, qureg.numQubitsRepresented);
820 
821  exchangePairStateVectorHalves(qureg, pairRank);
822  densmatr_mixTwoQubitDepolarisingDistributed(qureg, smallerQubit, biggerQubit, delta, GAMMA_PARTS_1_OR_2);
823 
824  // part 2
825  compressPairVectorForTwoQubitDepolarise(qureg, smallerQubit, biggerQubit);
826  rankIsUpperBiggerQubit = chunkIsUpperInOuterBlock(qureg.chunkId, qureg.numAmpsPerChunk, biggerQubit,
827  qureg.numQubitsRepresented);
828  pairRank = getChunkOuterBlockPairId(rankIsUpperBiggerQubit, qureg.chunkId, qureg.numAmpsPerChunk,
829  biggerQubit, qureg.numQubitsRepresented);
830 
831  exchangePairStateVectorHalves(qureg, pairRank);
832  densmatr_mixTwoQubitDepolarisingDistributed(qureg, smallerQubit, biggerQubit, delta, GAMMA_PARTS_1_OR_2);
833 
834  // part 3
835  compressPairVectorForTwoQubitDepolarise(qureg, smallerQubit, biggerQubit);
836  pairRank = getChunkOuterBlockPairIdForPart3(rankIsUpperSmallerQubit, rankIsUpperBiggerQubit,
837  qureg.chunkId, qureg.numAmpsPerChunk, smallerQubit, biggerQubit, qureg.numQubitsRepresented);
838  exchangePairStateVectorHalves(qureg, pairRank);
839  densmatr_mixTwoQubitDepolarisingDistributed(qureg, smallerQubit, biggerQubit, delta, gamma);
840 
841  }
842  }
843 
844 }
845 
846 void statevec_compactUnitary(Qureg qureg, const int targetQubit, Complex alpha, Complex beta)
847 {
848  // flag to require memory exchange. 1: an entire block fits on one rank, 0: at most half a block fits on one rank
849  int useLocalDataOnly = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targetQubit);
850  Complex rot1, rot2;
851 
852  // rank's chunk is in upper half of block
853  int rankIsUpper;
854  int pairRank; // rank of corresponding chunk
855 
856  if (useLocalDataOnly){
857  // all values required to update state vector lie in this rank
858  statevec_compactUnitaryLocal(qureg, targetQubit, alpha, beta);
859  } else {
860  // need to get corresponding chunk of state vector from other rank
861  rankIsUpper = chunkIsUpper(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
862  getRotAngle(rankIsUpper, &rot1, &rot2, alpha, beta);
863  pairRank = getChunkPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
864  // get corresponding values from my pair
865  exchangeStateVectors(qureg, pairRank);
866 
867  // this rank's values are either in the upper of lower half of the block.
868  // send values to compactUnitaryDistributed in the correct order
869  if (rankIsUpper){
870  statevec_compactUnitaryDistributed(qureg,rot1,rot2,
871  qureg.stateVec, //upper
872  qureg.pairStateVec, //lower
873  qureg.stateVec); //output
874  } else {
875  statevec_compactUnitaryDistributed(qureg,rot1,rot2,
876  qureg.pairStateVec, //upper
877  qureg.stateVec, //lower
878  qureg.stateVec); //output
879  }
880  }
881 }
882 
883 void statevec_unitary(Qureg qureg, const int targetQubit, ComplexMatrix2 u)
884 {
885  // flag to require memory exchange. 1: an entire block fits on one rank, 0: at most half a block fits on one rank
886  int useLocalDataOnly = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targetQubit);
887  Complex rot1, rot2;
888 
889  // rank's chunk is in upper half of block
890  int rankIsUpper;
891  int pairRank; // rank of corresponding chunk
892 
893  if (useLocalDataOnly){
894  // all values required to update state vector lie in this rank
895  statevec_unitaryLocal(qureg, targetQubit, u);
896  } else {
897  // need to get corresponding chunk of state vector from other rank
898  rankIsUpper = chunkIsUpper(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
899  getRotAngleFromUnitaryMatrix(rankIsUpper, &rot1, &rot2, u);
900  pairRank = getChunkPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
901  // get corresponding values from my pair
902  exchangeStateVectors(qureg, pairRank);
903 
904  // this rank's values are either in the upper of lower half of the block.
905  // send values to compactUnitaryDistributed in the correct order
906  if (rankIsUpper){
907  statevec_unitaryDistributed(qureg,rot1,rot2,
908  qureg.stateVec, //upper
909  qureg.pairStateVec, //lower
910  qureg.stateVec); //output
911  } else {
912  statevec_unitaryDistributed(qureg,rot1,rot2,
913  qureg.pairStateVec, //upper
914  qureg.stateVec, //lower
915  qureg.stateVec); //output
916  }
917  }
918 
919 
920 }
921 
922 void statevec_controlledCompactUnitary(Qureg qureg, const int controlQubit, const int targetQubit, Complex alpha, Complex beta)
923 {
924  // flag to require memory exchange. 1: an entire block fits on one rank, 0: at most half a block fits on one rank
925  int useLocalDataOnly = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targetQubit);
926  Complex rot1, rot2;
927 
928  // rank's chunk is in upper half of block
929  int rankIsUpper;
930  int pairRank; // rank of corresponding chunk
931 
932  if (useLocalDataOnly){
933  // all values required to update state vector lie in this rank
934  statevec_controlledCompactUnitaryLocal(qureg, controlQubit, targetQubit, alpha, beta);
935  } else {
936  // need to get corresponding chunk of state vector from other rank
937  rankIsUpper = chunkIsUpper(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
938  getRotAngle(rankIsUpper, &rot1, &rot2, alpha, beta);
939  pairRank = getChunkPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
940  //printf("%d rank has pair rank: %d\n", qureg.rank, pairRank);
941  // get corresponding values from my pair
942  exchangeStateVectors(qureg, pairRank);
943 
944  // this rank's values are either in the upper of lower half of the block. send values to controlledCompactUnitaryDistributed
945  // in the correct order
946  if (rankIsUpper){
947  statevec_controlledCompactUnitaryDistributed(qureg,controlQubit,rot1,rot2,
948  qureg.stateVec, //upper
949  qureg.pairStateVec, //lower
950  qureg.stateVec); //output
951  } else {
952  statevec_controlledCompactUnitaryDistributed(qureg,controlQubit,rot1,rot2,
953  qureg.pairStateVec, //upper
954  qureg.stateVec, //lower
955  qureg.stateVec); //output
956  }
957  }
958 }
959 
960 void statevec_controlledUnitary(Qureg qureg, const int controlQubit, const int targetQubit,
961  ComplexMatrix2 u)
962 {
963  // flag to require memory exchange. 1: an entire block fits on one rank, 0: at most half a block fits on one rank
964  int useLocalDataOnly = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targetQubit);
965  Complex rot1, rot2;
966 
967  // rank's chunk is in upper half of block
968  int rankIsUpper;
969  int pairRank; // rank of corresponding chunk
970 
971  if (useLocalDataOnly){
972  // all values required to update state vector lie in this rank
973  statevec_controlledUnitaryLocal(qureg, controlQubit, targetQubit, u);
974  } else {
975  // need to get corresponding chunk of state vector from other rank
976  rankIsUpper = chunkIsUpper(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
977  getRotAngleFromUnitaryMatrix(rankIsUpper, &rot1, &rot2, u);
978  pairRank = getChunkPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
979  //printf("%d rank has pair rank: %d\n", qureg.rank, pairRank);
980  // get corresponding values from my pair
981  exchangeStateVectors(qureg, pairRank);
982 
983  // this rank's values are either in the upper of lower half of the block. send values to controlledUnitaryDistributed
984  // in the correct order
985  if (rankIsUpper){
986  statevec_controlledUnitaryDistributed(qureg,controlQubit,rot1,rot2,
987  qureg.stateVec, //upper
988  qureg.pairStateVec, //lower
989  qureg.stateVec); //output
990  } else {
991  statevec_controlledUnitaryDistributed(qureg,controlQubit,rot1,rot2,
992  qureg.pairStateVec, //upper
993  qureg.stateVec, //lower
994  qureg.stateVec); //output
995  }
996  }
997 }
998 
999 void statevec_multiControlledUnitary(Qureg qureg, long long int ctrlQubitsMask, long long int ctrlFlipMask, const int targetQubit, ComplexMatrix2 u)
1000 {
1001  // flag to require memory exchange. 1: an entire block fits on one rank, 0: at most half a block fits on one rank
1002  int useLocalDataOnly = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targetQubit);
1003  Complex rot1, rot2;
1004 
1005  // rank's chunk is in upper half of block
1006  int rankIsUpper;
1007  int pairRank; // rank of corresponding chunk
1008 
1009  if (useLocalDataOnly){
1010  // all values required to update state vector lie in this rank
1011  statevec_multiControlledUnitaryLocal(qureg, targetQubit, ctrlQubitsMask, ctrlFlipMask, u);
1012  } else {
1013  // need to get corresponding chunk of state vector from other rank
1014  rankIsUpper = chunkIsUpper(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1015  getRotAngleFromUnitaryMatrix(rankIsUpper, &rot1, &rot2, u);
1016  pairRank = getChunkPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1017 
1018  // get corresponding values from my pair
1019  exchangeStateVectors(qureg, pairRank);
1020 
1021  // this rank's values are either in the upper of lower half of the block. send values to multiControlledUnitaryDistributed
1022  // in the correct order
1023  if (rankIsUpper){
1024  statevec_multiControlledUnitaryDistributed(qureg,targetQubit,ctrlQubitsMask,ctrlFlipMask,rot1,rot2,
1025  qureg.stateVec, //upper
1026  qureg.pairStateVec, //lower
1027  qureg.stateVec); //output
1028  } else {
1029  statevec_multiControlledUnitaryDistributed(qureg,targetQubit,ctrlQubitsMask,ctrlFlipMask,rot1,rot2,
1030  qureg.pairStateVec, //upper
1031  qureg.stateVec, //lower
1032  qureg.stateVec); //output
1033  }
1034  }
1035 }
1036 void statevec_pauliX(Qureg qureg, const int targetQubit)
1037 {
1038  // flag to require memory exchange. 1: an entire block fits on one rank, 0: at most half a block fits on one rank
1039  int useLocalDataOnly = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targetQubit);
1040 
1041  // rank's chunk is in upper half of block
1042  int rankIsUpper;
1043  int pairRank; // rank of corresponding chunk
1044 
1045  if (useLocalDataOnly){
1046  // all values required to update state vector lie in this rank
1047  statevec_pauliXLocal(qureg, targetQubit);
1048  } else {
1049  // need to get corresponding chunk of state vector from other rank
1050  rankIsUpper = chunkIsUpper(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1051  pairRank = getChunkPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1052  //printf("%d rank has pair rank: %d\n", qureg.rank, pairRank);
1053  // get corresponding values from my pair
1054  exchangeStateVectors(qureg, pairRank);
1055  // this rank's values are either in the upper of lower half of the block. pauliX just replaces
1056  // this rank's values with pair values
1058  qureg.pairStateVec, // in
1059  qureg.stateVec); // out
1060  }
1061 }
1062 
1063 void statevec_controlledNot(Qureg qureg, const int controlQubit, const int targetQubit)
1064 {
1065  // flag to require memory exchange. 1: an entire block fits on one rank, 0: at most half a block fits on one rank
1066  int useLocalDataOnly = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targetQubit);
1067  int rankIsUpper; // rank's chunk is in upper half of block
1068  int pairRank; // rank of corresponding chunk
1069 
1070  if (useLocalDataOnly){
1071  // all values required to update state vector lie in this rank
1072  statevec_controlledNotLocal(qureg, controlQubit, targetQubit);
1073  } else {
1074  // need to get corresponding chunk of state vector from other rank
1075  rankIsUpper = chunkIsUpper(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1076  pairRank = getChunkPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1077  // get corresponding values from my pair
1078  exchangeStateVectors(qureg, pairRank);
1079  // this rank's values are either in the upper of lower half of the block
1080  if (rankIsUpper){
1081  statevec_controlledNotDistributed(qureg,controlQubit,
1082  qureg.pairStateVec, //in
1083  qureg.stateVec); //out
1084  } else {
1085  statevec_controlledNotDistributed(qureg,controlQubit,
1086  qureg.pairStateVec, //in
1087  qureg.stateVec); //out
1088  }
1089  }
1090 }
1091 
1092 void statevec_pauliY(Qureg qureg, const int targetQubit)
1093 {
1094  int conjFac = 1;
1095 
1096  // flag to require memory exchange. 1: an entire block fits on one rank, 0: at most half a block fits on one rank
1097  int useLocalDataOnly = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targetQubit);
1098  int rankIsUpper; // rank's chunk is in upper half of block
1099  int pairRank; // rank of corresponding chunk
1100 
1101  if (useLocalDataOnly){
1102  statevec_pauliYLocal(qureg, targetQubit, conjFac);
1103  } else {
1104  // need to get corresponding chunk of state vector from other rank
1105  rankIsUpper = chunkIsUpper(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1106  pairRank = getChunkPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1107  // get corresponding values from my pair
1108  exchangeStateVectors(qureg, pairRank);
1109  // this rank's values are either in the upper of lower half of the block
1111  qureg.pairStateVec, // in
1112  qureg.stateVec, // out
1113  rankIsUpper, conjFac);
1114  }
1115 }
1116 
1117 void statevec_pauliYConj(Qureg qureg, const int targetQubit)
1118 {
1119  int conjFac = -1;
1120 
1121  // flag to require memory exchange. 1: an entire block fits on one rank, 0: at most half a block fits on one rank
1122  int useLocalDataOnly = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targetQubit);
1123  int rankIsUpper; // rank's chunk is in upper half of block
1124  int pairRank; // rank of corresponding chunk
1125 
1126  if (useLocalDataOnly){
1127  statevec_pauliYLocal(qureg, targetQubit, conjFac);
1128  } else {
1129  // need to get corresponding chunk of state vector from other rank
1130  rankIsUpper = chunkIsUpper(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1131  pairRank = getChunkPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1132  // get corresponding values from my pair
1133  exchangeStateVectors(qureg, pairRank);
1134  // this rank's values are either in the upper of lower half of the block
1136  qureg.pairStateVec, // in
1137  qureg.stateVec, // out
1138  rankIsUpper, conjFac);
1139  }
1140 }
1141 
1142 void statevec_controlledPauliY(Qureg qureg, const int controlQubit, const int targetQubit)
1143 {
1144  int conjFac = 1;
1145 
1146  // flag to require memory exchange. 1: an entire block fits on one rank, 0: at most half a block fits on one rank
1147  int useLocalDataOnly = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targetQubit);
1148  int rankIsUpper; // rank's chunk is in upper half of block
1149  int pairRank; // rank of corresponding chunk
1150 
1151  if (useLocalDataOnly){
1152  // all values required to update state vector lie in this rank
1153  statevec_controlledPauliYLocal(qureg, controlQubit, targetQubit, conjFac);
1154  } else {
1155  // need to get corresponding chunk of state vector from other rank
1156  rankIsUpper = chunkIsUpper(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1157  pairRank = getChunkPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1158  // get corresponding values from my pair
1159  exchangeStateVectors(qureg, pairRank);
1160  // this rank's values are either in the upper of lower half of the block
1161  if (rankIsUpper){
1162  statevec_controlledPauliYDistributed(qureg,controlQubit,
1163  qureg.pairStateVec, //in
1164  qureg.stateVec,
1165  conjFac); //out
1166  } else {
1167  statevec_controlledPauliYDistributed(qureg,controlQubit,
1168  qureg.pairStateVec, //in
1169  qureg.stateVec,
1170  -conjFac); //out
1171  }
1172  }
1173 }
1174 
1175 void statevec_controlledPauliYConj(Qureg qureg, const int controlQubit, const int targetQubit)
1176 {
1177  int conjFac = -1;
1178 
1179  // flag to require memory exchange. 1: an entire block fits on one rank, 0: at most half a block fits on one rank
1180  int useLocalDataOnly = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targetQubit);
1181  int rankIsUpper; // rank's chunk is in upper half of block
1182  int pairRank; // rank of corresponding chunk
1183 
1184  if (useLocalDataOnly){
1185  // all values required to update state vector lie in this rank
1186  statevec_controlledPauliYLocal(qureg, controlQubit, targetQubit, conjFac);
1187  } else {
1188  // need to get corresponding chunk of state vector from other rank
1189  rankIsUpper = chunkIsUpper(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1190  pairRank = getChunkPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1191  // get corresponding values from my pair
1192  exchangeStateVectors(qureg, pairRank);
1193  // this rank's values are either in the upper of lower half of the block
1194  if (rankIsUpper){
1195  statevec_controlledPauliYDistributed(qureg,controlQubit,
1196  qureg.pairStateVec, //in
1197  qureg.stateVec,
1198  conjFac); //out
1199  } else {
1200  statevec_controlledPauliYDistributed(qureg,controlQubit,
1201  qureg.pairStateVec, //in
1202  qureg.stateVec,
1203  -conjFac); //out
1204  }
1205  }
1206 }
1207 
1208 void statevec_hadamard(Qureg qureg, const int targetQubit)
1209 {
1210  // flag to require memory exchange. 1: an entire block fits on one rank, 0: at most half a block fits on one rank
1211  int useLocalDataOnly = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targetQubit);
1212 
1213  // rank's chunk is in upper half of block
1214  int rankIsUpper;
1215  int pairRank; // rank of corresponding chunk
1216 
1217  if (useLocalDataOnly){
1218  // all values required to update state vector lie in this rank
1219  statevec_hadamardLocal(qureg, targetQubit);
1220  } else {
1221  // need to get corresponding chunk of state vector from other rank
1222  rankIsUpper = chunkIsUpper(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1223  pairRank = getChunkPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1224  //printf("%d rank has pair rank: %d\n", qureg.rank, pairRank);
1225  // get corresponding values from my pair
1226  exchangeStateVectors(qureg, pairRank);
1227  // this rank's values are either in the upper of lower half of the block. send values to hadamardDistributed
1228  // in the correct order
1229  if (rankIsUpper){
1231  qureg.stateVec, //upper
1232  qureg.pairStateVec, //lower
1233  qureg.stateVec, rankIsUpper); //output
1234  } else {
1236  qureg.pairStateVec, //upper
1237  qureg.stateVec, //lower
1238  qureg.stateVec, rankIsUpper); //output
1239  }
1240  }
1241 }
1242 
1243 
1254 static int isChunkToSkipInFindPZero(int chunkId, long long int chunkSize, int measureQubit)
1255 {
1256  long long int sizeHalfBlock = 1LL << (measureQubit);
1257  int numChunksToSkip = sizeHalfBlock/chunkSize;
1258  // calculate probability by summing over numChunksToSkip, then skipping numChunksToSkip, etc
1259  int bitToCheck = chunkId & numChunksToSkip;
1260  return bitToCheck;
1261 }
1262 
1263 qreal statevec_calcProbOfOutcome(Qureg qureg, const int measureQubit, int outcome)
1264 {
1265  qreal stateProb=0, totalStateProb=0;
1266  int skipValuesWithinRank = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, measureQubit);
1267  if (skipValuesWithinRank) {
1268  stateProb = statevec_findProbabilityOfZeroLocal(qureg, measureQubit);
1269  } else {
1270  if (!isChunkToSkipInFindPZero(qureg.chunkId, qureg.numAmpsPerChunk, measureQubit)){
1271  stateProb = statevec_findProbabilityOfZeroDistributed(qureg);
1272  } else stateProb = 0;
1273  }
1274  MPI_Allreduce(&stateProb, &totalStateProb, 1, MPI_QuEST_REAL, MPI_SUM, MPI_COMM_WORLD);
1275  if (outcome==1) totalStateProb = 1.0 - totalStateProb;
1276  return totalStateProb;
1277 }
1278 
1279 qreal densmatr_calcProbOfOutcome(Qureg qureg, const int measureQubit, int outcome) {
1280 
1281  qreal zeroProb = densmatr_findProbabilityOfZeroLocal(qureg, measureQubit);
1282 
1283  qreal outcomeProb;
1284  MPI_Allreduce(&zeroProb, &outcomeProb, 1, MPI_QuEST_REAL, MPI_SUM, MPI_COMM_WORLD);
1285  if (outcome == 1)
1286  outcomeProb = 1.0 - outcomeProb;
1287 
1288  return outcomeProb;
1289 }
1290 
1292 
1293  qreal localPurity = densmatr_calcPurityLocal(qureg);
1294 
1295  qreal globalPurity;
1296  MPI_Allreduce(&localPurity, &globalPurity, 1, MPI_QuEST_REAL, MPI_SUM, MPI_COMM_WORLD);
1297 
1298  return globalPurity;
1299 }
1300 
1301 void statevec_collapseToKnownProbOutcome(Qureg qureg, const int measureQubit, int outcome, qreal totalStateProb)
1302 {
1303  int skipValuesWithinRank = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, measureQubit);
1304  if (skipValuesWithinRank) {
1305  statevec_collapseToKnownProbOutcomeLocal(qureg, measureQubit, outcome, totalStateProb);
1306  } else {
1307  if (!isChunkToSkipInFindPZero(qureg.chunkId, qureg.numAmpsPerChunk, measureQubit)){
1308  // chunk has amps for q=0
1309  if (outcome==0) statevec_collapseToKnownProbOutcomeDistributedRenorm(qureg, measureQubit,
1310  totalStateProb);
1312  } else {
1313  // chunk has amps for q=1
1314  if (outcome==1) statevec_collapseToKnownProbOutcomeDistributedRenorm(qureg, measureQubit,
1315  totalStateProb);
1317  }
1318  }
1319 }
1320 
1322  // init MT random number generator with three keys -- time and pid
1323  // for the MPI version, it is ok that all procs will get the same seed as random numbers will only be
1324  // used by the master process
1325 
1326  unsigned long int key[2];
1328  // this seed will be used to generate the same random number on all procs,
1329  // therefore we want to make sure all procs receive the same key
1330  MPI_Bcast(key, 2, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD);
1331  init_by_array(key, 2);
1332 }
1333 
1338 long long int getGlobalIndOfOddParityInChunk(Qureg qureg, int qb1, int qb2) {
1339  long long int chunkStartInd = qureg.numAmpsPerChunk * qureg.chunkId;
1340  long long int chunkEndInd = chunkStartInd + qureg.numAmpsPerChunk; // exclusive
1341  long long int oddParityInd;
1342 
1343  if (extractBit(qb1, chunkStartInd) != extractBit(qb2, chunkStartInd))
1344  return chunkStartInd;
1345 
1346  oddParityInd = flipBit(chunkStartInd, qb1);
1347  if (oddParityInd >= chunkStartInd && oddParityInd < chunkEndInd)
1348  return oddParityInd;
1349 
1350  oddParityInd = flipBit(chunkStartInd, qb2);
1351  if (oddParityInd >= chunkStartInd && oddParityInd < chunkEndInd)
1352  return oddParityInd;
1353 
1354  return -1;
1355 }
1356 
1357 
1358 void statevec_swapQubitAmps(Qureg qureg, int qb1, int qb2) {
1359 
1360  // perform locally if possible
1361  int qbBig = (qb1 > qb2)? qb1 : qb2;
1362  if (halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, qbBig))
1363  return statevec_swapQubitAmpsLocal(qureg, qb1, qb2);
1364 
1365  // do nothing if this node contains no amplitudes to swap
1366  long long int oddParityGlobalInd = getGlobalIndOfOddParityInChunk(qureg, qb1, qb2);
1367  if (oddParityGlobalInd == -1)
1368  return;
1369 
1370  // determine and swap amps with pair node
1371  int pairRank = flipBit(flipBit(oddParityGlobalInd, qb1), qb2) / qureg.numAmpsPerChunk;
1372  exchangeStateVectors(qureg, pairRank);
1373  statevec_swapQubitAmpsDistributed(qureg, pairRank, qb1, qb2);
1374 }
1375 
1385 void statevec_multiControlledTwoQubitUnitary(Qureg qureg, long long int ctrlMask, const int q1, const int q2, ComplexMatrix4 u) {
1386  int q1FitsInNode = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, q1);
1387  int q2FitsInNode = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, q2);
1388 
1389  if (q1FitsInNode && q2FitsInNode) {
1390  statevec_multiControlledTwoQubitUnitaryLocal(qureg, ctrlMask, q1, q2, u);
1391 
1392  } else if (q1FitsInNode) {
1393  int qSwap = (q1 > 0)? q1-1 : q1+1;
1394 
1395  // ensure ctrl == qSwap, ensure ctrlMask updates under the swap
1396  if (maskContainsBit(ctrlMask, qSwap))
1397  ctrlMask = flipBit(flipBit(ctrlMask, q2), qSwap);
1398 
1399  statevec_swapQubitAmps(qureg, q2, qSwap);
1400  statevec_multiControlledTwoQubitUnitaryLocal(qureg, ctrlMask, q1, qSwap, u);
1401  statevec_swapQubitAmps(qureg, q2, qSwap);
1402 
1403  } else if (q2FitsInNode) {
1404  int qSwap = (q2 > 0)? q2-1 : q2+1;
1405 
1406  // ensure ctrl == qSwap, ensure ctrlMask updates under the swap
1407  if (maskContainsBit(ctrlMask, qSwap))
1408  ctrlMask = flipBit(flipBit(ctrlMask, q1), qSwap);
1409 
1410  statevec_swapQubitAmps(qureg, q1, qSwap);
1411  statevec_multiControlledTwoQubitUnitaryLocal(qureg, ctrlMask, qSwap, q2, u);
1412  statevec_swapQubitAmps(qureg, q1, qSwap);
1413 
1414  } else {
1415  // we know with certainty that both q1 and q2 >= 2
1416  int swap1 = 0;
1417  int swap2 = 1;
1418 
1419  // if ctrl == swap1 or swap2, ensure ctrlMask updates under the swap
1420  if (maskContainsBit(ctrlMask, swap1))
1421  ctrlMask = flipBit(flipBit(ctrlMask, swap1), q1);
1422  if (maskContainsBit(ctrlMask, swap2))
1423  ctrlMask = flipBit(flipBit(ctrlMask, swap2), q2);
1424 
1425  statevec_swapQubitAmps(qureg, q1, swap1);
1426  statevec_swapQubitAmps(qureg, q2, swap2);
1427  statevec_multiControlledTwoQubitUnitaryLocal(qureg, ctrlMask, swap1, swap2, u);
1428  statevec_swapQubitAmps(qureg, q1, swap1);
1429  statevec_swapQubitAmps(qureg, q2, swap2);
1430  }
1431 }
1432 
1441 void statevec_multiControlledMultiQubitUnitary(Qureg qureg, long long int ctrlMask, int* targs, const int numTargs, ComplexMatrixN u) {
1442 
1443  // bit mask of target qubits (for quick collision checking)
1444  long long int targMask = getQubitBitMask(targs, numTargs);
1445 
1446  // find lowest qubit available for swapping (isn't in targs)
1447  int freeQb=0;
1448  while (maskContainsBit(targMask, freeQb))
1449  freeQb++;
1450 
1451  // assign indices of where each target will be swapped to (else itself)
1452  int swapTargs[numTargs];
1453  for (int t=0; t<numTargs; t++) {
1454  if (halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targs[t]))
1455  swapTargs[t] = targs[t];
1456  else {
1457  // mark swap
1458  swapTargs[t] = freeQb;
1459 
1460  // update ctrlMask if swapped-out qubit was a control
1461  if (maskContainsBit(ctrlMask, swapTargs[t]))
1462  ctrlMask = flipBit(flipBit(ctrlMask, swapTargs[t]), targs[t]); // swap targ and ctrl
1463 
1464  // locate next available on-chunk qubit
1465  freeQb++;
1466  while (maskContainsBit(targMask, freeQb))
1467  freeQb++;
1468  }
1469  }
1470 
1471  // perform swaps as necessary
1472  for (int t=0; t<numTargs; t++)
1473  if (swapTargs[t] != targs[t])
1474  statevec_swapQubitAmps(qureg, targs[t], swapTargs[t]);
1475 
1476  // all target qubits have now been swapped into local memory
1477  statevec_multiControlledMultiQubitUnitaryLocal(qureg, ctrlMask, swapTargs, numTargs, u);
1478 
1479  // undo swaps
1480  for (int t=0; t<numTargs; t++)
1481  if (swapTargs[t] != targs[t])
1482  statevec_swapQubitAmps(qureg, targs[t], swapTargs[t]);
1483 }
void destroyQuESTEnv(QuESTEnv env)
Destroy the QuEST environment.
void init_by_array(unsigned long init_key[], int key_length)
Definition: mt19937ar.c:80
void statevec_pauliX(Qureg qureg, const int targetQubit)
qreal statevec_getImagAmp(Qureg qureg, long long int index)
void syncQuESTEnv(QuESTEnv env)
Guarantees that all code up to the given point has been executed on all nodes (if running in distribu...
void compressPairVectorForSingleQubitDepolarise(Qureg qureg, const int targetQubit)
void statevec_hadamardLocal(Qureg qureg, const int targetQubit)
Definition: QuEST_cpu.c:2834
static void getRotAngle(int chunkIsUpper, Complex *rot1, Complex *rot2, Complex alpha, Complex beta)
Get rotation values for a given chunk.
qreal densmatr_calcInnerProduct(Qureg a, Qureg b)
qreal densmatr_calcHilbertSchmidtDistanceSquaredLocal(Qureg a, Qureg b)
computes Tr((a-b) conjTrans(a-b)) = sum of abs values of (a-b)
Definition: QuEST_cpu.c:922
void statevec_controlledPauliYConj(Qureg qureg, const int controlQubit, const int targetQubit)
void densmatr_mixTwoQubitDepolarisingDistributed(Qureg qureg, const int targetQubit, const int qubit2, qreal delta, qreal gamma)
Definition: QuEST_cpu.c:540
int rank
Definition: QuEST.h:201
void statevec_controlledNotDistributed(Qureg qureg, const int controlQubit, ComplexArray stateVecIn, ComplexArray stateVecOut)
Rotate a single qubit by {{0,1},{1,0}.
Definition: QuEST_cpu.c:2612
static int maskContainsBit(long long int mask, int bitInd)
static int isChunkToSkipInFindPZero(int chunkId, long long int chunkSize, int measureQubit)
Find chunks to skip when calculating probability of qubit being zero.
ComplexArray pairStateVec
Temporary storage for a chunk of the state vector received from another process in the MPI version.
Definition: QuEST.h:181
qreal statevec_findProbabilityOfZeroDistributed(Qureg qureg)
Measure the probability of a specified qubit being in the zero state across all amplitudes held in th...
Definition: QuEST_cpu.c:3222
void statevec_multiControlledTwoQubitUnitaryLocal(Qureg qureg, long long int ctrlMask, const int q1, const int q2, ComplexMatrix4 u)
Definition: QuEST_cpu.c:1715
void densmatr_mixTwoQubitDepolarisingQ1LocalQ2DistributedPart3(Qureg qureg, const int targetQubit, const int qubit2, qreal delta, qreal gamma)
Definition: QuEST_cpu.c:631
void statevec_controlledCompactUnitary(Qureg qureg, const int controlQubit, const int targetQubit, Complex alpha, Complex beta)
int numChunks
Number of chunks the state vector is broken up into – the number of MPI processes used.
Definition: QuEST.h:176
qreal densmatr_calcFidelity(Qureg qureg, Qureg pureState)
void statevec_swapQubitAmpsLocal(Qureg qureg, int qb1, int qb2)
It is ensured that all amplitudes needing to be swapped are on this node.
Definition: QuEST_cpu.c:3496
void getQuESTDefaultSeedKey(unsigned long int *key)
Definition: QuEST_common.c:181
void statevec_collapseToKnownProbOutcomeLocal(Qureg qureg, int measureQubit, int outcome, qreal totalProbability)
Update the state vector to be consistent with measuring measureQubit=0 if outcome=0 and measureQubit=...
Definition: QuEST_cpu.c:3340
void densmatr_mixDamping(Qureg qureg, const int targetQubit, qreal damping)
__forceinline__ __device__ int extractBit(int locationOfBitFromRight, long long int theEncodedNumber)
Definition: QuEST_gpu.cu:82
qreal statevec_findProbabilityOfZeroLocal(Qureg qureg, const int measureQubit)
Measure the total probability of a specified qubit being in the zero state across all amplitudes in t...
Definition: QuEST_cpu.c:3166
qreal densmatr_calcPurityLocal(Qureg qureg)
Definition: QuEST_cpu.c:860
void statevec_controlledPauliY(Qureg qureg, const int controlQubit, const int targetQubit)
Represents a 4x4 matrix of complex numbers.
Definition: QuEST.h:125
Information about the environment the program is running in.
Definition: QuEST.h:199
Represents a general 2^N by 2^N matrix of complex numbers.
Definition: QuEST.h:136
void statevec_pauliXDistributed(Qureg qureg, ComplexArray stateVecIn, ComplexArray stateVecOut)
Rotate a single qubit by {{0,1},{1,0}.
Definition: QuEST_cpu.c:2522
static int getChunkOuterBlockPairId(int chunkIsUpper, int chunkId, long long int chunkSize, int targetQubit, int numQubits)
void copyVecIntoMatrixPairState(Qureg matr, Qureg vec)
This copies/clones vec (a statevector) into every node's matr pairState.
Complex statevec_calcInnerProduct(Qureg bra, Qureg ket)
#define qreal
void statevec_unitary(Qureg qureg, const int targetQubit, ComplexMatrix2 u)
void exchangePairStateVectorHalves(Qureg qureg, int pairRank)
void statevec_pauliYConj(Qureg qureg, const int targetQubit)
void statevec_swapQubitAmps(Qureg qureg, int qb1, int qb2)
void compressPairVectorForTwoQubitDepolarise(Qureg qureg, const int targetQubit, const int qubit2)
void statevec_controlledNotLocal(Qureg qureg, const int controlQubit, const int targetQubit)
Definition: QuEST_cpu.c:2550
static int getChunkPairId(int chunkIsUpper, int chunkId, long long int chunkSize, int targetQubit)
get position of corresponding chunk, holding values required to update values in my chunk (with chunk...
void statevec_multiControlledMultiQubitUnitary(Qureg qureg, long long int ctrlMask, int *targs, const int numTargs, ComplexMatrixN u)
This calls swapQubitAmps only when it would involve a distributed communication; if the qubit chunks ...
qreal densmatr_calcPurity(Qureg qureg)
long long int getQubitBitMask(int *qubits, const int numQubits)
Definition: QuEST_common.c:43
void statevec_collapseToOutcomeDistributedSetZero(Qureg qureg)
Set all amplitudes in one chunk to 0.
Definition: QuEST_cpu.c:3461
int chunkId
The position of the chunk of the state vector held by this process in the full state vector.
Definition: QuEST.h:174
void densmatr_mixDampingLocal(Qureg qureg, const int targetQubit, qreal damping)
Definition: QuEST_cpu.c:174
qreal imag[2][2]
Definition: QuEST.h:117
void statevec_multiControlledUnitaryLocal(Qureg qureg, const int targetQubit, long long int ctrlQubitsMask, long long int ctrlFlipMask, ComplexMatrix2 u)
Definition: QuEST_cpu.c:2140
void statevec_controlledCompactUnitaryLocal(Qureg qureg, const int controlQubit, const int targetQubit, Complex alpha, Complex beta)
Definition: QuEST_cpu.c:2069
long long int numAmpsPerChunk
Number of probability amplitudes held in stateVec by this process In the non-MPI version,...
Definition: QuEST.h:170
void densmatr_mixTwoQubitDepolarisingLocal(Qureg qureg, int qubit1, int qubit2, qreal delta, qreal gamma)
Definition: QuEST_cpu.c:385
static void getRotAngleFromUnitaryMatrix(int chunkIsUpper, Complex *rot1, Complex *rot2, ComplexMatrix2 u)
Get rotation values for a given chunk given a unitary matrix.
void densmatr_mixDepolarisingDistributed(Qureg qureg, const int targetQubit, qreal depolLevel)
Definition: QuEST_cpu.c:224
void statevec_controlledUnitaryDistributed(Qureg qureg, const int controlQubit, Complex rot1, Complex rot2, ComplexArray stateVecUp, ComplexArray stateVecLo, ComplexArray stateVecOut)
Rotate a single qubit in the state vector of probability amplitudes, given two complex numbers alpha ...
Definition: QuEST_cpu.c:2347
int numRanks
Definition: QuEST.h:202
void statevec_compactUnitaryDistributed(Qureg qureg, Complex rot1, Complex rot2, ComplexArray stateVecUp, ComplexArray stateVecLo, ComplexArray stateVecOut)
Rotate a single qubit in the state vector of probability amplitudes, given two complex numbers alpha ...
Definition: QuEST_cpu.c:1969
void statevec_compactUnitaryLocal(Qureg qureg, const int targetQubit, Complex alpha, Complex beta)
Definition: QuEST_cpu.c:1656
qreal densmatr_findProbabilityOfZeroLocal(Qureg qureg, const int measureQubit)
Definition: QuEST_cpu.c:3111
qreal densmatr_calcTotalProb(Qureg qureg)
void densmatr_mixDepolarising(Qureg qureg, const int targetQubit, qreal depolLevel)
void exchangeStateVectors(Qureg qureg, int pairRank)
void densmatr_mixTwoQubitDephasing(Qureg qureg, const int qubit1, const int qubit2, qreal dephase)
Definition: QuEST_cpu.c:84
void statevec_controlledNot(Qureg qureg, const int controlQubit, const int targetQubit)
void statevec_controlledUnitary(Qureg qureg, const int controlQubit, const int targetQubit, ComplexMatrix2 u)
void densmatr_initPureStateLocal(Qureg targetQureg, Qureg copyQureg)
Definition: QuEST_cpu.c:1183
void statevec_collapseToKnownProbOutcome(Qureg qureg, const int measureQubit, int outcome, qreal totalStateProb)
__forceinline__ __device__ long long int flipBit(long long int number, int bitInd)
Definition: QuEST_gpu.cu:95
void statevec_collapseToKnownProbOutcomeDistributedRenorm(Qureg qureg, const int measureQubit, const qreal totalProbability)
Renormalise parts of the state vector where measureQubit=0 or 1, based on the total probability of th...
Definition: QuEST_cpu.c:3422
void densmatr_mixDepolarisingLocal(Qureg qureg, const int targetQubit, qreal depolLevel)
Definition: QuEST_cpu.c:125
Represents a system of qubits.
Definition: QuEST.h:160
qreal densmatr_calcInnerProductLocal(Qureg a, Qureg b)
computes Tr(conjTrans(a) b) = sum of (a_ij^* b_ij)
Definition: QuEST_cpu.c:957
static int getChunkIdFromIndex(Qureg qureg, long long int index)
void statevec_pauliXLocal(Qureg qureg, const int targetQubit)
Definition: QuEST_cpu.c:2464
void densmatr_mixTwoQubitDepolarising(Qureg qureg, int qubit1, int qubit2, qreal depolLevel)
void statevec_controlledUnitaryLocal(Qureg qureg, const int controlQubit, const int targetQubit, ComplexMatrix2 u)
Definition: QuEST_cpu.c:2207
void statevec_unitaryDistributed(Qureg qureg, Complex rot1, Complex rot2, ComplexArray stateVecUp, ComplexArray stateVecLo, ComplexArray stateVecOut)
Apply a unitary operation to a single qubit given a subset of the state vector with upper and lower b...
Definition: QuEST_cpu.c:2024
void densmatr_initPureState(Qureg targetQureg, Qureg copyQureg)
static int densityMatrixBlockFitsInChunk(long long int chunkSize, int numQubits, int targetQubit)
ComplexArray stateVec
Computational state amplitudes - a subset thereof in the MPI version.
Definition: QuEST.h:179
void statevec_unitaryLocal(Qureg qureg, const int targetQubit, ComplexMatrix2 u)
Definition: QuEST_cpu.c:1900
qreal real[2][2]
Definition: QuEST.h:116
void statevec_hadamard(Qureg qureg, const int targetQubit)
void seedQuESTDefault()
Seed the Mersenne Twister used for random number generation in the QuEST environment with an example ...
void statevec_pauliY(Qureg qureg, const int targetQubit)
static int halfMatrixBlockFitsInChunk(long long int chunkSize, int targetQubit)
return whether the current qubit rotation will use blocks that fit within a single chunk.
void statevec_multiControlledUnitaryDistributed(Qureg qureg, const int targetQubit, long long int ctrlQubitsMask, long long int ctrlFlipMask, Complex rot1, Complex rot2, ComplexArray stateVecUp, ComplexArray stateVecLo, ComplexArray stateVecOut)
Apply a unitary operation to a single qubit in the state vector of probability amplitudes,...
Definition: QuEST_cpu.c:2413
void reportQuESTEnv(QuESTEnv env)
Report information about the QuEST environment.
void statevec_controlledPauliYDistributed(Qureg qureg, const int controlQubit, ComplexArray stateVecIn, ComplexArray stateVecOut, const int conjFac)
Definition: QuEST_cpu.c:2793
long long int getGlobalIndOfOddParityInChunk(Qureg qureg, int qb1, int qb2)
returns -1 if this node contains no amplitudes where qb1 and qb2 have opposite parity,...
void statevec_pauliYLocal(Qureg qureg, const int targetQubit, const int conjFac)
Definition: QuEST_cpu.c:2647
void statevec_multiControlledMultiQubitUnitaryLocal(Qureg qureg, long long int ctrlMask, int *targs, const int numTargs, ComplexMatrixN u)
Definition: QuEST_cpu.c:1814
void densmatr_mixTwoQubitDepolarisingLocalPart1(Qureg qureg, int qubit1, int qubit2, qreal delta)
Definition: QuEST_cpu.c:487
int numQubitsRepresented
The number of qubits represented in either the state-vector or density matrix.
Definition: QuEST.h:165
int syncQuESTSuccess(int successCode)
Performs a logical AND on all successCodes held by all processes.
qreal real
Definition: QuEST.h:105
void statevec_swapQubitAmpsDistributed(Qureg qureg, int pairRank, int qb1, int qb2)
qureg.pairStateVec contains the entire set of amplitudes of the paired node which includes the set of...
Definition: QuEST_cpu.c:3539
qreal statevec_calcProbOfOutcome(Qureg qureg, const int measureQubit, int outcome)
qreal imag
Definition: QuEST.h:106
static int getChunkOuterBlockPairIdForPart3(int chunkIsUpperSmallerQubit, int chunkIsUpperBiggerQubit, int chunkId, long long int chunkSize, int smallerQubit, int biggerQubit, int numQubits)
void statevec_multiControlledTwoQubitUnitary(Qureg qureg, long long int ctrlMask, const int q1, const int q2, ComplexMatrix4 u)
This calls swapQubitAmps only when it would involve a distributed communication; if the qubit chunks ...
qreal statevec_calcTotalProb(Qureg qureg)
void statevec_pauliYDistributed(Qureg qureg, ComplexArray stateVecIn, ComplexArray stateVecOut, int updateUpper, const int conjFac)
Rotate a single qubit by +-{{0,-i},{i,0}.
Definition: QuEST_cpu.c:2704
void statevec_compactUnitary(Qureg qureg, const int targetQubit, Complex alpha, Complex beta)
static int chunkIsUpperInOuterBlock(int chunkId, long long int chunkSize, int targetQubit, int numQubits)
fix – do with masking instead
void validateNumRanks(int numRanks, const char *caller)
qreal densmatr_calcHilbertSchmidtDistance(Qureg a, Qureg b)
void statevec_controlledCompactUnitaryDistributed(Qureg qureg, const int controlQubit, Complex rot1, Complex rot2, ComplexArray stateVecUp, ComplexArray stateVecLo, ComplexArray stateVecOut)
Rotate a single qubit in the state vector of probability amplitudes, given two complex numbers alpha ...
Definition: QuEST_cpu.c:2285
Represents one complex number.
Definition: QuEST.h:103
QuESTEnv createQuESTEnv(void)
Create the QuEST execution environment.
void statevec_hadamardDistributed(Qureg qureg, ComplexArray stateVecUp, ComplexArray stateVecLo, ComplexArray stateVecOut, int updateUpper)
Rotate a single qubit by {{1,1},{1,-1}}/sqrt2.
Definition: QuEST_cpu.c:2894
void statevec_multiControlledUnitary(Qureg qureg, long long int ctrlQubitsMask, long long int ctrlFlipMask, const int targetQubit, ComplexMatrix2 u)
void statevec_controlledPauliYLocal(Qureg qureg, const int controlQubit, const int targetQubit, const int conjFac)
Definition: QuEST_cpu.c:2740
qreal statevec_getRealAmp(Qureg qureg, long long int index)
void densmatr_mixDampingDistributed(Qureg qureg, const int targetQubit, qreal damping)
Definition: QuEST_cpu.c:299
static int chunkIsUpper(int chunkId, long long int chunkSize, int targetQubit)
Returns whether a given chunk in position chunkId is in the upper or lower half of a block.
Complex statevec_calcInnerProductLocal(Qureg bra, Qureg ket)
Definition: QuEST_cpu.c:1070
qreal densmatr_calcProbOfOutcome(Qureg qureg, const int measureQubit, int outcome)
Represents a 2x2 matrix of complex numbers.
Definition: QuEST.h:114
qreal densmatr_calcFidelityLocal(Qureg qureg, Qureg pureState)
computes a few dens-columns-worth of (vec^*T) dens * vec
Definition: QuEST_cpu.c:989