QuEST_cpu_distributed.c File Reference
#include "QuEST.h"
#include "QuEST_internal.h"
#include "QuEST_precision.h"
#include "QuEST_validation.h"
#include "mt19937ar.h"
#include "QuEST_cpu_internal.h"
#include <unistd.h>
#include <mpi.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <sys/types.h>

Go to the source code of this file.

Macros

#define _BSD_SOURCE
 

Functions

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. More...
 
static int chunkIsUpperInOuterBlock (int chunkId, long long int chunkSize, int targetQubit, int numQubits)
 fix – do with masking instead More...
 
void compressPairVectorForSingleQubitDepolarise (Qureg qureg, const int targetQubit)
 
void compressPairVectorForTwoQubitDepolarise (Qureg qureg, const int targetQubit, const int qubit2)
 
void copyVecIntoMatrixPairState (Qureg matr, Qureg vec)
 This copies/clones vec (a statevector) into every node's matr pairState. More...
 
QuESTEnv createQuESTEnv (void)
 Create the QuEST execution environment. More...
 
static int densityMatrixBlockFitsInChunk (long long int chunkSize, int numQubits, int targetQubit)
 
qreal densmatr_calcFidelity (Qureg qureg, Qureg pureState)
 
qreal densmatr_calcHilbertSchmidtDistance (Qureg a, Qureg b)
 
qreal densmatr_calcInnerProduct (Qureg a, Qureg b)
 
qreal densmatr_calcProbOfOutcome (Qureg qureg, const int measureQubit, int outcome)
 
qreal densmatr_calcPurity (Qureg qureg)
 
qreal densmatr_calcTotalProb (Qureg qureg)
 
void densmatr_initPureState (Qureg targetQureg, Qureg copyQureg)
 
void densmatr_mixDamping (Qureg qureg, const int targetQubit, qreal damping)
 
void densmatr_mixDepolarising (Qureg qureg, const int targetQubit, qreal depolLevel)
 
void densmatr_mixTwoQubitDepolarising (Qureg qureg, int qubit1, int qubit2, qreal depolLevel)
 
void destroyQuESTEnv (QuESTEnv env)
 Destroy the QuEST environment. More...
 
void exchangePairStateVectorHalves (Qureg qureg, int pairRank)
 
void exchangeStateVectors (Qureg qureg, int pairRank)
 
static int getChunkIdFromIndex (Qureg qureg, long long int index)
 
static int getChunkOuterBlockPairId (int chunkIsUpper, int chunkId, long long int chunkSize, int targetQubit, int numQubits)
 
static int getChunkOuterBlockPairIdForPart3 (int chunkIsUpperSmallerQubit, int chunkIsUpperBiggerQubit, int chunkId, long long int chunkSize, int smallerQubit, int biggerQubit, int numQubits)
 
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 chunkId) when rotating targetQubit. More...
 
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, otherwise returns the global index of one of such contained amplitudes (not necessarily the first) More...
 
static void getRotAngle (int chunkIsUpper, Complex *rot1, Complex *rot2, Complex alpha, Complex beta)
 Get rotation values for a given chunk. More...
 
static void getRotAngleFromUnitaryMatrix (int chunkIsUpper, Complex *rot1, Complex *rot2, ComplexMatrix2 u)
 Get rotation values for a given chunk given a unitary matrix. More...
 
static int halfMatrixBlockFitsInChunk (long long int chunkSize, int targetQubit)
 return whether the current qubit rotation will use blocks that fit within a single chunk. More...
 
static int isChunkToSkipInFindPZero (int chunkId, long long int chunkSize, int measureQubit)
 Find chunks to skip when calculating probability of qubit being zero. More...
 
void reportQuESTEnv (QuESTEnv env)
 Report information about the QuEST environment. More...
 
void seedQuESTDefault ()
 Seed the Mersenne Twister used for random number generation in the QuEST environment with an example defualt seed. More...
 
Complex statevec_calcInnerProduct (Qureg bra, Qureg ket)
 
qreal statevec_calcProbOfOutcome (Qureg qureg, const int measureQubit, int outcome)
 
qreal statevec_calcTotalProb (Qureg qureg)
 
void statevec_collapseToKnownProbOutcome (Qureg qureg, const int measureQubit, int outcome, qreal totalStateProb)
 
void statevec_compactUnitary (Qureg qureg, const int targetQubit, Complex alpha, Complex beta)
 
