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

Simplify QubitizationWalkOperator: remove specializations for control/adjoint and use defaults instead #1481

Merged
merged 18 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from 15 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
27 changes: 15 additions & 12 deletions qualtran/bloqs/basic_gates/swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,19 +105,15 @@ def adjoint(self) -> 'Bloq':
return self

def get_ctrl_system(self, ctrl_spec: 'CtrlSpec') -> tuple['Bloq', 'AddControlledT']:
if ctrl_spec != CtrlSpec():
return super().get_ctrl_system(ctrl_spec=ctrl_spec)

cswap = TwoBitCSwap()

def adder(
bb: 'BloqBuilder', ctrl_soqs: Sequence['SoquetT'], in_soqs: Dict[str, 'SoquetT']
) -> Tuple[Iterable['SoquetT'], Iterable['SoquetT']]:
(ctrl,) = ctrl_soqs
ctrl, x, y = bb.add(cswap, ctrl=ctrl, x=in_soqs['x'], y=in_soqs['y'])
return [ctrl], [x, y]
from qualtran.bloqs.mcmt.specialized_ctrl import get_ctrl_system_1bit_cv_from_bloqs

return cswap, adder
return get_ctrl_system_1bit_cv_from_bloqs(
self,
ctrl_spec,
current_ctrl_bit=None,
bloq_with_ctrl=TwoBitCSwap(),
ctrl_reg_name='ctrl',
)


@bloq_example
Expand Down Expand Up @@ -201,6 +197,13 @@ def wire_symbol(self, reg: Optional['Register'], idx: Tuple[int, ...] = ()) -> '
else:
return TextBox('×')

def get_ctrl_system(self, ctrl_spec: 'CtrlSpec') -> tuple['Bloq', 'AddControlledT']:
from qualtran.bloqs.mcmt.specialized_ctrl import get_ctrl_system_1bit_cv_from_bloqs

return get_ctrl_system_1bit_cv_from_bloqs(
self, ctrl_spec, current_ctrl_bit=1, bloq_with_ctrl=self, ctrl_reg_name='ctrl'
)


