pyqit 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.
- pyqit/__init__.py +19 -0
- pyqit/ansatzes/__init__.py +18 -0
- pyqit/ansatzes/base.py +60 -0
- pyqit/ansatzes/basic_entangler.py +46 -0
- pyqit/ansatzes/cnot_ladder.py +45 -0
- pyqit/ansatzes/hardware_efficient.py +101 -0
- pyqit/ansatzes/sel.py +72 -0
- pyqit/ansatzes/simplified_two_design.py +59 -0
- pyqit/base/__init__.py +4 -0
- pyqit/base/_tags.py +574 -0
- pyqit/base/base_object.py +60 -0
- pyqit/core/__init__.py +59 -0
- pyqit/core/adapters/__init__.py +13 -0
- pyqit/core/adapters/lightning.py +284 -0
- pyqit/core/callbacks/__init__.py +14 -0
- pyqit/core/callbacks/base.py +84 -0
- pyqit/core/callbacks/checkpoint.py +368 -0
- pyqit/core/callbacks/early_stopping.py +119 -0
- pyqit/core/callbacks/history.py +45 -0
- pyqit/core/config.py +92 -0
- pyqit/core/embeddings.py +255 -0
- pyqit/core/losses/__init__.py +15 -0
- pyqit/core/losses/_registry.py +32 -0
- pyqit/core/losses/base.py +78 -0
- pyqit/core/losses/cross_entropy.py +75 -0
- pyqit/core/losses/hinge.py +44 -0
- pyqit/core/losses/mse.py +39 -0
- pyqit/core/measurements.py +38 -0
- pyqit/core/pipeline.py +561 -0
- pyqit/core/trainer/__init__.py +13 -0
- pyqit/core/trainer/_reporting.py +271 -0
- pyqit/core/trainer/history.py +95 -0
- pyqit/core/trainer/loops/__init__.py +14 -0
- pyqit/core/trainer/loops/_registry.py +52 -0
- pyqit/core/trainer/loops/base.py +151 -0
- pyqit/core/trainer/loops/lightning_loop.py +126 -0
- pyqit/core/trainer/loops/pennylane_loop.py +185 -0
- pyqit/core/trainer/trainer.py +404 -0
- pyqit/data/__init__.py +7 -0
- pyqit/data/datamodule.py +796 -0
- pyqit/models/__init__.py +21 -0
- pyqit/models/base/__init__.py +6 -0
- pyqit/models/base/base.py +168 -0
- pyqit/models/base/quantum_model.py +138 -0
- pyqit/models/classification/__init__.py +13 -0
- pyqit/models/classification/classifier_mixin.py +112 -0
- pyqit/models/classification/dressed.py +157 -0
- pyqit/models/classification/reuploading.py +153 -0
- pyqit/models/classification/vqc.py +128 -0
- pyqit/models/layers/__init__.py +6 -0
- pyqit/models/layers/dense.py +42 -0
- pyqit/models/layers/stages.py +163 -0
- pyqit/models/layers/vqc.py +118 -0
- pyqit/models/regression/__init__.py +6 -0
- pyqit/models/regression/regressor_mixin.py +15 -0
- pyqit/models/regression/vqr.py +132 -0
- pyqit/tests/__init__.py +0 -0
- pyqit/tests/_fixture_generators.py +117 -0
- pyqit/tests/scenarios.py +117 -0
- pyqit/tests/test_all_ansatz.py +98 -0
- pyqit/tests/test_all_embeddings.py +83 -0
- pyqit/tests/test_all_layers.py +35 -0
- pyqit/tests/test_all_losses.py +57 -0
- pyqit/tests/test_all_models.py +146 -0
- pyqit/tests/test_base.py +24 -0
- pyqit/tests/test_datamodule.py +247 -0
- pyqit/tests/test_pipeline.py +444 -0
- pyqit/tests/test_trainer.py +603 -0
- pyqit/utils/__init__.py +0 -0
- pyqit/utils/diagnostic.py +472 -0
- pyqit/utils/utils.py +133 -0
- pyqit-0.1.0.dist-info/METADATA +172 -0
- pyqit-0.1.0.dist-info/RECORD +76 -0
- pyqit-0.1.0.dist-info/WHEEL +5 -0
- pyqit-0.1.0.dist-info/licenses/LICENSE +191 -0
- pyqit-0.1.0.dist-info/top_level.txt +1 -0
pyqit/__init__.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from importlib.metadata import version as _version
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
__version__ = _version("pyqit")
|
|
5
|
+
|
|
6
|
+
logging.getLogger("pyqit").addHandler(logging.NullHandler())
|
|
7
|
+
|
|
8
|
+
from pyqit.core.config import get_backend, set_backend, set_seed
|
|
9
|
+
from pyqit.core.trainer import Trainer
|
|
10
|
+
from pyqit.data.datamodule import DataModule
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"DataModule",
|
|
14
|
+
"Trainer",
|
|
15
|
+
"__version__",
|
|
16
|
+
"get_backend",
|
|
17
|
+
"set_backend",
|
|
18
|
+
"set_seed",
|
|
19
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""A module for quantum ansatzes."""
|
|
2
|
+
|
|
3
|
+
from pyqit.ansatzes.base import BaseAnsatz
|
|
4
|
+
from pyqit.ansatzes.basic_entangler import BasicEntanglerAnsatz
|
|
5
|
+
from pyqit.ansatzes.cnot_ladder import CNOTLadderAnsatz
|
|
6
|
+
from pyqit.ansatzes.hardware_efficient import EfficientSU2Ansatz, RealAmplitudesAnsatz
|
|
7
|
+
from pyqit.ansatzes.sel import SELAnsatz
|
|
8
|
+
from pyqit.ansatzes.simplified_two_design import SimplifiedTwoDesignAnsatz
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"BaseAnsatz",
|
|
12
|
+
"BasicEntanglerAnsatz",
|
|
13
|
+
"CNOTLadderAnsatz",
|
|
14
|
+
"EfficientSU2Ansatz",
|
|
15
|
+
"RealAmplitudesAnsatz",
|
|
16
|
+
"SELAnsatz",
|
|
17
|
+
"SimplifiedTwoDesignAnsatz",
|
|
18
|
+
]
|
pyqit/ansatzes/base.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
|
|
3
|
+
from pyqit.base.base_object import _PyQitObject
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BaseAnsatz(_PyQitObject):
|
|
7
|
+
"""Base class for a parameterized quantum circuit block."""
|
|
8
|
+
|
|
9
|
+
_tags = {
|
|
10
|
+
"object_type": "ansatz",
|
|
11
|
+
"ansatz_type": None,
|
|
12
|
+
"n_qubits_min": 1,
|
|
13
|
+
"differentiable": None,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
def __init__(self, n_qubits: int, n_layers: int = 1):
|
|
17
|
+
"""
|
|
18
|
+
Base configuration for any Quantum Ansatz.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
n_qubits: int
|
|
23
|
+
The number of wires in the circuit.
|
|
24
|
+
n_layers: int
|
|
25
|
+
The depth of the circuit (repeating blocks).
|
|
26
|
+
"""
|
|
27
|
+
self.n_qubits = n_qubits
|
|
28
|
+
self.n_layers = n_layers
|
|
29
|
+
super().__init__()
|
|
30
|
+
|
|
31
|
+
@abstractmethod
|
|
32
|
+
def build_circuit(self, weights):
|
|
33
|
+
"""
|
|
34
|
+
The actual PennyLane circuit logic.
|
|
35
|
+
|
|
36
|
+
Parameters
|
|
37
|
+
----------
|
|
38
|
+
weights:
|
|
39
|
+
Trainable parameters.
|
|
40
|
+
"""
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
@abstractmethod
|
|
44
|
+
def get_weight_shapes(self) -> dict:
|
|
45
|
+
"""
|
|
46
|
+
Returns the shape of trainable weights required by this ansatz.
|
|
47
|
+
Used by wrappers to initialize parameters.
|
|
48
|
+
|
|
49
|
+
Returns
|
|
50
|
+
-------
|
|
51
|
+
dict:
|
|
52
|
+
e.g., {"weights": (n_layers, n_qubits, 3)}
|
|
53
|
+
"""
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
def get_circuit_func(self):
|
|
57
|
+
"""
|
|
58
|
+
Returns the bound method to be passed to a QNode.
|
|
59
|
+
"""
|
|
60
|
+
return self.build_circuit
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import pennylane as qml
|
|
2
|
+
|
|
3
|
+
from pyqit.ansatzes.base import BaseAnsatz
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BasicEntanglerAnsatz(BaseAnsatz):
|
|
7
|
+
"""Basic entangler layers: one rotation per qubit and a CNOT ring per layer.
|
|
8
|
+
|
|
9
|
+
Wraps PennyLane's `BasicEntanglerLayers` (Schuld et al. 2020 lineage).
|
|
10
|
+
The weights are one tensor, `weights`, of shape `(n_layers, n_qubits)`.
|
|
11
|
+
|
|
12
|
+
Parameters
|
|
13
|
+
----------
|
|
14
|
+
n_qubits : int
|
|
15
|
+
n_layers : int, default 2
|
|
16
|
+
rotation : type, optional
|
|
17
|
+
Single-qubit rotation gate class. PennyLane's default is `qml.RX`.
|
|
18
|
+
|
|
19
|
+
Examples
|
|
20
|
+
--------
|
|
21
|
+
>>> import pennylane as qml
|
|
22
|
+
>>> from pyqit.ansatzes import BasicEntanglerAnsatz
|
|
23
|
+
>>> ansatz = BasicEntanglerAnsatz(n_qubits=4, n_layers=2, rotation=qml.RY)
|
|
24
|
+
>>> ansatz.get_weight_shapes()
|
|
25
|
+
{'weights': (2, 4)}
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, n_qubits: int, n_layers: int = 2, rotation=None):
|
|
29
|
+
self.rotation = rotation
|
|
30
|
+
super().__init__(n_qubits, n_layers)
|
|
31
|
+
|
|
32
|
+
def build_circuit(self, weights):
|
|
33
|
+
"""Apply the layers. Expects `weights["weights"]` of shape
|
|
34
|
+
`(n_layers, n_qubits)`."""
|
|
35
|
+
qml.BasicEntanglerLayers(
|
|
36
|
+
weights["weights"], wires=range(self.n_qubits), rotation=self.rotation
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def get_weight_shapes(self) -> dict:
|
|
40
|
+
"""Return `{"weights": (n_layers, n_qubits)}`."""
|
|
41
|
+
return {"weights": qml.BasicEntanglerLayers.shape(self.n_layers, self.n_qubits)}
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
def get_test_params(cls):
|
|
45
|
+
"""List constructor kwargs used to parametrize this class in the test suite."""
|
|
46
|
+
return [{"n_qubits": 3, "n_layers": 2}, {"n_qubits": 2, "rotation": qml.RY}]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import pennylane as qml
|
|
2
|
+
|
|
3
|
+
from pyqit.ansatzes.base import BaseAnsatz
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CNOTLadderAnsatz(BaseAnsatz):
|
|
7
|
+
"""The variational block of Mari et al. (2020): a CNOT ladder, then RY.
|
|
8
|
+
|
|
9
|
+
Each layer applies CNOT to the wire pairs ``(0, 1), (2, 3), ...``, then to
|
|
10
|
+
``(1, 2), (3, 4), ...``, then one RY per wire.
|
|
11
|
+
|
|
12
|
+
Parameters
|
|
13
|
+
----------
|
|
14
|
+
n_qubits : int
|
|
15
|
+
n_layers : int, default 6
|
|
16
|
+
``q_depth`` in the paper.
|
|
17
|
+
|
|
18
|
+
References
|
|
19
|
+
----------
|
|
20
|
+
Mari, Bromley, Izaac, Schuld, Killoran, "Transfer learning in hybrid
|
|
21
|
+
classical-quantum neural networks", Quantum 4, 340 (2020). PennyLane's
|
|
22
|
+
"Quantum transfer learning" demo is the reference implementation.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, n_qubits: int, n_layers: int = 6):
|
|
26
|
+
super().__init__(n_qubits, n_layers)
|
|
27
|
+
|
|
28
|
+
def build_circuit(self, weights):
|
|
29
|
+
"""Apply the layers. Expects `weights["weights"]` of shape
|
|
30
|
+
`(n_layers, n_qubits)`."""
|
|
31
|
+
for layer in range(self.n_layers):
|
|
32
|
+
for start in (0, 1):
|
|
33
|
+
for i in range(start, self.n_qubits - 1, 2):
|
|
34
|
+
qml.CNOT(wires=[i, i + 1])
|
|
35
|
+
for w in range(self.n_qubits):
|
|
36
|
+
qml.RY(weights["weights"][layer, w], wires=w)
|
|
37
|
+
|
|
38
|
+
def get_weight_shapes(self) -> dict:
|
|
39
|
+
"""Return `{"weights": (n_layers, n_qubits)}`."""
|
|
40
|
+
return {"weights": (self.n_layers, self.n_qubits)}
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def get_test_params(cls):
|
|
44
|
+
"""List constructor kwargs used to parametrize this class in the test suite."""
|
|
45
|
+
return [{"n_qubits": 3, "n_layers": 2}, {"n_qubits": 2}]
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import pennylane as qml
|
|
2
|
+
from skbase.utils.dependencies import _check_soft_dependencies
|
|
3
|
+
|
|
4
|
+
from pyqit.ansatzes.base import BaseAnsatz
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RealAmplitudesAnsatz(BaseAnsatz):
|
|
8
|
+
"""Qiskit's `RealAmplitudes`: RY layers separated by CX entanglers.
|
|
9
|
+
|
|
10
|
+
The hardware-efficient ansatz of Kandala et al. 2017 (Nature) as Qiskit's
|
|
11
|
+
circuit library builds it, and the default ansatz of Qiskit ML's `VQC`.
|
|
12
|
+
The circuit is Qiskit's own, converted through the `pennylane-qiskit`
|
|
13
|
+
plugin, so it needs the `qiskit` extra and Python 3.11 or newer.
|
|
14
|
+
|
|
15
|
+
The weights are Qiskit's flat parameter vector, `weights`, of shape
|
|
16
|
+
`(n_qubits * (n_layers + 1),)`. With `skip_final_rotation_layer` it is
|
|
17
|
+
`(n_qubits * n_layers,)`.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
n_qubits : int
|
|
22
|
+
n_layers : int, default 3
|
|
23
|
+
Qiskit's `reps`: the number of entangling blocks. Rotation layers
|
|
24
|
+
number `n_layers + 1` unless `skip_final_rotation_layer`.
|
|
25
|
+
entanglement : str, default "reverse_linear"
|
|
26
|
+
Any entanglement Qiskit accepts, e.g. `"linear"`, `"full"`,
|
|
27
|
+
`"circular"`.
|
|
28
|
+
skip_final_rotation_layer : bool, default False
|
|
29
|
+
|
|
30
|
+
References
|
|
31
|
+
----------
|
|
32
|
+
Kandala et al., "Hardware-efficient variational quantum eigensolver for
|
|
33
|
+
small molecules and quantum magnets", Nature 549, 242 (2017).
|
|
34
|
+
|
|
35
|
+
Examples
|
|
36
|
+
--------
|
|
37
|
+
>>> from pyqit.ansatzes import RealAmplitudesAnsatz
|
|
38
|
+
>>> from pyqit.core import ZZFeatureMap
|
|
39
|
+
>>> from pyqit.models import VQCClassifier
|
|
40
|
+
>>> model = VQCClassifier(
|
|
41
|
+
... n_qubits=2, ansatz=RealAmplitudesAnsatz, encoder=ZZFeatureMap
|
|
42
|
+
... )
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
_tags = {"python_dependencies": "pennylane-qiskit"}
|
|
46
|
+
_qiskit_circuit = "real_amplitudes"
|
|
47
|
+
|
|
48
|
+
def __init__(
|
|
49
|
+
self,
|
|
50
|
+
n_qubits: int,
|
|
51
|
+
n_layers: int = 3,
|
|
52
|
+
entanglement: str = "reverse_linear",
|
|
53
|
+
skip_final_rotation_layer: bool = False,
|
|
54
|
+
):
|
|
55
|
+
self.entanglement = entanglement
|
|
56
|
+
self.skip_final_rotation_layer = skip_final_rotation_layer
|
|
57
|
+
super().__init__(n_qubits, n_layers)
|
|
58
|
+
|
|
59
|
+
if not _check_soft_dependencies("pennylane-qiskit", severity="none"):
|
|
60
|
+
raise ImportError(
|
|
61
|
+
f"{type(self).__name__} wraps Qiskit's circuit through the "
|
|
62
|
+
"pennylane-qiskit plugin, which is not installed. Install it with "
|
|
63
|
+
"`pip install pyqit[qiskit]`"
|
|
64
|
+
)
|
|
65
|
+
from qiskit.circuit import library
|
|
66
|
+
|
|
67
|
+
self.circuit = getattr(library, self._qiskit_circuit)(
|
|
68
|
+
n_qubits,
|
|
69
|
+
entanglement=entanglement,
|
|
70
|
+
reps=n_layers,
|
|
71
|
+
skip_final_rotation_layer=skip_final_rotation_layer,
|
|
72
|
+
)
|
|
73
|
+
self._template = qml.from_qiskit(self.circuit)
|
|
74
|
+
|
|
75
|
+
def build_circuit(self, weights):
|
|
76
|
+
"""Apply the circuit. Expects `weights["weights"]` as a flat vector in
|
|
77
|
+
the order of `circuit.parameters`."""
|
|
78
|
+
self._template(weights["weights"])
|
|
79
|
+
|
|
80
|
+
def get_weight_shapes(self) -> dict:
|
|
81
|
+
"""Return `{"weights": (n_params,)}`, Qiskit's flat parameter vector."""
|
|
82
|
+
return {"weights": (len(self.circuit.parameters),)}
|
|
83
|
+
|
|
84
|
+
@classmethod
|
|
85
|
+
def get_test_params(cls):
|
|
86
|
+
"""List constructor kwargs used to parametrize this class in the test suite."""
|
|
87
|
+
return [
|
|
88
|
+
{"n_qubits": 3, "n_layers": 2},
|
|
89
|
+
{"n_qubits": 2, "n_layers": 1, "entanglement": "circular"},
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class EfficientSU2Ansatz(RealAmplitudesAnsatz):
|
|
94
|
+
"""Qiskit's `EfficientSU2`: RY and RZ layers separated by CX entanglers.
|
|
95
|
+
|
|
96
|
+
`RealAmplitudesAnsatz` with an RZ layer after every RY layer, Qiskit's
|
|
97
|
+
default `su2_gates`. Same parameters and the same reference. The second
|
|
98
|
+
rotation doubles the weight vector to `(2 * n_qubits * (n_layers + 1),)`.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
_qiskit_circuit = "efficient_su2"
|
pyqit/ansatzes/sel.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import pennylane as qml
|
|
2
|
+
|
|
3
|
+
from pyqit.ansatzes.base import BaseAnsatz
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SELAnsatz(BaseAnsatz):
|
|
7
|
+
"""Strongly entangling layers of Schuld et al. (2020).
|
|
8
|
+
|
|
9
|
+
Wraps PennyLane's `StronglyEntanglingLayers`. Each layer applies three
|
|
10
|
+
rotations to every qubit, then a CNOT layer whose range grows with the
|
|
11
|
+
layer index. The weights are one tensor, `weights`, of shape
|
|
12
|
+
`(n_layers, n_qubits, 3)`.
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
n_qubits : int
|
|
17
|
+
n_layers : int, default 2
|
|
18
|
+
|
|
19
|
+
References
|
|
20
|
+
----------
|
|
21
|
+
Schuld, Bocharov, Svore, Wiebe, "Circuit-centric quantum classifiers",
|
|
22
|
+
Phys. Rev. A 101, 032308 (2020).
|
|
23
|
+
|
|
24
|
+
Examples
|
|
25
|
+
--------
|
|
26
|
+
>>> from pyqit.ansatzes import SELAnsatz
|
|
27
|
+
>>> from pyqit.models import VQCClassifier
|
|
28
|
+
>>> model = VQCClassifier(n_qubits=4, n_layers=3, ansatz=SELAnsatz)
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(self, n_qubits: int, n_layers: int = 2):
|
|
32
|
+
super().__init__(n_qubits, n_layers)
|
|
33
|
+
|
|
34
|
+
def build_circuit(self, weights):
|
|
35
|
+
"""
|
|
36
|
+
Construct and apply the strongly entangling layers to the quantum circuit.
|
|
37
|
+
|
|
38
|
+
Parameters
|
|
39
|
+
----------
|
|
40
|
+
weights : dict
|
|
41
|
+
A dictionary containing the parameter tensors. Must include the
|
|
42
|
+
key `"weights"` with a tensor of shape `(n_layers, n_qubits, 3)`.
|
|
43
|
+
|
|
44
|
+
"""
|
|
45
|
+
w_tensor = weights["weights"]
|
|
46
|
+
qml.templates.StronglyEntanglingLayers(w_tensor, wires=range(self.n_qubits))
|
|
47
|
+
|
|
48
|
+
def get_weight_shapes(self) -> dict:
|
|
49
|
+
"""
|
|
50
|
+
Get the shapes of the trainable weights required by the ansatz.
|
|
51
|
+
|
|
52
|
+
Returns
|
|
53
|
+
-------
|
|
54
|
+
dict
|
|
55
|
+
A dictionary mapping the weight parameter name (`"weights"`) to
|
|
56
|
+
its expected shape tuple `(n_layers, n_qubits, 3)`.
|
|
57
|
+
"""
|
|
58
|
+
shape = (self.n_layers, self.n_qubits, 3)
|
|
59
|
+
return {"weights": shape}
|
|
60
|
+
|
|
61
|
+
@classmethod
|
|
62
|
+
def get_test_params(cls):
|
|
63
|
+
"""
|
|
64
|
+
Retrieve a set of default parameters for testing the ansatz.
|
|
65
|
+
|
|
66
|
+
Returns
|
|
67
|
+
-------
|
|
68
|
+
list of dict
|
|
69
|
+
A list containing a dictionary of valid initialization parameters
|
|
70
|
+
for the class.
|
|
71
|
+
"""
|
|
72
|
+
return [{"n_qubits": 3, "n_layers": 2}]
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import pennylane as qml
|
|
2
|
+
|
|
3
|
+
from pyqit.ansatzes.base import BaseAnsatz
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SimplifiedTwoDesignAnsatz(BaseAnsatz):
|
|
7
|
+
"""Simplified two-design ansatz of Cerezo et al. 2021 (Nat. Commun.).
|
|
8
|
+
|
|
9
|
+
Wraps PennyLane's `SimplifiedTwoDesign`: an initial RY layer, then
|
|
10
|
+
`n_layers` of controlled-Z gates on alternating pairs each followed by RY
|
|
11
|
+
rotations. This is the circuit the local-cost trainability result was
|
|
12
|
+
proved on, so it pairs with the barren-plateau diagnostic.
|
|
13
|
+
|
|
14
|
+
There are two weight tensors, `initial_layer_weights` of shape
|
|
15
|
+
`(n_qubits,)` and `weights` of shape `(n_layers, n_qubits - 1, 2)`.
|
|
16
|
+
|
|
17
|
+
Parameters
|
|
18
|
+
----------
|
|
19
|
+
n_qubits : int
|
|
20
|
+
At least 2.
|
|
21
|
+
n_layers : int, default 2
|
|
22
|
+
|
|
23
|
+
References
|
|
24
|
+
----------
|
|
25
|
+
Cerezo, Sone, Volkoff, Cincio, Coles, "Cost function dependent barren
|
|
26
|
+
plateaus in shallow parametrized quantum circuits", Nat. Commun. 12, 1791
|
|
27
|
+
(2021).
|
|
28
|
+
|
|
29
|
+
Examples
|
|
30
|
+
--------
|
|
31
|
+
>>> from pyqit.ansatzes import SimplifiedTwoDesignAnsatz
|
|
32
|
+
>>> SimplifiedTwoDesignAnsatz(n_qubits=3, n_layers=2).get_weight_shapes()
|
|
33
|
+
{'initial_layer_weights': (3,), 'weights': (2, 2, 2)}
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
_tags = {"n_qubits_min": 2}
|
|
37
|
+
|
|
38
|
+
def __init__(self, n_qubits: int, n_layers: int = 2):
|
|
39
|
+
super().__init__(n_qubits, n_layers)
|
|
40
|
+
|
|
41
|
+
def build_circuit(self, weights):
|
|
42
|
+
"""Apply the layers. Expects `weights["initial_layer_weights"]` of shape
|
|
43
|
+
`(n_qubits,)` and `weights["weights"]` of shape `(n_layers, n_qubits - 1, 2)`.
|
|
44
|
+
"""
|
|
45
|
+
qml.SimplifiedTwoDesign(
|
|
46
|
+
weights["initial_layer_weights"],
|
|
47
|
+
weights["weights"],
|
|
48
|
+
wires=range(self.n_qubits),
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def get_weight_shapes(self) -> dict:
|
|
52
|
+
"""Return the two weight shapes, `initial_layer_weights` and `weights`."""
|
|
53
|
+
initial, layers = qml.SimplifiedTwoDesign.shape(self.n_layers, self.n_qubits)
|
|
54
|
+
return {"initial_layer_weights": initial, "weights": layers}
|
|
55
|
+
|
|
56
|
+
@classmethod
|
|
57
|
+
def get_test_params(cls):
|
|
58
|
+
"""List constructor kwargs used to parametrize this class in the test suite."""
|
|
59
|
+
return [{"n_qubits": 3, "n_layers": 2}, {"n_qubits": 2, "n_layers": 1}]
|
pyqit/base/__init__.py
ADDED