Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CHadamard #1114

Merged
merged 6 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion dev_tools/autogenerate-bloqs-notebooks-v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,10 @@
NotebookSpecV2(
title='Hadamard',
module=qualtran.bloqs.basic_gates.hadamard,
bloq_specs=[qualtran.bloqs.basic_gates.hadamard._HADAMARD_DOC],
bloq_specs=[
qualtran.bloqs.basic_gates.hadamard._HADAMARD_DOC,
qualtran.bloqs.basic_gates.hadamard._CHADAMARD_DOC,
],
),
NotebookSpecV2(
title='CNOT',
Expand Down
2 changes: 1 addition & 1 deletion qualtran/bloqs/basic_gates/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

from .cnot import CNOT
from .global_phase import GlobalPhase
from .hadamard import Hadamard
from .hadamard import CHadamard, Hadamard
from .identity import Identity
from .on_each import OnEach
from .power import Power
Expand Down
115 changes: 104 additions & 11 deletions qualtran/bloqs/basic_gates/hadamard.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -109,38 +109,131 @@
},
{
"cell_type": "markdown",
"id": "6dc303ab",
"id": "fbc791ac",
"metadata": {
"cq.autogen": "Hadamard.call_graph.md"
"cq.autogen": "CHadamard.bloq_doc.md"
},
"source": [
"### Call Graph"
"## `CHadamard`\n",
"The controlled Hadamard gate\n",
"\n",
"#### Registers\n",
" - `ctrl`: The control qubit.\n",
" - `q`: The target qubit.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ce21798b",
"metadata": {
"cq.autogen": "CHadamard.bloq_doc.py"
},
"outputs": [],
"source": [
"from qualtran.bloqs.basic_gates import CHadamard"
]
},
{
"cell_type": "markdown",
"id": "01eb9903",
"metadata": {
"cq.autogen": "CHadamard.example_instances.md"
},
"source": [
"### Example Instances"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6e79299e",
"metadata": {
"cq.autogen": "CHadamard.chadamard"
},
"outputs": [],
"source": [
"chadamard = Hadamard().controlled()\n",
"assert isinstance(chadamard, CHadamard)"
]
},
{
"cell_type": "markdown",
"id": "47b363a1",
"metadata": {
"cq.autogen": "CHadamard.graphical_signature.md"
},
"source": [
"#### Graphical Signature"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "19f5aeb1",
"id": "01cc88a3",
"metadata": {
"cq.autogen": "Hadamard.call_graph.py"
"cq.autogen": "CHadamard.graphical_signature.py"
},
"outputs": [],
"source": [
"from qualtran.resource_counting.generalizers import ignore_split_join\n",
"hadamard_g, hadamard_sigma = hadamard.call_graph(max_depth=1, generalizer=ignore_split_join)\n",
"show_call_graph(hadamard_g)\n",
"show_counts_sigma(hadamard_sigma)"
"from qualtran.drawing import show_bloqs\n",
"show_bloqs([chadamard],\n",
" ['`chadamard`'])"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cf5fe532-b44b-4570-a52c-cd915fb8bf3d",
"metadata": {},
"outputs": [],
"source": [
"show_bloq(chadamard, 'musical_score')"
]
},
{
"cell_type": "markdown",
"id": "bd22a519-40bd-4275-8fb1-e4981e6ac4d9",
"metadata": {},
"source": [
"### Specialty circuits\n",
"\n",
"The `CHadamard` bloq is atomic and cannot be decomposed with `.decompose_bloq()`. An actual implementation on an error-corrected quantum computer will likely be architecture-dependent. A naive circuit for CHadamard can be found using Cirq."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5608b887-c3c8-49db-858a-d06df9005078",
"metadata": {},
"outputs": [],
"source": [
"circuit = cirq.Circuit(cirq.decompose_multi_controlled_rotation(\n",
" cirq.unitary(cirq.H),\n",
" controls=[cirq.NamedQubit('ctrl')],\n",
" target=cirq.NamedQubit('q'),\n",
"))\n",
"circuit"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python"
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.8"
}
},
"nbformat": 4,
Expand Down
107 changes: 100 additions & 7 deletions qualtran/bloqs/basic_gates/hadamard.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,27 @@
# limitations under the License.

from functools import cached_property
from typing import Dict, List, Optional, Tuple, TYPE_CHECKING
from typing import Dict, Iterable, List, Optional, Sequence, Tuple, TYPE_CHECKING, Union

import numpy as np
from attrs import frozen

from qualtran import (
AddControlledT,
Bloq,
bloq_example,
BloqBuilder,
BloqDocSpec,
CompositeBloq,
ConnectionT,
CtrlSpec,
DecomposeTypeError,
Register,
Signature,
SoquetT,
)
from qualtran.cirq_interop.t_complexity_protocol import TComplexity
from qualtran.drawing import Text, TextBox, WireSymbol
from qualtran.drawing import Circle, Text, TextBox, WireSymbol

if TYPE_CHECKING:
import cirq
Expand Down Expand Up @@ -76,6 +80,23 @@ def my_tensors(
)
]

def get_ctrl_system(
self, ctrl_spec: Optional['CtrlSpec'] = None
) -> Tuple['Bloq', 'AddControlledT']:
if not (ctrl_spec is None or ctrl_spec == CtrlSpec()):
return super().get_ctrl_system(ctrl_spec=ctrl_spec)

bloq = CHadamard()

def _add_ctrled(
bb: 'BloqBuilder', ctrl_soqs: Sequence['SoquetT'], in_soqs: Dict[str, 'SoquetT']
) -> Tuple[Iterable['SoquetT'], Iterable['SoquetT']]:
(ctrl,) = ctrl_soqs
ctrl, q = bb.add(bloq, ctrl=ctrl, target=in_soqs['q'])
return ((ctrl,), (q,))

return bloq, _add_ctrled

def as_cirq_op(
self, qubit_manager: 'cirq.QubitManager', q: 'CirqQuregT' # type: ignore[type-var]
) -> Tuple['cirq.Operation', Dict[str, 'CirqQuregT']]: # type: ignore[type-var]
Expand All @@ -102,8 +123,80 @@ def _hadamard() -> Hadamard:
return hadamard


_HADAMARD_DOC = BloqDocSpec(
bloq_cls=Hadamard,
import_line='from qualtran.bloqs.basic_gates import Hadamard',
examples=[_hadamard],
)
_HADAMARD_DOC = BloqDocSpec(bloq_cls=Hadamard, examples=[_hadamard], call_graph_example=None)


@frozen
class CHadamard(Bloq):
r"""The controlled Hadamard gate

Registers:
ctrl: The control qubit.
target: The target qubit.
"""

@cached_property
def signature(self) -> 'Signature':
return Signature.build(ctrl=1, target=1)

def decompose_bloq(self) -> 'CompositeBloq':
raise DecomposeTypeError(f"{self} is atomic")

def adjoint(self) -> 'Bloq':
return self

def my_tensors(
self, incoming: Dict[str, 'ConnectionT'], outgoing: Dict[str, 'ConnectionT']
) -> List['qtn.Tensor']:
import quimb.tensor as qtn

unitary = np.eye(4, dtype=np.complex128).reshape((2, 2, 2, 2))
# Use these inds orderings to set the block where ctrl=1 to the desired gate.
inds = [
(outgoing['ctrl'], 0),
(outgoing['target'], 0),
(incoming['ctrl'], 0),
(incoming['target'], 0),
]
unitary[1, :, 1, :] = _HADAMARD

return [qtn.Tensor(data=unitary, inds=inds, tags=[str(self)])]

def as_cirq_op(
self, qubit_manager: 'cirq.QubitManager', ctrl: 'CirqQuregT', target: 'CirqQuregT'
) -> Tuple[Union['cirq.Operation', None], Dict[str, 'CirqQuregT']]:
import cirq

(ctrl,) = ctrl
(target,) = target
return cirq.H.on(target).controlled_by(ctrl), {
'ctrl': np.array([ctrl]),
'target': np.array([target]),
}

def _t_complexity_(self) -> 'TComplexity':
# This is based on the decomposition provided by `cirq.decompose_multi_controlled_rotation`
# which uses three cirq.MatrixGate's to do a controlled version of any single-qubit gate.
# The first MatrixGate happens to be a clifford, Hadamard operation in this case.
# The other two are considered 'rotations'.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please link #237 in the comments so we can update the T-complexity at a later point.

# https://github.com/quantumlib/Qualtran/issues/237
return TComplexity(rotations=2, clifford=4)

def wire_symbol(self, reg: Optional[Register], idx: Tuple[int, ...] = tuple()) -> 'WireSymbol':
if reg is None:
return Text('')
if reg.name == 'ctrl':
return Circle()
if reg.name == 'target':
return TextBox('H')
raise ValueError(f"Unknown register {reg}")


@bloq_example
def _chadamard() -> CHadamard:
chadamard = Hadamard().controlled()
assert isinstance(chadamard, CHadamard)
return chadamard


_CHADAMARD_DOC = BloqDocSpec(bloq_cls=CHadamard, examples=[_chadamard], call_graph_example=None)
47 changes: 45 additions & 2 deletions qualtran/bloqs/basic_gates/hadamard_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@
import pytest

from qualtran import BloqBuilder
from qualtran.bloqs.basic_gates import Hadamard, OneState
from qualtran.bloqs.basic_gates.hadamard import _hadamard
from qualtran.bloqs.basic_gates import Hadamard, OneEffect, OneState
from qualtran.bloqs.basic_gates.hadamard import _hadamard, CHadamard
from qualtran.cirq_interop import cirq_gate_to_bloq


def test_to_cirq():
Expand Down Expand Up @@ -47,3 +48,45 @@ def test_not_classical():
h = Hadamard()
with pytest.raises(NotImplementedError, match=r'.*is not classically simulable\.'):
h.call_classically(q=0)


def test_chadamard_vs_cirq():
bloq = Hadamard().controlled()
assert bloq == CHadamard()

gate = cirq.H.controlled()
np.testing.assert_allclose(cirq.unitary(gate), bloq.tensor_contract())


def test_cirq_interop():
circuit = CHadamard().as_composite_bloq().to_cirq_circuit()
should_be = cirq.Circuit(
[cirq.Moment(cirq.H(cirq.NamedQubit('target')).controlled_by(cirq.NamedQubit('ctrl')))]
)
assert circuit == should_be

(op,) = list(should_be.all_operations())
assert op.gate is not None
assert cirq_gate_to_bloq(op.gate) == CHadamard()


def test_active_chadamard_is_hadamard():
bb = BloqBuilder()
q = bb.add_register('q', 1)
ctrl_on = bb.add(OneState())
ctrl_on, q = bb.add(CHadamard(), ctrl=ctrl_on, target=q)
bb.add(OneEffect(), q=ctrl_on)
cbloq = bb.finalize(q=q)

np.testing.assert_allclose(Hadamard().tensor_contract(), cbloq.tensor_contract())


def test_chadamard_adjoint():
bb = BloqBuilder()
ctrl = bb.add_register('ctrl', 1)
q = bb.add_register('q', 1)
ctrl, q = bb.add(CHadamard(), ctrl=ctrl, target=q)
ctrl, q = bb.add(CHadamard().adjoint(), ctrl=ctrl, target=q)
cbloq = bb.finalize(ctrl=ctrl, q=q)

np.testing.assert_allclose(np.eye(4), cbloq.tensor_contract(), atol=1e-12)
3 changes: 2 additions & 1 deletion qualtran/bloqs/multiplexers/apply_lth_bloq_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
Soquet,
)
from qualtran.bloqs.basic_gates import (
CHadamard,
CNOT,
Hadamard,
Identity,
Expand Down Expand Up @@ -74,7 +75,7 @@ def test_bloq_has_consistent_decomposition():
def test_call_graph():
_, sigma = _apply_lth_bloq().call_graph(generalizer=ignore_split_join)
assert sigma == {
Controlled(Hadamard(), CtrlSpec()): 1,
CHadamard(): 1,
Controlled(TGate(), CtrlSpec()): 1,
Controlled(ZGate(), CtrlSpec()): 1,
CNOT(): 4,
Expand Down
2 changes: 2 additions & 0 deletions qualtran/cirq_interop/_cirq_to_bloq.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ def cirq_gate_to_bloq(gate: cirq.Gate) -> Bloq:
"""
from qualtran import Adjoint
from qualtran.bloqs.basic_gates import (
CHadamard,
CNOT,
CSwap,
CZPowGate,
Expand Down Expand Up @@ -367,6 +368,7 @@ def cirq_gate_to_bloq(gate: cirq.Gate) -> Bloq:
cirq.S: SGate(),
cirq.S**-1: SGate().adjoint(),
cirq.H: Hadamard(),
cirq.ControlledGate(cirq.H): CHadamard(),
cirq.CNOT: CNOT(),
cirq.TOFFOLI: Toffoli(),
cirq.X: XGate(),
Expand Down
Loading
Loading