|
8 | 8 | https://arxiv.org/abs/2001.00807 |
9 | 9 | """ |
10 | 10 |
|
| 11 | +import math |
| 12 | + |
11 | 13 | import psiqworkbench.qubricks as qbk |
12 | | -from psiqworkbench import QFixed, QInt, Qubits, QUInt |
| 14 | +from psiqworkbench import QFixed, QInt, Qubits, QUFixed, QUInt |
13 | 15 | from psiqworkbench.qubits.base_qubits import BaseQubits |
14 | 16 | from psiqworkbench.qubricks import Qubrick |
15 | 17 | from psiqworkbench.symbolics.qubrick_costs import QubrickCosts |
16 | 18 |
|
17 | | -from .common import AddConst, Negate |
18 | | -from .sqrt import Sqrt |
| 19 | +from ..utils.gates import ParallelCnot, ParallelCnotCtrl, write_int |
| 20 | +from ..utils.rotate import Div2, rotate_left, rotate_right |
19 | 21 | from ..utils.symbolic import alloc_temp_qreg_like |
| 22 | +from .bits import HighestSetBit |
| 23 | +from .common import AddConst, Negate, MultiplyConstAdd |
| 24 | +from .sqrt import Sqrt |
| 25 | +from .square import Square, SquareOptimized |
20 | 26 |
|
21 | 27 |
|
22 | 28 | def _sqrt_half(x: QFixed) -> QFixed: |
@@ -119,3 +125,97 @@ def _compute(self, x: QFixed): |
119 | 125 | cos_op = CosFbe(result_radix=self.result_radix) |
120 | 126 | cos_op.compute(x) |
121 | 127 | self.set_result_qreg(cos_op.get_result_qreg()) |
| 128 | + |
| 129 | + |
| 130 | +class Log2FbeSegment(Qubrick): |
| 131 | + """Computes log2(x) where 1<=x<2. |
| 132 | +
|
| 133 | + Reference: https://arxiv.org/abs/2001.00807, section 3.1.1. |
| 134 | + """ |
| 135 | + |
| 136 | + def _square(self, x: QUFixed) -> QUFixed: |
| 137 | + result = QUFixed(self.alloc_temp_qreg(x.num_qubits, name="a"), radix=x.radix) |
| 138 | + SquareOptimized(signed=False).compute(x, result) |
| 139 | + return result |
| 140 | + |
| 141 | + def _compute(self, x: QUFixed, result: QUFixed): |
| 142 | + assert x.num_qubits == 2 + x.radix |
| 143 | + assert result.num_qubits == result.radix |
| 144 | + a = self._square(x) |
| 145 | + |
| 146 | + for i in range(result.radix): |
| 147 | + result_bit = result[result.radix - 1 - i] |
| 148 | + result_bit.x(a[-1]) |
| 149 | + Div2().compute(a, ctrl=result_bit) |
| 150 | + a = self._square(a) |
| 151 | + |
| 152 | + |
| 153 | +class Log2Fbe(Qubrick): |
| 154 | + """Computes log2(x) where x>0.""" |
| 155 | + |
| 156 | + def __init__( |
| 157 | + self, |
| 158 | + *, |
| 159 | + result_radix: None | int = None, |
| 160 | + **kwargs, |
| 161 | + ): |
| 162 | + super().__init__(**kwargs) |
| 163 | + self.result_radix = result_radix |
| 164 | + |
| 165 | + def _compute(self, x: QUFixed): |
| 166 | + # Find most significant bit of `x`, set it it `msb`. |
| 167 | + xn = x.num_qubits |
| 168 | + msb = self.alloc_temp_qreg(xn, "msb") |
| 169 | + HighestSetBit().compute(x, msb) |
| 170 | + |
| 171 | + # Make shifted copy of input, such that second most significnat bit in |
| 172 | + # the copy corresponds to highest set bit in input. |
| 173 | + # This way value in x_copy is in range [1, 2). |
| 174 | + x_copy_qubits = self.alloc_temp_qreg(xn, name="x_copy") |
| 175 | + x_copy = QUFixed(x_copy_qubits, radix=xn - 2) |
| 176 | + for i in range(xn): |
| 177 | + # Controlled shift-copy. |
| 178 | + if i == xn - 1: |
| 179 | + ParallelCnotCtrl().compute(msb[i], x[1:], x_copy_qubits[0 : xn - 1]) |
| 180 | + else: |
| 181 | + shift_left = xn - 2 - i |
| 182 | + assert shift_left >= 0 |
| 183 | + ParallelCnotCtrl().compute(msb[i], x[0 : xn - shift_left], x_copy_qubits[shift_left:]) |
| 184 | + |
| 185 | + # Compute logarithm for shifted copy. |
| 186 | + r = self.result_radix or x.radix |
| 187 | + result_fract_part = QUFixed(self.alloc_temp_qreg(r, "result_frac"), radix=r) |
| 188 | + Log2FbeSegment().compute(x_copy, result_fract_part) |
| 189 | + |
| 190 | + # Add integer to result, corresponding to input's shift. |
| 191 | + int_part_size = math.ceil(math.log2(max(x.radix, xn - x.radix))) + 1 |
| 192 | + result_int_part = QInt(self.alloc_temp_qreg(int_part_size, "result_int")) |
| 193 | + for i in range(xn): |
| 194 | + write_int(result_int_part, i - x.radix, ctrl=msb[i]) |
| 195 | + |
| 196 | + self.set_result_qreg(QFixed(result_fract_part | result_int_part, radix=r)) |
| 197 | + |
| 198 | + |
| 199 | +class LogFbe(Qubrick): |
| 200 | + """Computes logarithm in given base.""" |
| 201 | + |
| 202 | + def __init__( |
| 203 | + self, |
| 204 | + base: float, |
| 205 | + *, |
| 206 | + result_radix: None | int = None, |
| 207 | + **kwargs, |
| 208 | + ): |
| 209 | + super().__init__(**kwargs) |
| 210 | + self.ans_multiplier = 1.0 / math.log2(base) |
| 211 | + self.result_radix = result_radix |
| 212 | + |
| 213 | + def _compute(self, x: QUFixed): |
| 214 | + op = Log2Fbe(result_radix=self.result_radix) |
| 215 | + with op.computed(x): |
| 216 | + _, ans = alloc_temp_qreg_like(self, op.get_result_qreg()) |
| 217 | + if self.ans_multiplier == 1.0: |
| 218 | + ParallelCnot().compute(op.get_result_qreg(), ans) |
| 219 | + else: |
| 220 | + MultiplyConstAdd(self.ans_multiplier).compute(ans, op.get_result_qreg()) |
| 221 | + self.set_result_qreg(ans) |
0 commit comments