Skip to content

Commit 7b131d2

Browse files
author
Corey Ostrove
committed
Merge branch 'develop' into lindbladblock-simplification
2 parents 64327cd + 5864f3e commit 7b131d2

309 files changed

Lines changed: 6445 additions & 2782 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.

.github/workflows/autodeploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ jobs:
3030
- name: Build wheels
3131
uses: pypa/cibuildwheel@v3.0.0
3232
env:
33-
CIBW_BUILD: cp39-* cp310-* cp311-* cp312-*
33+
CIBW_BUILD: cp310-* cp311-* cp312-* cp313-*
3434
CIBW_BUILD_VERBOSITY: 1
3535
CIBW_BEFORE_ALL_LINUX: ./.github/ci-scripts/before_install.sh
3636
CIBW_BEFORE_ALL_MACOS: ./.github/ci-scripts/before_install_macos.sh

.github/workflows/beta-master.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,21 @@ jobs:
1414
fail-fast: false
1515
matrix:
1616
os: [macos-latest, ubuntu-latest, windows-latest]
17-
python-version: [3.9, '3.10', '3.11', '3.12']
17+
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
1818
use-cython: ['true', 'false']
1919
uses: ./.github/workflows/reuseable-main.yml
2020
name: Run pyGSTi tests
2121
with:
2222
os: ${{ matrix.os }}
2323
python-version: ${{ matrix.python-version }}
24+
run-coverage: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == '3.13' && matrix.use-cython == 'true' }} # cover exactly one job
2425
use-cython: ${{ matrix.use-cython }}
2526
run-unit-tests: 'true'
2627
run-integration-tests: 'true'
2728
run-extra-tests: 'true'
28-
run-notebook-tests: 'false' # TODO: Turn off failing notebook tests for runner failures starting end of May 2024
29+
# Run the docs notebook regression on exactly one job (Linux + Python 3.13 +
30+
# Cython). The suite is slow, so we don't fan it out across the full OS/Python matrix.
31+
run-notebook-tests: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == '3.13' && matrix.use-cython == 'true' }}
2932

3033

3134

.github/workflows/bugfix.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@ jobs:
1515
fail-fast: false
1616
matrix:
1717
os: [ubuntu-latest, windows-latest] # No Mac
18-
python-version: [3.9, '3.10', '3.11', '3.12']
18+
python-version: ['3.10', '3.11', '3.12', '3.13']
1919
use-cython: ['true', 'false']
2020
uses: ./.github/workflows/reuseable-main.yml
2121
name: Run pyGSTi tests
2222
with:
2323
os: ${{ matrix.os }}
2424
python-version: ${{ matrix.python-version }}
25+
run-coverage: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == '3.13' && matrix.use-cython == 'true' }} # cover exactly one job
2526
use-cython: 'true' # Cython only
2627
run-unit-tests: 'true'
2728
run-integration-tests: 'true'

.github/workflows/develop.yml

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,22 @@ jobs:
1515
fail-fast: false
1616
matrix:
1717
os: [ubuntu-latest, windows-latest] # No Mac
18-
python-version: [3.9, '3.10', '3.11', '3.12']
18+
python-version: ['3.10', '3.11', '3.12', '3.13']
1919
use-cython: ['true', 'false']
2020
uses: ./.github/workflows/reuseable-main.yml
2121
name: Run pyGSTi tests
2222
with:
2323
os: ${{ matrix.os }}
2424
python-version: ${{ matrix.python-version }}
25+
run-coverage: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == '3.13' && matrix.use-cython == 'true' }} # cover exactly one job
2526
use-cython: 'true' # Cython only
2627
run-unit-tests: 'true'
2728
run-integration-tests: 'true'
2829
run-extra-tests: 'false' # No integration tests
29-
run-notebook-tests: 'false' # No notebook tests
30+
# Run the docs notebook regression on exactly one job (Linux + newest Python +
31+
# Cython), the same single-job pattern used for run-coverage above. The suite is
32+
# slow, so we don't fan it out across the whole matrix.
33+
run-notebook-tests: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == '3.13' && matrix.use-cython == 'true' }}
3034