void statevec_controlledCompactUnitary (Qureg qureg, const int controlQubit, const int targetQubit, Complex alpha, Complex beta)
 
void statevec_controlledNot (Qureg qureg, const int controlQubit, const int targetQubit)
 
void statevec_controlledPauliY (Qureg qureg, const int controlQubit, const int targetQubit)
 
void statevec_controlledPauliYConj (Qureg qureg, const int controlQubit, const int targetQubit)
 
void statevec_controlledUnitary (Qureg qureg, const int controlQubit, const int targetQubit, ComplexMatrix2 u)
 
qreal statevec_getImagAmp (Qureg qureg, long long int index)
 
qreal statevec_getRealAmp (Qureg qureg, long long int index)
 
void statevec_hadamard (Qureg qureg, const int targetQubit)
 
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 already fit in the node, it operates the unitary direct. More...
 
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 already fit in the node, it operates the unitary direct. More...
 
void statevec_multiControlledUnitary (Qureg qureg, long long int ctrlQubitsMask, long long int ctrlFlipMask, const int targetQubit, ComplexMatrix2 u)
 
void statevec_pauliX (Qureg qureg, const int targetQubit)
 
void statevec_pauliY (Qureg qureg, const int targetQubit)
 
void statevec_pauliYConj (Qureg qureg, const int targetQubit)
 
void statevec_swapQubitAmps (Qureg qureg, int qb1, int qb2)
 
void statevec_unitary (Qureg qureg, const int targetQubit, ComplexMatrix2 u)
 
void syncQuESTEnv (QuESTEnv env)
 Guarantees that all code up to the given point has been executed on all nodes (if running in distributed mode) More...
 
int syncQuESTSuccess (int successCode)
 Performs a logical AND on all successCodes held by all processes. More...
 

Detailed Description

An implementation of the backend in ../QuEST_ops.h for an MPI environment. Mostly pure-state wrappers for the local/distributed functions implemented in QuEST_cpu

Author
Ania Brown
Tyson Jones
Balint Koczor

Definition in file QuEST_cpu_distributed.c.

Macro Definition Documentation

◆ _BSD_SOURCE

#define _BSD_SOURCE

Definition at line 20 of file QuEST_cpu_distributed.c.

Function Documentation

◆ chunkIsUpper()

static int chunkIsUpper ( int  chunkId,
long long int  chunkSize,
int  targetQubit 
)
static

Returns whether a given chunk in position chunkId is in the upper or lower half of a block.

Parameters
[in]chunkIdid of chunk in state vector
[in]chunkSizenumber of amps in chunk
[in]targetQubitqubit being rotated
Returns
1: chunk is in upper half of block, 0: chunk is in lower half of block fix – is this the same as isChunkToSkip?

Definition at line 227 of file QuEST_cpu_distributed.c.

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 }

Referenced by getChunkOuterBlockPairId(), getChunkPairId(), getRotAngle(), getRotAngleFromUnitaryMatrix(), statevec_compactUnitary(), statevec_controlledCompactUnitary(), statevec_controlledNot(), statevec_controlledPauliY(), statevec_controlledPauliYConj(), statevec_controlledUnitary(), statevec_hadamard(), statevec_multiControlledUnitary(), statevec_pauliX(), statevec_pauliY(), statevec_pauliYConj(), and statevec_unitary().

◆ chunkIsUpperInOuterBlock()

static int chunkIsUpperInOuterBlock ( int  chunkId,
long long int  chunkSize,
int  targetQubit,
int  numQubits 
)
static

fix – do with masking instead

Definition at line 236 of file QuEST_cpu_distributed.c.

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 }

Referenced by densmatr_mixDamping(), densmatr_mixDepolarising(), and densmatr_mixTwoQubitDepolarising().

◆ compressPairVectorForSingleQubitDepolarise()

void compressPairVectorForSingleQubitDepolarise ( Qureg  qureg,
const int  targetQubit 
)

Definition at line 545 of file QuEST_cpu_distributed.c.

545  {
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 }

References Qureg::chunkId, extractBit(), Qureg::numAmpsPerChunk, Qureg::numQubitsRepresented, Qureg::pairStateVec, and Qureg::stateVec.

Referenced by densmatr_mixDamping(), and densmatr_mixDepolarising().

◆ compressPairVectorForTwoQubitDepolarise()

void compressPairVectorForTwoQubitDepolarise ( Qureg  qureg,
const int  targetQubit,
const int  qubit2 
)

