Documenting Bloqs#
Documentation is a crucial part of authoring a bloq. While the bloq methods can encode behavior and composition of a bloq, we still need to use words and latex to provide context. The source of truth for a bloq’s documentation is its class docstring.
Docstring basics#
Start with a one-line summary of the bloq, then an empty line followed by additional paragraphs of information per Python convention.
Use markdown syntax in the body of your docstring.
You can use latex by enclosing the expression in
$
for inline math or$$
for equations.Latex commands that start with a backslash
\
may conflict with Python escape characters. You may need to use a Python “raw string” for your docstring by starting it withr"""
.The docstring should be anchored to the class itself rather than any methods. Specifically: document any constructor arguments (class attributes) in the class docstring rather than adding a docstring to
__init__
. Describe the decomposition in the class docstring rather than thebuild_composite_bloq
method. This is so we can nicely render one cohesive document per bloq.We use Google-style docstring conventions for sections within the docstring, read on.
Docstring sections#
A docstring “section” has a heading followed by the body of the section. There are some notable sections:
Args#
The “Args” section is where you should document a bloq’s init-args; which ideally should be class attributes as well. Each entry should be the name of the variable, colon, a description.
@attrs.frozen
class PrepareUniformSuperposition(Bloq):
r"""Prepares a uniform superposition over first $n$ basis states using $O(log(n))$ T-gates.
Args:
n: The gate prepares a uniform superposition over first $n$ basis states.
cvs: Control values for each control qubit. If specified, a controlled version
of the gate is constructed.
"""
Registers#
We provide custom support for a “Registers” section where you should document a bloq’s registers similar to the (classical) args.
class And(Bloq):
"""A two-bit 'and' operation.
Registers:
ctrl: A two-bit control register.
target [right]: The output bit.
"""
References#
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.
class QROM(Bloq):
"""Bloq to load data[l] in the target register when the selection stores an index l.
References:
[Encoding Electronic Spectra in Quantum Circuits with Linear T Complexity](https://arxiv.org/abs/1805.03662).
Babbush et al. (2018). Figure 1.
[Compilation of Fault-Tolerant Quantum Heuristics for Combinatorial Optimization](https://arxiv.org/abs/2007.07391).
Babbush et al. (2020). Figure 3.
"""
Demo bloqs with BloqExample
#
A given bloq class has attributes that can be specified at runtime. For automatic testing and documentation, we need example instantiations of each bloq class and example code for building the instantiations. These instantiations and metadata are stored in qualtran.BloqExample
objects.
To provide an example of your bloq, you’ll likely want to use the @bloq_example
decorator. You can provide multiple examples for a given bloq class.
@bloq_example
def _modexp_small() -> ModExp:
modexp_small = ModExp(base=3, mod=15, exp_bitsize=3, x_bitsize=2048)
return modexp_small
@bloq_example
def _modexp() -> ModExp:
modexp = ModExp.make_for_shor(big_n=15 * 17, g=9)
return modexp
@bloq_example
def _modexp_symb() -> ModExp:
g, N, n_e, n_x = sympy.symbols('g N n_e, n_x')
modexp_symb = ModExp(base=g, mod=N, exp_bitsize=n_e, x_bitsize=n_x)
return modexp_symb
Important caveats#
The
@bloq_example
decorator tries to reduce the boilerplate for building a fullBloqExample
, but it needs your cooperation to do so.The function name is significant. It should be the name of the variable you assign the bloq instance to preceded by an underscore.
The return type annotation is required.
You must assign the bloq instance to a variable (before returning it).
By convention, the code should be located in the module defining the bloq class.
All this rigamarole is to support a feature where we will render the actual code to construct the instance into a jupyter notebook rather than just importing an existing instance from the library.
Automatically generate Jupyter notebooks for bloqs#
Armed with a good docstring and one-or-more bloq examples, we provide tooling for automatically generating Jupyter notebook cells to document and demo a bloq. First, you need to group the examples with the bloq class and any additional configuration objects. See the docstring for qualtran.BloqDocSpec
for more information.
_MODEXP_DOC = BloqDocSpec(
bloq_cls=ModExp,
examples=[_modexp_symb, _modexp_small, _modexp],
)
This code should be located in the module defining the bloq class.
Each bloq class has one BloqDocSpec
which may have multiple bloq examples. Each jupyter notebook roughly corresponds to one module or package and can document multiple bloq classes in it, so the final step is to plumb through the BloqDocSpec
into a NotebookSpec
. In dev_tools/qualtran_dev_tools/notebook_specs.py
, you can add a new NotebookSpecV2
to the big list or add your BloqDocSpec
to an existing one. If you execute this script, it will generate a new notebook or new cells in an existing notebook with documentation for your bloq. You may need to manually git add
the new notebook.
NotebookSpecV2(
title='Modular Exponentiation',
module=qualtran.bloqs.factoring.mod_exp,
bloq_specs=[qualtran.bloqs.factoring.mod_exp._MODEXP_DOC],
),