3135
push: # Push to stable "beta" branch on successful build
3236
runs-on: ubuntu-latest
@@ -47,6 +51,52 @@ jobs:
4751
git checkout beta
4852
git merge --ff-only ${GITHUB_SHA} && git push origin beta
4953
54+
docs-notebooks: # Rebuild the ReadTheDocs "develop-with-notebooks" branch
55+
# ReadTheDocs builds the hosted docs from `develop-with-notebooks`, NOT from
56+
# `develop`. That branch is `develop` plus jupytext-generated *.ipynb
57+
# companions committed next to each notebook *.md, so the Colab/Binder launch
58+
# buttons (which fetch from GitHub) resolve to real notebooks. We regenerate
59+
# the whole branch as a single atomic, force-pushed commit so RTD never sees
60+
# a half-built state.
61+
runs-on: ubuntu-latest
62+
63+
# Only after tests pass on a push to develop.
64+
needs: build
65+
if: github.ref == 'refs/heads/develop' && github.event_name == 'push'
66+
67+
steps:
68+
- uses: actions/checkout@v4
69+
with:
70+
fetch-depth: 0
71+
token: ${{ secrets.PYGSTI_TOKEN }}
72+
- uses: actions/setup-python@v5
73+
with:
74+
python-version: '3.11'
75+
- name: Install jupytext
76+
run: pip install 'jupytext>=1.16'
77+
- name: Build develop-with-notebooks branch
78+
run: |
79+
git config --global user.name 'PyGSTi'
80+
git config --global user.email 'pygsti@noreply.github.com'
81+
# Start the branch from the exact develop commit that just passed tests.
82+
git checkout -B develop-with-notebooks ${GITHUB_SHA}
83+
# Generate an .ipynb next to each notebook .md. --sync honors
84+
# docs/jupytext.toml (formats = "ipynb,md:myst") and skips plain-markdown
85+
# pages that lack a jupytext header (index/landing pages).
86+
shopt -s globstar
87+
jupytext --sync docs/markdown/**/*.md
88+
# Point the hosted build's launch links at this branch (the
89+
# edit_button_branch extension keeps Show source / Suggest edit on develop).
90+
# Only the repository.branch key (2-space indent) is rewritten; the
91+
# pygsti_edit_branch value stays on develop. The trailing comment is kept.
92+
sed -i -E 's|^( branch: )develop\b(.*)$|\1develop-with-notebooks\2|' docs/_config.yml
93+
grep -q '^ branch: develop-with-notebooks' docs/_config.yml # fail loudly if the rewrite missed
94+
# Force-add the generated notebooks (gitignored on develop) + config change.
95+
git add -f docs/markdown
96+
git add docs/_config.yml
97+
git commit -m "Auto-build notebooks for ReadTheDocs [skip ci]"
98+
git push --force origin develop-with-notebooks
99+
50100
51101
52102

.github/workflows/feature-branches.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,13 @@ jobs:
1919
fail-fast: false
2020
matrix:
2121
os: [ubuntu-latest, windows-latest]
22-
python-version: [3.9, '3.12'] # Only extremal Python versions
22+
python-version: ['3.10', '3.13'] # Only extremal Python versions
2323
uses: ./.github/workflows/reuseable-main.yml
2424
name: Run pyGSTi tests
2525
with:
2626
os: ${{ matrix.os }}
2727
python-version: ${{ matrix.python-version }}
28+
run-coverage: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == '3.13' }} # cover exactly one job
2829
use-cython: 'true' # Only test environment with Cython
2930
run-unit-tests: 'true'
3031
run-integration-tests: 'true'

.github/workflows/reuseable-main.yml

Lines changed: 62 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,29 @@ on:
2828
run-notebook-tests:
2929
required: true
3030
type: string
31+
# Whether THIS matrix entry measures coverage. Callers set it true for exactly
32+
# one job (ubuntu + highest Python, where coverage.py's sysmon core keeps the
33+
# tracer cheap on 3.12+). CI never uploads the report, so measuring it on every
34+
# job is pure overhead; one job preserves a human-readable number in the logs.
35+
run-coverage:
36+
required: false
37+
type: string
38+
default: 'false'
3139

3240
env:
3341
SKIP_DEAP: 1
3442
PYGSTI_NO_CUSTOMLM_SIGINT: 1
43+
# Resolve PyTorch (an explicit CI-build test dependency) from the CPU-only wheel index
44+
# instead of pip's default CUDA build. The latter drags in ~2.5 GB of nvidia-* wheels
45+
# that CI never uses. uv honors this for every resolution in the job.
46+
UV_TORCH_BACKEND: cpu
47+
# Use coverage.py's sys.monitoring core (PEP 669) for --cov runs. Python 3.12's
48+
# specializing adaptive interpreter de-optimizes the legacy C trace function,
49+
# which made coverage of call-heavy tests (e.g. the errgenproptools error-generator
50+
# composition tests) ~40x slower on 3.12 than on 3.9 -- a Python-version effect
51+
# previously misread as a numpy-2 regression. sysmon restores near-native speed;
52+
# it falls back gracefully (with a CoverageWarning) on Python < 3.12.
53+
COVERAGE_CORE: sysmon
3554