Definition at line 611 of file QuEST_cpu_distributed.c.

612  {
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 }

References Qureg::chunkId, extractBit(), Qureg::numAmpsPerChunk, Qureg::numQubitsRepresented, Qureg::pairStateVec, and Qureg::stateVec.

Referenced by densmatr_mixTwoQubitDepolarising().

◆ copyVecIntoMatrixPairState()

void copyVecIntoMatrixPairState ( Qureg  matr,
Qureg  vec 
)

This copies/clones vec (a statevector) into every node's matr pairState.

(where matr is a density matrix or equal number of qubits as vec)

Definition at line 371 of file QuEST_cpu_distributed.c.

371  {
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 }

References Qureg::chunkId, Qureg::numAmpsPerChunk, Qureg::numChunks, Qureg::pairStateVec, qreal, and Qureg::stateVec.

Referenced by densmatr_calcFidelity(), and densmatr_initPureState().

◆ densityMatrixBlockFitsInChunk()

static int densityMatrixBlockFitsInChunk ( long long int  chunkSize,
int  numQubits,
int  targetQubit 
)
static

Definition at line 363 of file QuEST_cpu_distributed.c.

363  {
364  long long int sizeOuterHalfBlock = 1LL << (targetQubit+numQubits);
365  if (chunkSize > sizeOuterHalfBlock) return 1;
366  else return 0;
367 }

Referenced by densmatr_mixDamping(), densmatr_mixDepolarising(), and densmatr_mixTwoQubitDepolarising().

◆ densmatr_calcFidelity()

qreal densmatr_calcFidelity ( Qureg  qureg,
Qureg  pureState 
)

Definition at line 415 of file QuEST_cpu_distributed.c.

415  {
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 }

References copyVecIntoMatrixPairState(), densmatr_calcFidelityLocal(), and qreal.

◆ densmatr_calcHilbertSchmidtDistance()

qreal densmatr_calcHilbertSchmidtDistance ( Qureg  a,
Qureg  b 
)

Definition at line 430 of file QuEST_cpu_distributed.c.

430  {
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 }

References densmatr_calcHilbertSchmidtDistanceSquaredLocal(), and qreal.

◆ densmatr_calcInnerProduct()

qreal densmatr_calcInnerProduct ( Qureg  a,
Qureg  b 
)

Definition at line 441 of file QuEST_cpu_distributed.c.

441  {
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 }

References densmatr_calcInnerProductLocal(), and qreal.

◆ densmatr_calcProbOfOutcome()

qreal densmatr_calcProbOfOutcome ( Qureg  qureg,
const int  measureQubit,
int  outcome 
)

Definition at line 1279 of file QuEST_cpu_distributed.c.

1279  {
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 }

References densmatr_findProbabilityOfZeroLocal(), and qreal.

◆ densmatr_calcPurity()

qreal densmatr_calcPurity ( Qureg  qureg)

Definition at line 1291 of file QuEST_cpu_distributed.c.

1291  {
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 }

References densmatr_calcPurityLocal(), and qreal.

◆ densmatr_calcTotalProb()

qreal densmatr_calcTotalProb ( Qureg  qureg)

Definition at line 53 of file QuEST_cpu_distributed.c.

53  {
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 }

References Qureg::chunkId, Qureg::numAmpsPerChunk, Qureg::numChunks, Qureg::numQubitsRepresented, qreal, and Qureg::stateVec.

◆ densmatr_initPureState()

void densmatr_initPureState ( Qureg  targetQureg,
Qureg  copyQureg 
)

Definition at line 452 of file QuEST_cpu_distributed.c.

452  {
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 }

References copyVecIntoMatrixPairState(), densmatr_initPureStateLocal(), Qureg::numChunks, Qureg::pairStateVec, qreal, and Qureg::stateVec.

◆ densmatr_mixDamping()

void densmatr_mixDamping ( Qureg  qureg,
const int  targetQubit,
qreal  damping 
)

Definition at line 727 of file QuEST_cpu_distributed.c.

727  {
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 }

References Qureg::chunkId, chunkIsUpperInOuterBlock(), compressPairVectorForSingleQubitDepolarise(), densityMatrixBlockFitsInChunk(), densmatr_mixDampingDistributed(), densmatr_mixDampingLocal(), exchangePairStateVectorHalves(), getChunkOuterBlockPairId(), Qureg::numAmpsPerChunk, and Qureg::numQubitsRepresented.

