From 3b3a59bf727f4f31ad6b4057877e5b747c200a24 Mon Sep 17 00:00:00 2001 From: daxfohl Date: Tue, 23 Nov 2021 10:56:55 -0800 Subject: [PATCH 01/19] Implement ArithmeticGate --- cirq-core/cirq/__init__.py | 1 + cirq-core/cirq/ops/__init__.py | 1 + cirq-core/cirq/ops/arithmetic_operation.py | 217 +++++++++++++++++- .../cirq/ops/arithmetic_operation_test.py | 68 ++++++ 4 files changed, 282 insertions(+), 5 deletions(-) diff --git a/cirq-core/cirq/__init__.py b/cirq-core/cirq/__init__.py index dd66f312590..09f2ee0c03b 100644 --- a/cirq-core/cirq/__init__.py +++ b/cirq-core/cirq/__init__.py @@ -177,6 +177,7 @@ AmplitudeDampingChannel, AnyIntegerPowerGateFamily, AnyUnitaryGateFamily, + ArithmeticGate, ArithmeticOperation, asymmetric_depolarize, AsymmetricDepolarizingChannel, diff --git a/cirq-core/cirq/ops/__init__.py b/cirq-core/cirq/ops/__init__.py index 0b4041e5641..5e3483eac6b 100644 --- a/cirq-core/cirq/ops/__init__.py +++ b/cirq-core/cirq/ops/__init__.py @@ -15,6 +15,7 @@ """ from cirq.ops.arithmetic_operation import ( + ArithmeticGate, ArithmeticOperation, ) diff --git a/cirq-core/cirq/ops/arithmetic_operation.py b/cirq-core/cirq/ops/arithmetic_operation.py index 67459136c43..218f8fcaf9a 100644 --- a/cirq-core/cirq/ops/arithmetic_operation.py +++ b/cirq-core/cirq/ops/arithmetic_operation.py @@ -15,20 +15,20 @@ import abc import itertools -from typing import Union, Iterable, List, Sequence, cast, TypeVar, TYPE_CHECKING +from typing import Union, Iterable, List, Sequence, cast, TypeVar, TYPE_CHECKING, Tuple import numpy as np - -from cirq.ops.raw_types import Operation +from cirq.ops import gate_operation, raw_types if TYPE_CHECKING: import cirq TSelf = TypeVar('TSelf', bound='ArithmeticOperation') +TSelfGate = TypeVar('TSelfGate', bound='ArithmeticGate') -class ArithmeticOperation(Operation, metaclass=abc.ABCMeta): +class ArithmeticOperation(raw_types.Operation, metaclass=abc.ABCMeta): """A helper class for implementing reversible classical arithmetic. Child classes must override the `registers`, `with_registers`, and `apply` @@ -241,8 +241,215 @@ def _apply_unitary_(self, args: 'cirq.ApplyUnitaryArgs'): return args.target_tensor +class ArithmeticGate(raw_types.Gate, metaclass=abc.ABCMeta): + """A helper gate for implementing reversible classical arithmetic. + + Child classes must override the `registers`, `with_registers`, and `apply` + methods. + + This class handles the details of ensuring that the scaling of implementing + the operation is O(2^n) instead of O(4^n) where n is the number of qubits + being acted on, by implementing an `_apply_unitary_` function in terms of + the registers and the apply function of the child class. It also handles the + boilerplate of implementing the `qubits` and `with_qubits` methods. + + Examples: + ``` + + >>> class Add(cirq.ArithmeticOperation): + ... def __init__(self, target_register, input_register): + ... self.target_register = target_register + ... self.input_register = input_register + ... + ... def registers(self): + ... return self.target_register, self.input_register + ... + ... def with_registers(self, *new_registers): + ... return Add(*new_registers) + ... + ... def apply(self, target_value, input_value): + ... return target_value + input_value + >>> cirq.unitary( + ... Add(target_register=cirq.LineQubit.range(2), + ... input_register=1) + ... ).astype(np.int32) + array([[0, 0, 0, 1], + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0]], dtype=int32) + >>> c = cirq.Circuit( + ... cirq.X(cirq.LineQubit(3)), + ... cirq.X(cirq.LineQubit(2)), + ... cirq.X(cirq.LineQubit(6)), + ... cirq.measure(*cirq.LineQubit.range(4, 8), key='before:in'), + ... cirq.measure(*cirq.LineQubit.range(4), key='before:out'), + ... + ... Add(target_register=cirq.LineQubit.range(4), + ... input_register=cirq.LineQubit.range(4, 8)), + ... + ... cirq.measure(*cirq.LineQubit.range(4, 8), key='after:in'), + ... cirq.measure(*cirq.LineQubit.range(4), key='after:out'), + ... ) + >>> cirq.sample(c).data + before:in before:out after:in after:out + 0 2 3 2 5 + + ``` + """ + + @abc.abstractmethod + def registers(self) -> Sequence[Union[int, Sequence[int]]]: + """The data acted upon by the arithmetic operation. + + Each register in the list can either be a classical constant (an `int`), + or else a list of qubits/qudits (a `List[cirq.Qid]`). Registers that + are set to a classical constant must not be mutated by the arithmetic + operation (their value must remain fixed when passed to `apply`). + + Registers are big endian. The first qubit is the most significant, the + last qubit is the 1s qubit, the before last qubit is the 2s qubit, etc. + + Returns: + A list of constants and qubit groups that the operation will act + upon. + """ + raise NotImplementedError() + + @abc.abstractmethod + def with_registers(self: TSelfGate, *new_registers: Union[int, Sequence[int]]) -> TSelfGate: + """Returns the same operation targeting different registers. + + Args: + new_registers: The new values that should be returned by the + `registers` method. + + Returns: + An instance of the same kind of operation, but acting on different + registers. + """ + raise NotImplementedError() + + @abc.abstractmethod + def apply(self, *register_values: int) -> Union[int, Iterable[int]]: + """Returns the result of the operation operating on classical values. + + For example, an addition takes two values (the target and the source), + adds the source into the target, then returns the target and source + as the new register values. + + The `apply` method is permitted to be sloppy in three ways: + + 1. The `apply` method is permitted to return values that have more bits + than the registers they will be stored into. The extra bits are + simply dropped. For example, if the value 5 is returned for a 2 + qubit register then 5 % 2**2 = 1 will be used instead. Negative + values are also permitted. For example, for a 3 qubit register the + value -2 becomes -2 % 2**3 = 6. + 2. When the value of the last `k` registers is not changed by the + operation, the `apply` method is permitted to omit these values + from the result. That is to say, when the length of the output is + less than the length of the input, it is padded up to the intended + length by copying from the same position in the input. + 3. When only the first register's value changes, the `apply` method is + permitted to return an `int` instead of a sequence of ints. + + The `apply` method *must* be reversible. Otherwise the operation will + not be unitary, and incorrect behavior will result. + + Examples: + + A fully detailed adder: + + ``` + def apply(self, target, offset): + return (target + offset) % 2**len(self.target_register), offset + ``` + + The same adder, with less boilerplate due to the details being + handled by the `ArithmeticOperation` class: + + ``` + def apply(self, target, offset): + return target + offset + ``` + """ + raise NotImplementedError() + + def _qid_shape_(self) -> Tuple[int, ...]: + shape = [] + for r in self.registers(): + if isinstance(r, Sequence): + for i in r: + shape.append(i) + return tuple(shape) + + def on(self, *qubits) -> 'cirq.Operation': + return gate_operation.GateOperation(self, qubits) + + def _apply_unitary_(self, args: 'cirq.ApplyUnitaryArgs'): + registers = self.registers() + input_ranges: List[Sequence[int]] = [] + shape = [] + overflow_sizes = [] + for register in registers: + if isinstance(register, int): + input_ranges.append([register]) + shape.append(1) + overflow_sizes.append(register + 1) + else: + size = int(np.prod([dim for dim in register], dtype=np.int64).item()) + shape.append(size) + input_ranges.append(range(size)) + overflow_sizes.append(size) + + leftover = args.target_tensor.size // np.prod(shape, dtype=np.int64).item() + new_shape = (*shape, leftover) + + transposed_args = args.with_axes_transposed_to_start() + src = transposed_args.target_tensor.reshape(new_shape) + dst = transposed_args.available_buffer.reshape(new_shape) + for input_seq in itertools.product(*input_ranges): + output = self.apply(*input_seq) + + # Wrap into list. + inputs: List[int] = list(input_seq) + outputs: List[int] = [output] if isinstance(output, int) else list(output) + + # Omitted tail values default to the corresponding input value. + if len(outputs) < len(inputs): + outputs += inputs[len(outputs) - len(inputs) :] + # Get indices into range. + for i in range(len(outputs)): + if isinstance(registers[i], int): + if outputs[i] != registers[i]: + raise ValueError( + _describe_bad_arithmetic_changed_const( + self.registers(), inputs, outputs + ) + ) + # Classical constants go to zero on a unit axe. + outputs[i] = 0 + inputs[i] = 0 + else: + # Quantum values get wrapped into range. + outputs[i] %= overflow_sizes[i] + + # Copy amplitude to new location. + cast(List[Union[int, slice]], outputs).append(slice(None)) + cast(List[Union[int, slice]], inputs).append(slice(None)) + dst[tuple(outputs)] = src[tuple(inputs)] + + # In case the reshaped arrays were copies instead of views. + dst.shape = transposed_args.available_buffer.shape + transposed_args.target_tensor[...] = dst + + return args.target_tensor + + def _describe_bad_arithmetic_changed_const( - registers: Sequence[Union[int, Sequence['cirq.Qid']]], inputs: List[int], outputs: List[int] + registers: Sequence[Union[int, Sequence[Union['cirq.Qid', int]]]], + inputs: List[int], + outputs: List[int], ) -> str: from cirq.circuits import TextDiagramDrawer diff --git a/cirq-core/cirq/ops/arithmetic_operation_test.py b/cirq-core/cirq/ops/arithmetic_operation_test.py index ad394d13286..5ea73766077 100644 --- a/cirq-core/cirq/ops/arithmetic_operation_test.py +++ b/cirq-core/cirq/ops/arithmetic_operation_test.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import Union, Sequence + import pytest import numpy as np @@ -121,6 +123,72 @@ def apply(self, target_value, input_value): ) +def test_arithmetic_gate_apply_unitary(): + class Add(cirq.ArithmeticGate): + def __init__( + self, + target_register: Union[int, Sequence[int]], + input_register: Union[int, Sequence[int]], + ): + self.target_register = target_register + self.input_register = input_register + + def registers(self): + return self.target_register, self.input_register + + def with_registers(self, *new_registers): + raise NotImplementedError() + + def apply(self, target_value, input_value): + return target_value + input_value + + qubits = [cirq.LineQubit.range(i) for i in range(6)] + + inc2 = Add([2, 2], 1) + np.testing.assert_allclose(cirq.unitary(inc2), shift_matrix(4, 1), atol=1e-8) + np.testing.assert_allclose(cirq.unitary(inc2.on(*qubits[2])), shift_matrix(4, 1), atol=1e-8) + + dec3 = Add([2, 2, 2], -1) + np.testing.assert_allclose(cirq.unitary(dec3), shift_matrix(8, -1), atol=1e-8) + np.testing.assert_allclose(cirq.unitary(dec3.on(*qubits[3])), shift_matrix(8, -1), atol=1e-8) + + add3from2 = Add([2, 2, 2], [2, 2]) + np.testing.assert_allclose(cirq.unitary(add3from2), adder_matrix(8, 4), atol=1e-8) + np.testing.assert_allclose( + cirq.unitary(add3from2.on(*qubits[5])), adder_matrix(8, 4), atol=1e-8 + ) + + add2from3 = Add([2, 2], [2, 2, 2]) + np.testing.assert_allclose(cirq.unitary(add2from3), adder_matrix(4, 8), atol=1e-8) + np.testing.assert_allclose( + cirq.unitary(add2from3.on(*qubits[5])), adder_matrix(4, 8), atol=1e-8 + ) + + with pytest.raises(ValueError, match='affected by the operation'): + _ = cirq.unitary(Add(1, [2, 2])) + + with pytest.raises(ValueError, match='affected by the operation'): + _ = cirq.unitary(Add(1, [2, 2]).on(*qubits[2])) + + with pytest.raises(ValueError, match='affected by the operation'): + _ = cirq.unitary(Add(1, 1)) + + with pytest.raises(ValueError, match='affected by the operation'): + _ = cirq.unitary(Add(1, 1).on()) + + np.testing.assert_allclose(cirq.unitary(Add(1, 0)), np.eye(1)) + np.testing.assert_allclose(cirq.unitary(Add(1, 0).on()), np.eye(1)) + + cirq.testing.assert_has_consistent_apply_unitary(Add([2, 2], [2, 2])) + cirq.testing.assert_has_consistent_apply_unitary(Add([2, 2], [2, 2]).on(*qubits[4])) + + with pytest.raises(ValueError, match='Wrong number of qubits'): + _ = Add(1, [2, 2]).on(*qubits[3]) + + with pytest.raises(ValueError, match='Wrong shape of qids'): + _ = Add(1, [2, 3]).on(*qubits[2]) + + def test_arithmetic_operation_qubits(): class Three(cirq.ArithmeticOperation): def __init__(self, a, b, c): From a902872a1dbfc1e7f75fbc9c87cf704f84d80624 Mon Sep 17 00:00:00 2001 From: daxfohl Date: Tue, 23 Nov 2021 11:09:30 -0800 Subject: [PATCH 02/19] simplify --- cirq-core/cirq/ops/arithmetic_operation.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/cirq-core/cirq/ops/arithmetic_operation.py b/cirq-core/cirq/ops/arithmetic_operation.py index 218f8fcaf9a..0813099c388 100644 --- a/cirq-core/cirq/ops/arithmetic_operation.py +++ b/cirq-core/cirq/ops/arithmetic_operation.py @@ -15,20 +15,21 @@ import abc import itertools -from typing import Union, Iterable, List, Sequence, cast, TypeVar, TYPE_CHECKING, Tuple +from typing import Union, Iterable, List, Sequence, cast, TypeVar, TYPE_CHECKING import numpy as np -from cirq.ops import gate_operation, raw_types + +from cirq.ops.raw_types import Operation, Gate +from cirq.ops.gate_operation import GateOperation if TYPE_CHECKING: import cirq TSelf = TypeVar('TSelf', bound='ArithmeticOperation') -TSelfGate = TypeVar('TSelfGate', bound='ArithmeticGate') -class ArithmeticOperation(raw_types.Operation, metaclass=abc.ABCMeta): +class ArithmeticOperation(Operation, metaclass=abc.ABCMeta): """A helper class for implementing reversible classical arithmetic. Child classes must override the `registers`, `with_registers`, and `apply` @@ -241,7 +242,10 @@ def _apply_unitary_(self, args: 'cirq.ApplyUnitaryArgs'): return args.target_tensor -class ArithmeticGate(raw_types.Gate, metaclass=abc.ABCMeta): +TSelfGate = TypeVar('TSelfGate', bound='ArithmeticGate') + + +class ArithmeticGate(Gate, metaclass=abc.ABCMeta): """A helper gate for implementing reversible classical arithmetic. Child classes must override the `registers`, `with_registers`, and `apply` @@ -375,7 +379,7 @@ def apply(self, target, offset): """ raise NotImplementedError() - def _qid_shape_(self) -> Tuple[int, ...]: + def _qid_shape_(self): shape = [] for r in self.registers(): if isinstance(r, Sequence): @@ -384,7 +388,7 @@ def _qid_shape_(self) -> Tuple[int, ...]: return tuple(shape) def on(self, *qubits) -> 'cirq.Operation': - return gate_operation.GateOperation(self, qubits) + return GateOperation(self, qubits) def _apply_unitary_(self, args: 'cirq.ApplyUnitaryArgs'): registers = self.registers() From bcf08da47a3d5f6ddebfa24cdf610fe1d38ccf5d Mon Sep 17 00:00:00 2001 From: daxfohl Date: Tue, 23 Nov 2021 11:33:48 -0800 Subject: [PATCH 03/19] docs --- cirq-core/cirq/ops/arithmetic_operation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cirq-core/cirq/ops/arithmetic_operation.py b/cirq-core/cirq/ops/arithmetic_operation.py index 0813099c388..a8065908561 100644 --- a/cirq-core/cirq/ops/arithmetic_operation.py +++ b/cirq-core/cirq/ops/arithmetic_operation.py @@ -306,9 +306,9 @@ def registers(self) -> Sequence[Union[int, Sequence[int]]]: """The data acted upon by the arithmetic operation. Each register in the list can either be a classical constant (an `int`), - or else a list of qubits/qudits (a `List[cirq.Qid]`). Registers that - are set to a classical constant must not be mutated by the arithmetic - operation (their value must remain fixed when passed to `apply`). + or else a list of qubit/qudit dimensions. Registers that are set to a + classical constant must not be mutated by the arithmetic operation + (their value must remain fixed when passed to `apply`). Registers are big endian. The first qubit is the most significant, the last qubit is the 1s qubit, the before last qubit is the 2s qubit, etc. From 57fb38fc287eb32789ebd3c431def0808f022867 Mon Sep 17 00:00:00 2001 From: daxfohl Date: Wed, 22 Dec 2021 18:22:21 -0800 Subject: [PATCH 04/19] shor --- examples/examples_test.py | 28 +++++++++++------------ examples/shor.py | 48 ++++++++++++++++----------------------- 2 files changed, 34 insertions(+), 42 deletions(-) diff --git a/examples/examples_test.py b/examples/examples_test.py index bfcff9d30f4..1c4ecad10ef 100644 --- a/examples/examples_test.py +++ b/examples/examples_test.py @@ -137,45 +137,45 @@ def test_example_noisy_simulation(): def test_example_shor_modular_exp_register_size(): with pytest.raises(ValueError): _ = examples.shor.ModularExp( - target=cirq.LineQubit.range(2), exponent=cirq.LineQubit.range(2, 5), base=4, modulus=5 + target=[2, 2], exponent=[2, 2, 2], base=4, modulus=5 ) def test_example_shor_modular_exp_register_type(): operation = examples.shor.ModularExp( - target=cirq.LineQubit.range(3), exponent=cirq.LineQubit.range(3, 5), base=4, modulus=5 + target=[2, 2, 2], exponent=[2, 2], base=4, modulus=5 ) with pytest.raises(ValueError): - _ = operation.with_registers(cirq.LineQubit.range(3)) + _ = operation.with_registers([2, 2, 2]) with pytest.raises(ValueError): - _ = operation.with_registers(1, cirq.LineQubit.range(3, 6), 4, 5) + _ = operation.with_registers(1, [2, 2, 2], 4, 5) with pytest.raises(ValueError): _ = operation.with_registers( - cirq.LineQubit.range(3), cirq.LineQubit.range(3, 6), cirq.LineQubit.range(6, 9), 5 + [2, 2, 2], [2, 2, 2], [2, 2, 2], 5 ) with pytest.raises(ValueError): _ = operation.with_registers( - cirq.LineQubit.range(3), cirq.LineQubit.range(3, 6), 4, cirq.LineQubit.range(6, 9) + [2, 2, 2], [2, 2, 2], 4, [2, 2, 2] ) def test_example_shor_modular_exp_registers(): - target = cirq.LineQubit.range(3) - exponent = cirq.LineQubit.range(3, 5) + target = [2, 2, 2] + exponent = [2, 2] operation = examples.shor.ModularExp(target, exponent, 4, 5) assert operation.registers() == (target, exponent, 4, 5) - new_target = cirq.LineQubit.range(5, 8) - new_exponent = cirq.LineQubit.range(8, 12) + new_target = [2, 2, 2] + new_exponent = [2, 2, 2, 2] new_operation = operation.with_registers(new_target, new_exponent, 6, 7) assert new_operation.registers() == (new_target, new_exponent, 6, 7) def test_example_shor_modular_exp_diagram(): - target = cirq.LineQubit.range(3) - exponent = cirq.LineQubit.range(3, 5) + target = [2, 2, 2] + exponent = [2, 2] operation = examples.shor.ModularExp(target, exponent, 4, 5) - circuit = cirq.Circuit(operation) + circuit = cirq.Circuit(operation.on(*cirq.LineQubit.range(5))) cirq.testing.assert_has_diagram( circuit, """ @@ -192,7 +192,7 @@ def test_example_shor_modular_exp_diagram(): ) operation = operation.with_registers(target, 2, 4, 5) - circuit = cirq.Circuit(operation) + circuit = cirq.Circuit(operation.on(*cirq.LineQubit.range(3))) cirq.testing.assert_has_diagram( circuit, """ diff --git a/examples/shor.py b/examples/shor.py index e0624242425..348f1fa460f 100644 --- a/examples/shor.py +++ b/examples/shor.py @@ -101,7 +101,7 @@ def naive_order_finder(x: int, n: int) -> Optional[int]: return r -class ModularExp(cirq.ArithmeticOperation): +class ModularExp(cirq.ArithmeticGate): """Quantum modular exponentiation. This class represents the unitary which multiplies base raised to exponent @@ -130,8 +130,8 @@ class ModularExp(cirq.ArithmeticOperation): def __init__( self, - target: Sequence[cirq.Qid], - exponent: Union[int, Sequence[cirq.Qid]], + target: Sequence[int], + exponent: Union[int, Sequence[int]], base: int, modulus: int, ) -> None: @@ -144,12 +144,12 @@ def __init__( self.base = base self.modulus = modulus - def registers(self) -> Sequence[Union[int, Sequence[cirq.Qid]]]: + def registers(self) -> Sequence[Union[int, Sequence[int]]]: return self.target, self.exponent, self.base, self.modulus def with_registers( self, - *new_registers: Union[int, Sequence['cirq.Qid']], + *new_registers: Union[int, Sequence[int]], ) -> 'ModularExp': if len(new_registers) != 4: raise ValueError( @@ -177,22 +177,12 @@ def _circuit_diagram_info_( args: cirq.CircuitDiagramInfoArgs, ) -> cirq.CircuitDiagramInfo: assert args.known_qubits is not None - wire_symbols: List[str] = [] - t, e = 0, 0 - for qubit in args.known_qubits: - if qubit in self.target: - if t == 0: - if isinstance(self.exponent, Sequence): - e_str = 'e' - else: - e_str = str(self.exponent) - wire_symbols.append(f'ModularExp(t*{self.base}**{e_str} % {self.modulus})') - else: - wire_symbols.append('t' + str(t)) - t += 1 - if isinstance(self.exponent, Sequence) and qubit in self.exponent: - wire_symbols.append('e' + str(e)) - e += 1 + wire_symbols = [f't{i}' for i in range(len(self.target))] + e_str = str(self.exponent) + if isinstance(self.exponent, Sequence): + e_str = 'e' + wire_symbols += [f'e{i}' for i in range(len(self.exponent))] + wire_symbols[0] = f'ModularExp(t*{self.base}**{e_str} % {self.modulus})' return cirq.CircuitDiagramInfo(wire_symbols=tuple(wire_symbols)) @@ -225,14 +215,16 @@ def make_order_finding_circuit(x: int, n: int) -> cirq.Circuit: Quantum circuit for finding the order of x modulo n """ L = n.bit_length() - target = cirq.LineQubit.range(L) - exponent = cirq.LineQubit.range(L, 3 * L + 3) + target = (2,) * L + target_q = cirq.LineQubit.range(L) + exponent = (2,) * (2 * L + 3) + exponent_q = cirq.LineQubit.range(L, 3 * L + 3) return cirq.Circuit( - cirq.X(target[L - 1]), - cirq.H.on_each(*exponent), - ModularExp(target, exponent, x, n), - cirq.qft(*exponent, inverse=True), - cirq.measure(*exponent, key='exponent'), + cirq.X(target_q[L - 1]), + cirq.H.on_each(*exponent_q), + ModularExp(target, exponent, x, n).on(*(target_q + exponent_q)), + cirq.qft(*exponent_q, inverse=True), + cirq.measure(*exponent_q, key='exponent'), ) From aa3a226b272bac02e8b8847ee9c40a889a07fd68 Mon Sep 17 00:00:00 2001 From: daxfohl Date: Wed, 22 Dec 2021 19:47:10 -0800 Subject: [PATCH 05/19] quirk --- .../interop/quirk/cells/arithmetic_cells.py | 32 ++++++++----------- .../quirk/cells/arithmetic_cells_test.py | 8 ++--- .../quirk/cells/composite_cell_test.py | 10 +++--- .../interop/quirk/cells/input_cells_test.py | 4 +-- 4 files changed, 25 insertions(+), 29 deletions(-) diff --git a/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py b/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py index fd421fb20f2..68a9606df8d 100644 --- a/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py +++ b/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py @@ -35,7 +35,7 @@ @value.value_equality -class QuirkArithmeticOperation(ops.ArithmeticOperation): +class QuirkArithmeticOperation(ops.ArithmeticGate): """Applies arithmetic to a target and some inputs. Implements Quirk-specific implicit effects like assuming that the presence @@ -51,8 +51,8 @@ class QuirkArithmeticOperation(ops.ArithmeticOperation): def __init__( self, identifier: str, - target: Sequence['cirq.Qid'], - inputs: Sequence[Union[Sequence['cirq.Qid'], int]], + target: Sequence[int], + inputs: Sequence[Union[Sequence[int], int]], ): """Inits QuirkArithmeticOperation. @@ -63,21 +63,15 @@ def __init__( determine what happens to the target. Raises: - ValueError: If given overlapping registers, or the target is too - small for a modular operation with too small modulus. + ValueError: If the target is too small for a modular operation with + too small modulus. """ self.identifier = identifier - self.target: Tuple['cirq.Qid', ...] = tuple(target) - self.inputs: Tuple[Union[Sequence['cirq.Qid'], int], ...] = tuple( + self.target: Tuple[int, ...] = tuple(target) + self.inputs: Tuple[Union[Sequence[int], int], ...] = tuple( e if isinstance(e, int) else tuple(e) for e in inputs ) - for input_register in self.inputs: - if isinstance(input_register, int): - continue - if set(self.target) & set(input_register): - raise ValueError(f'Overlapping registers: {self.target} {self.inputs}') - if self.operation.is_modular: r = inputs[-1] if isinstance(r, int): @@ -94,11 +88,11 @@ def operation(self) -> '_QuirkArithmeticCallable': def _value_equality_values_(self) -> Any: return self.identifier, self.target, self.inputs - def registers(self) -> Sequence[Union[int, Sequence['cirq.Qid']]]: + def registers(self) -> Sequence[Union[int, Sequence[int]]]: return [self.target, *self.inputs] def with_registers( - self, *new_registers: Union[int, Sequence['cirq.Qid']] + self, *new_registers: Union[int, Sequence[int]] ) -> 'QuirkArithmeticOperation': if len(new_registers) != len(self.inputs) + 1: raise ValueError( @@ -244,11 +238,13 @@ def operations(self) -> 'cirq.OP_TREE': if missing_inputs: raise ValueError(f'Missing input: {sorted(missing_inputs)}') + inputs = [i if isinstance(i, int) else [q.dimension for q in i] for i in self.inputs] + qubits = self.target + tuple(q for i in self.inputs if isinstance(i, Sequence) for q in i) return QuirkArithmeticOperation( self.identifier, - self.target, - cast(Sequence[Union[Sequence['cirq.Qid'], int]], self.inputs), - ) + [q.dimension for q in self.target], + inputs, + ).on(*qubits) def _indented_list_lines_repr(items: Sequence[Any]) -> str: diff --git a/cirq-core/cirq/interop/quirk/cells/arithmetic_cells_test.py b/cirq-core/cirq/interop/quirk/cells/arithmetic_cells_test.py index 12dd482bd20..879024acfd7 100644 --- a/cirq-core/cirq/interop/quirk/cells/arithmetic_cells_test.py +++ b/cirq-core/cirq/interop/quirk/cells/arithmetic_cells_test.py @@ -545,7 +545,7 @@ def test_with_registers(): '["+=AB3",1,1,"inputB2"]' ']}' ) - op = cast(cirq.ArithmeticOperation, circuit[0].operations[0]) + op = cast(cirq.ArithmeticGate, circuit[0].operations[0].gate) with pytest.raises(ValueError, match='number of registers'): _ = op.with_registers() @@ -554,11 +554,11 @@ def test_with_registers(): _ = op.with_registers(1, 2, 3) op2 = op.with_registers([], 5, 5) - np.testing.assert_allclose(cirq.unitary(cirq.Circuit(op2)), np.array([[1]]), atol=1e-8) + np.testing.assert_allclose(cirq.unitary(cirq.Circuit(op2())), np.array([[1]]), atol=1e-8) - op2 = op.with_registers([*cirq.LineQubit.range(3)], 5, 5) + op2 = op.with_registers([2, 2, 2], 5, 5) np.testing.assert_allclose( - cirq.final_state_vector(cirq.Circuit(op2), initial_state=0), + cirq.final_state_vector(cirq.Circuit(op2(*cirq.LineQubit.range(3))), initial_state=0), cirq.one_hot(index=25 % 8, shape=8, dtype=np.complex64), atol=1e-8, ) diff --git a/cirq-core/cirq/interop/quirk/cells/composite_cell_test.py b/cirq-core/cirq/interop/quirk/cells/composite_cell_test.py index 58b249d1630..00044f84589 100644 --- a/cirq-core/cirq/interop/quirk/cells/composite_cell_test.py +++ b/cirq-core/cirq/interop/quirk/cells/composite_cell_test.py @@ -100,13 +100,13 @@ def test_custom_circuit_gate(): # With internal input. assert_url_to_circuit_returns( '{"cols":[["~a5ls"]],"gates":[{"id":"~a5ls","circuit":{"cols":[["inputA1","+=A1"]]}}]}', - cirq.Circuit(cirq.interop.quirk.QuirkArithmeticOperation('+=A1', target=[b], inputs=[[a]])), + cirq.Circuit(cirq.interop.quirk.QuirkArithmeticOperation('+=A1', target=[2], inputs=[[2]]).on(b, a)), ) # With external input. assert_url_to_circuit_returns( '{"cols":[["inputA1","~r79k"]],"gates":[{"id":"~r79k","circuit":{"cols":[["+=A1"]]}}]}', - cirq.Circuit(cirq.interop.quirk.QuirkArithmeticOperation('+=A1', target=[b], inputs=[[a]])), + cirq.Circuit(cirq.interop.quirk.QuirkArithmeticOperation('+=A1', target=[2], inputs=[[2]]).on(b, a)), ) # With external control. @@ -135,9 +135,9 @@ def test_custom_circuit_gate(): '{"cols":[["~q1fh",1,1,"inputA2"]],"gates":[{"id":"~q1fh",' '"circuit":{"cols":[["+=A2"],[1,"+=A2"],[1,"+=A2"]]}}]}', cirq.Circuit( - cirq.interop.quirk.QuirkArithmeticOperation('+=A2', target=[a, b], inputs=[[d, e]]), - cirq.interop.quirk.QuirkArithmeticOperation('+=A2', target=[b, c], inputs=[[d, e]]), - cirq.interop.quirk.QuirkArithmeticOperation('+=A2', target=[b, c], inputs=[[d, e]]), + cirq.interop.quirk.QuirkArithmeticOperation('+=A2', target=[2, 2], inputs=[[2, 2]]).on(a, b, d, e), + cirq.interop.quirk.QuirkArithmeticOperation('+=A2', target=[2, 2], inputs=[[2, 2]]).on(b, c, d, e), + cirq.interop.quirk.QuirkArithmeticOperation('+=A2', target=[2, 2], inputs=[[2, 2]]).on(b, c, d, e), ), ) diff --git a/cirq-core/cirq/interop/quirk/cells/input_cells_test.py b/cirq-core/cirq/interop/quirk/cells/input_cells_test.py index e54703ebbdd..483da55c71b 100644 --- a/cirq-core/cirq/interop/quirk/cells/input_cells_test.py +++ b/cirq-core/cirq/interop/quirk/cells/input_cells_test.py @@ -44,7 +44,7 @@ def test_input_cell(): ) # Overlaps with effect. - with pytest.raises(ValueError, match='Overlapping registers'): + with pytest.raises(ValueError, match='Duplicate qids'): _ = quirk_url_to_circuit( 'https://algassert.com/quirk#circuit={"cols":[["+=A3","inputA3"]]}' ) @@ -71,7 +71,7 @@ def test_reversed_input_cell(): ) # Overlaps with effect. - with pytest.raises(ValueError, match='Overlapping registers'): + with pytest.raises(ValueError, match='Duplicate qids'): _ = quirk_url_to_circuit( 'https://algassert.com/quirk#circuit={"cols":[["+=A3","revinputA3"]]}' ) From 12578a8270bdfadeead82e7a377a6c1c22051e2b Mon Sep 17 00:00:00 2001 From: daxfohl Date: Mon, 27 Dec 2021 05:18:02 -0800 Subject: [PATCH 06/19] tests --- cirq-core/cirq/ops/arithmetic_operation.py | 2 + .../cirq/ops/arithmetic_operation_test.py | 67 ++++++++++--------- 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/cirq-core/cirq/ops/arithmetic_operation.py b/cirq-core/cirq/ops/arithmetic_operation.py index a8065908561..deccdd970da 100644 --- a/cirq-core/cirq/ops/arithmetic_operation.py +++ b/cirq-core/cirq/ops/arithmetic_operation.py @@ -19,6 +19,7 @@ import numpy as np +from cirq._compat import deprecated_class from cirq.ops.raw_types import Operation, Gate from cirq.ops.gate_operation import GateOperation @@ -29,6 +30,7 @@ TSelf = TypeVar('TSelf', bound='ArithmeticOperation') +@deprecated_class(deadline='v0.16', fix='Use cirq.ArithmeticGate') class ArithmeticOperation(Operation, metaclass=abc.ABCMeta): """A helper class for implementing reversible classical arithmetic. diff --git a/cirq-core/cirq/ops/arithmetic_operation_test.py b/cirq-core/cirq/ops/arithmetic_operation_test.py index 5ea73766077..0d85b962557 100644 --- a/cirq-core/cirq/ops/arithmetic_operation_test.py +++ b/cirq-core/cirq/ops/arithmetic_operation_test.py @@ -98,29 +98,30 @@ def with_registers(self, *new_registers): def apply(self, target_value, input_value): return target_value + input_value - inc2 = Add(cirq.LineQubit.range(2), 1) - np.testing.assert_allclose(cirq.unitary(inc2), shift_matrix(4, 1), atol=1e-8) + with cirq.testing.assert_deprecated(deadline='v0.16', count=8): + inc2 = Add(cirq.LineQubit.range(2), 1) + np.testing.assert_allclose(cirq.unitary(inc2), shift_matrix(4, 1), atol=1e-8) - dec3 = Add(cirq.LineQubit.range(3), -1) - np.testing.assert_allclose(cirq.unitary(dec3), shift_matrix(8, -1), atol=1e-8) + dec3 = Add(cirq.LineQubit.range(3), -1) + np.testing.assert_allclose(cirq.unitary(dec3), shift_matrix(8, -1), atol=1e-8) - add3from2 = Add(cirq.LineQubit.range(3), cirq.LineQubit.range(2)) - np.testing.assert_allclose(cirq.unitary(add3from2), adder_matrix(8, 4), atol=1e-8) + add3from2 = Add(cirq.LineQubit.range(3), cirq.LineQubit.range(2)) + np.testing.assert_allclose(cirq.unitary(add3from2), adder_matrix(8, 4), atol=1e-8) - add2from3 = Add(cirq.LineQubit.range(2), cirq.LineQubit.range(3)) - np.testing.assert_allclose(cirq.unitary(add2from3), adder_matrix(4, 8), atol=1e-8) + add2from3 = Add(cirq.LineQubit.range(2), cirq.LineQubit.range(3)) + np.testing.assert_allclose(cirq.unitary(add2from3), adder_matrix(4, 8), atol=1e-8) - with pytest.raises(ValueError, match='affected by the operation'): - _ = cirq.unitary(Add(1, cirq.LineQubit.range(2))) + with pytest.raises(ValueError, match='affected by the operation'): + _ = cirq.unitary(Add(1, cirq.LineQubit.range(2))) - with pytest.raises(ValueError, match='affected by the operation'): - _ = cirq.unitary(Add(1, 1)) + with pytest.raises(ValueError, match='affected by the operation'): + _ = cirq.unitary(Add(1, 1)) - np.testing.assert_allclose(cirq.unitary(Add(1, 0)), np.eye(1)) + np.testing.assert_allclose(cirq.unitary(Add(1, 0)), np.eye(1)) - cirq.testing.assert_has_consistent_apply_unitary( - Add(cirq.LineQubit.range(2), cirq.LineQubit.range(2)) - ) + cirq.testing.assert_has_consistent_apply_unitary( + Add(cirq.LineQubit.range(2), cirq.LineQubit.range(2)) + ) def test_arithmetic_gate_apply_unitary(): @@ -205,22 +206,23 @@ def with_registers(self, *new_registers): def apply(self, target_value, input_value): raise NotImplementedError() - q0, q1, q2, q3, q4, q5 = cirq.LineQubit.range(6) - op = Three([q0], [], [q4, q5]) - assert op.qubits == (q0, q4, q5) - assert op.registers() == ([q0], [], [q4, q5]) + with cirq.testing.assert_deprecated(deadline='v0.16', count=4): + q0, q1, q2, q3, q4, q5 = cirq.LineQubit.range(6) + op = Three([q0], [], [q4, q5]) + assert op.qubits == (q0, q4, q5) + assert op.registers() == ([q0], [], [q4, q5]) - op2 = op.with_qubits(q2, q4, q1) - assert op2.qubits == (q2, q4, q1) - assert op2.registers() == ([q2], [], [q4, q1]) + op2 = op.with_qubits(q2, q4, q1) + assert op2.qubits == (q2, q4, q1) + assert op2.registers() == ([q2], [], [q4, q1]) - op3 = op.with_registers([q0, q1, q3], [q5], 1) - assert op3.qubits == (q0, q1, q3, q5) - assert op3.registers() == ([q0, q1, q3], [q5], 1) + op3 = op.with_registers([q0, q1, q3], [q5], 1) + assert op3.qubits == (q0, q1, q3, q5) + assert op3.registers() == ([q0, q1, q3], [q5], 1) - op4 = op3.with_qubits(q0, q1, q2, q3) - assert op4.registers() == ([q0, q1, q2], [q3], 1) - assert op4.qubits == (q0, q1, q2, q3) + op4 = op3.with_qubits(q0, q1, q2, q3) + assert op4.registers() == ([q0, q1, q2], [q3], 1) + assert op4.qubits == (q0, q1, q2, q3) def test_reshape_referencing(): @@ -234,6 +236,7 @@ def registers(self): def with_registers(self, *new_registers): raise NotImplementedError() - state = np.ones(4, dtype=np.complex64) / 2 - output = cirq.final_state_vector(cirq.Circuit(Op1()), initial_state=state) - np.testing.assert_allclose(state, output) + with cirq.testing.assert_deprecated(deadline='v0.16'): + state = np.ones(4, dtype=np.complex64) / 2 + output = cirq.final_state_vector(cirq.Circuit(Op1()), initial_state=state) + np.testing.assert_allclose(state, output) From bf24b3d907320259b5f237da3aff4abbc63897ec Mon Sep 17 00:00:00 2001 From: daxfohl Date: Mon, 27 Dec 2021 05:30:03 -0800 Subject: [PATCH 07/19] rename --- cirq-core/cirq/interop/quirk/__init__.py | 2 +- .../cirq/interop/quirk/cells/__init__.py | 2 +- .../interop/quirk/cells/arithmetic_cells.py | 12 +++++------ .../quirk/cells/composite_cell_test.py | 20 ++++++++++++++----- examples/examples_test.py | 16 ++++----------- 5 files changed, 27 insertions(+), 25 deletions(-) diff --git a/cirq-core/cirq/interop/quirk/__init__.py b/cirq-core/cirq/interop/quirk/__init__.py index 6b8fab56626..a93f2a8e44c 100644 --- a/cirq-core/cirq/interop/quirk/__init__.py +++ b/cirq-core/cirq/interop/quirk/__init__.py @@ -20,7 +20,7 @@ # Imports from cells are only to ensure operation reprs work correctly. from cirq.interop.quirk.cells import ( - QuirkArithmeticOperation, + QuirkArithmeticGate, QuirkInputRotationOperation, QuirkQubitPermutationGate, ) diff --git a/cirq-core/cirq/interop/quirk/cells/__init__.py b/cirq-core/cirq/interop/quirk/cells/__init__.py index bd10f94f820..a8e673ec785 100644 --- a/cirq-core/cirq/interop/quirk/cells/__init__.py +++ b/cirq-core/cirq/interop/quirk/cells/__init__.py @@ -33,7 +33,7 @@ ) from cirq.interop.quirk.cells.arithmetic_cells import ( - QuirkArithmeticOperation, + QuirkArithmeticGate, ) from cirq.interop.quirk.cells.input_rotation_cells import ( diff --git a/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py b/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py index 68a9606df8d..d85dced1604 100644 --- a/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py +++ b/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py @@ -35,7 +35,7 @@ @value.value_equality -class QuirkArithmeticOperation(ops.ArithmeticGate): +class QuirkArithmeticGate(ops.ArithmeticGate): """Applies arithmetic to a target and some inputs. Implements Quirk-specific implicit effects like assuming that the presence @@ -54,7 +54,7 @@ def __init__( target: Sequence[int], inputs: Sequence[Union[Sequence[int], int]], ): - """Inits QuirkArithmeticOperation. + """Inits QuirkArithmeticGate. Args: identifier: The quirk identifier string for this operation. @@ -93,7 +93,7 @@ def registers(self) -> Sequence[Union[int, Sequence[int]]]: def with_registers( self, *new_registers: Union[int, Sequence[int]] - ) -> 'QuirkArithmeticOperation': + ) -> 'QuirkArithmeticGate': if len(new_registers) != len(self.inputs) + 1: raise ValueError( 'Wrong number of registers.\n' @@ -108,7 +108,7 @@ def with_registers( f'{new_registers[0]}.' ) - return QuirkArithmeticOperation(self.identifier, new_registers[0], new_registers[1:]) + return QuirkArithmeticGate(self.identifier, new_registers[0], new_registers[1:]) def apply(self, *registers: int) -> Union[int, Iterable[int]]: return self.operation(*registers) @@ -134,7 +134,7 @@ def _circuit_diagram_info_(self, args: 'cirq.CircuitDiagramInfoArgs') -> List[st def __repr__(self) -> str: return ( - 'cirq.interop.quirk.QuirkArithmeticOperation(\n' + 'cirq.interop.quirk.QuirkArithmeticGate(\n' f' {repr(self.identifier)},\n' f' target={repr(self.target)},\n' f' inputs={_indented_list_lines_repr(self.inputs)},\n' @@ -240,7 +240,7 @@ def operations(self) -> 'cirq.OP_TREE': inputs = [i if isinstance(i, int) else [q.dimension for q in i] for i in self.inputs] qubits = self.target + tuple(q for i in self.inputs if isinstance(i, Sequence) for q in i) - return QuirkArithmeticOperation( + return QuirkArithmeticGate( self.identifier, [q.dimension for q in self.target], inputs, diff --git a/cirq-core/cirq/interop/quirk/cells/composite_cell_test.py b/cirq-core/cirq/interop/quirk/cells/composite_cell_test.py index 00044f84589..8fc46885b21 100644 --- a/cirq-core/cirq/interop/quirk/cells/composite_cell_test.py +++ b/cirq-core/cirq/interop/quirk/cells/composite_cell_test.py @@ -100,13 +100,17 @@ def test_custom_circuit_gate(): # With internal input. assert_url_to_circuit_returns( '{"cols":[["~a5ls"]],"gates":[{"id":"~a5ls","circuit":{"cols":[["inputA1","+=A1"]]}}]}', - cirq.Circuit(cirq.interop.quirk.QuirkArithmeticOperation('+=A1', target=[2], inputs=[[2]]).on(b, a)), + cirq.Circuit( + cirq.interop.quirk.QuirkArithmeticGate('+=A1', target=[2], inputs=[[2]]).on(b, a) + ), ) # With external input. assert_url_to_circuit_returns( '{"cols":[["inputA1","~r79k"]],"gates":[{"id":"~r79k","circuit":{"cols":[["+=A1"]]}}]}', - cirq.Circuit(cirq.interop.quirk.QuirkArithmeticOperation('+=A1', target=[2], inputs=[[2]]).on(b, a)), + cirq.Circuit( + cirq.interop.quirk.QuirkArithmeticGate('+=A1', target=[2], inputs=[[2]]).on(b, a) + ), ) # With external control. @@ -135,9 +139,15 @@ def test_custom_circuit_gate(): '{"cols":[["~q1fh",1,1,"inputA2"]],"gates":[{"id":"~q1fh",' '"circuit":{"cols":[["+=A2"],[1,"+=A2"],[1,"+=A2"]]}}]}', cirq.Circuit( - cirq.interop.quirk.QuirkArithmeticOperation('+=A2', target=[2, 2], inputs=[[2, 2]]).on(a, b, d, e), - cirq.interop.quirk.QuirkArithmeticOperation('+=A2', target=[2, 2], inputs=[[2, 2]]).on(b, c, d, e), - cirq.interop.quirk.QuirkArithmeticOperation('+=A2', target=[2, 2], inputs=[[2, 2]]).on(b, c, d, e), + cirq.interop.quirk.QuirkArithmeticGate('+=A2', target=[2, 2], inputs=[[2, 2]]).on( + a, b, d, e + ), + cirq.interop.quirk.QuirkArithmeticGate('+=A2', target=[2, 2], inputs=[[2, 2]]).on( + b, c, d, e + ), + cirq.interop.quirk.QuirkArithmeticGate('+=A2', target=[2, 2], inputs=[[2, 2]]).on( + b, c, d, e + ), ), ) diff --git a/examples/examples_test.py b/examples/examples_test.py index 1c4ecad10ef..c5406ddc93f 100644 --- a/examples/examples_test.py +++ b/examples/examples_test.py @@ -136,27 +136,19 @@ def test_example_noisy_simulation(): def test_example_shor_modular_exp_register_size(): with pytest.raises(ValueError): - _ = examples.shor.ModularExp( - target=[2, 2], exponent=[2, 2, 2], base=4, modulus=5 - ) + _ = examples.shor.ModularExp(target=[2, 2], exponent=[2, 2, 2], base=4, modulus=5) def test_example_shor_modular_exp_register_type(): - operation = examples.shor.ModularExp( - target=[2, 2, 2], exponent=[2, 2], base=4, modulus=5 - ) + operation = examples.shor.ModularExp(target=[2, 2, 2], exponent=[2, 2], base=4, modulus=5) with pytest.raises(ValueError): _ = operation.with_registers([2, 2, 2]) with pytest.raises(ValueError): _ = operation.with_registers(1, [2, 2, 2], 4, 5) with pytest.raises(ValueError): - _ = operation.with_registers( - [2, 2, 2], [2, 2, 2], [2, 2, 2], 5 - ) + _ = operation.with_registers([2, 2, 2], [2, 2, 2], [2, 2, 2], 5) with pytest.raises(ValueError): - _ = operation.with_registers( - [2, 2, 2], [2, 2, 2], 4, [2, 2, 2] - ) + _ = operation.with_registers([2, 2, 2], [2, 2, 2], 4, [2, 2, 2]) def test_example_shor_modular_exp_registers(): From 8eef3dfd37e44e41e9e2dc187effefc3f4e52a21 Mon Sep 17 00:00:00 2001 From: daxfohl Date: Mon, 27 Dec 2021 05:36:58 -0800 Subject: [PATCH 08/19] mypy --- cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py b/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py index d85dced1604..e95ddd758f3 100644 --- a/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py +++ b/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py @@ -91,9 +91,7 @@ def _value_equality_values_(self) -> Any: def registers(self) -> Sequence[Union[int, Sequence[int]]]: return [self.target, *self.inputs] - def with_registers( - self, *new_registers: Union[int, Sequence[int]] - ) -> 'QuirkArithmeticGate': + def with_registers(self, *new_registers: Union[int, Sequence[int]]) -> 'QuirkArithmeticGate': if len(new_registers) != len(self.inputs) + 1: raise ValueError( 'Wrong number of registers.\n' @@ -238,12 +236,12 @@ def operations(self) -> 'cirq.OP_TREE': if missing_inputs: raise ValueError(f'Missing input: {sorted(missing_inputs)}') - inputs = [i if isinstance(i, int) else [q.dimension for q in i] for i in self.inputs] + inputs = cast(Sequence[Union[Sequence['cirq.Qid'], int]], self.inputs) qubits = self.target + tuple(q for i in self.inputs if isinstance(i, Sequence) for q in i) return QuirkArithmeticGate( self.identifier, [q.dimension for q in self.target], - inputs, + [i if isinstance(i, int) else [q.dimension for q in i] for i in inputs], ).on(*qubits) From 0d9f50394cc5b9c57b624d8af8946775956524de Mon Sep 17 00:00:00 2001 From: daxfohl Date: Mon, 27 Dec 2021 05:44:17 -0800 Subject: [PATCH 09/19] bring b --- cirq-core/cirq/interop/quirk/__init__.py | 1 + .../cirq/interop/quirk/cells/__init__.py | 1 + .../interop/quirk/cells/arithmetic_cells.py | 112 ++++++++++++++++++ cirq-core/cirq/ops/arithmetic_operation.py | 2 +- .../cirq/ops/arithmetic_operation_test.py | 6 +- 5 files changed, 118 insertions(+), 4 deletions(-) diff --git a/cirq-core/cirq/interop/quirk/__init__.py b/cirq-core/cirq/interop/quirk/__init__.py index a93f2a8e44c..8d82f15b6bf 100644 --- a/cirq-core/cirq/interop/quirk/__init__.py +++ b/cirq-core/cirq/interop/quirk/__init__.py @@ -21,6 +21,7 @@ # Imports from cells are only to ensure operation reprs work correctly. from cirq.interop.quirk.cells import ( QuirkArithmeticGate, + QuirkArithmeticOperation, QuirkInputRotationOperation, QuirkQubitPermutationGate, ) diff --git a/cirq-core/cirq/interop/quirk/cells/__init__.py b/cirq-core/cirq/interop/quirk/cells/__init__.py index a8e673ec785..832a24165e8 100644 --- a/cirq-core/cirq/interop/quirk/cells/__init__.py +++ b/cirq-core/cirq/interop/quirk/cells/__init__.py @@ -34,6 +34,7 @@ from cirq.interop.quirk.cells.arithmetic_cells import ( QuirkArithmeticGate, + QuirkArithmeticOperation, ) from cirq.interop.quirk.cells.input_rotation_cells import ( diff --git a/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py b/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py index e95ddd758f3..1a3ebf3c742 100644 --- a/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py +++ b/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py @@ -28,12 +28,124 @@ ) from cirq import ops, value +from cirq._compat import deprecated_class from cirq.interop.quirk.cells.cell import Cell, CellMaker, CELL_SIZES if TYPE_CHECKING: import cirq +@deprecated_class(deadline='v0.15', fix='Use cirq.QuirkArithmeticGate') +@value.value_equality +class QuirkArithmeticOperation(ops.ArithmeticOperation): + """Applies arithmetic to a target and some inputs. + Implements Quirk-specific implicit effects like assuming that the presence + of an 'r' input implies modular arithmetic. + In Quirk, modular operations have no effect on values larger than the + modulus. This convention is used because unitarity forces *some* convention + on out-of-range values (they cannot simply disappear or raise exceptions), + and the simplest is to do nothing. This call handles ensuring that happens, + and ensuring the new target register value is normalized modulo the modulus. + """ + + def __init__( + self, + identifier: str, + target: Sequence['cirq.Qid'], + inputs: Sequence[Union[Sequence['cirq.Qid'], int]], + ): + """Inits QuirkArithmeticOperation. + Args: + identifier: The quirk identifier string for this operation. + target: The target qubit register. + inputs: Qubit registers (or classical constants) that + determine what happens to the target. + Raises: + ValueError: If given overlapping registers, or the target is too + small for a modular operation with too small modulus. + """ + self.identifier = identifier + self.target: Tuple['cirq.Qid', ...] = tuple(target) + self.inputs: Tuple[Union[Sequence['cirq.Qid'], int], ...] = tuple( + e if isinstance(e, int) else tuple(e) for e in inputs + ) + + for input_register in self.inputs: + if isinstance(input_register, int): + continue + if set(self.target) & set(input_register): + raise ValueError(f'Overlapping registers: {self.target} {self.inputs}') + + if self.operation.is_modular: + r = inputs[-1] + if isinstance(r, int): + over = r > 1 << len(target) + else: + over = len(cast(Sequence, r)) > len(target) + if over: + raise ValueError(f'Target too small for modulus.\nTarget: {target}\nModulus: {r}') + + @property + def operation(self) -> '_QuirkArithmeticCallable': + return ARITHMETIC_OP_TABLE[self.identifier] + + def _value_equality_values_(self) -> Any: + return self.identifier, self.target, self.inputs + + def registers(self) -> Sequence[Union[int, Sequence['cirq.Qid']]]: + return [self.target, *self.inputs] + + def with_registers( + self, *new_registers: Union[int, Sequence['cirq.Qid']] + ) -> 'QuirkArithmeticOperation': + if len(new_registers) != len(self.inputs) + 1: + raise ValueError( + 'Wrong number of registers.\n' + f'New registers: {repr(new_registers)}\n' + f'Operation: {repr(self)}' + ) + + if isinstance(new_registers[0], int): + raise ValueError( + 'The first register is the mutable target. ' + 'It must be a list of qubits, not the constant ' + f'{new_registers[0]}.' + ) + + return QuirkArithmeticOperation(self.identifier, new_registers[0], new_registers[1:]) + + def apply(self, *registers: int) -> Union[int, Iterable[int]]: + return self.operation(*registers) + + def _circuit_diagram_info_(self, args: 'cirq.CircuitDiagramInfoArgs') -> List[str]: + lettered_args = list(zip(self.operation.letters, self.inputs)) + + result: List[str] = [] + + # Target register labels. + consts = ''.join( + f',{letter}={reg}' for letter, reg in lettered_args if isinstance(reg, int) + ) + result.append(f'Quirk({self.identifier}{consts})') + result.extend(f'#{i}' for i in range(2, len(self.target) + 1)) + + # Input register labels. + for letter, reg in lettered_args: + if not isinstance(reg, int): + result.extend(f'{letter.upper()}{i}' for i in range(len(cast(Sequence, reg)))) + + return result + + def __repr__(self) -> str: + return ( + 'cirq.interop.quirk.QuirkArithmeticOperation(\n' + f' {repr(self.identifier)},\n' + f' target={repr(self.target)},\n' + f' inputs={_indented_list_lines_repr(self.inputs)},\n' + ')' + ) + + @value.value_equality class QuirkArithmeticGate(ops.ArithmeticGate): """Applies arithmetic to a target and some inputs. diff --git a/cirq-core/cirq/ops/arithmetic_operation.py b/cirq-core/cirq/ops/arithmetic_operation.py index deccdd970da..fde240622b9 100644 --- a/cirq-core/cirq/ops/arithmetic_operation.py +++ b/cirq-core/cirq/ops/arithmetic_operation.py @@ -30,7 +30,7 @@ TSelf = TypeVar('TSelf', bound='ArithmeticOperation') -@deprecated_class(deadline='v0.16', fix='Use cirq.ArithmeticGate') +@deprecated_class(deadline='v0.15', fix='Use cirq.ArithmeticGate') class ArithmeticOperation(Operation, metaclass=abc.ABCMeta): """A helper class for implementing reversible classical arithmetic. diff --git a/cirq-core/cirq/ops/arithmetic_operation_test.py b/cirq-core/cirq/ops/arithmetic_operation_test.py index 0d85b962557..eb9fda98375 100644 --- a/cirq-core/cirq/ops/arithmetic_operation_test.py +++ b/cirq-core/cirq/ops/arithmetic_operation_test.py @@ -98,7 +98,7 @@ def with_registers(self, *new_registers): def apply(self, target_value, input_value): return target_value + input_value - with cirq.testing.assert_deprecated(deadline='v0.16', count=8): + with cirq.testing.assert_deprecated(deadline='v0.15', count=8): inc2 = Add(cirq.LineQubit.range(2), 1) np.testing.assert_allclose(cirq.unitary(inc2), shift_matrix(4, 1), atol=1e-8) @@ -206,7 +206,7 @@ def with_registers(self, *new_registers): def apply(self, target_value, input_value): raise NotImplementedError() - with cirq.testing.assert_deprecated(deadline='v0.16', count=4): + with cirq.testing.assert_deprecated(deadline='v0.15', count=4): q0, q1, q2, q3, q4, q5 = cirq.LineQubit.range(6) op = Three([q0], [], [q4, q5]) assert op.qubits == (q0, q4, q5) @@ -236,7 +236,7 @@ def registers(self): def with_registers(self, *new_registers): raise NotImplementedError() - with cirq.testing.assert_deprecated(deadline='v0.16'): + with cirq.testing.assert_deprecated(deadline='v0.15'): state = np.ones(4, dtype=np.complex64) / 2 output = cirq.final_state_vector(cirq.Circuit(Op1()), initial_state=state) np.testing.assert_allclose(state, output) From 8475df26d8f06c4546219f5bae7de79aee669173 Mon Sep 17 00:00:00 2001 From: daxfohl Date: Mon, 27 Dec 2021 05:55:54 -0800 Subject: [PATCH 10/19] docs --- .../cirq/interop/quirk/cells/arithmetic_cells.py | 4 ++++ cirq-core/cirq/ops/arithmetic_operation.py | 13 ++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py b/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py index 1a3ebf3c742..a947afc20d2 100644 --- a/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py +++ b/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py @@ -39,8 +39,10 @@ @value.value_equality class QuirkArithmeticOperation(ops.ArithmeticOperation): """Applies arithmetic to a target and some inputs. + Implements Quirk-specific implicit effects like assuming that the presence of an 'r' input implies modular arithmetic. + In Quirk, modular operations have no effect on values larger than the modulus. This convention is used because unitarity forces *some* convention on out-of-range values (they cannot simply disappear or raise exceptions), @@ -55,11 +57,13 @@ def __init__( inputs: Sequence[Union[Sequence['cirq.Qid'], int]], ): """Inits QuirkArithmeticOperation. + Args: identifier: The quirk identifier string for this operation. target: The target qubit register. inputs: Qubit registers (or classical constants) that determine what happens to the target. + Raises: ValueError: If given overlapping registers, or the target is too small for a modular operation with too small modulus. diff --git a/cirq-core/cirq/ops/arithmetic_operation.py b/cirq-core/cirq/ops/arithmetic_operation.py index fde240622b9..2eb04676108 100644 --- a/cirq-core/cirq/ops/arithmetic_operation.py +++ b/cirq-core/cirq/ops/arithmetic_operation.py @@ -256,13 +256,12 @@ class ArithmeticGate(Gate, metaclass=abc.ABCMeta): This class handles the details of ensuring that the scaling of implementing the operation is O(2^n) instead of O(4^n) where n is the number of qubits being acted on, by implementing an `_apply_unitary_` function in terms of - the registers and the apply function of the child class. It also handles the - boilerplate of implementing the `qubits` and `with_qubits` methods. + the registers and the apply function of the child class. Examples: ``` - >>> class Add(cirq.ArithmeticOperation): + >>> class Add(cirq.ArithmeticGate): ... def __init__(self, target_register, input_register): ... self.target_register = target_register ... self.input_register = input_register @@ -276,8 +275,8 @@ class ArithmeticGate(Gate, metaclass=abc.ABCMeta): ... def apply(self, target_value, input_value): ... return target_value + input_value >>> cirq.unitary( - ... Add(target_register=cirq.LineQubit.range(2), - ... input_register=1) + ... Add(target_register=[2, 2], + ... input_register=1).on(*cirq.LineQubit.range(2)) ... ).astype(np.int32) array([[0, 0, 0, 1], [1, 0, 0, 0], @@ -290,8 +289,8 @@ class ArithmeticGate(Gate, metaclass=abc.ABCMeta): ... cirq.measure(*cirq.LineQubit.range(4, 8), key='before:in'), ... cirq.measure(*cirq.LineQubit.range(4), key='before:out'), ... - ... Add(target_register=cirq.LineQubit.range(4), - ... input_register=cirq.LineQubit.range(4, 8)), + ... Add(target_register=[2] * 4, + ... input_register=[2] * 4).on(*cirq.LineQubit.range(8)), ... ... cirq.measure(*cirq.LineQubit.range(4, 8), key='after:in'), ... cirq.measure(*cirq.LineQubit.range(4), key='after:out'), From 4c5ab89231e9de4aaa2cd78e01e8fb30e9cba7d7 Mon Sep 17 00:00:00 2001 From: daxfohl Date: Mon, 27 Dec 2021 13:19:49 -0800 Subject: [PATCH 11/19] simplify shor --- examples/shor.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/examples/shor.py b/examples/shor.py index 348f1fa460f..c98fe5b0b3b 100644 --- a/examples/shor.py +++ b/examples/shor.py @@ -215,16 +215,14 @@ def make_order_finding_circuit(x: int, n: int) -> cirq.Circuit: Quantum circuit for finding the order of x modulo n """ L = n.bit_length() - target = (2,) * L - target_q = cirq.LineQubit.range(L) - exponent = (2,) * (2 * L + 3) - exponent_q = cirq.LineQubit.range(L, 3 * L + 3) + target = cirq.LineQubit.range(L) + exponent = cirq.LineQubit.range(L, 3 * L + 3) return cirq.Circuit( - cirq.X(target_q[L - 1]), - cirq.H.on_each(*exponent_q), - ModularExp(target, exponent, x, n).on(*(target_q + exponent_q)), - cirq.qft(*exponent_q, inverse=True), - cirq.measure(*exponent_q, key='exponent'), + cirq.X(target[L - 1]), + cirq.H.on_each(*exponent), + ModularExp([2] * len(target), [2] * len(exponent), x, n).on(*target + exponent), + cirq.qft(*exponent, inverse=True), + cirq.measure(*exponent, key='exponent'), ) From cc0815f3807164b91bd338544c84475f7196071b Mon Sep 17 00:00:00 2001 From: daxfohl Date: Mon, 28 Feb 2022 12:37:24 -0800 Subject: [PATCH 12/19] lint --- examples/shor.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/shor.py b/examples/shor.py index c98fe5b0b3b..9fec0288d2b 100644 --- a/examples/shor.py +++ b/examples/shor.py @@ -51,8 +51,7 @@ import fractions import math import random - -from typing import Callable, List, Optional, Sequence, Union +from typing import Callable, Optional, Sequence, Union import sympy From c4b6ac4c9a5e8b8de9b42feb844a0445be276e82 Mon Sep 17 00:00:00 2001 From: daxfohl Date: Thu, 3 Mar 2022 10:08:20 -0800 Subject: [PATCH 13/19] code cleanup --- cirq-core/cirq/ops/arithmetic_operation.py | 35 ++++++++++------------ 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/cirq-core/cirq/ops/arithmetic_operation.py b/cirq-core/cirq/ops/arithmetic_operation.py index 2eb04676108..d1e5d9fd763 100644 --- a/cirq-core/cirq/ops/arithmetic_operation.py +++ b/cirq-core/cirq/ops/arithmetic_operation.py @@ -15,13 +15,12 @@ import abc import itertools -from typing import Union, Iterable, List, Sequence, cast, TypeVar, TYPE_CHECKING +from typing import Union, Iterable, List, Sequence, cast, Tuple, TypeVar, TYPE_CHECKING import numpy as np from cirq._compat import deprecated_class from cirq.ops.raw_types import Operation, Gate -from cirq.ops.gate_operation import GateOperation if TYPE_CHECKING: import cirq @@ -254,7 +253,7 @@ class ArithmeticGate(Gate, metaclass=abc.ABCMeta): methods. This class handles the details of ensuring that the scaling of implementing - the operation is O(2^n) instead of O(4^n) where n is the number of qubits + the gate is O(2^n) instead of O(4^n) where n is the number of qubits being acted on, by implementing an `_apply_unitary_` function in terms of the registers and the apply function of the child class. @@ -304,39 +303,38 @@ class ArithmeticGate(Gate, metaclass=abc.ABCMeta): @abc.abstractmethod def registers(self) -> Sequence[Union[int, Sequence[int]]]: - """The data acted upon by the arithmetic operation. + """The data acted upon by the arithmetic gate. Each register in the list can either be a classical constant (an `int`), or else a list of qubit/qudit dimensions. Registers that are set to a - classical constant must not be mutated by the arithmetic operation + classical constant must not be mutated by the arithmetic gate (their value must remain fixed when passed to `apply`). Registers are big endian. The first qubit is the most significant, the last qubit is the 1s qubit, the before last qubit is the 2s qubit, etc. Returns: - A list of constants and qubit groups that the operation will act - upon. + A list of constants and qubit groups that the gate will act upon. """ raise NotImplementedError() @abc.abstractmethod def with_registers(self: TSelfGate, *new_registers: Union[int, Sequence[int]]) -> TSelfGate: - """Returns the same operation targeting different registers. + """Returns the same fate targeting different registers. Args: new_registers: The new values that should be returned by the `registers` method. Returns: - An instance of the same kind of operation, but acting on different + An instance of the same kind of gate, but acting on different registers. """ raise NotImplementedError() @abc.abstractmethod def apply(self, *register_values: int) -> Union[int, Iterable[int]]: - """Returns the result of the operation operating on classical values. + """Returns the result of the gate operating on classical values. For example, an addition takes two values (the target and the source), adds the source into the target, then returns the target and source @@ -351,14 +349,14 @@ def apply(self, *register_values: int) -> Union[int, Iterable[int]]: values are also permitted. For example, for a 3 qubit register the value -2 becomes -2 % 2**3 = 6. 2. When the value of the last `k` registers is not changed by the - operation, the `apply` method is permitted to omit these values + gate, the `apply` method is permitted to omit these values from the result. That is to say, when the length of the output is less than the length of the input, it is padded up to the intended length by copying from the same position in the input. 3. When only the first register's value changes, the `apply` method is permitted to return an `int` instead of a sequence of ints. - The `apply` method *must* be reversible. Otherwise the operation will + The `apply` method *must* be reversible. Otherwise the gate will not be unitary, and incorrect behavior will result. Examples: @@ -371,7 +369,7 @@ def apply(self, target, offset): ``` The same adder, with less boilerplate due to the details being - handled by the `ArithmeticOperation` class: + handled by the `ArithmeticGate` class: ``` def apply(self, target, offset): @@ -380,7 +378,7 @@ def apply(self, target, offset): """ raise NotImplementedError() - def _qid_shape_(self): + def _qid_shape_(self) -> Tuple[int, ...]: shape = [] for r in self.registers(): if isinstance(r, Sequence): @@ -388,14 +386,11 @@ def _qid_shape_(self): shape.append(i) return tuple(shape) - def on(self, *qubits) -> 'cirq.Operation': - return GateOperation(self, qubits) - def _apply_unitary_(self, args: 'cirq.ApplyUnitaryArgs'): registers = self.registers() input_ranges: List[Sequence[int]] = [] - shape = [] - overflow_sizes = [] + shape: List[int] = [] + overflow_sizes: List[int] = [] for register in registers: if isinstance(register, int): input_ranges.append([register]) @@ -470,7 +465,7 @@ def _describe_bad_arithmetic_changed_const( drawer.write(3, i + 1, str(outputs[i])) return ( "A register cannot be set to an int (a classical constant) unless its " - "value is not affected by the operation.\n" + "value is not affected by the gate.\n" "\nExample case where a constant changed:\n" + drawer.render(horizontal_spacing=1, vertical_spacing=0) ) From 4b5519c861633d72481ed778cf236748bcc0d5e2 Mon Sep 17 00:00:00 2001 From: daxfohl Date: Thu, 3 Mar 2022 10:15:37 -0800 Subject: [PATCH 14/19] code cleanup --- cirq-core/cirq/ops/arithmetic_operation.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/cirq-core/cirq/ops/arithmetic_operation.py b/cirq-core/cirq/ops/arithmetic_operation.py index d1e5d9fd763..df7eb802bec 100644 --- a/cirq-core/cirq/ops/arithmetic_operation.py +++ b/cirq-core/cirq/ops/arithmetic_operation.py @@ -261,17 +261,21 @@ class ArithmeticGate(Gate, metaclass=abc.ABCMeta): ``` >>> class Add(cirq.ArithmeticGate): - ... def __init__(self, target_register, input_register): + ... def __init__( + ... self, + ... target_register: [int, Sequence[int]], + ... input_register: Union[int, Sequence[int]], + ... ): ... self.target_register = target_register ... self.input_register = input_register ... - ... def registers(self): + ... def registers(self) -> Sequence[Union[int, Sequence[int]]]: ... return self.target_register, self.input_register ... - ... def with_registers(self, *new_registers): + ... def with_registers(self, *new_registers: Union[int, Sequence[int]]) -> TSelfGate: ... return Add(*new_registers) ... - ... def apply(self, target_value, input_value): + ... def apply(self, target_value: int, input_value: int) -> Union[int, Iterable[int]]: ... return target_value + input_value >>> cirq.unitary( ... Add(target_register=[2, 2], From 067303e1c4db5c332fb54de767f7812b45248704 Mon Sep 17 00:00:00 2001 From: daxfohl Date: Thu, 3 Mar 2022 10:18:24 -0800 Subject: [PATCH 15/19] code cleanup --- cirq-core/cirq/ops/arithmetic_operation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cirq-core/cirq/ops/arithmetic_operation.py b/cirq-core/cirq/ops/arithmetic_operation.py index df7eb802bec..6b91d146e5c 100644 --- a/cirq-core/cirq/ops/arithmetic_operation.py +++ b/cirq-core/cirq/ops/arithmetic_operation.py @@ -275,8 +275,8 @@ class ArithmeticGate(Gate, metaclass=abc.ABCMeta): ... def with_registers(self, *new_registers: Union[int, Sequence[int]]) -> TSelfGate: ... return Add(*new_registers) ... - ... def apply(self, target_value: int, input_value: int) -> Union[int, Iterable[int]]: - ... return target_value + input_value + ... def apply(self, *register_values: int) -> Union[int, Iterable[int]]: + ... return sum(register_values) >>> cirq.unitary( ... Add(target_register=[2, 2], ... input_register=1).on(*cirq.LineQubit.range(2)) From 115573663934066b7d2835d8ab4b08446c6576fd Mon Sep 17 00:00:00 2001 From: daxfohl Date: Thu, 3 Mar 2022 13:27:38 -0800 Subject: [PATCH 16/19] fix test --- cirq-core/cirq/ops/arithmetic_operation_test.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cirq-core/cirq/ops/arithmetic_operation_test.py b/cirq-core/cirq/ops/arithmetic_operation_test.py index eb9fda98375..45a2930cb46 100644 --- a/cirq-core/cirq/ops/arithmetic_operation_test.py +++ b/cirq-core/cirq/ops/arithmetic_operation_test.py @@ -111,10 +111,10 @@ def apply(self, target_value, input_value): add2from3 = Add(cirq.LineQubit.range(2), cirq.LineQubit.range(3)) np.testing.assert_allclose(cirq.unitary(add2from3), adder_matrix(4, 8), atol=1e-8) - with pytest.raises(ValueError, match='affected by the operation'): + with pytest.raises(ValueError, match='affected by the gate'): _ = cirq.unitary(Add(1, cirq.LineQubit.range(2))) - with pytest.raises(ValueError, match='affected by the operation'): + with pytest.raises(ValueError, match='affected by the gate'): _ = cirq.unitary(Add(1, 1)) np.testing.assert_allclose(cirq.unitary(Add(1, 0)), np.eye(1)) @@ -165,16 +165,16 @@ def apply(self, target_value, input_value): cirq.unitary(add2from3.on(*qubits[5])), adder_matrix(4, 8), atol=1e-8 ) - with pytest.raises(ValueError, match='affected by the operation'): + with pytest.raises(ValueError, match='affected by the gate'): _ = cirq.unitary(Add(1, [2, 2])) - with pytest.raises(ValueError, match='affected by the operation'): + with pytest.raises(ValueError, match='affected by the gate'): _ = cirq.unitary(Add(1, [2, 2]).on(*qubits[2])) - with pytest.raises(ValueError, match='affected by the operation'): + with pytest.raises(ValueError, match='affected by the gate'): _ = cirq.unitary(Add(1, 1)) - with pytest.raises(ValueError, match='affected by the operation'): + with pytest.raises(ValueError, match='affected by the gate'): _ = cirq.unitary(Add(1, 1).on()) np.testing.assert_allclose(cirq.unitary(Add(1, 0)), np.eye(1)) From 844dff74c2bacaaf84c8b4a2d0488cf9ca2bf50c Mon Sep 17 00:00:00 2001 From: daxfohl Date: Tue, 29 Mar 2022 12:05:18 -0700 Subject: [PATCH 17/19] format --- examples/examples_test.py | 2 +- examples/shor.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/examples_test.py b/examples/examples_test.py index 61e487a467c..0a93666d9b4 100644 --- a/examples/examples_test.py +++ b/examples/examples_test.py @@ -259,7 +259,7 @@ def test_example_shor_find_factor_with_prime_n(n, order_finder): assert d is None -@pytest.mark.parametrize('n', (2, 3, 15, 17, 2 ** 89 - 1)) +@pytest.mark.parametrize('n', (2, 3, 15, 17, 2**89 - 1)) def test_example_runs_shor_valid(n): examples.shor.main(n=n) diff --git a/examples/shor.py b/examples/shor.py index 9fec0288d2b..9f3ff6e5395 100644 --- a/examples/shor.py +++ b/examples/shor.py @@ -169,7 +169,7 @@ def apply(self, *register_values: int) -> int: target, exponent, base, modulus = register_values if target >= modulus: return target - return (target * base ** exponent) % modulus + return (target * base**exponent) % modulus def _circuit_diagram_info_( self, @@ -245,7 +245,7 @@ def read_eigenphase(result: cirq.Result) -> float: """ exponent_as_integer = result.data['exponent'][0] exponent_num_bits = result.measurements['exponent'].shape[1] - return float(exponent_as_integer / 2 ** exponent_num_bits) + return float(exponent_as_integer / 2**exponent_num_bits) def quantum_order_finder(x: int, n: int) -> Optional[int]: @@ -276,7 +276,7 @@ def quantum_order_finder(x: int, n: int) -> Optional[int]: if f.numerator == 0: return None # coverage: ignore r = f.denominator - if x ** r % n != 1: + if x**r % n != 1: return None # coverage: ignore return r @@ -286,10 +286,10 @@ def find_factor_of_prime_power(n: int) -> Optional[int]: for k in range(2, math.floor(math.log2(n)) + 1): c = math.pow(n, 1 / k) c1 = math.floor(c) - if c1 ** k == n: + if c1**k == n: return c1 c2 = math.ceil(c) - if c2 ** k == n: + if c2**k == n: return c2 return None From 9027b74f5f09ee8dc379c7011da6ee2b69ff4a5e Mon Sep 17 00:00:00 2001 From: daxfohl Date: Thu, 14 Apr 2022 12:31:53 -0700 Subject: [PATCH 18/19] lint --- cirq-core/cirq/ops/arithmetic_operation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq-core/cirq/ops/arithmetic_operation.py b/cirq-core/cirq/ops/arithmetic_operation.py index d141eca122f..57a6665d797 100644 --- a/cirq-core/cirq/ops/arithmetic_operation.py +++ b/cirq-core/cirq/ops/arithmetic_operation.py @@ -327,7 +327,7 @@ def with_registers(self: TSelfGate, *new_registers: Union[int, Sequence[int]]) - """Returns the same fate targeting different registers. Args: - new_registers: The new values that should be returned by the + *new_registers: The new values that should be returned by the `registers` method. Returns: From c4b9a62ed7809fae2885e57edf8745c07ff38433 Mon Sep 17 00:00:00 2001 From: daxfohl Date: Thu, 5 May 2022 13:56:44 -0700 Subject: [PATCH 19/19] nits --- cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py | 5 +++-- examples/examples_test.py | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py b/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py index ebc744d6409..0072686cf15 100644 --- a/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py +++ b/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py @@ -172,8 +172,9 @@ def __init__( Args: identifier: The quirk identifier string for this operation. target: The target qubit register. - inputs: Qubit registers (or classical constants) that - determine what happens to the target. + inputs: Qubit registers, which correspond to the qid shape of the + qubits from which the input will be read, or classical + constants, that determine what happens to the target. Raises: ValueError: If the target is too small for a modular operation with diff --git a/examples/examples_test.py b/examples/examples_test.py index 1d3081960e6..f7603438671 100644 --- a/examples/examples_test.py +++ b/examples/examples_test.py @@ -185,8 +185,8 @@ def test_example_shor_modular_exp_registers(): def test_example_shor_modular_exp_diagram(): target = [2, 2, 2] exponent = [2, 2] - operation = examples.shor.ModularExp(target, exponent, 4, 5) - circuit = cirq.Circuit(operation.on(*cirq.LineQubit.range(5))) + gate = examples.shor.ModularExp(target, exponent, 4, 5) + circuit = cirq.Circuit(gate.on(*cirq.LineQubit.range(5))) cirq.testing.assert_has_diagram( circuit, """ @@ -202,8 +202,8 @@ def test_example_shor_modular_exp_diagram(): """, ) - operation = operation.with_registers(target, 2, 4, 5) - circuit = cirq.Circuit(operation.on(*cirq.LineQubit.range(3))) + gate = gate.with_registers(target, 2, 4, 5) + circuit = cirq.Circuit(gate.on(*cirq.LineQubit.range(3))) cirq.testing.assert_has_diagram( circuit, """