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 void exchangeStateVectors(Qureg qureg, int pairRank){
480  // MPI send/receive vars
481  int TAG=100;
482  MPI_Status status;
483 
484  // Multiple messages are required as MPI uses int rather than long long int for count
485  // For openmpi, messages are further restricted to 2GB in size -- do this for all cases
486  // to be safe
487  long long int maxMessageCount = MPI_MAX_AMPS_IN_MSG;
488  if (qureg.numAmpsPerChunk < maxMessageCount)
489  maxMessageCount = qureg.numAmpsPerChunk;
490 
491  // safely assume MPI_MAX... = 2^n, so division always exact
492  int numMessages = qureg.numAmpsPerChunk/maxMessageCount;
493  int i;
494  long long int offset;
495  // send my state vector to pairRank's qureg.pairStateVec
496  // receive pairRank's state vector into qureg.pairStateVec
497  for (i=0; i<numMessages; i++){
498  offset = i*maxMessageCount;
499  MPI_Sendrecv(&qureg.stateVec.real[offset], maxMessageCount, MPI_QuEST_REAL, pairRank, TAG,
500  &qureg.pairStateVec.real[offset], maxMessageCount, MPI_QuEST_REAL,
501  pairRank, TAG, MPI_COMM_WORLD, &status);
502  //printf("rank: %d err: %d\n", qureg.rank, err);
503  MPI_Sendrecv(&qureg.stateVec.imag[offset], maxMessageCount, MPI_QuEST_REAL, pairRank, TAG,
504  &qureg.pairStateVec.imag[offset], maxMessageCount, MPI_QuEST_REAL,
505  pairRank, TAG, MPI_COMM_WORLD, &status);
506  }
507 }
508 
509 void exchangePairStateVectorHalves(Qureg qureg, int pairRank){
510  // MPI send/receive vars
511  int TAG=100;
512  MPI_Status status;
513  long long int numAmpsToSend = qureg.numAmpsPerChunk >> 1;
514 
515  // Multiple messages are required as MPI uses int rather than long long int for count
516  // For openmpi, messages are further restricted to 2GB in size -- do this for all cases
517  // to be safe
518  long long int maxMessageCount = MPI_MAX_AMPS_IN_MSG;
519  if (numAmpsToSend < maxMessageCount)
520  maxMessageCount = numAmpsToSend;
521 
522  // safely assume MPI_MAX... = 2^n, so division always exact
523  int numMessages = numAmpsToSend/maxMessageCount;
524  int i;
525  long long int offset;
526  // send the bottom half of my state vector to the top half of pairRank's qureg.pairStateVec
527  // receive pairRank's state vector into the top of qureg.pairStateVec
528  for (i=0; i<numMessages; i++){
529  offset = i*maxMessageCount;
530  MPI_Sendrecv(&qureg.pairStateVec.real[offset+numAmpsToSend], maxMessageCount,
531  MPI_QuEST_REAL, pairRank, TAG,
532  &qureg.pairStateVec.real[offset], maxMessageCount, MPI_QuEST_REAL,
533  pairRank, TAG, MPI_COMM_WORLD, &status);
534  //printf("rank: %d err: %d\n", qureg.rank, err);
535  MPI_Sendrecv(&qureg.pairStateVec.imag[offset+numAmpsToSend], maxMessageCount,
536  MPI_QuEST_REAL, pairRank, TAG,
537  &qureg.pairStateVec.imag[offset], maxMessageCount, MPI_QuEST_REAL,
538  pairRank, TAG, MPI_COMM_WORLD, &status);
539  }
540 }
541 
542 //TODO -- decide where this function should go. It is a preparation for MPI data transfer function
544  long long int sizeInnerBlock, sizeInnerHalfBlock;
545  long long int sizeOuterColumn, sizeOuterHalfColumn;
546  long long int thisInnerBlock, // current block
547  thisOuterColumn, // current column in density matrix
548  thisIndex, // current index in (density matrix representation) state vector
549  thisIndexInOuterColumn,
550  thisIndexInInnerBlock;
551 
552  int outerBit;
553 
554  long long int thisTask;
555  long long int numTasks=qureg.numAmpsPerChunk>>1;
556 
557  // set dimensions
558  sizeInnerHalfBlock = 1LL << targetQubit;
559  sizeInnerBlock = 2LL * sizeInnerHalfBlock;
560  sizeOuterHalfColumn = 1LL << qureg.numQubitsRepresented;
561  sizeOuterColumn = 2LL * sizeOuterHalfColumn;
562 
563 # ifdef _OPENMP
564 # pragma omp parallel \
565  default (none) \
566  shared (sizeInnerBlock,sizeInnerHalfBlock,sizeOuterColumn,sizeOuterHalfColumn, \
567  qureg,numTasks,targetQubit) \
568  private (thisTask,thisInnerBlock,thisOuterColumn,thisIndex,thisIndexInOuterColumn, \
569  thisIndexInInnerBlock,outerBit)
570 # endif
571  {
572 # ifdef _OPENMP
573 # pragma omp for schedule (static)
574 # endif
575  // thisTask iterates over half the elements in this process' chunk of the density matrix
576  // treat this as iterating over all columns, then iterating over half the values
577  // within one column.
578  // If this function has been called, this process' chunk contains half an
579  // outer block or less
580  for (thisTask=0; thisTask<numTasks; thisTask++) {
581  // we want to process all columns in the density matrix,
582  // updating the values for half of each column (one half of each inner block)
583  thisOuterColumn = thisTask / sizeOuterHalfColumn;
584  thisIndexInOuterColumn = thisTask&(sizeOuterHalfColumn-1); // thisTask % sizeOuterHalfColumn
585  thisInnerBlock = thisIndexInOuterColumn/sizeInnerHalfBlock;
586  // get index in state vector corresponding to upper inner block
587  thisIndexInInnerBlock = thisTask&(sizeInnerHalfBlock-1); // thisTask % sizeInnerHalfBlock
588  thisIndex = thisOuterColumn*sizeOuterColumn + thisInnerBlock*sizeInnerBlock
589  + thisIndexInInnerBlock;
590  // check if we are in the upper or lower half of an outer block
591  outerBit = extractBit(targetQubit, (thisIndex+qureg.numAmpsPerChunk*qureg.chunkId)>>qureg.numQubitsRepresented);
592  // if we are in the lower half of an outer block, shift to be in the lower half
593  // of the inner block as well (we want to dephase |0><0| and |1><1| only)
594  thisIndex += outerBit*(sizeInnerHalfBlock);
595 
596  // NOTE: at this point thisIndex should be the index of the element we want to
597  // dephase in the chunk of the state vector on this process, in the
598  // density matrix representation.
599  // thisTask is the index of the pair element in pairStateVec
600  // we will populate the second half of pairStateVec with this process'
601  // data to send
602 
603  qureg.pairStateVec.real[thisTask+numTasks] = qureg.stateVec.real[thisIndex];
604  qureg.pairStateVec.imag[thisTask+numTasks] = qureg.stateVec.imag[thisIndex];
605 
606  }
607  }
608 }
609 
610 void compressPairVectorForTwoQubitDepolarise(Qureg qureg, int targetQubit,
611  int qubit2) {
612 
613  long long int sizeInnerBlockQ1, sizeInnerHalfBlockQ1;
614  long long int sizeInnerBlockQ2, sizeInnerHalfBlockQ2, sizeInnerQuarterBlockQ2;
615  long long int sizeOuterColumn, sizeOuterQuarterColumn;
616  long long int
617  thisInnerBlockQ2,
618  thisOuterColumn, // current column in density matrix
619  thisIndex, // current index in (density matrix representation) state vector
620  thisIndexInOuterColumn,
621  thisIndexInInnerBlockQ1,
622  thisIndexInInnerBlockQ2,
623  thisInnerBlockQ1InInnerBlockQ2;
624  int outerBitQ1, outerBitQ2;
625 
626  long long int thisTask;
627  long long int numTasks=qureg.numAmpsPerChunk>>2;
628 
629  // set dimensions
630  sizeInnerHalfBlockQ1 = 1LL << targetQubit;
631  sizeInnerHalfBlockQ2 = 1LL << qubit2;
632  sizeInnerQuarterBlockQ2 = sizeInnerHalfBlockQ2 >> 1;
633  sizeInnerBlockQ2 = sizeInnerHalfBlockQ2 << 1;
634  sizeInnerBlockQ1 = 2LL * sizeInnerHalfBlockQ1;
635  sizeOuterColumn = 1LL << qureg.numQubitsRepresented;
636  sizeOuterQuarterColumn = sizeOuterColumn >> 2;
637 
638 # ifdef _OPENMP
639 # pragma omp parallel \
640  default (none) \
641  shared (sizeInnerBlockQ1,sizeInnerHalfBlockQ1,sizeInnerQuarterBlockQ2,sizeInnerHalfBlockQ2,sizeInnerBlockQ2, \
642  sizeOuterColumn,sizeOuterQuarterColumn,qureg,numTasks,targetQubit,qubit2) \
643  private (thisTask,thisInnerBlockQ2,thisOuterColumn,thisIndex,thisIndexInOuterColumn, \
644  thisIndexInInnerBlockQ1,thisIndexInInnerBlockQ2,thisInnerBlockQ1InInnerBlockQ2,outerBitQ1,outerBitQ2)
645 # endif
646  {
647 # ifdef _OPENMP
648 # pragma omp for schedule (static)
649 # endif
650  // thisTask iterates over half the elements in this process' chunk of the density matrix
651  // treat this as iterating over all columns, then iterating over half the values
652  // within one column.
653  // If this function has been called, this process' chunk contains half an
654  // outer block or less
655  for (thisTask=0; thisTask<numTasks; thisTask++) {
656  // we want to process all columns in the density matrix,
657  // updating the values for half of each column (one half of each inner block)
658  thisOuterColumn = thisTask / sizeOuterQuarterColumn;
659  // thisTask % sizeOuterQuarterColumn
660  thisIndexInOuterColumn = thisTask&(sizeOuterQuarterColumn-1);
661  thisInnerBlockQ2 = thisIndexInOuterColumn / sizeInnerQuarterBlockQ2;
662  // thisTask % sizeInnerQuarterBlockQ2;
663  thisIndexInInnerBlockQ2 = thisTask&(sizeInnerQuarterBlockQ2-1);
664  thisInnerBlockQ1InInnerBlockQ2 = thisIndexInInnerBlockQ2 / sizeInnerHalfBlockQ1;
665  // thisTask % sizeInnerHalfBlockQ1;
666  thisIndexInInnerBlockQ1 = thisTask&(sizeInnerHalfBlockQ1-1);
667 
668  // get index in state vector corresponding to upper inner block
669  thisIndex = thisOuterColumn*sizeOuterColumn + thisInnerBlockQ2*sizeInnerBlockQ2
670  + thisInnerBlockQ1InInnerBlockQ2*sizeInnerBlockQ1 + thisIndexInInnerBlockQ1;
671 
672  // check if we are in the upper or lower half of an outer block for Q1
673  outerBitQ1 = extractBit(targetQubit, (thisIndex+qureg.numAmpsPerChunk*qureg.chunkId)>>qureg.numQubitsRepresented);
674  // if we are in the lower half of an outer block, shift to be in the lower half
675  // of the inner block as well (we want to dephase |0><0| and |1><1| only)
676  thisIndex += outerBitQ1*(sizeInnerHalfBlockQ1);
677 
678  // check if we are in the upper or lower half of an outer block for Q2
679  outerBitQ2 = extractBit(qubit2, (thisIndex+qureg.numAmpsPerChunk*qureg.chunkId)>>qureg.numQubitsRepresented);
680  // if we are in the lower half of an outer block, shift to be in the lower half
681  // of the inner block as well (we want to dephase |0><0| and |1><1| only)
682  thisIndex += outerBitQ2*(sizeInnerQuarterBlockQ2<<1);
683 
684  // NOTE: at this point thisIndex should be the index of the element we want to
685  // dephase in the chunk of the state vector on this process, in the
686  // density matrix representation.
687  // thisTask is the index of the pair element in pairStateVec
688 
689  // state[thisIndex] = (1-depolLevel)*state[thisIndex] + depolLevel*(state[thisIndex]
690  // + pair[thisTask])/2
691  qureg.pairStateVec.real[thisTask+numTasks*2] = qureg.stateVec.real[thisIndex];
692  qureg.pairStateVec.imag[thisTask+numTasks*2] = qureg.stateVec.imag[thisIndex];
693  }
694  }
695 }
696 
697 
698 void densmatr_mixDepolarising(Qureg qureg, int targetQubit, qreal depolLevel) {
699  if (depolLevel == 0)
700  return;
701 
702  int rankIsUpper; // rank is in the upper half of an outer block
703  int pairRank; // rank of corresponding chunk
704 
705  int useLocalDataOnly = densityMatrixBlockFitsInChunk(qureg.numAmpsPerChunk,
706  qureg.numQubitsRepresented, targetQubit);
707 
708  if (useLocalDataOnly){
709  densmatr_mixDepolarisingLocal(qureg, targetQubit, depolLevel);
710  } else {
711  // pack data to send to my pair process into the first half of pairStateVec
712  compressPairVectorForSingleQubitDepolarise(qureg, targetQubit);
713 
714  rankIsUpper = chunkIsUpperInOuterBlock(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit,
715  qureg.numQubitsRepresented);
716  pairRank = getChunkOuterBlockPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk,
717  targetQubit, qureg.numQubitsRepresented);
718 
719  exchangePairStateVectorHalves(qureg, pairRank);
720  densmatr_mixDepolarisingDistributed(qureg, targetQubit, depolLevel);
721  }
722 
723 }
724 
725 void densmatr_mixDamping(Qureg qureg, int targetQubit, qreal damping) {
726  if (damping == 0)
727  return;
728 
729  int rankIsUpper; // rank is in the upper half of an outer block
730  int pairRank; // rank of corresponding chunk
731 
732  int useLocalDataOnly = densityMatrixBlockFitsInChunk(qureg.numAmpsPerChunk,
733  qureg.numQubitsRepresented, targetQubit);
734 
735  if (useLocalDataOnly){
736  densmatr_mixDampingLocal(qureg, targetQubit, damping);
737  } else {
738  // pack data to send to my pair process into the first half of pairStateVec
739  compressPairVectorForSingleQubitDepolarise(qureg, targetQubit);
740 
741  rankIsUpper = chunkIsUpperInOuterBlock(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit,
742  qureg.numQubitsRepresented);
743  pairRank = getChunkOuterBlockPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk,
744  targetQubit, qureg.numQubitsRepresented);
745 
746  exchangePairStateVectorHalves(qureg, pairRank);
747  densmatr_mixDampingDistributed(qureg, targetQubit, damping);
748  }
749 
750 }
751 
752 void densmatr_mixTwoQubitDepolarising(Qureg qureg, int qubit1, int qubit2, qreal depolLevel){
753  if (depolLevel == 0)
754  return;
755  int rankIsUpperBiggerQubit, rankIsUpperSmallerQubit;
756  int pairRank; // rank of corresponding chunk
757  int biggerQubit, smallerQubit;
758 
759  densmatr_mixTwoQubitDephasing(qureg, qubit1, qubit2, depolLevel);
760 
761  qreal eta = 2/depolLevel;
762  qreal delta = eta - 1 - sqrt( (eta-1)*(eta-1) - 1 );
763  qreal gamma = 1+delta;
764  gamma = 1/(gamma*gamma*gamma);
765  qreal GAMMA_PARTS_1_OR_2 = 1.0;
766  // TODO -- test delta too small
767  /*
768  if (fabs(4*delta*(1+delta)*gamma-depolLevel)>1e-5){
769  printf("Numerical error in delta; for small error rates try Taylor expansion.\n");
770  exit(1);
771  }
772  */
773 
774  biggerQubit = qubit1 > qubit2 ? qubit1 : qubit2;
775  smallerQubit = qubit1 < qubit2 ? qubit1 : qubit2;
776  int useLocalDataOnlyBigQubit, useLocalDataOnlySmallQubit;
777 
778  useLocalDataOnlyBigQubit = densityMatrixBlockFitsInChunk(qureg.numAmpsPerChunk,
779  qureg.numQubitsRepresented, biggerQubit);
780  if (useLocalDataOnlyBigQubit){
781  // does parts 1, 2 and 3 locally in one go
782  densmatr_mixTwoQubitDepolarisingLocal(qureg, qubit1, qubit2, delta, gamma);
783  } else {
784  useLocalDataOnlySmallQubit = densityMatrixBlockFitsInChunk(qureg.numAmpsPerChunk,
785  qureg.numQubitsRepresented, smallerQubit);
786  if (useLocalDataOnlySmallQubit){
787  // do part 1 locally
788  densmatr_mixTwoQubitDepolarisingLocalPart1(qureg, smallerQubit, biggerQubit, delta);
789 
790  // do parts 2 and 3 distributed (if part 2 is distributed part 3 is also distributed)
791  // part 2 will be distributed and the value of the small qubit won't matter
792  compressPairVectorForTwoQubitDepolarise(qureg, smallerQubit, biggerQubit);
793  rankIsUpperBiggerQubit = chunkIsUpperInOuterBlock(qureg.chunkId, qureg.numAmpsPerChunk, biggerQubit,
794  qureg.numQubitsRepresented);
795  pairRank = getChunkOuterBlockPairId(rankIsUpperBiggerQubit, qureg.chunkId, qureg.numAmpsPerChunk,
796  biggerQubit, qureg.numQubitsRepresented);
797 
798  exchangePairStateVectorHalves(qureg, pairRank);
799  densmatr_mixTwoQubitDepolarisingDistributed(qureg, smallerQubit, biggerQubit, delta, GAMMA_PARTS_1_OR_2);
800 
801  // part 3 will be distributed but involve rearranging for the smaller qubit
802  compressPairVectorForTwoQubitDepolarise(qureg, smallerQubit, biggerQubit);
803  rankIsUpperBiggerQubit = chunkIsUpperInOuterBlock(qureg.chunkId, qureg.numAmpsPerChunk, biggerQubit,
804  qureg.numQubitsRepresented);
805  pairRank = getChunkOuterBlockPairId(rankIsUpperBiggerQubit, qureg.chunkId, qureg.numAmpsPerChunk,
806  biggerQubit, qureg.numQubitsRepresented);
807 
808  exchangePairStateVectorHalves(qureg, pairRank);
809  densmatr_mixTwoQubitDepolarisingQ1LocalQ2DistributedPart3(qureg, smallerQubit, biggerQubit, delta, gamma);
810  } else {
811  // do part 1, 2 and 3 distributed
812  // part 1
813  compressPairVectorForTwoQubitDepolarise(qureg, smallerQubit, biggerQubit);
814  rankIsUpperSmallerQubit = chunkIsUpperInOuterBlock(qureg.chunkId, qureg.numAmpsPerChunk, smallerQubit,
815  qureg.numQubitsRepresented);
816  pairRank = getChunkOuterBlockPairId(rankIsUpperSmallerQubit, qureg.chunkId, qureg.numAmpsPerChunk,
817  smallerQubit, qureg.numQubitsRepresented);
818 
819  exchangePairStateVectorHalves(qureg, pairRank);
820  densmatr_mixTwoQubitDepolarisingDistributed(qureg, smallerQubit, biggerQubit, delta, GAMMA_PARTS_1_OR_2);
821 
822  // part 2
823  compressPairVectorForTwoQubitDepolarise(qureg, smallerQubit, biggerQubit);
824  rankIsUpperBiggerQubit = chunkIsUpperInOuterBlock(qureg.chunkId, qureg.numAmpsPerChunk, biggerQubit,
825  qureg.numQubitsRepresented);
826  pairRank = getChunkOuterBlockPairId(rankIsUpperBiggerQubit, qureg.chunkId, qureg.numAmpsPerChunk,
827  biggerQubit, qureg.numQubitsRepresented);
828 
829  exchangePairStateVectorHalves(qureg, pairRank);
830  densmatr_mixTwoQubitDepolarisingDistributed(qureg, smallerQubit, biggerQubit, delta, GAMMA_PARTS_1_OR_2);
831 
832  // part 3
833  compressPairVectorForTwoQubitDepolarise(qureg, smallerQubit, biggerQubit);
834  pairRank = getChunkOuterBlockPairIdForPart3(rankIsUpperSmallerQubit, rankIsUpperBiggerQubit,
835  qureg.chunkId, qureg.numAmpsPerChunk, smallerQubit, biggerQubit, qureg.numQubitsRepresented);
836  exchangePairStateVectorHalves(qureg, pairRank);
837  densmatr_mixTwoQubitDepolarisingDistributed(qureg, smallerQubit, biggerQubit, delta, gamma);
838 
839  }
840  }
841 
842 }
843 
844 void statevec_compactUnitary(Qureg qureg, int targetQubit, Complex alpha, Complex beta)
845 {
846  // flag to require memory exchange. 1: an entire block fits on one rank, 0: at most half a block fits on one rank
847  int useLocalDataOnly = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targetQubit);
848  Complex rot1, rot2;
849 
850  // rank's chunk is in upper half of block
851  int rankIsUpper;
852  int pairRank; // rank of corresponding chunk
853 
854  if (useLocalDataOnly){
855  // all values required to update state vector lie in this rank
856  statevec_compactUnitaryLocal(qureg, targetQubit, alpha, beta);
857  } else {
858  // need to get corresponding chunk of state vector from other rank
859  rankIsUpper = chunkIsUpper(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
860  getRotAngle(rankIsUpper, &rot1, &rot2, alpha, beta);
861  pairRank = getChunkPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
862  // get corresponding values from my pair
863  exchangeStateVectors(qureg, pairRank);
864 
865  // this rank's values are either in the upper of lower half of the block.
866  // send values to compactUnitaryDistributed in the correct order
867  if (rankIsUpper){
868  statevec_compactUnitaryDistributed(qureg,rot1,rot2,
869  qureg.stateVec, //upper
870  qureg.pairStateVec, //lower
871  qureg.stateVec); //output
872  } else {
873  statevec_compactUnitaryDistributed(qureg,rot1,rot2,
874  qureg.pairStateVec, //upper
875  qureg.stateVec, //lower
876  qureg.stateVec); //output
877  }
878  }
879 }
880 
881 void statevec_unitary(Qureg qureg, int targetQubit, ComplexMatrix2 u)
882 {
883  // flag to require memory exchange. 1: an entire block fits on one rank, 0: at most half a block fits on one rank
884  int useLocalDataOnly = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targetQubit);
885  Complex rot1, rot2;
886 
887  // rank's chunk is in upper half of block
888  int rankIsUpper;
889  int pairRank; // rank of corresponding chunk
890 
891  if (useLocalDataOnly){
892  // all values required to update state vector lie in this rank
893  statevec_unitaryLocal(qureg, targetQubit, u);
894  } else {
895  // need to get corresponding chunk of state vector from other rank
896  rankIsUpper = chunkIsUpper(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
897  getRotAngleFromUnitaryMatrix(rankIsUpper, &rot1, &rot2, u);
898  pairRank = getChunkPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
899  // get corresponding values from my pair
900  exchangeStateVectors(qureg, pairRank);
901 
902  // this rank's values are either in the upper of lower half of the block.
903  // send values to compactUnitaryDistributed in the correct order
904  if (rankIsUpper){
905  statevec_unitaryDistributed(qureg,rot1,rot2,
906  qureg.stateVec, //upper
907  qureg.pairStateVec, //lower
908  qureg.stateVec); //output
909  } else {
910  statevec_unitaryDistributed(qureg,rot1,rot2,
911  qureg.pairStateVec, //upper
912  qureg.stateVec, //lower
913  qureg.stateVec); //output
914  }
915  }
916 
917 
918 }
919 
920 void statevec_controlledCompactUnitary(Qureg qureg, int controlQubit, int targetQubit, Complex alpha, Complex beta)
921 {
922  // flag to require memory exchange. 1: an entire block fits on one rank, 0: at most half a block fits on one rank
923  int useLocalDataOnly = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targetQubit);
924  Complex rot1, rot2;
925 
926  // rank's chunk is in upper half of block
927  int rankIsUpper;
928  int pairRank; // rank of corresponding chunk
929 
930  if (useLocalDataOnly){
931  // all values required to update state vector lie in this rank
932  statevec_controlledCompactUnitaryLocal(qureg, controlQubit, targetQubit, alpha, beta);
933  } else {
934  // need to get corresponding chunk of state vector from other rank
935  rankIsUpper = chunkIsUpper(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
936  getRotAngle(rankIsUpper, &rot1, &rot2, alpha, beta);
937  pairRank = getChunkPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
938  //printf("%d rank has pair rank: %d\n", qureg.rank, pairRank);
939  // get corresponding values from my pair
940  exchangeStateVectors(qureg, pairRank);
941 
942  // this rank's values are either in the upper of lower half of the block. send values to controlledCompactUnitaryDistributed
943  // in the correct order
944  if (rankIsUpper){
945  statevec_controlledCompactUnitaryDistributed(qureg,controlQubit,rot1,rot2,
946  qureg.stateVec, //upper
947  qureg.pairStateVec, //lower
948  qureg.stateVec); //output
949  } else {
950  statevec_controlledCompactUnitaryDistributed(qureg,controlQubit,rot1,rot2,
951  qureg.pairStateVec, //upper
952  qureg.stateVec, //lower
953  qureg.stateVec); //output
954  }
955  }
956 }
957 
958 void statevec_controlledUnitary(Qureg qureg, int controlQubit, int targetQubit,
959  ComplexMatrix2 u)
960 {
961  // flag to require memory exchange. 1: an entire block fits on one rank, 0: at most half a block fits on one rank
962  int useLocalDataOnly = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targetQubit);
963  Complex rot1, rot2;
964 
965  // rank's chunk is in upper half of block
966  int rankIsUpper;
967  int pairRank; // rank of corresponding chunk
968 
969  if (useLocalDataOnly){
970  // all values required to update state vector lie in this rank
971  statevec_controlledUnitaryLocal(qureg, controlQubit, targetQubit, u);
972  } else {
973  // need to get corresponding chunk of state vector from other rank
974  rankIsUpper = chunkIsUpper(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
975  getRotAngleFromUnitaryMatrix(rankIsUpper, &rot1, &rot2, u);
976  pairRank = getChunkPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
977  //printf("%d rank has pair rank: %d\n", qureg.rank, pairRank);
978  // get corresponding values from my pair
979  exchangeStateVectors(qureg, pairRank);
980 
981  // this rank's values are either in the upper of lower half of the block. send values to controlledUnitaryDistributed
982  // in the correct order
983  if (rankIsUpper){
984  statevec_controlledUnitaryDistributed(qureg,controlQubit,rot1,rot2,
985  qureg.stateVec, //upper
986  qureg.pairStateVec, //lower
987  qureg.stateVec); //output
988  } else {
989  statevec_controlledUnitaryDistributed(qureg,controlQubit,rot1,rot2,
990  qureg.pairStateVec, //upper
991  qureg.stateVec, //lower
992  qureg.stateVec); //output
993  }
994  }
995 }
996 
997 void statevec_multiControlledUnitary(Qureg qureg, long long int ctrlQubitsMask, long long int ctrlFlipMask, int targetQubit, ComplexMatrix2 u)
998 {
999  // flag to require memory exchange. 1: an entire block fits on one rank, 0: at most half a block fits on one rank
1000  int useLocalDataOnly = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targetQubit);
1001  Complex rot1, rot2;
1002 
1003  // rank's chunk is in upper half of block
1004  int rankIsUpper;
1005  int pairRank; // rank of corresponding chunk
1006 
1007  if (useLocalDataOnly){
1008  // all values required to update state vector lie in this rank
1009  statevec_multiControlledUnitaryLocal(qureg, targetQubit, ctrlQubitsMask, ctrlFlipMask, u);
1010  } else {
1011  // need to get corresponding chunk of state vector from other rank
1012  rankIsUpper = chunkIsUpper(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1013  getRotAngleFromUnitaryMatrix(rankIsUpper, &rot1, &rot2, u);
1014  pairRank = getChunkPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1015 
1016  // get corresponding values from my pair
1017  exchangeStateVectors(qureg, pairRank);
1018 
1019  // this rank's values are either in the upper of lower half of the block. send values to multiControlledUnitaryDistributed
1020  // in the correct order
1021  if (rankIsUpper){
1022  statevec_multiControlledUnitaryDistributed(qureg,targetQubit,ctrlQubitsMask,ctrlFlipMask,rot1,rot2,
1023  qureg.stateVec, //upper
1024  qureg.pairStateVec, //lower
1025  qureg.stateVec); //output
1026  } else {
1027  statevec_multiControlledUnitaryDistributed(qureg,targetQubit,ctrlQubitsMask,ctrlFlipMask,rot1,rot2,
1028  qureg.pairStateVec, //upper
1029  qureg.stateVec, //lower
1030  qureg.stateVec); //output
1031  }
1032  }
1033 }
1034 void statevec_pauliX(Qureg qureg, int targetQubit)
1035 {
1036  // flag to require memory exchange. 1: an entire block fits on one rank, 0: at most half a block fits on one rank
1037  int useLocalDataOnly = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targetQubit);
1038 
1039  // rank's chunk is in upper half of block
1040  int rankIsUpper;
1041  int pairRank; // rank of corresponding chunk
1042 
1043  if (useLocalDataOnly){
1044  // all values required to update state vector lie in this rank
1045  statevec_pauliXLocal(qureg, targetQubit);
1046  } else {
1047  // need to get corresponding chunk of state vector from other rank
1048  rankIsUpper = chunkIsUpper(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1049  pairRank = getChunkPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1050  //printf("%d rank has pair rank: %d\n", qureg.rank, pairRank);
1051  // get corresponding values from my pair
1052  exchangeStateVectors(qureg, pairRank);
1053  // this rank's values are either in the upper of lower half of the block. pauliX just replaces
1054  // this rank's values with pair values
1056  qureg.pairStateVec, // in
1057  qureg.stateVec); // out
1058  }
1059 }
1060 
1061 void statevec_controlledNot(Qureg qureg, int controlQubit, int targetQubit)
1062 {
1063  // flag to require memory exchange. 1: an entire block fits on one rank, 0: at most half a block fits on one rank
1064  int useLocalDataOnly = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targetQubit);
1065  int rankIsUpper; // rank's chunk is in upper half of block
1066  int pairRank; // rank of corresponding chunk
1067 
1068  if (useLocalDataOnly){
1069  // all values required to update state vector lie in this rank
1070  statevec_controlledNotLocal(qureg, controlQubit, targetQubit);
1071  } else {
1072  // need to get corresponding chunk of state vector from other rank
1073  rankIsUpper = chunkIsUpper(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1074  pairRank = getChunkPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1075  // get corresponding values from my pair
1076  exchangeStateVectors(qureg, pairRank);
1077  // this rank's values are either in the upper of lower half of the block
1078  if (rankIsUpper){
1079  statevec_controlledNotDistributed(qureg,controlQubit,
1080  qureg.pairStateVec, //in
1081  qureg.stateVec); //out
1082  } else {
1083  statevec_controlledNotDistributed(qureg,controlQubit,
1084  qureg.pairStateVec, //in
1085  qureg.stateVec); //out
1086  }
1087  }
1088 }
1089 
1090 void statevec_pauliY(Qureg qureg, int targetQubit)
1091 {
1092  int conjFac = 1;
1093 
1094  // flag to require memory exchange. 1: an entire block fits on one rank, 0: at most half a block fits on one rank
1095  int useLocalDataOnly = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targetQubit);
1096  int rankIsUpper; // rank's chunk is in upper half of block
1097  int pairRank; // rank of corresponding chunk
1098 
1099  if (useLocalDataOnly){
1100  statevec_pauliYLocal(qureg, targetQubit, conjFac);
1101  } else {
1102  // need to get corresponding chunk of state vector from other rank
1103  rankIsUpper = chunkIsUpper(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1104  pairRank = getChunkPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1105  // get corresponding values from my pair
1106  exchangeStateVectors(qureg, pairRank);
1107  // this rank's values are either in the upper of lower half of the block
1109  qureg.pairStateVec, // in
1110  qureg.stateVec, // out
1111  rankIsUpper, conjFac);
1112  }
1113 }
1114 
1115 void statevec_pauliYConj(Qureg qureg, int targetQubit)
1116 {
1117  int conjFac = -1;
1118 
1119  // flag to require memory exchange. 1: an entire block fits on one rank, 0: at most half a block fits on one rank
1120  int useLocalDataOnly = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targetQubit);
1121  int rankIsUpper; // rank's chunk is in upper half of block
1122  int pairRank; // rank of corresponding chunk
1123 
1124  if (useLocalDataOnly){
1125  statevec_pauliYLocal(qureg, targetQubit, conjFac);
1126  } else {
1127  // need to get corresponding chunk of state vector from other rank
1128  rankIsUpper = chunkIsUpper(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1129  pairRank = getChunkPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1130  // get corresponding values from my pair
1131  exchangeStateVectors(qureg, pairRank);
1132  // this rank's values are either in the upper of lower half of the block
1134  qureg.pairStateVec, // in
1135  qureg.stateVec, // out
1136  rankIsUpper, conjFac);
1137  }
1138 }
1139 
1140 void statevec_controlledPauliY(Qureg qureg, int controlQubit, int targetQubit)
1141 {
1142  int conjFac = 1;
1143 
1144  // flag to require memory exchange. 1: an entire block fits on one rank, 0: at most half a block fits on one rank
1145  int useLocalDataOnly = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targetQubit);
1146  int rankIsUpper; // rank's chunk is in upper half of block
1147  int pairRank; // rank of corresponding chunk
1148 
1149  if (useLocalDataOnly){
1150  // all values required to update state vector lie in this rank
1151  statevec_controlledPauliYLocal(qureg, controlQubit, targetQubit, conjFac);
1152  } else {
1153  // need to get corresponding chunk of state vector from other rank
1154  rankIsUpper = chunkIsUpper(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1155  pairRank = getChunkPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1156  // get corresponding values from my pair
1157  exchangeStateVectors(qureg, pairRank);
1158  // this rank's values are either in the upper of lower half of the block
1159  if (rankIsUpper){
1160  statevec_controlledPauliYDistributed(qureg,controlQubit,
1161  qureg.pairStateVec, //in
1162  qureg.stateVec,
1163  conjFac); //out
1164  } else {
1165  statevec_controlledPauliYDistributed(qureg,controlQubit,
1166  qureg.pairStateVec, //in
1167  qureg.stateVec,
1168  -conjFac); //out
1169  }
1170  }
1171 }
1172 
1173 void statevec_controlledPauliYConj(Qureg qureg, int controlQubit, int targetQubit)
1174 {
1175  int conjFac = -1;
1176 
1177  // flag to require memory exchange. 1: an entire block fits on one rank, 0: at most half a block fits on one rank
1178  int useLocalDataOnly = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targetQubit);
1179  int rankIsUpper; // rank's chunk is in upper half of block
1180  int pairRank; // rank of corresponding chunk
1181 
1182  if (useLocalDataOnly){
1183  // all values required to update state vector lie in this rank
1184  statevec_controlledPauliYLocal(qureg, controlQubit, targetQubit, conjFac);
1185  } else {
1186  // need to get corresponding chunk of state vector from other rank
1187  rankIsUpper = chunkIsUpper(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1188  pairRank = getChunkPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1189  // get corresponding values from my pair
1190  exchangeStateVectors(qureg, pairRank);
1191  // this rank's values are either in the upper of lower half of the block
1192  if (rankIsUpper){
1193  statevec_controlledPauliYDistributed(qureg,controlQubit,
1194  qureg.pairStateVec, //in
1195  qureg.stateVec,
1196  conjFac); //out
1197  } else {
1198  statevec_controlledPauliYDistributed(qureg,controlQubit,
1199  qureg.pairStateVec, //in
1200  qureg.stateVec,
1201  -conjFac); //out
1202  }
1203  }
1204 }
1205 
1206 void statevec_hadamard(Qureg qureg, int targetQubit)
1207 {
1208  // flag to require memory exchange. 1: an entire block fits on one rank, 0: at most half a block fits on one rank
1209  int useLocalDataOnly = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targetQubit);
1210 
1211  // rank's chunk is in upper half of block
1212  int rankIsUpper;
1213  int pairRank; // rank of corresponding chunk
1214 
1215  if (useLocalDataOnly){
1216  // all values required to update state vector lie in this rank
1217  statevec_hadamardLocal(qureg, targetQubit);
1218  } else {
1219  // need to get corresponding chunk of state vector from other rank
1220  rankIsUpper = chunkIsUpper(qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1221  pairRank = getChunkPairId(rankIsUpper, qureg.chunkId, qureg.numAmpsPerChunk, targetQubit);
1222  //printf("%d rank has pair rank: %d\n", qureg.rank, pairRank);
1223  // get corresponding values from my pair
1224  exchangeStateVectors(qureg, pairRank);
1225  // this rank's values are either in the upper of lower half of the block. send values to hadamardDistributed
1226  // in the correct order
1227  if (rankIsUpper){
1229  qureg.stateVec, //upper
1230  qureg.pairStateVec, //lower
1231  qureg.stateVec, rankIsUpper); //output
1232  } else {
1234  qureg.pairStateVec, //upper
1235  qureg.stateVec, //lower
1236  qureg.stateVec, rankIsUpper); //output
1237  }
1238  }
1239 }
1240 
1251 static int isChunkToSkipInFindPZero(int chunkId, long long int chunkSize, int measureQubit)
1252 {
1253  long long int sizeHalfBlock = 1LL << (measureQubit);
1254  int numChunksToSkip = sizeHalfBlock/chunkSize;
1255  // calculate probability by summing over numChunksToSkip, then skipping numChunksToSkip, etc
1256  int bitToCheck = chunkId & numChunksToSkip;
1257  return bitToCheck;
1258 }
1259 
1260 qreal statevec_calcProbOfOutcome(Qureg qureg, int measureQubit, int outcome)
1261 {
1262  qreal stateProb=0, totalStateProb=0;
1263  int skipValuesWithinRank = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, measureQubit);
1264  if (skipValuesWithinRank) {
1265  stateProb = statevec_findProbabilityOfZeroLocal(qureg, measureQubit);
1266  } else {
1267  if (!isChunkToSkipInFindPZero(qureg.chunkId, qureg.numAmpsPerChunk, measureQubit)){
1268  stateProb = statevec_findProbabilityOfZeroDistributed(qureg);
1269  } else stateProb = 0;
1270  }
1271  MPI_Allreduce(&stateProb, &totalStateProb, 1, MPI_QuEST_REAL, MPI_SUM, MPI_COMM_WORLD);
1272  if (outcome==1) totalStateProb = 1.0 - totalStateProb;
1273  return totalStateProb;
1274 }
1275 
1276 qreal densmatr_calcProbOfOutcome(Qureg qureg, int measureQubit, int outcome) {
1277 
1278  qreal zeroProb = densmatr_findProbabilityOfZeroLocal(qureg, measureQubit);
1279 
1280  qreal outcomeProb;
1281  MPI_Allreduce(&zeroProb, &outcomeProb, 1, MPI_QuEST_REAL, MPI_SUM, MPI_COMM_WORLD);
1282  if (outcome == 1)
1283  outcomeProb = 1.0 - outcomeProb;
1284 
1285  return outcomeProb;
1286 }
1287 
1289 
1290  qreal localPurity = densmatr_calcPurityLocal(qureg);
1291 
1292  qreal globalPurity;
1293  MPI_Allreduce(&localPurity, &globalPurity, 1, MPI_QuEST_REAL, MPI_SUM, MPI_COMM_WORLD);
1294 
1295  return globalPurity;
1296 }
1297 
1298 void statevec_collapseToKnownProbOutcome(Qureg qureg, int measureQubit, int outcome, qreal totalStateProb)
1299 {
1300  int skipValuesWithinRank = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, measureQubit);
1301  if (skipValuesWithinRank) {
1302  statevec_collapseToKnownProbOutcomeLocal(qureg, measureQubit, outcome, totalStateProb);
1303  } else {
1304  if (!isChunkToSkipInFindPZero(qureg.chunkId, qureg.numAmpsPerChunk, measureQubit)){
1305  // chunk has amps for q=0
1306  if (outcome==0) statevec_collapseToKnownProbOutcomeDistributedRenorm(qureg, measureQubit,
1307  totalStateProb);
1309  } else {
1310  // chunk has amps for q=1
1311  if (outcome==1) statevec_collapseToKnownProbOutcomeDistributedRenorm(qureg, measureQubit,
1312  totalStateProb);
1314  }
1315  }
1316 }
1317 
1319  // init MT random number generator with three keys -- time and pid
1320  // for the MPI version, it is ok that all procs will get the same seed as random numbers will only be
1321  // used by the master process
1322 
1323  unsigned long int key[2];
1325  // this seed will be used to generate the same random number on all procs,
1326  // therefore we want to make sure all procs receive the same key
1327  MPI_Bcast(key, 2, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD);
1328  init_by_array(key, 2);
1329 }
1330 
1335 long long int getGlobalIndOfOddParityInChunk(Qureg qureg, int qb1, int qb2) {
1336  long long int chunkStartInd = qureg.numAmpsPerChunk * qureg.chunkId;
1337  long long int chunkEndInd = chunkStartInd + qureg.numAmpsPerChunk; // exclusive
1338  long long int oddParityInd;
1339 
1340  if (extractBit(qb1, chunkStartInd) != extractBit(qb2, chunkStartInd))
1341  return chunkStartInd;
1342 
1343  oddParityInd = flipBit(chunkStartInd, qb1);
1344  if (oddParityInd >= chunkStartInd && oddParityInd < chunkEndInd)
1345  return oddParityInd;
1346 
1347  oddParityInd = flipBit(chunkStartInd, qb2);
1348  if (oddParityInd >= chunkStartInd && oddParityInd < chunkEndInd)
1349  return oddParityInd;
1350 
1351  return -1;
1352 }
1353 
1354 void statevec_swapQubitAmps(Qureg qureg, int qb1, int qb2) {
1355 
1356  // perform locally if possible
1357  int qbBig = (qb1 > qb2)? qb1 : qb2;
1358  if (halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, qbBig))
1359  return statevec_swapQubitAmpsLocal(qureg, qb1, qb2);
1360 
1361  // do nothing if this node contains no amplitudes to swap
1362  long long int oddParityGlobalInd = getGlobalIndOfOddParityInChunk(qureg, qb1, qb2);
1363  if (oddParityGlobalInd == -1)
1364  return;
1365 
1366  // determine and swap amps with pair node
1367  int pairRank = flipBit(flipBit(oddParityGlobalInd, qb1), qb2) / qureg.numAmpsPerChunk;
1368  exchangeStateVectors(qureg, pairRank);
1369  statevec_swapQubitAmpsDistributed(qureg, pairRank, qb1, qb2);
1370 }
1371 
1381 void statevec_multiControlledTwoQubitUnitary(Qureg qureg, long long int ctrlMask, int q1, int q2, ComplexMatrix4 u) {
1382  int q1FitsInNode = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, q1);
1383  int q2FitsInNode = halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, q2);
1384 
1385  if (q1FitsInNode && q2FitsInNode) {
1386  statevec_multiControlledTwoQubitUnitaryLocal(qureg, ctrlMask, q1, q2, u);
1387 
1388  } else if (q1FitsInNode) {
1389  int qSwap = (q1 > 0)? q1-1 : q1+1;
1390 
1391  // ensure ctrl == qSwap, ensure ctrlMask updates under the swap
1392  if (maskContainsBit(ctrlMask, qSwap))
1393  ctrlMask = flipBit(flipBit(ctrlMask, q2), qSwap);
1394 
1395  statevec_swapQubitAmps(qureg, q2, qSwap);
1396  statevec_multiControlledTwoQubitUnitaryLocal(qureg, ctrlMask, q1, qSwap, u);
1397  statevec_swapQubitAmps(qureg, q2, qSwap);
1398 
1399  } else if (q2FitsInNode) {
1400  int qSwap = (q2 > 0)? q2-1 : q2+1;
1401 
1402  // ensure ctrl == qSwap, ensure ctrlMask updates under the swap
1403  if (maskContainsBit(ctrlMask, qSwap))
1404  ctrlMask = flipBit(flipBit(ctrlMask, q1), qSwap);
1405 
1406  statevec_swapQubitAmps(qureg, q1, qSwap);
1407  statevec_multiControlledTwoQubitUnitaryLocal(qureg, ctrlMask, qSwap, q2, u);
1408  statevec_swapQubitAmps(qureg, q1, qSwap);
1409 
1410  } else {
1411  // we know with certainty that both q1 and q2 >= 2
1412  int swap1 = 0;
1413  int swap2 = 1;
1414 
1415  // if ctrl == swap1 or swap2, ensure ctrlMask updates under the swap
1416  if (maskContainsBit(ctrlMask, swap1))
1417  ctrlMask = flipBit(flipBit(ctrlMask, swap1), q1);
1418  if (maskContainsBit(ctrlMask, swap2))
1419  ctrlMask = flipBit(flipBit(ctrlMask, swap2), q2);
1420 
1421  statevec_swapQubitAmps(qureg, q1, swap1);
1422  statevec_swapQubitAmps(qureg, q2, swap2);
1423  statevec_multiControlledTwoQubitUnitaryLocal(qureg, ctrlMask, swap1, swap2, u);
1424  statevec_swapQubitAmps(qureg, q1, swap1);
1425  statevec_swapQubitAmps(qureg, q2, swap2);
1426  }
1427 }
1428 
1437 void statevec_multiControlledMultiQubitUnitary(Qureg qureg, long long int ctrlMask, int* targs, int numTargs, ComplexMatrixN u) {
1438 
1439  // bit mask of target qubits (for quick collision checking)
1440  long long int targMask = getQubitBitMask(targs, numTargs);
1441 
1442  // find lowest qubit available for swapping (isn't in targs)
1443  int freeQb=0;
1444  while (maskContainsBit(targMask, freeQb))
1445  freeQb++;
1446 
1447  // assign indices of where each target will be swapped to (else itself)
1448  int swapTargs[numTargs];
1449  for (int t=0; t<numTargs; t++) {
1450  if (halfMatrixBlockFitsInChunk(qureg.numAmpsPerChunk, targs[t]))
1451  swapTargs[t] = targs[t];
1452  else {
1453  // mark swap
1454  swapTargs[t] = freeQb;
1455 
1456  // update ctrlMask if swapped-out qubit was a control
1457  if (maskContainsBit(ctrlMask, swapTargs[t]))
1458  ctrlMask = flipBit(flipBit(ctrlMask, swapTargs[t]), targs[t]); // swap targ and ctrl
1459 
1460  // locate next available on-chunk qubit
1461  freeQb++;
1462  while (maskContainsBit(targMask, freeQb))
1463  freeQb++;
1464  }
1465  }
1466 
1467  // perform swaps as necessary
1468  for (int t=0; t<numTargs; t++)
1469  if (swapTargs[t] != targs[t])
1470  statevec_swapQubitAmps(qureg, targs[t], swapTargs[t]);
1471 
1472  // all target qubits have now been swapped into local memory
1473  statevec_multiControlledMultiQubitUnitaryLocal(qureg, ctrlMask, swapTargs, numTargs, u);
1474 
1475  // undo swaps
1476  for (int t=0; t<numTargs; t++)
1477  if (swapTargs[t] != targs[t])
1478  statevec_swapQubitAmps(qureg, targs[t], swapTargs[t]);
1479 }
1480 
1481 
1483 
1484  /* since, for every elem in 2^N op, there is a column in 2^N x 2^N qureg,
1485  * we know immediately (by each node containing at least 1 element of op)
1486  * that every node contains at least 1 column. Hence, we know that pairStateVec
1487  * of qureg can fit the entirety of op.
1488  */
1489 
1490  // load up our local contribution
1491  long long int localOffset = qureg.chunkId * op.numElemsPerChunk;
1492  memcpy(&qureg.pairStateVec.real[localOffset], op.real, op.numElemsPerChunk * sizeof(qreal));
1493  memcpy(&qureg.pairStateVec.imag[localOffset], op.imag, op.numElemsPerChunk * sizeof(qreal));
1494 
1495  // work out how many messages are needed to send op chunks (2GB limit)
1496  long long int maxMsgSize = MPI_MAX_AMPS_IN_MSG;
1497  if (op.numElemsPerChunk < maxMsgSize)
1498  maxMsgSize = op.numElemsPerChunk;
1499  int numMsgs = op.numElemsPerChunk / maxMsgSize; // since MPI_MAX... = 2^n, division is exact
1500 
1501  // each node has a turn at broadcasting its contribution of op
1502  for (int broadcaster=0; broadcaster < qureg.numChunks; broadcaster++) {
1503  long long int broadOffset = broadcaster * op.numElemsPerChunk;
1504 
1505  // (while keeping each message smaller than MPI max)
1506  for (int i=0; i<numMsgs; i++) {
1507  MPI_Bcast(
1508  &qureg.pairStateVec.real[broadOffset + i*maxMsgSize],
1509  maxMsgSize, MPI_QuEST_REAL, broadcaster, MPI_COMM_WORLD);
1510  MPI_Bcast(
1511  &qureg.pairStateVec.imag[broadOffset + i*maxMsgSize],
1512  maxMsgSize, MPI_QuEST_REAL, broadcaster, MPI_COMM_WORLD);
1513  }
1514  }
1515 }
1516 
1518 
1519  copyDiagOpIntoMatrixPairState(qureg, op);
1520  densmatr_applyDiagonalOpLocal(qureg, op);
1521 }
1522 
1524 
1525  Complex localExpec = statevec_calcExpecDiagonalOpLocal(qureg, op);
1526  if (qureg.numChunks == 1)
1527  return localExpec;
1528 
1529  qreal localReal = localExpec.real;
1530  qreal localImag = localExpec.imag;
1531  qreal globalReal, globalImag;
1532  MPI_Allreduce(&localReal, &globalReal, 1, MPI_QuEST_REAL, MPI_SUM, MPI_COMM_WORLD);
1533  MPI_Allreduce(&localImag, &globalImag, 1, MPI_QuEST_REAL, MPI_SUM, MPI_COMM_WORLD);
1534 
1535  Complex globalExpec;
1536  globalExpec.real = globalReal;
1537  globalExpec.imag = globalImag;
1538  return globalExpec;
1539 }
1540 
1542 
1543  Complex localVal = densmatr_calcExpecDiagonalOpLocal(qureg, op);
1544  if (qureg.numChunks == 1)
1545  return localVal;
1546 
1547  qreal localRe = localVal.real;
1548  qreal localIm = localVal.imag;
1549  qreal globalRe, globalIm;
1550 
1551  MPI_Allreduce(&localRe, &globalRe, 1, MPI_QuEST_REAL, MPI_SUM, MPI_COMM_WORLD);
1552  MPI_Allreduce(&localIm, &globalIm, 1, MPI_QuEST_REAL, MPI_SUM, MPI_COMM_WORLD);
1553 
1554  Complex globalVal;
1555  globalVal.real = globalRe;
1556  globalVal.imag = globalIm;
1557  return globalVal;
1558 }
void densmatr_mixDepolarising(Qureg qureg, int targetQubit, qreal depolLevel)
void destroyQuESTEnv(QuESTEnv env)
Destroy the QuEST environment.
void init_by_array(unsigned long init_key[], int key_length)
Definition: mt19937ar.c:80
qreal statevec_calcProbOfOutcome(Qureg qureg, int measureQubit, int outcome)
qreal densmatr_calcProbOfOutcome(Qureg qureg, int measureQubit, int outcome)
qreal statevec_getImagAmp(Qureg qureg, long long int index)
void statevec_hadamard(Qureg qureg, int targetQubit)
void syncQuESTEnv(QuESTEnv env)
Guarantees that all code up to the given point has been executed on all nodes (if running in distribu...
void statevec_controlledNotLocal(Qureg qureg, int controlQubit, int targetQubit)
Definition: QuEST_cpu.c:2584
static void getRotAngle(int chunkIsUpper, Complex *rot1, Complex *rot2, Complex alpha, Complex beta)
Get rotation values for a given chunk.
void statevec_pauliYLocal(Qureg qureg, int targetQubit, int conjFac)
Definition: QuEST_cpu.c:2682
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:923
int rank
Definition: QuEST.h:244
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:224
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:3262
void densmatr_mixDepolarisingDistributed(Qureg qureg, int targetQubit, qreal depolLevel)
Definition: QuEST_cpu.c:224
void densmatr_mixTwoQubitDepolarisingQ1LocalQ2DistributedPart3(Qureg qureg, int targetQubit, int qubit2, qreal delta, qreal gamma)
Definition: QuEST_cpu.c:632
int numChunks
Number of chunks the state vector is broken up into – the number of MPI processes used.
Definition: QuEST.h:219
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:3536
void statevec_multiControlledUnitaryDistributed(Qureg qureg, 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:2447
void getQuESTDefaultSeedKey(unsigned long int *key)
Definition: QuEST_common.c:182
void statevec_controlledUnitaryDistributed(Qureg qureg, 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:2381
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:3380
Complex statevec_calcExpecDiagonalOp(Qureg qureg, DiagonalOp op)
Complex densmatr_calcExpecDiagonalOp(Qureg qureg, DiagonalOp op)
void compressPairVectorForTwoQubitDepolarise(Qureg qureg, int targetQubit, int qubit2)
void statevec_compactUnitaryLocal(Qureg qureg, int targetQubit, Complex alpha, Complex beta)
Definition: QuEST_cpu.c:1688
qreal densmatr_calcPurityLocal(Qureg qureg)
Definition: QuEST_cpu.c:861
Complex statevec_calcExpecDiagonalOpLocal(Qureg qureg, DiagonalOp op)
Definition: QuEST_cpu.c:3738
Represents a 4x4 matrix of complex numbers.
Definition: QuEST.h:125
Information about the environment the program is running in.
Definition: QuEST.h:242
void statevec_multiControlledMultiQubitUnitaryLocal(Qureg qureg, long long int ctrlMask, int *targs, int numTargs, ComplexMatrixN u)
Definition: QuEST_cpu.c:1846
void statevec_multiControlledTwoQubitUnitaryLocal(Qureg qureg, long long int ctrlMask, int q1, int q2, ComplexMatrix4 u)
Definition: QuEST_cpu.c:1747
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:2556
static int getChunkOuterBlockPairId(int chunkIsUpper, int chunkId, long long int chunkSize, int targetQubit, int numQubits)
void statevec_controlledNot(Qureg qureg, int controlQubit, int targetQubit)
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_multiControlledUnitary(Qureg qureg, long long int ctrlQubitsMask, long long int ctrlFlipMask, int targetQubit, ComplexMatrix2 u)
void statevec_pauliYConj(Qureg qureg, int targetQubit)
void exchangePairStateVectorHalves(Qureg qureg, int pairRank)
__forceinline__ __device__ long long int flipBit(const long long int number, const int bitInd)
Definition: QuEST_gpu.cu:95
void statevec_controlledPauliY(Qureg qureg, int controlQubit, int targetQubit)
void statevec_swapQubitAmps(Qureg qureg, int qb1, int qb2)
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...
qreal densmatr_calcPurity(Qureg qureg)
void statevec_collapseToOutcomeDistributedSetZero(Qureg qureg)
Set all amplitudes in one chunk to 0.
Definition: QuEST_cpu.c:3501
int chunkId
The position of the chunk of the state vector held by this process in the full state vector.
Definition: QuEST.h:217
qreal imag[2][2]
Definition: QuEST.h:117
qreal * imag
The imaginary values of the 2^numQubits complex elements.
Definition: QuEST.h:191
void statevec_multiControlledTwoQubitUnitary(Qureg qureg, long long int ctrlMask, int q1, int q2, ComplexMatrix4 u)
This calls swapQubitAmps only when it would involve a distributed communication; if the qubit chunks ...
void compressPairVectorForSingleQubitDepolarise(Qureg qureg, int targetQubit)
long long int numAmpsPerChunk
Number of probability amplitudes held in stateVec by this process In the non-MPI version,...
Definition: QuEST.h:213
void densmatr_mixTwoQubitDepolarisingLocal(Qureg qureg, int qubit1, int qubit2, qreal delta, qreal gamma)
Definition: QuEST_cpu.c:387
static void getRotAngleFromUnitaryMatrix(int chunkIsUpper, Complex *rot1, Complex *rot2, ComplexMatrix2 u)
Get rotation values for a given chunk given a unitary matrix.
void statevec_controlledPauliYLocal(Qureg qureg, int controlQubit, int targetQubit, int conjFac)
Definition: QuEST_cpu.c:2776
int numRanks
Definition: QuEST.h:245
void densmatr_mixTwoQubitDephasing(Qureg qureg, int qubit1, int qubit2, qreal dephase)
Definition: QuEST_cpu.c:84
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:2001
qreal densmatr_calcTotalProb(Qureg qureg)
void exchangeStateVectors(Qureg qureg, int pairRank)
void statevec_multiControlledUnitaryLocal(Qureg qureg, int targetQubit, long long int ctrlQubitsMask, long long int ctrlFlipMask, ComplexMatrix2 u)
Definition: QuEST_cpu.c:2173
long long int getQubitBitMask(int *qubits, int numQubits)
Definition: QuEST_common.c:44
Represents a diagonal complex operator on the full Hilbert state of a Qureg.
Definition: QuEST.h:178
void statevec_pauliX(Qureg qureg, int targetQubit)
void statevec_pauliYDistributed(Qureg qureg, ComplexArray stateVecIn, ComplexArray stateVecOut, int updateUpper, int conjFac)
Rotate a single qubit by +-{{0,-i},{i,0}.
Definition: QuEST_cpu.c:2739
void densmatr_mixDampingDistributed(Qureg qureg, int targetQubit, qreal damping)
Definition: QuEST_cpu.c:300
Complex densmatr_calcExpecDiagonalOpLocal(Qureg qureg, DiagonalOp op)
Definition: QuEST_cpu.c:3781
void statevec_pauliXLocal(Qureg qureg, int targetQubit)
Definition: QuEST_cpu.c:2498
void densmatr_initPureStateLocal(Qureg targetQureg, Qureg copyQureg)
Definition: QuEST_cpu.c:1184
__forceinline__ __device__ int extractBit(const int locationOfBitFromRight, const long long int theEncodedNumber)
Definition: QuEST_gpu.cu:82
void densmatr_mixDampingLocal(Qureg qureg, int targetQubit, qreal damping)
Definition: QuEST_cpu.c:174
Represents a system of qubits.
Definition: QuEST.h:203
void densmatr_mixDamping(Qureg qureg, int targetQubit, qreal damping)
qreal densmatr_calcInnerProductLocal(Qureg a, Qureg b)
computes Tr(conjTrans(a) b) = sum of (a_ij^* b_ij)
Definition: QuEST_cpu.c:958
static int getChunkIdFromIndex(Qureg qureg, long long int index)
void densmatr_mixTwoQubitDepolarising(Qureg qureg, int qubit1, int qubit2, qreal depolLevel)
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:2056
void statevec_controlledCompactUnitaryDistributed(Qureg qureg, 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:2319
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:222
qreal real[2][2]
Definition: QuEST.h:116
void statevec_compactUnitary(Qureg qureg, int targetQubit, Complex alpha, Complex beta)
void densmatr_mixDepolarisingLocal(Qureg qureg, int targetQubit, qreal depolLevel)
Definition: QuEST_cpu.c:125
void statevec_collapseToKnownProbOutcomeDistributedRenorm(Qureg qureg, int measureQubit, qreal totalProbability)
Renormalise parts of the state vector where measureQubit=0 or 1, based on the total probability of th...
Definition: QuEST_cpu.c:3462
void statevec_multiControlledMultiQubitUnitary(Qureg qureg, long long int ctrlMask, int *targs, int numTargs, ComplexMatrixN u)
This calls swapQubitAmps only when it would involve a distributed communication; if the qubit chunks ...
void seedQuESTDefault()
Seed the Mersenne Twister used for random number generation in the QuEST environment with an example ...
long long int numElemsPerChunk
The number of the 2^numQubits amplitudes stored on each distributed node.
Definition: QuEST.h:183
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_hadamardLocal(Qureg qureg, int targetQubit)
Definition: QuEST_cpu.c:2872
void reportQuESTEnv(QuESTEnv env)
Report information about the QuEST environment.
void densmatr_applyDiagonalOpLocal(Qureg qureg, DiagonalOp op)
Definition: QuEST_cpu.c:3696
void statevec_controlledUnitaryLocal(Qureg qureg, int controlQubit, int targetQubit, ComplexMatrix2 u)
Definition: QuEST_cpu.c:2241
void densmatr_applyDiagonalOp(Qureg qureg, DiagonalOp op)
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 densmatr_mixTwoQubitDepolarisingLocalPart1(Qureg qureg, int qubit1, int qubit2, qreal delta)
Definition: QuEST_cpu.c:488
void copyDiagOpIntoMatrixPairState(Qureg qureg, DiagonalOp op)
void statevec_controlledNotDistributed(Qureg qureg, int controlQubit, ComplexArray stateVecIn, ComplexArray stateVecOut)
Rotate a single qubit by {{0,1},{1,0}.
Definition: QuEST_cpu.c:2646
void statevec_controlledCompactUnitary(Qureg qureg, int controlQubit, int targetQubit, Complex alpha, Complex beta)
int numQubitsRepresented
The number of qubits represented in either the state-vector or density matrix.
Definition: QuEST.h:208
int syncQuESTSuccess(int successCode)
Performs a logical AND on all successCodes held by all processes.
qreal * real
The real values of the 2^numQubits complex elements.
Definition: QuEST.h:189
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:3579
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 densmatr_mixTwoQubitDepolarisingDistributed(Qureg qureg, int targetQubit, int qubit2, qreal delta, qreal gamma)
Definition: QuEST_cpu.c:541
void statevec_unitaryLocal(Qureg qureg, int targetQubit, ComplexMatrix2 u)
Definition: QuEST_cpu.c:1932
qreal statevec_calcTotalProb(Qureg qureg)
qreal densmatr_findProbabilityOfZeroLocal(Qureg qureg, int measureQubit)
Definition: QuEST_cpu.c:3151
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)
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:2932
static int maskContainsBit(const long long int mask, const int bitInd)
qreal statevec_getRealAmp(Qureg qureg, long long int index)
void statevec_pauliY(Qureg qureg, int targetQubit)
void statevec_controlledUnitary(Qureg qureg, int controlQubit, int targetQubit, ComplexMatrix2 u)
void statevec_controlledPauliYDistributed(Qureg qureg, int controlQubit, ComplexArray stateVecIn, ComplexArray stateVecOut, int conjFac)
Definition: QuEST_cpu.c:2830
void statevec_controlledCompactUnitaryLocal(Qureg qureg, int controlQubit, int targetQubit, Complex alpha, Complex beta)
Definition: QuEST_cpu.c:2101
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.
void statevec_controlledPauliYConj(Qureg qureg, int controlQubit, int targetQubit)
Complex statevec_calcInnerProductLocal(Qureg bra, Qureg ket)
Definition: QuEST_cpu.c:1071
void statevec_collapseToKnownProbOutcome(Qureg qureg, int measureQubit, int outcome, qreal totalStateProb)
Represents a 2x2 matrix of complex numbers.
Definition: QuEST.h:114
void statevec_unitary(Qureg qureg, int targetQubit, ComplexMatrix2 u)
qreal densmatr_calcFidelityLocal(Qureg qureg, Qureg pureState)
computes a few dens-columns-worth of (vec^*T) dens * vec
Definition: QuEST_cpu.c:990
qreal statevec_findProbabilityOfZeroLocal(Qureg qureg, int measureQubit)
Measure the total probability of a specified qubit being in the zero state across all amplitudes in t...
Definition: QuEST_cpu.c:3206