microsoft/qdk
Publicmirrored fromhttps://github.com/microsoft/qdkAvailable
library/chemistry/src/JordanWigner/BlockEncoding.qs
215lines · modecode
| 1 | // Copyright (c) Microsoft Corporation. |
| 2 | // Licensed under the MIT License. |
| 3 | |
| 4 | export JWBlockEncodingGeneratorSystem; |
| 5 | |
| 6 | import Std.Arrays.IndexRange; |
| 7 | |
| 8 | import Generators.GeneratorIndex; |
| 9 | import Generators.GeneratorSystem; |
| 10 | import Generators.HTermToGenIdx; |
| 11 | import Utils.IsNotZero; |
| 12 | import Utils.RangeAsIntArray; |
| 13 | import JordanWigner.Data.JWOptimizedHTerms; |
| 14 | |
| 15 | // This block encoding for qubitization runs off data optimized for a Jordan–Wigner encoding. |
| 16 | // This collects terms Z, ZZ, PQandPQQR, hpqrs separately. |
| 17 | // This only apples the needed hpqrs XXXX XXYY terms. |
| 18 | |
| 19 | // Convention for GeneratorIndex = ((Int[],Double[]), Int[]) |
| 20 | // We index single Paulis as 0 for I, 1 for X, 2 for Y, 3 for Z. |
| 21 | // We index Pauli strings with arrays of integers e.g. a = [3,1,1,2] for ZXXY. |
| 22 | // We assume the zeroth element of Double[] is the angle of rotation |
| 23 | // We index the qubits that Pauli strings act on with arrays of integers e.g. q = [2,4,5,8] for Z_2 X_4 X_5, Y_8 |
| 24 | // An example of a Pauli string GeneratorIndex is thus ((a,b), q) |
| 25 | |
| 26 | // Consider the Hamiltonian H = 0.1 XI + 0.2 IX + 0.3 ZY |
| 27 | // Its GeneratorTerms are (([1],b),[0]), 0.1), (([1],b),[1]), 0.2), (([3,2],b),[0,1]), 0.3). |
| 28 | |
| 29 | /// # Summary |
| 30 | /// Converts a Hamiltonian described by `JWOptimizedHTerms` |
| 31 | /// to a `GeneratorSystem` expressed in terms of the Pauli |
| 32 | /// `GeneratorIndex`. |
| 33 | /// |
| 34 | /// # Input |
| 35 | /// ## data |
| 36 | /// Description of Hamiltonian in `JWOptimizedHTerms` format. |
| 37 | /// |
| 38 | /// # Output |
| 39 | /// Representation of Hamiltonian as `GeneratorSystem`. |
| 40 | function JWBlockEncodingGeneratorSystem(data : JWOptimizedHTerms) : GeneratorSystem { |
| 41 | let ZData = data.HTerm0; |
| 42 | let ZZData = data.HTerm1; |
| 43 | let PQandPQQRData = data.HTerm2; |
| 44 | let h0123Data = data.HTerm3; |
| 45 | mutable genIdxes = Repeated( |
| 46 | new GeneratorIndex { Term = ([0], [0.0]), Subsystem = [0] }, |
| 47 | ((Length(ZData) + Length(ZZData)) + 2 * Length(PQandPQQRData)) + 8 * Length(h0123Data) |
| 48 | ); |
| 49 | mutable startIdx = 0; |
| 50 | |
| 51 | for idx in IndexRange(ZData) { |
| 52 | // Array of Arrays of Length 1 |
| 53 | genIdxes w/= idx <- (ZTermToPauliGenIdx(HTermToGenIdx(ZData[idx], [0])))[0]; |
| 54 | } |
| 55 | |
| 56 | startIdx = Length(ZData); |
| 57 | |
| 58 | for idx in IndexRange(ZZData) { |
| 59 | // Array of Arrays of Length 1 |
| 60 | genIdxes w/= startIdx + idx <- (ZZTermToPauliGenIdx(HTermToGenIdx(ZZData[idx], [1])))[0]; |
| 61 | } |
| 62 | |
| 63 | startIdx = startIdx + Length(ZZData); |
| 64 | |
| 65 | for idx in IndexRange(PQandPQQRData) { |
| 66 | |
| 67 | // Array of Arrays of Length 2 |
| 68 | let genArr = PQandPQQRTermToPauliGenIdx(HTermToGenIdx(PQandPQQRData[idx], [2])); |
| 69 | genIdxes w/= startIdx + 2 * idx <- genArr[0]; |
| 70 | genIdxes w/= (startIdx + 2 * idx) + 1 <- genArr[1]; |
| 71 | } |
| 72 | |
| 73 | startIdx = startIdx + 2 * Length(PQandPQQRData); |
| 74 | mutable finalIdx = startIdx; |
| 75 | |
| 76 | for idx in 0..Length(h0123Data) - 1 { |
| 77 | // Array of Arrays of Length up to 8 |
| 78 | let genArr = V0123TermToPauliGenIdx(HTermToGenIdx(h0123Data[idx], [3])); |
| 79 | |
| 80 | for idx0123 in IndexRange(genArr) { |
| 81 | genIdxes w/= finalIdx <- genArr[idx0123]; |
| 82 | finalIdx += 1; |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | let genIdxes = genIdxes[0..finalIdx - 1]; |
| 87 | return new GeneratorSystem { NumEntries = finalIdx, EntryAt = idx -> genIdxes[idx] }; |
| 88 | } |
| 89 | |
| 90 | /// # Summary |
| 91 | /// Converts a `GeneratorIndex` describing a Z term to |
| 92 | /// an expression `GeneratorIndex[]` in terms of Paulis. |
| 93 | /// |
| 94 | /// # Input |
| 95 | /// ## term |
| 96 | /// `GeneratorIndex` representing a Z term. |
| 97 | /// |
| 98 | /// # Output |
| 99 | /// `GeneratorIndex[]` expressing Z term as Pauli terms. |
| 100 | function ZTermToPauliGenIdx(term : GeneratorIndex) : GeneratorIndex[] { |
| 101 | let (_, coeff) = term.Term; |
| 102 | return [new GeneratorIndex { Term = ([3], coeff), Subsystem = term.Subsystem }]; |
| 103 | } |
| 104 | |
| 105 | /// # Summary |
| 106 | /// Converts a `GeneratorIndex` describing a ZZ term to |
| 107 | /// an expression `GeneratorIndex[]` in terms of Paulis. |
| 108 | /// |
| 109 | /// # Input |
| 110 | /// ## term |
| 111 | /// `GeneratorIndex` representing a ZZ term. |
| 112 | /// |
| 113 | /// # Output |
| 114 | /// `GeneratorIndex[]` expressing ZZ term as Pauli terms. |
| 115 | function ZZTermToPauliGenIdx(term : GeneratorIndex) : GeneratorIndex[] { |
| 116 | let (_, coeff) = term.Term; |
| 117 | return [new GeneratorIndex { Term = ([3, 3], coeff), Subsystem = term.Subsystem }]; |
| 118 | } |
| 119 | |
| 120 | /// # Summary |
| 121 | /// Converts a `GeneratorIndex` describing a PQ term to |
| 122 | /// an expression `GeneratorIndex[]` in terms of Paulis |
| 123 | /// |
| 124 | /// # Input |
| 125 | /// ## term |
| 126 | /// `GeneratorIndex` representing a PQ term. |
| 127 | /// |
| 128 | /// # Output |
| 129 | /// `GeneratorIndex[]` expressing PQ term as Pauli terms. |
| 130 | function PQTermToPauliGenIdx(term : GeneratorIndex) : GeneratorIndex[] { |
| 131 | let (_, coeff) = term.Term; |
| 132 | let newCoeff = [coeff[0]]; |
| 133 | let qubitPidx = term.Subsystem[0]; |
| 134 | let qubitQidx = term.Subsystem[1]; |
| 135 | let qubitIndices = RangeAsIntArray(qubitPidx..qubitQidx); |
| 136 | return [ |
| 137 | new GeneratorIndex { Term = (([1] + Repeated(3, Length(qubitIndices) - 2)) + [1], newCoeff), Subsystem = qubitIndices }, |
| 138 | new GeneratorIndex { Term = (([2] + Repeated(3, Length(qubitIndices) - 2)) + [2], newCoeff), Subsystem = qubitIndices } |
| 139 | ]; |
| 140 | } |
| 141 | |
| 142 | /// # Summary |
| 143 | /// Converts a `GeneratorIndex` describing a PQ or PQQR term to |
| 144 | /// an expression `GeneratorIndex[]` in terms of Paulis |
| 145 | /// |
| 146 | /// # Input |
| 147 | /// ## term |
| 148 | /// `GeneratorIndex` representing a PQ or PQQR term. |
| 149 | /// |
| 150 | /// # Output |
| 151 | /// `GeneratorIndex[]` expressing PQ or PQQR term as Pauli terms. |
| 152 | function PQandPQQRTermToPauliGenIdx(term : GeneratorIndex) : GeneratorIndex[] { |
| 153 | let (_, coeff) = term.Term; |
| 154 | let newCoeff = [coeff[0]]; |
| 155 | |
| 156 | if Length(term.Subsystem) == 2 { |
| 157 | return PQTermToPauliGenIdx(term); |
| 158 | } else { |
| 159 | let qubitPidx = term.Subsystem[0]; |
| 160 | let qubitQidx = term.Subsystem[1]; |
| 161 | let qubitRidx = term.Subsystem[3]; |
| 162 | |
| 163 | if (qubitPidx < qubitQidx and qubitQidx < qubitRidx) { |
| 164 | |
| 165 | // Apply XZ..ZIZ..ZX |
| 166 | let qubitIndices = RangeAsIntArray(qubitPidx..qubitQidx - 1) + RangeAsIntArray(qubitQidx + 1..qubitRidx); |
| 167 | return [ |
| 168 | new GeneratorIndex { Term = (([1] + Repeated(3, Length(qubitIndices) - 2)) + [1], newCoeff), Subsystem = qubitIndices }, |
| 169 | new GeneratorIndex { Term = (([2] + Repeated(3, Length(qubitIndices) - 2)) + [2], newCoeff), Subsystem = qubitIndices } |
| 170 | ]; |
| 171 | } else { |
| 172 | |
| 173 | // Apply ZI..IXZ..ZX or XZ..ZXI..IZ |
| 174 | let qubitIndices = RangeAsIntArray(qubitPidx..qubitRidx) + [qubitQidx]; |
| 175 | return [ |
| 176 | new GeneratorIndex { Term = (([1] + Repeated(3, Length(qubitIndices) - 3)) + [1, 3], newCoeff), Subsystem = qubitIndices }, |
| 177 | new GeneratorIndex { Term = (([2] + Repeated(3, Length(qubitIndices) - 3)) + [2, 3], newCoeff), Subsystem = qubitIndices } |
| 178 | ]; |
| 179 | } |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | /// # Summary |
| 184 | /// Converts a `GeneratorIndex` describing a PQRS term to |
| 185 | /// an expression `GeneratorIndex[]` in terms of Paulis |
| 186 | /// |
| 187 | /// # Input |
| 188 | /// ## term |
| 189 | /// `GeneratorIndex` representing a PQRS term. |
| 190 | /// |
| 191 | /// # Output |
| 192 | /// `GeneratorIndex[]` expressing PQRS term as Pauli terms. |
| 193 | function V0123TermToPauliGenIdx(term : GeneratorIndex) : GeneratorIndex[] { |
| 194 | let (_, v0123) = term.Term; |
| 195 | let qubitsPQ = term.Subsystem[0..1]; |
| 196 | let qubitsRS = term.Subsystem[2..3]; |
| 197 | let qubitsPQJW = RangeAsIntArray(qubitsPQ[0] + 1..qubitsPQ[1] - 1); |
| 198 | let qubitsRSJW = RangeAsIntArray(qubitsRS[0] + 1..qubitsRS[1] - 1); |
| 199 | let ops = [[1, 1, 1, 1], [1, 1, 2, 2], [1, 2, 1, 2], [2, 1, 1, 2], [2, 2, 2, 2], [2, 2, 1, 1], [2, 1, 2, 1], [1, 2, 2, 1]]; |
| 200 | mutable genIdxes = Repeated(new GeneratorIndex { Term = ([0], [0.0]), Subsystem = [0] }, 8); |
| 201 | mutable nonZero = 0; |
| 202 | |
| 203 | for idxOp in IndexRange(ops) { |
| 204 | if (IsNotZero(v0123[idxOp % 4])) { |
| 205 | let newCoeff = [v0123[idxOp % 4]]; |
| 206 | genIdxes w/= nonZero <- new GeneratorIndex { |
| 207 | Term = (ops[idxOp] + Repeated(3, Length(qubitsPQJW) + Length(qubitsRSJW)), newCoeff), |
| 208 | Subsystem = ((qubitsPQ + qubitsRS) + qubitsPQJW) + qubitsRSJW |
| 209 | }; |
| 210 | nonZero = nonZero + 1; |
| 211 | } |
| 212 | } |
| 213 | |
| 214 | return genIdxes[0..nonZero - 1]; |
| 215 | } |
| 216 | |