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

Support arbitrary CtrlSpec to single qubit control #1137

Merged
merged 15 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from 11 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
6 changes: 6 additions & 0 deletions dev_tools/autogenerate-bloqs-notebooks-v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
import qualtran.bloqs.factoring.mod_exp
import qualtran.bloqs.hamiltonian_simulation.hamiltonian_simulation_by_gqsp
import qualtran.bloqs.mcmt.and_bloq
import qualtran.bloqs.mcmt.ctrl_spec_and
import qualtran.bloqs.mod_arithmetic.mod_addition
import qualtran.bloqs.multiplexers.apply_gate_to_lth_target
import qualtran.bloqs.multiplexers.apply_lth_bloq
Expand Down Expand Up @@ -200,6 +201,11 @@
qualtran.bloqs.bookkeeping.cast._CAST_DOC,
],
),
NotebookSpecV2(
title='Control Specification (And)',
module=qualtran.bloqs.mcmt.ctrl_spec_and,
bloq_specs=[qualtran.bloqs.mcmt.ctrl_spec_and._CTRLSPEC_AND_DOC],
),
]


Expand Down
1 change: 1 addition & 0 deletions docs/bloqs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Bloqs Library
basic_gates/global_phase.ipynb
basic_gates/identity.ipynb
bookkeeping/bookkeeping.ipynb
mcmt/ctrl_spec_and.ipynb

.. toctree::
:maxdepth: 2
Expand Down
17 changes: 10 additions & 7 deletions qualtran/bloqs/bookkeeping/partition.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import numpy as np
from attrs import evolve, field, frozen, validators
from numpy.typing import NDArray

