diff --git a/cirq-core/cirq/__init__.py b/cirq-core/cirq/__init__.py
index 42282c6973a..3420b1b0591 100644
--- a/cirq-core/cirq/__init__.py
+++ b/cirq-core/cirq/__init__.py
@@ -186,7 +186,6 @@
AnyIntegerPowerGateFamily,
AnyUnitaryGateFamily,
ArithmeticGate,
- ArithmeticOperation,
asymmetric_depolarize,
AsymmetricDepolarizingChannel,
BaseDensePauliString,
diff --git a/cirq-core/cirq/interop/quirk/__init__.py b/cirq-core/cirq/interop/quirk/__init__.py
index a3fa4bc5daf..a23f7177244 100644
--- a/cirq-core/cirq/interop/quirk/__init__.py
+++ b/cirq-core/cirq/interop/quirk/__init__.py
@@ -21,7 +21,6 @@
# 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 b03af2a493f..cf8e56a0c0a 100644
--- a/cirq-core/cirq/interop/quirk/cells/__init__.py
+++ b/cirq-core/cirq/interop/quirk/cells/__init__.py
@@ -21,7 +21,7 @@
from cirq.interop.quirk.cells.qubit_permutation_cells import QuirkQubitPermutationGate
-from cirq.interop.quirk.cells.arithmetic_cells import QuirkArithmeticGate, QuirkArithmeticOperation
+from cirq.interop.quirk.cells.arithmetic_cells import QuirkArithmeticGate
from cirq.interop.quirk.cells.input_rotation_cells import QuirkInputRotationOperation
diff --git a/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py b/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py
index 52eaebc62c1..19d0aa3e277 100644
--- a/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py
+++ b/cirq-core/cirq/interop/quirk/cells/arithmetic_cells.py
@@ -28,128 +28,12 @@
)
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.16', 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/__init__.py b/cirq-core/cirq/ops/__init__.py
index f728681b454..bc342b8aa24 100644
--- a/cirq-core/cirq/ops/__init__.py
+++ b/cirq-core/cirq/ops/__init__.py
@@ -14,7 +14,7 @@
"""Gates (unitary and non-unitary), operations, base types, and gate sets.
"""
-from cirq.ops.arithmetic_operation import ArithmeticGate, ArithmeticOperation
+from cirq.ops.arithmetic_operation import ArithmeticGate
from cirq.ops.clifford_gate import CliffordGate, PauliTransform, SingleQubitCliffordGate
diff --git a/cirq-core/cirq/ops/arithmetic_operation.py b/cirq-core/cirq/ops/arithmetic_operation.py
index 32828613698..4a89aae5727 100644
--- a/cirq-core/cirq/ops/arithmetic_operation.py
+++ b/cirq-core/cirq/ops/arithmetic_operation.py
@@ -19,235 +19,17 @@
import numpy as np
-from cirq._compat import deprecated_class
-from cirq.ops.raw_types import Operation, Gate
+from cirq.ops.raw_types import Gate
if TYPE_CHECKING:
import cirq
-TSelf = TypeVar('TSelf', bound='ArithmeticOperation')
-
-
-@deprecated_class(deadline='v0.16', fix='Use cirq.ArithmeticGate')
-class ArithmeticOperation(Operation, metaclass=abc.ABCMeta):
- r"""A helper class 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['cirq.Qid']]]:
- """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: TSelf, *new_registers: Union[int, Sequence['cirq.Qid']]) -> TSelf:
- """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()
-
- @property
- def qubits(self):
- return tuple(
- qubit
- for register in self.registers()
- if not isinstance(register, int)
- for qubit in register
- )
-
- def with_qubits(self: TSelf, *new_qubits: 'cirq.Qid') -> TSelf:
- new_registers: List[Union[int, Sequence['cirq.Qid']]] = []
- qs = iter(new_qubits)
- for register in self.registers():
- if isinstance(register, int):
- new_registers.append(register)
- else:
- new_registers.append([next(qs) for _ in register])
- return self.with_registers(*new_registers)
-
- 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([q.dimension for q 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
-
-
TSelfGate = TypeVar('TSelfGate', bound='ArithmeticGate')
class ArithmeticGate(Gate, metaclass=abc.ABCMeta):
- """A helper gate for implementing reversible classical arithmetic.
+ r"""A helper gate for implementing reversible classical arithmetic.
Child classes must override the `registers`, `with_registers`, and `apply`
methods.
diff --git a/cirq-core/cirq/ops/arithmetic_operation_test.py b/cirq-core/cirq/ops/arithmetic_operation_test.py
index de9ac218758..00edcf19534 100644
--- a/cirq-core/cirq/ops/arithmetic_operation_test.py
+++ b/cirq-core/cirq/ops/arithmetic_operation_test.py
@@ -85,47 +85,6 @@ def test_the_tests():
)
-def test_arithmetic_operation_apply_unitary():
- 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):
- raise NotImplementedError()
-
- def apply(self, target_value, input_value):
- return target_value + input_value
-
- 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)
-
- 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)
-
- 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 gate'):
- _ = cirq.unitary(Add(1, 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))
- )
-
-
def test_arithmetic_gate_apply_unitary():
class Add(cirq.ArithmeticGate):
def __init__(
@@ -190,55 +149,3 @@ def apply(self, target_value, input_value):
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):
- self.a = a
- self.b = b
- self.c = c
-
- def registers(self):
- return self.a, self.b, self.c
-
- def with_registers(self, *new_registers):
- return Three(*new_registers)
-
- def apply(self, target_value, input_value):
- raise NotImplementedError()
-
- 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])
-
- 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)
-
-
-def test_reshape_referencing():
- class Op1(cirq.ArithmeticOperation):
- def apply(self, *register_values: int):
- return register_values[0] + 1
-
- def registers(self):
- return [cirq.LineQubit.range(2)[::-1]]
-
- def with_registers(self, *new_registers):
- raise NotImplementedError()
-
- 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)
diff --git a/docs/experiments/shor.ipynb b/docs/experiments/shor.ipynb
index eb20d703127..da8883fa710 100644
--- a/docs/experiments/shor.ipynb
+++ b/docs/experiments/shor.ipynb
@@ -103,7 +103,7 @@
"\n",
"import numpy as np\n",
"import sympy\n",
- "from typing import Callable, List, Optional, Sequence, Union\n",
+ "from typing import Callable, Iterable, List, Optional, Sequence, Union\n",
"\n",
"import cirq"
]
@@ -324,9 +324,9 @@
"id": "sdm5KtkiYArk"
},
"source": [
- "Quantum order finding is essentially quantum phase estimation with unitary $U$ that computes the modular exponential function $f_x(z)$ for some randomly chosen $x \\in \\mathbb{Z}_n$. The full details of how $U$ is computed in terms of elementary gates can be complex to unravel, especially on a first reading. In this tutorial, we'll use arithmetic operations in Cirq which can implement such a unitary $U$ without fully delving into the details of elementary gates.\n",
+ "Quantum order finding is essentially quantum phase estimation with unitary $U$ that computes the modular exponential function $f_x(z)$ for some randomly chosen $x \\in \\mathbb{Z}_n$. The full details of how $U$ is computed in terms of elementary gates can be complex to unravel, especially on a first reading. In this tutorial, we'll use arithmetic gates in Cirq which can implement such a unitary $U$ without fully delving into the details of elementary gates.\n",
"\n",
- "Below we first show an example of a simple arithmetic operation in Cirq (addition) then discuss the operation we care about (modular exponentiation)."
+ "Below we first show an example of a simple arithmetic gate in Cirq (addition) then discuss the gate we care about (modular exponentiation)."
]
},
{
@@ -335,7 +335,7 @@
"id": "I9wao-eNYArl"
},
"source": [
- "### Quantum arithmetic operations in Cirq"
+ "### Quantum arithmetic gates in Cirq"
]
},
{
@@ -344,13 +344,15 @@
"id": "03iL9sDpYArl"
},
"source": [
- "Here we discuss an example of defining an arithmetic operation in Cirq, namely modular addition. This operation adds the value of the input register into the target register. More specifically, this operation acts on two qubit registers as\n",
+ "Here we discuss an example of defining an arithmetic gate in Cirq, namely modular addition. This gate adds the value of the input register into the target register. More specifically, this gate acts on two qubit registers as\n",
"\n",
"$$ |a\\rangle_i |b\\rangle_t \\mapsto |a\\rangle_i |a + b \\text{ mod } N_t \\rangle_t . $$\n",
"\n",
"Here, the subscripts $i$ and $t$ denote input and target register, respectively, and $N_t$ is the dimension of the target register.\n",
"\n",
- "To define this operation, called `Adder`, we inherit from `cirq.ArithmeticOperation` and override the four methods shown below. The main method is the `apply` method which defines the arithmetic. Here, we simply state the expression as $a + b$ instead of the more accurate $a + b \\text{ mod } N_t$ above -- the `cirq.ArithmeticOperation` class is able to deduce what we mean by simply $a + b$ since the operation must be reversible. "
+ "To define this gate, called `Adder`, we inherit from `cirq.ArithmeticGate` and override the four methods shown below. Note the register arguments specify the shape of the qubits to which they can be applied.\n",
+ "\n",
+ "The main method is the `apply` method which defines the arithmetic. Here, we simply state the expression as $a + b$ instead of the more accurate $a + b \\text{ mod } N_t$ above -- the `cirq.ArithmeticGate` class is able to deduce what we mean by simply $a + b$ since the gate must be reversible. "
]
},
{
@@ -361,21 +363,27 @@
},
"outputs": [],
"source": [
- "\"\"\"Example of defining an arithmetic (quantum) operation in Cirq.\"\"\"\n",
- "class Adder(cirq.ArithmeticOperation):\n",
+ "\"\"\"Example of defining an arithmetic (quantum) gate in Cirq.\"\"\"\n",
+ "class Adder(cirq.ArithmeticGate):\n",
" \"\"\"Quantum addition.\"\"\"\n",
- " def __init__(self, target_register, input_register):\n",
- " self.input_register = input_register\n",
+ " def __init__(\n",
+ " self,\n",
+ " target_register: [int, Sequence[int]],\n",
+ " input_register: Union[int, Sequence[int]],\n",
+ " ):\n",
" self.target_register = target_register\n",
- " \n",
- " def registers(self):\n",
+ " self.input_register = input_register\n",
+ "\n",
+ " def registers(self) -> Sequence[Union[int, Sequence[int]]]:\n",
" return self.target_register, self.input_register\n",
- " \n",
- " def with_registers(self, *new_registers):\n",
+ "\n",
+ " def with_registers(\n",
+ " self, *new_registers: Union[int, Sequence[int]]\n",
+ " ) -> 'Adder':\n",
" return Adder(*new_registers)\n",
- " \n",
- " def apply(self, target_value, input_value):\n",
- " return target_value + input_value\n",
+ "\n",
+ " def apply(self, *register_values: int) -> Union[int, Iterable[int]]:\n",
+ " return sum(register_values)",
"\n",
" def _circuit_diagram_info_(self, args: cirq.CircuitDiagramInfoArgs):\n",
" wire_symbols = [' + ' for _ in range(len(self.input_register)+len(self.target_register))]\n",
@@ -388,7 +396,7 @@
"id": "XxOfZE6yYArq"
},
"source": [
- "Now that we have the operation defined, we can use it in a circuit. The cell below creates two qubit registers, then sets the first register to be $|10\\rangle$ (in binary) and the second register to be $|01\\rangle$ (in binary) via $X$ gates. Then, we use the `Adder` operation, then measure all the qubits.\n",
+ "Now that we have the gate defined, we can use it in a circuit. The cell below creates two qubit registers, then sets the first register to be $|10\\rangle$ (in binary) and the second register to be $|01\\rangle$ (in binary) via $X$ gates. Then, we use the `Adder` gate, then measure all the qubits.\n",
"\n",
"Since $10 + 01 = 11$ (in binary), we expect to measure $|11\\rangle$ in the target register every time. Additionally, since we do not alter the input register, we expect to measure $|10\\rangle$ in the input register every time. In short, the only bitstring we expect to measure is $1011$. "
]
@@ -406,11 +414,14 @@
"qreg1 = cirq.LineQubit.range(2)\n",
"qreg2 = cirq.LineQubit.range(2, 4)\n",
"\n",
+ "# Define an adder gate for two 2D input and target qubits.\n",
+ "adder = Adder(input_register=[2, 2], target_register=[2, 2])\n",
+ "\n",
"# Define the circuit.\n",
"circ = cirq.Circuit(\n",
" cirq.X.on(qreg1[0]),\n",
" cirq.X.on(qreg2[1]),\n",
- " Adder(input_register=qreg1, target_register=qreg2),\n",
+ " adder.on(*qreg1, *qreg2),\n",
" cirq.measure_each(*qreg1),\n",
" cirq.measure_each(*qreg2)\n",
")\n",
@@ -430,7 +441,7 @@
"id": "Q2ZlalhkYArv"
},
"source": [
- "In the output of this code block, we first see the circuit which shows the initial $X$ gates, the `Adder` operation, then the final measurements. Next, we see the measurement outcomes which are all the bitstring $1011$ as expected."
+ "In the output of this code block, we first see the circuit which shows the initial $X$ gates, the `Adder` gate, then the final measurements. Next, we see the measurement outcomes which are all the bitstring $1011$ as expected."
]
},
{
@@ -439,7 +450,7 @@
"id": "jEnqlr6pYArv"
},
"source": [
- "It is also possible to see the unitary of the adder operation, which we do below. Here, we set the target register to be two qubits in the zero state, i.e. $|00\\rangle$. We specify the input register as the integer one which corresponds to the qubit register $|01\\rangle$. "
+ "It is also possible to see the unitary of the adder gate, which we do below. Here, we set the target register to be two qubits in the zero state, i.e. $|00\\rangle$. We specify the input register as the integer one which corresponds to the qubit register $|01\\rangle$. "
]
},
{
@@ -450,9 +461,9 @@
},
"outputs": [],
"source": [
- "\"\"\"Example of the unitary of an Adder operation.\"\"\"\n",
+ "\"\"\"Example of the unitary of an Adder gate.\"\"\"\n",
"cirq.unitary(\n",
- " Adder(target_register=cirq.LineQubit.range(2),\n",
+ " Adder(target_register=[2, 2],\n",
" input_register=1)\n",
").real"
]
@@ -472,7 +483,7 @@
"id": "EqevQK8OYAr0"
},
"source": [
- "### Modular exponential arithmetic operation"
+ "### Modular exponential arithmetic gate"
]
},
{
@@ -481,7 +492,7 @@
"id": "U3A2OnVTYAr1"
},
"source": [
- "We can define the modular exponential arithmetic operation in a similar way to the simple addition arithmetic operation, shown below. For the purposes of understanding Shor's algorithm, the most important part of the following code block is the `apply` method which defines the arithmetic operation."
+ "We can define the modular exponential arithmetic gate in a similar way to the simple addition arithmetic gate, shown below. For the purposes of understanding Shor's algorithm, the most important part of the following code block is the `apply` method which defines the arithmetic gate."
]
},
{
@@ -492,8 +503,8 @@
},
"outputs": [],
"source": [
- "\"\"\"Defines the modular exponential operation used in Shor's algorithm.\"\"\"\n",
- "class ModularExp(cirq.ArithmeticOperation):\n",
+ "\"\"\"Defines the modular exponential gate used in Shor's algorithm.\"\"\"\n",
+ "class ModularExp(cirq.ArithmeticGate):\n",
" \"\"\"Quantum modular exponentiation.\n",
"\n",
" This class represents the unitary which multiplies base raised to exponent\n",
@@ -514,41 +525,47 @@
" U|y⟩ = |y⟩ n <= y\n",
" \"\"\"\n",
" def __init__(\n",
- " self, \n",
- " target: Sequence[cirq.Qid],\n",
- " exponent: Union[int, Sequence[cirq.Qid]], \n",
+ " self,\n",
+ " target: Sequence[int],\n",
+ " exponent: Union[int, Sequence[int]],\n",
" base: int,\n",
" modulus: int\n",
" ) -> None:\n",
" if len(target) < modulus.bit_length():\n",
- " raise ValueError(f'Register with {len(target)} qubits is too small '\n",
- " f'for modulus {modulus}')\n",
+ " raise ValueError(\n",
+ " f'Register with {len(target)} qubits is too small for modulus'\n",
+ " f' {modulus}'\n",
+ " )\n",
" self.target = target\n",
" self.exponent = exponent\n",
" self.base = base\n",
" self.modulus = modulus\n",
"\n",
- " def registers(self) -> Sequence[Union[int, Sequence[cirq.Qid]]]:\n",
+ " def registers(self) -> Sequence[Union[int, Sequence[int]]]:\n",
" return self.target, self.exponent, self.base, self.modulus\n",
"\n",
" def with_registers(\n",
- " self,\n",
- " *new_registers: Union[int, Sequence['cirq.Qid']],\n",
- " ) -> cirq.ArithmeticOperation:\n",
+ " self, *new_registers: Union[int, Sequence[int]]\n",
+ " ) -> 'ModularExp':\n",
" \"\"\"Returns a new ModularExp object with new registers.\"\"\"\n",
" if len(new_registers) != 4:\n",
- " raise ValueError(f'Expected 4 registers (target, exponent, base, '\n",
- " f'modulus), but got {len(new_registers)}')\n",
+ " raise ValueError(\n",
+ " f'Expected 4 registers (target, exponent, base, '\n",
+ " f'modulus), but got {len(new_registers)}'\n",
+ " )\n",
" target, exponent, base, modulus = new_registers\n",
" if not isinstance(target, Sequence):\n",
" raise ValueError(\n",
- " f'Target must be a qubit register, got {type(target)}')\n",
+ " f'Target must be a qubit register, got {type(target)}'\n",
+ " )\n",
" if not isinstance(base, int):\n",
" raise ValueError(\n",
- " f'Base must be a classical constant, got {type(base)}')\n",
+ " f'Base must be a classical constant, got {type(base)}'\n",
+ " )\n",
" if not isinstance(modulus, int):\n",
" raise ValueError(\n",
- " f'Modulus must be a classical constant, got {type(modulus)}')\n",
+ " f'Modulus must be a classical constant, got {type(modulus)}'\n",
+ " )\n",
" return ModularExp(target, exponent, base, modulus)\n",
"\n",
" def apply(self, *register_values: int) -> int:\n",
@@ -571,8 +588,7 @@
" return (target * base**exponent) % modulus\n",
"\n",
" def _circuit_diagram_info_(\n",
- " self,\n",
- " args: cirq.CircuitDiagramInfoArgs,\n",
+ " self, args: cirq.CircuitDiagramInfoArgs\n",
" ) -> cirq.CircuitDiagramInfo:\n",
" \"\"\"Returns a 'CircuitDiagramInfo' object for printing circuits.\n",
"\n",
@@ -581,23 +597,12 @@
" appropriately as exponent ('e') and target ('t').\n",
" \"\"\"\n",
" assert args.known_qubits is not None\n",
- " wire_symbols: List[str] = []\n",
- " t, e = 0, 0\n",
- " for qubit in args.known_qubits:\n",
- " if qubit in self.target:\n",
- " if t == 0:\n",
- " if isinstance(self.exponent, Sequence):\n",
- " e_str = 'e'\n",
- " else:\n",
- " e_str = str(self.exponent)\n",
- " wire_symbols.append(\n",
- " f'ModularExp(t*{self.base}**{e_str} % {self.modulus})')\n",
- " else:\n",
- " wire_symbols.append('t' + str(t))\n",
- " t += 1\n",
- " if isinstance(self.exponent, Sequence) and qubit in self.exponent:\n",
- " wire_symbols.append('e' + str(e))\n",
- " e += 1\n",
+ " wire_symbols = [f't{i}' for i in range(len(self.target))]\n",
+ " e_str = str(self.exponent)\n",
+ " if isinstance(self.exponent, Sequence):\n",
+ " e_str = 'e'\n",
+ " wire_symbols += [f'e{i}' for i in range(len(self.exponent))]\n",
+ " wire_symbols[0] = f'ModularExp(t*{self.base}**{e_str} % {self.modulus})'\n",
" return cirq.CircuitDiagramInfo(wire_symbols=tuple(wire_symbols))"
]
},
@@ -649,7 +654,7 @@
"id": "JUoP7b2BYAr_"
},
"source": [
- "As with the simple adder operation, this modular exponential operation has a unitary which we can display (memory permitting) as follows."
+ "As with the simple adder gate, this modular exponential gate has a unitary which we can display (memory permitting) as follows."
]
},
{
@@ -660,7 +665,7 @@
},
"outputs": [],
"source": [
- "\"\"\"See (part of) the unitary for a modular exponential operation.\"\"\"\n",
+ "\"\"\"See (part of) the unitary for a modular exponential gate.\"\"\"\n",
"# Pick some element of the multiplicative group modulo n.\n",
"x = 5\n",
"\n",
@@ -674,7 +679,7 @@
"id": "RlLEpUbPYAsC"
},
"source": [
- "## Using the modular exponentional operation in a circuit"
+ "## Using the modular exponential gate in a circuit"
]
},
{
@@ -683,7 +688,7 @@
"id": "Tq9LhevvYAsD"
},
"source": [
- "The quantum part of Shor's algorithm is just phase estimation with the unitary $U$ corresponding to the modular exponential operation. The following cell defines a function which creates the circuit for Shor's algorithm using the `ModularExp` operation we defined above."
+ "The quantum part of Shor's algorithm is just phase estimation with the unitary $U$ corresponding to the modular exponential gate. The following cell defines a function which creates the circuit for Shor's algorithm using the `ModularExp` gate we defined above."
]
},
{
@@ -714,10 +719,14 @@
" L = n.bit_length()\n",
" target = cirq.LineQubit.range(L)\n",
" exponent = cirq.LineQubit.range(L, 3 * L + 3)\n",
+ "\n",
+ " # Create a ModularExp gate sized for these registers.\n",
+ " mod_exp = ModularExp([2] * L, [2] * (2 * L + 3), x, n)\n",
+ "\n",
" return cirq.Circuit(\n",
" cirq.X(target[L - 1]),\n",
" cirq.H.on_each(*exponent),\n",
- " ModularExp(target, exponent, x, n),\n",
+ " mod_exp.on(*target, *exponent),\n",
" cirq.qft(*exponent, inverse=True),\n",
" cirq.measure(*exponent, key='exponent'),\n",
" )"
@@ -753,7 +762,7 @@
"id": "xDRxEMRlYAsL"
},
"source": [
- "As previously described, we put the exponent register into an equal superposition via Hadamard gates. The $X$ gate on the last qubit in the target register is used for phase kickback. The modular exponential operation performs the sequence of controlled unitaries in phase estimation, then we apply the inverse quantum Fourier transform to the exponent register and measure to read out the result."
+ "As previously described, we put the exponent register into an equal superposition via Hadamard gates. The $X$ gate on the last qubit in the target register is used for phase kickback. The modular exponential gate performs the sequence of controlled unitaries in phase estimation, then we apply the inverse quantum Fourier transform to the exponent register and measure to read out the result."
]
},
{
diff --git a/docs/gatezoo.ipynb b/docs/gatezoo.ipynb
index dbe4fe71669..c83e0b9eda4 100644
--- a/docs/gatezoo.ipynb
+++ b/docs/gatezoo.ipynb
@@ -489,4 +489,4 @@
},
"nbformat": 4,
"nbformat_minor": 0
-}
\ No newline at end of file
+}
diff --git a/docs/start/intro.ipynb b/docs/start/intro.ipynb
index 95eb8da4579..7acd2167aba 100644
--- a/docs/start/intro.ipynb
+++ b/docs/start/intro.ipynb
@@ -2926,4 +2926,4 @@
},
"nbformat": 4,
"nbformat_minor": 0
-}
\ No newline at end of file
+}
diff --git a/docs/transform/custom_transformers.ipynb b/docs/transform/custom_transformers.ipynb
index 85d034894c4..b31d277530b 100644
--- a/docs/transform/custom_transformers.ipynb
+++ b/docs/transform/custom_transformers.ipynb
@@ -487,4 +487,4 @@
},
"nbformat": 4,
"nbformat_minor": 0
-}
\ No newline at end of file
+}