diff --git a/dev_tools/bibliography.ipynb b/dev_tools/bibliography.ipynb index 038aaabfa..f91482a3f 100644 --- a/dev_tools/bibliography.ipynb +++ b/dev_tools/bibliography.ipynb @@ -129,7 +129,7 @@ "metadata": {}, "outputs": [], "source": [ - "for url, clss in backrefs.items():\n", + "for url, clss in sorted(backrefs.items(), key=lambda x: -len(x[1])):\n", " text = f'### [{titles[url]}]({url})\\n'\n", " for cls in clss:\n", " text += f' - {cls.__name__}\\n'\n", diff --git a/dev_tools/qualtran_dev_tools/tensor_report_card.py b/dev_tools/qualtran_dev_tools/tensor_report_card.py new file mode 100644 index 000000000..c2e3fa2df --- /dev/null +++ b/dev_tools/qualtran_dev_tools/tensor_report_card.py @@ -0,0 +1,152 @@ +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# 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. +import multiprocessing.connection +import time +from typing import Any, Callable, Dict, List, Optional, Tuple + +from attrs import define + +from qualtran import Bloq +from qualtran.simulation.tensor import cbloq_to_quimb + + +@define +class _Pending: + """Helper dataclass to track currently executing processes in `ExecuteWithTimeout`.""" + + p: multiprocessing.Process + recv: multiprocessing.connection.Connection + start_time: float + kwargs: Dict[str, Any] + + +class ExecuteWithTimeout: + """Execute tasks in processes where each task will be killed if it exceeds `timeout`. + + Seemingly all the existing "timeout" parameters in the various built-in concurrency + primitives in Python won't actually terminate the process. This one does. + """ + + def __init__(self, timeout: float, max_workers: int): + self.timeout = timeout + self.max_workers = max_workers + + self.queued: List[Tuple[Callable, Dict[str, Any]]] = [] + self.pending: List[_Pending] = [] + + @property + def work_to_be_done(self) -> int: + """The number of tasks currently executing or queued.""" + return len(self.queued) + len(self.pending) + + def submit(self, func: Callable, kwargs: Dict[str, Any]) -> None: + """Add a task to the queue. + + `func` must be a callable that can accept `kwargs` in addition to + a keyword argument `cxn` which is a multiprocessing `Connection` object that forms + the sending-half of a `mp.Pipe`. The callable must call `cxn.send(...)` + to return a result. + """ + self.queued.append((func, kwargs)) + + def _submit_from_queue(self): + # helper method that takes an item from the queue, launches a process, + # and records it in the `pending` attribute. This must only be called + # if we're allowed to spawn a new process. + func, kwargs = self.queued.pop(0) + recv, send = multiprocessing.Pipe(duplex=False) + kwargs['cxn'] = send + p = multiprocessing.Process(target=func, kwargs=kwargs) + start_time = time.time() + p.start() + self.pending.append(_Pending(p=p, recv=recv, start_time=start_time, kwargs=kwargs)) + + def _scan_pendings(self) -> Optional[_Pending]: + # helper method that goes through the currently pending tasks, terminates the ones + # that have been going on too long, and accounts for ones that have finished. + # Returns the `_Pending` of the killed or completed job or `None` if each pending + # task is still running but none have exceeded the timeout. + for i in range(len(self.pending)): + pen = self.pending[i] + + if not pen.p.is_alive(): + self.pending.pop(i) + pen.p.join() + return pen + + if time.time() - pen.start_time > self.timeout: + pen.p.terminate() + self.pending.pop(i) + return pen + + return None + + def next_result(self) -> Tuple[Dict[str, Any], Optional[Any]]: + """Get the next available result. + + This call is blocking, but should never take longer than `self.timeout`. This should + be called in a loop to make sure the queue continues to be processed. + + Returns: + task kwargs: The keyword arguments used to submit the task. + result: If the process finished successfully, this is the object that was + sent through the multiprocessing pipe as the result. Otherwise, the result + is None. + """ + while len(self.queued) > 0 and len(self.pending) < self.max_workers: + self._submit_from_queue() + + while True: + finished = self._scan_pendings() + if finished is not None: + break + + if finished.p.exitcode == 0: + result = finished.recv.recv() + else: + result = None + + finished.recv.close() + + while len(self.queued) > 0 and len(self.pending) < self.max_workers: + self._submit_from_queue() + + return (finished.kwargs, result) + + +def report_on_tensors(name: str, cls_name: str, bloq: Bloq, cxn) -> None: + """Get timing information for tensor functionality. + + This should be used with `ExecuteWithTimeout`. The resultant + record dictionary is sent over `cxn`. + """ + record: Dict[str, Any] = {'name': name, 'cls': cls_name} + + try: + start = time.perf_counter() + flat = bloq.as_composite_bloq().flatten() + record['flat_dur'] = time.perf_counter() - start + + start = time.perf_counter() + tn = cbloq_to_quimb(flat) + record['tn_dur'] = time.perf_counter() - start + + start = time.perf_counter() + record['width'] = tn.contraction_width() + record['width_dur'] = time.perf_counter() - start + + except Exception as e: # pylint: disable=broad-exception-caught + record['err'] = str(e) + + cxn.send(record) diff --git a/dev_tools/tensor-report-card.ipynb b/dev_tools/tensor-report-card.ipynb new file mode 100644 index 000000000..49d08b1f9 --- /dev/null +++ b/dev_tools/tensor-report-card.ipynb @@ -0,0 +1,262 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "9a9335f1", + "metadata": {}, + "source": [ + "# Tensor Report Card\n", + "\n", + "Not all bloq examples support tensor simulation. This report card automatically determines which bloq examples should be tensor simulable.\n", + "\n", + " - State vector simulation uses $2^n$ numbers to simulate a quantum state. The tensor protocol uses quimb to try to find more efficient contraction orderings. Quimb reports the contraction width, which is the minimum size of any intermediate tensor encountered in the contraciton. The simulation uses $2^w$ numbers, where $w$ is the width. We consider a width under 25 qubits as simulable.\n", + " - Qualtran requires \"flattening\" out the bloq to turn it into an efficient tensor network. This may take too much time itself for large algorithms with many levels of abstraction. If the process of turning a bloq into a quimb tensor network and finding a contraction ordering takes longer than 8 seconds, we don't consider the bloq simulable.\n", + " - The flattened structure needs to have explicit tensors. For bloqs with symbolic parameters, we either can't decompose & flatten them, or the tensors would be symbolic, which we don't support." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4d2bf2bc-1b55-4f68-b0f4-68f62dd68bae", + "metadata": {}, + "outputs": [], + "source": [ + "from qualtran_dev_tools.bloq_finder import get_bloq_examples\n", + "from qualtran_dev_tools.tensor_report_card import report_on_tensors, ExecuteWithTimeout" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "86168342-d740-446a-bc88-06eaec8ae3a8", + "metadata": {}, + "outputs": [], + "source": [ + "bes = get_bloq_examples()\n", + "\n", + "# Imports to exclude certain bloqs, see following comment\n", + "from qualtran.bloqs.multiplexers.apply_gate_to_lth_target import ApplyGateToLthQubit" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51781740-6417-4b3b-b5d8-86f51340a016", + "metadata": {}, + "outputs": [], + "source": [ + "exec = ExecuteWithTimeout(timeout=8., max_workers=4)\n", + "for i, be in enumerate(bes):\n", + "\n", + " if be.bloq_cls == ApplyGateToLthQubit:\n", + " # This bloq uses a lambda function as one of its attributes, which\n", + " # can't be pickled and used with multiprocessing.\n", + " continue\n", + " \n", + " exec.submit(report_on_tensors, kwargs=dict(name=be.name, cls_name=be.bloq_cls.__name__, bloq=be.make()))\n", + "\n", + "records = []\n", + "while exec.work_to_be_done:\n", + " kwargs, record = exec.next_result()\n", + " #print(kwargs['name'], end=' ', flush=True)\n", + " print('\\r', f'{exec.work_to_be_done:5d} remaining', end='', flush=True)\n", + " \n", + " if record is None:\n", + " records.append({\n", + " 'name': kwargs['name'],\n", + " 'cls': kwargs['cls_name'],\n", + " 'err': 'Timeout',\n", + " })\n", + " else:\n", + " records.append(record)\n", + "\n", + "import pandas as pd\n", + "df = pd.DataFrame(records)" + ] + }, + { + "cell_type": "markdown", + "id": "393a5aad-7a78-4167-8dd5-407d8a0b3241", + "metadata": {}, + "source": [ + "## Number of bloq examples considered" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5d674962-27d7-40d1-9916-f0aba3457934", + "metadata": {}, + "outputs": [], + "source": [ + "print(len(df))" + ] + }, + { + "cell_type": "markdown", + "id": "6a59804e-afc6-4227-b74f-cbf82f14b773", + "metadata": {}, + "source": [ + "## Number of bloq examples successfully flattened" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8b048464-083a-4e04-8135-176f449450ca", + "metadata": {}, + "outputs": [], + "source": [ + "print(len(df[df['flat_dur'] > 0]))" + ] + }, + { + "cell_type": "markdown", + "id": "a4194f99-1551-4a5e-b93d-fb043cc56f9e", + "metadata": {}, + "source": [ + "## Number of bloq examples with tensors" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5cdfa4e3-e1db-4869-832c-63695a3fd3f4", + "metadata": {}, + "outputs": [], + "source": [ + "print(len(df[df['width'] > 0]))" + ] + }, + { + "cell_type": "markdown", + "id": "05bffa4b-6bbe-4901-b1de-aba1abebe552", + "metadata": {}, + "source": [ + "## Bloqs that are tensor simulable" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "578fd2b5-9f7e-4edc-81b2-a8aad989be68", + "metadata": {}, + "outputs": [], + "source": [ + "print(len(df[df['width'] <= 25]))\n", + "df[df['width'] <= 25]" + ] + }, + { + "cell_type": "markdown", + "id": "4ef49322-cffb-4ff9-8cb1-6e5a87749492", + "metadata": {}, + "source": [ + "## Bloqs whose tensor network is too big" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d529d144-b3c5-45e5-ae14-f6d3fb86f153", + "metadata": {}, + "outputs": [], + "source": [ + "df[df['width'] > 25].sort_values(by='width')" + ] + }, + { + "cell_type": "markdown", + "id": "41141f6f-4377-4cee-9ca7-d2981c94d2e4", + "metadata": {}, + "source": [ + "## Bloqs without tensors\n", + "\n", + "Due to errors encountered in flattening or if the bloq's callees don't support tensor simulation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c3386091-491a-404f-b13d-72b401d2c267", + "metadata": {}, + "outputs": [], + "source": [ + "df[df['width'].isna()]" + ] + }, + { + "cell_type": "markdown", + "id": "46a79e7f-0715-4007-8c11-d959f8f2255b", + "metadata": {}, + "source": [ + "## Slowest to flatten\n", + "\n", + "Within the overall timeout" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "df522d3f-4392-46ea-851e-321d0fdfd52f", + "metadata": {}, + "outputs": [], + "source": [ + "df.sort_values(by='flat_dur', ascending=False).head()" + ] + }, + { + "cell_type": "markdown", + "id": "48f419ef-2723-42ea-8670-81a729164cf7", + "metadata": {}, + "source": [ + "## Flattening is the rate-limiting step.\n", + "\n", + "For bloqs that have been successfully flattened, the maximum tensor-network-construction and tensor-contraction-ordering durations are less than 0.5s. Note: the contraction finding code uses the fast, naive approach. One can choose more expensive approaches where the contraciton-ordering-finding is more expensive." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eae32349-ad93-4072-9704-044ef390e59e", + "metadata": {}, + "outputs": [], + "source": [ + "# Slowest tn_dur\n", + "df.sort_values(by='tn_dur', ascending=False).head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9249f813-1d42-47f4-bd1a-f198b79298f6", + "metadata": {}, + "outputs": [], + "source": [ + "# Slowest width_dur\n", + "df.sort_values(by='width_dur', ascending=False).head()" + ] + } + ], + "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.8" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/qualtran/Autodoc.ipynb b/qualtran/Autodoc.ipynb index cada98e49..b70bf764b 100644 --- a/qualtran/Autodoc.ipynb +++ b/qualtran/Autodoc.ipynb @@ -61,7 +61,7 @@ "\n", "### References\n", "\n", - "We provide custom support for a \"References\" section where you should reference the source(s) of the construction. References are newline seperated. They must start with a markdown-style link of `[title](url)`. This can optionally be followed by a period and any additional information in standard markdown format. To balance structure vs. readability, reference links should be kept to a single line and need not respect the 100-character line limit.\n", + "We provide custom support for a \"References\" section where you should reference the source(s) of the construction. References are newline seperated. They must start with a markdown-style link of `[title](url)`. This can optionally be followed by a period and any additional information in standard markdown format. To balance structure vs. readability, reference links should be kept to a single line and need not respect the 100-character line limit. Canonically, the next line should include one or two author last names, the first year of publication, and specific figures or equations relevant to the bloq. Prefer arxiv links. Link to the abstract page; not directly to the pdf.\n", "\n", "```python\n", "class QROM(Bloq):\n", @@ -166,7 +166,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.7" + "version": "3.11.8" } }, "nbformat": 4, diff --git a/qualtran/_infra/adjoint.py b/qualtran/_infra/adjoint.py index 675592287..0601a6248 100644 --- a/qualtran/_infra/adjoint.py +++ b/qualtran/_infra/adjoint.py @@ -16,7 +16,6 @@ from functools import cached_property from typing import Dict, List, Optional, Tuple, TYPE_CHECKING -import cirq from attrs import frozen from .composite_bloq import _binst_to_cxns, _cxns_to_soq_dict, _map_soqs, _reg_to_soq, BloqBuilder @@ -25,6 +24,8 @@ from .registers import Signature if TYPE_CHECKING: + import cirq + from qualtran import Bloq, CompositeBloq, Register, Signature, SoquetT from qualtran.drawing import WireSymbol from qualtran.resource_counting import BloqCountDictT, SympySymbolAllocator @@ -144,7 +145,9 @@ def decompose_bloq(self) -> 'CompositeBloq': def _circuit_diagram_info_( self, args: 'cirq.CircuitDiagramInfoArgs' - ) -> cirq.CircuitDiagramInfo: + ) -> 'cirq.CircuitDiagramInfo': + import cirq + sub_info = cirq.circuit_diagram_info(self.subbloq, args, default=NotImplemented) if sub_info is NotImplemented: return NotImplemented diff --git a/qualtran/_infra/composite_bloq_test.py b/qualtran/_infra/composite_bloq_test.py index 58972c9d9..ffe39213c 100644 --- a/qualtran/_infra/composite_bloq_test.py +++ b/qualtran/_infra/composite_bloq_test.py @@ -16,7 +16,6 @@ from typing import Dict, List, Tuple import attrs -import cirq import networkx as nx import numpy as np import pytest @@ -164,7 +163,8 @@ def test_map_soqs(): assert isinstance(cbloq, CompositeBloq) -def test_bb_composite_bloq(): +def test_to_from_cirq_circuit(): + cirq = pytest.importorskip('cirq') cbloq_auto = TestTwoCNOT().decompose_bloq() circuit, _ = cbloq_auto.to_cirq_circuit_and_quregs( q1=[cirq.LineQubit(1)], q2=[cirq.LineQubit(2)] @@ -348,6 +348,7 @@ def build_composite_bloq( def test_complicated_target_register(): + cirq = pytest.importorskip('cirq') bloq = TestMultiCNOT() cbloq = qlt_testing.assert_valid_bloq_decomposition(bloq) assert len(cbloq.bloq_instances) == 2 * 3 diff --git a/qualtran/_infra/controlled.py b/qualtran/_infra/controlled.py index f417bc2b4..e43a2d2b9 100644 --- a/qualtran/_infra/controlled.py +++ b/qualtran/_infra/controlled.py @@ -27,7 +27,6 @@ ) import attrs -import cirq import numpy as np from numpy.typing import NDArray @@ -37,6 +36,7 @@ from .registers import Register, Side, Signature if TYPE_CHECKING: + import cirq import quimb.tensor as qtn from qualtran import Bloq, BloqBuilder, CompositeBloq, ConnectionT, SoquetT @@ -203,8 +203,10 @@ def __eq__(self, other: Any) -> bool: def __hash__(self): return hash((self.qdtypes, self.shapes, self._cvs_tuple)) - def to_cirq_cv(self) -> cirq.SumOfProducts: + def to_cirq_cv(self) -> 'cirq.SumOfProducts': """Convert CtrlSpec to cirq.SumOfProducts representation of control values.""" + import cirq + cirq_cv = [] for qdtype, cv in zip(self.qdtypes, self.cvs): for idx in Register('', qdtype, cv.shape).all_idxs(): @@ -214,7 +216,7 @@ def to_cirq_cv(self) -> cirq.SumOfProducts: @classmethod def from_cirq_cv( cls, - cirq_cv: cirq.ops.AbstractControlValues, + cirq_cv: 'cirq.ops.AbstractControlValues', *, qdtypes: Optional[Sequence[QDType]] = None, shapes: Optional[Sequence[Tuple[int, ...]]] = None, @@ -438,6 +440,8 @@ def _tensor_data(self): def _unitary_(self): if isinstance(self.subbloq, GateWithRegisters): # subbloq is a cirq gate, use the cirq-style API to derive a unitary. + import cirq + return cirq.unitary( cirq.ControlledGate(self.subbloq, control_values=self.ctrl_spec.to_cirq_cv()) ) @@ -494,7 +498,11 @@ def as_cirq_op( cirq_quregs | ctrl_regs, ) - def _circuit_diagram_info_(self, args: cirq.CircuitDiagramInfoArgs) -> cirq.CircuitDiagramInfo: + def _circuit_diagram_info_( + self, args: 'cirq.CircuitDiagramInfoArgs' + ) -> 'cirq.CircuitDiagramInfo': + import cirq + from qualtran.cirq_interop._bloq_to_cirq import _wire_symbol_to_cirq_diagram_info if isinstance(self.subbloq, cirq.Gate): diff --git a/qualtran/_infra/controlled_test.py b/qualtran/_infra/controlled_test.py index b25201b25..34b26def8 100644 --- a/qualtran/_infra/controlled_test.py +++ b/qualtran/_infra/controlled_test.py @@ -14,7 +14,6 @@ from typing import Dict, List, Tuple, TYPE_CHECKING import attrs -import cirq import numpy as np import pytest @@ -55,6 +54,8 @@ from qualtran.simulation.tensor import cbloq_to_quimb, get_right_and_left_inds if TYPE_CHECKING: + import cirq + from qualtran import SoquetT @@ -84,6 +85,7 @@ def test_ctrl_spec_shape(): def test_ctrl_spec_to_cirq_cv_roundtrip(): + cirq = pytest.importorskip('cirq') cirq_cv = cirq.ProductOfSums([0, 1, 0, 1]) assert CtrlSpec.from_cirq_cv(cirq_cv) == CtrlSpec(cvs=[0, 1, 0, 1]) @@ -98,17 +100,21 @@ def test_ctrl_spec_to_cirq_cv_roundtrip(): assert CtrlSpec.from_cirq_cv(cirq_cv, qdtypes=ctrl_spec.qdtypes, shapes=ctrl_spec.shapes) +def _test_cirq_equivalence(bloq: Bloq, gate: 'cirq.Gate'): + import cirq + + left_quregs = get_named_qubits(bloq.signature.lefts()) + circuit1 = bloq.as_composite_bloq().to_cirq_circuit(cirq_quregs=left_quregs) + circuit2 = cirq.Circuit( + gate.on(*merge_qubits(bloq.signature, **get_named_qubits(bloq.signature))) + ) + cirq.testing.assert_same_circuits(circuit1, circuit2) + + def test_ctrl_bloq_as_cirq_op(): + cirq = pytest.importorskip('cirq') subbloq = XGate() - def _test_cirq_equivalence(bloq: Bloq, gate: cirq.Gate): - left_quregs = get_named_qubits(bloq.signature.lefts()) - circuit1 = bloq.as_composite_bloq().to_cirq_circuit(cirq_quregs=left_quregs) - circuit2 = cirq.Circuit( - gate.on(*merge_qubits(bloq.signature, **get_named_qubits(bloq.signature))) - ) - cirq.testing.assert_same_circuits(circuit1, circuit2) - # Simple ctrl spec _test_cirq_equivalence(subbloq, cirq.X) _test_cirq_equivalence(subbloq.controlled(), cirq.X.controlled()) @@ -340,7 +346,9 @@ def test_notebook(): qlt_testing.execute_notebook('../Controlled') -def _verify_ctrl_tensor_for_unitary(ctrl_spec: CtrlSpec, bloq: Bloq, gate: cirq.Gate): +def _verify_ctrl_tensor_for_unitary(ctrl_spec: CtrlSpec, bloq: Bloq, gate: 'cirq.Gate'): + import cirq + ctrl_bloq = Controlled(bloq, ctrl_spec) cgate = cirq.ControlledGate(gate, control_values=ctrl_spec.to_cirq_cv()) np.testing.assert_allclose(ctrl_bloq.tensor_contract(), cirq.unitary(cgate), atol=1e-8) @@ -357,6 +365,7 @@ def _verify_ctrl_tensor_for_unitary(ctrl_spec: CtrlSpec, bloq: Bloq, gate: cirq. @pytest.mark.parametrize('ctrl_spec', interesting_ctrl_specs) def test_controlled_tensor_for_unitary(ctrl_spec: CtrlSpec): + cirq = pytest.importorskip('cirq') # Test one qubit unitaries _verify_ctrl_tensor_for_unitary(ctrl_spec, XGate(), cirq.X) _verify_ctrl_tensor_for_unitary(ctrl_spec, YGate(), cirq.Y) @@ -366,6 +375,7 @@ def test_controlled_tensor_for_unitary(ctrl_spec: CtrlSpec): def test_controlled_tensor_without_decompose(): + cirq = pytest.importorskip('cirq') ctrl_spec = CtrlSpec() bloq = TwoBitCSwap() ctrl_bloq = Controlled(bloq, ctrl_spec) @@ -442,6 +452,7 @@ def test_controlled_tensor_for_and_bloq(ctrl_spec: CtrlSpec): def test_controlled_diagrams(): + cirq = pytest.importorskip('cirq') ctrl_gate = XPowGate(0.25).controlled() cirq.testing.assert_has_diagram( cirq.Circuit(ctrl_gate.on_registers(**get_named_qubits(ctrl_gate.signature))), diff --git a/qualtran/_infra/data_types_test.py b/qualtran/_infra/data_types_test.py index 227c8de7b..10347b702 100644 --- a/qualtran/_infra/data_types_test.py +++ b/qualtran/_infra/data_types_test.py @@ -15,7 +15,6 @@ import random from typing import Any, Sequence, Union -import cirq import numpy as np import pytest import sympy @@ -312,6 +311,7 @@ def test_quint_to_and_from_bits(): def test_bits_to_int(): + cirq = pytest.importorskip('cirq') rs = np.random.RandomState(52) bitstrings = rs.choice([0, 1], size=(100, 23)) @@ -328,6 +328,7 @@ def test_bits_to_int(): def test_int_to_bits(): + cirq = pytest.importorskip('cirq') rs = np.random.RandomState(52) nums = rs.randint(0, 2**23 - 1, size=(100,), dtype=np.uint64) bitstrings = QUInt(23).to_bits_array(nums) diff --git a/qualtran/_infra/gate_with_registers.py b/qualtran/_infra/gate_with_registers.py index 219f3e995..e59bd4a20 100644 --- a/qualtran/_infra/gate_with_registers.py +++ b/qualtran/_infra/gate_with_registers.py @@ -49,8 +49,8 @@ def total_bits(registers: Iterable[Register]) -> int: def split_qubits( - registers: Iterable[Register], qubits: Sequence[cirq.Qid] -) -> Dict[str, NDArray[cirq.Qid]]: # type: ignore[type-var] + registers: Iterable[Register], qubits: Sequence['cirq.Qid'] +) -> Dict[str, NDArray['cirq.Qid']]: # type: ignore[type-var] """Splits the flat list of qubits into a dictionary of appropriately shaped qubit arrays.""" qubit_regs = {} @@ -65,11 +65,11 @@ def split_qubits( def merge_qubits( registers: Iterable[Register], - **qubit_regs: Union[cirq.Qid, Sequence[cirq.Qid], NDArray[cirq.Qid]], -) -> List[cirq.Qid]: + **qubit_regs: Union['cirq.Qid', Sequence['cirq.Qid'], NDArray['cirq.Qid']], +) -> List['cirq.Qid']: """Merges the dictionary of appropriately shaped qubit arrays into a flat list of qubits.""" - ret: List[cirq.Qid] = [] + ret: List['cirq.Qid'] = [] for reg in registers: if reg.name not in qubit_regs: raise ValueError(f"All qubit registers must be present. {reg.name} not in qubit_regs") @@ -84,7 +84,7 @@ def merge_qubits( return ret -def get_named_qubits(registers: Iterable[Register]) -> Dict[str, NDArray[cirq.Qid]]: +def get_named_qubits(registers: Iterable[Register]) -> Dict[str, NDArray['cirq.Qid']]: """Returns a dictionary of appropriately shaped named qubit signature for input `signature`.""" def _qubit_array(reg: Register): @@ -114,7 +114,7 @@ def _qubits_for_reg(reg: Register): def _get_all_and_output_quregs_from_input( registers: Iterable[Register], - qubit_manager: cirq.QubitManager, + qubit_manager: 'cirq.QubitManager', in_quregs: Dict[str, 'CirqQuregT'], ) -> Tuple[Dict[str, 'CirqQuregT'], Dict[str, 'CirqQuregT']]: """Takes care of necessary (de-/)allocations to obtain output & all qubit registers from input. @@ -171,7 +171,7 @@ def _get_cirq_cv( num_controls: Optional[int] = None, control_values=None, control_qid_shape: Optional[Tuple[int, ...]] = None, -) -> cirq.ops.AbstractControlValues: +) -> 'cirq.ops.AbstractControlValues': """Logic copied from `cirq.ControlledGate` to help convert cirq-style spec to `CtrlSpec`""" if isinstance(control_values, cirq.SumOfProducts) and len(control_values._conjunctions) == 1: control_values = control_values._conjunctions[0] @@ -325,13 +325,13 @@ def _num_qubits_(self) -> int: return total_bits(self.signature) def decompose_from_registers( - self, *, context: cirq.DecompositionContext, **quregs: NDArray[cirq.Qid] - ) -> cirq.OP_TREE: + self, *, context: 'cirq.DecompositionContext', **quregs: NDArray['cirq.Qid'] + ) -> 'cirq.OP_TREE': raise DecomposeNotImplementedError(f"{self} does not declare a decomposition.") def _decompose_with_context_( - self, qubits: Sequence[cirq.Qid], context: Optional[cirq.DecompositionContext] = None - ) -> cirq.OP_TREE: + self, qubits: Sequence['cirq.Qid'], context: Optional['cirq.DecompositionContext'] = None + ) -> 'cirq.OP_TREE': from qualtran.cirq_interop._bloq_to_cirq import _cirq_style_decompose_from_decompose_bloq quregs = split_qubits(self.signature, qubits) @@ -349,7 +349,7 @@ def _decompose_with_context_( pass return NotImplemented - def _decompose_(self, qubits: Sequence[cirq.Qid]) -> cirq.OP_TREE: + def _decompose_(self, qubits: Sequence['cirq.Qid']) -> 'cirq.OP_TREE': return self._decompose_with_context_(qubits) def on(self, *qubits) -> 'cirq.Operation': @@ -359,8 +359,8 @@ def on(self, *qubits) -> 'cirq.Operation': return cirq.Gate.on(self, *qubits) def on_registers( - self, **qubit_regs: Union[cirq.Qid, Sequence[cirq.Qid], NDArray[cirq.Qid]] - ) -> cirq.Operation: + self, **qubit_regs: Union['cirq.Qid', Sequence['cirq.Qid'], NDArray['cirq.Qid']] + ) -> 'cirq.Operation': return self.on(*merge_qubits(self.signature, **qubit_regs)) def __pow__(self, power: int) -> 'GateWithRegisters': @@ -441,7 +441,7 @@ def controlled( self, num_controls: Optional[int] = None, control_values: Optional[ - Union[cirq.ops.AbstractControlValues, Sequence[Union[int, Collection[int]]]] + Union['cirq.ops.AbstractControlValues', Sequence[Union[int, Collection[int]]]] ] = None, control_qid_shape: Optional[Tuple[int, ...]] = None, ) -> 'GateWithRegisters': @@ -456,7 +456,7 @@ def controlled( self, num_controls: Union[Optional[int], 'CtrlSpec'] = None, control_values: Optional[ - Union[cirq.ops.AbstractControlValues, Sequence[Union[int, Collection[int]]]] + Union['cirq.ops.AbstractControlValues', Sequence[Union[int, Collection[int]]]] ] = None, control_qid_shape: Optional[Tuple[int, ...]] = None, *, @@ -518,7 +518,9 @@ def my_tensors( else: return super().my_tensors(incoming=incoming, outgoing=outgoing) - def _circuit_diagram_info_(self, args: cirq.CircuitDiagramInfoArgs) -> cirq.CircuitDiagramInfo: + def _circuit_diagram_info_( + self, args: 'cirq.CircuitDiagramInfoArgs' + ) -> 'cirq.CircuitDiagramInfo': """Default diagram info that uses register names to name the boxes in multi-qubit gates. Descendants can override this method with more meaningful circuit diagram information. diff --git a/qualtran/_infra/registers_test.py b/qualtran/_infra/registers_test.py index 8beb0163a..f52b1af7a 100644 --- a/qualtran/_infra/registers_test.py +++ b/qualtran/_infra/registers_test.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import cirq import numpy as np import pytest import sympy @@ -100,6 +99,13 @@ def test_signature(): assert list(signature) == [r1, r2, r3] + +def test_get_named_qubits(): + cirq = pytest.importorskip('cirq') + r1 = Register("r1", QAny(5)) + r2 = Register("r2", QAny(2)) + r3 = Register("r3", QBit()) + signature = Signature([r1, r2, r3]) expected_named_qubits = { "r1": cirq.NamedQubit.range(5, prefix="r1"), "r2": cirq.NamedQubit.range(2, prefix="r2"), @@ -179,6 +185,7 @@ def test_agg_split(): def test_get_named_qubits_multidim(): + cirq = pytest.importorskip('cirq') regs = Signature([Register('q', shape=(2, 3), dtype=QAny(4))]) quregs = get_named_qubits(regs.lefts()) assert quregs['q'].shape == (2, 3, 4) diff --git a/qualtran/bloqs/arithmetic/comparison.ipynb b/qualtran/bloqs/arithmetic/comparison.ipynb index 537c80f36..4a0c8ad6d 100644 --- a/qualtran/bloqs/arithmetic/comparison.ipynb +++ b/qualtran/bloqs/arithmetic/comparison.ipynb @@ -485,7 +485,7 @@ "https://github.com/quantumlib/Qualtran/issues/389\n", "\n", "#### References\n", - " - Supplementary Materials: Improved Techniques for Preparing Eigenstates of Fermionic Hamiltonians https://static-content.springer.com/esm/art%3A10.1038%2Fs41534-018-0071-5/MediaObjects/41534_2018_71_MOESM1_ESM.pdf\n" + " - [Supplementary Materials: Improved Techniques for Preparing Eigenstates of Fermionic Hamiltonians](https://static-content.springer.com/esm/art%3A10.1038%2Fs41534-018-0071-5/MediaObjects/41534_2018_71_MOESM1_ESM.pdf).\n" ] }, { @@ -702,7 +702,7 @@ "\n", "#### References\n", " - [Encoding Electronic Spectra in Quantum Circuits with Linear T Complexity](https://arxiv.org/abs/1805.03662). \n", - " - Supplementary Materials: Improved Techniques for Preparing Eigenstates of Fermionic Hamiltonians. https://static-content.springer.com/esm/art%3A10.1038%2Fs41534-018-0071-5/MediaObjects/41534_2018_71_MOESM1_ESM.pdf\n" + " - [Supplementary Materials: Improved Techniques for Preparing Eigenstates of Fermionic Hamiltonians](https://static-content.springer.com/esm/art%3A10.1038%2Fs41534-018-0071-5/MediaObjects/41534_2018_71_MOESM1_ESM.pdf).\n" ] }, { diff --git a/qualtran/bloqs/arithmetic/comparison.py b/qualtran/bloqs/arithmetic/comparison.py index 272ea8abe..c546aee77 100644 --- a/qualtran/bloqs/arithmetic/comparison.py +++ b/qualtran/bloqs/arithmetic/comparison.py @@ -233,8 +233,7 @@ class BiQubitsMixer(GateWithRegisters): https://github.com/quantumlib/Qualtran/issues/389 References: - Supplementary Materials: Improved Techniques for Preparing Eigenstates of Fermionic Hamiltonians - https://static-content.springer.com/esm/art%3A10.1038%2Fs41534-018-0071-5/MediaObjects/41534_2018_71_MOESM1_ESM.pdf + [Supplementary Materials: Improved Techniques for Preparing Eigenstates of Fermionic Hamiltonians](https://static-content.springer.com/esm/art%3A10.1038%2Fs41534-018-0071-5/MediaObjects/41534_2018_71_MOESM1_ESM.pdf). """ is_adjoint: bool = False @@ -432,8 +431,7 @@ class LessThanEqual(GateWithRegisters, cirq.ArithmeticGate): # type: ignore[mis References: [Encoding Electronic Spectra in Quantum Circuits with Linear T Complexity](https://arxiv.org/abs/1805.03662). - Supplementary Materials: Improved Techniques for Preparing Eigenstates of Fermionic Hamiltonians. - https://static-content.springer.com/esm/art%3A10.1038%2Fs41534-018-0071-5/MediaObjects/41534_2018_71_MOESM1_ESM.pdf + [Supplementary Materials: Improved Techniques for Preparing Eigenstates of Fermionic Hamiltonians](https://static-content.springer.com/esm/art%3A10.1038%2Fs41534-018-0071-5/MediaObjects/41534_2018_71_MOESM1_ESM.pdf). """ x_bitsize: 'SymbolicInt' diff --git a/qualtran/bloqs/arithmetic/multiplication.ipynb b/qualtran/bloqs/arithmetic/multiplication.ipynb index 0909cd39a..40eff7a98 100644 --- a/qualtran/bloqs/arithmetic/multiplication.ipynb +++ b/qualtran/bloqs/arithmetic/multiplication.ipynb @@ -474,7 +474,7 @@ " - `result`: a r_bitsize sized output fixed-point register. \n", "\n", "#### References\n", - " - [Compilation of Fault-Tolerant Quantum Heuristics for Combinatorial Optimization]( https://arxiv.org/pdf/2007.07391.pdf) pg 70.\n" + " - [Compilation of Fault-Tolerant Quantum Heuristics for Combinatorial Optimization](https://arxiv.org/abs/2007.07391). pg 70.\n" ] }, { diff --git a/qualtran/bloqs/arithmetic/multiplication.py b/qualtran/bloqs/arithmetic/multiplication.py index 464eacf0c..efed2d665 100644 --- a/qualtran/bloqs/arithmetic/multiplication.py +++ b/qualtran/bloqs/arithmetic/multiplication.py @@ -315,9 +315,8 @@ class Product(Bloq): result: A 2*`max(a_bitsize, b_bitsize)` bit-sized output register to store the result a*b. References: - [Fault-Tolerant Quantum Simulations of Chemistry in First - Quantization](https://arxiv.org/abs/2105.12767) pg 81 gives a Toffoli - complexity for multiplying two numbers. + [Fault-Tolerant Quantum Simulations of Chemistry in First Quantization](https://arxiv.org/abs/2105.12767). + pg 81 gives a Toffoli complexity for multiplying two numbers. """ a_bitsize: SymbolicInt @@ -377,8 +376,8 @@ class ScaleIntByReal(Bloq): result: a r_bitsize sized output fixed-point register. References: - [Compilation of Fault-Tolerant Quantum Heuristics for Combinatorial Optimization]( - https://arxiv.org/pdf/2007.07391.pdf) pg 70. + [Compilation of Fault-Tolerant Quantum Heuristics for Combinatorial Optimization](https://arxiv.org/abs/2007.07391). + pg 70. """ r_bitsize: SymbolicInt diff --git a/qualtran/bloqs/arithmetic/subtraction.ipynb b/qualtran/bloqs/arithmetic/subtraction.ipynb index 584a0dce4..ce9e32211 100644 --- a/qualtran/bloqs/arithmetic/subtraction.ipynb +++ b/qualtran/bloqs/arithmetic/subtraction.ipynb @@ -53,7 +53,7 @@ " - `b`: A b_dtype.bitsize-sized input/output register (register b above). \n", "\n", "#### References\n", - " - [Compilation of Fault-Tolerant Quantum Heuristics for Combinatorial Optimization, page 9](https://arxiv.org/pdf/2007.07391). \n" + " - [Compilation of Fault-Tolerant Quantum Heuristics for Combinatorial Optimization](https://arxiv.org/abs/2007.07391). Page 9.\n" ] }, { diff --git a/qualtran/bloqs/arithmetic/subtraction.py b/qualtran/bloqs/arithmetic/subtraction.py index f9bb91437..ef5cb5199 100644 --- a/qualtran/bloqs/arithmetic/subtraction.py +++ b/qualtran/bloqs/arithmetic/subtraction.py @@ -68,7 +68,8 @@ class Subtract(Bloq): b: A b_dtype.bitsize-sized input/output register (register b above). References: - [Compilation of Fault-Tolerant Quantum Heuristics for Combinatorial Optimization, page 9](https://arxiv.org/pdf/2007.07391) + [Compilation of Fault-Tolerant Quantum Heuristics for Combinatorial Optimization](https://arxiv.org/abs/2007.07391). + Page 9. """ a_dtype: Union[QInt, QUInt, QMontgomeryUInt] = field() diff --git a/qualtran/bloqs/basic_gates/swap.py b/qualtran/bloqs/basic_gates/swap.py index 84aaf666a..dc8d27e24 100644 --- a/qualtran/bloqs/basic_gates/swap.py +++ b/qualtran/bloqs/basic_gates/swap.py @@ -15,7 +15,6 @@ from functools import cached_property from typing import Dict, Iterable, List, Optional, Sequence, Tuple, TYPE_CHECKING, Union -import cirq import numpy as np import sympy from attrs import frozen @@ -40,9 +39,11 @@ from qualtran.resource_counting.generalizers import ignore_split_join if TYPE_CHECKING: + import cirq import quimb.tensor as qtn from qualtran import AddControlledT, CompositeBloq + from qualtran.cirq_interop import CirqQuregT from qualtran.resource_counting import BloqCountDictT, SympySymbolAllocator from qualtran.simulation.classical_sim import ClassicalValT @@ -81,6 +82,8 @@ def as_cirq_op( ) -> Tuple['cirq.Operation', Dict[str, 'CirqQuregT']]: # type: ignore[type-var] (x,) = x (y,) = y + import cirq + return cirq.SWAP.on(x, y), {'x': np.asarray([x]), 'y': np.asarray([y])} def my_tensors( @@ -150,6 +153,8 @@ def decompose_bloq(self) -> 'CompositeBloq': raise DecomposeTypeError(f"{self} is atomic.") def to_clifford_t_circuit(self) -> 'cirq.FrozenCircuit': + import cirq + ctrl = cirq.NamedQubit('ctrl') x = cirq.NamedQubit('x') y = cirq.NamedQubit('y') @@ -350,12 +355,16 @@ def on_classical_vals( @classmethod def make_on( - cls, **quregs: Union[Sequence[cirq.Qid], NDArray[cirq.Qid]] # type: ignore[type-var] - ) -> cirq.Operation: + cls, **quregs: Union[Sequence['cirq.Qid'], NDArray['cirq.Qid']] # type: ignore[type-var] + ) -> 'cirq.Operation': """Helper constructor to automatically deduce bitsize attributes.""" return cls(bitsize=len(quregs['x'])).on_registers(**quregs) - def _circuit_diagram_info_(self, args: cirq.CircuitDiagramInfoArgs) -> cirq.CircuitDiagramInfo: + def _circuit_diagram_info_( + self, args: 'cirq.CircuitDiagramInfoArgs' + ) -> 'cirq.CircuitDiagramInfo': + import cirq + if not args.use_unicode_characters: return cirq.CircuitDiagramInfo( ("@",) + ("swap_x",) * self.bitsize + ("swap_y",) * self.bitsize diff --git a/qualtran/bloqs/block_encoding/linear_combination.ipynb b/qualtran/bloqs/block_encoding/linear_combination.ipynb index 085c41cda..415f99e84 100644 --- a/qualtran/bloqs/block_encoding/linear_combination.ipynb +++ b/qualtran/bloqs/block_encoding/linear_combination.ipynb @@ -64,7 +64,7 @@ " - `resource`: The resource register (present only if bitsize > 0). \n", "\n", "#### References\n", - " - [Quantum algorithms: A survey of applications and end-to-end complexities]( https://arxiv.org/abs/2310.03011). Dalzell et al. (2023). Ch. 10.2.\n" + " - [Quantum algorithms: A survey of applications and end-to-end complexities](https://arxiv.org/abs/2310.03011). Dalzell et al. (2023). Ch. 10.2.\n" ] }, { diff --git a/qualtran/bloqs/block_encoding/linear_combination.py b/qualtran/bloqs/block_encoding/linear_combination.py index f890166d6..16586cf61 100644 --- a/qualtran/bloqs/block_encoding/linear_combination.py +++ b/qualtran/bloqs/block_encoding/linear_combination.py @@ -75,8 +75,8 @@ class LinearCombination(BlockEncoding): resource: The resource register (present only if bitsize > 0). References: - [Quantum algorithms: A survey of applications and end-to-end complexities]( - https://arxiv.org/abs/2310.03011). Dalzell et al. (2023). Ch. 10.2. + [Quantum algorithms: A survey of applications and end-to-end complexities](https://arxiv.org/abs/2310.03011). + Dalzell et al. (2023). Ch. 10.2. """ _block_encodings: Tuple[BlockEncoding, ...] = field( diff --git a/qualtran/bloqs/block_encoding/product.ipynb b/qualtran/bloqs/block_encoding/product.ipynb index 7e097923d..727fb156b 100644 --- a/qualtran/bloqs/block_encoding/product.ipynb +++ b/qualtran/bloqs/block_encoding/product.ipynb @@ -69,7 +69,7 @@ " - `resource`: The resource register (present only if bitsize > 0). \n", "\n", "#### References\n", - " - [Quantum algorithms: A survey of applications and end-to-end complexities]( https://arxiv.org/abs/2310.03011). Dalzell et al. (2023). Ch. 10.2.\n" + " - [Quantum algorithms: A survey of applications and end-to-end complexities](https://arxiv.org/abs/2310.03011). Dalzell et al. (2023). Ch. 10.2.\n" ] }, { diff --git a/qualtran/bloqs/block_encoding/product.py b/qualtran/bloqs/block_encoding/product.py index 0dc50007d..aa2507f99 100644 --- a/qualtran/bloqs/block_encoding/product.py +++ b/qualtran/bloqs/block_encoding/product.py @@ -81,8 +81,8 @@ class Product(BlockEncoding): resource: The resource register (present only if bitsize > 0). References: - [Quantum algorithms: A survey of applications and end-to-end complexities]( - https://arxiv.org/abs/2310.03011). Dalzell et al. (2023). Ch. 10.2. + [Quantum algorithms: A survey of applications and end-to-end complexities](https://arxiv.org/abs/2310.03011). + Dalzell et al. (2023). Ch. 10.2. """ block_encodings: Tuple[BlockEncoding, ...] = field( diff --git a/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/prepare_t.py b/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/prepare_t.py index d305b6a93..211c6cc15 100644 --- a/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/prepare_t.py +++ b/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/prepare_t.py @@ -110,10 +110,11 @@ class PrepareTFirstQuantizationWithProj(Bloq): s: a register encoding bits for each component of the momenta. References: - [Quantum computation of stopping power for inertial fusion target design]( - https://arxiv.org/abs/2308.12352) page 11, C3 also page 31 App A. Sec 2 b. - [Fault-Tolerant Quantum Simulations of Chemistry in First Quantization]( - https://arxiv.org/abs/2105.12767) page 19, section B + [Quantum computation of stopping power for inertial fusion target design](https://arxiv.org/abs/2308.12352). + page 11, C3 also page 31 App A. Sec 2 b. + + [Fault-Tolerant Quantum Simulations of Chemistry in First Quantization](https://arxiv.org/abs/2105.12767). + page 19, section B """ num_bits_p: int diff --git a/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/projectile.ipynb b/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/projectile.ipynb index b9a407ca6..3184e60fe 100644 --- a/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/projectile.ipynb +++ b/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/projectile.ipynb @@ -128,7 +128,7 @@ " - `flags`: A 4 qubit flag register indicating which component of the Hamiltonian to apply. \n", "\n", "#### References\n", - " - [Fault-Tolerant Quantum Simulations of Chemistry in First Quantization]( https://arxiv.org/abs/2105.12767)\n" + " - [Fault-Tolerant Quantum Simulations of Chemistry in First Quantization](https://arxiv.org/abs/2105.12767). \n" ] }, { @@ -260,7 +260,7 @@ " - `proj`: The system register. Will store a single register (x, y and z) components of size num_bits_n. \n", "\n", "#### References\n", - " - [Fault-Tolerant Quantum Simulations of Chemistry in First Quantization]( https://arxiv.org/abs/2105.12767)\n" + " - [Fault-Tolerant Quantum Simulations of Chemistry in First Quantization](https://arxiv.org/abs/2105.12767). \n" ] }, { diff --git a/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/select_and_prepare.py b/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/select_and_prepare.py index 581e30295..dde8e7eb6 100644 --- a/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/select_and_prepare.py +++ b/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/select_and_prepare.py @@ -233,8 +233,7 @@ class PrepareFirstQuantizationWithProj(PrepareOracle): flags: A 4 qubit flag register indicating which component of the Hamiltonian to apply. References: - [Fault-Tolerant Quantum Simulations of Chemistry in First Quantization]( - https://arxiv.org/abs/2105.12767) + [Fault-Tolerant Quantum Simulations of Chemistry in First Quantization](https://arxiv.org/abs/2105.12767). """ num_bits_p: int @@ -409,8 +408,7 @@ class SelectFirstQuantizationWithProj(SelectOracle): components of size num_bits_n. References: - [Fault-Tolerant Quantum Simulations of Chemistry in First Quantization]( - https://arxiv.org/abs/2105.12767) + [Fault-Tolerant Quantum Simulations of Chemistry in First Quantization](https://arxiv.org/abs/2105.12767). """ num_bits_p: int diff --git a/qualtran/bloqs/chemistry/quad_fermion/givens_bloq.ipynb b/qualtran/bloqs/chemistry/quad_fermion/givens_bloq.ipynb index beaf8caba..c0f4c81e2 100644 --- a/qualtran/bloqs/chemistry/quad_fermion/givens_bloq.ipynb +++ b/qualtran/bloqs/chemistry/quad_fermion/givens_bloq.ipynb @@ -98,7 +98,7 @@ " - `phase_gradient`: QFxp data type representing the phase gradient register \n", "\n", "#### References\n", - " - [Compilation of Fault-Tolerant Quantum Heuristics for Combinatorial Optimization]( https://arxiv.org/abs/2007.07391). Section II-C: Oracles for phasing by cost function. Appendix A: Addition for controlled rotations\n" + " - [Compilation of Fault-Tolerant Quantum Heuristics for Combinatorial Optimization](https://arxiv.org/abs/2007.07391). Section II-C: Oracles for phasing by cost function. Appendix A: Addition for controlled rotations.\n" ] }, { diff --git a/qualtran/bloqs/chemistry/quad_fermion/givens_bloq.py b/qualtran/bloqs/chemistry/quad_fermion/givens_bloq.py index 75a7690f1..8546f518d 100644 --- a/qualtran/bloqs/chemistry/quad_fermion/givens_bloq.py +++ b/qualtran/bloqs/chemistry/quad_fermion/givens_bloq.py @@ -86,10 +86,9 @@ class RealGivensRotationByPhaseGradient(Bloq): phase_gradient: QFxp data type representing the phase gradient register References: - [Compilation of Fault-Tolerant Quantum Heuristics for Combinatorial Optimization]( - https://arxiv.org/abs/2007.07391). + [Compilation of Fault-Tolerant Quantum Heuristics for Combinatorial Optimization](https://arxiv.org/abs/2007.07391). Section II-C: Oracles for phasing by cost function. Appendix A: Addition for controlled - rotations + rotations. """ phasegrad_bitsize: int diff --git a/qualtran/bloqs/chemistry/trotter/grid_ham/inverse_sqrt.py b/qualtran/bloqs/chemistry/trotter/grid_ham/inverse_sqrt.py index 1434f8760..d9beebf87 100644 --- a/qualtran/bloqs/chemistry/trotter/grid_ham/inverse_sqrt.py +++ b/qualtran/bloqs/chemistry/trotter/grid_ham/inverse_sqrt.py @@ -200,8 +200,7 @@ class PolynmomialEvaluationInverseSquareRoot(Bloq): out: Output register to store polynomial approximation to inverse square root. References: - [Quantum computation of stopping power for inertial fusion target design]( - https://arxiv.org/pdf/2308.12352.pdf) + [Quantum computation of stopping power for inertial fusion target design](https://arxiv.org/abs/2308.12352). """ x_sq_bitsize: int diff --git a/qualtran/bloqs/chemistry/trotter/grid_ham/trotter.ipynb b/qualtran/bloqs/chemistry/trotter/grid_ham/trotter.ipynb index 1a9d0dda8..27a61ad02 100644 --- a/qualtran/bloqs/chemistry/trotter/grid_ham/trotter.ipynb +++ b/qualtran/bloqs/chemistry/trotter/grid_ham/trotter.ipynb @@ -49,7 +49,7 @@ " - `out`: Output register to store polynomial approximation to inverse square root. \n", "\n", "#### References\n", - " - [Quantum computation of stopping power for inertial fusion target design]( https://arxiv.org/pdf/2308.12352.pdf)\n" + " - [Quantum computation of stopping power for inertial fusion target design](https://arxiv.org/abs/2308.12352). \n" ] }, { diff --git a/qualtran/bloqs/chemistry/trotter/hubbard/hopping.py b/qualtran/bloqs/chemistry/trotter/hubbard/hopping.py index b5806ed41..7e9a93856 100644 --- a/qualtran/bloqs/chemistry/trotter/hubbard/hopping.py +++ b/qualtran/bloqs/chemistry/trotter/hubbard/hopping.py @@ -174,8 +174,8 @@ class HoppingTileHWP(HoppingTile): system: The system register of size 2 `length`. References: - [Early fault-tolerant simulations of the Hubbard model]( - https://arxiv.org/abs/2012.09238) see Eq. 21 and App E. + [Early fault-tolerant simulations of the Hubbard model](https://arxiv.org/abs/2012.09238). + Eq. 21 and App E. """ def short_name(self) -> str: diff --git a/qualtran/bloqs/chemistry/trotter/hubbard/hubbard.ipynb b/qualtran/bloqs/chemistry/trotter/hubbard/hubbard.ipynb index d0f82b9ab..54fab69e6 100644 --- a/qualtran/bloqs/chemistry/trotter/hubbard/hubbard.ipynb +++ b/qualtran/bloqs/chemistry/trotter/hubbard/hubbard.ipynb @@ -463,7 +463,7 @@ " - `system`: The system register of size 2 `length`. \n", "\n", "#### References\n", - " - [Early fault-tolerant simulations of the Hubbard model]( https://arxiv.org/abs/2012.09238) see Eq. 21 and App E.\n" + " - [Early fault-tolerant simulations of the Hubbard model](https://arxiv.org/abs/2012.09238). Eq. 21 and App E.\n" ] }, { @@ -581,7 +581,7 @@ " - `system`: The system register of size 2 `length`. \n", "\n", "#### References\n", - " - [Early fault-tolerant simulations of the Hubbard model]( https://arxiv.org/abs/2012.09238) Eq. page 13 paragraph 1, and page 14 paragraph 3 right column. The apply 2 batches of $L^2/2$ rotations.\n" + " - [Early fault-tolerant simulations of the Hubbard model](https://arxiv.org/abs/2012.09238). Eq. page 13 paragraph 1, and page 14 paragraph 3 right column. They apply 2 batches of $L^2/2$ rotations.\n" ] }, { diff --git a/qualtran/bloqs/chemistry/trotter/hubbard/interaction.py b/qualtran/bloqs/chemistry/trotter/hubbard/interaction.py index 30b515299..9783ee833 100644 --- a/qualtran/bloqs/chemistry/trotter/hubbard/interaction.py +++ b/qualtran/bloqs/chemistry/trotter/hubbard/interaction.py @@ -91,9 +91,9 @@ class InteractionHWP(Bloq): system: The system register of size 2 `length`. References: - [Early fault-tolerant simulations of the Hubbard model]( - https://arxiv.org/abs/2012.09238) Eq. page 13 paragraph 1, and page - 14 paragraph 3 right column. The apply 2 batches of $L^2/2$ rotations. + [Early fault-tolerant simulations of the Hubbard model](https://arxiv.org/abs/2012.09238). + Eq. page 13 paragraph 1, and page 14 paragraph 3 right column. + They apply 2 batches of $L^2/2$ rotations. """ length: SymbolicInt diff --git a/qualtran/bloqs/chemistry/trotter/trotterized_unitary.ipynb b/qualtran/bloqs/chemistry/trotter/trotterized_unitary.ipynb index 2dbaea6e4..c31c756fd 100644 --- a/qualtran/bloqs/chemistry/trotter/trotterized_unitary.ipynb +++ b/qualtran/bloqs/chemistry/trotter/trotterized_unitary.ipynb @@ -85,8 +85,8 @@ " - `system`: The system register to which to apply the unitary. \n", "\n", "#### References\n", - " - [Theory of Trotter Error with Commutator Scaling]( https://journals.aps.org/prx/abstract/10.1103/PhysRevX.11.011020) Eq. 12 page 7.\n", - " - [Trotter error with commutator scaling for the Fermi-Hubbard model]( https://arxiv.org/abs/2306.10603) see github repo for software to produce splittings.\n" + " - [Theory of Trotter Error with Commutator Scaling](https://journals.aps.org/prx/abstract/10.1103/PhysRevX.11.011020). Eq. 12 page 7.\n", + " - [Trotter error with commutator scaling for the Fermi-Hubbard model](https://arxiv.org/abs/2306.10603). See github repo for software to produce splittings.\n" ] }, { diff --git a/qualtran/bloqs/chemistry/trotter/trotterized_unitary.py b/qualtran/bloqs/chemistry/trotter/trotterized_unitary.py index 91dd0736a..f362ad22e 100644 --- a/qualtran/bloqs/chemistry/trotter/trotterized_unitary.py +++ b/qualtran/bloqs/chemistry/trotter/trotterized_unitary.py @@ -74,11 +74,11 @@ class TrotterizedUnitary(Bloq): system: The system register to which to apply the unitary. References: - [Theory of Trotter Error with Commutator Scaling]( - https://journals.aps.org/prx/abstract/10.1103/PhysRevX.11.011020) Eq. 12 page 7. + [Theory of Trotter Error with Commutator Scaling](https://journals.aps.org/prx/abstract/10.1103/PhysRevX.11.011020) + Eq. 12 page 7. - [Trotter error with commutator scaling for the Fermi-Hubbard model]( - https://arxiv.org/abs/2306.10603) see github repo for software to produce splittings. + [Trotter error with commutator scaling for the Fermi-Hubbard model](https://arxiv.org/abs/2306.10603). + See github repo for software to produce splittings. """ bloqs: Sequence[Bloq] diff --git a/qualtran/bloqs/multiplexers/apply_lth_bloq.ipynb b/qualtran/bloqs/multiplexers/apply_lth_bloq.ipynb index 3e6cdbf17..ff8d6499f 100644 --- a/qualtran/bloqs/multiplexers/apply_lth_bloq.ipynb +++ b/qualtran/bloqs/multiplexers/apply_lth_bloq.ipynb @@ -56,7 +56,7 @@ " - `[user_spec]`: The output registers of the bloqs in `ops`. \n", "\n", "#### References\n", - " - [Encoding Electronic Spectra in Quantum Circuits with Linear T Complexity]( https://arxiv.org/abs/1805.03662). Babbush et al. (2018). Section III.A. and Figure 7.\n" + " - [Encoding Electronic Spectra in Quantum Circuits with Linear T Complexity](https://arxiv.org/abs/1805.03662). Babbush et al. (2018). Section III.A. and Figure 7.\n" ] }, { diff --git a/qualtran/bloqs/multiplexers/apply_lth_bloq.py b/qualtran/bloqs/multiplexers/apply_lth_bloq.py index 8cb4888a5..a0de211a6 100644 --- a/qualtran/bloqs/multiplexers/apply_lth_bloq.py +++ b/qualtran/bloqs/multiplexers/apply_lth_bloq.py @@ -51,8 +51,8 @@ class ApplyLthBloq(UnaryIterationGate, SpecializedSingleQubitControlledExtension [user_spec]: The output registers of the bloqs in `ops`. References: - [Encoding Electronic Spectra in Quantum Circuits with Linear T Complexity]( - https://arxiv.org/abs/1805.03662). Babbush et al. (2018). Section III.A. and Figure 7. + [Encoding Electronic Spectra in Quantum Circuits with Linear T Complexity](https://arxiv.org/abs/1805.03662). + Babbush et al. (2018). Section III.A. and Figure 7. """ # type ignore needed here for Bloq as NDArray parameter diff --git a/qualtran/bloqs/phase_estimation/lp_resource_state.py b/qualtran/bloqs/phase_estimation/lp_resource_state.py index d17866a49..53cadba44 100644 --- a/qualtran/bloqs/phase_estimation/lp_resource_state.py +++ b/qualtran/bloqs/phase_estimation/lp_resource_state.py @@ -106,11 +106,10 @@ class LPResourceState(QPEWindowStateBase): References: - [Optimum phase-shift estimation and the quantum description of the phase - difference](https://journals.aps.org/pra/abstract/10.1103/PhysRevA.54.4564) + [Optimum phase-shift estimation and the quantum description of the phase difference](https://journals.aps.org/pra/abstract/10.1103/PhysRevA.54.4564). - [Encoding Electronic Spectra in Quantum Circuits with Linear T - Complexity](https://arxiv.org/abs/1805.03662) Section II-B + [Encoding Electronic Spectra in Quantum Circuits with Linear T Complexity](https://arxiv.org/abs/1805.03662). + Section II-B """ bitsize: SymbolicInt diff --git a/qualtran/bloqs/rotations/hamming_weight_phasing.ipynb b/qualtran/bloqs/rotations/hamming_weight_phasing.ipynb index 6d718e094..3279a78ef 100644 --- a/qualtran/bloqs/rotations/hamming_weight_phasing.ipynb +++ b/qualtran/bloqs/rotations/hamming_weight_phasing.ipynb @@ -199,7 +199,7 @@ " - `phase_grad`: Phase gradient THRU register of size `O(log2(1/eps))`, to be used to apply the phasing via addition. \n", "\n", "#### References\n", - " - 1. [Compilation of Fault-Tolerant Quantum Heuristics for Combinatorial Optimization] (https://arxiv.org/abs/2007.07391), Appendix A: Addition for controlled rotations\n" + " - [Compilation of Fault-Tolerant Quantum Heuristics for Combinatorial Optimization](https://arxiv.org/abs/2007.07391). Appendix A: Addition for controlled rotations\n" ] }, { diff --git a/qualtran/bloqs/rotations/hamming_weight_phasing.py b/qualtran/bloqs/rotations/hamming_weight_phasing.py index 7f6334fd4..2c55d84b5 100644 --- a/qualtran/bloqs/rotations/hamming_weight_phasing.py +++ b/qualtran/bloqs/rotations/hamming_weight_phasing.py @@ -154,8 +154,8 @@ class HammingWeightPhasingViaPhaseGradient(GateWithRegisters): apply the phasing via addition. References: - 1. [Compilation of Fault-Tolerant Quantum Heuristics for Combinatorial Optimization] - (https://arxiv.org/abs/2007.07391), Appendix A: Addition for controlled rotations + [Compilation of Fault-Tolerant Quantum Heuristics for Combinatorial Optimization](https://arxiv.org/abs/2007.07391). + Appendix A: Addition for controlled rotations """ bitsize: int diff --git a/qualtran/bloqs/rotations/phase_gradient.ipynb b/qualtran/bloqs/rotations/phase_gradient.ipynb index 0245fccf8..ffe411603 100644 --- a/qualtran/bloqs/rotations/phase_gradient.ipynb +++ b/qualtran/bloqs/rotations/phase_gradient.ipynb @@ -70,7 +70,7 @@ "\n", "#### References\n", " - [Compilation of Fault-Tolerant Quantum Heuristics for Combinatorial Optimization](https://arxiv.org/abs/2007.07391). Appendix A: Addition for controlled rotations\n", - " - [Halving the cost of quantum addition](https://quantum-journal.org/papers/q-2018-06-18-74/pdf/). \n" + " - [Halving the cost of quantum addition](https://arxiv.org/abs/1709.06648). Gidney (2017).\n" ] }, { diff --git a/qualtran/bloqs/rotations/phase_gradient.py b/qualtran/bloqs/rotations/phase_gradient.py index 5a1d481fb..0a6dded65 100644 --- a/qualtran/bloqs/rotations/phase_gradient.py +++ b/qualtran/bloqs/rotations/phase_gradient.py @@ -88,7 +88,8 @@ class PhaseGradientUnitary(GateWithRegisters): [Compilation of Fault-Tolerant Quantum Heuristics for Combinatorial Optimization](https://arxiv.org/abs/2007.07391) Appendix A: Addition for controlled rotations - [Halving the cost of quantum addition](https://quantum-journal.org/papers/q-2018-06-18-74/pdf/) + [Halving the cost of quantum addition](https://arxiv.org/abs/1709.06648). + Gidney (2017). """ bitsize: 'SymbolicInt' diff --git a/qualtran/bloqs/state_preparation/state_preparation_alias_sampling.ipynb b/qualtran/bloqs/state_preparation/state_preparation_alias_sampling.ipynb index fd94fe55f..081add39d 100644 --- a/qualtran/bloqs/state_preparation/state_preparation_alias_sampling.ipynb +++ b/qualtran/bloqs/state_preparation/state_preparation_alias_sampling.ipynb @@ -241,7 +241,8 @@ "Total space will be $(2 \\log(L) + \\log(d) + 2 \\mu + 1)$ work qubits + $log(L)$ ancillas for QROM.\n", "\n", "#### References\n", - " - [1] [Qubitization of Arbitrary Basis Quantum Chemistry Leveraging Sparsity and Low Rank Factorization](https://arxiv.org/pdf/1902.02134#page=15.30) Berry et al. (2019). Section 5, Eqs. 43, 44. [2] [Encoding Electronic Spectra in Quantum Circuits with Linear T Complexity](https://arxiv.org/abs/1805.03662). Babbush et al. (2018). Section III.D. and Figure 11.\n" + " - [Qubitization of Arbitrary Basis Quantum Chemistry Leveraging Sparsity and Low Rank Factorization](https://arxiv.org/abs/1902.02134). Berry et al. (2019). Section 5, Eqs. 43, 44.\n", + " - [Encoding Electronic Spectra in Quantum Circuits with Linear T Complexity](https://arxiv.org/abs/1805.03662). Babbush et al. (2018). Section III.D. and Figure 11.\n" ] }, { diff --git a/qualtran/bloqs/state_preparation/state_preparation_alias_sampling.py b/qualtran/bloqs/state_preparation/state_preparation_alias_sampling.py index 258a33c87..4850ef95d 100644 --- a/qualtran/bloqs/state_preparation/state_preparation_alias_sampling.py +++ b/qualtran/bloqs/state_preparation/state_preparation_alias_sampling.py @@ -348,9 +348,10 @@ class SparseStatePreparationAliasSampling(PrepareOracle): Total space will be $(2 \log(L) + \log(d) + 2 \mu + 1)$ work qubits + $log(L)$ ancillas for QROM. References: - [1] [Qubitization of Arbitrary Basis Quantum Chemistry Leveraging Sparsity and Low Rank Factorization](https://arxiv.org/pdf/1902.02134#page=15.30) + [Qubitization of Arbitrary Basis Quantum Chemistry Leveraging Sparsity and Low Rank Factorization](https://arxiv.org/abs/1902.02134). Berry et al. (2019). Section 5, Eqs. 43, 44. - [2] [Encoding Electronic Spectra in Quantum Circuits with Linear T Complexity](https://arxiv.org/abs/1805.03662). + + [Encoding Electronic Spectra in Quantum Circuits with Linear T Complexity](https://arxiv.org/abs/1805.03662). Babbush et al. (2018). Section III.D. and Figure 11. """ diff --git a/qualtran/linalg/matrix.py b/qualtran/linalg/matrix.py index cc800bbec..d87eb7e1e 100644 --- a/qualtran/linalg/matrix.py +++ b/qualtran/linalg/matrix.py @@ -13,12 +13,13 @@ # limitations under the License. import numpy as np -from cirq.testing.lin_alg_utils import random_orthogonal from numpy.typing import NDArray def random_hermitian_matrix(dim: int, random_state: np.random.RandomState) -> NDArray: """Return a random Hermitian matrix of given dimension and norm <= 1.""" + from cirq.testing.lin_alg_utils import random_orthogonal + a = random_orthogonal(dim, random_state=random_state) return (a + a.conj().T) / 2 diff --git a/qualtran/resource_counting/t_counts_from_sigma.py b/qualtran/resource_counting/t_counts_from_sigma.py index 3eaf867cf..02c6673c6 100644 --- a/qualtran/resource_counting/t_counts_from_sigma.py +++ b/qualtran/resource_counting/t_counts_from_sigma.py @@ -14,8 +14,6 @@ import warnings from typing import Mapping -import cirq - from qualtran import Bloq, Controlled from qualtran.symbolics import ceil, SymbolicInt @@ -23,6 +21,9 @@ def t_counts_from_sigma(sigma: Mapping['Bloq', SymbolicInt]) -> SymbolicInt: """Aggregates T-counts from a sigma dictionary by summing T-costs for all rotation bloqs.""" warnings.warn("This function is deprecated. Use `get_cost_value`.", DeprecationWarning) + # TODO: remove dependence on cirq.has_stabilizer_effect. + import cirq + from qualtran.bloqs.basic_gates import TGate, Toffoli, TwoBitCSwap from qualtran.cirq_interop.t_complexity_protocol import TComplexity from qualtran.resource_counting.classify_bloqs import bloq_is_rotation diff --git a/qualtran/simulation/tensor/_dense_test.py b/qualtran/simulation/tensor/_dense_test.py index 991d68df9..45f1e4223 100644 --- a/qualtran/simulation/tensor/_dense_test.py +++ b/qualtran/simulation/tensor/_dense_test.py @@ -15,8 +15,8 @@ from functools import cached_property from typing import Dict, List -import cirq import numpy as np +import pytest import quimb.tensor as qtn from attrs import frozen @@ -121,6 +121,7 @@ def build_composite_bloq(self, bb: 'BloqBuilder', s: 'SoquetT') -> Dict[str, 'So def test_nest(): + cirq = pytest.importorskip('cirq') x = XNest() should_be = cirq.unitary(cirq.X) np.testing.assert_allclose(should_be, x.tensor_contract()) @@ -128,6 +129,7 @@ def test_nest(): def test_double_nest(): + cirq = pytest.importorskip('cirq') xx = XDoubleNest() should_be = cirq.unitary(cirq.X) np.testing.assert_allclose(should_be, xx.tensor_contract()) @@ -150,6 +152,7 @@ def build_composite_bloq( def test_bloq_with_non_trivial_inds(): + cirq = pytest.importorskip('cirq') bloq = BloqWithNonTrivialInds() assert_valid_bloq_decomposition(bloq) cirq_qubits = cirq.LineQubit.range(2) diff --git a/qualtran/symbolics/types.py b/qualtran/symbolics/types.py index b9f017fc0..18714b6f8 100644 --- a/qualtran/symbolics/types.py +++ b/qualtran/symbolics/types.py @@ -15,17 +15,16 @@ import sympy from attrs import field, frozen, validators -from cirq._doc import document from typing_extensions import TypeIs SymbolicFloat = Union[float, sympy.Expr] -document(SymbolicFloat, """A floating point value or a sympy expression.""") +"""A floating point value or a sympy expression.""" SymbolicInt = Union[int, sympy.Expr] -document(SymbolicFloat, """A floating point value or a sympy expression.""") +"""An integer value or a sympy expression.""" SymbolicComplex = Union[complex, sympy.Expr] -document(SymbolicComplex, """A complex value or a sympy expression.""") +"""A complex value or a sympy expression.""" @frozen