qmlkit 0.1.0__py3-none-any.whl
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/__init__.py +495 -0
- qmlkit/_aliases.py +135 -0
- qmlkit/algorithms/__init__.py +82 -0
- qmlkit/algorithms/adapt.py +297 -0
- qmlkit/algorithms/autoencoder.py +206 -0
- qmlkit/algorithms/chemistry.py +222 -0
- qmlkit/algorithms/clustering.py +149 -0
- qmlkit/algorithms/hamiltonians.py +143 -0
- qmlkit/algorithms/molecule.py +442 -0
- qmlkit/algorithms/qaoa.py +208 -0
- qmlkit/algorithms/rl.py +198 -0
- qmlkit/algorithms/vqe.py +198 -0
- qmlkit/ansatz/__init__.py +68 -0
- qmlkit/ansatz/blocks.py +348 -0
- qmlkit/ansatz/library.py +570 -0
- qmlkit/ansatz/reupload.py +168 -0
- qmlkit/baselines.py +604 -0
- qmlkit/budget.py +234 -0
- qmlkit/core/__init__.py +1 -0
- qmlkit/core/backends/__init__.py +22 -0
- qmlkit/core/backends/_sampling.py +43 -0
- qmlkit/core/backends/base.py +256 -0
- qmlkit/core/backends/cirq_backend.py +110 -0
- qmlkit/core/backends/cirq_density_backend.py +71 -0
- qmlkit/core/backends/noisy.py +86 -0
- qmlkit/core/backends/numpy_backend.py +276 -0
- qmlkit/core/backends/qiskit_aer_backend.py +79 -0
- qmlkit/core/backends/qiskit_backend.py +104 -0
- qmlkit/core/backends/registry.py +210 -0
- qmlkit/core/backends/spinqit_backend.py +233 -0
- qmlkit/core/backends/torch_backend.py +185 -0
- qmlkit/core/builder.py +189 -0
- qmlkit/core/execute.py +193 -0
- qmlkit/core/gates.py +243 -0
- qmlkit/core/ir.py +320 -0
- qmlkit/core/observables.py +269 -0
- qmlkit/datasets.py +178 -0
- qmlkit/diagnostics.py +719 -0
- qmlkit/draw.py +177 -0
- qmlkit/encoding/__init__.py +63 -0
- qmlkit/encoding/amplitude.py +178 -0
- qmlkit/encoding/angle.py +61 -0
- qmlkit/encoding/feature_maps.py +353 -0
- qmlkit/encoding/hamiltonian.py +206 -0
- qmlkit/encoding/pipeline.py +198 -0
- qmlkit/encoding/scaling.py +139 -0
- qmlkit/evaluate.py +686 -0
- qmlkit/fourier.py +124 -0
- qmlkit/generative.py +406 -0
- qmlkit/gradients/__init__.py +61 -0
- qmlkit/gradients/adjoint.py +138 -0
- qmlkit/gradients/batch.py +275 -0
- qmlkit/gradients/dispatch.py +247 -0
- qmlkit/gradients/hadamard.py +108 -0
- qmlkit/gradients/parameter_shift.py +142 -0
- qmlkit/gradients/rules.py +151 -0
- qmlkit/gradients/spsa.py +134 -0
- qmlkit/imbalance.py +335 -0
- qmlkit/info.py +153 -0
- qmlkit/interop.py +778 -0
- qmlkit/kernels/__init__.py +69 -0
- qmlkit/kernels/estimators.py +206 -0
- qmlkit/kernels/matrix.py +439 -0
- qmlkit/kernels/models.py +315 -0
- qmlkit/metrics.py +394 -0
- qmlkit/nn/__init__.py +18 -0
- qmlkit/nn/advanced.py +254 -0
- qmlkit/nn/layer.py +343 -0
- qmlkit/nn/losses.py +124 -0
- qmlkit/nn/models.py +245 -0
- qmlkit/optim.py +306 -0
- qmlkit/provenance.py +271 -0
- qmlkit/py.typed +0 -0
- qmlkit/search.py +561 -0
- qmlkit/shadows.py +117 -0
- qmlkit/utils/__init__.py +19 -0
- qmlkit/utils/errors.py +130 -0
- qmlkit/utils/shots.py +55 -0
- qmlkit-0.1.0.dist-info/METADATA +745 -0
- qmlkit-0.1.0.dist-info/RECORD +83 -0
- qmlkit-0.1.0.dist-info/WHEEL +4 -0
- qmlkit-0.1.0.dist-info/licenses/LICENSE +202 -0
- qmlkit-0.1.0.dist-info/licenses/NOTICE +4 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
r"""Data re-uploading, as a pattern rather than a fixed structure.
|
|
2
|
+
|
|
3
|
+
Re-uploading is *any* interleaving of an encoding with a trainable block. The
|
|
4
|
+
encoding can be any feature map, the trainable block any ansatz block, and the
|
|
5
|
+
order, depth and sharing are all free. Treating it as one hardcoded class is a
|
|
6
|
+
category error — so it is not one here.
|
|
7
|
+
|
|
8
|
+
:func:`reupload` is a convenience over that freedom, not a replacement for it:
|
|
9
|
+
|
|
10
|
+
reupload(fmap, n_layers=3) # S W S W S W
|
|
11
|
+
reupload(fmap, n_layers=3, order="WS") # W S W S W S
|
|
12
|
+
reupload(fmap, n_layers=3, block=RotationLayer("ry") + EntanglerLayer("cz", "ring"))
|
|
13
|
+
reupload(fmap, n_layers=3, share_weights=True) # one tied block, reused
|
|
14
|
+
|
|
15
|
+
Anything it cannot express, compose directly — that is the same vocabulary:
|
|
16
|
+
|
|
17
|
+
Ansatz(n, EncodingLayer(zz) + RotationLayer("ry") + EncodingLayer(angle),
|
|
18
|
+
n_inputs=...)
|
|
19
|
+
|
|
20
|
+
**Frequencies.** ``L`` uploads reach frequencies ``0..L`` only when the trainable
|
|
21
|
+
block does not commute with the encoding rotation; if it does, the uploads merge
|
|
22
|
+
into a single rotation. :func:`reupload` checks this and warns.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import warnings
|
|
28
|
+
from collections.abc import Sequence
|
|
29
|
+
from typing import Any
|
|
30
|
+
|
|
31
|
+
import numpy.typing as npt
|
|
32
|
+
from numpy.typing import ArrayLike
|
|
33
|
+
|
|
34
|
+
from qmlkit.ansatz.blocks import (
|
|
35
|
+
Block,
|
|
36
|
+
EncodingLayer,
|
|
37
|
+
EntanglerLayer,
|
|
38
|
+
RotationLayer,
|
|
39
|
+
repeat,
|
|
40
|
+
share,
|
|
41
|
+
)
|
|
42
|
+
from qmlkit.ansatz.library import Ansatz
|
|
43
|
+
from qmlkit.utils.errors import unknown
|
|
44
|
+
|
|
45
|
+
__all__ = ["reupload", "ReuploadModel"]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _commutes_with_encoding(block: Block, feature_map: object) -> bool:
|
|
49
|
+
"""True if every trainable rotation shares the encoding's generator.
|
|
50
|
+
|
|
51
|
+
``Ry(x) Ry(t1) Ry(x) Ry(t2) = Ry(2x + t1 + t2)`` — the uploads collapse into one
|
|
52
|
+
rotation, the model reaches a single frequency, and the weights become a phase.
|
|
53
|
+
"""
|
|
54
|
+
encoding = getattr(feature_map, "rotation", None)
|
|
55
|
+
if encoding is None:
|
|
56
|
+
return False # a multi-gate map (ZZ, Pauli) never fully commutes
|
|
57
|
+
gates: set[str] = set()
|
|
58
|
+
|
|
59
|
+
def walk(b: Block) -> None:
|
|
60
|
+
if isinstance(b, RotationLayer):
|
|
61
|
+
gates.update(b.gates)
|
|
62
|
+
for attr in ("blocks", "block"):
|
|
63
|
+
child = getattr(b, attr, None)
|
|
64
|
+
if isinstance(child, Block):
|
|
65
|
+
walk(child)
|
|
66
|
+
elif isinstance(child, tuple):
|
|
67
|
+
for c in child:
|
|
68
|
+
walk(c)
|
|
69
|
+
|
|
70
|
+
walk(block)
|
|
71
|
+
return bool(gates) and gates <= {encoding}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def reupload(
|
|
75
|
+
feature_map: object,
|
|
76
|
+
n_layers: int = 3,
|
|
77
|
+
block: Block | None = None,
|
|
78
|
+
order: str = "SW",
|
|
79
|
+
entangler: str | None = "cx",
|
|
80
|
+
pattern: str = "chain",
|
|
81
|
+
rotations: Sequence[str] = ("rz", "ry", "rz"),
|
|
82
|
+
share_weights: bool = False,
|
|
83
|
+
name: str = "reupload",
|
|
84
|
+
) -> Ansatz:
|
|
85
|
+
"""Build a re-uploading ansatz from any feature map and any trainable block.
|
|
86
|
+
|
|
87
|
+
Parameters
|
|
88
|
+
----------
|
|
89
|
+
feature_map
|
|
90
|
+
Any :class:`~qmlkit.encoding.feature_maps.FeatureMap`.
|
|
91
|
+
block
|
|
92
|
+
The trainable block. Defaults to a rotation layer plus an entangler.
|
|
93
|
+
order
|
|
94
|
+
``"SW"`` encodes then varies; ``"WS"`` varies then encodes. The difference
|
|
95
|
+
is real: ``"WS"`` lets the model transform the state before the first
|
|
96
|
+
upload, ``"SW"`` does not.
|
|
97
|
+
share_weights
|
|
98
|
+
Tie one trainable block across every layer — far fewer parameters, and the
|
|
99
|
+
gradient sums over occurrences.
|
|
100
|
+
"""
|
|
101
|
+
if n_layers < 1:
|
|
102
|
+
raise ValueError("n_layers must be at least 1")
|
|
103
|
+
if order not in ("SW", "WS"):
|
|
104
|
+
raise unknown(
|
|
105
|
+
"order",
|
|
106
|
+
order,
|
|
107
|
+
("SW", "WS"),
|
|
108
|
+
hint="'SW' encodes then varies; 'WS' varies before the first upload.",
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
n_qubits = int(feature_map.n_qubits) # type: ignore[attr-defined]
|
|
112
|
+
n_inputs = int(feature_map.n_angles) # type: ignore[attr-defined]
|
|
113
|
+
|
|
114
|
+
if block is None:
|
|
115
|
+
block = RotationLayer(rotations)
|
|
116
|
+
if entangler and n_qubits > 1:
|
|
117
|
+
block = block + EntanglerLayer(entangler, pattern)
|
|
118
|
+
|
|
119
|
+
if _commutes_with_encoding(block, feature_map):
|
|
120
|
+
warnings.warn(
|
|
121
|
+
f"the trainable block only uses {rotations}, which commutes with the "
|
|
122
|
+
f"encoding rotation: the uploads collapse into a single rotation, so the "
|
|
123
|
+
f"model reaches one frequency instead of 0..{n_layers} and its weights do "
|
|
124
|
+
'nothing beyond a phase. Use a non-commuting block such as ("rz", "ry", "rz").',
|
|
125
|
+
UserWarning,
|
|
126
|
+
stacklevel=2,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
encoding = EncodingLayer(feature_map)
|
|
130
|
+
layer = (encoding + block) if order == "SW" else (block + encoding)
|
|
131
|
+
body = share(n_layers, layer) if share_weights else repeat(n_layers, layer)
|
|
132
|
+
return ReuploadModel(n_qubits, body, name, n_inputs, feature_map, n_layers)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class ReuploadModel(Ansatz):
|
|
136
|
+
"""An :class:`Ansatz` that also knows its encoding and upload count."""
|
|
137
|
+
|
|
138
|
+
def __init__(
|
|
139
|
+
self,
|
|
140
|
+
n_qubits: int,
|
|
141
|
+
block: Block,
|
|
142
|
+
name: str,
|
|
143
|
+
n_inputs: int,
|
|
144
|
+
feature_map: object,
|
|
145
|
+
n_uploads: int,
|
|
146
|
+
) -> None:
|
|
147
|
+
super().__init__(n_qubits, block, name, n_inputs)
|
|
148
|
+
self.feature_map = feature_map
|
|
149
|
+
self.n_uploads = n_uploads
|
|
150
|
+
|
|
151
|
+
@property
|
|
152
|
+
def n_frequencies(self) -> int:
|
|
153
|
+
"""Reachable frequencies ``0..L``, so ``L + 1`` of them."""
|
|
154
|
+
return self.n_uploads + 1
|
|
155
|
+
|
|
156
|
+
def angles(self, x: ArrayLike) -> npt.NDArray[Any]:
|
|
157
|
+
"""The encoding angles for ``x`` — the first ``n_inputs`` parameters."""
|
|
158
|
+
return self.feature_map.angles(x) # type: ignore[attr-defined]
|
|
159
|
+
|
|
160
|
+
def angle_jacobian(self, x: ArrayLike) -> npt.NDArray[Any]:
|
|
161
|
+
"""``d(angle)/d(feature)``, for the chain rule down to the data."""
|
|
162
|
+
return self.feature_map.angle_jacobian(x) # type: ignore[attr-defined]
|
|
163
|
+
|
|
164
|
+
def __repr__(self) -> str:
|
|
165
|
+
return (
|
|
166
|
+
f"ReuploadModel({self.name!r}, n_qubits={self.n_qubits}, "
|
|
167
|
+
f"uploads={self.n_uploads}, inputs={self.n_inputs}, weights={self.n_weights})"
|
|
168
|
+
)
|