Skip to content

Commit 8ec6507

Browse files
authored
Soquet to _Soquet, _QVar, Soquet and QVar (#1831)
In Qualtran, we compose quantum operations into larger operations by connecting the outputs of a predecessor bloq to the input "ports" of a successor bloq. We've used the `Soquet` frozen attrs class to represent these nodes in the compute graph, which address the "ports" of a particular instance of the bloq. Previously, the immutable `Soquet` object represented both the "quantum variables" being passed around during bloq building as well as the nodes of the compute graph. With this PR, these concerns are separated. The compute graph nodes are frozen dataclasses of type `_Soquet`, and the quantum variables are *mutable* objects of type `_QVar`. We will put additional helper attributes and methods onto `_QVar` to assist in bloq building. For backwards compatibility, the `Soquet` name is now a `typing.Protocol` that encapsulates the duck-typing behavior of `_Soquet` and `_QVar`. Bloqs in the wild should not have to update the type annotations in `build_composite_bloq` with this backwards compatibilty typing shim. `isinstance(..., Soquet)` checks will emit a deprecation warning and return True for *either* `_Soquet` or `_QVar`. If you're using isinstance(soq, Soquet) to determine whether an item is a single object or an ndarray of those objects, use `BloqBuilder.is_single(x)` or `BloqBuilder.is_ndarray(x)`. See the documentation in `QVarT` for an example. If you're developing library functionality, you can port isinstance checks to either `_Soquet` or `QVar` as appropriate. ---- *Backwards compatibility note:* Despite the fundamental change to the data model, this PR is designed to be runtime-backwards compatible with deprecation warnings. The way I've accomplished this is through some dark Python incantations, so no guarantees. Please send reports if this breaks something. Note that type checkers like `mypy` will likely still complain about things even if the code runs fine without modification following this PR. ---- ### High-priority deprecations - Please avoid constructing `Soquet`s yourself. You really shouldn't have been doing this. `_Soquet` is now "private" - If you're copying or modifying `CompositeBloq` by iterating over connections and using `map_soqs`, please urgently upgrade your code to use the new idiom where you must initialize the mapping with `bb.initial_soq_map`. The shim I put in is brittle. #1742
1 parent 80364b6 commit 8ec6507

24 files changed

Lines changed: 601 additions & 308 deletions

qualtran/__init__.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,11 @@
4646
CompositeBloq,
4747
BloqBuilder,
4848
DidNotFlattenAnythingError,
49+
Soquet,
4950
SoquetT,
5051
ConnectionT,
52+
QVar,
53+
QVarT,
5154
)
5255

5356
from ._infra.data_types import (
@@ -84,14 +87,7 @@
8487

8588
# Internal imports: none
8689
# External imports: none
87-
from ._infra.quantum_graph import (
88-
BloqInstance,
89-
Connection,
90-
DanglingT,
91-
LeftDangle,
92-
RightDangle,
93-
Soquet,
94-
)
90+
from ._infra.quantum_graph import BloqInstance, Connection, DanglingT, LeftDangle, RightDangle
9591

9692
from ._infra.gate_with_registers import GateWithRegisters
9793

qualtran/_infra/adjoint.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,28 @@
1818

1919
from attrs import frozen
2020

21-
from .composite_bloq import _binst_to_cxns, _cxns_to_soq_dict, _map_soqs, _reg_to_soq, BloqBuilder
21+
from .composite_bloq import (
22+
_binst_to_cxns,
23+
_cxns_to_soq_dict,
24+
_map_soqs,
25+
_reg_to_soq,
26+
_SoquetT,
27+
BloqBuilder,
28+
QVarT,
29+
)
2230
from .gate_with_registers import GateWithRegisters
2331
from .quantum_graph import LeftDangle, RightDangle
2432
from .registers import Signature
2533

2634
if TYPE_CHECKING:
2735
import cirq
2836

29-
from qualtran import Bloq, CompositeBloq, Register, Signature, SoquetT
37+
from qualtran import Bloq, CompositeBloq, Register, Signature
3038
from qualtran.drawing import WireSymbol
3139
from qualtran.resource_counting import BloqCountDictT, SympySymbolAllocator
3240

3341

34-
def _adjoint_final_soqs(cbloq: 'CompositeBloq', new_signature: Signature) -> Dict[str, 'SoquetT']:
42+
def _adjoint_final_soqs(cbloq: 'CompositeBloq', new_signature: Signature) -> Dict[str, '_SoquetT']:
3543
"""`CompositeBloq.final_soqs()` but backwards."""
3644
if LeftDangle not in cbloq._binst_graph:
3745
return {}
@@ -57,15 +65,15 @@ def _adjoint_cbloq(cbloq: 'CompositeBloq') -> 'CompositeBloq':
5765
# First, we reverse the registers to initialize the BloqBuilder.
5866
old_signature = cbloq.signature
5967
new_signature = cbloq.signature.adjoint()
60-
old_i_soqs = [_reg_to_soq(RightDangle, reg) for reg in old_signature.rights()]
61-
new_i_soqs = [_reg_to_soq(LeftDangle, reg) for reg in new_signature.lefts()]
62-
soq_map: List[Tuple[SoquetT, SoquetT]] = list(zip(old_i_soqs, new_i_soqs))
6368

6469
# Then we reverse the order of subbloqs
6570
bloqnections = reversed(list(cbloq.iter_bloqnections()))
6671

6772
# And add subbloq.adjoint() back in for each subbloq.
6873
bb, _ = BloqBuilder.from_signature(new_signature)
74+
old_i_soqs = [_reg_to_soq(RightDangle, reg) for reg in old_signature.rights()]
75+
new_i_soqs = [bb._reg_to_qvar(LeftDangle, reg) for reg in new_signature.lefts()]
76+
soq_map: List[Tuple[_SoquetT, QVarT]] = list(zip(old_i_soqs, new_i_soqs))
6977
for binst, preds, succs in bloqnections:
7078
# Instead of get_me returning the right element of a predecessor connection,
7179
# it's the left element of a successor connection.

qualtran/_infra/composite_bloq.ipynb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@
343343
"bb, _ = BloqBuilder.from_signature(cbloq.signature)\n",
344344
"\n",
345345
"# We'll have to \"map\" the soquets from our template cbloq to our new one\n",
346-
"soq_map: List[Tuple[SoquetT, SoquetT]] = []\n",
346+
"soq_map = bb.initial_soq_map(cbloq.signature.lefts())\n",
347347
" \n",
348348
"# Iteration yields each bloq instance as well as its input and output soquets.\n",
349349
"for binst, in_soqs, old_out_soqs in cbloq.iter_bloqsoqs():\n",
@@ -512,8 +512,8 @@
512512
"# Go through and decompose each subbloq\n",
513513
"# We'll manually code this up in this notebook since this isn't a useful operation.\n",
514514
"bb, _ = BloqBuilder.from_signature(flat_three_p.signature)\n",
515-
"soq_map: List[Tuple[SoquetT, SoquetT]] = []\n",
516-
" \n",
515+
"soq_map: List[Tuple[SoquetT, SoquetT]] = bb.initial_soq_map(flat_three_p.signature.lefts())\n",
516+
"\n",
517517
"for binst, in_soqs, old_out_soqs in flat_three_p.iter_bloqsoqs():\n",
518518
" in_soqs = bb.map_soqs(in_soqs, soq_map)\n",
519519
" \n",

0 commit comments

Comments
 (0)