Contributing to quchip

Contributions from scientists and developers are welcome. Bug reports, physics discrepancies, documentation corrections, and code changes are all useful. Open a GitHub Issue for a concrete problem or model request. Use GitHub Discussions for open-ended questions.

Development setup

quchip requires Python 3.11 or newer.

git clone https://github.com/quchip/quchip.git
cd quchip
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e '.[dev,test,dynamiqs]'

QuTiP is the default backend. The dynamiqs extra enables JAX-native solver, gradient, and batching tests.

Tests

Run the complete test suite with:

python -m pytest

The suite is also divided into lanes:

python -m pytest -m core
python -m pytest -m physics_sentinel
python -m pytest -m extended
  • core covers analytical behavior and public API contracts.

  • physics_sentinel checks simulation-backed physics invariants.

  • extended contains slower and long-tail coverage.

Tests that require dynamiqs use the optional_backend marker and call pytest.importorskip("dynamiqs"), so they skip cleanly when dynamiqs is unavailable.

Test public behavior and physical invariants, not implementation details. A behavior-preserving refactor should not require mechanical test edits; see Change-Detector Tests Considered Harmful.

Before opening a pull request, run:

python -m pytest
ruff check .
python -m mypy quchip tests/typing/external_declarative_models.py

Ruff uses a 120-character line limit. Public API docstrings use NumPy-style sections and imperative summaries ending with periods. Every test has a one-line docstring stating the invariant under test.

Examples and notebooks

Examples are executable physics guides. Keep each one focused on a single question and use the real device, coupling, control, and solver APIs. Do not replace the declared model with raw QuTiP operators or a hand-written effective Hamiltonian.

  • Keep the flow linear and use object references for devices, couplings, and drives.

  • Separate quchip setup and simulation from supporting calculations, result extraction, and custom plotting.

  • Avoid one-use aliases and helper layers that hide the main quchip calls.

  • Use chip.state() for dressed-state preparation. Use chip.bare_state() only when the question requires a bare product state, and explain why.

  • Let analysis methods such as chip.freq() dress automatically.

  • Use sequence.simulate_batch() for simulation sweeps; do not write one solve per parameter value.

  • Put explanation in Markdown. Reserve stdout for compact RESULT name=value receipts.

  • Keep figures limited to what the example needs, with labeled axes and explicit reductions or traced-out subsystems.

Readable Jupytext Markdown is the canonical source. Commit its executed .ipynb partner with the python3 kernel, identical code cells, and inspected outputs. Use standard fenced Python cells rather than percent-format scripts.

Notebook outputs remain in the executed .ipynb; save only figures selected for the website under docs/images/ and include the canonical Markdown from its page under docs/guides/.

From the repository root, replace <name> with the example stem and run:

jupytext --sync examples/<name>.md
jupyter nbconvert --to notebook --execute --inplace \
  --ExecutePreprocessor.timeout=300 \
  --ExecutePreprocessor.record_timing=False \
  examples/<name>.ipynb
jupytext --diff --diff-format md examples/<name>.ipynb examples/<name>.md
jupytext --to md --test-strict examples/<name>.ipynb

The diff must be empty and the strict round trip must pass. Inspect the executed notebook before committing: setup cells stay silent, stdout contains only intended receipts, and every intended figure appears once. Run the focused example test and the project checks above.

Physics conventions

Read PHYSICS.md before changing Hamiltonians, frames, approximations, observables, dissipation, or solver assembly.

  • Use ordinary GHz for frequencies and energies, ns for time, and mK for temperature.

  • Keep traced paths compatible with JAX. Do not call float(), int(), or bool() on traced values or branch on them with Python. Use jax.numpy and JAX control flow.

  • State approximations, omitted terms, and validity regimes through the model’s declared approximation and physics_notes().

  • Use derived tolerances based on an analytic limit, truncation error, solver convergence, an independent calculation, or a cited reference. State the basis next to the assertion. Never tune a tolerance only to make a test pass.

Pull requests

Keep pull requests small and focused. Explain the physical or user-visible change, include tests for changed behavior, and update the relevant documentation.

Open an issue before starting a large change or adding a device, coupling, drive, envelope, or noise model. Include the model Hamiltonian, assumptions, intended use, and a reference when available so the scope can be agreed before implementation.

Policies

  • There is no CLA or DCO. By opening a pull request you agree that your contribution is provided under the project’s license.

  • If AI assistance was used, disclose it in the pull request description. The contributor remains accountable for every claim and change.

  • Be respectful and constructive in issues, discussions, and reviews. See CODE_OF_CONDUCT.md.