qmlkit 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qmlkit-0.1.0/.gitattributes +10 -0
- qmlkit-0.1.0/.github/workflows/ci.yml +188 -0
- qmlkit-0.1.0/.github/workflows/docs.yml +60 -0
- qmlkit-0.1.0/.github/workflows/release.yml +115 -0
- qmlkit-0.1.0/.gitignore +34 -0
- qmlkit-0.1.0/AGENTS.md +81 -0
- qmlkit-0.1.0/CHANGELOG.md +959 -0
- qmlkit-0.1.0/HANDOFF.md +228 -0
- qmlkit-0.1.0/LICENSE +202 -0
- qmlkit-0.1.0/NOTICE +4 -0
- qmlkit-0.1.0/PKG-INFO +745 -0
- qmlkit-0.1.0/README.md +686 -0
- qmlkit-0.1.0/RELEASING.md +131 -0
- qmlkit-0.1.0/docs/about/changelog.md +3 -0
- qmlkit-0.1.0/docs/about/releasing.md +1 -0
- qmlkit-0.1.0/docs/about/stability.md +64 -0
- qmlkit-0.1.0/docs/about/validation.md +141 -0
- qmlkit-0.1.0/docs/guides/agents.md +160 -0
- qmlkit-0.1.0/docs/guides/backends.md +182 -0
- qmlkit-0.1.0/docs/guides/choosing-a-gradient.md +140 -0
- qmlkit-0.1.0/docs/guides/evaluation.md +226 -0
- qmlkit-0.1.0/docs/guides/extending.md +248 -0
- qmlkit-0.1.0/docs/guides/index.md +16 -0
- qmlkit-0.1.0/docs/guides/noise.md +218 -0
- qmlkit-0.1.0/docs/guides/parameter-shift.md +146 -0
- qmlkit-0.1.0/docs/index.md +127 -0
- qmlkit-0.1.0/docs/install.md +110 -0
- qmlkit-0.1.0/docs/javascripts/mathjax.js +5 -0
- qmlkit-0.1.0/docs/llms-full.txt +5469 -0
- qmlkit-0.1.0/docs/llms.txt +100 -0
- qmlkit-0.1.0/docs/reference/algorithms.md +78 -0
- qmlkit-0.1.0/docs/reference/analysis.md +45 -0
- qmlkit-0.1.0/docs/reference/ansatz.md +15 -0
- qmlkit-0.1.0/docs/reference/core.md +40 -0
- qmlkit-0.1.0/docs/reference/encoding.md +30 -0
- qmlkit-0.1.0/docs/reference/evaluation.md +34 -0
- qmlkit-0.1.0/docs/reference/gradients.md +35 -0
- qmlkit-0.1.0/docs/reference/index.md +18 -0
- qmlkit-0.1.0/docs/reference/kernels.md +15 -0
- qmlkit-0.1.0/docs/reference/nn.md +15 -0
- qmlkit-0.1.0/docs/studies/01-imbalanced-classification.md +124 -0
- qmlkit-0.1.0/docs/studies/02-quantum-kernels.md +98 -0
- qmlkit-0.1.0/docs/studies/03-regression.md +114 -0
- qmlkit-0.1.0/docs/studies/04-chemistry.md +114 -0
- qmlkit-0.1.0/docs/studies/05-beyond-classification.md +104 -0
- qmlkit-0.1.0/docs/studies/06-clinical.md +136 -0
- qmlkit-0.1.0/docs/studies/07-images-and-structure.md +130 -0
- qmlkit-0.1.0/docs/studies/index.md +44 -0
- qmlkit-0.1.0/docs/tutorials/01-first-circuit.md +231 -0
- qmlkit-0.1.0/docs/tutorials/02-encoding-data.md +197 -0
- qmlkit-0.1.0/docs/tutorials/03-gradients.md +258 -0
- qmlkit-0.1.0/docs/tutorials/04-ansatz-design.md +204 -0
- qmlkit-0.1.0/docs/tutorials/05-training-torch.md +176 -0
- qmlkit-0.1.0/docs/tutorials/06-quantum-kernels.md +230 -0
- qmlkit-0.1.0/docs/tutorials/07-reuploading.md +216 -0
- qmlkit-0.1.0/docs/tutorials/08-trainability.md +174 -0
- qmlkit-0.1.0/docs/tutorials/index.md +67 -0
- qmlkit-0.1.0/examples/accelerate_pennylane.py +173 -0
- qmlkit-0.1.0/examples/benchmark_pennylane.py +252 -0
- qmlkit-0.1.0/examples/compare_pennylane.py +268 -0
- qmlkit-0.1.0/examples/credit_data.py +169 -0
- qmlkit-0.1.0/examples/credit_risk.py +351 -0
- qmlkit-0.1.0/examples/experiments.py +238 -0
- qmlkit-0.1.0/examples/head_to_head.py +337 -0
- qmlkit-0.1.0/examples/quickstart.py +277 -0
- qmlkit-0.1.0/examples/toward_hardware.py +222 -0
- qmlkit-0.1.0/mkdocs.yml +143 -0
- qmlkit-0.1.0/pyproject.toml +159 -0
- qmlkit-0.1.0/scripts/generate_llms_txt.py +287 -0
- qmlkit-0.1.0/scripts/verify_install.py +146 -0
- qmlkit-0.1.0/src/qmlkit/__init__.py +495 -0
- qmlkit-0.1.0/src/qmlkit/_aliases.py +135 -0
- qmlkit-0.1.0/src/qmlkit/algorithms/__init__.py +82 -0
- qmlkit-0.1.0/src/qmlkit/algorithms/adapt.py +297 -0
- qmlkit-0.1.0/src/qmlkit/algorithms/autoencoder.py +206 -0
- qmlkit-0.1.0/src/qmlkit/algorithms/chemistry.py +222 -0
- qmlkit-0.1.0/src/qmlkit/algorithms/clustering.py +149 -0
- qmlkit-0.1.0/src/qmlkit/algorithms/hamiltonians.py +143 -0
- qmlkit-0.1.0/src/qmlkit/algorithms/molecule.py +442 -0
- qmlkit-0.1.0/src/qmlkit/algorithms/qaoa.py +208 -0
- qmlkit-0.1.0/src/qmlkit/algorithms/rl.py +198 -0
- qmlkit-0.1.0/src/qmlkit/algorithms/vqe.py +198 -0
- qmlkit-0.1.0/src/qmlkit/ansatz/__init__.py +68 -0
- qmlkit-0.1.0/src/qmlkit/ansatz/blocks.py +348 -0
- qmlkit-0.1.0/src/qmlkit/ansatz/library.py +570 -0
- qmlkit-0.1.0/src/qmlkit/ansatz/reupload.py +168 -0
- qmlkit-0.1.0/src/qmlkit/baselines.py +604 -0
- qmlkit-0.1.0/src/qmlkit/budget.py +234 -0
- qmlkit-0.1.0/src/qmlkit/core/__init__.py +1 -0
- qmlkit-0.1.0/src/qmlkit/core/backends/__init__.py +22 -0
- qmlkit-0.1.0/src/qmlkit/core/backends/_sampling.py +43 -0
- qmlkit-0.1.0/src/qmlkit/core/backends/base.py +256 -0
- qmlkit-0.1.0/src/qmlkit/core/backends/cirq_backend.py +110 -0
- qmlkit-0.1.0/src/qmlkit/core/backends/cirq_density_backend.py +71 -0
- qmlkit-0.1.0/src/qmlkit/core/backends/noisy.py +86 -0
- qmlkit-0.1.0/src/qmlkit/core/backends/numpy_backend.py +276 -0
- qmlkit-0.1.0/src/qmlkit/core/backends/qiskit_aer_backend.py +79 -0
- qmlkit-0.1.0/src/qmlkit/core/backends/qiskit_backend.py +104 -0
- qmlkit-0.1.0/src/qmlkit/core/backends/registry.py +210 -0
- qmlkit-0.1.0/src/qmlkit/core/backends/spinqit_backend.py +233 -0
- qmlkit-0.1.0/src/qmlkit/core/backends/torch_backend.py +185 -0
- qmlkit-0.1.0/src/qmlkit/core/builder.py +189 -0
- qmlkit-0.1.0/src/qmlkit/core/execute.py +193 -0
- qmlkit-0.1.0/src/qmlkit/core/gates.py +243 -0
- qmlkit-0.1.0/src/qmlkit/core/ir.py +320 -0
- qmlkit-0.1.0/src/qmlkit/core/observables.py +269 -0
- qmlkit-0.1.0/src/qmlkit/datasets.py +178 -0
- qmlkit-0.1.0/src/qmlkit/diagnostics.py +719 -0
- qmlkit-0.1.0/src/qmlkit/draw.py +177 -0
- qmlkit-0.1.0/src/qmlkit/encoding/__init__.py +63 -0
- qmlkit-0.1.0/src/qmlkit/encoding/amplitude.py +178 -0
- qmlkit-0.1.0/src/qmlkit/encoding/angle.py +61 -0
- qmlkit-0.1.0/src/qmlkit/encoding/feature_maps.py +353 -0
- qmlkit-0.1.0/src/qmlkit/encoding/hamiltonian.py +206 -0
- qmlkit-0.1.0/src/qmlkit/encoding/pipeline.py +198 -0
- qmlkit-0.1.0/src/qmlkit/encoding/scaling.py +139 -0
- qmlkit-0.1.0/src/qmlkit/evaluate.py +686 -0
- qmlkit-0.1.0/src/qmlkit/fourier.py +124 -0
- qmlkit-0.1.0/src/qmlkit/generative.py +406 -0
- qmlkit-0.1.0/src/qmlkit/gradients/__init__.py +61 -0
- qmlkit-0.1.0/src/qmlkit/gradients/adjoint.py +138 -0
- qmlkit-0.1.0/src/qmlkit/gradients/batch.py +275 -0
- qmlkit-0.1.0/src/qmlkit/gradients/dispatch.py +247 -0
- qmlkit-0.1.0/src/qmlkit/gradients/hadamard.py +108 -0
- qmlkit-0.1.0/src/qmlkit/gradients/parameter_shift.py +142 -0
- qmlkit-0.1.0/src/qmlkit/gradients/rules.py +151 -0
- qmlkit-0.1.0/src/qmlkit/gradients/spsa.py +134 -0
- qmlkit-0.1.0/src/qmlkit/imbalance.py +335 -0
- qmlkit-0.1.0/src/qmlkit/info.py +153 -0
- qmlkit-0.1.0/src/qmlkit/interop.py +778 -0
- qmlkit-0.1.0/src/qmlkit/kernels/__init__.py +69 -0
- qmlkit-0.1.0/src/qmlkit/kernels/estimators.py +206 -0
- qmlkit-0.1.0/src/qmlkit/kernels/matrix.py +439 -0
- qmlkit-0.1.0/src/qmlkit/kernels/models.py +315 -0
- qmlkit-0.1.0/src/qmlkit/metrics.py +394 -0
- qmlkit-0.1.0/src/qmlkit/nn/__init__.py +18 -0
- qmlkit-0.1.0/src/qmlkit/nn/advanced.py +254 -0
- qmlkit-0.1.0/src/qmlkit/nn/layer.py +343 -0
- qmlkit-0.1.0/src/qmlkit/nn/losses.py +124 -0
- qmlkit-0.1.0/src/qmlkit/nn/models.py +245 -0
- qmlkit-0.1.0/src/qmlkit/optim.py +306 -0
- qmlkit-0.1.0/src/qmlkit/provenance.py +271 -0
- qmlkit-0.1.0/src/qmlkit/py.typed +0 -0
- qmlkit-0.1.0/src/qmlkit/search.py +561 -0
- qmlkit-0.1.0/src/qmlkit/shadows.py +117 -0
- qmlkit-0.1.0/src/qmlkit/utils/__init__.py +19 -0
- qmlkit-0.1.0/src/qmlkit/utils/errors.py +130 -0
- qmlkit-0.1.0/src/qmlkit/utils/shots.py +55 -0
- qmlkit-0.1.0/tests/test_advanced.py +336 -0
- qmlkit-0.1.0/tests/test_agent_api.py +460 -0
- qmlkit-0.1.0/tests/test_algorithms.py +450 -0
- qmlkit-0.1.0/tests/test_analysis.py +538 -0
- qmlkit-0.1.0/tests/test_ansatz.py +305 -0
- qmlkit-0.1.0/tests/test_baseline.py +265 -0
- qmlkit-0.1.0/tests/test_batch.py +364 -0
- qmlkit-0.1.0/tests/test_budget.py +101 -0
- qmlkit-0.1.0/tests/test_builder.py +200 -0
- qmlkit-0.1.0/tests/test_core.py +202 -0
- qmlkit-0.1.0/tests/test_cross_backend.py +216 -0
- qmlkit-0.1.0/tests/test_docs.py +84 -0
- qmlkit-0.1.0/tests/test_encoding.py +387 -0
- qmlkit-0.1.0/tests/test_evaluate.py +245 -0
- qmlkit-0.1.0/tests/test_grad_batch.py +318 -0
- qmlkit-0.1.0/tests/test_gradient_methods.py +499 -0
- qmlkit-0.1.0/tests/test_gradients.py +290 -0
- qmlkit-0.1.0/tests/test_imbalance.py +243 -0
- qmlkit-0.1.0/tests/test_import.py +532 -0
- qmlkit-0.1.0/tests/test_injection.py +172 -0
- qmlkit-0.1.0/tests/test_interop.py +226 -0
- qmlkit-0.1.0/tests/test_kernels.py +332 -0
- qmlkit-0.1.0/tests/test_nn.py +287 -0
- qmlkit-0.1.0/tests/test_noisy_backends.py +403 -0
- qmlkit-0.1.0/tests/test_observables.py +240 -0
- qmlkit-0.1.0/tests/test_pennylane_parity.py +814 -0
- qmlkit-0.1.0/tests/test_provenance.py +128 -0
- qmlkit-0.1.0/tests/test_search.py +288 -0
- qmlkit-0.1.0/tests/test_spinqit_backend.py +63 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Normalise to LF in the repository and in the sdist. Without this, a checkout on
|
|
2
|
+
# Windows commits CRLF, and `ruff format --check` then disagrees with a Linux CI run
|
|
3
|
+
# over files nobody edited.
|
|
4
|
+
* text=auto eol=lf
|
|
5
|
+
|
|
6
|
+
# Never touch these.
|
|
7
|
+
*.png binary
|
|
8
|
+
*.jpg binary
|
|
9
|
+
*.pdf binary
|
|
10
|
+
*.ipynb text eol=lf
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
# Core: no optional SDK installed. Proves `pip install qmlkit` is genuinely
|
|
15
|
+
# self-sufficient and that a missing backend degrades to a clear message.
|
|
16
|
+
core:
|
|
17
|
+
name: core · py${{ matrix.python }} · ${{ matrix.os }}
|
|
18
|
+
runs-on: ${{ matrix.os }}
|
|
19
|
+
strategy:
|
|
20
|
+
fail-fast: false
|
|
21
|
+
matrix:
|
|
22
|
+
os: [ubuntu-latest]
|
|
23
|
+
python: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
24
|
+
include:
|
|
25
|
+
- os: windows-latest
|
|
26
|
+
python: "3.12"
|
|
27
|
+
- os: macos-latest
|
|
28
|
+
python: "3.12"
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v4
|
|
31
|
+
- uses: actions/setup-python@v5
|
|
32
|
+
with:
|
|
33
|
+
python-version: ${{ matrix.python }}
|
|
34
|
+
cache: pip
|
|
35
|
+
- run: pip install -e ".[dev]"
|
|
36
|
+
- name: Lint
|
|
37
|
+
run: |
|
|
38
|
+
ruff check src tests
|
|
39
|
+
ruff format --check src tests
|
|
40
|
+
- name: Type-check
|
|
41
|
+
run: mypy
|
|
42
|
+
- name: Test
|
|
43
|
+
run: coverage run --parallel-mode --source=qmlkit -m pytest -q
|
|
44
|
+
- uses: actions/upload-artifact@v4
|
|
45
|
+
with:
|
|
46
|
+
name: coverage-core-${{ matrix.os }}-${{ matrix.python }}
|
|
47
|
+
path: .coverage.*
|
|
48
|
+
include-hidden-files: true
|
|
49
|
+
|
|
50
|
+
# Every optional dependency gets a job where its marked tests actually execute.
|
|
51
|
+
# The core job installs none of them, so without this they would only ever skip:
|
|
52
|
+
# a suite that silently skips its hardest tests is worse than no suite.
|
|
53
|
+
#
|
|
54
|
+
# qiskit, cirq -> the cross-backend equivalence suite
|
|
55
|
+
# torch -> the autograd bridge and the backprop gradient
|
|
56
|
+
# pennylane -> 301 cross-validation cases against a second implementation
|
|
57
|
+
sdk:
|
|
58
|
+
name: ${{ matrix.sdk }} · py3.12
|
|
59
|
+
runs-on: ubuntu-latest
|
|
60
|
+
strategy:
|
|
61
|
+
fail-fast: false
|
|
62
|
+
matrix:
|
|
63
|
+
sdk: [qiskit, cirq, torch, pennylane]
|
|
64
|
+
steps:
|
|
65
|
+
- uses: actions/checkout@v4
|
|
66
|
+
- uses: actions/setup-python@v5
|
|
67
|
+
with:
|
|
68
|
+
python-version: "3.12"
|
|
69
|
+
cache: pip
|
|
70
|
+
- run: pip install -e ".[dev,${{ matrix.sdk }}]"
|
|
71
|
+
# This line is load-bearing. If the SDK were missing, importorskip and skipif
|
|
72
|
+
# would turn every test in this job green by skipping it -- verified locally:
|
|
73
|
+
# `pytest -m spinqit` without SpinQit installed exits 0, not 5, because the
|
|
74
|
+
# tests are collected and then skipped. An SDK job that proves nothing is worse
|
|
75
|
+
# than no SDK job, so assert the import before trusting the run below.
|
|
76
|
+
- name: Confirm ${{ matrix.sdk }} actually imports
|
|
77
|
+
run: python -c "import ${{ matrix.sdk }}"
|
|
78
|
+
- name: Run the ${{ matrix.sdk }}-marked tests
|
|
79
|
+
run: coverage run --parallel-mode --source=qmlkit -m pytest -q -m ${{ matrix.sdk }}
|
|
80
|
+
- uses: actions/upload-artifact@v4
|
|
81
|
+
with:
|
|
82
|
+
name: coverage-${{ matrix.sdk }}
|
|
83
|
+
path: .coverage.*
|
|
84
|
+
include-hidden-files: true
|
|
85
|
+
|
|
86
|
+
# Everything installed at once, running the whole suite rather than a marker
|
|
87
|
+
# selection. Two reasons. First, the coverage number: the core jobs have no optional
|
|
88
|
+
# SDK and the sdk jobs each run one marker, so between them a file can be covered by
|
|
89
|
+
# nobody -- test_advanced.py ran in *no* CI job for exactly that reason, and
|
|
90
|
+
# nn/advanced.py sat at 66%. Second, integration: this is the only job where torch,
|
|
91
|
+
# Qiskit, Cirq and PennyLane are present together, which is how most users install.
|
|
92
|
+
full:
|
|
93
|
+
name: All extras · py3.12
|
|
94
|
+
runs-on: ubuntu-latest
|
|
95
|
+
steps:
|
|
96
|
+
- uses: actions/checkout@v4
|
|
97
|
+
- uses: actions/setup-python@v5
|
|
98
|
+
with:
|
|
99
|
+
python-version: "3.12"
|
|
100
|
+
cache: pip
|
|
101
|
+
- run: pip install -e ".[dev,torch,qiskit,cirq,aer,sklearn,pennylane,viz]"
|
|
102
|
+
# aer is here and not in the matrix above because its distribution and its
|
|
103
|
+
# import name differ, and because it is the only way the qiskit-aer noisy
|
|
104
|
+
# backend gets exercised at all: without it every test in
|
|
105
|
+
# tests/test_noisy_backends.py skips itself and the job still goes green.
|
|
106
|
+
- name: Confirm every extra actually imported
|
|
107
|
+
run: python -c "import torch, qiskit, qiskit_aer, cirq, sklearn, pennylane, matplotlib"
|
|
108
|
+
- name: Whole suite, no marker filter
|
|
109
|
+
run: coverage run --parallel-mode --source=qmlkit -m pytest -q
|
|
110
|
+
- uses: actions/upload-artifact@v4
|
|
111
|
+
with:
|
|
112
|
+
name: coverage-full
|
|
113
|
+
path: .coverage.*
|
|
114
|
+
include-hidden-files: true
|
|
115
|
+
|
|
116
|
+
# SpinQit only has wheels for 3.8-3.10 and pins numpy<2, so it gets its own job
|
|
117
|
+
# on 3.10. Allowed to fail without blocking: the NumPy backend is the reference,
|
|
118
|
+
# and a SpinQit packaging problem upstream is not a qmlkit regression.
|
|
119
|
+
spinqit:
|
|
120
|
+
name: spinqit · py3.10
|
|
121
|
+
runs-on: ubuntu-latest
|
|
122
|
+
continue-on-error: true
|
|
123
|
+
steps:
|
|
124
|
+
- uses: actions/checkout@v4
|
|
125
|
+
- uses: actions/setup-python@v5
|
|
126
|
+
with:
|
|
127
|
+
python-version: "3.10"
|
|
128
|
+
cache: pip
|
|
129
|
+
- run: pip install -e ".[dev,spinqit]"
|
|
130
|
+
- name: Convention self-check
|
|
131
|
+
run: |
|
|
132
|
+
python -c "
|
|
133
|
+
import qmlkit as qk
|
|
134
|
+
r = qk.get_backend('spinqit').verify_conventions()
|
|
135
|
+
print(r)
|
|
136
|
+
assert all(r.values()), [k for k, v in r.items() if not v]
|
|
137
|
+
"
|
|
138
|
+
- name: Cross-backend equivalence
|
|
139
|
+
run: pytest -q -m spinqit
|
|
140
|
+
|
|
141
|
+
# One honest coverage number, measured across every environment, because no
|
|
142
|
+
# single interpreter can import all four backends.
|
|
143
|
+
coverage:
|
|
144
|
+
name: Combined coverage
|
|
145
|
+
needs: [core, sdk, full]
|
|
146
|
+
runs-on: ubuntu-latest
|
|
147
|
+
steps:
|
|
148
|
+
- uses: actions/checkout@v4
|
|
149
|
+
- uses: actions/setup-python@v5
|
|
150
|
+
with:
|
|
151
|
+
python-version: "3.12"
|
|
152
|
+
- run: pip install -e ".[dev]"
|
|
153
|
+
- uses: actions/download-artifact@v4
|
|
154
|
+
with:
|
|
155
|
+
pattern: coverage-*
|
|
156
|
+
merge-multiple: true
|
|
157
|
+
- run: |
|
|
158
|
+
coverage combine
|
|
159
|
+
coverage report --fail-under=90
|
|
160
|
+
coverage xml
|
|
161
|
+
- uses: actions/upload-artifact@v4
|
|
162
|
+
with:
|
|
163
|
+
name: coverage-xml
|
|
164
|
+
path: coverage.xml
|
|
165
|
+
|
|
166
|
+
build:
|
|
167
|
+
name: Build and check the distribution
|
|
168
|
+
runs-on: ubuntu-latest
|
|
169
|
+
steps:
|
|
170
|
+
- uses: actions/checkout@v4
|
|
171
|
+
- uses: actions/setup-python@v5
|
|
172
|
+
with:
|
|
173
|
+
python-version: "3.12"
|
|
174
|
+
- run: pip install build twine
|
|
175
|
+
- run: python -m build
|
|
176
|
+
- run: twine check dist/*
|
|
177
|
+
# Install the built wheel into an environment that has nothing else, and run
|
|
178
|
+
# the library from there. An editable install imports out of src/ and would
|
|
179
|
+
# keep working even if a module were missing from the wheel entirely.
|
|
180
|
+
- name: Verify the wheel in a clean environment
|
|
181
|
+
run: |
|
|
182
|
+
python -m venv /tmp/clean
|
|
183
|
+
/tmp/clean/bin/pip install --quiet dist/*.whl
|
|
184
|
+
/tmp/clean/bin/python scripts/verify_install.py
|
|
185
|
+
- uses: actions/upload-artifact@v4
|
|
186
|
+
with:
|
|
187
|
+
name: dist
|
|
188
|
+
path: dist/
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Build the documentation on every change, and publish it from main.
|
|
2
|
+
#
|
|
3
|
+
# The build runs `pytest tests/test_docs.py` first, which executes every Python
|
|
4
|
+
# block on every page. Docs that no longer run are treated as a broken build, not
|
|
5
|
+
# as cosmetic drift -- the first thing a new reader does is copy the first snippet
|
|
6
|
+
# on the page, and a tutorial that stopped working two releases ago looks exactly
|
|
7
|
+
# like one that still works.
|
|
8
|
+
name: Docs
|
|
9
|
+
|
|
10
|
+
on:
|
|
11
|
+
push:
|
|
12
|
+
branches: [main]
|
|
13
|
+
pull_request:
|
|
14
|
+
workflow_dispatch:
|
|
15
|
+
|
|
16
|
+
permissions:
|
|
17
|
+
contents: read
|
|
18
|
+
|
|
19
|
+
concurrency:
|
|
20
|
+
group: docs-${{ github.ref }}
|
|
21
|
+
cancel-in-progress: true
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
build:
|
|
25
|
+
name: Build and verify
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v4
|
|
29
|
+
- uses: actions/setup-python@v5
|
|
30
|
+
with:
|
|
31
|
+
python-version: "3.12"
|
|
32
|
+
cache: pip
|
|
33
|
+
# sklearn and torch so the pages that need them run rather than skip
|
|
34
|
+
- run: pip install -e ".[docs,dev,sklearn,torch]"
|
|
35
|
+
- name: Every snippet in the docs must run
|
|
36
|
+
run: pytest tests/test_docs.py -q
|
|
37
|
+
# llms.txt is generated from these pages and from the package, and committed.
|
|
38
|
+
# Regenerating here would hide the drift; checking makes the staleness the failure.
|
|
39
|
+
- name: The llms.txt files must be current
|
|
40
|
+
run: python scripts/generate_llms_txt.py --check
|
|
41
|
+
# --strict turns broken internal links and missing nav entries into failures
|
|
42
|
+
- run: mkdocs build --strict
|
|
43
|
+
- uses: actions/upload-pages-artifact@v3
|
|
44
|
+
with:
|
|
45
|
+
path: site
|
|
46
|
+
|
|
47
|
+
deploy:
|
|
48
|
+
name: Publish to GitHub Pages
|
|
49
|
+
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
|
50
|
+
needs: build
|
|
51
|
+
runs-on: ubuntu-latest
|
|
52
|
+
permissions:
|
|
53
|
+
pages: write
|
|
54
|
+
id-token: write
|
|
55
|
+
environment:
|
|
56
|
+
name: github-pages
|
|
57
|
+
url: ${{ steps.deploy.outputs.page_url }}
|
|
58
|
+
steps:
|
|
59
|
+
- id: deploy
|
|
60
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Publish to PyPI when a version tag is pushed.
|
|
2
|
+
#
|
|
3
|
+
# git tag v0.1.0 && git push origin v0.1.0
|
|
4
|
+
#
|
|
5
|
+
# Authentication is PyPI Trusted Publishing (OIDC), so there is no API token in
|
|
6
|
+
# this repository's secrets and nothing to rotate or leak. It has to be configured
|
|
7
|
+
# once on PyPI first: Your projects -> qmlkit -> Publishing -> add a GitHub
|
|
8
|
+
# publisher for owner `Ziadt160`, repository `qmlkit`, workflow `release.yml`,
|
|
9
|
+
# environment `pypi`. For the very first upload, where the project does not exist
|
|
10
|
+
# yet, add it under "Pending publishers" instead.
|
|
11
|
+
name: Release
|
|
12
|
+
|
|
13
|
+
on:
|
|
14
|
+
push:
|
|
15
|
+
tags: ["v*"]
|
|
16
|
+
workflow_dispatch:
|
|
17
|
+
|
|
18
|
+
permissions:
|
|
19
|
+
contents: read
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
# Never publish something the test suite has not seen: a tag can be pushed to a
|
|
23
|
+
# commit CI never ran on. Deliberately narrower than ci.yml -- the version floor
|
|
24
|
+
# and a current release, with PennyLane so the 301 cross-validation cases run.
|
|
25
|
+
# Resolving torch + qiskit + cirq + pennylane together across every interpreter is
|
|
26
|
+
# fragile, and ci.yml already covers each of them on its own on every push.
|
|
27
|
+
verify:
|
|
28
|
+
name: Test before publishing
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
strategy:
|
|
31
|
+
fail-fast: true
|
|
32
|
+
matrix:
|
|
33
|
+
python: ["3.10", "3.12"]
|
|
34
|
+
steps:
|
|
35
|
+
- uses: actions/checkout@v4
|
|
36
|
+
- uses: actions/setup-python@v5
|
|
37
|
+
with:
|
|
38
|
+
python-version: ${{ matrix.python }}
|
|
39
|
+
cache: pip
|
|
40
|
+
- run: pip install -e ".[dev,pennylane]"
|
|
41
|
+
- run: ruff check src tests && ruff format --check src tests
|
|
42
|
+
- run: mypy
|
|
43
|
+
- run: pytest -q
|
|
44
|
+
|
|
45
|
+
build:
|
|
46
|
+
name: Build and verify the distribution
|
|
47
|
+
needs: verify
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
outputs:
|
|
50
|
+
version: ${{ steps.version.outputs.version }}
|
|
51
|
+
steps:
|
|
52
|
+
- uses: actions/checkout@v4
|
|
53
|
+
- uses: actions/setup-python@v5
|
|
54
|
+
with:
|
|
55
|
+
python-version: "3.12"
|
|
56
|
+
- run: pip install build twine
|
|
57
|
+
- run: python -m build
|
|
58
|
+
- run: twine check dist/*
|
|
59
|
+
- name: Verify the wheel in a clean environment
|
|
60
|
+
run: |
|
|
61
|
+
python -m venv /tmp/clean
|
|
62
|
+
/tmp/clean/bin/pip install --quiet dist/*.whl
|
|
63
|
+
/tmp/clean/bin/python scripts/verify_install.py
|
|
64
|
+
# A tag that disagrees with pyproject.toml would publish a version nobody
|
|
65
|
+
# asked for, and PyPI will not let it be replaced afterwards.
|
|
66
|
+
- name: Tag must match the packaged version
|
|
67
|
+
id: version
|
|
68
|
+
run: |
|
|
69
|
+
packaged=$(python -c "import tomllib,pathlib;print(tomllib.loads(pathlib.Path('pyproject.toml').read_text())['project']['version'])")
|
|
70
|
+
echo "version=$packaged" >> "$GITHUB_OUTPUT"
|
|
71
|
+
if [ "${GITHUB_REF_TYPE}" = "tag" ] && [ "${GITHUB_REF_NAME}" != "v$packaged" ]; then
|
|
72
|
+
echo "::error::tag ${GITHUB_REF_NAME} does not match pyproject version $packaged"
|
|
73
|
+
exit 1
|
|
74
|
+
fi
|
|
75
|
+
- uses: actions/upload-artifact@v4
|
|
76
|
+
with:
|
|
77
|
+
name: dist
|
|
78
|
+
path: dist/
|
|
79
|
+
|
|
80
|
+
# TestPyPI first, so a broken description or a bad filename is caught somewhere
|
|
81
|
+
# that can be thrown away. A PyPI version number can never be reused.
|
|
82
|
+
testpypi:
|
|
83
|
+
name: Publish to TestPyPI
|
|
84
|
+
needs: build
|
|
85
|
+
runs-on: ubuntu-latest
|
|
86
|
+
environment:
|
|
87
|
+
name: testpypi
|
|
88
|
+
url: https://test.pypi.org/p/qmlkit
|
|
89
|
+
permissions:
|
|
90
|
+
id-token: write
|
|
91
|
+
steps:
|
|
92
|
+
- uses: actions/download-artifact@v4
|
|
93
|
+
with:
|
|
94
|
+
name: dist
|
|
95
|
+
path: dist/
|
|
96
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
97
|
+
with:
|
|
98
|
+
repository-url: https://test.pypi.org/legacy/
|
|
99
|
+
skip-existing: true
|
|
100
|
+
|
|
101
|
+
pypi:
|
|
102
|
+
name: Publish to PyPI
|
|
103
|
+
needs: testpypi
|
|
104
|
+
runs-on: ubuntu-latest
|
|
105
|
+
environment:
|
|
106
|
+
name: pypi
|
|
107
|
+
url: https://pypi.org/p/qmlkit
|
|
108
|
+
permissions:
|
|
109
|
+
id-token: write
|
|
110
|
+
steps:
|
|
111
|
+
- uses: actions/download-artifact@v4
|
|
112
|
+
with:
|
|
113
|
+
name: dist
|
|
114
|
+
path: dist/
|
|
115
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
qmlkit-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
.eggs/
|
|
8
|
+
|
|
9
|
+
# Testing / typing caches
|
|
10
|
+
.pytest_cache/
|
|
11
|
+
.mypy_cache/
|
|
12
|
+
.ruff_cache/
|
|
13
|
+
.coverage
|
|
14
|
+
.coverage.*
|
|
15
|
+
htmlcov/
|
|
16
|
+
coverage.xml
|
|
17
|
+
|
|
18
|
+
# Environments
|
|
19
|
+
.venv/
|
|
20
|
+
venv/
|
|
21
|
+
env/
|
|
22
|
+
|
|
23
|
+
# OS / editors
|
|
24
|
+
.DS_Store
|
|
25
|
+
Thumbs.db
|
|
26
|
+
Desktop.ini
|
|
27
|
+
.vscode/
|
|
28
|
+
.idea/
|
|
29
|
+
|
|
30
|
+
# Built documentation
|
|
31
|
+
site/
|
|
32
|
+
|
|
33
|
+
# the Kaggle credit-risk CSV: redistributable only from Kaggle, and 1.8 MB
|
|
34
|
+
credit_risk_dataset.csv
|
qmlkit-0.1.0/AGENTS.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Instructions for a coding agent working **on** qmlkit. To *use* the library, read
|
|
4
|
+
[`docs/llms.txt`](docs/llms.txt) instead — it is the same information written for
|
|
5
|
+
the caller rather than the contributor.
|
|
6
|
+
|
|
7
|
+
[`HANDOFF.md`](HANDOFF.md) is the long version of this file: current status, why
|
|
8
|
+
each convention exists, and what to do next. Read it before anything non-trivial.
|
|
9
|
+
This page is the short list of things that will break the build if you get them
|
|
10
|
+
wrong.
|
|
11
|
+
|
|
12
|
+
## Commands
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install -e ".[dev,torch,qiskit,cirq,sklearn,pennylane]"
|
|
16
|
+
pytest -q # the whole suite
|
|
17
|
+
pytest -q -m "not pennylane" # faster, skips the 301 parity cases
|
|
18
|
+
ruff check src tests && mypy # both must be clean
|
|
19
|
+
python scripts/generate_llms_txt.py # after any docs or public-API change
|
|
20
|
+
python scripts/verify_install.py # the core really does import with only NumPy
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
SpinQit needs its own interpreter — it ships wheels for Python 3.8–3.10 only and
|
|
24
|
+
pins `numpy<2`:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
C:/Users/pc/miniconda3/envs/spinq_env/python.exe -m pytest -m spinqit
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Rules the tests enforce
|
|
31
|
+
|
|
32
|
+
1. **An algorithm owns its loop, not its circuit.** Every model takes `ansatz=` /
|
|
33
|
+
`feature_map=` / `filter=` and must actually use it. `tests/test_injection.py`
|
|
34
|
+
injects two different sizes and asserts the parameter count follows.
|
|
35
|
+
2. **Estimators must be scikit-learn clonable.** Constructor arguments stored under
|
|
36
|
+
their own names, plus `SklearnCompatible`. This keeps scikit-learn optional while
|
|
37
|
+
letting QSVC/QSVR run inside `Pipeline` and `GridSearchCV`.
|
|
38
|
+
3. **The core depends on NumPy and nothing else.** Not SciPy — use `math.erf` and the
|
|
39
|
+
stdlib. CI installs nothing else in the `core` jobs, and this has been broken once.
|
|
40
|
+
4. **Documentation is executable.** `tests/test_docs.py` runs every Python block on
|
|
41
|
+
every page, so an API change and its docs go in the same commit.
|
|
42
|
+
5. **Names have to stay findable.** `qmlkit/_aliases.py` maps what PennyLane and
|
|
43
|
+
Qiskit call each thing; `tests/test_agent_api.py` asserts every target still
|
|
44
|
+
exists. Rename a public name and you update that table in the same commit.
|
|
45
|
+
6. **`docs/llms.txt` is generated and committed.** Change the docs or the public API
|
|
46
|
+
and regenerate it, or CI fails on the stale copy.
|
|
47
|
+
|
|
48
|
+
## Traps that have already cost time
|
|
49
|
+
|
|
50
|
+
- **Never use a NumPy-2-only API** (`np.trapezoid`, `np.in1d`, …) in `src/` or
|
|
51
|
+
`tests/`. SpinQit pins `numpy<2` and the suite must pass in both environments.
|
|
52
|
+
- **Always write `npt.NDArray[Any]`, never bare `np.ndarray`.** Type-parameter
|
|
53
|
+
defaults only arrived in NumPy 2.3, so 3.10 CI fails with 60 `type-arg` errors.
|
|
54
|
+
- **Never set `python_version` in `[tool.mypy]`.** It makes mypy parse dependency
|
|
55
|
+
stubs at that version too, and NumPy's stubs use PEP 695 `type` statements, which
|
|
56
|
+
are a syntax error before 3.12. Cross-version signal comes from CI running mypy
|
|
57
|
+
on 3.10.
|
|
58
|
+
- **The PennyLane parity fuzzer draws gate names from a snapshot taken at import**,
|
|
59
|
+
not from the live registry — other test modules register throwaway gates at run
|
|
60
|
+
time, which made it pass alone and fail in a full run.
|
|
61
|
+
- **Extend `tests/test_pennylane_parity.py` when adding a gate.** A test there
|
|
62
|
+
asserts the mapping covers every built-in gate, so a new one cannot escape
|
|
63
|
+
cross-validation. Every bug found in this project has been the
|
|
64
|
+
plausible-wrong-number kind that only a second implementation catches.
|
|
65
|
+
|
|
66
|
+
## Design commitments
|
|
67
|
+
|
|
68
|
+
- **Simulator-only for the whole 0.x line.** This is a constraint that propagates,
|
|
69
|
+
not a scope trim: it makes `adjoint` the correct default gradient, makes shot
|
|
70
|
+
noise opt-in, and demotes anything whose value is cutting *measurement* cost.
|
|
71
|
+
Parameter-shift stays the teaching subject and the reference that validates
|
|
72
|
+
adjoint — never the performance default.
|
|
73
|
+
- **Simple on top, open underneath.** Three layers, and nothing at a higher one
|
|
74
|
+
hides a lower one. Every extension point is a registry.
|
|
75
|
+
- **When something is named after a pattern rather than a structure, make it a
|
|
76
|
+
composition, not a class.** Data re-uploading is `EncodingLayer` in the block
|
|
77
|
+
vocabulary, with `reupload()` as a convenience over it.
|
|
78
|
+
- **The error message is the documentation.** Most callers are models that will not
|
|
79
|
+
read the docs site; they read the traceback. An error about a name says what was
|
|
80
|
+
wrong, what was probably meant, and what is allowed — build it with
|
|
81
|
+
`qmlkit.utils.errors.unknown`.
|