@bloq_example
def _cswap_bit() -> TwoBitCSwap:
Expand Down
7 changes: 7 additions & 0 deletions qualtran/bloqs/basic_gates/y_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,13 @@ def wire_symbol(
return TextBox('Y')
raise ValueError(f"Unknown register {reg}.")

def get_ctrl_system(self, ctrl_spec: 'CtrlSpec') -> Tuple['Bloq', 'AddControlledT']:
from qualtran.bloqs.mcmt.specialized_ctrl import get_ctrl_system_1bit_cv_from_bloqs

return get_ctrl_system_1bit_cv_from_bloqs(
self, ctrl_spec, current_ctrl_bit=1, bloq_with_ctrl=self, ctrl_reg_name='ctrl'
)


@bloq_example
def _cy_gate() -> CYGate:
Expand Down
7 changes: 7 additions & 0 deletions qualtran/bloqs/basic_gates/z_basis.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,13 @@ def wire_symbol(self, reg: Optional[Register], idx: Tuple[int, ...] = tuple()) -
return Circle()
raise ValueError(f'Unknown wire symbol register name: {reg.name}')

def get_ctrl_system(self, ctrl_spec: 'CtrlSpec') -> Tuple['Bloq', 'AddControlledT']:
from qualtran.bloqs.mcmt.specialized_ctrl import get_ctrl_system_1bit_cv_from_bloqs

return get_ctrl_system_1bit_cv_from_bloqs(
self, ctrl_spec, current_ctrl_bit=1, bloq_with_ctrl=self, ctrl_reg_name='q1'
)


@bloq_example
def _cz() -> CZ:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ class SelectHubbard(SelectOracle):
def __attrs_post_init__(self):
if self.x_dim != self.y_dim:
raise NotImplementedError("Currently only supports the case where x_dim=y_dim.")
if self.control_val == 0:
raise ValueError("control_val=0 not supported")
anurudhp marked this conversation as resolved.
Show resolved Hide resolved

@cached_property
def control_registers(self) -> Tuple[Register, ...]:
Expand Down Expand Up @@ -191,18 +193,24 @@ def __str__(self):
return s

def get_ctrl_system(self, ctrl_spec: 'CtrlSpec') -> Tuple['Bloq', 'AddControlledT']:
from qualtran.bloqs.mcmt.specialized_ctrl import get_ctrl_system_1bit_cv
from qualtran.bloqs.mcmt.specialized_ctrl import get_ctrl_system_1bit_cv_from_bloqs

return get_ctrl_system_1bit_cv(
return get_ctrl_system_1bit_cv_from_bloqs(
self,
ctrl_spec=ctrl_spec,
current_ctrl_bit=self.control_val,
get_ctrl_bloq_and_ctrl_reg_name=lambda cv: (
attrs.evolve(self, control_val=cv),
'control',
),
bloq_with_ctrl=attrs.evolve(self, control_val=1),
ctrl_reg_name='control',
)

def adjoint(self) -> 'Bloq':
from qualtran.bloqs.mcmt.specialized_ctrl import (
AdjointWithSpecializedCtrl,
SpecializeOnCtrlBit,
)

return AdjointWithSpecializedCtrl(self, specialize_on_ctrl=SpecializeOnCtrlBit.ONE)


@bloq_example
def _sel_hubb() -> SelectHubbard:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@
# 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 unittest.mock import ANY

import pytest

from qualtran.bloqs.chemistry.hubbard_model.qubitization.select_hubbard import (
_sel_hubb,
SelectHubbard,
)
from qualtran.cirq_interop.t_complexity_protocol import t_complexity
from qualtran.resource_counting import GateCounts, get_cost_value, QECGatesCost


def test_sel_hubb_auto(bloq_autotester):
Expand All @@ -28,8 +29,19 @@ def test_sel_hubb_auto(bloq_autotester):
@pytest.mark.parametrize('dim', [*range(2, 10)])
def test_select_t_complexity(dim):
select = SelectHubbard(x_dim=dim, y_dim=dim, control_val=1)
cost = t_complexity(select)
cost = get_cost_value(select, QECGatesCost())
N = 2 * dim * dim
logN = 2 * (dim - 1).bit_length() + 1
assert cost.t == 10 * N + 14 * logN - 8
assert cost.rotations == 0
assert cost == GateCounts(
cswap=2 * logN, and_bloq=5 * (N // 2) - 2, measurement=5 * (N // 2) - 2, clifford=ANY
)
assert cost.total_t_count() == 10 * N + 14 * logN - 8


def test_adjoint_controlled():
bloq = _sel_hubb()

adj_ctrl_bloq = bloq.controlled().adjoint()
ctrl_adj_bloq = bloq.adjoint().controlled()

assert adj_ctrl_bloq == ctrl_adj_bloq
201 changes: 201 additions & 0 deletions qualtran/bloqs/mcmt/specialized_ctrl.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "0",
"metadata": {},
"source": [
"## Bloqs with specialized controlled implementations\n",
"\n",
"In some cases, a bloq may have a specialized singly-controlled version (e.g. `LCUBlockEncoding`).\n",
"Qualtran provides a convenience methods `get_ctrl_system_1bit_cv` and `get_ctrl_system_1bit_cv_from_bloqs` to override the `get_ctrl_system`. These methods ensure that multiply-controlled bloqs are correctly reduced to the provided singly-controlled variants.\n",
"\n",
"- `get_ctrl_system_1bit_cv_from_bloqs` - Override when a specialized controlled-by-1 implementation is available.\n",
"- `get_ctrl_system_1bit_cv` - Override when both specialized controlled-by-1 and controlled-by-0 implementations are available.\n",
"\n",
"The following demonstrates an example for a bloq implementing $T^\\dagger X T$, where the controlled version only needs to control the $X$."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1",
"metadata": {},
"outputs": [],
"source": [
"import attrs\n",
"from qualtran import Bloq, BloqBuilder, Soquet, SoquetT, Signature, CtrlSpec, AddControlledT\n",
"from qualtran.bloqs.basic_gates import TGate, XGate, CNOT\n",
"\n",
"\n",
"@attrs.frozen\n",
"class BloqWithSpecializedCtrl(Bloq):\n",
" \"\"\"Bloq implementing $T^\\dagger X T$\"\"\"\n",
" is_controlled: bool = False\n",
"\n",
" @property\n",
" def signature(self) -> 'Signature':\n",
" n_ctrls = 1 if self.is_controlled else 0\n",
" return Signature.build(ctrl=n_ctrls, q=1)\n",
" \n",
" def build_composite_bloq(self, bb: 'BloqBuilder', q: 'Soquet', **soqs) -> dict[str, 'SoquetT']:\n",
" ctrl = soqs.pop('ctrl', None)\n",
" \n",
" q = bb.add(TGate(), q=q)\n",
" if self.is_controlled:\n",
" ctrl, q = bb.add(CNOT(), ctrl=ctrl, target=q)\n",
" else:\n",
" ctrl, q = bb.add(XGate(), ctrl=ctrl, target=q)\n",
" q = bb.add(TGate().adjoint(), q=q)\n",
" \n",
" out_soqs = {'q': q}\n",
" if ctrl:\n",
" out_soqs |= {'ctrl': ctrl}\n",
" return out_soqs\n",
" \n",
" def get_ctrl_system(self, ctrl_spec: 'CtrlSpec') -> tuple['Bloq', 'AddControlledT']:\n",
" from qualtran.bloqs.mcmt.specialized_ctrl import get_ctrl_system_1bit_cv_from_bloqs\n",
"\n",
" return get_ctrl_system_1bit_cv_from_bloqs(\n",
" self,\n",
" ctrl_spec,\n",
" current_ctrl_bit=1 if self.is_controlled else None,\n",
" bloq_with_ctrl=attrs.evolve(self, is_controlled=True),\n",
" ctrl_reg_name='ctrl',\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2",
"metadata": {},
"outputs": [],
"source": [
"from qualtran.drawing import show_bloq, show_call_graph\n",
"\n",
"bloq = BloqWithSpecializedCtrl().controlled().controlled()\n",
"show_bloq(bloq.decompose_bloq().flatten())\n",
"show_call_graph(bloq)"
]
},
{
"cell_type": "markdown",
"id": "3",
"metadata": {},
"source": [
"## Propagating the Adjoint\n",
"\n",
"In the above bloq, calling controlled on the adjoint does not push the controls into the bloq, and therefore does not use the specialized implementation provided."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4",
"metadata": {},
"outputs": [],
"source": [
"BloqWithSpecializedCtrl().adjoint().controlled()"
]
},
{
"cell_type": "markdown",
"id": "5",
"metadata": {},
"source": [
"This can be fixed by overriding the adjoint using a special wrapper for this case - `AdjointWithSpecializedCtrl`. This is a subclass of the default `Adjoint` metabloq, and ensures that single-qubit controls are pushed into the underlying bloq."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6",
"metadata": {},
"outputs": [],
"source": [
"@attrs.frozen\n",
"class BloqWithSpecializedCtrlWithAdjoint(Bloq):\n",
" \"\"\"Bloq implementing $T^\\dagger X T$\"\"\"\n",
" is_controlled: bool = False\n",
"\n",
" @property\n",
" def signature(self) -> 'Signature':\n",
" n_ctrls = 1 if self.is_controlled else 0\n",
" return Signature.build(ctrl=n_ctrls, q=1)\n",
" \n",
" def build_composite_bloq(self, bb: 'BloqBuilder', q: 'Soquet', **soqs) -> dict[str, 'SoquetT']:\n",
" ctrl = soqs.pop('ctrl', None)\n",
" \n",
" q = bb.add(TGate(), q=q)\n",
" if self.is_controlled:\n",
" ctrl, q = bb.add(CNOT(), ctrl=ctrl, target=q)\n",
" else:\n",
" ctrl, q = bb.add(XGate(), ctrl=ctrl, target=q)\n",
" q = bb.add(TGate().adjoint(), q=q)\n",
" \n",
" out_soqs = {'q': q}\n",
" if ctrl:\n",
" out_soqs |= {'ctrl': ctrl}\n",
" return out_soqs\n",
" \n",
" def get_ctrl_system(self, ctrl_spec: 'CtrlSpec') -> tuple['Bloq', 'AddControlledT']:\n",
" from qualtran.bloqs.mcmt.specialized_ctrl import get_ctrl_system_1bit_cv_from_bloqs\n",
"\n",
" return get_ctrl_system_1bit_cv_from_bloqs(\n",
" self,\n",
" ctrl_spec,\n",
" current_ctrl_bit=1 if self.is_controlled else None,\n",
" bloq_with_ctrl=attrs.evolve(self, is_controlled=True),\n",
" ctrl_reg_name='ctrl',\n",
" )\n",
"\n",
" def adjoint(self):\n",
" from qualtran.bloqs.mcmt.specialized_ctrl import AdjointWithSpecializedCtrl, SpecializeOnCtrlBit\n",
" \n",
" return AdjointWithSpecializedCtrl(self, SpecializeOnCtrlBit.ONE)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7",
"metadata": {},
"outputs": [],
"source": [
"BloqWithSpecializedCtrlWithAdjoint().adjoint().controlled()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8",
"metadata": {},
"outputs": [],
"source": [
"assert BloqWithSpecializedCtrlWithAdjoint().adjoint().controlled() == BloqWithSpecializedCtrlWithAdjoint(is_controlled=True).adjoint()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.9"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Loading
Loading