Skip to content

Commit

Permalink
Move MultiTargetCNOT to separate file (#1262)
Browse files Browse the repository at this point in the history
* Move `MultiTargetCNOT` to separate file

* auto test bloqs
  • Loading branch information
anurudhp authored Aug 7, 2024
1 parent 5fbfe53 commit 211c6ce
Show file tree
Hide file tree
Showing 11 changed files with 133 additions and 87 deletions.
5 changes: 3 additions & 2 deletions dev_tools/autogenerate-bloqs-notebooks-v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -749,12 +749,13 @@
),
NotebookSpecV2(
title='Multi-Paulis',
module=qualtran.bloqs.mcmt.multi_control_multi_target_pauli,
module=qualtran.bloqs.mcmt,
bloq_specs=[
qualtran.bloqs.mcmt.multi_control_multi_target_pauli._C_MULTI_NOT_DOC,
qualtran.bloqs.mcmt.multi_target_cnot._C_MULTI_NOT_DOC,
qualtran.bloqs.mcmt.multi_control_multi_target_pauli._CC_PAULI_DOC,
],
directory=f'{SOURCE_DIR}/bloqs/mcmt/',
path_stem='multi_control_multi_target_pauli',
),
NotebookSpecV2(
title='Generic Select',
Expand Down
2 changes: 1 addition & 1 deletion qualtran/bloqs/arithmetic/subtraction.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
from qualtran.bloqs.arithmetic.bitwise import BitwiseNot
from qualtran.bloqs.basic_gates import OnEach, XGate
from qualtran.bloqs.bookkeeping import Allocate, Cast, Free
from qualtran.bloqs.mcmt.multi_control_multi_target_pauli import MultiTargetCNOT
from qualtran.bloqs.mcmt.multi_target_cnot import MultiTargetCNOT
from qualtran.drawing import Text

if TYPE_CHECKING:
Expand Down
7 changes: 2 additions & 5 deletions qualtran/bloqs/mcmt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,5 @@
from qualtran.bloqs.mcmt.and_bloq import And, MultiAnd
from qualtran.bloqs.mcmt.controlled_via_and import ControlledViaAnd
from qualtran.bloqs.mcmt.ctrl_spec_and import CtrlSpecAnd
from qualtran.bloqs.mcmt.multi_control_multi_target_pauli import (
MultiControlPauli,
MultiControlX,
MultiTargetCNOT,
)
from qualtran.bloqs.mcmt.multi_control_multi_target_pauli import MultiControlPauli, MultiControlX
from qualtran.bloqs.mcmt.multi_target_cnot import MultiTargetCNOT
61 changes: 1 addition & 60 deletions qualtran/bloqs/mcmt/multi_control_multi_target_pauli.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,12 @@
# limitations under the License.

from functools import cached_property
from typing import Dict, Iterator, Set, Tuple, TYPE_CHECKING, Union
from typing import Dict, Set, Tuple, TYPE_CHECKING, Union

import cirq
import numpy as np
import sympy
from attrs import field, frozen
from numpy.typing import NDArray

from qualtran import (
Bloq,
Expand All @@ -44,64 +43,6 @@
from qualtran.simulation.classical_sim import ClassicalValT


@frozen
class MultiTargetCNOT(GateWithRegisters):
r"""Implements single control, multi-target $C[X^{\otimes n}]$ gate.
Implements $|0><0| I + |1><1| X^{\otimes n}$ using a circuit of depth $2\log(n) + 1$
containing only CNOT gates.
References:
[Trading T-gates for dirty qubits in state preparation and unitary synthesis](https://arxiv.org/abs/1812.00954).
Appendix B.1.
"""

bitsize: 'SymbolicInt'

@cached_property
def signature(self) -> Signature:
return Signature.build(control=1, targets=self.bitsize)

def decompose_from_registers(
self,
*,
context: cirq.DecompositionContext,
control: NDArray[cirq.Qid], # type: ignore[type-var]
targets: NDArray[cirq.Qid], # type: ignore[type-var]
):
def cnots_for_depth_i(i: int, q: NDArray[cirq.Qid]) -> Iterator[cirq.OP_TREE]: # type: ignore[type-var]
for c, t in zip(q[: 2**i], q[2**i : min(len(q), 2 ** (i + 1))]):
yield cirq.CNOT(c, t)

depth = len(targets).bit_length()
for i in range(depth):
yield cirq.Moment(cnots_for_depth_i(depth - i - 1, targets))
yield cirq.CNOT(*control, targets[0])
for i in range(depth):
yield cirq.Moment(cnots_for_depth_i(i, targets))

def _circuit_diagram_info_(self, _) -> cirq.CircuitDiagramInfo:
if isinstance(self.bitsize, sympy.Expr):
raise ValueError(f'Symbolic bitsize {self.bitsize} not supported')
return cirq.CircuitDiagramInfo(wire_symbols=["@"] + ["X"] * self.bitsize)


@bloq_example
def _c_multi_not_symb() -> MultiTargetCNOT:
n = sympy.Symbol('n')
c_multi_not_symb = MultiTargetCNOT(bitsize=n)
return c_multi_not_symb


@bloq_example
def _c_multi_not() -> MultiTargetCNOT:
c_multi_not = MultiTargetCNOT(bitsize=5)
return c_multi_not


_C_MULTI_NOT_DOC = BloqDocSpec(bloq_cls=MultiTargetCNOT, examples=(_c_multi_not_symb, _c_multi_not))


@frozen
class MultiControlPauli(GateWithRegisters):
r"""Implements multi-control, single-target C^{n}P gate.
Expand Down
15 changes: 0 additions & 15 deletions qualtran/bloqs/mcmt/multi_control_multi_target_pauli_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
_ccpauli_symb,
MultiControlPauli,
MultiControlX,
MultiTargetCNOT,
)


Expand All @@ -35,20 +34,6 @@ def test_ccpauli_symb():
assert bloq.t_complexity().t == 4 * bloq.n_ctrls - 4


@pytest.mark.parametrize("num_targets", [3, 4, 6, 8, 10])
def test_multi_target_cnot(num_targets):
qubits = cirq.LineQubit.range(num_targets + 1)
naive_circuit = cirq.Circuit(cirq.CNOT(qubits[0], q) for q in qubits[1:])
bloq = MultiTargetCNOT(num_targets)
op = bloq.on(*qubits)
cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent(
cirq.Circuit(op), naive_circuit, atol=1e-6
)
optimal_circuit = cirq.Circuit(cirq.decompose_once(op))
assert len(optimal_circuit) == 2 * np.ceil(np.log2(num_targets)) + 1
qlt_testing.assert_valid_bloq_decomposition(bloq)


@pytest.mark.parametrize("num_controls", [0, 1, 2, *range(7, 17)])
@pytest.mark.parametrize("pauli", [cirq.X, cirq.Y, cirq.Z])
@pytest.mark.parametrize('cv', [0, 1])
Expand Down
81 changes: 81 additions & 0 deletions qualtran/bloqs/mcmt/multi_target_cnot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from functools import cached_property
from typing import Iterator

import cirq
import sympy
from attrs import frozen
from numpy.typing import NDArray

from qualtran import bloq_example, BloqDocSpec, GateWithRegisters, Signature
from qualtran.symbolics import SymbolicInt


@frozen
class MultiTargetCNOT(GateWithRegisters):
r"""Implements single control, multi-target $C[X^{\otimes n}]$ gate.
Implements $|0><0| I + |1><1| X^{\otimes n}$ using a circuit of depth $2\log(n) + 1$
containing only CNOT gates.
References:
[Trading T-gates for dirty qubits in state preparation and unitary synthesis](https://arxiv.org/abs/1812.00954).
Appendix B.1.
"""

bitsize: 'SymbolicInt'

@cached_property
def signature(self) -> Signature:
return Signature.build(control=1, targets=self.bitsize)

def decompose_from_registers(
self,
*,
context: cirq.DecompositionContext,
control: NDArray[cirq.Qid], # type: ignore[type-var]
targets: NDArray[cirq.Qid], # type: ignore[type-var]
):
def cnots_for_depth_i(i: int, q: NDArray[cirq.Qid]) -> Iterator[cirq.OP_TREE]: # type: ignore[type-var]
for c, t in zip(q[: 2**i], q[2**i : min(len(q), 2 ** (i + 1))]):
yield cirq.CNOT(c, t)

depth = len(targets).bit_length()
for i in range(depth):
yield cirq.Moment(cnots_for_depth_i(depth - i - 1, targets))
yield cirq.CNOT(*control, targets[0])
for i in range(depth):
yield cirq.Moment(cnots_for_depth_i(i, targets))

def _circuit_diagram_info_(self, _) -> cirq.CircuitDiagramInfo:
if isinstance(self.bitsize, sympy.Expr):
raise ValueError(f'Symbolic bitsize {self.bitsize} not supported')
return cirq.CircuitDiagramInfo(wire_symbols=["@"] + ["X"] * self.bitsize)


@bloq_example
def _c_multi_not_symb() -> MultiTargetCNOT:
n = sympy.Symbol('n')
c_multi_not_symb = MultiTargetCNOT(bitsize=n)
return c_multi_not_symb


@bloq_example
def _c_multi_not() -> MultiTargetCNOT:
c_multi_not = MultiTargetCNOT(bitsize=5)
return c_multi_not


_C_MULTI_NOT_DOC = BloqDocSpec(bloq_cls=MultiTargetCNOT, examples=(_c_multi_not_symb, _c_multi_not))
41 changes: 41 additions & 0 deletions qualtran/bloqs/mcmt/multi_target_cnot_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import cirq
import numpy as np
import pytest

import qualtran.testing as qlt_testing
from qualtran.bloqs.mcmt.multi_target_cnot import _c_multi_not, _c_multi_not_symb, MultiTargetCNOT


def test_examples(bloq_autotester):
bloq_autotester(_c_multi_not)


def test_symbolic_examples(bloq_autotester):
bloq_autotester(_c_multi_not_symb)


@pytest.mark.parametrize("num_targets", [3, 4, 6, 8, 10])
def test_multi_target_cnot(num_targets):
qubits = cirq.LineQubit.range(num_targets + 1)
naive_circuit = cirq.Circuit(cirq.CNOT(qubits[0], q) for q in qubits[1:])
bloq = MultiTargetCNOT(num_targets)
op = bloq.on(*qubits)
cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent(
cirq.Circuit(op), naive_circuit, atol=1e-6
)
optimal_circuit = cirq.Circuit(cirq.decompose_once(op))
assert len(optimal_circuit) == 2 * np.ceil(np.log2(num_targets)) + 1
qlt_testing.assert_valid_bloq_decomposition(bloq)
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
from qualtran.bloqs.arithmetic import LessThanConstant, LessThanEqual
from qualtran.bloqs.basic_gates import ZPowGate
from qualtran.bloqs.basic_gates.swap import CSwap
from qualtran.bloqs.mcmt import MultiControlPauli, MultiTargetCNOT
from qualtran.bloqs.mcmt.and_bloq import And
from qualtran.bloqs.mcmt.multi_control_multi_target_pauli import MultiControlPauli, MultiTargetCNOT
from qualtran.bloqs.reflections.prepare_identity import PrepareIdentity
from qualtran.bloqs.reflections.reflection_using_prepare import (
_refl_around_zero,
Expand Down
2 changes: 1 addition & 1 deletion qualtran/bloqs/swap_network/cswap_approx.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from qualtran import bloq_example, BloqDocSpec, GateWithRegisters, Signature
from qualtran.bloqs.basic_gates import TGate
from qualtran.bloqs.bookkeeping import ArbitraryClifford
from qualtran.bloqs.mcmt.multi_control_multi_target_pauli import MultiTargetCNOT
from qualtran.bloqs.mcmt.multi_target_cnot import MultiTargetCNOT
from qualtran.resource_counting.generalizers import (
cirq_to_bloqs,
generalize_rotation_angle,
Expand Down
2 changes: 1 addition & 1 deletion qualtran/resource_counting/classify_bloqs.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def bloq_is_clifford(b: Bloq):
ZGate,
)
from qualtran.bloqs.bookkeeping import ArbitraryClifford
from qualtran.bloqs.mcmt.multi_control_multi_target_pauli import MultiTargetCNOT
from qualtran.bloqs.mcmt.multi_target_cnot import MultiTargetCNOT

if isinstance(b, Adjoint):
b = b.subbloq
Expand Down
2 changes: 1 addition & 1 deletion qualtran/serialization/resolver_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@
"qualtran.bloqs.mcmt.controlled_via_and.ControlledViaAnd": qualtran.bloqs.mcmt.controlled_via_and.ControlledViaAnd,
"qualtran.bloqs.mcmt.multi_control_multi_target_pauli.MultiControlPauli": qualtran.bloqs.mcmt.multi_control_multi_target_pauli.MultiControlPauli,
"qualtran.bloqs.mcmt.multi_control_multi_target_pauli.MultiControlX": qualtran.bloqs.mcmt.multi_control_multi_target_pauli.MultiControlX,
"qualtran.bloqs.mcmt.multi_control_multi_target_pauli.MultiTargetCNOT": qualtran.bloqs.mcmt.multi_control_multi_target_pauli.MultiTargetCNOT,
"qualtran.bloqs.mcmt.multi_target_cnot.MultiTargetCNOT": qualtran.bloqs.mcmt.multi_target_cnot.MultiTargetCNOT,
"qualtran.bloqs.mean_estimation.complex_phase_oracle.ComplexPhaseOracle": qualtran.bloqs.mean_estimation.complex_phase_oracle.ComplexPhaseOracle,
"qualtran.bloqs.mean_estimation.mean_estimation_operator.MeanEstimationOperator": qualtran.bloqs.mean_estimation.mean_estimation_operator.MeanEstimationOperator,
"qualtran.bloqs.multiplexers.apply_gate_to_lth_target.ApplyGateToLthQubit": qualtran.bloqs.multiplexers.apply_gate_to_lth_target.ApplyGateToLthQubit,
Expand Down

0 comments on commit 211c6ce

Please sign in to comment.