From 3625c9b6b30c71d45a85fc6ecce58764cb1a777b Mon Sep 17 00:00:00 2001 From: Matthew Harrigan Date: Thu, 16 May 2024 13:43:05 -0700 Subject: [PATCH 1/6] SwapWithZero --- qualtran/bloqs/swap_network/swap_with_zero.py | 10 +- .../bloqs/swap_network/swap_with_zero_test.py | 102 +++++++++--------- 2 files changed, 53 insertions(+), 59 deletions(-) diff --git a/qualtran/bloqs/swap_network/swap_with_zero.py b/qualtran/bloqs/swap_network/swap_with_zero.py index 77ac6868f..0e3df062e 100644 --- a/qualtran/bloqs/swap_network/swap_with_zero.py +++ b/qualtran/bloqs/swap_network/swap_with_zero.py @@ -16,16 +16,15 @@ from typing import Dict, Iterable, Set, Tuple, TYPE_CHECKING, Union import attrs -import cirq import numpy as np from numpy.typing import NDArray from qualtran import ( + Bloq, bloq_example, BloqBuilder, BloqDocSpec, BoundedQUInt, - GateWithRegisters, QAny, Register, Signature, @@ -50,7 +49,7 @@ def _to_tuple(x: Union[SymbolicInt, Iterable[SymbolicInt]]) -> Tuple[SymbolicInt @attrs.frozen -class SwapWithZero(GateWithRegisters): +class SwapWithZero(Bloq): r"""Swaps $|\Psi_0\rangle$ with $|\Psi_x\rangle$ if selection register stores index `x`. Implements the unitary @@ -155,11 +154,6 @@ def build_call_graph(self, ssa: 'SympySymbolAllocator') -> Set['BloqCountT']: num_swaps = prod(*[x for x in self.n_target_registers]) - 1 return {(CSwapApprox(self.target_bitsize), num_swaps)} - def _circuit_diagram_info_(self, args) -> cirq.CircuitDiagramInfo: - from qualtran.cirq_interop._bloq_to_cirq import _wire_symbol_to_cirq_diagram_info - - return _wire_symbol_to_cirq_diagram_info(self, args) - def wire_symbol(self, reg: Register, idx: Tuple[int, ...] = tuple()) -> 'WireSymbol': if reg is None: return super().wire_symbol(reg, idx) diff --git a/qualtran/bloqs/swap_network/swap_with_zero_test.py b/qualtran/bloqs/swap_network/swap_with_zero_test.py index 9f4df1f93..9c8039dd8 100644 --- a/qualtran/bloqs/swap_network/swap_with_zero_test.py +++ b/qualtran/bloqs/swap_network/swap_with_zero_test.py @@ -76,27 +76,27 @@ def test_swap_with_zero_cirq_gate_diagram(): cirq.testing.assert_has_diagram( cirq.Circuit(gh.operation, cirq.decompose_once(gh.operation)), """ -selection0: ──────@(r⇋0)─────────────────────────────────────── +selection0: ──────@(r⇋0)──────────────────────── │ -selection1: ──────@(r⇋0)───────────────────────────@(approx)─── - │ │ -selection2: ──────@(r⇋0)───@(approx)───@(approx)───┼─────────── - │ │ │ │ -targets[0][0]: ───swap_0───×(x)────────┼───────────×(x)──────── - │ │ │ │ -targets[0][1]: ───swap_0───×(x)────────┼───────────×(x)──────── - │ │ │ │ -targets[1][0]: ───swap_1───×(y)────────┼───────────┼─────────── - │ │ │ │ -targets[1][1]: ───swap_1───×(y)────────┼───────────┼─────────── - │ │ │ -targets[2][0]: ───swap_2───────────────×(x)────────×(y)──────── - │ │ │ -targets[2][1]: ───swap_2───────────────×(x)────────×(y)──────── - │ │ -targets[3][0]: ───swap_3───────────────×(y)──────────────────── - │ │ -targets[3][1]: ───swap_3───────────────×(y)──────────────────── +selection1: ──────@(r⇋0)─────────────────@────── + │ │ +selection2: ──────@(r⇋0)───@──────@──────┼────── + │ │ │ │ +targets[0][0]: ───swap_0───×(x)───┼──────×(x)─── + │ │ │ │ +targets[0][1]: ───swap_0───×(x)───┼──────×(x)─── + │ │ │ │ +targets[1][0]: ───swap_1───×(y)───┼──────┼────── + │ │ │ │ +targets[1][1]: ───swap_1───×(y)───┼──────┼────── + │ │ │ +targets[2][0]: ───swap_2──────────×(x)───×(y)─── + │ │ │ +targets[2][1]: ───swap_2──────────×(x)───×(y)─── + │ │ +targets[3][0]: ───swap_3──────────×(y)────────── + │ │ +targets[3][1]: ───swap_3──────────×(y)────────── """, ) @@ -107,37 +107,37 @@ def test_swap_with_zero_cirq_gate_diagram_multi_dim(): cirq.testing.assert_has_diagram( cirq.Circuit(gh.operation, cirq.decompose_once(gh.operation)), """ - ┌──────────────────┐ -selection0_0: ───────@(r⇋0)────────────────────────────────────────────────────@(approx)─── - │ │ -selection0_1: ───────@(r⇋0)──────────────────────────────@(approx)─────────────┼─────────── - │ │ │ -selection1_: ────────@(r⇋0)─────@(approx)───@(approx)────┼────────@(approx)────┼─────────── - │ │ │ │ │ │ -targets[0, 0][0]: ───swap_0_0───×(x)────────┼────────────×(x)─────┼────────────×(x)──────── - │ │ │ │ │ │ -targets[0, 0][1]: ───swap_0_0───×(x)────────┼────────────×(x)─────┼────────────×(x)──────── - │ │ │ │ │ │ -targets[0, 1][0]: ───swap_0_1───×(y)────────┼────────────┼────────┼────────────┼─────────── - │ │ │ │ │ │ -targets[0, 1][1]: ───swap_0_1───×(y)────────┼────────────┼────────┼────────────┼─────────── - │ │ │ │ │ -targets[1, 0][0]: ───swap_1_0───────────────×(x)─────────×(y)─────┼────────────┼─────────── - │ │ │ │ │ -targets[1, 0][1]: ───swap_1_0───────────────×(x)─────────×(y)─────┼────────────┼─────────── - │ │ │ │ -targets[1, 1][0]: ───swap_1_1───────────────×(y)──────────────────┼────────────┼─────────── - │ │ │ │ -targets[1, 1][1]: ───swap_1_1───────────────×(y)──────────────────┼────────────┼─────────── - │ │ │ -targets[2, 0][0]: ───swap_2_0─────────────────────────────────────×(x)─────────×(y)──────── - │ │ │ -targets[2, 0][1]: ───swap_2_0─────────────────────────────────────×(x)─────────×(y)──────── - │ │ -targets[2, 1][0]: ───swap_2_1─────────────────────────────────────×(y)───────────────────── - │ │ -targets[2, 1][1]: ───swap_2_1─────────────────────────────────────×(y)───────────────────── - └──────────────────┘ + ┌────────┐ +selection0_0: ───────@(r⇋0)────────────────────────────────@────── + │ │ +selection0_1: ───────@(r⇋0)────────────────────@───────────┼────── + │ │ │ +selection1_: ────────@(r⇋0)─────@──────@───────┼───@───────┼────── + │ │ │ │ │ │ +targets[0, 0][0]: ───swap_0_0───×(x)───┼───────×(x)┼───────×(x)─── + │ │ │ │ │ │ +targets[0, 0][1]: ───swap_0_0───×(x)───┼───────×(x)┼───────×(x)─── + │ │ │ │ │ │ +targets[0, 1][0]: ───swap_0_1───×(y)───┼───────┼───┼───────┼────── + │ │ │ │ │ │ +targets[0, 1][1]: ───swap_0_1───×(y)───┼───────┼───┼───────┼────── + │ │ │ │ │ +targets[1, 0][0]: ───swap_1_0──────────×(x)────×(y)┼───────┼────── + │ │ │ │ │ +targets[1, 0][1]: ───swap_1_0──────────×(x)────×(y)┼───────┼────── + │ │ │ │ +targets[1, 1][0]: ───swap_1_1──────────×(y)────────┼───────┼────── + │ │ │ │ +targets[1, 1][1]: ───swap_1_1──────────×(y)────────┼───────┼────── + │ │ │ +targets[2, 0][0]: ───swap_2_0──────────────────────×(x)────×(y)─── + │ │ │ +targets[2, 0][1]: ───swap_2_0──────────────────────×(x)────×(y)─── + │ │ +targets[2, 1][0]: ───swap_2_1──────────────────────×(y)─────────── + │ │ +targets[2, 1][1]: ───swap_2_1──────────────────────×(y)─────────── + └────────┘ """, ) From 82a996ad6519f25a660de2a34bbeb7e9343b0158 Mon Sep 17 00:00:00 2001 From: Matthew Harrigan Date: Thu, 16 May 2024 13:57:18 -0700 Subject: [PATCH 2/6] t_complexity testing can use bloq decomps --- qualtran/cirq_interop/testing.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/qualtran/cirq_interop/testing.py b/qualtran/cirq_interop/testing.py index c8c31c69c..34428b838 100644 --- a/qualtran/cirq_interop/testing.py +++ b/qualtran/cirq_interop/testing.py @@ -20,7 +20,7 @@ import numpy as np from numpy.typing import NDArray -from qualtran import Bloq, Signature +from qualtran import Bloq, DecomposeNotImplementedError, DecomposeTypeError, Signature from qualtran._infra.gate_with_registers import get_named_qubits, merge_qubits from qualtran.cirq_interop import t_complexity_protocol from qualtran.cirq_interop.decompose_protocol import _decompose_once_considering_known_decomposition @@ -130,17 +130,17 @@ def assert_decompose_is_consistent_with_t_complexity(val: Any): expected = NotImplemented if t_complexity_method is None else t_complexity_method() if expected is NotImplemented or expected is None: raise AssertionError("No consistent t_complexity: no _t_complexity_.") - decomposition = _decompose_once_considering_known_decomposition(val) - if decomposition is None: - raise AssertionError("No consistent t_complexity: no decomposition.") - from_decomposition = t_complexity_protocol._from_iterable(decomposition) - assert expected == from_decomposition, f'{expected} != {from_decomposition}' - from qualtran import Bloq - from qualtran.bloqs.basic_gates import TGate + if isinstance(val, Bloq): + try: + cbloq = val.decompose_bloq() + except (DecomposeNotImplementedError, DecomposeTypeError) as e: + raise AssertionError("No consistent t_complexity: no decomposition.") from e + from_decomposition = t_complexity_protocol._from_bloq_build_call_graph(cbloq) + else: + decomposition = _decompose_once_considering_known_decomposition(val) + if decomposition is None: + raise AssertionError("No consistent t_complexity: no decomposition.") + from_decomposition = t_complexity_protocol._from_iterable(decomposition) - if not isinstance(val, Bloq): - return - _, sigma = val.call_graph() - actual = sigma.get(TGate(), 0) + sigma.get(TGate(is_adjoint=True), 0) - assert expected.t == actual, f'{expected.t} != {actual}' + assert expected == from_decomposition, f'{expected} != {from_decomposition}' From a615ecd70d7c4ff36354948988ac4c973fa388e0 Mon Sep 17 00:00:00 2001 From: Matthew Harrigan Date: Thu, 16 May 2024 13:58:05 -0700 Subject: [PATCH 3/6] CSwapApprox --- qualtran/bloqs/swap_network/cswap_approx.py | 23 ++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/qualtran/bloqs/swap_network/cswap_approx.py b/qualtran/bloqs/swap_network/cswap_approx.py index 09395917d..9ddbbd4ba 100644 --- a/qualtran/bloqs/swap_network/cswap_approx.py +++ b/qualtran/bloqs/swap_network/cswap_approx.py @@ -13,17 +13,19 @@ # limitations under the License. from functools import cached_property -from typing import Dict, Iterator, Set, TYPE_CHECKING +from typing import Dict, Iterator, Optional, Set, Tuple, TYPE_CHECKING import cirq from attrs import frozen from numpy.typing import NDArray -from qualtran import bloq_example, BloqDocSpec, GateWithRegisters, Signature +from qualtran import Bloq, bloq_example, BloqDocSpec, CompositeBloq, Register, Signature from qualtran.bloqs.basic_gates import TGate from qualtran.bloqs.mcmt.multi_control_multi_target_pauli import MultiTargetCNOT from qualtran.bloqs.util_bloqs import ArbitraryClifford +from qualtran.cirq_interop import decompose_from_cirq_style_method from qualtran.cirq_interop.t_complexity_protocol import TComplexity +from qualtran.drawing import Circle, TextBox, WireSymbol from qualtran.resource_counting.generalizers import ( cirq_to_bloqs, generalize_rotation_angle, @@ -38,7 +40,7 @@ @frozen -class CSwapApprox(GateWithRegisters): +class CSwapApprox(Bloq): r"""Approximately implements a multi-target controlled swap unitary using only $4n$ T-gates. Implements $\mathrm{CSWAP}_n = |0 \rangle\langle 0| I + |1 \rangle\langle 1| \mathrm{SWAP}_n$ @@ -66,6 +68,9 @@ class CSwapApprox(GateWithRegisters): def signature(self) -> Signature: return Signature.build(ctrl=1, x=self.bitsize, y=self.bitsize) + def decompose_bloq(self) -> 'CompositeBloq': + return decompose_from_cirq_style_method(self) + def decompose_from_registers( self, *, context: cirq.DecompositionContext, **quregs: NDArray[cirq.Qid] # type: ignore[type-var] ) -> Iterator[cirq.OP_TREE]: @@ -114,6 +119,18 @@ def _circuit_diagram_info_(self, args: cirq.CircuitDiagramInfoArgs) -> cirq.Circ ("@(approx)",) + ("×(x)",) * self.bitsize + ("×(y)",) * self.bitsize ) + def wire_symbol( + self, reg: Optional['Register'], idx: Tuple[int, ...] = tuple() + ) -> 'WireSymbol': + if reg is None: + return TextBox("approx") + if reg.name == 'ctrl': + return Circle() + if reg.name == 'x': + return TextBox("×(x)") + if reg.name == 'y': + return TextBox("×(y)") + def build_call_graph(self, ssa: 'SympySymbolAllocator') -> Set['BloqCountT']: n = self.bitsize # 4 * n: G gates, each wth 1 T and 4 single qubit cliffords From ea52d7a9cdec3414bb599d26d60687387766c0ee Mon Sep 17 00:00:00 2001 From: Matthew Harrigan Date: Thu, 16 May 2024 13:58:21 -0700 Subject: [PATCH 4/6] style --- qualtran/_infra/gate_with_registers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qualtran/_infra/gate_with_registers.py b/qualtran/_infra/gate_with_registers.py index bf42cba1d..1f1488330 100644 --- a/qualtran/_infra/gate_with_registers.py +++ b/qualtran/_infra/gate_with_registers.py @@ -285,7 +285,7 @@ def decompose_bloq(self) -> 'CompositeBloq': - `build_composite_bloq` raises a `DecomposeNotImplementedError` and - `decompose_from_registers` raises a `DecomposeNotImplementedError`. """ - from qualtran.cirq_interop._cirq_to_bloq import decompose_from_cirq_style_method + from qualtran.cirq_interop import decompose_from_cirq_style_method try: return Bloq.decompose_bloq(self) From 8c72047518969ea0a6daf77814b89ac8482e1fc1 Mon Sep 17 00:00:00 2001 From: Matthew Harrigan Date: Thu, 16 May 2024 14:44:50 -0700 Subject: [PATCH 5/6] mypy --- qualtran/bloqs/swap_network/cswap_approx.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qualtran/bloqs/swap_network/cswap_approx.py b/qualtran/bloqs/swap_network/cswap_approx.py index 9ddbbd4ba..8cde9abec 100644 --- a/qualtran/bloqs/swap_network/cswap_approx.py +++ b/qualtran/bloqs/swap_network/cswap_approx.py @@ -130,6 +130,7 @@ def wire_symbol( return TextBox("×(x)") if reg.name == 'y': return TextBox("×(y)") + raise ValueError(f"Unknown register {reg}.") def build_call_graph(self, ssa: 'SympySymbolAllocator') -> Set['BloqCountT']: n = self.bitsize From e2945fac7b90ce502c9d32647353b21cad32abae Mon Sep 17 00:00:00 2001 From: Matthew Harrigan Date: Tue, 6 Aug 2024 10:19:10 -0400 Subject: [PATCH 6/6] lint --- qualtran/bloqs/swap_network/cswap_approx.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qualtran/bloqs/swap_network/cswap_approx.py b/qualtran/bloqs/swap_network/cswap_approx.py index 5c5cd63b1..937183929 100644 --- a/qualtran/bloqs/swap_network/cswap_approx.py +++ b/qualtran/bloqs/swap_network/cswap_approx.py @@ -24,7 +24,6 @@ from qualtran.bloqs.bookkeeping import ArbitraryClifford from qualtran.bloqs.mcmt.multi_control_multi_target_pauli import MultiTargetCNOT from qualtran.cirq_interop import decompose_from_cirq_style_method -from qualtran.cirq_interop.t_complexity_protocol import TComplexity from qualtran.drawing import Circle, TextBox, WireSymbol from qualtran.resource_counting.generalizers import ( cirq_to_bloqs,