qudenoise 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 (37) hide show
  1. qudenoise-0.1.0/LICENSE +21 -0
  2. qudenoise-0.1.0/MANIFEST.in +4 -0
  3. qudenoise-0.1.0/PKG-INFO +123 -0
  4. qudenoise-0.1.0/README.md +103 -0
  5. qudenoise-0.1.0/docs/design_notes.md +27 -0
  6. qudenoise-0.1.0/examples/ghz_state.py +15 -0
  7. qudenoise-0.1.0/examples/noisy_bell_pair.py +9 -0
  8. qudenoise-0.1.0/examples/qae_config.yaml +14 -0
  9. qudenoise-0.1.0/examples/train_qae_denoiser.py +34 -0
  10. qudenoise-0.1.0/pyproject.toml +49 -0
  11. qudenoise-0.1.0/setup.cfg +4 -0
  12. qudenoise-0.1.0/src/qudenoise/__init__.py +43 -0
  13. qudenoise-0.1.0/src/qudenoise/backend.py +256 -0
  14. qudenoise-0.1.0/src/qudenoise/circuit.py +362 -0
  15. qudenoise-0.1.0/src/qudenoise/cli.py +241 -0
  16. qudenoise-0.1.0/src/qudenoise/gates.py +191 -0
  17. qudenoise-0.1.0/src/qudenoise/mps.py +451 -0
  18. qudenoise-0.1.0/src/qudenoise/noise.py +312 -0
  19. qudenoise-0.1.0/src/qudenoise/observables.py +147 -0
  20. qudenoise-0.1.0/src/qudenoise/py.typed +0 -0
  21. qudenoise-0.1.0/src/qudenoise/qae.py +464 -0
  22. qudenoise-0.1.0/src/qudenoise/reference.py +107 -0
  23. qudenoise-0.1.0/src/qudenoise/simulator.py +344 -0
  24. qudenoise-0.1.0/src/qudenoise/utils.py +108 -0
  25. qudenoise-0.1.0/src/qudenoise.egg-info/PKG-INFO +123 -0
  26. qudenoise-0.1.0/src/qudenoise.egg-info/SOURCES.txt +35 -0
  27. qudenoise-0.1.0/src/qudenoise.egg-info/dependency_links.txt +1 -0
  28. qudenoise-0.1.0/src/qudenoise.egg-info/entry_points.txt +2 -0
  29. qudenoise-0.1.0/src/qudenoise.egg-info/requires.txt +15 -0
  30. qudenoise-0.1.0/src/qudenoise.egg-info/top_level.txt +1 -0
  31. qudenoise-0.1.0/tests/conftest.py +35 -0
  32. qudenoise-0.1.0/tests/test_backend.py +180 -0
  33. qudenoise-0.1.0/tests/test_cli.py +176 -0
  34. qudenoise-0.1.0/tests/test_gates.py +107 -0
  35. qudenoise-0.1.0/tests/test_mps_vs_dense.py +397 -0
  36. qudenoise-0.1.0/tests/test_noise_channels.py +375 -0
  37. qudenoise-0.1.0/tests/test_qae_training.py +263 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 QuDenoise contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,4 @@
