Training with PyTorch#
Execution and training are intentionally separate. A trainable program is an
fq.Module inside an ordinary PyTorch training loop.
import torch
import flagquantum as fq
def build_circuit(parameters, inputs=None):
return (
fq.Circuit(n_qubits=2)
.ry(0, theta=parameters[0])
.cx(0, 1)
.ry(1, theta=parameters[1])
)
module = fq.Module(
build_circuit,
n_parameters=2,
policy=fq.RuntimePolicy(observable_wires=(1,)),
)
optimizer = torch.optim.Adam(module.parameters(), lr=0.01)
training = fq.train(
module,
optimizer=optimizer,
objective=lambda value: value.mean(),
steps=100,
)
print(training.losses[-1])
Module behaviour#
module(inputs)andmodule.forward(inputs)return an autograd-compatible tensor, so gradients flow into quantum and classical parameters from the same backward call.module.execute(inputs)returnsfq.ExecutionResultwhen the caller needs provenance, runtime diagnostics or explicit backend information.fq.runaccepts a circuit, IR or execution plan β not a module β and never updates parameters.
fq.train is deliberately a minimal, caller-owned optimizer loop: it performs
zero_grad, backward and step, then returns fq.TrainingResult. Use an
ordinary PyTorch loop when the quantum module is part of a larger classical
model.
Precision#
fq.Module owns one end-to-end precision choice through PrecisionPolicy,
which determines the real parameter dtype and the complex circuit/execution
dtype. Circuit builders that omit dtype inherit that choice, and an explicit
ExecutionOptions.precision or circuit dtype must agree with it. The module
fails before execution instead of silently casting.
Named parameter groups#
Named groups remove positional-index bookkeeping for larger circuits:
def named_circuit(parameters):
return (fq.Circuit(2)
.ry(0, parameters["encoder"][0])
.rx(1, parameters["readout"]))
model = fq.Module(
named_circuit,
parameters={"encoder": (4,), "readout": ()},
init={"encoder": "uniform", "readout": 0.1},
seed=42,
)
init="uniform" samples angles from [0, 2Ο), while init="normal" samples
from a zero-mean normal distribution with standard deviation 0.01. seed
uses a module-local generator and does not reset PyTorchβs global random state.
Checkpoints#
Module parameters and policy participate in state_dict() save and load, and
checkpoint and resume stay on Module.save_checkpoint() and
Module.load_checkpoint() rather than becoming hidden options of fq.train.
The circuit builder remains application code and must be supplied when
reconstructing the module, matching normal PyTorch module construction.
Distributed training#
Under an initialized multi-rank process group, the same module and result surface automatically use the native sharded statevector runtime. Owner-sharded statevector and MPS training also expose separate distributed entry points, which remain experimental. See Distributed execution.