# Quick start

Train a single-qubit circuit with one adjustable angle to match a Pauli-Z expectation target of `0.2`. This example follows the ArcQML introduction under Application tutorials on the homepage. Learn the training workflow here before moving to the complete VQE and QNN tasks.

## Preparing the environment

Read [Installation and environment setup](docs/arcqml/installation.html) and [Release status and license](docs/arcqml/index.html#license) first. This is an experimental Windows preview using CPython 3.11. Follow the current [ArcQML repository](https://github.com/Quill-ArcLight/ArcQML) instructions to install the supplied Windows wheel. Linux commands on the installation page remain as historical environment examples.

For the native Rust interface, start with the [VQE tutorial](docs/arcqml/vqe.html) or [QNN tutorial](docs/arcqml/qnn.html). Both display Rust by default; their paired code blocks can switch together to Python.

## Running a minimal training example

Save the code below as `quick_start.py` and run `python quick_start.py` in a Python environment with ArcQML installed. It builds the circuit, defines the training target, updates parameters, and reads the result.

```python
# Build a one-qubit circuit with one trainable angle.
import arcqml

circuit = arcqml.Circuit(num_qubits=1)
circuit.ry(angle=0.3, qubit=0)
print("trainable parameters:", circuit.num_parameters)

# Define the observable and simulator, then set the objective and optimizer.
observable = arcqml.PauliSum.z(num_qubits=1, qubit=0)
simulator = arcqml.StateVectorSimulator(num_qubits=1)

target = arcqml.tensor(0.2)
optimizer = arcqml.Adam(learning_rate=0.05)

# Compute the expectation and loss, then backpropagate and update circuit parameters.
for step in range(1, 101):
    optimizer.zero_grad(circuit=circuit)

    prediction = simulator.run(circuit=circuit, observable=observable)
    loss = arcqml.mse_loss(prediction=prediction, target=target)

    loss.backward()
    optimizer.step(circuit=circuit)

    if step == 1 or step % 20 == 0:
        print(f"step {step:>2}: loss = {loss.item():.6f}")

# Disable gradient recording for inference; read the expectation after training.
with arcqml.no_grad():
    prediction = simulator.run(circuit=circuit, observable=observable).item()

print(f"final <Z> = {prediction:.6f}")
print("target    = 0.200000")
```

## Understanding the training process

- `circuit.ry` adds a trainable rotation gate whose angle is adjusted by the optimizer.
- `simulator.run` returns a differentiable expectation `Tensor`; `mse_loss` computes its mean squared error against the target.
- `loss.backward()` propagates the loss gradient to circuit parameters, and `optimizer.step` updates them. Clear old gradients each iteration to avoid accumulation.
- `no_grad()` disables gradient recording when reading the final result.

The program prints the parameter count, losses from selected training steps, and the final expectation. Check whether it approaches `0.2`, and compare early and late losses; they need not decrease monotonically at every step.

See [Data flow through a trainable forward and backward pass](docs/arcqml/installation.html#section-2-4) for how circuits join classical autograd graphs. Rust training loops and custom losses appear in [Losses and optimizers](docs/arcqml/training.html).

## Further learning

- [VQE: solving the H₂ ground-state energy](docs/arcqml/vqe.html): learn Hamiltonians, initial-state preparation, and variational energy optimization.
- [QNN: building a quantum neural-network classifier](docs/arcqml/qnn.html): learn data encoding, batch forward passes, classification losses, and validation.
