Skip to content

Commit 8ee3e02

Browse files
authored
Merge branch 'main' into 2024/SR/planted-noisy-kxor-algorithm
2 parents f66be98 + 8ec6507 commit 8ee3e02

90 files changed

Lines changed: 6840 additions & 379 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gemini/config.yaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# Summary: configure Gemini Code Assist (https://codeassist.google/).
16+
# See https://github.com/marketplace/gemini-code-assist for more info.
17+
18+
have_fun: false
19+
code_review:
20+
disable: false
21+
comment_severity_threshold: HIGH
22+
max_review_comments: -1
23+
pull_request_opened:
24+
help: false
25+
summary: false
26+
code_review: true
27+
include_drafts: false
28+
ignore_patterns: []

dev_tools/dump-bloq-manifest.py

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""
16+
Script to dump a manifest of all bloq classes and bloq examples.
17+
18+
This script finds all Bloq classes and examples in the library, serializes the examples
19+
to their objectstring representation, and writes the list of class names and
20+
example objectstrings to `qualtran/bloqs/manifest.py`.
21+
22+
See `qualtran.l1.load_objectstring()` to load bloq objects from these strings.
23+
"""
24+
25+
from functools import cached_property
26+
from typing import List, Tuple
27+
28+
from attrs import frozen
29+
from qualtran_dev_tools.bloq_finder import get_bloq_classes, get_bloq_examples
30+
from qualtran_dev_tools.git_tools import get_git_root
31+
32+
from qualtran import Bloq
33+
from qualtran.l1 import eval_cvalue_node, parse_objectstring, to_cobject_node
34+
from qualtran.l1.nodes import CArgNode, CObjectNode, LiteralNode
35+
36+
MAXLEN = 300
37+
"""If the objectstring is too long, we make the executive decision to truncate it."""
38+
39+
COPYRIGHT_NOTICE = """# Copyright 2026 Google LLC
40+
#
41+
# Licensed under the Apache License, Version 2.0 (the "License");
42+
# you may not use this file except in compliance with the License.
43+
# You may obtain a copy of the License at
44+
#
45+
# https://www.apache.org/licenses/LICENSE-2.0
46+
#
47+
# Unless required by applicable law or agreed to in writing, software
48+
# distributed under the License is distributed on an "AS IS" BASIS,
49+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
50+
# See the License for the specific language governing permissions and
51+
# limitations under the License."""
52+
53+
54+
@frozen
55+
class BloqExampleListItem:
56+
"""Formatting a bloq example and its serialized form.
57+
58+
Attributes:
59+
bloq: The bloq instance from the example.
60+
bloq_example_name: The name of the bloq example.
61+
cobject_node: The CObjectNode representing the serialized bloq.
62+
"""
63+
64+
bloq: Bloq
65+
bloq_example_name: str
66+
cobject_node: CObjectNode
67+
68+
@cached_property
69+
def objectstring(self) -> str:
70+
"""Returns the canonical string representation of the CObjectNode."""
71+
return self.cobject_node.canonical_str()
72+
73+
def maybe_commented_out(self, be_column_width: int = 30) -> Tuple[str, str, str]:
74+
"""Generates a string representation for the manifest, potentially commented out.
75+
76+
This method checks if the object string is too long, unparsable, unloadable, or
77+
if the re-loaded bloq is unequal to the original. If any of these conditions are met,
78+
it returns a commented-out string with a reason. Otherwise, it returns the
79+
executable string representation.
80+
81+
Args:
82+
be_column_width: The width of the column for the bloq example name for nicer formatting.
83+
84+
Returns:
85+
manifest_entry_str: The string to be written to the manifest (possibly commented out).
86+
reason: A reason string if it was commented out (empty otherwise).
87+
reason_details: Detailed error message if applicable (empty otherwise).
88+
"""
89+
quoted_be = f"'{self.bloq_example_name}',"
90+
be = f'{quoted_be:{be_column_width}}'
91+
be2 = f'{quoted_be:{be_column_width+2}}'
92+
93+
if len(self.objectstring) > MAXLEN:
94+
trunc = repr(self.objectstring)[:120] + '...'
95+
return f'# ({be}{trunc}', 'too long', ''
96+
97+
try:
98+
node = parse_objectstring(self.objectstring)
99+
except Exception as e: # pylint: disable=broad-except
100+
return f'# ({be}{self.objectstring!r}),', 'unparsable', str(e)
101+
102+
try:
103+
bloq = eval_cvalue_node(node, safe=False)
104+
except Exception as e: # pylint: disable=broad-except
105+
return f'# ({be}{self.objectstring!r}),', 'unloadable', str(e)
106+
107+
if bloq != self.bloq:
108+
return f'# ({be}{self.objectstring!r}),', 'unequal', ''
109+
110+
return f'({be2}{self.objectstring!r}),', '', ''
111+
112+
113+
def main():
114+
"""Main entry point for the script.
115+
116+
Finds all bloq classes and examples, processes them, and writes the
117+
`BLOQ_CLASS_NAMES` and `BLOQ_EXAMPLE_OBJECTSTRINGS` lists to
118+
`qualtran/bloqs/manifest.py`.
119+
"""
120+
bcs = get_bloq_classes()
121+
names = sorted(bc._class_name_in_pkg_() for bc in bcs)
122+
123+
bes = get_bloq_examples()
124+
items: List[BloqExampleListItem] = []
125+
for be in bes:
126+
bloq = be.make()
127+
try:
128+
cobject_node = to_cobject_node(bloq)
129+
assert isinstance(cobject_node, CObjectNode)
130+
except Exception as e: # pylint: disable=broad-except
131+
cobject_node = CObjectNode(
132+
name=bloq._class_name_in_pkg_(), cargs=[CArgNode(None, LiteralNode(str(e)))]
133+
)
134+
items.append(
135+
BloqExampleListItem(bloq=bloq, bloq_example_name=be.name, cobject_node=cobject_node)
136+
)
137+
138+
items = sorted(items, key=lambda x: x.objectstring)
139+
include_commented_out = True
140+
be_objectstrings = []
141+
for item in items:
142+
serstr, reason, details = item.maybe_commented_out()
143+
144+
if (not reason) or include_commented_out:
145+
be_objectstrings.append(serstr)
146+
147+
if reason:
148+
reason = f'({reason})'
149+
print(f"Skipping {reason:20s} {serstr}")
150+
151+
if details:
152+
print(f' {"":20s} ->', details)
153+
154+
reporoot = get_git_root()
155+
with (reporoot / 'qualtran/bloqs/manifest.py').open('w') as f:
156+
f.write(COPYRIGHT_NOTICE)
157+
f.write('\n\n')
158+
f.write('# This file is autogenerated\n')
159+
f.write('# See dev_tools/dump-bloq-manifest.py\n')
160+
f.write('# fmt: off\n\n')
161+
f.write('BLOQ_CLASS_NAMES = [\n')
162+
f.write('\n'.join([f' "{name}",' for name in names]))
163+
f.write('\n]\n')
164+
165+
f.write('\n')
166+
167+
f.write('BLOQ_EXAMPLE_OBJECTSTRINGS = [\n')
168+
f.write('\n'.join([f' {objstr}' for objstr in be_objectstrings]))
169+
f.write('\n]\n')
170+
171+
172+
if __name__ == '__main__':
173+
main()