3655
jobs:
3756
build-and-test:
@@ -56,25 +75,39 @@ jobs:
5675
uses: actions/setup-python@v5
5776
with:
5877
python-version: ${{ inputs.python-version }}
59-
- name: Cache pip packages
78+
# uv gives fast, parallel installs and CPU-only torch resolution (see env above).
79+
# The astral-sh/setup-uv action is blocked by the org Actions allowlist, so we
80+
# bootstrap uv from PyPI with the stock pip (a `run:` step is not an action) and
81+
# cache its downloads with the GitHub-owned actions/cache.
82+
- name: Set up uv
83+
run: python -m pip install --upgrade pip uv
84+
- name: Cache uv downloads
6085
uses: actions/cache@v4
6186
with:
62-
path: ~/.cache/pip
63-
key: ${{ runner.os }}-pip-${{ hashFiles('setup.py') }}-${{ hashFiles('**/*requirements.txt') }}
64-
- name: Install pip packages
65-
run: |
66-
python -m pip install --upgrade pip
67-
python -m pip install wheel
68-
python -m pip install flake8
87+
# Default uv cache dir, listed for each OS; non-matching paths are skipped.
88+
path: |
89+
~/.cache/uv
90+
~/Library/Caches/uv
91+
~\AppData\Local\uv\cache
92+
# Key on the real dependency manifests (deps live in pyproject.toml).
93+
key: ${{ runner.os }}-uv-${{ hashFiles('pyproject.toml', 'setup.py') }}
94+
restore-keys: |
95+
${{ runner.os }}-uv-
96+
- name: Install build and lint tools
97+
# flake8 for the lint step; setuptools/wheel kept on hand for any dependency that
98+
# still imports pkg_resources at runtime.
99+
run: uv pip install --system setuptools wheel flake8
69100
- name: Install package (Cython)
70101
if: ${{ inputs.use-cython == 'true' }}
102+
# The PEP 660 editable install compiles the Cython extensions in-place via build
103+
# isolation, so a separate `setup.py build_ext --inplace` is redundant (verified:
104+
# the editable install alone yields importable .so for all 19 extensions).
71105
run: |
72-
python -m pip install -e .[testing]
73-
python setup.py build_ext --inplace
106+
uv pip install --system -e .[testing,pytorch]
74107
- name: Install package (No Cython, Linux only)
75108
if: ${{ inputs.use-cython != 'true' && inputs.os == 'ubuntu-latest' }}
76109
run: |
77-
PYGSTI_CYTHON_SKIP=1 python -m pip install -e .[testing_no_cython]
110+
PYGSTI_CYTHON_SKIP=1 uv pip install --system -e .[testing_no_cython,pytorch]
78111
- name: Lint with flake8 (Linux only)
79112
if: ${{ inputs.os == 'ubuntu-latest'}}
80113
run: |
@@ -84,26 +117,36 @@ jobs:
84117
flake8 . --exit-zero --statistics
85118
- name: Run unit tests
86119
if: ${{ inputs.run-unit-tests == 'true' }}
120+
# bash on every runner (Git Bash on Windows) so $COV_FLAG expands portably.
121+
shell: bash
122+
env:
123+
COV_FLAG: ${{ inputs.run-coverage == 'true' && '--cov=pygsti' || '' }}
87124
run: |
88125
python -Ic "import pygsti; print(pygsti.__version__); print(pygsti.__path__)"
89-
python -m pytest -n auto --dist loadscope --cov=pygsti --durations=25 test/unit
126+
python -m pytest -n auto --dist loadscope $COV_FLAG --durations=25 test/unit
90127
- name: Run workflow tests
91128
if: ${{ inputs.run-integration-tests == 'true' }}
129+
shell: bash
130+
env:
131+
COV_FLAG: ${{ inputs.run-coverage == 'true' && '--cov=pygsti' || '' }}
92132
run: |
93133
python -Ic "import pygsti; print(pygsti.__version__); print(pygsti.__path__)"
94-
python -m pytest -n auto --dist loadscope --cov=pygsti test/integration
134+
python -m pytest -n auto --dist loadscope $COV_FLAG test/integration
95135
- name: Run test_packages
96136
if: ${{ inputs.run-extra-tests == 'true' }}
97137
run: |
98138
python -Ic "import pygsti; print(pygsti.__version__); print(pygsti.__path__)"
99139
python -m pytest -v -n auto --dist loadscope --ignore=test/test_packages/notebooks --durations=25 test/test_packages
100140
- name: Run notebook regression
101-
if: ${{ inputs.run-notebook-tests == 'true' }}
141+
# Linux-only: this step compiles CHP with gcc and writes it to /usr/local/bin,
142+
# so it cannot run on the Windows/macOS runners even if a caller requests it.
143+
if: ${{ inputs.run-notebook-tests == 'true' && inputs.os == 'ubuntu-latest' }}
102144
run: |
103-
# Tutorials live as MyST Markdown under docs/markdown/; jupytext
104-
# materializes the paired .ipynb mirror under docs/notebooks/ for
105-
# nbval to consume.
106-
pip install -e .[docs]
145+
# Tutorials live as MyST Markdown under docs/markdown/; jupytext pairs each
146+
# .md with an .ipynb sitting NEXT TO IT in the same directory (see
147+
# docs/jupytext.toml), so the materialized notebooks land back under
148+
# docs/markdown/ for nbval to consume.
149+
uv pip install --system -e .[docs]
107150
(cd docs && jupytext --sync 'markdown/**/*.md')
108151
109152
# Download and compile CHP for the Clifford-simulation notebook;
@@ -112,4 +155,4 @@ jobs:
112155
gcc -o /usr/local/bin/chp /tmp/chp.c
113156
114157
python -Ic "import pygsti; print(pygsti.__version__); print(pygsti.__path__)"
115-
python -m pytest -n auto --nbval-lax --dist loadscope --nbval-current-env --durations=25 docs/notebooks
158+
python -m pytest -n auto --nbval-lax --dist loadscope --nbval-current-env --durations=25 docs/markdown