1
+ include README.md LICENSE pyproject.toml
2
+ recursive-include tests *.py
3
+ recursive-include examples *.py *.json *.yaml
4
+ recursive-include docs *.md
@@ -0,0 +1,123 @@
1
+ Metadata-Version: 2.1
2
+ Name: qudenoise
3
+ Version: 0.1.0
4
+ Summary: Pure-Python MPS quantum circuit simulator with Kraus-channel noise trajectories and a quantum-autoencoder denoiser
5
+ Author: QuDenoise contributors
6
+ License: MIT
7
+ Keywords: quantum,simulator,matrix product state,tensor network,noise,autoencoder
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3 :: Only
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: Topic :: Scientific/Engineering :: Physics
13
+ Requires-Python: >=3.9
14
+ Description-Content-Type: text/markdown
15
+ Provides-Extra: autodiff
16
+ Provides-Extra: gpu
17
+ Provides-Extra: test
18
+ Provides-Extra: viz
19
+ License-File: LICENSE
20
+
21
+ # QuDenoise
22
+
23
+ A from-scratch, pure-Python **Matrix Product State (MPS) quantum circuit simulator** with
24
+ **Kraus-channel noise (quantum trajectories)** and a **quantum-autoencoder (QAE) denoiser**.
25
+ Runs on NumPy everywhere; optionally runs every tensor operation on a GPU through CuPy.
26
+
27
+ ## Install
28
+
29
+ ```bash
30
+ pip install qudenoise # NumPy/SciPy only (CPU)
31
+ pip install "qudenoise[viz]" # + matplotlib for plotting examples
32
+ ```
33
+
34
+ ### GPU (optional)
35
+
36
+ CuPy publishes one wheel per CUDA version, so QuDenoise does **not** pin a CuPy variant. Install the one
37
+ matching your toolkit yourself, e.g.
38
+
39
+ ```bash
40
+ pip install cupy-cuda12x # CUDA 12.x
41
+ pip install cupy-cuda11x # CUDA 11.x
42
+ ```
43
+
44
+ (`pip install "qudenoise[gpu]"` pulls the generic `cupy` source package, which needs a local CUDA toolchain.)
45
+ CuPy is imported lazily: without it QuDenoise imports and runs with no errors or warnings. With
46
+ `device="auto"` (default) the GPU is used when available and this is logged once
47
+ (`QuDenoise: running on GPU (CuPy)` / `running on CPU (NumPy)`); force a backend with
48
+ `Simulator(device="cupy")` or `device="numpy"`.
49
+
50
+ ## Quick start
51
+
52
+ ```python
53
+ from qudenoise import Circuit, Simulator
54
+
55
+ c = Circuit(20).h(0)
56
+ for i in range(19):
57
+ c.cnot(i, i + 1)
58
+
59
+ sim = Simulator(bond_dim=8) # chi cap; also truncation_threshold=...
60
+ state = sim.run(c) # -> MPS
61
+ print(state.bond_dimensions(), state.fidelity_estimate)
62
+ samples, report = sim.sample(c, shots=1000, seed=1)
63
+ ```
64
+
65
+ Non-adjacent two-qubit gates are routed with SWAP chains automatically; every insertion is logged
66
+ (logger `qudenoise`, INFO) and recorded in `sim.last_report.routing_log`.
67
+
68
+ ### Noise
69
+
70
+ ```python
71
+ c = Circuit(2).h(0).cnot(0, 1).depolarizing(0, 0.05).amplitude_damping(1, 0.1)
72
+ res = Simulator().run_observable(c, lambda m: observables.expectation(m, {0: gates.Z(), 1: gates.Z()}).real,
73
+ n_trajectories=2000, seed=0)
74
+ print(res.mean, "+/-", res.sem)
75
+ ```
76
+
77
+ Channels: `depolarizing` (1q/2q), `amplitude_damping`, `phase_damping`, `bit_flip`, `phase_flip`, or any
78
+ `KrausChannel`. Trajectories are seeded per index (`spawn_seeds`), so results are identical for any worker
79
+ count. CPU runs with 8+ trajectories use a process pool; GPU runs are sequential in-process.
80
+
81
+ ### Quantum autoencoder
82
+
83
+ ```python
84
+ from qudenoise import QAE
85
+ qae = QAE(n_qubits=4, n_latent_qubits=2, ansatz_depth=2, seed=1)
86
+ qae.fit(training_states, epochs=60, lr=0.1) # MPS / Circuit / dense vectors
87
+ clean_estimate = qae.denoise(noisy_state)
88
+ ```
89
+
90
+ Cost = 1 - mean probability of the trash qubits being `|0..0>`, computed directly from the MPS. Gradients
91
+ use the parameter-shift rule through `qudenoise.qae.compute_gradient`; pass `gradient_fn=` to plug in a JAX
92
+ (`[autodiff]`) backend without changing the API.
93
+
94
+ ### CLI
95
+
96
+ ```bash
97
+ qudenoise run circuit.json --qubits 20 --bond-dim 32 --shots 1000 [--device auto|numpy|cupy] [-o out.json]
98
+ qudenoise train-qae config.yaml
99
+ ```
100
+
101
+ Circuit JSON: `{"n_qubits": N, "ops": [{"gate": "h", "qubits": [0]}, {"gate": "rz", "qubits": [1], "params": [0.3]},
102
+ {"noise": "depolarizing", "qubits": [0], "param": 0.01}]}`. A `.py` file defining `circuit` or
103
+ `build_circuit(n)` also works (executed as ordinary Python - only run files you trust). See
104
+ `examples/qae_config.yaml`.
105
+
106
+ ## Conventions and limits
107
+
108
+ * Big-endian qubit order (`|q0 q1 ...>`); rotations are `exp(-i theta P / 2)`.
109
+ * `MPS.to_dense()` refuses above 24 qubits unless forced. `qudenoise.reference` (dense/density-matrix
110
+ simulators) exists to validate the MPS code in tests; it is not a supported product feature.
111
+ * With truncation, `state.truncation_error`, `state.fidelity_estimate` (product of `1 - eps_k`) and
112
+ `state.fidelity_lower_bound` track accuracy loss.
113
+
114
+ ## Development
115
+
116
+ ```bash
117
+ pip install -e ".[test]" && pytest
118
+ ```
119
+
120
+ Tests run on NumPy and, when CuPy + a GPU are present, are repeated on CuPy (backend-parity check).
121
+ See `docs/design_notes.md`.
122
+
123
+ MIT licensed.
@@ -0,0 +1,103 @@
1
+ # QuDenoise
2
+
3
+ A from-scratch, pure-Python **Matrix Product State (MPS) quantum circuit simulator** with
4
+ **Kraus-channel noise (quantum trajectories)** and a **quantum-autoencoder (QAE) denoiser**.
5
+ Runs on NumPy everywhere; optionally runs every tensor operation on a GPU through CuPy.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pip install qudenoise # NumPy/SciPy only (CPU)
11
+ pip install "qudenoise[viz]" # + matplotlib for plotting examples
12
+ ```
13
+
14
+ ### GPU (optional)
15
+
16
+ CuPy publishes one wheel per CUDA version, so QuDenoise does **not** pin a CuPy variant. Install the one
17
+ matching your toolkit yourself, e.g.
18
+
19
+ ```bash
20
+ pip install cupy-cuda12x # CUDA 12.x
21
+ pip install cupy-cuda11x # CUDA 11.x
22
+ ```
23
+
24
+ (`pip install "qudenoise[gpu]"` pulls the generic `cupy` source package, which needs a local CUDA toolchain.)
25
+ CuPy is imported lazily: without it QuDenoise imports and runs with no errors or warnings. With
26
+ `device="auto"` (default) the GPU is used when available and this is logged once
27
+ (`QuDenoise: running on GPU (CuPy)` / `running on CPU (NumPy)`); force a backend with
28
+ `Simulator(device="cupy")` or `device="numpy"`.
29
+
30
+ ## Quick start
31
+
32
+ ```python
33
+ from qudenoise import Circuit, Simulator
34
+
35
+ c = Circuit(20).h(0)
36
+ for i in range(19):
37
+ c.cnot(i, i + 1)
38
+
39
+ sim = Simulator(bond_dim=8) # chi cap; also truncation_threshold=...
40
+ state = sim.run(c) # -> MPS
41
+ print(state.bond_dimensions(), state.fidelity_estimate)
42
+ samples, report = sim.sample(c, shots=1000, seed=1)
43
+ ```
44
+
45
+ Non-adjacent two-qubit gates are routed with SWAP chains automatically; every insertion is logged
46
+ (logger `qudenoise`, INFO) and recorded in `sim.last_report.routing_log`.
47
+
48
+ ### Noise
49
+
50
+ ```python
51
+ c = Circuit(2).h(0).cnot(0, 1).depolarizing(0, 0.05).amplitude_damping(1, 0.1)
52
+ res = Simulator().run_observable(c, lambda m: observables.expectation(m, {0: gates.Z(), 1: gates.Z()}).real,
53
+ n_trajectories=2000, seed=0)
54
+ print(res.mean, "+/-", res.sem)
55
+ ```
56
+
57
+ Channels: `depolarizing` (1q/2q), `amplitude_damping`, `phase_damping`, `bit_flip`, `phase_flip`, or any
58
+ `KrausChannel`. Trajectories are seeded per index (`spawn_seeds`), so results are identical for any worker
59
+ count. CPU runs with 8+ trajectories use a process pool; GPU runs are sequential in-process.
60
+
61
+ ### Quantum autoencoder
62
+
63
+ ```python
64
+ from qudenoise import QAE
65
+ qae = QAE(n_qubits=4, n_latent_qubits=2, ansatz_depth=2, seed=1)
66
+ qae.fit(training_states, epochs=60, lr=0.1) # MPS / Circuit / dense vectors
67
+ clean_estimate = qae.denoise(noisy_state)
68
+ ```
69
+
70
+ Cost = 1 - mean probability of the trash qubits being `|0..0>`, computed directly from the MPS. Gradients
71
+ use the parameter-shift rule through `qudenoise.qae.compute_gradient`; pass `gradient_fn=` to plug in a JAX
72
+ (`[autodiff]`) backend without changing the API.
73
+
74
+ ### CLI
75
+
76
+ ```bash
77
+ qudenoise run circuit.json --qubits 20 --bond-dim 32 --shots 1000 [--device auto|numpy|cupy] [-o out.json]
78
+ qudenoise train-qae config.yaml
79
+ ```
80
+
81
+ Circuit JSON: `{"n_qubits": N, "ops": [{"gate": "h", "qubits": [0]}, {"gate": "rz", "qubits": [1], "params": [0.3]},
82
+ {"noise": "depolarizing", "qubits": [0], "param": 0.01}]}`. A `.py` file defining `circuit` or
83
+ `build_circuit(n)` also works (executed as ordinary Python - only run files you trust). See
84
+ `examples/qae_config.yaml`.
85
+
86
+ ## Conventions and limits
87
+
88
+ * Big-endian qubit order (`|q0 q1 ...>`); rotations are `exp(-i theta P / 2)`.
89
+ * `MPS.to_dense()` refuses above 24 qubits unless forced. `qudenoise.reference` (dense/density-matrix
90
+ simulators) exists to validate the MPS code in tests; it is not a supported product feature.
91
+ * With truncation, `state.truncation_error`, `state.fidelity_estimate` (product of `1 - eps_k`) and
92
+ `state.fidelity_lower_bound` track accuracy loss.
93
+
94
+ ## Development
95
+
96
+ ```bash
97
+ pip install -e ".[test]" && pytest
98
+ ```
99
+
100
+ Tests run on NumPy and, when CuPy + a GPU are present, are repeated on CuPy (backend-parity check).
101
+ See `docs/design_notes.md`.
102
+
103
+ MIT licensed.
@@ -0,0 +1,27 @@
1
+ # Design notes
2
+
3
+ **Backend.** `backend.py` is the only place (besides `reference.py`) that imports NumPy directly. All modules
4
+ call `get_backend()` at use time, never cache arrays across backends, and take RNGs explicitly
5
+ (`get_rng(seed)`, `spawn_seeds`). CuPy is imported lazily; missing CuPy never warns.
6
+
7
+ **MPS.** Site tensors `(D_left, 2, D_right)`, complex128, tracked orthogonality centre. Two-qubit gates act on
8
+ adjacent sites via two-site tensor + SVD (`utils.svd_truncate`, fixed chi and/or discarded-weight threshold).
9
+ Each truncation records `eps_k` (discarded weight); `fidelity_estimate = prod(1 - eps_k)` and the rigorous
10
+ `1 - (sum sqrt(eps_k))^2` bound are exposed.
11
+
12
+ **Routing.** Non-adjacent 2q gates are compiled into SWAP chains (out and back) and each insertion is logged,
13
+ never silent.
14
+
15
+ **Noise.** Quantum trajectories: per channel, branch probabilities come from the local norm after each Kraus
16
+ operator; Pauli-type channels (scaled unitaries) take a state-independent fast path. Trajectory `i` uses seed
17
+ `spawn_seeds(seed, n)[i]`, so parallel and serial runs agree exactly. GPU runs are sequential (CUDA contexts
18
+ do not survive fork the way NumPy workers do).
19
+
20
+ **QAE.** Ansatz of RY/RZ layers plus generic `RXX*RYY*RZZ` entanglers on a brick pattern (adjacent only).
21
+ Every parameter feeds one `exp(-i t P/2)` gate, so the two-term parameter-shift rule is exact without
22
+ truncation. Gradients live behind `qae.compute_gradient` / `gradient_fn`. Latent = leading qubits, trash =
23
+ trailing qubits so projection and extension are cheap MPS boundary operations.
24
+
25
+ **Known limitations.** Parameter-shift cost scales as 2 x n_params x n_states simulations per epoch (pure
26
+ Python); the QAE targets small registers. Trajectories give unbiased averages but need many samples for
27
+ small effects. Learning may hit local minima on entangled families; use `restarts=`.
@@ -0,0 +1,15 @@
1
+ """60-qubit GHZ state with bond dimension 2 - far beyond dense simulation."""
2
+ from qudenoise import Circuit, Simulator
3
+
4
+ n = 60
5
+ c = Circuit(n).h(0)
6
+ for i in range(n - 1):
7
+ c.cnot(i, i + 1)
8
+
9
+ sim = Simulator(bond_dim=4)
10
+ state = sim.run(c)
11
+ samples, _ = sim.sample(c, shots=8, seed=1)
12
+ print("max bond dimension:", state.max_bond_dimension)
13
+ print("samples (all-equal rows expected):")
14
+ for row in samples:
15
+ print("".join(str(int(b)) for b in row))
@@ -0,0 +1,9 @@
1
+ """Depolarizing noise on a Bell pair: trajectory average vs exact density matrix."""
2
+ from qudenoise import Circuit, Simulator, gates, observables
3
+
4
+ p = 0.1
5
+ c = Circuit(2).h(0).cnot(0, 1).depolarizing(0, p).depolarizing(1, p)
6
+ zz = lambda m: observables.expectation(m, {0: gates.Z(), 1: gates.Z()}).real
7
+ res = Simulator(device="numpy").run_observable(c, zz, n_trajectories=2000, seed=0)
8
+ print(f"<ZZ> trajectories: {res.mean:.4f} +/- {res.sem:.4f}")
9
+ print(f"<ZZ> exact : {(1 - 4 * p / 3) ** 2:.4f}") # each qubit's Z shrinks by 1 - 4p/3
@@ -0,0 +1,14 @@
1
+ # qudenoise train-qae examples/qae_config.yaml
2
+ n_qubits: 3
3
+ n_latent_qubits: 2
4
+ ansatz_depth: 1
5
+ seed: 1
6
+ output: qae_model.json
7
+ fit: {epochs: 40, lr: 0.2, seed: 2}
8
+ training_states:
9
+ - {n_qubits: 3, ops: [{gate: ry, qubits: [0], params: [0.3]}, {gate: ry, qubits: [1], params: [1.1]}, {gate: ry, qubits: [2], params: [0.9]}]}
10
+ - {n_qubits: 3, ops: [{gate: ry, qubits: [0], params: [1.5]}, {gate: ry, qubits: [1], params: [0.4]}, {gate: ry, qubits: [2], params: [0.9]}]}
11
+ - {n_qubits: 3, ops: [{gate: ry, qubits: [0], params: [2.2]}, {gate: ry, qubits: [1], params: [2.0]}, {gate: ry, qubits: [2], params: [0.9]}]}
12
+ test_pairs:
13
+ - clean: {n_qubits: 3, ops: [{gate: ry, qubits: [0], params: [0.8]}, {gate: ry, qubits: [1], params: [1.7]}, {gate: ry, qubits: [2], params: [0.9]}]}
14
+ noisy: {n_qubits: 3, ops: [{gate: ry, qubits: [0], params: [0.8]}, {gate: ry, qubits: [1], params: [1.7]}, {gate: ry, qubits: [2], params: [0.9]}, {gate: rx, qubits: [2], params: [0.5]}]}
@@ -0,0 +1,34 @@
1
+ """Train a QAE on a family of product states and use it to denoise perturbed states."""
2
+ import math
3
+ import random
4
+
5
+ from qudenoise import Circuit, MPS, QAE
6
+ from qudenoise.backend import get_rng
7
+ from qudenoise.simulator import execute_ops
8
+
9
+
10
+ def family(seed, n=4, k=2):
11
+ r = random.Random(seed)
12
+ c = Circuit(n)
13
+ for q in range(k):
14
+ c.ry(q, r.uniform(0, math.pi)).rz(q, r.uniform(0, 2 * math.pi))
15
+ for q in range(k, n):
16
+ c.ry(q, 0.9).rz(q, 0.4)
17
+ return c
18
+
19
+
20
+ def state(c):
21
+ return execute_ops(c.compile(), MPS(c.n_qubits, device="numpy"), get_rng(0))
22
+
23
+
24
+ qae = QAE(4, 2, ansatz_depth=2, seed=1)
25
+ res = qae.fit([family(s) for s in range(6)], epochs=60, lr=0.1, seed=3)
26
+ print(f"trash cost: {res.initial_cost:.3f} -> {res.final_cost:.5f}")
27
+
28
+ for s in range(3):
29
+ clean = family(100 + s)
30
+ noisy = clean.copy()
31
+ for q in range(4):
32
+ noisy.rx(q, 0.25 * (-1) ** q)
33
+ r = qae.denoise_fidelity(state(noisy), state(clean))
34
+ print(f"fidelity {r['before']:.4f} -> {r['after']:.4f}")
@@ -0,0 +1,49 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "qudenoise"
7
+ version = "0.1.0" # keep in sync with src/qudenoise/__init__.py::__version__
8
+ description = "Pure-Python MPS quantum circuit simulator with Kraus-channel noise trajectories and a quantum-autoencoder denoiser"
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.9"
12
+ authors = [{ name = "QuDenoise contributors" }]
13
+ keywords = ["quantum", "simulator", "matrix product state", "tensor network", "noise", "autoencoder"]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3 :: Only",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Intended Audience :: Science/Research",
19
+ "Topic :: Scientific/Engineering :: Physics",
20
+ ]
21
+ dependencies = [
22
+ "numpy>=1.22",
23
+ "scipy>=1.8",
24
+ "PyYAML>=5.4", # train-qae config files
25
+ ]
26
+
27
+ [project.optional-dependencies]
28
+ # CuPy ships one wheel per CUDA version (cupy-cuda12x, cupy-cuda11x, ...), so `[gpu]` pulls the
29
+ # generic source package name only as a convenience; on most machines install the matching wheel
30
+ # yourself (see README) instead of using this extra.
31
+ gpu = ["cupy"]
32
+ autodiff = ["jax"]
33
+ viz = ["matplotlib>=3.5"]
34
+ test = ["pytest>=7"]
35
+
36
+ [project.scripts]
37
+ qudenoise = "qudenoise.cli:main"
38
+
39
+ [tool.setuptools]
40
+ package-dir = { "" = "src" }
41
+
42
+ [tool.setuptools.packages.find]
43
+ where = ["src"]
44
+
45
+ [tool.setuptools.package-data]
46
+ qudenoise = ["py.typed"]
47
+
48
+ [tool.pytest.ini_options]
49
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,43 @@
1
+ """QuDenoise: pure-Python MPS quantum circuit simulator with noise and a QAE denoiser."""
2
+ from __future__ import annotations
3
+
4
+ __version__ = "0.1.0"
5
+
6
+ from . import backend, gates, noise, observables
7
+ from .backend import (
8
+ get_backend,
9
+ get_rng,
10
+ gpu_available,
11
+ set_backend,
12
+ to_backend,
13
+ to_numpy,
14
+ use_backend,
15
+ )
16
+ from .circuit import Circuit, Op
17
+ from .mps import MPS
18
+ from .noise import KrausChannel
19
+ from .qae import QAE
20
+ from .simulator import ObservableResult, SimulationReport, Simulator
21
+
22
+ __all__ = [
23
+ "__version__",
24
+ "backend",
25
+ "gates",
26
+ "noise",
27
+ "observables",
28
+ "get_backend",
29
+ "get_rng",
30
+ "gpu_available",
31
+ "set_backend",
32
+ "to_backend",
33
+ "to_numpy",
34
+ "use_backend",
35
+ "Circuit",
36
+ "Op",
37
+ "MPS",
38
+ "KrausChannel",
39
+ "QAE",
40
+ "Simulator",
41
+ "SimulationReport",
42
+ "ObservableResult",
43
+ ]