◆ densmatr_mixDepolarising()

void densmatr_mixDepolarising ( Qureg  qureg,
const int  targetQubit,
qreal  depolLevel 
)

Definition at line 700 of file QuEST_cpu_distributed.c.

700  {
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 }

References Qureg::chunkId, chunkIsUpperInOuterBlock(), compressPairVectorForSingleQubitDepolarise(), densityMatrixBlockFitsInChunk(), densmatr_mixDepolarisingDistributed(), densmatr_mixDepolarisingLocal(), exchangePairStateVectorHalves(), getChunkOuterBlockPairId(), Qureg::numAmpsPerChunk, and Qureg::numQubitsRepresented.

◆ densmatr_mixTwoQubitDepolarising()

void densmatr_mixTwoQubitDepolarising ( Qureg  qureg,
int  qubit1,
int  qubit2,
qreal  depolLevel 
)

Definition at line 754 of file QuEST_cpu_distributed.c.

754  {
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 }

References Qureg::chunkId, chunkIsUpperInOuterBlock(), compressPairVectorForTwoQubitDepolarise(), densityMatrixBlockFitsInChunk(), densmatr_mixTwoQubitDephasing(), densmatr_mixTwoQubitDepolarisingDistributed(), densmatr_mixTwoQubitDepolarisingLocal(), densmatr_mixTwoQubitDepolarisingLocalPart1(), densmatr_mixTwoQubitDepolarisingQ1LocalQ2DistributedPart3(), exchangePairStateVectorHalves(), getChunkOuterBlockPairId(), getChunkOuterBlockPairIdForPart3(), Qureg::numAmpsPerChunk, Qureg::numQubitsRepresented, and qreal.

◆ exchangePairStateVectorHalves()

void exchangePairStateVectorHalves ( Qureg  qureg,
int  pairRank 
)

Definition at line 511 of file QuEST_cpu_distributed.c.

511  {
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 }

References Qureg::numAmpsPerChunk, and Qureg::pairStateVec.

Referenced by densmatr_mixDamping(), densmatr_mixDepolarising(), and densmatr_mixTwoQubitDepolarising().

◆ exchangeStateVectors()

void exchangeStateVectors ( Qureg  qureg,
int  pairRank 
)

Definition at line 481 of file QuEST_cpu_distributed.c.

481  {
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 }

References Qureg::numAmpsPerChunk, Qureg::pairStateVec, and Qureg::stateVec.

Referenced by statevec_compactUnitary(), statevec_controlledCompactUnitary(), statevec_controlledNot(), statevec_controlledPauliY(), statevec_controlledPauliYConj(), statevec_controlledUnitary(), statevec_hadamard(), statevec_multiControlledUnitary(), statevec_pauliX(), statevec_pauliY(), statevec_pauliYConj(), statevec_swapQubitAmps(), and statevec_unitary().

◆ getChunkIdFromIndex()

int getChunkIdFromIndex ( Qureg  qureg,
long long int  index 
)
static

Definition at line 194 of file QuEST_cpu_distributed.c.

194  {
195  return index/qureg.numAmpsPerChunk; // this is numAmpsPerChunk
196 }

References Qureg::numAmpsPerChunk.

Referenced by statevec_getImagAmp(), and statevec_getRealAmp().

◆ getChunkOuterBlockPairId()

static int getChunkOuterBlockPairId ( int  chunkIsUpper,
int  chunkId,
long long int  chunkSize,
int  targetQubit,
int  numQubits 
)
static

Definition at line 314 of file QuEST_cpu_distributed.c.

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 }

References chunkIsUpper().

Referenced by densmatr_mixDamping(), densmatr_mixDepolarising(), and densmatr_mixTwoQubitDepolarising().

◆ getChunkOuterBlockPairIdForPart3()

static int getChunkOuterBlockPairIdForPart3 ( int  chunkIsUpperSmallerQubit,
int  chunkIsUpperBiggerQubit,
int  chunkId,
long long int  chunkSize,
int  smallerQubit,
int  biggerQubit,
int  numQubits 
)
static

Definition at line 325 of file QuEST_cpu_distributed.c.

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 }

Referenced by densmatr_mixTwoQubitDepolarising().

◆ getChunkPairId()

static int getChunkPairId ( int  chunkIsUpper,
int  chunkId,
long long int  chunkSize,
int  targetQubit 
)
static