dev_tools/qualtran_dev_tools/make_reference_docs/_make.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,12 @@
7878
]
7979
MINIMAL = True
8080

81-
DEFINED_IN_CONTAINER_EXCEPTIONS = ['qualtran.dtype', 'qualtran.exception', 'qualtran.cirq_interop']
81+
DEFINED_IN_CONTAINER_EXCEPTIONS = [
82+
'qualtran.dtype',
83+
'qualtran.exception',
84+
'qualtran.cirq_interop',
85+
'qualtran.l1.nodes',
86+
]
8287

8388

8489
def _get_all_aliases(obj: Union[griffe.Object, griffe.Alias]) -> Set[str]:

docs/bloq_infra.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,5 @@ types (``Register``), and algorithms (``CompositeBloq``).
5555
drawing/musical_score.ipynb
5656
drawing/drawing_call_graph.ipynb
5757
simulation/xcheck_classical_quimb.ipynb
58+
l1/L1-Objectstring.ipynb
5859
Autodoc.ipynb

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: 18 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.
@@ -174,6 +182,10 @@ def __str__(self) -> str:
174182
"""Delegate to subbloq's `__str__` method."""
175183
return f'{str(self.subbloq)}†'
176184

185+
@classmethod
186+
def _pkg_(cls) -> str:
187+
return 'qualtran'
188+
177189
def wire_symbol(
178190
self, reg: Optional['Register'], idx: Tuple[int, ...] = tuple()
179191
) -> 'WireSymbol':

qualtran/_infra/bloq.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,10 @@ def wire_symbol(
695695
def __str__(self):
696696
return self.__class__.__name__
697697

698+
@classmethod
699+
def _pkg_(cls) -> str:
700+
return '.'.join(cls.__module__.split('.')[:-1])
701+
698702
@classmethod
699703
def _class_name_in_pkg_(cls) -> str:
700704
"""The bloq class's name with its package.
@@ -703,5 +707,4 @@ def _class_name_in_pkg_(cls) -> str:
703707
`qualtran.bloqs.*`. Each bloq class is defined in a module (i.e. the
704708
"*.py" file) and re-exported one level up.
705709
"""
706-
pkg = '.'.join(cls.__module__.split('.')[:-1])
707-
return f'{pkg}.{cls.__name__}'
710+
return f'{cls._pkg_()}.{cls.__name__}'

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)