from qualtran import (
bloq_example,
Expand Down Expand Up @@ -137,22 +138,24 @@ def _classical_partition(self, x: 'ClassicalValT') -> Dict[str, 'ClassicalValT']
start += size
return out_vals

def _classical_unpartition(self, **vals: 'ClassicalValT'):
out_vals = []
def _classical_unpartition_to_bits(self, **vals: 'ClassicalValT') -> NDArray[np.uint]:
out_vals: list[list[int]] = []
for reg in self.regs:
reg_val = vals[reg.name]
if isinstance(reg_val, np.ndarray):
out_vals.append(ints_to_bits(reg_val.ravel(), reg.bitsize).ravel())
out_vals.extend(reg.dtype.to_bits(val) for val in reg_val.ravel())
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why this change? Also, why this change only in the _classical_unpartition and not in _classical_partition ? Was the previous logic wrong for certain cases? Can you please add tests which pass with the new version and fail with the original one?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm using this to convert the cvs to bits in CtrlSpecAnd which allows any dtype. But the current classical simulation protocol only supports integer values in ClassicalValT, so seems like it worked either way.

Copy link
Contributor Author

@anurudhp anurudhp Jul 15, 2024

Choose a reason for hiding this comment

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

Also bits_to_ints / ints_to_bits are faster than manually calling to_bits / from_bits on each element. (similar to #1072). So only changed the part I'm using for now.

We should port ints_to_bits and bits_to_ints into QDType: #811

Copy link
Collaborator

Choose a reason for hiding this comment

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

Sorry, I don't understand. What was the part which was failing earlier and now gets fixed? I presume even now, CtrlSpecAnd would support only QUInt, QInt and QAny types (or, types for which bits_to_ints works correctly) since the classical bits need to be partitioned correctly (right now, partitioning is still based on bits_to_ints but unpartitioning uses dtype.to_bits()) ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

CtrlSpecAnd also works for QFxp as it's just bitwise equality

Copy link
Collaborator

Choose a reason for hiding this comment

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

The decomposition of CtrlSpecAnd is unpartition -> MultiAnd -> partition. For QFxp, you had to update unpartition to correctly convert a QFxp object to bits; and you are suggesting that you don't need to update partition ? The partition needs to correctly combine the bits such that the result is a QFxp object. That would not happen right now and you would get an incorrect result.

Can you add a test for QFxp and try to figure out why it's working (if it's working) because technically it shouldn't

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm using this only for extracting the classical control bits in CtrlSpecAnd (i.e. _flat_cvs), so I never merge these bits back to get the constants.

But this would break the classical simulator, but as the classical sim only supports ints now, we don't have any tests that use Fxp.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think for now, it's better to say that CtrlSpecAnd does not support QFxp, because in reality it does not. The decomposition would be incorrect because Partition and Unpartition doesn't support QFxp. So it's fine to fail early at _flat_cvs instead of having a wrong decomposition.

The alternative would be to compute the _flat_cvs without invoking Partition so that the computation of _flat_cvs works for all data types and update Partition bloq for _classical_unpartition and _classical_partition whenever we are ready to do so.

The current logic leaves the state of things in a complicatedly broken way where one direction works (maybe) but the other doesn't.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I reverted these changes. For now CtrlSpecAnd only supports integer types (checked in post_init).

Will add more support after the classical sim for QFxp is merged.

else:
out_vals.append(ints_to_bits(reg_val, reg.bitsize)[0])
big_int = np.concatenate(out_vals)
return {'x': bits_to_ints(big_int)[0]}
out_vals.append(reg.dtype.to_bits(reg_val))
big_int_bits = np.concatenate(out_vals)
return big_int_bits

def on_classical_vals(self, **vals: 'ClassicalValT') -> Dict[str, 'ClassicalValT']:
if self.partition:
return self._classical_partition(vals['x'])
else:
return self._classical_unpartition(**vals)
big_int_bits = self._classical_unpartition_to_bits(**vals)
(big_int,) = bits_to_ints(big_int_bits)
return {'x': big_int}

def wire_symbol(self, reg: Register, idx: Tuple[int, ...] = tuple()) -> 'WireSymbol':
if reg is None:
Expand Down
1 change: 1 addition & 0 deletions qualtran/bloqs/mcmt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# limitations under the License.

from qualtran.bloqs.mcmt.and_bloq import And, MultiAnd
from qualtran.bloqs.mcmt.ctrl_spec_and import CtrlSpecAnd
from qualtran.bloqs.mcmt.multi_control_multi_target_pauli import (
MultiControlPauli,
MultiControlX,
Expand Down
210 changes: 210 additions & 0 deletions qualtran/bloqs/mcmt/ctrl_spec_and.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "0beec492",
"metadata": {
"cq.autogen": "title_cell"
},
"source": [
"# Control Specification (And)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4d31094e",
"metadata": {
"cq.autogen": "top_imports"
},
"outputs": [],
"source": [
"from qualtran import Bloq, CompositeBloq, BloqBuilder, Signature, Register\n",
"from qualtran import QBit, QInt, QUInt, QAny\n",
"from qualtran.drawing import show_bloq, show_call_graph, show_counts_sigma\n",
"from typing import *\n",
"import numpy as np\n",
"import sympy\n",
"import cirq"
]
},
{
"cell_type": "markdown",
"id": "12aba876",
"metadata": {
"cq.autogen": "CtrlSpecAnd.bloq_doc.md"
},
"source": [
"## `CtrlSpecAnd`\n",
"Computes a single qubit which is `1` iff the CtrlSpec of And clauses is satisfied.\n",
"\n",
"This reduces an arbitrary 'And' clause control spec to a single qubit, which can be used\n",
"to then control a bloq. Therefore, a bloq author is only required to implement a\n",
"single-controlled version of their bloq, and can be generalized to arbitrary controls.\n",
"\n",
"The control registers are passed through as-is. If the same control bit is required for\n",
"multiple bloqs, the user can use the `target` qubit of this bloq multiple times, and only\n",
"uncompute at the very end. For more custom strategies and trade-offs, see Ref. [1].\n",
"\n",
".. note::\n",
"\n",
" This only applies to CtrlSpec being a logical AND of all registers, and each register\n",
" being equal to a constant. See documentation for :class:`CtrlSpec` for more details.\n",
"\n",
"#### Parameters\n",
" - `ctrl_spec`: The control specification. \n",
"\n",
"#### Registers\n",
" - `ctrl_i`: The control register for the i-th ctrl dtype in the `ctrl_spec`.\n",
" - `junk [right]`: `ctrl_spec.num_qubits - 2` qubits that can be cleaned up by the inverse. Only present if the above size is non-zero.\n",
" - `target [right]`: The output bit storing the result of the `ctrl_spec`. \n",
"\n",
"#### References\n",
" - [Unqomp: synthesizing uncomputation in Quantum circuits](https://dl.acm.org/doi/10.1145/3453483.3454040). Paradis et. al. 2021.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "842307af",
"metadata": {
"cq.autogen": "CtrlSpecAnd.bloq_doc.py"
},
"outputs": [],
"source": [
"from qualtran.bloqs.mcmt import CtrlSpecAnd"
]
},
{
"cell_type": "markdown",
"id": "76f2965f",
"metadata": {
"cq.autogen": "CtrlSpecAnd.example_instances.md"
},
"source": [
"### Example Instances"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "68a43214",
"metadata": {
"cq.autogen": "CtrlSpecAnd.ctrl_on_int"
},
"outputs": [],
"source": [
"from qualtran import CtrlSpec, QUInt\n",
"\n",
"ctrl_on_int = CtrlSpecAnd(CtrlSpec(qdtypes=QUInt(4), cvs=[0b0101]))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "945fa4a4",
"metadata": {
"cq.autogen": "CtrlSpecAnd.ctrl_on_bits"
},
"outputs": [],
"source": [
"from qualtran import CtrlSpec, QBit\n",
"\n",
"ctrl_on_bits = CtrlSpecAnd(CtrlSpec(qdtypes=QBit(), cvs=[0, 1, 0, 1]))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5e6374b2",
"metadata": {
"cq.autogen": "CtrlSpecAnd.ctrl_on_nd_bits"
},
"outputs": [],
"source": [
"import numpy as np\n",
"\n",
"from qualtran import CtrlSpec, QBit\n",
"\n",
"ctrl_on_nd_bits = CtrlSpecAnd(CtrlSpec(qdtypes=QBit(), cvs=np.array([[0, 1], [1, 0]])))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2047fccf",
"metadata": {
"cq.autogen": "CtrlSpecAnd.ctrl_on_multiple_values"
},
"outputs": [],
"source": [
"from qualtran import CtrlSpec, QInt, QUInt\n",
"\n",
"ctrl_on_multiple_values = CtrlSpecAnd(\n",
" CtrlSpec(qdtypes=(QUInt(4), QInt(4)), cvs=([0b0101, 0b1100], [2, -2]))\n",
")"
]
},
{
"cell_type": "markdown",
"id": "55581cc0",
"metadata": {
"cq.autogen": "CtrlSpecAnd.graphical_signature.md"
},
"source": [
"#### Graphical Signature"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5ea9e6e5",
"metadata": {
"cq.autogen": "CtrlSpecAnd.graphical_signature.py"
},
"outputs": [],
"source": [
"from qualtran.drawing import show_bloqs\n",
"show_bloqs([ctrl_on_int, ctrl_on_bits, ctrl_on_nd_bits, ctrl_on_multiple_values],\n",
" ['`ctrl_on_int`', '`ctrl_on_bits`', '`ctrl_on_nd_bits`', '`ctrl_on_multiple_values`'])"
]
},
{
"cell_type": "markdown",
"id": "3f5bb7d6",
"metadata": {
"cq.autogen": "CtrlSpecAnd.call_graph.md"
},
"source": [
"### Call Graph"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f83e1715",
"metadata": {
"cq.autogen": "CtrlSpecAnd.call_graph.py"
},
"outputs": [],
"source": [
"from qualtran.resource_counting.generalizers import ignore_split_join\n",
"ctrl_on_int_g, ctrl_on_int_sigma = ctrl_on_int.call_graph(max_depth=1, generalizer=ignore_split_join)\n",
"show_call_graph(ctrl_on_int_g)\n",
"show_counts_sigma(ctrl_on_int_sigma)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Loading
Loading