> ## Documentation Index
> Fetch the complete documentation index at: https://docs.haiqu.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Variational algorithms

> Execute variational quantum algorithms

## Variational optimization with Haiqu SDK

Use `haiqu.variational_optimization()` to minimize the expectation value of an observable for a parameterized ansatz circuit.

<Steps>
  <Step title="Define the variational problem">
    ```python theme={null}
    from qiskit.circuit.library import EfficientSU2
    from qiskit.quantum_info import SparsePauliOp
    from haiqu.sdk.qml import VariationalProblem

    #Build a parameterized QuantumCircuit (ansatz)
    num_qubits = 3
    ansatz = EfficientSU2(num_qubits=num_qubits, reps=1)

    #Define the cost observable as SparsePauliOp
    observable = SparsePauliOp.from_list([("Z" * num_qubits, 1.0)])

    # Wrap both in VariationalProblem
    problem = VariationalProblem(ansatz, observable)
    ```

    <Info>
      `VariationalProblem` requires a parameterized ansatz circuit and a `SparsePauliOp` observable.
    </Info>
  </Step>

  <Step title="Configure optimizer and submit the job">
    <Tabs>
      <Tab title="NFT (default)">
        ```python theme={null}
        from haiqu.sdk import haiqu
        from haiqu.sdk.qml import NFTOptimizerOptions

        optimizer_options = NFTOptimizerOptions(
            maxiter=100,
            maxfev=200,
            reset_interval=32,
            randomized_order=False,
        )

        job = haiqu.variational_optimization(
            problem=problem,
            device_id="aer_simulator",
            shots=1000,
            seed=42,
            optimizer_options=optimizer_options,
            use_mitigation=False,
            use_session=False,
        )
        ```
      </Tab>

      <Tab title="Scipy">
        ```python theme={null}
        from haiqu.sdk import haiqu
        from haiqu.sdk.qml import ScipyOptimizerOptions

        optimizer_options = ScipyOptimizerOptions(
            method="cobyla",
            maxfev=200,
            options={"rhobeg": 0.5},
        )

        job = haiqu.variational_optimization(
            problem=problem,
            device_id="aer_simulator",
            shots=1000,
            seed=42,
            optimizer_options=optimizer_options,
            use_mitigation=False,
            use_session=False,
        )
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="Track progress and fetch results">
    ```python theme={null}
    job.progress()
    result = job.result()

    print("Minimum loss:", result.min_loss)
    print("Optimal parameters:", result.optimal_parameters)
    print("Iterations:", len(result.loss_history))
    ```

    <Check>
      `job.result()` returns a `VariationalResult` with `min_loss`, `optimal_parameters`, and `loss_history`.
    </Check>
  </Step>
</Steps>

## API details

The variational optimisation function is defined as:

```python theme={null}
haiqu.variational_optimization(
    problem,
    shots=1000,
    device=None,
    device_id=None,
    options=None,
    initial_parameters=None,
    seed=None,
    optimizer_options=None,
    use_mitigation=False,
    use_session=False,
)
```

| Argument               | Description                                                                                                                           |
| :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------ |
| `problem`              | `VariationalProblem(ansatz, observable)`                                                                                              |
| `device` / `device_id` | Target backend. At least one must be provided.                                                                                        |
| `shots`                | Number of shots per circuit evaluation.                                                                                               |
| `seed`                 | Reproducible random initialization in `[-0.1π, 0.1π]`.                                                                                |
| `initial_parameters`   | Explicit initial values (length must match ansatz parameters).                                                                        |
| `optimizer_options`    | Optimizer configuration. Either `NFTOptimizerOptions(...)` (default) or `ScipyOptimizerOptions(method=..., maxfev=..., options=...)`. |
| `use_mitigation`       | Enables mitigation pipeline in backend execution.                                                                                     |
| `use_session`          | Enables Qiskit Runtime Session mode.                                                                                                  |

## Optimizer details

`haiqu.variational_optimization` accepts two optimizer types via `optimizer_options`: `NFTOptimizerOptions` (the default) and `ScipyOptimizerOptions`. Both are gradient-free.

### NFT (default)

Haiqu uses the [NFT](https://arxiv.org/abs/1903.12166) optimizer by default for variational optimization.

NFT, short for Nakanishi-Fujii-Todo, is a gradient-free optimizer designed for parameterized quantum circuits. Instead of estimating a full gradient, NFT updates circuit parameters sequentially. For a common class of ansatz circuits, the cost function as a function of one parameter has a simple trigonometric form, so each parameter update can be minimized efficiently from a small number of cost evaluations.

This makes NFT a good default choice for noisy variational workloads:

* It is gradient-free, so it avoids the high measurement cost of full gradient estimation.
* It is efficient compared with many general-purpose gradient-free optimizers.
* Recent scaling studies also rank NFT among the more noise-resilient classical optimizers, while showing that stochastic noise can still create serious scalability challenges for large variational optimization problems; see [Scalability challenges in variational quantum optimization under stochastic noise](https://journals.aps.org/pra/abstract/10.1103/rgyh-8xw8).

#### Requirements and limitations

NFT assumes the variational problem satisfies the following preconditions:

* **Independent parameters:** each ansatz parameter should be independent. Reusing the same parameter across multiple rotation gates is not supported by the standard NFT update rule.

* **Supported gate structure:** the parameterized circuit should be composed of fixed unitary gates and rotation gates of the form `R_j(theta_j) = exp(-i * theta_j * A_j / 2)`, where `A_j^2 = I`.

* **Simple expectation-value cost:** the objective should be a weighted sum of expectation values, `L(theta) = Σ_k w_k <phi_k|U†(theta) H_k U(theta)|phi_k>`.

<Info>
  NFT can improve optimizer robustness, but it does not remove the fundamental sampling and noise-scaling challenges of variational quantum algorithms.
</Info>

### Derivative-free scipy methods

For ansatz / problem combinations that violate NFT's preconditions, or for comparison against established baselines, `ScipyOptimizerOptions` dispatches to any of four derivative-free `scipy.optimize.minimize` methods:

* **`cobyla`:** Constrained Optimization BY Linear Approximation. Trust-region method with linear surrogates.
* **`nelder-mead`:** Downhill simplex. No surrogate model; forgiving on noisy or non-smooth objectives but tends to need more evaluations.
* **`powell`:** Direction-set method that minimizes along conjugate directions.
* **`cobyqa`:** COBYLA's quadratic-approximation successor; usually higher quality per evaluation at modest extra cost.

`maxfev` is exposed as a typed top-level field (default 200). Everything else flows through the free-form `options` dict, validated at construction time against a per-method whitelist:

| Method        | Allowed `options` keys               |
| :------------ | :----------------------------------- |
| `cobyla`      | `rhobeg`, `catol`, `disp`            |
| `nelder-mead` | `xatol`, `fatol`, `adaptive`, `disp` |
| `powell`      | `xtol`, `ftol`, `direc`, `disp`      |
| `cobyqa`      | `rhobeg`, `final_tr_radius`, `disp`  |

Example:

```python theme={null}
from haiqu.sdk.qml import ScipyOptimizerOptions

optimizer_options = ScipyOptimizerOptions(
    method="cobyla",
    maxfev=200,
    options={"rhobeg": 0.5},
)
```

<Info>
  Scipy methods can wander after they find a good point, so Haiqu returns the best-so-far parameters tracked across the optimization, not the final scipy iterate.
</Info>
