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.
Files changed (177) hide show
  1. qmlkit-0.1.0/.gitattributes +10 -0
  2. qmlkit-0.1.0/.github/workflows/ci.yml +188 -0
  3. qmlkit-0.1.0/.github/workflows/docs.yml +60 -0
  4. qmlkit-0.1.0/.github/workflows/release.yml +115 -0
  5. qmlkit-0.1.0/.gitignore +34 -0
  6. qmlkit-0.1.0/AGENTS.md +81 -0
  7. qmlkit-0.1.0/CHANGELOG.md +959 -0
  8. qmlkit-0.1.0/HANDOFF.md +228 -0
  9. qmlkit-0.1.0/LICENSE +202 -0
  10. qmlkit-0.1.0/NOTICE +4 -0
  11. qmlkit-0.1.0/PKG-INFO +745 -0
  12. qmlkit-0.1.0/README.md +686 -0
  13. qmlkit-0.1.0/RELEASING.md +131 -0
  14. qmlkit-0.1.0/docs/about/changelog.md +3 -0
  15. qmlkit-0.1.0/docs/about/releasing.md +1 -0
  16. qmlkit-0.1.0/docs/about/stability.md +64 -0
  17. qmlkit-0.1.0/docs/about/validation.md +141 -0
  18. qmlkit-0.1.0/docs/guides/agents.md +160 -0
  19. qmlkit-0.1.0/docs/guides/backends.md +182 -0
  20. qmlkit-0.1.0/docs/guides/choosing-a-gradient.md +140 -0
  21. qmlkit-0.1.0/docs/guides/evaluation.md +226 -0
  22. qmlkit-0.1.0/docs/guides/extending.md +248 -0
  23. qmlkit-0.1.0/docs/guides/index.md +16 -0
  24. qmlkit-0.1.0/docs/guides/noise.md +218 -0
  25. qmlkit-0.1.0/docs/guides/parameter-shift.md +146 -0
  26. qmlkit-0.1.0/docs/index.md +127 -0
  27. qmlkit-0.1.0/docs/install.md +110 -0
  28. qmlkit-0.1.0/docs/javascripts/mathjax.js +5 -0
  29. qmlkit-0.1.0/docs/llms-full.txt +5469 -0
  30. qmlkit-0.1.0/docs/llms.txt +100 -0
  31. qmlkit-0.1.0/docs/reference/algorithms.md +78 -0
  32. qmlkit-0.1.0/docs/reference/analysis.md +45 -0
  33. qmlkit-0.1.0/docs/reference/ansatz.md +15 -0
  34. qmlkit-0.1.0/docs/reference/core.md +40 -0
  35. qmlkit-0.1.0/docs/reference/encoding.md +30 -0
  36. qmlkit-0.1.0/docs/reference/evaluation.md +34 -0
  37. qmlkit-0.1.0/docs/reference/gradients.md +35 -0
  38. qmlkit-0.1.0/docs/reference/index.md +18 -0
  39. qmlkit-0.1.0/docs/reference/kernels.md +15 -0
  40. qmlkit-0.1.0/docs/reference/nn.md +15 -0
  41. qmlkit-0.1.0/docs/studies/01-imbalanced-classification.md +124 -0
  42. qmlkit-0.1.0/docs/studies/02-quantum-kernels.md +98 -0
  43. qmlkit-0.1.0/docs/studies/03-regression.md +114 -0
  44. qmlkit-0.1.0/docs/studies/04-chemistry.md +114 -0
  45. qmlkit-0.1.0/docs/studies/05-beyond-classification.md +104 -0
  46. qmlkit-0.1.0/docs/studies/06-clinical.md +136 -0
  47. qmlkit-0.1.0/docs/studies/07-images-and-structure.md +130 -0
  48. qmlkit-0.1.0/docs/studies/index.md +44 -0
  49. qmlkit-0.1.0/docs/tutorials/01-first-circuit.md +231 -0
  50. qmlkit-0.1.0/docs/tutorials/02-encoding-data.md +197 -0
  51. qmlkit-0.1.0/docs/tutorials/03-gradients.md +258 -0
  52. qmlkit-0.1.0/docs/tutorials/04-ansatz-design.md +204 -0
  53. qmlkit-0.1.0/docs/tutorials/05-training-torch.md +176 -0
  54. qmlkit-0.1.0/docs/tutorials/06-quantum-kernels.md +230 -0
  55. qmlkit-0.1.0/docs/tutorials/07-reuploading.md +216 -0
  56. qmlkit-0.1.0/docs/tutorials/08-trainability.md +174 -0
  57. qmlkit-0.1.0/docs/tutorials/index.md +67 -0
  58. qmlkit-0.1.0/examples/accelerate_pennylane.py +173 -0
  59. qmlkit-0.1.0/examples/benchmark_pennylane.py +252 -0
  60. qmlkit-0.1.0/examples/compare_pennylane.py +268 -0
  61. qmlkit-0.1.0/examples/credit_data.py +169 -0
  62. qmlkit-0.1.0/examples/credit_risk.py +351 -0
  63. qmlkit-0.1.0/examples/experiments.py +238 -0
  64. qmlkit-0.1.0/examples/head_to_head.py +337 -0
  65. qmlkit-0.1.0/examples/quickstart.py +277 -0
  66. qmlkit-0.1.0/examples/toward_hardware.py +222 -0
  67. qmlkit-0.1.0/mkdocs.yml +143 -0
  68. qmlkit-0.1.0/pyproject.toml +159 -0
  69. qmlkit-0.1.0/scripts/generate_llms_txt.py +287 -0
  70. qmlkit-0.1.0/scripts/verify_install.py +146 -0
  71. qmlkit-0.1.0/src/qmlkit/__init__.py +495 -0
  72. qmlkit-0.1.0/src/qmlkit/_aliases.py +135 -0
  73. qmlkit-0.1.0/src/qmlkit/algorithms/__init__.py +82 -0
  74. qmlkit-0.1.0/src/qmlkit/algorithms/adapt.py +297 -0
  75. qmlkit-0.1.0/src/qmlkit/algorithms/autoencoder.py +206 -0
  76. qmlkit-0.1.0/src/qmlkit/algorithms/chemistry.py +222 -0
  77. qmlkit-0.1.0/src/qmlkit/algorithms/clustering.py +149 -0
  78. qmlkit-0.1.0/src/qmlkit/algorithms/hamiltonians.py +143 -0
  79. qmlkit-0.1.0/src/qmlkit/algorithms/molecule.py +442 -0
  80. qmlkit-0.1.0/src/qmlkit/algorithms/qaoa.py +208 -0
  81. qmlkit-0.1.0/src/qmlkit/algorithms/rl.py +198 -0
  82. qmlkit-0.1.0/src/qmlkit/algorithms/vqe.py +198 -0
  83. qmlkit-0.1.0/src/qmlkit/ansatz/__init__.py +68 -0
  84. qmlkit-0.1.0/src/qmlkit/ansatz/blocks.py +348 -0
  85. qmlkit-0.1.0/src/qmlkit/ansatz/library.py +570 -0
  86. qmlkit-0.1.0/src/qmlkit/ansatz/reupload.py +168 -0
  87. qmlkit-0.1.0/src/qmlkit/baselines.py +604 -0
  88. qmlkit-0.1.0/src/qmlkit/budget.py +234 -0
  89. qmlkit-0.1.0/src/qmlkit/core/__init__.py +1 -0
  90. qmlkit-0.1.0/src/qmlkit/core/backends/__init__.py +22 -0
  91. qmlkit-0.1.0/src/qmlkit/core/backends/_sampling.py +43 -0
  92. qmlkit-0.1.0/src/qmlkit/core/backends/base.py +256 -0
  93. qmlkit-0.1.0/src/qmlkit/core/backends/cirq_backend.py +110 -0
  94. qmlkit-0.1.0/src/qmlkit/core/backends/cirq_density_backend.py +71 -0
  95. qmlkit-0.1.0/src/qmlkit/core/backends/noisy.py +86 -0
  96. qmlkit-0.1.0/src/qmlkit/core/backends/numpy_backend.py +276 -0
  97. qmlkit-0.1.0/src/qmlkit/core/backends/qiskit_aer_backend.py +79 -0
  98. qmlkit-0.1.0/src/qmlkit/core/backends/qiskit_backend.py +104 -0
  99. qmlkit-0.1.0/src/qmlkit/core/backends/registry.py +210 -0
  100. qmlkit-0.1.0/src/qmlkit/core/backends/spinqit_backend.py +233 -0
  101. qmlkit-0.1.0/src/qmlkit/core/backends/torch_backend.py +185 -0
  102. qmlkit-0.1.0/src/qmlkit/core/builder.py +189 -0
  103. qmlkit-0.1.0/src/qmlkit/core/execute.py +193 -0
  104. qmlkit-0.1.0/src/qmlkit/core/gates.py +243 -0
  105. qmlkit-0.1.0/src/qmlkit/core/ir.py +320 -0
  106. qmlkit-0.1.0/src/qmlkit/core/observables.py +269 -0
  107. qmlkit-0.1.0/src/qmlkit/datasets.py +178 -0
  108. qmlkit-0.1.0/src/qmlkit/diagnostics.py +719 -0
  109. qmlkit-0.1.0/src/qmlkit/draw.py +177 -0
  110. qmlkit-0.1.0/src/qmlkit/encoding/__init__.py +63 -0
  111. qmlkit-0.1.0/src/qmlkit/encoding/amplitude.py +178 -0
  112. qmlkit-0.1.0/src/qmlkit/encoding/angle.py +61 -0
  113. qmlkit-0.1.0/src/qmlkit/encoding/feature_maps.py +353 -0
  114. qmlkit-0.1.0/src/qmlkit/encoding/hamiltonian.py +206 -0
  115. qmlkit-0.1.0/src/qmlkit/encoding/pipeline.py +198 -0
  116. qmlkit-0.1.0/src/qmlkit/encoding/scaling.py +139 -0
  117. qmlkit-0.1.0/src/qmlkit/evaluate.py +686 -0
  118. qmlkit-0.1.0/src/qmlkit/fourier.py +124 -0
  119. qmlkit-0.1.0/src/qmlkit/generative.py +406 -0
  120. qmlkit-0.1.0/src/qmlkit/gradients/__init__.py +61 -0
  121. qmlkit-0.1.0/src/qmlkit/gradients/adjoint.py +138 -0
  122. qmlkit-0.1.0/src/qmlkit/gradients/batch.py +275 -0
  123. qmlkit-0.1.0/src/qmlkit/gradients/dispatch.py +247 -0
  124. qmlkit-0.1.0/src/qmlkit/gradients/hadamard.py +108 -0
  125. qmlkit-0.1.0/src/qmlkit/gradients/parameter_shift.py +142 -0
  126. qmlkit-0.1.0/src/qmlkit/gradients/rules.py +151 -0
  127. qmlkit-0.1.0/src/qmlkit/gradients/spsa.py +134 -0
  128. qmlkit-0.1.0/src/qmlkit/imbalance.py +335 -0
  129. qmlkit-0.1.0/src/qmlkit/info.py +153 -0
  130. qmlkit-0.1.0/src/qmlkit/interop.py +778 -0
  131. qmlkit-0.1.0/src/qmlkit/kernels/__init__.py +69 -0
  132. qmlkit-0.1.0/src/qmlkit/kernels/estimators.py +206 -0
  133. qmlkit-0.1.0/src/qmlkit/kernels/matrix.py +439 -0
  134. qmlkit-0.1.0/src/qmlkit/kernels/models.py +315 -0
  135. qmlkit-0.1.0/src/qmlkit/metrics.py +394 -0
  136. qmlkit-0.1.0/src/qmlkit/nn/__init__.py +18 -0
  137. qmlkit-0.1.0/src/qmlkit/nn/advanced.py +254 -0
  138. qmlkit-0.1.0/src/qmlkit/nn/layer.py +343 -0
  139. qmlkit-0.1.0/src/qmlkit/nn/losses.py +124 -0
  140. qmlkit-0.1.0/src/qmlkit/nn/models.py +245 -0
  141. qmlkit-0.1.0/src/qmlkit/optim.py +306 -0
  142. qmlkit-0.1.0/src/qmlkit/provenance.py +271 -0
  143. qmlkit-0.1.0/src/qmlkit/py.typed +0 -0
  144. qmlkit-0.1.0/src/qmlkit/search.py +561 -0
  145. qmlkit-0.1.0/src/qmlkit/shadows.py +117 -0
  146. qmlkit-0.1.0/src/qmlkit/utils/__init__.py +19 -0
  147. qmlkit-0.1.0/src/qmlkit/utils/errors.py +130 -0
  148. qmlkit-0.1.0/src/qmlkit/utils/shots.py +55 -0
  149. qmlkit-0.1.0/tests/test_advanced.py +336 -0
  150. qmlkit-0.1.0/tests/test_agent_api.py +460 -0
  151. qmlkit-0.1.0/tests/test_algorithms.py +450 -0
  152. qmlkit-0.1.0/tests/test_analysis.py +538 -0
  153. qmlkit-0.1.0/tests/test_ansatz.py +305 -0
  154. qmlkit-0.1.0/tests/test_baseline.py +265 -0
  155. qmlkit-0.1.0/tests/test_batch.py +364 -0
  156. qmlkit-0.1.0/tests/test_budget.py +101 -0
  157. qmlkit-0.1.0/tests/test_builder.py +200 -0
  158. qmlkit-0.1.0/tests/test_core.py +202 -0
  159. qmlkit-0.1.0/tests/test_cross_backend.py +216 -0
  160. qmlkit-0.1.0/tests/test_docs.py +84 -0
  161. qmlkit-0.1.0/tests/test_encoding.py +387 -0
  162. qmlkit-0.1.0/tests/test_evaluate.py +245 -0
  163. qmlkit-0.1.0/tests/test_grad_batch.py +318 -0
  164. qmlkit-0.1.0/tests/test_gradient_methods.py +499 -0
  165. qmlkit-0.1.0/tests/test_gradients.py +290 -0
  166. qmlkit-0.1.0/tests/test_imbalance.py +243 -0
  167. qmlkit-0.1.0/tests/test_import.py +532 -0
  168. qmlkit-0.1.0/tests/test_injection.py +172 -0
  169. qmlkit-0.1.0/tests/test_interop.py +226 -0
  170. qmlkit-0.1.0/tests/test_kernels.py +332 -0
  171. qmlkit-0.1.0/tests/test_nn.py +287 -0
  172. qmlkit-0.1.0/tests/test_noisy_backends.py +403 -0
  173. qmlkit-0.1.0/tests/test_observables.py +240 -0
  174. qmlkit-0.1.0/tests/test_pennylane_parity.py +814 -0
  175. qmlkit-0.1.0/tests/test_provenance.py +128 -0
  176. qmlkit-0.1.0/tests/test_search.py +288 -0
  177. 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
@@ -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`.