Skip to content

Commit

Permalink
Merge branch 'main' into deps
Browse files Browse the repository at this point in the history
  • Loading branch information
charlesyuan314 authored Jul 19, 2024
2 parents 98b4bfe + 7eac4ff commit c0c9d81
Show file tree
Hide file tree
Showing 9 changed files with 262 additions and 23 deletions.
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'.
# 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

0 comments on commit c0c9d81

Please sign in to comment.