get position of corresponding chunk, holding values required to update values in my chunk (with chunkId) when rotating targetQubit.

Parameters
[in]chunkIsUpper1: chunk is in upper half of block, 0: chunk is in lower half
[in]chunkIdid of chunk in state vector
[in]chunkSizenumber of amps in chunk
[in]targetQubitqubit being rotated
Returns
chunkId of chunk required to rotate targetQubit

Definition at line 303 of file QuEST_cpu_distributed.c.

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 }

References chunkIsUpper().

Referenced by statevec_compactUnitary(), statevec_controlledCompactUnitary(), statevec_controlledNot(), statevec_controlledPauliY(), statevec_controlledPauliYConj(), statevec_controlledUnitary(), statevec_hadamard(), statevec_multiControlledUnitary(), statevec_pauliX(), statevec_pauliY(), statevec_pauliYConj(), and statevec_unitary().

◆ getGlobalIndOfOddParityInChunk()

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, otherwise returns the global index of one of such contained amplitudes (not necessarily the first)

Definition at line 1338 of file QuEST_cpu_distributed.c.

1338  {
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 }

References Qureg::chunkId, extractBit(), flipBit(), and Qureg::numAmpsPerChunk.

Referenced by statevec_swapQubitAmps().

◆ getRotAngle()

static void getRotAngle ( int  chunkIsUpper,
Complex rot1,
Complex rot2,
Complex  alpha,
Complex  beta 
)
static

Get rotation values for a given chunk.

Parameters
[in]chunkIsUpper1: chunk is in upper half of block, 0: chunk is in lower half
[out]rot1,rot2rotation values to use, allocated for upper/lower such that
stateUpper = rot1 * stateUpper + conj(rot2)  * stateLower
or
stateLower = rot1 * stateUpper + conj(rot2)  * stateLower
[in]alpha,betainitial rotation values

Definition at line 258 of file QuEST_cpu_distributed.c.

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 }

References chunkIsUpper(), Complex::imag, and Complex::real.

Referenced by statevec_compactUnitary(), and statevec_controlledCompactUnitary().

◆ getRotAngleFromUnitaryMatrix()

static void getRotAngleFromUnitaryMatrix ( int  chunkIsUpper,
Complex rot1,
Complex rot2,
ComplexMatrix2  u 
)
static

Get rotation values for a given chunk given a unitary matrix.

Parameters
[in]chunkIsUpper1: chunk is in upper half of block, 0: chunk is in lower half
[out]rot1,rot2rotation values to use, allocated for upper/lower such that
stateUpper = rot1 * stateUpper + conj(rot2)  * stateLower
or
stateLower = rot1 * stateUpper + conj(rot2)  * stateLower
[in]uunitary matrix operation

Definition at line 283 of file QuEST_cpu_distributed.c.

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 }

References chunkIsUpper(), ComplexMatrix2::imag, Complex::real, and ComplexMatrix2::real.

Referenced by statevec_controlledUnitary(), statevec_multiControlledUnitary(), and statevec_unitary().

◆ halfMatrixBlockFitsInChunk()

static int halfMatrixBlockFitsInChunk ( long long int  chunkSize,
int  targetQubit 
)
static

return whether the current qubit rotation will use blocks that fit within a single chunk.

Parameters
[in]chunkSizenumber of amps in chunk
[in]targetQubitqubit being rotated
Returns
1: one chunk fits in one block 0: chunk is larger than block fix – this should be renamed to matrixBlockFitsInChunk

Definition at line 356 of file QuEST_cpu_distributed.c.

357 {
358  long long int sizeHalfBlock = 1LL << (targetQubit);
359  if (chunkSize > sizeHalfBlock) return 1;
360  else return 0;
361 }

Referenced by statevec_calcProbOfOutcome(), statevec_collapseToKnownProbOutcome(), statevec_compactUnitary(), statevec_controlledCompactUnitary(), statevec_controlledNot(), statevec_controlledPauliY(), statevec_controlledPauliYConj(), statevec_controlledUnitary(), statevec_hadamard(), statevec_multiControlledMultiQubitUnitary(), statevec_multiControlledTwoQubitUnitary(), statevec_multiControlledUnitary(), statevec_pauliX(), statevec_pauliY(), statevec_pauliYConj(), statevec_swapQubitAmps(), and statevec_unitary().

◆ isChunkToSkipInFindPZero()

