|
| 1 | +# Copyright 2023 Google LLC |
| 2 | +# |
| 3 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +# you may not use this file except in compliance with the License. |
| 5 | +# You may obtain a copy of the License at |
| 6 | +# |
| 7 | +# https://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +# |
| 9 | +# Unless required by applicable law or agreed to in writing, software |
| 10 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +# See the License for the specific language governing permissions and |
| 13 | +# limitations under the License. |
| 14 | +r"""The Givens rotation Bloqs help count costs for similarity transforming |
| 15 | +fermionic ladder operators to produce linear combinations of fermionic ladder operators. |
| 16 | +
|
| 17 | +Following notation from Reference [1] we note that a single |
| 18 | +ladder operator can be similarity transformed by a basis rotation to produce a linear |
| 19 | +combination of ladder operators |
| 20 | +$$ |
| 21 | +U(Q)a_{q}U(Q)^{\dagger} = \sum_{p}Q_{pq}^{*}a_{p} = \overrightarrow{a}_{q}\\ |
| 22 | +U(Q)a_{q}^{\dagger}U(Q)^{\dagger} = \sum_{p}Q_{pq}a_{p}^{\dagger} = |
| 23 | +\overrightarrow{a}_{q}^{\dagger} |
| 24 | +$$ |
| 25 | +Each vector of operators can be implemented by a $N$ (size of basis) Givens rotation unitaries as |
| 26 | +$$ |
| 27 | +V_{\overrightarrow{Q}_{q}} a_{0} V_{\overrightarrow{Q}_{q}}^{\dagger} = |
| 28 | +\overrightarrow{a}_{q} \\ |
| 29 | +V_{\overrightarrow{Q}_{q}} a_{0}^{\dagger} V_{\overrightarrow{Q}_{q}}^{\dagger} = |
| 30 | +\overrightarrow{a}_{q}^{\dagger} |
| 31 | +$$ |
| 32 | +where |
| 33 | +$$ |
| 34 | +V_{\overrightarrow{Q}_{q}} = V_{n-1,n-2}(0, \phi_{n-1}) V_{n-2, n-3}(\theta_{n-2}, \phi_{n-2}) |
| 35 | +V_{n-3,n-4}(\theta_{n-2}, \phi_{n-2})...V_{2, 1}(\theta_{1}, \phi_{1}) |
| 36 | +V_{1, 0}(\theta_{0}, \phi_{0}) |
| 37 | +$$ |
| 38 | +with each $V_{ij}(\theta, \phi) = \mathrm{RZ}_{j}(\pi)\mathrm{R}_{ij}(\theta)$. |
| 39 | +and $1$ Rz rotation for real valued $\overrightarrow{Q}$. |
| 40 | +
|
| 41 | +
|
| 42 | +References: |
| 43 | + 1. Vera von Burg, Guang Hao Low, Thomas H ̈aner, Damian S. Steiger, Markus Reiher, |
| 44 | + Martin Roetteler, and Matthias Troyer, “Quantum computing enhanced computational catalysis,” |
| 45 | + Phys. Rev. Res. 3, 033055 (2021). |
| 46 | +
|
| 47 | +""" |
| 48 | +from functools import cached_property |
| 49 | +from typing import Dict |
| 50 | + |
| 51 | +from attrs import frozen |
| 52 | + |
| 53 | +from qualtran import Bloq, bloq_example, BloqBuilder, BloqDocSpec, QBit, QFxp, Signature, SoquetT |
| 54 | +from qualtran.bloqs.basic_gates import CNOT, Hadamard, SGate, Toffoli, XGate |
| 55 | +from qualtran.bloqs.rotations.phase_gradient import AddIntoPhaseGrad |
| 56 | + |
| 57 | + |
| 58 | +class RzAddIntoPhaseGradient(AddIntoPhaseGrad): |
| 59 | + r"""Temporary controlled adder to give the right complexity for Rz rotations by |
| 60 | + phase gradient state addition. |
| 61 | +
|
| 62 | + References: |
| 63 | + [Compilation of Fault-Tolerant Quantum Heuristics for Combinatorial Optimization]( |
| 64 | + https://arxiv.org/abs/2007.07391). |
| 65 | + Section II-C: Oracles for phasing by cost function. Appendix A: Addition for controlled |
| 66 | + rotations |
| 67 | + """ |
| 68 | + |
| 69 | + def bloq_counts(self): |
| 70 | + return {Toffoli(): self.x_bitsize - 2} |
| 71 | + |
| 72 | + |
| 73 | +@frozen |
| 74 | +class RealGivensRotationByPhaseGradient(Bloq): |
| 75 | + r"""Givens rotation corresponding to a 2-fermion mode transformation generated by |
| 76 | +
|
| 77 | + $$ |
| 78 | + e^{\theta (a_{i}^{\dagger}a_{j} - a_{j}^{\dagger}a_{i})} = e^{i \theta (YX + XY) / 2} |
| 79 | + $$ |
| 80 | +
|
| 81 | + corresponding to the circuit |
| 82 | +
|
| 83 | + i: ───X───X───S^-1───X───Rz(theta)───X───X───@───────X───S^-1─── |
| 84 | + │ │ │ │ │ |
| 85 | + j: ───S───@───H──────@───Rz(theta)───@───────X───H───@────────── |
| 86 | +
|
| 87 | + The rotation is performed by addition into a phase state and the fractional binary for |
| 88 | + $\theta$ is stored in an additional register. |
| 89 | +
|
| 90 | + The Toffoli cost for this block comes from the cost of two rotations by addition into |
| 91 | + the phase gradient state which which is $2(b_{\mathrm{grad}}-2)$ where $b_{\mathrm{grad}}$ |
| 92 | + is the size of the phasegradient register. |
| 93 | +
|
| 94 | + Args: |
| 95 | + phasegrad_bitsize int: size of phase gradient which is also the size of the register |
| 96 | + representing the binary fraction of the rotation angle |
| 97 | + Registers: |
| 98 | + target_i: 1st-qubit QBit type register |
| 99 | + target_j: 2nd-qubit Qbit type register |
| 100 | + rom_data: QFxp data representing fractional binary for real part of rotation |
| 101 | + phase_gradient: QFxp data type representing the phase gradient register |
| 102 | +
|
| 103 | + References: |
| 104 | + [Compilation of Fault-Tolerant Quantum Heuristics for Combinatorial Optimization]( |
| 105 | + https://arxiv.org/abs/2007.07391). |
| 106 | + Section II-C: Oracles for phasing by cost function. Appendix A: Addition for controlled |
| 107 | + rotations |
| 108 | + """ |
| 109 | + phasegrad_bitsize: int |
| 110 | + |
| 111 | + @cached_property |
| 112 | + def signature(self) -> Signature: |
| 113 | + return Signature.build_from_dtypes( |
| 114 | + target_i=QBit(), |
| 115 | + target_j=QBit(), |
| 116 | + rom_data=QFxp(self.phasegrad_bitsize, self.phasegrad_bitsize, signed=False), |
| 117 | + phase_gradient=QFxp(self.phasegrad_bitsize, self.phasegrad_bitsize, signed=False), |
| 118 | + ) |
| 119 | + |
| 120 | + def build_composite_bloq( |
| 121 | + self, |
| 122 | + bb: BloqBuilder, |
| 123 | + target_i: SoquetT, |
| 124 | + target_j: SoquetT, |
| 125 | + rom_data: SoquetT, |
| 126 | + phase_gradient: SoquetT, |
| 127 | + ) -> Dict[str, SoquetT]: |
| 128 | + # set up rz-rotation via phase-gradient state |
| 129 | + add_into_phasegrad_gate = RzAddIntoPhaseGradient( |
| 130 | + x_bitsize=self.phasegrad_bitsize, |
| 131 | + phase_bitsize=self.phasegrad_bitsize, |
| 132 | + right_shift=0, |
| 133 | + sign=1, |
| 134 | + controlled=1, |
| 135 | + ) |
| 136 | + |
| 137 | + # clifford block |
| 138 | + target_i = bb.add(XGate(), q=target_i) |
| 139 | + target_j = bb.add(SGate(), q=target_j) |
| 140 | + target_j, target_i = bb.add(CNOT(), ctrl=target_j, target=target_i) |
| 141 | + target_j = bb.add(Hadamard(), q=target_j) |
| 142 | + target_i = bb.add(SGate(is_adjoint=True), q=target_i) |
| 143 | + target_j, target_i = bb.add(CNOT(), ctrl=target_j, target=target_i) |
| 144 | + |
| 145 | + # parallel rz (Can probably be improved with single out of place adder into a single ancilla |
| 146 | + target_i, rom_data, phase_gradient = bb.add( |
| 147 | + add_into_phasegrad_gate, x=rom_data, phase_grad=phase_gradient, ctrl=target_i |
| 148 | + ) |
| 149 | + target_j, rom_data, phase_gradient = bb.add( |
| 150 | + add_into_phasegrad_gate, x=rom_data, phase_grad=phase_gradient, ctrl=target_j |
| 151 | + ) |
| 152 | + |
| 153 | + # clifford block |
| 154 | + target_j, target_i = bb.add(CNOT(), ctrl=target_j, target=target_i) |
| 155 | + target_i = bb.add(XGate(), q=target_i) |
| 156 | + target_i, target_j = bb.add(CNOT(), ctrl=target_i, target=target_j) |
| 157 | + target_j = bb.add(Hadamard(), q=target_j) |
| 158 | + target_j, target_i = bb.add(CNOT(), ctrl=target_j, target=target_i) |
| 159 | + target_i = bb.add(SGate(), q=target_i) |
| 160 | + |
| 161 | + return { |
| 162 | + 'target_i': target_i, |
| 163 | + 'target_j': target_j, |
| 164 | + 'rom_data': rom_data, |
| 165 | + 'phase_gradient': phase_gradient, |
| 166 | + } |
| 167 | + |
| 168 | + |
| 169 | +@bloq_example |
| 170 | +def _real_givens() -> RealGivensRotationByPhaseGradient: |
| 171 | + r_givens = RealGivensRotationByPhaseGradient(phasegrad_bitsize=4) |
| 172 | + return r_givens |
| 173 | + |
| 174 | + |
| 175 | +_REAL_GIVENS_DOC = BloqDocSpec( |
| 176 | + bloq_cls=RealGivensRotationByPhaseGradient, |
| 177 | + import_line='from qualtran.bloqs.chemistry.quad_fermion.givens_bloq import RealGivensRotationByPhaseGradient', |
| 178 | + examples=(_real_givens,), |
| 179 | +) |
| 180 | + |
| 181 | + |
| 182 | +@frozen |
| 183 | +class ComplexGivensRotationByPhaseGradient(Bloq): |
| 184 | + r"""Complex Givens rotation corresponding to a 2-fermion mode transformation generated by |
| 185 | +
|
| 186 | + $$ |
| 187 | + e^{i \phi n_{j}}e^{\theta (a_{i}^{\dagger}a_{j} - a_{j}^{\dagger}a_{i})} = e^{i \phi Z_{j}/2}e^{i \theta (YX + XY) / 2} |
| 188 | + $$ |
| 189 | +
|
| 190 | + corresponding to the circuit |
| 191 | +
|
| 192 | + i: ───X───X───S^-1───X───Rz(theta)───X───X───@───────X──S^-1───── |
| 193 | + │ │ │ │ │ |
| 194 | + j: ───S───@───H──────@───Rz(theta)───@───────X───H───@──Rz(phi)── |
| 195 | +
|
| 196 | + The rotation is performed by addition into a phase state and the fractional binary for |
| 197 | + $\theta$ is stored in an additional register. |
| 198 | +
|
| 199 | + Args: |
| 200 | + phasegrad_bitsize int: size of phase gradient which is also the size of the register |
| 201 | + representing the binary fraction of the rotation angles |
| 202 | + Registers: |
| 203 | + target_i: 1st-qubit QBit type register |
| 204 | + target_j: 2nd-qubit Qbit type register |
| 205 | + real_rom_data: QFxp data representing fractional binary for real part of rotation |
| 206 | + cplx_rom_data: QFxp data representing fractional binary for imag part of rotation |
| 207 | + phase_gradient: QFxp data type representing the phase gradient register |
| 208 | + """ |
| 209 | + |
| 210 | + phasegrad_bitsize: int |
| 211 | + |
| 212 | + @cached_property |
| 213 | + def signature(self) -> Signature: |
| 214 | + return Signature.build_from_dtypes( |
| 215 | + target_i=QBit(), |
| 216 | + target_j=QBit(), |
| 217 | + real_rom_data=QFxp(self.phasegrad_bitsize, self.phasegrad_bitsize, signed=False), |
| 218 | + cplx_rom_data=QFxp(self.phasegrad_bitsize, self.phasegrad_bitsize, signed=False), |
| 219 | + phase_gradient=QFxp(self.phasegrad_bitsize, self.phasegrad_bitsize, signed=False), |
| 220 | + ) |
| 221 | + |
| 222 | + def build_composite_bloq( |
| 223 | + self, |
| 224 | + bb: BloqBuilder, |
| 225 | + target_i: SoquetT, |
| 226 | + target_j: SoquetT, |
| 227 | + real_rom_data: SoquetT, |
| 228 | + cplx_rom_data: SoquetT, |
| 229 | + phase_gradient: SoquetT, |
| 230 | + ) -> Dict[str, SoquetT]: |
| 231 | + real_givens_gate = RealGivensRotationByPhaseGradient( |
| 232 | + phasegrad_bitsize=self.phasegrad_bitsize |
| 233 | + ) |
| 234 | + |
| 235 | + # real-valued Givens rotation |
| 236 | + target_i, target_j, real_rom_data, phase_gradient = bb.add( |
| 237 | + real_givens_gate, |
| 238 | + target_i=target_i, |
| 239 | + target_j=target_j, |
| 240 | + rom_data=real_rom_data, |
| 241 | + phase_gradient=phase_gradient, |
| 242 | + ) |
| 243 | + |
| 244 | + # set up rz-rotation on j-bit by phase-gradient state |
| 245 | + add_into_phasegrad_gate = RzAddIntoPhaseGradient( |
| 246 | + x_bitsize=self.phasegrad_bitsize, |
| 247 | + phase_bitsize=self.phasegrad_bitsize, |
| 248 | + right_shift=0, |
| 249 | + sign=1, |
| 250 | + controlled=1, |
| 251 | + ) |
| 252 | + target_j, cplx_rom_data, phase_gradient = bb.add( |
| 253 | + add_into_phasegrad_gate, x=cplx_rom_data, phase_grad=phase_gradient, ctrl=target_j |
| 254 | + ) |
| 255 | + return { |
| 256 | + 'target_i': target_i, |
| 257 | + 'target_j': target_j, |
| 258 | + 'real_rom_data': real_rom_data, |
| 259 | + 'cplx_rom_data': cplx_rom_data, |
| 260 | + 'phase_gradient': phase_gradient, |
| 261 | + } |
| 262 | + |
| 263 | + |
| 264 | +@bloq_example |
| 265 | +def _cplx_givens() -> ComplexGivensRotationByPhaseGradient: |
| 266 | + c_givens = ComplexGivensRotationByPhaseGradient(phasegrad_bitsize=4) |
| 267 | + return c_givens |
| 268 | + |
| 269 | + |
| 270 | +_CPLX_GIVENS_DOC = BloqDocSpec( |
| 271 | + bloq_cls=ComplexGivensRotationByPhaseGradient, |
| 272 | + import_line='from qualtran.bloqs.chemistry.quad_fermion.givens_bloq import ComplexGivensRotationByPhaseGradient', |
| 273 | + examples=(_cplx_givens,), |
| 274 | +) |
0 commit comments