Skip to content

Commit 1087510

Browse files
authored
Doc system notebook execution: WriteIfDifferent. Misc doc fixes (#1648)
Feature: `jupyter_autogen.py` will only re-write `*.ipynb` files if there's been a change. The tool to execute the notebooks is already to set up to only re-execute notebooks if they're out of date. But every time you re-run the first autogen script, it will re-up the modification date and everything will always be re-evaluated, which is expensive. Misc fixes - Use a distinct extension for `autotoc.rst.inc`, so sphinx doesn't treat it as a page and emit warnings about it - include missing TOC entries for notebooks - fix flaky notebook assertion by using more idiomatic sympy methods - grammar and arg docstring fixes in gf_poly
1 parent 1d1b3e2 commit 1087510

11 files changed

Lines changed: 119 additions & 32 deletions

File tree

dev_tools/autogenerate-bloqs-notebooks-v2.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
'chemistry/writing_algorithms.ipynb',
6666
'cryptography/rsa/factoring-via-modexp.ipynb',
6767
'state_preparation/state_preparation_via_rotation_tutorial.ipynb',
68+
'optimization/k_xor_sat/kikuchi_guiding_state_tutorial.ipynb',
6869
]
6970

7071

dev_tools/qualtran_dev_tools/jupyter_autogen.py

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import abc
1818
import inspect
19+
import io
1920
import re
2021
import textwrap
2122
from pathlib import Path
@@ -45,7 +46,6 @@
4546
show_bloq(bloq)\
4647
"""
4748

48-
4949
_K_CQ_AUTOGEN = 'cq.autogen'
5050
"""The jupyter metadata key we use to identify cells we've autogenerated.
5151
@@ -310,6 +310,84 @@ def _init_notebook(
310310
return nb, nb_path
311311

312312

313+
class WriteIfDifferent:
314+
"""A file-like object that only writes to disk if the new content
315+
differs from the existing content.
316+
317+
Args:
318+
path: The path to write, which may already exist.
319+
"""
320+
321+
def __init__(self, path: Path):
322+
self.path = path
323+
self._buffer = io.StringIO()
324+
325+
def write(self, s: str):
326+
return self._buffer.write(s)
327+
328+
def writelines(self, lines):
329+
for line in lines:
330+
self.write(line)
331+
332+
def flush(self):
333+
self._buffer.flush()
334+
335+
def close(self):
336+
"""Closes the adapter.
337+
338+
This triggers the comparison of buffered content
339+
with the disk file's content and writes to disk only if different.
340+
"""
341+
new_content = self._buffer.getvalue()
342+
self._buffer.close()
343+
344+
existing_content = None
345+
if self.path.is_file():
346+
with self.path.open('r') as f_read:
347+
existing_content = f_read.read()
348+
if new_content == existing_content:
349+
print(f"{self.path} unchanged.")
350+
return
351+
352+
with self.path.open('w') as f_write:
353+
f_write.write(new_content)
354+
355+
def __enter__(self):
356+
return self
357+
358+
def __exit__(self, exc_type, exc_val, exc_tb):
359+
self.close()
360+
# Do not suppress exceptions from the 'with' block body.
361+
return False
362+
363+
@property
364+
def closed(self):
365+
return self._buffer.closed
366+
367+
def readable(self):
368+
"""Returns False, as this adapter is write-only like a file from `open('w')`."""
369+
return False
370+
371+
def writable(self):
372+
"""Returns True if the adapter is not closed, False otherwise."""
373+
return self._buffer.writable()
374+
375+
def seekable(self):
376+
"""Returns False, as this adapter is not seekable like a disk file opened in 'w' mode."""
377+
return False
378+
379+
def tell(self):
380+
"""Returns the current stream position in the internal buffer."""
381+
return self._buffer.tell()
382+
383+
def truncate(self, size=None):
384+
"""
385+
Resizes the internal buffer to the given number of bytes.
386+
If size is not specified, resizes to the current position.
387+
"""
388+
return self._buffer.truncate(size)
389+
390+
313391
def render_notebook(nbspec: NotebookSpecV2) -> None:
314392
# 1. get a notebook (existing or empty)
315393
nb, nb_path = _init_notebook(path_stem=nbspec.path_stem, directory=nbspec.directory)
@@ -349,5 +427,5 @@ def render_notebook(nbspec: NotebookSpecV2) -> None:
349427
nb.cells.append(new_nbnode)
350428

351429
# 5. Write the notebook.
352-
with nb_path.open('w') as f:
353-
nbformat.write(nb, f)
430+
with WriteIfDifferent(nb_path) as woc:
431+
nbformat.write(nb, woc)

dev_tools/qualtran_dev_tools/reference_docs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ def generate_ref_toc(reporoot: Path):
386386
for path in page_paths:
387387
grouped_paths[path.parent].append(path)
388388

389-
with (output_dir / 'autotoc.rst').open('w') as f:
389+
with (output_dir / 'autotoc.rst.inc').open('w') as f:
390390
write_ref_toc(f, grouped_paths, output_dir)
391391

392392

docs/.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
_build/
33

44
# Generated reference toc
5-
reference/autotoc.rst
5+
reference/autotoc.rst.inc
66

77
# All the generated reference pages
88
**/*.md

docs/bloq_infra.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ types (``Register``), and algorithms (``CompositeBloq``).
3939
cirq_interop/t_complexity.ipynb
4040
qref_interop/bartiq_demo.ipynb
4141
_infra/gate_with_registers.ipynb
42+
bloqs/mcmt/specialized_ctrl.ipynb
4243
drawing/graphviz.ipynb
4344
drawing/musical_score.ipynb
4445
drawing/drawing_call_graph.ipynb

docs/bloqs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Bloqs Library
2121
chemistry/writing_algorithms.ipynb
2222
cryptography/rsa/factoring-via-modexp.ipynb
2323
state_preparation/state_preparation_via_rotation_tutorial.ipynb
24+
optimization/k_xor_sat/kikuchi_guiding_state_tutorial.ipynb
2425

2526
.. toctree::
2627
:maxdepth: 1

docs/reference/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ in ``qualtran``.
1212
qualtran.md
1313

1414

15-
.. include:: autotoc.rst
15+
.. include:: autotoc.rst.inc

qualtran/bloqs/chemistry/trotter/hubbard/qpe_cost_optimization.ipynb

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -535,9 +535,7 @@
535535
"source": [
536536
"# check the symbolic counts match the expected counts\n",
537537
"t_counts_orig, n_rot = t_and_rot_counts_from_bloq(trotter_step)\n",
538-
"# for some reason substituting both at once leads to a precision error\n",
539-
"t_counts = t_counts.evalf(subs={s_eps_r: eps_single_rot})\n",
540-
"t_counts_symb = t_counts.evalf(subs={s_length: length})\n",
538+
"t_counts_symb = t_counts.subs(s_length, length)\n",
541539
"assert t_counts_orig == t_counts_symb"
542540
]
543541
},
@@ -573,25 +571,26 @@
573571
"metadata": {},
574572
"outputs": [],
575573
"source": [
576-
"symb_t_count = qpe_cost_symb.evalf(\n",
577-
" subs={\n",
574+
"symb_t_count = qpe_cost_symb.subs(\n",
575+
" {\n",
578576
" s_length: length,\n",
579577
" s_delta_ht: delta_ht,\n",
580578
" s_delta_pe: delta_pe,\n",
581579
" s_delta_ts: delta_ts,\n",
582580
" s_xi: xi_bound,\n",
583581
" s_n_rot: n_rot,\n",
584-
" }\n",
585-
")\n",
586-
"symb_t_count = symb_t_count.evalf(subs={s_p: prod_ord}, maxn=500)\n",
582+
" s_p: prod_ord,\n",
583+
" }.items()\n",
584+
").n()\n",
587585
"tot_t_count = qpe_t_count(delta_pe, delta_ts, delta_ht, n_rot, n_t, xi_bound, prod_ord)\n",
588-
"assert int(symb_t_count) == int(tot_t_count)"
586+
"import math\n",
587+
"assert math.ceil(symb_t_count) == math.ceil(tot_t_count)"
589588
]
590589
}
591590
],
592591
"metadata": {
593592
"kernelspec": {
594-
"display_name": "qualtran",
593+
"display_name": "Python 3 (ipykernel)",
595594
"language": "python",
596595
"name": "python3"
597596
},
@@ -605,9 +604,9 @@
605604
"name": "python",
606605
"nbconvert_exporter": "python",
607606
"pygments_lexer": "ipython3",
608-
"version": "3.10.4"
607+
"version": "3.11.8"
609608
}
610609
},
611610
"nbformat": 4,
612-
"nbformat_minor": 2
611+
"nbformat_minor": 4
613612
}

qualtran/bloqs/gf_poly_arithmetic/gf_poly_split_and_join.ipynb

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
"galois field GF($p^m$). Given an input quantum register representing a degree $n$ polynomial\n",
4343
"$f(x)$, this bloq splits it into $n + 1$ registers of type $QGF(p, m)$.\n",
4444
"\n",
45-
"Give a polynomial\n",
45+
"Given a polynomial\n",
4646
"$$\n",
4747
" f(x) = \\sum_{i = 0}^{n} a_{i} x^{i} \\\\ \\forall a_{i} \\in GF(p^m)\n",
4848
"$$\n",
@@ -55,7 +55,7 @@
5555
"See `GFPolyJoin` for the inverse operation.\n",
5656
"\n",
5757
"#### Parameters\n",
58-
" - `qgf_poly`: An instance of `QGFPoly` type that represents a degree $n$ polynomial defined over a galois field GF($p^m$). \n",
58+
" - `dtype`: An instance of `QGFPoly` type that represents a degree $n$ polynomial defined over a galois field GF($p^m$). \n",
5959
"\n",
6060
"#### Registers\n",
6161
" - `reg`: The register to be split. On its left, it is of the type `qgf_poly`. On the right, it is an array of `QGF`s of shape `(qgf_poly.degree + 1,)`.\n"
@@ -136,12 +136,12 @@
136136
"representing coefficients of a degree $n$ polynomial $f(x)$, this bloq joins it into\n",
137137
"a register of type `QGFPoly`.\n",
138138
"\n",
139-
"Give a polynomial\n",
139+
"Given a polynomial\n",
140140
"$$\n",
141141
" f(x) = \\sum_{i = 0}^{n} a_{i} x^{i} \\\\ \\forall a_{i} \\in GF(p^m)\n",
142142
"$$\n",
143143
"\n",
144-
"the bloq joins register representing coefficients of the polynomial in big-endian representation\n",
144+
"the bloq joins registers representing coefficients of the polynomial in big-endian representation\n",
145145
"such that\n",
146146
"$$\n",
147147
" \\ket{a_{n}}\\ket{a_{n - 1}} \\cdots \\ket{a_0} \\xrightarrow{\\text{join}} \\ket{f(x)}\n",
@@ -150,11 +150,10 @@
150150
"See `GFPolySplit` for the inverse operation.\n",
151151
"\n",
152152
"#### Parameters\n",
153-
" - `qgf_poly`: An instance of `QGFPoly` type that represents a degree $n$ polynomial defined over a galois field GF($p^m$). \n",
153+
" - `dtype`: An instance of `QGFPoly` type that represents a degree $n$ polynomial defined over a galois field GF($p^m$). \n",
154154
"\n",
155155
"#### Registers\n",
156-
" - `reg`: The register to be joined. On its left, it is an array of `QGF`s of shape\n",
157-
" - ```: \n"
156+
" - `reg`: The register to be joined. On its left, it is an array of `QGF`s of shape `(qgf_poly.degree + 1,)`. On the right, it is of the type `qgf_poly`.\n"
158157
]
159158
},
160159
{

qualtran/bloqs/gf_poly_arithmetic/gf_poly_split_and_join.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class GFPolySplit(_BookkeepingBloq):
5353
galois field GF($p^m$). Given an input quantum register representing a degree $n$ polynomial
5454
$f(x)$, this bloq splits it into $n + 1$ registers of type $QGF(p, m)$.
5555
56-
Give a polynomial
56+
Given a polynomial
5757
$$
5858
f(x) = \sum_{i = 0}^{n} a_{i} x^{i} \\ \forall a_{i} \in GF(p^m)
5959
$$
@@ -66,7 +66,7 @@ class GFPolySplit(_BookkeepingBloq):
6666
See `GFPolyJoin` for the inverse operation.
6767
6868
Args:
69-
qgf_poly: An instance of `QGFPoly` type that represents a degree $n$ polynomial defined
69+
dtype: An instance of `QGFPoly` type that represents a degree $n$ polynomial defined
7070
over a galois field GF($p^m$).
7171
7272
Registers:
@@ -158,12 +158,12 @@ class GFPolyJoin(_BookkeepingBloq):
158158
representing coefficients of a degree $n$ polynomial $f(x)$, this bloq joins it into
159159
a register of type `QGFPoly`.
160160
161-
Give a polynomial
161+
Given a polynomial
162162
$$
163163
f(x) = \sum_{i = 0}^{n} a_{i} x^{i} \\ \forall a_{i} \in GF(p^m)
164164
$$
165165
166-
the bloq joins register representing coefficients of the polynomial in big-endian representation
166+
the bloq joins registers representing coefficients of the polynomial in big-endian representation
167167
such that
168168
$$
169169
\ket{a_{n}}\ket{a_{n - 1}} \cdots \ket{a_0} \xrightarrow{\text{join}} \ket{f(x)}
@@ -172,12 +172,12 @@ class GFPolyJoin(_BookkeepingBloq):
172172
See `GFPolySplit` for the inverse operation.
173173
174174
Args:
175-
qgf_poly: An instance of `QGFPoly` type that represents a degree $n$ polynomial defined
175+
dtype: An instance of `QGFPoly` type that represents a degree $n$ polynomial defined
176176
over a galois field GF($p^m$).
177177
178178
Registers:
179179
reg: The register to be joined. On its left, it is an array of `QGF`s of shape
180-
`(qgf_poly.degree + 1,)`. On the right, it is of the type `qgf_poly`.
180+
`(qgf_poly.degree + 1,)`. On the right, it is of the type `qgf_poly`.
181181
182182
"""
183183

0 commit comments

Comments
 (0)