static int isChunkToSkipInFindPZero ( int  chunkId,
long long int  chunkSize,
int  measureQubit 
)
static

Find chunks to skip when calculating probability of qubit being zero.

When calculating probability of a bit q being zero, sum up 2^q values, then skip 2^q values, etc. This function finds if an entire chunk is in the range of values to be skipped

Parameters
[in]chunkIdid of chunk in state vector
[in]chunkSizenumber of amps in chunk
[in]measureQubiqubit being measured
Returns
int – 1: skip, 0: don't skip

Definition at line 1254 of file QuEST_cpu_distributed.c.

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 }

Referenced by statevec_calcProbOfOutcome(), and statevec_collapseToKnownProbOutcome().

◆ statevec_calcInnerProduct()

Complex statevec_calcInnerProduct ( Qureg  bra,
Qureg  ket 
)

Definition at line 35 of file QuEST_cpu_distributed.c.

35  {
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 }

References Complex::imag, Qureg::numChunks, qreal, Complex::real, and statevec_calcInnerProductLocal().

◆ statevec_calcProbOfOutcome()

qreal statevec_calcProbOfOutcome ( Qureg  qureg,
const int  measureQubit,
int  outcome 
)

Definition at line 1263 of file QuEST_cpu_distributed.c.

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 }

References Qureg::chunkId, halfMatrixBlockFitsInChunk(), isChunkToSkipInFindPZero(), Qureg::numAmpsPerChunk, qreal, statevec_findProbabilityOfZeroDistributed(), and statevec_findProbabilityOfZeroLocal().

◆ statevec_calcTotalProb()

qreal statevec_calcTotalProb ( Qureg  qureg)

Definition at line 88 of file QuEST_cpu_distributed.c.

88  {
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 }

References Qureg::numAmpsPerChunk, Qureg::numChunks, qreal, and Qureg::stateVec.

◆ statevec_collapseToKnownProbOutcome()

void statevec_collapseToKnownProbOutcome ( Qureg  qureg,
const int  measureQubit,
int  outcome,
qreal  totalStateProb 
)

Definition at line 1301 of file QuEST_cpu_distributed.c.

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 }

References Qureg::chunkId, halfMatrixBlockFitsInChunk(), isChunkToSkipInFindPZero(), Qureg::numAmpsPerChunk, statevec_collapseToKnownProbOutcomeDistributedRenorm(), statevec_collapseToKnownProbOutcomeLocal(), and statevec_collapseToOutcomeDistributedSetZero().

◆ statevec_compactUnitary()

void statevec_compactUnitary ( Qureg  qureg,
const int  targetQubit,
Complex  alpha,
Complex  beta 
)

Definition at line 846 of file QuEST_cpu_distributed.c.

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 }

References Qureg::chunkId, chunkIsUpper(), exchangeStateVectors(), getChunkPairId(), getRotAngle(), halfMatrixBlockFitsInChunk(), Qureg::numAmpsPerChunk, Qureg::pairStateVec, Qureg::stateVec, statevec_compactUnitaryDistributed(), and statevec_compactUnitaryLocal().

◆ statevec_controlledCompactUnitary()

void statevec_controlledCompactUnitary ( Qureg  qureg,
const int  controlQubit,
const int  targetQubit,
Complex  alpha,
Complex  beta 
)

Definition at line 922 of file QuEST_cpu_distributed.c.

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 }

References Qureg::chunkId, chunkIsUpper(), exchangeStateVectors(), getChunkPairId(), getRotAngle(), halfMatrixBlockFitsInChunk(), Qureg::numAmpsPerChunk, Qureg::pairStateVec, Qureg::stateVec, statevec_controlledCompactUnitaryDistributed(), and statevec_controlledCompactUnitaryLocal().

◆ statevec_controlledNot()

void statevec_controlledNot ( Qureg  qureg,
const int  controlQubit,
const int  targetQubit 
)

Definition at line 1063 of file QuEST_cpu_distributed.c.

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 }

References Qureg::chunkId, chunkIsUpper(), exchangeStateVectors(), getChunkPairId(), halfMatrixBlockFitsInChunk(), Qureg::numAmpsPerChunk, Qureg::pairStateVec, Qureg::stateVec, statevec_controlledNotDistributed(), and statevec_controlledNotLocal().

◆ statevec_controlledPauliY()

void statevec_controlledPauliY ( Qureg  qureg,
const int  controlQubit,
const int  targetQubit 
)

