Contributing¶
Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given.
Code of Conduct¶
Everyone interacting in the krotov
project’s code base,
issue tracker, and any communication channels is expected to follow the
PyPA Code of Conduct.
Report Bugs¶
Report bugs at https://github.com/qucontrol/krotov/issues.
If you are reporting a bug, please include:
Your operating system name and version.
Any details about your local setup that might be helpful in troubleshooting.
Detailed steps to reproduce the bug.
Submit Feedback¶
The best way to send feedback is to file an issue at https://github.com/qucontrol/krotov/issues.
If you are proposing a feature:
Explain in detail how it would work.
Keep the scope as narrow as possible, to make it easier to implement.
Remember that this is a volunteer-driven project, and that contributions are welcome :)
Pull Request Guidelines¶
Before you submit a pull request, check that it meets these guidelines:
The pull request should include tests.
If the pull request adds functionality, the docs should be updated. Put your new functionality into a function with a docstring, and add the feature to the list in
docs/04_features.rst
and/orHISTORY.rst
.Check https://github.com/qucontrol/krotov/actions and make sure that the tests pass for all supported Python versions.
Get Started!¶
Ready to contribute? Follow Aaron Meurer’s Git Workflow Notes (with qucontrol/krotov
instead of sympy/sympy
)
In short, if you are not a member of the qucontrol organization,
Clone the repository from
git@github.com:qucontrol/krotov.git
Fork the repo on GitHub to your personal account.
Add your fork as a remote.
Pull in the latest changes from the master branch.
Create a topic branch.
Make your changes and commit them (testing locally).
Push changes to the topic branch on your remote.
Make a pull request against the base master branch through the Github website of your fork.
The project uses hatch for automated testing accross multiple versions of Python and for various development tasks such as linting and generating the documentation. See Development Prerequisites for details.
There is also a Makefile
that wraps around hatch
, for
convenience on Unix-based systems. In your checked-out clone, run
make help
to see the available make targets. If you cannot use make
, the Makefile
still provides a convenient reference for hatch
commands that are useful
for development.
If you are a member of the qucontrol organization, there is no need to fork
krotov
- you can directly pull and push to git@github.com:qucontrol/krotov.git
.
Development Prerequisites¶
Contributing to the package’s developments requires that you have Python 3.8 and hatch installed (tested with Hatch 1.11). It is strongly recommended that you also have installations of all other supported Python versions. The recommended way to install multiple versions of Python at the same time is through pyenv (or pyenv-win on Windows).
Branching Model¶
For developers with direct access to the repository,
krotov
uses a simple branching model where all
developments happens directly on the master
branch. Releases are tags on
master
. All commits on master
should pass all tests and be
well-documented. This is so that git bisect
can be effective. For any
non-trivial issue, it is recommended to create a topic branch, instead of
working on master
. There are no restrictions on commits on topic branches,
they do not need to contain complete documentation, pass any tests, or even be
able to run.
To create a topic-branch named issue1
:
git branch issue1
git checkout issue1
You can then make commits, and push them to Github to trigger Continuous Integration testing:
git push -u origin issue1
Commit early and often! At the same time, try to keep your topic branch as clean and organized as possible.
Avoid having a series of meaningless granular commits like “start bugfix”, “continue development”, “add more work on bugfix”, “fix typos”, and so forth. Instead, use
git commit --amend
to add to your previous commit. This is the ideal way to “commit early and often”. You do not have to wait until a commit is “perfect”; it is a good idea to make hourly/daily “snapshots” of work in progress. Amending a commit also allows you to change the commit message of your last commit.You can combine multiple existing commits by “squashing” them. For example, use
git rebase -i HEAD~4
to combined the previous four commits into one. See the “Rewriting History” section of Pro Git book for details (if you feel this is too far outside of your git comfort zone, just skip it).If you work on a topic branch for a long time, and there is significant work on
master
in the meantime, periodically rebase your topic branch on the current master (git rebase master
). Avoid mergingmaster
into your topic branch. See Merging vs. Rebasing.
If you have already pushed your topic branch to the remote origin, you can
force-push the issue branch (git push --force
). If you are collaborating
with others on the branch, coordinate with them before force pushing. A
force-push rewrites history. You must never rewrite history on the master
branch (nor will you be able to, as the master
branch is “protected” and
can only be force-pushed to in coordination with the project maintainer). If
something goes wrong with any advanced “history rewriting”, there is always
“git reflog” as a safety net – you will never lose work that was committed
before.
When you are done with a topic branch (the issue has been fixed), finish up by
creating a pull-request for merging the branch into master
(follow the
propmts on the Github website).
Summarize the changes of the branch relative to master
in the pull request.
Commit Message Guidelines¶
Write commit messages according to this template:
Short (50 chars or less) summary ("subject line")
More detailed explanatory text. Wrap it to 72 characters. The blank
line separating the summary from the body is critical (unless you omit
the body entirely).
Write your subject line in the imperative: "Fix bug" and not "Fixed
bug" or "Fixes bug." This convention matches up with commit messages
generated by commands like git merge and git revert. A properly formed
git commit subject line should always be able to complete the sentence
"If applied, this commit will <your subject line here>".
Further paragraphs come after blank lines.
- Bullet points are okay, too.
- Typically a hyphen or asterisk is used for the bullet, followed by a
single space. Use a hanging indent.
You should reference any issue that is being addressed in the commit, as
e.g. "#1" for issue #1. If the commit closes an issue, state this on the
last line of the message (see below). This will automatically close the
issue on Github as soon as the commit is pushed there.
Closes #1
See Closing issues using keywords for details on references to issues that Github will understand.
Testing¶
The Krotov package includes a full test-suite using pytest. We strive for a test coverage above 90%.
From a checkout of the krotov
repository, you can use
make test
to run the entire test suite, or
hatch run test
if make
is not available.
The tests are organized in the tests
subfolder. It includes python scripts
whose name start with test_
, which contain functions whose names also start
with test_
. Any such functions in any such files are picked up by pytest
for testing. In addition, doctests from any docstring or any documentation
file (*.rst
) are picked up (by the pytest doctest plugin). Lastly, all
example notebooks are validated as a test, through
the nbval plugin.
Code Style¶
All code must be compatible with PEP 8. The line length limit is 79 characters, although exceptions are permissible if this improves readability significantly.
Beyond PEP 8, this project adopts the Black code style, with
--skip-string-normalization --line-length 79
. You can
run make black-check
or hatch run lint:black-check
to check adherence to the
code style, and make black
or hatch run lint:black
to apply it.
Imports within python modules must be sorted according to the isort
configuration in pyproject.toml
. The command make isort-check
or
hatch run lint:isort-check
checks whether all imports are sorted correctly,
and make isort
or hatch run lint:isort
modifies all Python modules
in-place with the proper sorting.
The code style is enforced as part of the test suite, as well as through git pre-commit hooks that prevent committing code not does not meet the requirements. These hooks are managed through the pre-commit framework.
Warning
After cloning the krotov
repository, you should run
make init
, or pre-commit install
from within the project root folder.
You may use make flake8-check
or hatch run lint:flake8
and make
pylint-check
or hatch run lint:pylint
for additional checks on the code with
flake8 and pylint, but there is no strict requirement for a perfect score
with either one of these linters. They only serve as a guideline for code that
might be improved.
Write Documentation¶
The krotov
package could always use more documentation, whether
as part of the official docs, in docstrings, or even on the web in blog posts,
articles, and such.
The package documentation is generated with Sphinx, the
documentation (and docstrings) are formatted using the
Restructured Text markup language (file extension rst
).
See also the Matplotlib Sphinx cheat sheet for some helpful tips.
Each function or class must have a docstring; this docstring must
be written in the “Google Style” format (as implemented by
Sphinx’ napoleon extension). Docstrings and any other part of the
documentation can include mathematical formulas in LaTeX syntax
(using mathjax). In addition to Sphinx’ normal syntax for inline math
(:math:`x`
), you may also use easier-to-read dollar signs ($x$
).
The Krotov package defines some custom tex macros for quantum mechanics, which
you are strongly encouraged to use. These include:
\bra
, e.g.$\bra{\Psi}$
for \(\bra{\Psi}\) (or\\Bra{}
for auto-resizing). Do not use\langle
/\rangle
/\vert
manually!\ket
, e.g.$\ket{\Psi}$
for \(\ket{\Psi}\) (or\Ket{}
for auto-resizing).\Braket
, e.g.$\Braket{\Phi}{\Psi}$
for \(\Braket{\Phi}{\Psi}\).\Op
for quantum operators, e.g.$\Op{H}$
for \(\Op{H}\).\Abs
for absolute values, e.g.$\Abs{x}$
for \(\Abs{x}\).\AbsSq
for the absolute-square, e.g.$\AbsSq{\Braket{\Phi}{\Psi}}$
for \(\AbsSq{\Braket{\Phi}{\Psi}}\).\avg
for the expectation values, e.g.$\avg{\Op{H}}$
for \(\avg{\Op{H}}\) (or\Avg{}
for auto-resizing).\Norm
for the norm, e.g.$\Norm{\ket{\Psi}}$
for \(\Norm{\ket{\Psi}}\).\identity
for the identity operator, \(\identity\).\Liouville
for the Liouvillian symbol, \(\Liouville\).\DynMap
for the symbolic dynamical map, \(\DynMap\).\dd
for the differential, e.g.$\int f(x) \dd x$
for \(\int f(x) \dd x\).Function names / mathematical operators
\tr
,\diag
,\abs
,\pop
.Text labels
\aux
,\opt
,\tgt
,\init
,\lab
,\rwa
.
Also see Math in Example Notebooks.
You may use the BibTeX plugin for citations.
At any point, from a checkout of the krotov
repository, you may run
make docs
or
hatch run docs:build
to generate the documentation locally.
Deploy the documentation¶
The documentation is automatically deployed to
https://qucontrol.github.io/krotov/ (the gh-pages associated with the
krotov
package’s Github repository) every time commits are pushed to
Github. This is done via
Github Actions
as configured in the
workflow file
at .github/workflows/docs.yml
.
The documentation for all versions of krotov
is visible on the
gh-pages
git branch. Any changes that are committed and pushed from this
branch will be deployed to the online documentation. Do not routinely perform
manual edits on the gh-pages
branch! Let Github Actions do its job of
automatically deploying documentation instead.
Contribute Examples¶
Examples should be contributed in the form of Jupyter notebooks.
Example notebooks are automatically rendered as part of the documentation (Examples), and they are also verified by the automated tests. For this to work properly, the following steps must be taken:
Put all imports near the top of the notebook, with
# NBVAL_IGNORE_OUTPUT
as the first line. Use the watermark package to print out the versions of imported packages. For example:# NBVAL_IGNORE_OUTPUT %load_ext watermark import qutip import numpy as np import scipy import matplotlib import matplotlib.pylab as plt %watermark -v --iversions
Put the notebook in the folder
docs/notebooks/
.Before committing, re-evaluate all example notebooks in a well-defined virtual environment by running
make notebooks
Check that the examples can be verified across different Python version by running
make test
You may also verify that the example is properly integrated in the documentation by running
make docs
Math in Example Notebooks¶
You may use the same tex macros described in the Write Documentation section. However, for the macros to work when viewing the notebook by itself, they must be redefined locally. To this end, add a markdown cell underneath the top cell that contains the imported packages (see above). The cell must contain the following:
$\newcommand{tr}[0]{\operatorname{tr}}
\newcommand{diag}[0]{\operatorname{diag}}
\newcommand{abs}[0]{\operatorname{abs}}
\newcommand{pop}[0]{\operatorname{pop}}
\newcommand{aux}[0]{\text{aux}}
\newcommand{opt}[0]{\text{opt}}
\newcommand{tgt}[0]{\text{tgt}}
\newcommand{init}[0]{\text{init}}
\newcommand{lab}[0]{\text{lab}}
\newcommand{rwa}[0]{\text{rwa}}
\newcommand{bra}[1]{\langle#1\vert}
\newcommand{ket}[1]{\vert#1\rangle}
\newcommand{Bra}[1]{\left\langle#1\right\vert}
\newcommand{Ket}[1]{\left\vert#1\right\rangle}
\newcommand{Braket}[2]{\left\langle #1\vphantom{#2} \mid #2\vphantom{#1}\right\rangle}
\newcommand{op}[1]{\hat{#1}}
\newcommand{Op}[1]{\hat{#1}}
\newcommand{dd}[0]{\,\text{d}}
\newcommand{Liouville}[0]{\mathcal{L}}
\newcommand{DynMap}[0]{\mathcal{E}}
\newcommand{identity}[0]{\mathbf{1}}
\newcommand{Norm}[1]{\lVert#1\rVert}
\newcommand{Abs}[1]{\left\vert#1\right\vert}
\newcommand{avg}[1]{\langle#1\rangle}
\newcommand{Avg}[1]{\left\langle#1\right\rangle}
\newcommand{AbsSq}[1]{\left\vert#1\right\vert^2}
\newcommand{Re}[0]{\operatorname{Re}}
\newcommand{Im}[0]{\operatorname{Im}}$
Upon executing the cell the definitions will be hidden, but the defined macros will be available in any cell in the rest of the notebook.
Versioning¶
Releases should follow Semantic Versioning, and version numbers published to PyPI must be compatible with PEP 440.
In short, versions number follow the pattern major.minor.patch, e.g.
0.1.0
for the first release, and 1.0.0
for the first stable release.
If necessary, pre-release versions might be published as e.g:
1.0.0-dev1 # developer's preview 1 for release 1.0.0
1.0.0-rc1 # release candidate 1 for 1.0.0
Errors in the release metadata or documentation only may be fixed in a post-release, e.g.:
1.0.0.post1 # first post-release after 1.0.0
Post-releases should be used sparingly, but they are acceptable even though they are not supported by the Semantic Versioning specification.
The current version is available through the __version__
attribute of the
krotov
package:
>>> import krotov
>>> krotov.__version__
Between releases, __version__
on the master branch should either be the
version number of the last release, with “+dev” appended (as a
“local version identifier”), or the version number of the next planned
release, with “-dev” appended (“pre-release identifier” with extra dash).
The version string “1.0.0-dev1+dev” is a valid value after the “1.0.0-dev1”
pre-release. The “+dev” suffix must never be included in a release to PyPI.
Note that twine applies normalization to the above recommended forms to make them strictly compatible with PEP 440, before uploading to PyPI. Users installing the package through pip may use the original version specification as well as the normalized one (or any other variation that normalizes to the same result).
Making a Release¶
Relesases can only be made by administrators of the Krotov Github repo who are also listed as Maintainers on https://pypi.org/project/krotov/.
They must have GPG set up to allow for signed commits, and be able to locally
produce documentation artifacts (make docs-artifacts
).
A release is made by running
make release
which executes scripts/release.py
. Follow all the prompts.
Releases must be tagged in git, using the version string prefixed by “v”,
e.g. v1.0.0-dev1
and v1.0.0
. As prompted for by the release script,
after pushing the tag, an official Github-release must be created manually at
https://github.com/qucontrol/krotov/releases, with the proper release notes and
the documentation artifacts as binary attachments.
Developers’ How-Tos¶
The following assumes your current working directory is a checkout of
krotov
, and that have hatch
installed on your system.
How to run a jupyter notebook server for working on the example notebooks¶
A notebook server that is isolated to the proper testing environment can be started via the Makefile:
make jupyter-notebook
This is equivalent to:
hatch run -- jupyter lab
You may run this with your own options, if you prefer. The
--config=/dev/null
guarantees that the notebook server is completely
isolated. Otherwise, configuration files from your home directly (see
Jupyter’s Common Configuration system) may influence the server. Of
course, if you know what you’re doing, you may want this.
If you prefer, you may also use the newer jupyterlab:
make jupyter-lab
How to convert an example notebook to a script for easier debugging¶
Interactive debugging in notebooks is difficult. It becomes much easier if you convert the notebook to a script first. To convert a notebook to an (I)Python script and run it with automatic debugging, execute e.g.:
hatch run -- jupyter nbconvert --to=python --stdout docs/notebooks/01_example_transmon_xgate.ipynb > debug.py
hatch run -- ipython --pdb debug.py
You can then also set a manual breakpoint by inserting the following line anywhere in the code:
from IPython.terminal.debugger import set_trace; set_trace() # DEBUG
How to make git diff
work for notebooks¶
Install nbdime and run nbdime config-git --enable --global
to enable the git integration.
How to commit failing tests or example notebooks¶
The test-suite on the master
branch should always pass without error. If you
would like to commit any example notebooks or tests that currently fail, as a
form of test-driven development, you have two options:
Push onto a topic branch (which are allowed to have failing tests), see the Branching Model. The failing tests can then be fixed by adding commits to the same branch.
Mark the test as failing. For normal tests, add a decorator:
@pytest.mark.xfail
See the pytest documentation on skip and xfail for details.
For notebooks, the equivalent to the decorator is to add a comment to the first line of the failing cell, either:
# NBVAL_RAISES_EXCEPTION
(preferably), or:
# NBVAL_SKIP
(this may affect subsequent cells, as the marked cell is not executed at all). See the documentation of the nbval pluging on skipping and exceptions for details.
How to run a subset of tests¶
To run e.g. only the tests defined in tests/test_krotov.py
, use any of the following:
make test TESTS=tests/test_krotov.py
hatch run test -- tests/test_krotov.py
hatch -e py38 run test -- test/test_krotov.py
See the pytest test selection docs for details. The -e py38
selects the
Python version to run the tests under. You may replace this with an equivalent
name for any supported Python versions.
How to run only as single test¶
Decorate the test with e.g. @pytest.mark.xxx
, and then run, e.g:
hatch run -- pytest -m xxx tests/
See the pytest documentation on markers for details.
How to go into an interactive debugger¶
Optionally, install the pdbpp package into the testing environment, for a better experience:
hatch run -- pip install pdbpp
Then:
before the line where you went to enter the debugger, insert a line:
from IPython.terminal.debugger import set_trace; set_trace() # DEBUG
Run
pytest
with the option-s
, e.g.:hatch run -- pytest -m xxx -s tests/
You may also see the pytest documentation on automatic debugging.
How to see the debug logger output in the example notebooks¶
The optimize_pulses()
routine generates some logger messages for
debugging purposes. To see these messages, set the level of “krotov” logger to
INFO or DEBUG:
import logging
logger = logging.getLogger('krotov')
logger.setLevel(logging.DEBUG)
You can also configure the logger with custom formatters, e.g. to show the messages with time stamps:
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
formatter = logging.Formatter("%(asctime)s:%(message)s")
ch.setFormatter(formatter)
logger.addHandler(ch)
logging.getLogger().handlers = [] # disable root handlers
See the Configure Logging section of the Python documentation for more details.
How to use quantum mechanical tex macros¶
For docstrings or *.rst
files, see Write Documentation. For notebooks, see Math in Example Notebooks.