.gitignore

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,21 @@ test/test_packages/cmp_chk_files/Fake_Dataset_none.txt.cache
4646
# Documentation autogenerated #
4747
###############################
4848
docs/_autosummary
49+
docs/_autosummary_manual
4950
docs/_build
50-
docs/notebooks/**/*.ipynb
51-
docs/tutorial_files
52-
docs/example_files
51+
# jupytext-generated notebook companions (see docs/jupytext.toml). These are
52+
# build artifacts on develop; CI commits them on the develop-with-notebooks branch.
53+
docs/markdown/**/*.ipynb
54+
# Scratch dirs the tutorials read/write at run time (notebooks reference them as
55+
# ../../tutorial_files and ../../example_files). Track an empty .keep so the dirs
56+
# exist on a fresh checkout, but ignore everything the notebooks generate inside.
57+
docs/tutorial_files/*
58+
!docs/tutorial_files/.keep
59+
docs/example_files/*
60+
!docs/example_files/.keep
61+
# Offline JS/CSS asset bundle that pyGSTi report writing drops next to the running
62+
# notebook (CWD) when generating reports; a build artifact of running the tutorials.
63+
docs/markdown/**/offline/
5364

5465
# Compiled source #
5566
###################
@@ -89,6 +100,12 @@ parsetab_string.py
89100
*.tar
90101
*.zip
91102

