Models#
A model owns the QNode. It builds the circuit from an ansatz and an embedding,
holds the weights, and exposes forward. BaseModel defines the
contract and BaseQuantumModel adds the quantum-specific plumbing that
every circuit model shares.
VQCClassifier is the model to start with. It takes a qubit count, a
depth, an ansatz class and an embedding class, and wires them together:
from pyqit.ansatzes import SELAnsatz
from pyqit.core import AngleEmbedding
from pyqit.models import VQCClassifier
model = VQCClassifier(
n_qubits=4,
n_layers=3,
ansatz=SELAnsatz,
encoder=AngleEmbedding,
)
The ansatz and encoder arrive as classes, not instances. The model builds them
with its own n_qubits, so the two cannot disagree about width.
Available models#
Each page gives the circuit, the paper it follows, every constructor argument and a runnable example.
Variational quantum classifier: an embedding, an ansatz, a measurement. |
|
Variational quantum regressor: the |
|
Data re-uploading classifier of Perez-Salinas et al. (2020). |
|
Dressed quantum circuit of Mari et al. (2020): dense, circuit, dense. |
Two tags say what a model is. model_type is "quantum", "hybrid"
or "classical"; estimator_type is "classifier" or "regressor".
Trainer prints both in its summary, and all_objects filters on them:
from pyqit.base import all_objects
all_objects("model", filter_tags={"model_type": "hybrid"})
Classifiers and regressors#
A mixin decides how the Trainer reads a model’s output.
ClassifierMixin turns it into class probabilities and hard labels.
RegressorMixin passes the raw output through, and the training loops
record accuracy as NaN for it.
Models that encode their own input#
A model that takes encoder exposes the built embedding as embedding_obj,
and the DataModule prescales the input for it. A model that takes
n_features instead encodes the input inside its own circuit. It has no
embedding_obj, so the DataModule normalizes the features and leaves them
unscaled.
Hybrid networks are pipelines#
A hybrid model mixes classical and quantum layers in one network and trains
them together. Every hybrid model here is built the same way. It is a
BaseQuantumModel that builds its layers from pyqit.models.layers
and runs them through a pipeline inside forward. Its
class page names the paper it follows and the layers it uses.
From the outside it is an ordinary model. The same Trainer
fits, checkpoints and evaluates it, check_bp runs on it, and it can be a
stage in a larger pipeline. The layers’ weights are the model’s own and sit in
the same flat dict as any other model’s, under one prefix per layer.
from pyqit.models import DressedQuantumClassifier
model = DressedQuantumClassifier(n_features=8, n_qubits=4, n_layers=6)
history = pyqit.Trainer(max_epochs=20).fit(model, dm)
model.weights # one prefix per layer
A hybrid model takes n_features instead of an encoder. The DataModule
normalizes its input and leaves it unscaled, and the pipeline inside prescales
each quantum layer’s input for that layer’s embedding.
The layers in pyqit.models.layers are reusable blocks, used
by quantum and hybrid models alike. The pipeline page shows
how to compose and train them yourself.
Weights exist before training#
A model draws its weights in __init__, not on the first fit. Two things
follow. Seeding afterwards will not reproduce them, and calling
set_backend() afterwards will not move the model, because each
object reads the backend once in its own __init__ and caches it.
Weights come back as a flat dict keyed "<qnode_name>.<weight_name>", the
same on both backends:
model.weights # {"main_circuit.weights": array(...)}
update_weights writes that dict back. It is a no-op under torch, where
autograd owns the parameters directly.
Devices#
device= goes straight to qml.device, so any PennyLane device name works,
plugins included. shots=None asks for analytic simulation, and the defaults
assume a local analytic simulator. PennyLane picks the differentiation method
per device. default.qubit gets backprop, lightning.qubit adjoint, and
any device with shots, or real hardware, parameter-shift. Parameter-shift runs
1 + 2 * n_params circuits for every gradient, so a model that trains in
seconds locally can take hours on a queue. diff_methods tells you which
method you are getting, and Trainer(verbose=2) prints it in the model
summary next to the device. diff_method= names one instead; forcing
"parameter-shift" on a simulator rehearses a hardware run’s gradient cost,
and check_bp then counts the executions it takes.
model = VQCClassifier(n_qubits=4, device="qiskit.aer", shots=1024)
model.diff_methods(dm.X_train[:1]) # {"main_circuit": "parameter-shift"}
rehearsal = VQCClassifier(n_qubits=4, diff_method="parameter-shift")
pip install pyqit[qiskit] adds the Qiskit plugin. Its local simulators
sample even at shots=None, where they run 1024 shots, so expect
parameter-shift and shot noise there. No real QPU has been run against pyqit
yet.
Writing a new model#
Expose the encoder as embedding_obj. The framework reads that attribute to
decide prescaling, and a mismatched name disables prescaling silently instead of
raising, which is the kind of bug that produces plausible numbers for weeks.
Beyond that, give the class an object_type tag of "model" and a
get_test_params() method returning a list of kwarg dicts. There is no
registration step. The suite discovers the class by walking the package and
parametrizes every model test over it. Then add the class name to the list
above.
You subclass these and never instantiate them directly.
Base class for all trainable models in PyQit. |
|
Base class wiring a PennyLane QNode into either backend. |
|
Turns raw circuit output into class probabilities and hard labels. |
|
Marks a model as a regressor: predictions are its raw output. |
See the contributing guide for the checklist and the PR conventions.