microsoft/qdk
Publicmirrored from https://github.com/microsoft/qdkAvailable
library/chemistry/src/JordanWigner/OptimizedBEOperator.qs
168lines · modecode
| 1 | // Copyright (c) Microsoft Corporation. |
| 2 | // Licensed under the MIT License. |
| 3 | |
| 4 | export OptimizedBEXY; |
| 5 | |
| 6 | import Std.Arrays.IndexRange; |
| 7 | import Std.Arrays.Partitioned; |
| 8 | import Std.Math.Max; |
| 9 | |
| 10 | import Generators.MultiplexOperationsFromGenerator; |
| 11 | |
| 12 | /// # Summary |
| 13 | /// Applies a sequence of Z operations and either an X or Y operation to |
| 14 | /// a register of qubits, where the selection of target qubits and basis |
| 15 | /// are conditioned on the state of a control register. |
| 16 | /// |
| 17 | /// # Description |
| 18 | /// This operation can be described by a unitary matrix $U$ that applies |
| 19 | /// the Pauli string on $(X^{z+1}\_pY^{z}\_p)Z\_{p-1}...Z_0$ on |
| 20 | /// qubits $0..p$ conditioned on an index $z\in\{0,1\}$ and $p$. |
| 21 | /// |
| 22 | /// That is, |
| 23 | /// $$ |
| 24 | /// \begin{align} |
| 25 | /// U\ket{z}\ket{p}\ket{\psi} = \ket{z}\ket{p}(X^{z+1}\_pY^{z}\_p)Z\_{p-1}...Z_0\ket{\psi} |
| 26 | /// \end{align} |
| 27 | /// $$ |
| 28 | /// |
| 29 | /// # Input |
| 30 | /// ## pauliBasis |
| 31 | /// When this qubit is in state $\ket{0}$, an `X` operation is applied. When it is in state $\ket{1}$, `Y` is applied. |
| 32 | /// ## indexRegister |
| 33 | /// The state $\ket{p}$ of this register determines the qubit on which `X` or `Y` is applied. |
| 34 | /// ## targetRegister |
| 35 | /// Register of qubits on which the Pauli operators are applied. |
| 36 | /// |
| 37 | /// # References |
| 38 | /// - [Encoding Electronic Spectra in Quantum Circuits with Linear T Complexity](https://arxiv.org/abs/1805.03662) |
| 39 | /// Ryan Babbush, Craig Gidney, Dominic W. Berry, Nathan Wiebe, Jarrod McClean, Alexandru Paler, Austin Fowler, Hartmut Neven |
| 40 | operation OptimizedBEXY( |
| 41 | pauliBasis : Qubit, |
| 42 | indexRegister : Qubit[], |
| 43 | targetRegister : Qubit[] |
| 44 | ) : Unit is Adj + Ctl { |
| 45 | let unitaryGenerator = (Length(targetRegister), idx -> OptimizedBEXYImpl(idx, _, _, _)); |
| 46 | |
| 47 | use accumulator = Qubit(); |
| 48 | |
| 49 | // This assumes that MultiplexOperationsFromGenerator applies unitaries indexed in unitaryGenerator in ascending order. |
| 50 | X(accumulator); |
| 51 | MultiplexOperationsFromGenerator(unitaryGenerator, indexRegister, (pauliBasis, accumulator, targetRegister)); |
| 52 | // If indexRegister encodes an integer that is larger than Length(targetRegister), |
| 53 | // _OptimizedBEXY_ will fail due to an out of range error. In this situation, |
| 54 | // releasing the accumulator qubit will throw an error as it will be in the One state. |
| 55 | } |
| 56 | |
| 57 | // Subroutine of OptimizedBEXY. |
| 58 | operation OptimizedBEXYImpl( |
| 59 | targetIndex : Int, |
| 60 | pauliBasis : Qubit, |
| 61 | accumulator : Qubit, |
| 62 | targetRegister : Qubit[] |
| 63 | ) : Unit is Adj + Ctl { |
| 64 | |
| 65 | body (...) { |
| 66 | // This should always be called as a controlled operation. |
| 67 | fail "_OptimizedBEXY should always be called as a controlled operation."; |
| 68 | } |
| 69 | |
| 70 | controlled (ctrl, ...) { |
| 71 | if Length(targetRegister) <= targetIndex { |
| 72 | fail "targetIndex out of range."; |
| 73 | } |
| 74 | |
| 75 | Controlled X(ctrl, accumulator); |
| 76 | within { |
| 77 | Controlled Adjoint S([pauliBasis], targetRegister[targetIndex]); |
| 78 | } apply { |
| 79 | Controlled X(ctrl, targetRegister[targetIndex]); |
| 80 | } |
| 81 | Controlled Z([accumulator], targetRegister[targetIndex]); |
| 82 | } |
| 83 | |
| 84 | } |
| 85 | |
| 86 | /// # Summary |
| 87 | /// Applies a Z operation to a qubit indicated by the state of another |
| 88 | /// register. |
| 89 | /// |
| 90 | /// # Description |
| 91 | /// The operation can be represented by a unitary matrix $U$ that applies |
| 92 | /// the `Std.Intrinsic.Z` operation on a qubit $p$ |
| 93 | /// conditioned on an index state $\ket{p}$. That is, |
| 94 | /// $$ |
| 95 | /// \begin{align} |
| 96 | /// U\ket{p}\ket{\psi} = \ket{p}Z\_p\ket{\psi}. |
| 97 | /// \end{align} |
| 98 | /// $$ |
| 99 | /// |
| 100 | /// # Input |
| 101 | /// ## indexRegister |
| 102 | /// A register in the state $\ket{p}$, determining the qubit on which $Z$ is applied. |
| 103 | /// ## targetRegister |
| 104 | /// Register of qubits on which the Pauli operators are applied. |
| 105 | operation SelectZ(indexRegister : Qubit[], targetRegister : Qubit[]) : Unit is Adj + Ctl { |
| 106 | let unitaryGenerator = (Length(targetRegister), idx -> (qs => Z(qs[idx]))); |
| 107 | MultiplexOperationsFromGenerator(unitaryGenerator, indexRegister, targetRegister); |
| 108 | // If indexRegister encodes an integer that is larger than Length(targetRegister), |
| 109 | // _SelectZ_ will fail due to an out of range error. In this situation, |
| 110 | // releasing the accumulator qubit will throw an error as it will be in the One state. |
| 111 | } |
| 112 | |
| 113 | operation JWSelect( |
| 114 | signQubit : Qubit, |
| 115 | selectZControlRegisters : Qubit[], |
| 116 | OptimizedBEControlRegisters : Qubit[], |
| 117 | pauliBases : Qubit[], |
| 118 | indexRegisters : Qubit[][], |
| 119 | targetRegister : Qubit[] |
| 120 | ) : Unit is Adj + Ctl { |
| 121 | Z(signQubit); |
| 122 | |
| 123 | for idxRegister in IndexRange(OptimizedBEControlRegisters) { |
| 124 | Controlled OptimizedBEXY( |
| 125 | [OptimizedBEControlRegisters[idxRegister]], |
| 126 | (pauliBases[idxRegister], indexRegisters[idxRegister], targetRegister) |
| 127 | ); |
| 128 | } |
| 129 | |
| 130 | for idxRegister in IndexRange(selectZControlRegisters) { |
| 131 | Controlled SelectZ([selectZControlRegisters[idxRegister]], (indexRegisters[idxRegister], targetRegister)); |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | function JWSelectQubitCount(nZ : Int, nMaj : Int, nIdxRegQubits : Int) : (Int, (Int, Int, Int, Int, Int[])) { |
| 136 | let signQubit = 1; |
| 137 | let selectZControlRegisters = nZ; |
| 138 | let OptimizedBEControlRegisters = nMaj; |
| 139 | let pauliBases = nMaj; |
| 140 | let indexRegisters = Repeated(nIdxRegQubits, Max([nZ, nMaj])); |
| 141 | let nTotal = ((1 + nZ) + 2 * nMaj) + Max([nZ, nMaj]) * nIdxRegQubits; |
| 142 | return (nTotal, (signQubit, selectZControlRegisters, OptimizedBEControlRegisters, pauliBases, indexRegisters)); |
| 143 | } |
| 144 | |
| 145 | function JWSelectQubitManager( |
| 146 | nZ : Int, |
| 147 | nMaj : Int, |
| 148 | nIdxRegQubits : Int, |
| 149 | ctrlRegister : Qubit[], |
| 150 | targetRegister : Qubit[] |
| 151 | ) : ((Qubit, Qubit[], Qubit[], Qubit[], Qubit[][], Qubit[]), Qubit[]) { |
| 152 | let (nTotal, (a, b, c, d, e)) = JWSelectQubitCount(nZ, nMaj, nIdxRegQubits); |
| 153 | let split = [a, b, c, d] + e; |
| 154 | let registers = Partitioned(split, ctrlRegister); |
| 155 | let signQubit = registers[0]; |
| 156 | let selectZControlRegisters = registers[1]; |
| 157 | let OptimizedBEControlRegisters = registers[2]; |
| 158 | let pauliBases = registers[3]; |
| 159 | let indexRegistersTmp = registers[4..(4 + Length(e)) - 1]; |
| 160 | let rest = registers[Length(registers) - 1]; |
| 161 | |
| 162 | mutable indexRegisters = []; |
| 163 | for idx in IndexRange(e) { |
| 164 | indexRegisters += [indexRegistersTmp[idx]]; |
| 165 | } |
| 166 | |
| 167 | return ((signQubit[0], selectZControlRegisters, OptimizedBEControlRegisters, pauliBases, indexRegisters, targetRegister), rest); |
| 168 | } |
| 169 | |