Skip to content

Commit ba64a93

Browse files
committed
added non-trace-preserving Kraus maps
1 parent 0bb18c5 commit ba64a93

File tree

5 files changed

+510
-14
lines changed

5 files changed

+510
-14
lines changed

QuEST/include/QuEST.h

+133-3
Original file line numberDiff line numberDiff line change
@@ -4854,13 +4854,14 @@ void multiControlledMultiQubitUnitary(Qureg qureg, int* ctrls, int numCtrls, int
48544854
* \f[
48554855
\sum \limits_i^{\text{numOps}} K_i^\dagger K_i = I
48564856
* \f]
4857-
* where \f$ I \f$ is the identity matrix.
4857+
* where \f$ I \f$ is the identity matrix. Use mixNonTPKrausMap() to relax this condition.
48584858
*
48594859
* Note that in distributed mode, this routine requires that each node contains at least 4 amplitudes.
48604860
* This means an q-qubit register can be distributed by at most 2^(q-2) numTargs nodes.
48614861
*
48624862
* @see
48634863
* - ::ComplexMatrix2
4864+
* - mixNonTPKrausMap()
48644865
* - mixTwoQubitKrausMap()
48654866
* - mixMultiQubitKrausMap()
48664867
* - mixDephasing()
@@ -4894,7 +4895,8 @@ void mixKrausMap(Qureg qureg, int target, ComplexMatrix2 *ops, int numOps);
48944895
* \f[
48954896
\sum \limits_i^{\text{numOps}} K_i^\dagger K_i = I
48964897
* \f]
4897-
* where \f$ I \f$ is the identity matrix.
4898+
* where \f$ I \f$ is the identity matrix. Use mixNonTPTwoQubitKrausMap() to relax this
4899+
* this condition.
48984900
*
48994901
* \p targetQubit1 is treated as the \p least significant qubit in each op in \p ops.
49004902
*
@@ -4903,6 +4905,7 @@ void mixKrausMap(Qureg qureg, int target, ComplexMatrix2 *ops, int numOps);
49034905
*
49044906
* @see
49054907
* - ::ComplexMatrix4
4908+
* - mixNonTPTwoQubitKrausMap()
49064909
* - mixMultiQubitKrausMap()
49074910
* - mixKrausMap()
49084911
*
@@ -4932,7 +4935,8 @@ void mixTwoQubitKrausMap(Qureg qureg, int target1, int target2, ComplexMatrix4 *
49324935
* \f[
49334936
\sum \limits_i^{\text{numOps}} K_i^\dagger K_i = I
49344937
* \f]
4935-
* where \f$ I \f$ is the identity matrix.
4938+
* where \f$ I \f$ is the identity matrix. Use mixNonTPMultiQubitKrausMap() to relax
4939+
* this condition.
49364940
*
49374941
* The first qubit in \p targets is treated as the \p least significant qubit in each op in \p ops.
49384942
*
@@ -4952,6 +4956,7 @@ void mixTwoQubitKrausMap(Qureg qureg, int target1, int target2, ComplexMatrix4 *
49524956
* @see
49534957
* - createComplexMatrixN()
49544958
* - initComplexMatrixN()
4959+
* - mixNonTPMultiQubitKrausMap()
49554960
* - mixKrausMap()
49564961
* - mixTwoQubitKrausMap()
49574962
*
@@ -4974,6 +4979,131 @@ void mixTwoQubitKrausMap(Qureg qureg, int target1, int target2, ComplexMatrix4 *
49744979
*/
49754980
void mixMultiQubitKrausMap(Qureg qureg, int* targets, int numTargets, ComplexMatrixN* ops, int numOps);
49764981

4982+
/** Apply a general non-trace-preserving single-qubit Kraus map to a density matrix,
4983+
* as specified by at most four operators, \f$K_i\f$ (\p ops).
4984+
* This effects
4985+
* \f[
4986+
\rho \to \sum\limits_i^{\text{numOps}} K_i \rho K_i^\dagger
4987+
* \f]
4988+
* where \f$K_i\f$ are permitted to be any matrix. This means the density matrix
4989+
* can enter a non-physical state.
4990+
*
4991+
* Use mixKrausMap() to enforce that the channel is trace preserving and completely positive.
4992+
*
4993+
* Note that in distributed mode, this routine requires that each node contains at least 4 amplitudes.
4994+
* This means an q-qubit register can be distributed by at most 2^(q-2) numTargs nodes.
4995+
*
4996+
* @see
4997+
* - ::ComplexMatrix2
4998+
* - mixKrausMap()
4999+
* - mixNonTPTwoQubitKrausMap()
5000+
* - mixNonTPMultiQubitKrausMap()
5001+
*
5002+
* @ingroup decoherence
5003+
* @param[in,out] qureg the density matrix to which to apply the map
5004+
* @param[in] target the target qubit of the map
5005+
* @param[in] ops an array of at most 4 Kraus operators
5006+
* @param[in] numOps the number of operators in \p ops which must be >0 and <= 4.
5007+
* @throws invalidQuESTInputError()
5008+
* - if \p qureg is not a density matrix
5009+
* - if \p target is outside of [0, \p qureg.numQubitsRepresented)
5010+
* - if \p numOps is outside [1, 4]
5011+
* - if a node cannot fit 4 amplitudes in distributed mode
5012+
* @author Tyson Jones
5013+
* @author Balint Koczor (backend code)
5014+
*/
5015+
void mixNonTPKrausMap(Qureg qureg, int target, ComplexMatrix2 *ops, int numOps);
5016+
5017+
/** Apply a general non-trace-preserving two-qubit Kraus map to a density matrix,
5018+
* as specified by at most sixteen operators, \f$K_i\f$ (\p ops).
5019+
*
5020+
* This effects
5021+
* \f[
5022+
\rho \to \sum\limits_i^{\text{numOps}} K_i \rho K_i^\dagger
5023+
* \f]
5024+
* where the matrices \f$K_i\f$ are unconstrained, and hence the effective map is permitted
5025+
* to be non-completely-positive and non-trace-preserving.
5026+
* Use mixTwoQubitKrausMap() to enforce that the map be completely positive.
5027+
*
5028+
* \p targetQubit1 is treated as the \p least significant qubit in each op in \p ops.
5029+
*
5030+
* Note that in distributed mode, this routine requires that each node contains at least 16 amplitudes.
5031+
* This means an q-qubit register can be distributed by at most 2^(q-4) numTargs nodes.
5032+
*
5033+
* @see
5034+
* - ::ComplexMatrix4
5035+
* - mixTwoQubitKrausMap()
5036+
* - mixNonTPKrausMap()
5037+
* - mixNonTPMultiQubitKrausMap()
5038+
*
5039+
* @ingroup decoherence
5040+
* @param[in,out] qureg the density matrix to which to apply the map
5041+
* @param[in] target1 the least significant target qubit in \p ops
5042+
* @param[in] target2 the most significant target qubit in \p ops
5043+
* @param[in] ops an array of at most 16 Kraus operators
5044+
* @param[in] numOps the number of operators in \p ops which must be >0 and <= 16.
5045+
* @throws invalidQuESTInputError()
5046+
* - if \p qureg is not a density matrix
5047+
* - if either \p target1 or \p target2 is outside of [0, \p qureg.numQubitsRepresented)
5048+
* - if \p target1 = \p target2
5049+
* - if \p numOps is outside [1, 16]
5050+
* - if a node cannot fit 16 amplitudes in distributed mode
5051+
* @author Tyson Jones
5052+
* @author Balint Koczor (backend code)
5053+
*/
5054+
void mixNonTPTwoQubitKrausMap(Qureg qureg, int target1, int target2, ComplexMatrix4 *ops, int numOps);
5055+
5056+
/** Apply a general N-qubit non-trace-preserving Kraus map to a density matrix,
5057+
* as specified by at most (2N)^2 operators.
5058+
*
5059+
* This effects
5060+
* \f[
5061+
\rho \to \sum\limits_i^{\text{numOps}} K_i \rho K_i^\dagger
5062+
* \f]
5063+
* where the matrices \f$ K_i \f$ are unconstrained, and hence the effective map is permitted
5064+
* to be non-completely-positive and non-trace-preserving.
5065+
* Use mixMultiQubitKrausMap() to enforce that the map be completely positive.
5066+
*
5067+
* The first qubit in \p targets is treated as the \p least significant qubit in each op in \p ops.
5068+
*
5069+
* Note that in distributed mode, this routine requires that each node contains at least (2N)^2 amplitudes.
5070+
* This means an q-qubit register can be distributed by at most 2^(q-2)/N^2 nodes.
5071+
*
5072+
* Note too that this routine internally creates a 'superoperator'; a complex matrix of dimensions
5073+
* 2^(2*numTargets) by 2^(2*numTargets). Therefore, invoking this function incurs,
5074+
* for numTargs={1,2,3,4,5, ...}, an additional memory overhead of (at double-precision)
5075+
* {0.25 KiB, 4 KiB, 64 KiB, 1 MiB, 16 MiB, ...} (respectively).
5076+
* At quad precision (usually 10 B per number, but possibly 16 B due to alignment),
5077+
* this costs at most double the amount of memory.
5078+
* For numTargets < 4, this superoperator will be created in the runtime
5079+
* stack. For numTargs >= 4, the superoperator will be allocated in the heap and
5080+
* therefore this routine may suffer an anomalous slowdown.
5081+
*
5082+
* @see
5083+
* - createComplexMatrixN()
5084+
* - initComplexMatrixN()
5085+
* - mixMultiQubitKrausMap()
5086+
* - mixNonTPKrausMap()
5087+
* - mixNonTPTwoQubitKrausMap()
5088+
*
5089+
* @ingroup decoherence
5090+
* @param[in,out] qureg the density matrix to which to apply the map
5091+
* @param[in] targets a list of target qubit indices, the first of which is treated as least significant in each op in \p ops
5092+
* @param[in] numTargets the length of \p targets
5093+
* @param[in] ops an array of at most (2N)^2 Kraus operators
5094+
* @param[in] numOps the number of operators in \p ops which must be >0 and <= (2N)^2.
5095+
* @throws invalidQuESTInputError()
5096+
* - if \p qureg is not a density matrix
5097+
* - if any target in \p targets is outside of [0, \p qureg.numQubitsRepresented)
5098+
* - if any qubit in \p targets is repeated
5099+
* - if \p numOps is outside [1, (2 \p numTargets)^2]
5100+
* - if any ComplexMatrixN in \p ops does not have op.numQubits == \p numTargets
5101+
* - if a node cannot fit (2N)^2 amplitudes in distributed mode
5102+
* @author Tyson Jones
5103+
* @author Balint Koczor (backend code)
5104+
*/
5105+
void mixNonTPMultiQubitKrausMap(Qureg qureg, int* targets, int numTargets, ComplexMatrixN* ops, int numOps);
5106+
49775107
/** Computes the Hilbert Schmidt distance between two density matrices \p a and \p b,
49785108
* defined as the Frobenius norm of the difference between them.
49795109
* That is, we define the Hilbert Schmidt distance

QuEST/src/QuEST.c

+30
Original file line numberDiff line numberDiff line change
@@ -1344,6 +1344,36 @@ void mixMultiQubitKrausMap(Qureg qureg, int* targets, int numTargets, ComplexMat
13441344
"Here, an undisclosed %d-qubit Kraus map was applied to undisclosed qubits", numTargets);
13451345
}
13461346

1347+
void mixNonTPKrausMap(Qureg qureg, int target, ComplexMatrix2 *ops, int numOps) {
1348+
validateDensityMatrQureg(qureg, __func__);
1349+
validateTarget(qureg, target, __func__);
1350+
validateOneQubitKrausMapDimensions(qureg, ops, numOps, __func__);
1351+
1352+
densmatr_mixKrausMap(qureg, target, ops, numOps);
1353+
qasm_recordComment(qureg,
1354+
"Here, an undisclosed non-trace-preserving Kraus map was effected on qubit %d", target);
1355+
}
1356+
1357+
void mixNonTPTwoQubitKrausMap(Qureg qureg, int target1, int target2, ComplexMatrix4 *ops, int numOps) {
1358+
validateDensityMatrQureg(qureg, __func__);
1359+
validateMultiTargets(qureg, (int[]) {target1,target2}, 2, __func__);
1360+
validateTwoQubitKrausMapDimensions(qureg, ops, numOps, __func__);
1361+
1362+
densmatr_mixTwoQubitKrausMap(qureg, target1, target2, ops, numOps);
1363+
qasm_recordComment(qureg,
1364+
"Here, an undisclosed non-trace-preserving two-qubit Kraus map was effected on qubits %d and %d", target1, target2);
1365+
}
1366+
1367+
void mixNonTPMultiQubitKrausMap(Qureg qureg, int* targets, int numTargets, ComplexMatrixN* ops, int numOps) {
1368+
validateDensityMatrQureg(qureg, __func__);
1369+
validateMultiTargets(qureg, targets, numTargets, __func__);
1370+
validateMultiQubitKrausMapDimensions(qureg, numTargets, ops, numOps, __func__);
1371+
1372+
densmatr_mixMultiQubitKrausMap(qureg, targets, numTargets, ops, numOps);
1373+
qasm_recordComment(qureg,
1374+
"Here, an undisclosed non-trace-preserving %d-qubit Kraus map was applied to undisclosed qubits", numTargets);
1375+
}
1376+
13471377
/*
13481378
* other data structures
13491379
*/

QuEST/src/QuEST_validation.c

+17-11
Original file line numberDiff line numberDiff line change
@@ -618,31 +618,35 @@ void validateNumPauliSumTerms(int numTerms, const char* caller) {
618618
QuESTAssert(numTerms > 0, E_INVALID_NUM_SUM_TERMS, caller);
619619
}
620620

621-
void validateOneQubitKrausMap(Qureg qureg, ComplexMatrix2* ops, int numOps, const char* caller) {
621+
void validateOneQubitKrausMapDimensions(Qureg qureg, ComplexMatrix2* ops, int numOps, const char* caller) {
622622
int opNumQubits = 1;
623623
int superOpNumQubits = 2*opNumQubits;
624624
int maxNumOps = superOpNumQubits*superOpNumQubits;
625625
QuESTAssert(numOps > 0 && numOps <= maxNumOps, E_INVALID_NUM_ONE_QUBIT_KRAUS_OPS, caller);
626626

627627
validateMultiQubitMatrixFitsInNode(qureg, superOpNumQubits, caller);
628-
629-
int isPos = isCompletelyPositiveMap2(ops, numOps);
630-
QuESTAssert(isPos, E_INVALID_KRAUS_OPS, caller);
631628
}
632629

633-
void validateTwoQubitKrausMap(Qureg qureg, ComplexMatrix4* ops, int numOps, const char* caller) {
630+
void validateOneQubitKrausMap(Qureg qureg, ComplexMatrix2* ops, int numOps, const char* caller) {
631+
validateOneQubitKrausMapDimensions(qureg, ops, numOps, caller);
632+
QuESTAssert(isCompletelyPositiveMap2(ops, numOps), E_INVALID_KRAUS_OPS, caller);
633+
}
634+
635+
void validateTwoQubitKrausMapDimensions(Qureg qureg, ComplexMatrix4* ops, int numOps, const char* caller) {
634636
int opNumQubits = 2;
635637
int superOpNumQubits = 2*opNumQubits;
636638
int maxNumOps = superOpNumQubits*superOpNumQubits;
637639
QuESTAssert(numOps > 0 && numOps <= maxNumOps, E_INVALID_NUM_TWO_QUBIT_KRAUS_OPS, caller);
638640

639641
validateMultiQubitMatrixFitsInNode(qureg, superOpNumQubits, caller);
642+
}
640643

641-
int isPos = isCompletelyPositiveMap4(ops, numOps);
642-
QuESTAssert(isPos, E_INVALID_KRAUS_OPS, caller);
644+
void validateTwoQubitKrausMap(Qureg qureg, ComplexMatrix4* ops, int numOps, const char* caller) {
645+
validateTwoQubitKrausMapDimensions(qureg, ops, numOps, caller);
646+
QuESTAssert(isCompletelyPositiveMap4(ops, numOps), E_INVALID_KRAUS_OPS, caller);
643647
}
644648

645-
void validateMultiQubitKrausMap(Qureg qureg, int numTargs, ComplexMatrixN* ops, int numOps, const char* caller) {
649+
void validateMultiQubitKrausMapDimensions(Qureg qureg, int numTargs, ComplexMatrixN* ops, int numOps, const char* caller) {
646650
int opNumQubits = numTargs;
647651
int superOpNumQubits = 2*opNumQubits;
648652
int maxNumOps = superOpNumQubits*superOpNumQubits;
@@ -654,9 +658,11 @@ void validateMultiQubitKrausMap(Qureg qureg, int numTargs, ComplexMatrixN* ops,
654658
}
655659

656660
validateMultiQubitMatrixFitsInNode(qureg, superOpNumQubits, caller);
657-
658-
int isPos = isCompletelyPositiveMapN(ops, numOps);
659-
QuESTAssert(isPos, E_INVALID_KRAUS_OPS, caller);
661+
}
662+
663+
void validateMultiQubitKrausMap(Qureg qureg, int numTargs, ComplexMatrixN* ops, int numOps, const char* caller) {
664+
validateMultiQubitKrausMapDimensions(qureg, numTargs, ops, numOps, caller);
665+
QuESTAssert(isCompletelyPositiveMapN(ops, numOps), E_INVALID_KRAUS_OPS, caller);
660666
}
661667

662668
void validateHamilParams(int numQubits, int numTerms, const char* caller) {

QuEST/src/QuEST_validation.h

+6
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,12 @@ void validateTwoQubitKrausMap(Qureg qureg, ComplexMatrix4* ops, int numOps, cons
108108

109109
void validateMultiQubitKrausMap(Qureg qureg, int numTargs, ComplexMatrixN* ops, int numOps, const char* caller);
110110

111+
void validateOneQubitKrausMapDimensions(Qureg qureg, ComplexMatrix2* ops, int numOps, const char* caller);
112+
113+
void validateTwoQubitKrausMapDimensions(Qureg qureg, ComplexMatrix4* ops, int numOps, const char* caller);
114+
115+
void validateMultiQubitKrausMapDimensions(Qureg qureg, int numTargs, ComplexMatrixN* ops, int numOps, const char* caller);
116+
111117
void validateOneQubitDampingProb(qreal prob, const char* caller);
112118

113119
void validateHamilParams(int numQubits, int numTerms, const char* caller);

0 commit comments

Comments
 (0)