Callbacks#

A pyqit callback implements up to three hooks, on_fit_start, on_epoch_end and on_fit_end, each taking one LoopState. Write it once and both backends honour it.

import pyqit
from pyqit.core import EarlyStopping, ModelCheckpoint

trainer = pyqit.Trainer(
    max_epochs=100,
    loss_fn="cross_entropy",
    callbacks=[
        EarlyStopping(monitor="val_loss", patience=3),
        ModelCheckpoint(dirpath="ckpts", save_best=True, save_last=True),
    ],
)
history = trainer.fit(model, dm)
[EarlyStopping] Stopped at epoch 18 - val_loss did not improve for 3 epoch(s)
[Checkpoint] Restored best weights from epoch 15 (val_loss: 0.3721)

A checkpoint holds the optimizer state and history alongside the weights, so ModelCheckpoint(resume_from=...) continues a run; its page shows how.

Available callbacks#

EarlyStopping

Request a stop after patience epochs without improvement.

ModelCheckpoint

Save checkpoints, restore the best epoch's weights, or resume from one.

HistoryCallback

Copy each epoch's metrics into a TrainingHistory.

Why Lightning callbacks are rejected#

They are typed against Lightning’s hooks, so the PennyLane loop could only ignore them. An ignored EarlyStopping hands back a fully trained model without saying so, and that failure is invisible. Rejecting them at the door is the louder option.

On the torch backend a shim reads Lightning’s callback_metrics into the same metric names and forwards state.stop onto trainer.should_stop, so the same callback object works on both sides.

Writing your own#

from pyqit.core import BaseCallback

class StopWhenConverged(BaseCallback):
    def on_epoch_end(self, state):
        if state.metrics["train_loss"] < 0.01:
            state.stop = True

state carries the model, datamodule, history, reporter, epoch index and this epoch’s metrics. state.stop is the one field a callback may write.

BaseCallback

Base class for pyqit callbacks.

LoopState

Everything a callback may read, and the one flag it may write.