Definition at line 1142 of file QuEST_cpu_distributed.c.

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 }

References Qureg::chunkId, chunkIsUpper(), exchangeStateVectors(), getChunkPairId(), halfMatrixBlockFitsInChunk(), Qureg::numAmpsPerChunk, Qureg::pairStateVec, Qureg::stateVec, statevec_controlledPauliYDistributed(), and statevec_controlledPauliYLocal().

◆ statevec_controlledPauliYConj()

void statevec_controlledPauliYConj ( Qureg  qureg,
const int  controlQubit,
const int  targetQubit 
)

Definition at line 1175 of file QuEST_cpu_distributed.c.

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 }

References Qureg::chunkId, chunkIsUpper(), exchangeStateVectors(), getChunkPairId(), halfMatrixBlockFitsInChunk(), Qureg::numAmpsPerChunk, Qureg::pairStateVec, Qureg::stateVec, statevec_controlledPauliYDistributed(), and statevec_controlledPauliYLocal().

◆ statevec_controlledUnitary()

void statevec_controlledUnitary ( Qureg  qureg,
const int  controlQubit,
const int  targetQubit,
ComplexMatrix2  u 
)

Definition at line 960 of file QuEST_cpu_distributed.c.

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 }

References Qureg::chunkId, chunkIsUpper(), exchangeStateVectors(), getChunkPairId(), getRotAngleFromUnitaryMatrix(), halfMatrixBlockFitsInChunk(), Qureg::numAmpsPerChunk, Qureg::pairStateVec, Qureg::stateVec, statevec_controlledUnitaryDistributed(), and statevec_controlledUnitaryLocal().

◆ statevec_getImagAmp()

qreal statevec_getImagAmp ( Qureg  qureg,
long long int  index 
)

Definition at line 208 of file QuEST_cpu_distributed.c.

208  {
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 }

References Qureg::chunkId, getChunkIdFromIndex(), Qureg::numAmpsPerChunk, qreal, and Qureg::stateVec.

◆ statevec_getRealAmp()

qreal statevec_getRealAmp ( Qureg  qureg,
long long int  index 
)

Definition at line 198 of file QuEST_cpu_distributed.c.

198  {
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 }

References Qureg::chunkId, getChunkIdFromIndex(), Qureg::numAmpsPerChunk, qreal, and Qureg::stateVec.

◆ statevec_hadamard()

void statevec_hadamard ( Qureg  qureg,
const int  targetQubit 
)

Definition at line 1208 of file QuEST_cpu_distributed.c.

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 }

References Qureg::chunkId, chunkIsUpper(), exchangeStateVectors(), getChunkPairId(), halfMatrixBlockFitsInChunk(), Qureg::numAmpsPerChunk, Qureg::pairStateVec, Qureg::stateVec, statevec_hadamardDistributed(), and statevec_hadamardLocal().

◆ statevec_multiControlledMultiQubitUnitary()

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 already fit in the node, it operates the unitary direct.

It is already gauranteed here that all target qubits can fit on each node (this is validated in the front-end)

@TODO: refactor so that the 'swap back' isn't performed; instead the qubit locations are updated.

Definition at line 1441 of file QuEST_cpu_distributed.c.

1441  {
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 }

References flipBit(), getQubitBitMask(), halfMatrixBlockFitsInChunk(), maskContainsBit(), Qureg::numAmpsPerChunk, statevec_multiControlledMultiQubitUnitaryLocal(), and statevec_swapQubitAmps().

◆ statevec_multiControlledTwoQubitUnitary()

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 already fit in the node, it operates the unitary direct.

Note the order of q1 and q2 in the call to twoQubitUnitaryLocal is important.

@TODO: refactor so that the 'swap back' isn't performed; instead the qubit locations are updated. @TODO: the double swap (q1,q2 to 0,1) may be possible simultaneously by a bespoke swap routine.

Definition at line 1385 of file QuEST_cpu_distributed.c.

1385  {
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 }

References flipBit(), halfMatrixBlockFitsInChunk(), maskContainsBit(), Qureg::numAmpsPerChunk, statevec_multiControlledTwoQubitUnitaryLocal(), and statevec_swapQubitAmps().

◆ statevec_multiControlledUnitary()

void statevec_multiControlledUnitary ( Qureg  qureg,
long long int  ctrlQubitsMask,
long long int  ctrlFlipMask,
const int  targetQubit,
ComplexMatrix2  u 
)

