|
28 | 28 | ) |
29 | 29 |
|
30 | 30 | from cirq import ops, value |
| 31 | +from cirq._compat import deprecated_class |
31 | 32 | from cirq.interop.quirk.cells.cell import Cell, CellMaker, CELL_SIZES |
32 | 33 |
|
33 | 34 | if TYPE_CHECKING: |
34 | 35 | import cirq |
35 | 36 |
|
36 | 37 |
|
| 38 | +@deprecated_class(deadline='v0.15', fix='Use cirq.QuirkArithmeticGate') |
37 | 39 | @value.value_equality |
38 | 40 | class QuirkArithmeticOperation(ops.ArithmeticOperation): |
39 | 41 | """Applies arithmetic to a target and some inputs. |
@@ -148,6 +150,110 @@ def __repr__(self) -> str: |
148 | 150 | ) |
149 | 151 |
|
150 | 152 |
|
| 153 | +@value.value_equality |
| 154 | +class QuirkArithmeticGate(ops.ArithmeticGate): |
| 155 | + """Applies arithmetic to a target and some inputs. |
| 156 | +
|
| 157 | + Implements Quirk-specific implicit effects like assuming that the presence |
| 158 | + of an 'r' input implies modular arithmetic. |
| 159 | +
|
| 160 | + In Quirk, modular operations have no effect on values larger than the |
| 161 | + modulus. This convention is used because unitarity forces *some* convention |
| 162 | + on out-of-range values (they cannot simply disappear or raise exceptions), |
| 163 | + and the simplest is to do nothing. This call handles ensuring that happens, |
| 164 | + and ensuring the new target register value is normalized modulo the modulus. |
| 165 | + """ |
| 166 | + |
| 167 | + def __init__( |
| 168 | + self, identifier: str, target: Sequence[int], inputs: Sequence[Union[Sequence[int], int]] |
| 169 | + ): |
| 170 | + """Inits QuirkArithmeticGate. |
| 171 | +
|
| 172 | + Args: |
| 173 | + identifier: The quirk identifier string for this operation. |
| 174 | + target: The target qubit register. |
| 175 | + inputs: Qubit registers, which correspond to the qid shape of the |
| 176 | + qubits from which the input will be read, or classical |
| 177 | + constants, that determine what happens to the target. |
| 178 | +
|
| 179 | + Raises: |
| 180 | + ValueError: If the target is too small for a modular operation with |
| 181 | + too small modulus. |
| 182 | + """ |
| 183 | + self.identifier = identifier |
| 184 | + self.target: Tuple[int, ...] = tuple(target) |
| 185 | + self.inputs: Tuple[Union[Sequence[int], int], ...] = tuple( |
| 186 | + e if isinstance(e, int) else tuple(e) for e in inputs |
| 187 | + ) |
| 188 | + |
| 189 | + if self.operation.is_modular: |
| 190 | + r = inputs[-1] |
| 191 | + if isinstance(r, int): |
| 192 | + over = r > 1 << len(target) |
| 193 | + else: |
| 194 | + over = len(cast(Sequence, r)) > len(target) |
| 195 | + if over: |
| 196 | + raise ValueError(f'Target too small for modulus.\nTarget: {target}\nModulus: {r}') |
| 197 | + |
| 198 | + @property |
| 199 | + def operation(self) -> '_QuirkArithmeticCallable': |
| 200 | + return ARITHMETIC_OP_TABLE[self.identifier] |
| 201 | + |
| 202 | + def _value_equality_values_(self) -> Any: |
| 203 | + return self.identifier, self.target, self.inputs |
| 204 | + |
| 205 | + def registers(self) -> Sequence[Union[int, Sequence[int]]]: |
| 206 | + return [self.target, *self.inputs] |
| 207 | + |
| 208 | + def with_registers(self, *new_registers: Union[int, Sequence[int]]) -> 'QuirkArithmeticGate': |
| 209 | + if len(new_registers) != len(self.inputs) + 1: |
| 210 | + raise ValueError( |
| 211 | + 'Wrong number of registers.\n' |
| 212 | + f'New registers: {repr(new_registers)}\n' |
| 213 | + f'Operation: {repr(self)}' |
| 214 | + ) |
| 215 | + |
| 216 | + if isinstance(new_registers[0], int): |
| 217 | + raise ValueError( |
| 218 | + 'The first register is the mutable target. ' |
| 219 | + 'It must be a list of qubits, not the constant ' |
| 220 | + f'{new_registers[0]}.' |
| 221 | + ) |
| 222 | + |
| 223 | + return QuirkArithmeticGate(self.identifier, new_registers[0], new_registers[1:]) |
| 224 | + |
| 225 | + def apply(self, *registers: int) -> Union[int, Iterable[int]]: |
| 226 | + return self.operation(*registers) |
| 227 | + |
| 228 | + def _circuit_diagram_info_(self, args: 'cirq.CircuitDiagramInfoArgs') -> List[str]: |
| 229 | + lettered_args = list(zip(self.operation.letters, self.inputs)) |
| 230 | + |
| 231 | + result: List[str] = [] |
| 232 | + |
| 233 | + # Target register labels. |
| 234 | + consts = ''.join( |
| 235 | + f',{letter}={reg}' for letter, reg in lettered_args if isinstance(reg, int) |
| 236 | + ) |
| 237 | + result.append(f'Quirk({self.identifier}{consts})') |
| 238 | + result.extend(f'#{i}' for i in range(2, len(self.target) + 1)) |
| 239 | + |
| 240 | + # Input register labels. |
| 241 | + for letter, reg in lettered_args: |
| 242 | + if not isinstance(reg, int): |
| 243 | + result.extend(f'{letter.upper()}{i}' for i in range(len(cast(Sequence, reg)))) |
| 244 | + |
| 245 | + return result |
| 246 | + |
| 247 | + def __repr__(self) -> str: |
| 248 | + return ( |
| 249 | + 'cirq.interop.quirk.QuirkArithmeticGate(\n' |
| 250 | + f' {repr(self.identifier)},\n' |
| 251 | + f' target={repr(self.target)},\n' |
| 252 | + f' inputs={_indented_list_lines_repr(self.inputs)},\n' |
| 253 | + ')' |
| 254 | + ) |
| 255 | + |
| 256 | + |
151 | 257 | _IntsToIntCallable = Union[ |
152 | 258 | Callable[[int], int], |
153 | 259 | Callable[[int, int], int], |
@@ -244,11 +350,13 @@ def operations(self) -> 'cirq.OP_TREE': |
244 | 350 | if missing_inputs: |
245 | 351 | raise ValueError(f'Missing input: {sorted(missing_inputs)}') |
246 | 352 |
|
247 | | - return QuirkArithmeticOperation( |
| 353 | + inputs = cast(Sequence[Union[Sequence['cirq.Qid'], int]], self.inputs) |
| 354 | + qubits = self.target + tuple(q for i in self.inputs if isinstance(i, Sequence) for q in i) |
| 355 | + return QuirkArithmeticGate( |
248 | 356 | self.identifier, |
249 | | - self.target, |
250 | | - cast(Sequence[Union[Sequence['cirq.Qid'], int]], self.inputs), |
251 | | - ) |
| 357 | + [q.dimension for q in self.target], |
| 358 | + [i if isinstance(i, int) else [q.dimension for q in i] for i in inputs], |
| 359 | + ).on(*qubits) |
252 | 360 |
|
253 | 361 |
|
254 | 362 | def _indented_list_lines_repr(items: Sequence[Any]) -> str: |
|
0 commit comments