2020#include " quest/src/comm/comm_routines.hpp"
2121
2222#include < iostream>
23+ #include < utility>
2324#include < vector>
2425#include < string>
2526#include < array>
@@ -87,7 +88,7 @@ void freeAllMemoryIfAnyAllocsFailed(PauliStrSum sum) {
8788
8889
8990/*
90- * INTERNAL UTILITIES
91+ * INTERNAL PauliStr UTILITIES
9192 *
9293 * callable by other internal files but which are not exposed in the header
9394 * because we do not wish to make them visible to users. Ergo other internal
@@ -139,12 +140,6 @@ int paulis_getIndOfLefmostNonIdentityPauli(PauliStr* strings, qindex numStrings)
139140}
140141
141142
142- int paulis_getIndOfLefmostNonIdentityPauli (PauliStrSum sum) {
143-
144- return paulis_getIndOfLefmostNonIdentityPauli (sum.strings , sum.numTerms );
145- }
146-
147-
148143bool paulis_containsXOrY (PauliStr str) {
149144
150145 int maxInd = paulis_getIndOfLefmostNonIdentityPauli (str);
@@ -160,16 +155,6 @@ bool paulis_containsXOrY(PauliStr str) {
160155}
161156
162157
163- bool paulis_containsXOrY (PauliStrSum sum) {
164-
165- for (qindex i=0 ; i<sum.numTerms ; i++)
166- if (paulis_containsXOrY (sum.strings [i]))
167- return true ;
168-
169- return false ;
170- }
171-
172-
173158bool paulis_hasOddNumY (PauliStr str) {
174159
175160 bool odd = false ;
@@ -182,6 +167,13 @@ bool paulis_hasOddNumY(PauliStr str) {
182167}
183168
184169
170+ int paulis_getSignOfPauliStrConj (PauliStr str) {
171+
172+ // conj(Y) = -Y, conj(YY) = YY
173+ return paulis_hasOddNumY (str)? -1 : 1 ;
174+ }
175+
176+
185177int paulis_getPrefixZSign (Qureg qureg, vector<int > prefixZ) {
186178
187179 int sign = 1 ;
@@ -239,7 +231,7 @@ qindex paulis_getTargetBitMask(PauliStr str) {
239231}
240232
241233
242- array<vector<int >,3 > paulis_getSeparateInds (PauliStr str, Qureg qureg ) {
234+ array<vector<int >,3 > paulis_getSeparateInds (PauliStr str) {
243235
244236 vector<int > iXYZ = paulis_getTargetInds (str);
245237 vector<int > iX, iY, iZ;
@@ -279,18 +271,25 @@ PauliStr paulis_getShiftedPauliStr(PauliStr str, int pauliShift) {
279271}
280272
281273
282- PauliStr paulis_getKetAndBraPauliStr (PauliStr str, Qureg qureg) {
274+ PauliStr paulis_getTensorProdOfPauliStr (PauliStr left, PauliStr right, int numQubits) {
275+
276+ // computes left (tensor) right, assuming right is smaller than numQubits
277+ PauliStr shifted = paulis_getShiftedPauliStr (left, numQubits);
283278
284- PauliStr shifted = paulis_getShiftedPauliStr (str, qureg.numQubits );
285-
286279 // return a new stack PauliStr instance (avoiding C++20 initialiser)
287280 PauliStr out;
288- out.lowPaulis = str .lowPaulis | shifted.lowPaulis ;
289- out.highPaulis = str .highPaulis | shifted.highPaulis ;
281+ out.lowPaulis = right .lowPaulis | shifted.lowPaulis ;
282+ out.highPaulis = right .highPaulis | shifted.highPaulis ;
290283 return out;
291284}
292285
293286
287+ PauliStr paulis_getKetAndBraPauliStr (PauliStr str, Qureg qureg) {
288+
289+ return paulis_getTensorProdOfPauliStr (str, str, qureg.numQubits );
290+ }
291+
292+
294293PAULI_MASK_TYPE paulis_getKeyOfSameMixedAmpsGroup (PauliStr str) {
295294
296295 PAULI_MASK_TYPE key = 0 ;
@@ -312,6 +311,54 @@ PAULI_MASK_TYPE paulis_getKeyOfSameMixedAmpsGroup(PauliStr str) {
312311}
313312
314313
314+ std::pair<qcomp,PauliStr> paulis_getPauliStrProd (PauliStr strA, PauliStr strB) {
315+
316+ // a . b = coeff * (a ^ b)
317+ PauliStr strOut;
318+ strOut.lowPaulis = strA.lowPaulis ^ strB.lowPaulis ;
319+ strOut.highPaulis = strA.highPaulis ^ strB.highPaulis ;
320+
321+ // coeff = product of single-site product coeffs
322+ qcomp coeff = 1 ;
323+ for (int i=0 ; i<MAX_NUM_PAULIS_PER_STR; i++) {
324+ int pA = paulis_getPauliAt (strA, i);
325+ int pB = paulis_getPauliAt (strB, i);
326+
327+ // I.P = P.I = P and P.P = I contribute factor=1
328+ if (pA == 0 || pB == 0 || pA == pB)
329+ continue ;
330+
331+ // XY,YZ,ZX=i, XZ,YX,ZY=-i
332+ int dif = pB - pA;
333+ coeff *= qcomp (0 , (dif == 1 || dif == -2 )? 1 : -1 );
334+ }
335+
336+ return {coeff, strOut};
337+ }
338+
339+
340+
341+ /*
342+ * INTERNAL PauliStrSum UTILITIES
343+ */
344+
345+
346+ int paulis_getIndOfLefmostNonIdentityPauli (PauliStrSum sum) {
347+
348+ return paulis_getIndOfLefmostNonIdentityPauli (sum.strings , sum.numTerms );
349+ }
350+
351+
352+ bool paulis_containsXOrY (PauliStrSum sum) {
353+
354+ for (qindex i=0 ; i<sum.numTerms ; i++)
355+ if (paulis_containsXOrY (sum.strings [i]))
356+ return true ;
357+
358+ return false ;
359+ }
360+
361+
315362qindex paulis_getTargetBitMask (PauliStrSum sum) {
316363
317364 qindex mask = 0 ;
@@ -324,6 +371,87 @@ qindex paulis_getTargetBitMask(PauliStrSum sum) {
324371}
325372
326373
374+ void paulis_setPauliStrSumToScaledTensorProdOfConjWithSelf (PauliStrSum out, qreal factor, PauliStrSum in, int numQubits) {
375+
376+ // sets out = factor * conj(in) (x) in, where in has dim of numQubits
377+ if (paulis_getIndOfLefmostNonIdentityPauli (in) >= numQubits)
378+ error_pauliStrSumHasMoreQubitsThanSpecifiedInTensorProd ();
379+ if (out.numTerms != in.numTerms * in.numTerms )
380+ error_pauliStrSumTensorProdHasIncorrectNumTerms ();
381+
382+ // conj(in) (x) in = sum_jk conj(c_j) c_k conj(P_j) (x) P_k...
383+ qindex i = 0 ;
384+ for (qindex j=0 ; j<in.numTerms ; j++) {
385+ for (qindex k=0 ; k<in.numTerms ; k++) {
386+
387+ // ... where conj(P_j) = sign_j P_j
388+ out.strings [i] = paulis_getTensorProdOfPauliStr (in.strings [j], in.strings [k], numQubits);
389+ out.coeffs [i] = factor * std::conj (in.coeffs [j]) * in.coeffs [k] * paulis_getSignOfPauliStrConj (in.strings [j]);
390+ i++;
391+ }
392+ }
393+ }
394+
395+
396+ qindex paulis_getNumTermsInPauliStrSumProdOfAdjointWithSelf (PauliStrSum in) {
397+
398+ // adj(in).in has fewer terms than the numTerms^2 bound, since
399+ // a.a = I (causing -n and +1 below) and a.b ~ b.a (causing /2);
400+ // we do not however consider any cancellations of coefficients
401+ int n = in.numTerms ;
402+ return 1 + (n*n - n)/2 ;
403+ }
404+
405+
406+ void paulis_setPauliStrSumToScaledProdOfAdjointWithSelf (PauliStrSum out, qreal factor, PauliStrSum in) {
407+
408+ // sets out = factor * adj(in) . in, permitting duplicate strings
409+ if (out.numTerms != paulis_getNumTermsInPauliStrSumProdOfAdjointWithSelf (in))
410+ error_pauliStrSumProdHasIncorrectNumTerms ();
411+
412+ // since out definitely contains an identity (when neglecting coeff cancellation)
413+ // which is contributed toward by all j=k iterations below, we keep it at i=0
414+ out.strings [0 ] = getPauliStr (" I" );
415+ out.coeffs [0 ] = 0 ;
416+ qindex i = 1 ;
417+
418+ // we leverage that sum_jk a_j^* a_k P_j P_k...
419+ for (qindex j=0 ; j<in.numTerms ; j++) {
420+
421+ // = sum_j ( |a_j|^2 Id + sum_k<j ...)
422+ out.coeffs [0 ] += factor * std::norm (in.coeffs [j]);
423+
424+ // containing sum_k<j (a_j^* a_k P_j P_k + a_k^* a_j P_k P_j)
425+ for (qindex k=0 ; k<j; k++) {
426+
427+ // = (a_j^* a_k b_jk + a_k^* a_j b_jk^*) P'
428+ auto [coeff, str] = paulis_getPauliStrProd (in.strings [j], in.strings [k]);
429+
430+ // = (x + x^*) P' = 2 Re[x] P'
431+ out.strings [i] = str;
432+ out.coeffs [i] = factor * 2 * std::real (std::conj (in.coeffs [j]) * in.coeffs [k] * coeff);
433+ i++;
434+ }
435+ }
436+ }
437+
438+
439+ void paulis_setPauliStrSumToShiftedConj (PauliStrSum out, PauliStrSum in, int numQubits) {
440+
441+ // sets out = conj(in) (x) I
442+ if (paulis_getIndOfLefmostNonIdentityPauli (in) >= numQubits)
443+ error_pauliStrSumHasMoreQubitsThanSpecifiedInConjShift ();
444+ if (out.numTerms != in.numTerms )
445+ error_pauliStrSumConjHasIncorrectNumTerms ();
446+
447+ // where conj(c P) = conj(c) sign P
448+ for (qindex i=0 ; i<out.numTerms ; i++) {
449+ out.strings [i] = paulis_getShiftedPauliStr (in.strings [i], numQubits);
450+ out.coeffs [i] = std::conj (in.coeffs [i]) * paulis_getSignOfPauliStrConj (in.strings [i]);
451+ }
452+ }
453+
454+
327455
328456/*
329457 * PAULI STRING INITIALISATION
0 commit comments