103+
# Uncompressed differential circuit corpus (test/performance/circuit_corpus.py).
104+
# A full corpus is ~120 MB; only the compressed baseline is tracked (see the
105+
# negation in test/performance/.gitignore). Guarded here too in case it is
106+
# generated from the repo root, not just under test/performance/.
107+
*.jsonl
108+
92109
# Logs and databases #
93110
######################
94111
*.log
@@ -110,3 +127,7 @@ HighImpactPaper1/texFiles/.DS_Store
110127
local
111128
pygsti/_version.py
112129
1_1.profile/0.prof
130+
131+
# committed golden fixtures for the Circuit characterization tests
132+
!test/unit/circuits/golden/*.pkl
133+
!test/unit/circuits/golden/*.pkl.gz

.readthedocs.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ build:
1010
commands:
1111
# rtd-requirements.txt installs `.[docs]` which pulls in pyGSTi itself
1212
# plus jupyter-book, jupytext, matplotlib, numpy.
13-
- pip install -r rtd-requirements.txt
13+
# PYGSTI_SKIP_CYTHON: the API docs only need pyGSTi importable; the
14+
# pure-Python fallback reps suffice, so don't spend build time compiling.
15+
- PYGSTI_SKIP_CYTHON=1 pip install -r rtd-requirements.txt
1416
- jb build docs/
17+
# .doctrees is Sphinx's intermediate cache (~130 MB); don't publish it.
18+
- rm -rf docs/_build/html/.doctrees
1519
- mkdir -p $READTHEDOCS_OUTPUT/html
1620
- cp -r docs/_build/html/. $READTHEDOCS_OUTPUT/html/

README.md

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ In particular, there are a number of characterization protocols currently implem
3636

3737
PyGSTi is designed with a modular structure so as to be highly customizable
3838
and easily integrated to new or existing python software. It runs using
39-
python 3.9 or higher. To facilitate integration with software for running
39+
python 3.10 or higher. To facilitate integration with software for running
4040
cloud-QIP experiments, pyGSTi `Circuit` objects can be converted to IBM's
4141
**OpenQASM** and Rigetti Quantum Computing's **Quil** circuit description languages.
4242

@@ -188,23 +188,21 @@ Assuming you've followed the *local installation* directions above:
188188
* Change to the docs directory, by running:
189189
``cd docs``
190190
191-
* Convert the Markdown files to their corresponding notebooks:
192-
``jupytext --sync markdown/**/*.md``
193-
194-
* Change to the newly populated `notebooks` directory:
195-
``cd notebooks``
191+
* Build the notebooks from their Markdown sources, by running:
192+
``jupytext --to notebook markdown/**/*.md``
193+
This writes an `.ipynb` next to each `.md` under `markdown/`.
196194
197195
* Start up the Jupyter notebook server by running ``jupyter notebook`` or a JupyterLab server by running ``jupyter lab``.
198196
199197
The Jupyter server should open up your web browser to the server root, from
200-
where you can start the first `intro.ipynb` notebook. Note that the key
198+
where you can start the first `markdown/intro.ipynb` notebook. Note that the key
201199
command to execute a cell within the Jupyter notebook is ``Shift+Enter``, not
202200
just ``Enter``.
203201
204202
#### Contributing notebook changes
205-
**Only the `docs/markdown/*.md` files are version-controlled.** The `docs/notebooks/` directory is gitignored —
206-
it's a build artifact materialized by `jupytext --sync`. The canonical source is the `.md` file, and edits there
207-
"win" on the next sync. So:
203+
**Only the `docs/markdown/*.md` files are version-controlled.** The paired `.ipynb` files — generated next to
204+
each `.md` under `docs/markdown/` — are gitignored build artifacts. The canonical source is the `.md` file, and
205+
edits there "win" on the next sync. So:
208206
209207
- If you find it more convenient to edit the `.ipynb` (e.g. for interactive iteration), **sync your edits back to
210208
Markdown before committing**: from the `docs/` folder run `jupytext --sync markdown/**/*.md`, then commit the updated `.md`.

agent-docs/01-representation.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ A few notes on the structure:
150150

151151
The third layer has a non-obvious consequence: **representation degeneracies in state prep** (and to a lesser extent measurement). Multiple distinct parameter-vector values can map to the same physical noisy SPAM, because what you're modeling is the *combined* prep-plus-noisy-op, and the split between "ideal prep" and "noisy op" is non-unique. This is a sort of "gauge freedom" (although not the kind relevant to gauge optimization). If you're debugging a fit and SPAM's contributions to the parameter vector look weird, this might be why.
152152

153+
**Instruments** extend the same pattern to mid-circuit measurements. Each member factors as a measurement effect followed by a post-measurement CPTP gate, `I_k(ρ) = G_k(E_k^½ ρ E_k^½)` with `E_k = I_k†(I)`, and is represented as a single `ComposedOp([RootConjOperator(E_k), G_k])`. The effects `{E_k}` are gathered into one shared [`ComposedPOVM`](../pygsti/modelmembers/povms/composedpovm.py) — its completeness `Σ_k E_k = I` is exactly the instrument's joint trace preservation — and each `G_k` is parameterized independently (a CP-constrained `G_k` makes *that member* CP). Build these with the classmethods `Instrument.from_effects` / `Instrument.from_cptr_superops` (logic in [`modelmembers/instruments/_construction.py`](../pygsti/modelmembers/instruments/_construction.py)), or reach them via `convert(instrument, 'CPTPLND', basis)` / `set_all_parameterizations('CPTPLND')`. An n-outcome instrument needs only **n effects and n gates**. Note the shared-POVM layout makes the *later* members' parameter indices **non-contiguous** (they share the early POVM indices but own late gate indices); this is handled, but it's why `_submember_rpindices` can be an array rather than a slice.
154+
153155
## Pitfalls and gotchas
154156

155157
- **`gpindices` is the central glue.** Every `ModelMember` has a `gpindices` slice or array that says "I own indices `[a, b)` of the parent Model's `_paramvec`." A `gpindices` of `None` means "unallocated — will be assigned on add-to-model." Copying a member from one Model to another typically requires rebinding indices; `Model._rebuild_paramvec()` is the canonical way to force a fresh allocation.

0 commit comments

Comments
 (0)