from pyqit.ansatzes.sel import SELAnsatz
from pyqit.core.embeddings import AngleEmbedding
from pyqit.models.classification.classifier_mixin import ClassifierMixin
from pyqit.models.layers.vqc import BaseVQC
[docs]
class VQCClassifier(BaseVQC, ClassifierMixin):
"""Variational quantum classifier: an embedding, an ansatz, a measurement.
Parameters
----------
n_qubits : int, default 4
n_layers : int, default 3
Depth passed to `ansatz`.
ansatz : type, default SELAnsatz
Ansatz class, not an instance.
encoder : type, default AngleEmbedding
Embedding class, not an instance. Drives `DataModule` prescaling.
n_classes : int, default 2
Binary reads one expectation value; multi-class bins the
`2 ** n_qubits` basis-state probabilities by index modulo `n_classes`,
the Qiskit ML `VQC` readout.
measure_fn : callable, optional
Defaults to `measure_expval_z` for binary, `measure_probs` otherwise.
measure_wires : list of int, optional
Defaults to `[0]` for binary, all wires otherwise.
device : str, default "default.qubit"
Any PennyLane device name.
shots : int, optional
diff_method : str, default "best"
Passed to the QNode. ``"best"`` picks backprop on a simulator;
``"parameter-shift"`` rehearses a hardware run's gradient cost.
`None` runs analytic (infinite-shot) simulation.
References
----------
Havlicek et al., "Supervised learning with quantum-enhanced feature
spaces", Nature 567, 209 (2019). Readout follows Qiskit ML's ``VQC``.
Examples
--------
>>> import pyqit
>>> from pyqit.models import VQCClassifier
>>> model = VQCClassifier(n_qubits=4, n_layers=2)
>>> history = pyqit.Trainer(max_epochs=5).fit(model, dm) # doctest: +SKIP
"""
def __init__(
self,
n_qubits=4,
n_layers=3,
ansatz=SELAnsatz,
encoder=AngleEmbedding,
n_classes=2,
measure_fn=None,
measure_wires=None,
device="default.qubit",
shots=None,
diff_method="best",
):
self.n_classes = n_classes
super().__init__(
n_qubits=n_qubits,
n_layers=n_layers,
ansatz=ansatz,
encoder=encoder,
measure_fn=measure_fn,
measure_wires=measure_wires,
device=device,
shots=shots,
diff_method=diff_method,
)
def __repr__(self):
return (
f"VQCClassifier(n_qubits={self.n_qubits}, n_layers={self.n_layers}, "
f"n_classes={self.n_classes}, ansatz={self._ansatz_name}, "
f"encoder={self._encoder_name}, device='{self.device}')"
)
[docs]
def forward(self, X, **custom_weights):
"""Run the circuit and return class probabilities.
Parameters
----------
X : array-like
Batch, already prescaled by the DataModule.
**custom_weights
Override the model's own weights, keyed as in `weights`.
Returns
-------
array-like
Probability of class 1 for binary; a `(n_samples, n_classes)`
probability matrix otherwise.
"""
raw_output = self.execute_qnode("main_circuit", X, **custom_weights)
return self._to_probabilities(raw_output)
[docs]
@classmethod
def get_test_params(cls):
"""List constructor kwargs used to parametrize this class in the test suite."""
from pyqit.core.embeddings import (
AmplitudeEmbedding,
IQPEmbedding,
ZZFeatureMap,
)
return [
{},
{
"n_qubits": 3,
"n_layers": 2,
"n_classes": 2,
"ansatz": SELAnsatz,
"encoder": IQPEmbedding,
"trainer_kwargs": {"check_bp": True, "loss_fn": "hinge"},
},
{
"n_qubits": 4,
"n_layers": 3,
"n_classes": 4,
"ansatz": SELAnsatz,
"encoder": AmplitudeEmbedding,
"trainer_kwargs": {"loss_fn": "cross_entropy"},
},
{"n_qubits": 3, "n_layers": 1, "encoder": ZZFeatureMap},
]