Definition at line 999 of file QuEST_cpu_distributed.c.

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 }

References Qureg::chunkId, chunkIsUpper(), exchangeStateVectors(), getChunkPairId(), getRotAngleFromUnitaryMatrix(), halfMatrixBlockFitsInChunk(), Qureg::numAmpsPerChunk, Qureg::pairStateVec, Qureg::stateVec, statevec_multiControlledUnitaryDistributed(), and statevec_multiControlledUnitaryLocal().

◆ statevec_pauliX()

void statevec_pauliX ( Qureg  qureg,
const int  targetQubit 
)

Definition at line 1036 of file QuEST_cpu_distributed.c.

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 }

References Qureg::chunkId, chunkIsUpper(), exchangeStateVectors(), getChunkPairId(), halfMatrixBlockFitsInChunk(), Qureg::numAmpsPerChunk, Qureg::pairStateVec, Qureg::stateVec, statevec_pauliXDistributed(), and statevec_pauliXLocal().

◆ statevec_pauliY()

void statevec_pauliY ( Qureg  qureg,
const int  targetQubit 
)

Definition at line 1092 of file QuEST_cpu_distributed.c.

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 }

References Qureg::chunkId, chunkIsUpper(), exchangeStateVectors(), getChunkPairId(), halfMatrixBlockFitsInChunk(), Qureg::numAmpsPerChunk, Qureg::pairStateVec, Qureg::stateVec, statevec_pauliYDistributed(), and statevec_pauliYLocal().

◆ statevec_pauliYConj()

void statevec_pauliYConj ( Qureg  qureg,
const int  targetQubit 
)

Definition at line 1117 of file QuEST_cpu_distributed.c.

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 }

References Qureg::chunkId, chunkIsUpper(), exchangeStateVectors(), getChunkPairId(), halfMatrixBlockFitsInChunk(), Qureg::numAmpsPerChunk, Qureg::pairStateVec, Qureg::stateVec, statevec_pauliYDistributed(), and statevec_pauliYLocal().

◆ statevec_swapQubitAmps()

void statevec_swapQubitAmps ( Qureg  qureg,
int  qb1,
int  qb2 
)

Definition at line 1358 of file QuEST_cpu_distributed.c.

1358  {
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 }

References exchangeStateVectors(), flipBit(), getGlobalIndOfOddParityInChunk(), halfMatrixBlockFitsInChunk(), Qureg::numAmpsPerChunk, statevec_swapQubitAmpsDistributed(), and statevec_swapQubitAmpsLocal().

Referenced by statevec_multiControlledMultiQubitUnitary(), and statevec_multiControlledTwoQubitUnitary().

◆ statevec_unitary()

void statevec_unitary ( Qureg  qureg,
const int  targetQubit,
ComplexMatrix2  u 
)

Definition at line 883 of file QuEST_cpu_distributed.c.

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 }

References Qureg::chunkId, chunkIsUpper(), exchangeStateVectors(), getChunkPairId(), getRotAngleFromUnitaryMatrix(), halfMatrixBlockFitsInChunk(), Qureg::numAmpsPerChunk, Qureg::pairStateVec, Qureg::stateVec, statevec_unitaryDistributed(), and statevec_unitaryLocal().

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_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 densmatr_mixTwoQubitDepolarisingDistributed(Qureg qureg, const int targetQubit, const int qubit2, qreal delta, qreal gamma)
Definition: QuEST_cpu.c:540
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
int numChunks
Number of chunks the state vector is broken up into – the number of MPI processes used.
Definition: QuEST.h:176
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 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
__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_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.
#define qreal
void exchangePairStateVectorHalves(Qureg qureg, int pairRank)
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...
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
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
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 densmatr_initPureStateLocal(Qureg targetQureg, Qureg copyQureg)
Definition: QuEST_cpu.c:1183
__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
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 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
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
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 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
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 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_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
static int chunkIsUpperInOuterBlock(int chunkId, long long int chunkSize, int targetQubit, int numQubits)
fix – do with masking instead
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
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_controlledPauliYLocal(Qureg qureg, const int controlQubit, const int targetQubit, const int conjFac)
Definition: QuEST_cpu.c:2740
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_calcFidelityLocal(Qureg qureg, Qureg pureState)
computes a few dens-columns-worth of (vec^*T) dens * vec
Definition: QuEST_cpu.c:989