| 2076 | |
| 2077 | |
| 2078 | qcomp localiser_statevec_calcExpecPauliStrSum(Qureg qureg, PauliStrSum sum) { |
| 2079 | assert_localiserGivenStateVec(qureg); |
| 2080 | |
| 2081 | // this function does not process each PauliStr within sum independently; instead, we |
| 2082 | // leverage that strings differing only by I <-> Z and X <-> Y in the prefix qubits |
| 2083 | // have identical communication patterns, and so can all be processed after one round |
| 2084 | // of amps exchange. |
| 2085 | |
| 2086 | /// @todo |
| 2087 | /// We can optimise further in a similar spirit to above by grouping identically- |
| 2088 | /// communicating strings into those which differ only by suffix I <-> Z and X <-> Y, |
| 2089 | /// which have identical amplitude-mixing patterns, to avoid repeated enumeration of |
| 2090 | /// all amplitudes and reduce the associated memory-movement / caching costs. |
| 2091 | /// (This is facilitated "for free" by cuStateVec in GPU settings, although we do not |
| 2092 | /// wish to here differentiate localiser logic based on CPU vs GPU deployment) |
| 2093 | |
| 2094 | qcomp totalValue = 0; |
| 2095 | |
| 2096 | using Key = PAULI_MASK_TYPE; |
| 2097 | using Term = tuple<PauliStr,qcomp>; |
| 2098 | std::unordered_map<Key, vector<Term>> groups; |
| 2099 | |
| 2100 | // group sum's terms into those with identical communication patterns |
| 2101 | for (int i=0; i<sum.numTerms; i++) { |
| 2102 | Term term = tuple{sum.strings[i], sum.coeffs[i]}; |
| 2103 | Key totalKey = paulis_getKeyOfSameMixedAmpsGroup(sum.strings[i]); |
| 2104 | Key prefixKey = getBitsLeftOfIndex(totalKey, qureg.logNumAmpsPerNode - 1); // 0 if !qureg.isDistributed |
| 2105 | |
| 2106 | groups[prefixKey].push_back(term); |
| 2107 | } |
| 2108 | |
| 2109 | // process each group in-turn |
| 2110 | for (auto& [key, terms] : groups) { |
| 2111 | |
| 2112 | // perform communication once per-group, if necessary |
| 2113 | int pairRank = flipBits(qureg.rank, key); |
| 2114 | if (pairRank != qureg.rank) |
| 2115 | comm_exchangeAmpsToBuffers(qureg, pairRank); |
| 2116 | |
| 2117 | // determine backend function to invoke upon all terms in group (likely _subB) |
| 2118 | auto termFunc = (pairRank == qureg.rank)? |
| 2119 | getStateVecExpecAllSuffixPauliStr : |
| 2120 | accel_statevec_calcExpecPauliStr_subB; |
| 2121 | |
| 2122 | // for each term within the current group... |
| 2123 | for (auto& [str, coeff] : terms) { |
| 2124 | auto [targsX, targsY, targsZ] = paulis_getSeparateInds(str); |
| 2125 | auto [prefixX, suffixX] = util_getPrefixAndSuffixQubits(targsX, qureg); |
| 2126 | auto [prefixY, suffixY] = util_getPrefixAndSuffixQubits(targsY, qureg); |
| 2127 | auto [prefixZ, suffixZ] = util_getPrefixAndSuffixQubits(targsZ, qureg); |
| 2128 | |
| 2129 | // contribute coeff * prefix-coeff * suffix-sum |
| 2130 | qcomp termFactor = paulis_getPrefixPaulisElem(qureg, prefixY, prefixZ); |
| 2131 | qcomp termValue = termFactor * termFunc(qureg, suffixX, suffixY, suffixZ); |
| 2132 | |
| 2133 | /// @todo |
| 2134 | /// use Kahan summation to improve (for free) the accuracy of totalValue |
| 2135 | /// here! This sum is always serial, so we should always use it! It is |
no test coverage detected