> ## 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 Quantum Optimization

> Variational quantum optimization with the NFT or scipy optimizers.

#### 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\_packing=False, pack\_size=None, use\_session=False, job\_name=None)

Optimize a variational quantum circuit to minimize the expectation value of input observable.

Defaults to the NFT (Nakanishi-Fujii-Todo) optimizer, a gradient-free optimizer
designed for variational quantum algorithms ([https://arxiv.org/abs/1903.12166](https://arxiv.org/abs/1903.12166)).
Pass a `ScipyOptimizerOptions` instance as `optimizer_options` to dispatch
to any derivative-free `scipy.optimize.minimize` method instead (`cobyla`,
`nelder-mead`, `powell`, `cobyqa`).

* **Parameters:**
  * **problem** ([*VariationalProblem*](#haiqu.sdk.qml.problem.VariationalProblem)) -- problem instance containing the ansatz circuit and observable.
  * **shots** (*int*) -- Number of shots per circuit evaluation. Defaults to 1000.
  * **device** (*DeviceModel* *|* *None*) -- Device to execute on. If specified, device\_id is ignored.
  * **device\_id** (*str* *|* *None*) -- ID of the device to execute on. Defaults to None.
  * **options** (*dict* *|* *None*) -- Additional device options.
  * **initial\_parameters** (*list* \*\[\**float* *]*  *|* *None*) -- Initial parameter values. Cannot be used together with seed.
    If neither is provided, random parameters in \[-0.1π, 0.1π] are generated.
  * **seed** (*int* *|* *None*) -- Random seed for reproducible generation of initial parameters from a uniform
    distribution in \[-0.1π, 0.1π]. Cannot be used together with initial\_parameters.
  * **optimizer\_options** (*OptimizerOptions* *|* *None*) -- Configuration for the optimizer. If None, defaults to
    NFTOptimizerOptions(). Pass a ScipyOptimizerOptions instance to use any
    derivative-free scipy method (cobyla, nelder-mead, powell, cobyqa) instead.
  * **use\_mitigation** (*bool*) -- Whether to use error mitigation techniques. Defaults to False.
  * **use\_packing** (*bool*) -- Whether to use circuit packing for efficient device utilization. Defaults to False.
    **Warning:** Experimental — packing replicates circuits on unused device qubits
    to run multiple copies in parallel, which may increase errors for deeper input circuits.
    For example, a 4-qubit circuit with pack\_size=2 and 1000 shots runs two copies
    in parallel with 500 shots each, yielding 1000 shots of results while only paying
    for 500 shot executions on the QPU — a 2x cost saving.
  * **pack\_size** (*int* *|* *None*) -- Number of circuit copies to pack onto the device. Must be >= 2.
    Only valid when `use_packing=True`. If `None` (default), the backend will
    pack into at most 2/3 of the device qubits.
  * **use\_session** (*bool*) -- Whether to use IBM Qiskit Runtime Session for execution. Defaults to False.
  * **job\_name** (*str* *|* *None*) -- The name for the job. If `None` (default), a name will be automatically generated.
* **Returns:**
  Job handle to track optimization progress and retrieve results.
  : Call `job.result()` to retrieve a `VariationalResult` exposing `optimal_parameters` (`list[float]`),
  `min_loss` (`float`), and `loss_history` (`list[float]`). `job.info` exposes auxiliary metadata
  (`loss_history`, `qpu_cost`, `session_cost`).
  Use `job.progress()` for live status updates and `help(job.result)` for the full description of result
  and `info` contents.
* **Return type:**
  VariationalJobModel

#### Examples

Default optimizer settings:

```python theme={null}
>>> from qiskit import QuantumCircuit
>>> from qiskit.circuit import Parameter
>>> from qiskit.quantum_info import SparsePauliOp
>>> from haiqu.sdk.qml import VariationalProblem
>>> theta = Parameter('θ')
>>> ansatz = QuantumCircuit(2)
>>> ansatz.ry(theta, 0)
>>> ansatz.cx(0, 1)
>>> obs = SparsePauliOp.from_list([("ZZ", 1.0), ("XI", 0.5)])
>>> problem = VariationalProblem(ansatz, obs)
>>> job = haiqu.variational_optimization(problem, shots=1000, device_id="aer_simulator")
>>> result = job.result()
>>> print(result.min_loss)
```

Custom optimizer settings:

```python theme={null}
>>> from haiqu.sdk.qml import NFTOptimizerOptions
>>> optimizer = NFTOptimizerOptions(maxfev=2048, maxiter=100)
>>> job = haiqu.variational_optimization(problem, shots=1000, device_id="aer_simulator", optimizer_options=optimizer)
```

Scipy COBYLA instead of NFT:

```python theme={null}
>>> from haiqu.sdk.qml import ScipyOptimizerOptions
>>> optimizer = ScipyOptimizerOptions(method="cobyla", maxiter=200, options={"rhobeg": 0.5})
>>> job = haiqu.variational_optimization(problem, shots=1000, device_id="aer_simulator", optimizer_options=optimizer)
```

### *class* haiqu.sdk.qml.problem.VariationalProblem(ansatz, observable)

A variational quantum optimization problem definition.

Bundles a parameterized ansatz circuit with an observable to minimize.

* **Parameters:**
  * **ansatz** (*QuantumCircuit*) -- Parameterized quantum circuit.
  * **observable** (*SparsePauliOp*) -- The observable as a SparsePauliOp.

- **Raises:**
  * **TypeError** -- If inputs are wrong types.
  * **ValueError** -- If ansatz is not parameterized.

### Example

```python theme={null}
>>> from qiskit import QuantumCircuit
>>> from qiskit.circuit import Parameter
>>> from qiskit.quantum_info import SparsePauliOp
>>> from haiqu.sdk.qml import VariationalProblem
>>> theta = Parameter('θ')
>>> ansatz = QuantumCircuit(2)
>>> ansatz.ry(theta, 0)
>>> ansatz.cx(0, 1)
>>> obs = SparsePauliOp.from_list([("ZZ", 1.0), ("XI", 0.5)])
>>> problem = VariationalProblem(ansatz, obs)
```

#### SEE ALSO

[`haiqu.sdk.quantum_haiqu.Haiqu.variational_optimization()`](../index.md#haiqu.sdk.quantum_haiqu.Haiqu.variational_optimization): Submit problem to Haiqu cloud.

### *class* haiqu.sdk.qml.optimizer.NFTOptimizerOptions(\*, type='nft', randomized\_order=False, reset\_interval=32, maxfev=1024, maxiter=500, eps=1e-32)

Configuration options for the NFT (Nakanishi-Fujii-Todo) optimizer.

The NFT algorithm is a gradient-free optimizer designed for variational quantum
algorithms. For detailed information about the algorithm, see the paper:
[https://arxiv.org/abs/1903.12166](https://arxiv.org/abs/1903.12166)

Preconditions:
: NFT requires the following conditions on the parameterized quantum circuit:

<br />

1. Parameters must be independent: each parameter must appear in exactly one
   gate (no reusing the same parameter across multiple gates).
2. Parameterized gates must be rotations of the form `R_j(θ_j) = exp(-i*θ_j*A_j/2)`
   where `A_j² = I` (e.g., RX, RY, RZ gates satisfy this).
3. The cost function must be a sum of expectation values of Hermitian operators:
   `L(θ) = Σ_k w_k ⟨ψ_k|U†(θ) H_k U(θ)|ψ_k⟩`.

Scaling:
: NFT updates one parameter at a time. Each full sweep through N parameters
requires ≥2N function evaluations (depending on reset\_interval).

* **Parameters:**
  * **randomized\_order** (*bool*) -- If True, shuffles the order of parameters to update
    each lap (full sweep through all parameters). Default: False.
  * **reset\_interval** (*int*) -- How often to reset the recycled loss value.
    Set to 0 to disable resets. Default: 32.
  * **maxfev** (*int*) -- Maximum number of function evaluations (circuit executions).
    Optimization stops when this limit is reached. Default: 1024.
  * **maxiter** (*int*) -- Maximum number of iterations (parameter updates). Default: 500.
  * **eps** (*float*) -- Small epsilon value to avoid division by zero in the analytic
    solution. Default: 1e-32.
  * **type** (*Literal* *\[* *'nft'* *]*)

### Notes

Stopping criterion: Optimization stops when **either** maxfev or maxiter
is reached, whichever comes first.

Function evaluations per iteration: Each iteration uses 2-3 function
evaluations. The very first iteration and the first iteration of each
reset interval use 3 evaluations. Subsequent iterations reuse the
previous optimal value, requiring only 2 evaluations.

### Example

```python theme={null}
>>> from haiqu.sdk.qml import NFTOptimizerOptions
>>> optimizer = NFTOptimizerOptions(maxfev=2048, maxiter=100)
```

### *class* haiqu.sdk.qml.optimizer.ScipyOptimizerOptions(\*, type='scipy', method, maxiter=500, options=\<factory>)

Configuration for any derivative-free `scipy.optimize.minimize` method.

The Haiqu backend wraps `scipy.optimize.minimize` for the four supported
derivative-free methods. `maxiter` is the only option that is universal
across all of them, so it gets a typed slot; everything else goes in the
free-form `options` dict and is validated against a per-method whitelist
at construction time, mirroring what `scipy.optimize.minimize` accepts.

### - \`\`cobyla\`\`

Constrained Optimization BY Linear Approximation. Trust-region
method with linear surrogates; robust default on noisy expectation values.

### - \`\`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;
often fast on well-conditioned problems.

### - \`\`cobyqa\`\`

COBYLA's quadratic-approximation successor; typically higher
quality per evaluation than COBYLA at modest extra cost.

* **Parameters:**
  * **method** (*Literal* *\[* *'cobyla'* *,*  *'nelder-mead'* *,*  *'powell'* *,*  *'cobyqa'* *]*) -- scipy method name. One of `cobyla`, `nelder-mead`, `powell`, `cobyqa`.
  * **maxiter** (*int*) -- Maximum number of iterations. Default: 500. Scipy interprets this
    slightly differently per method; see the per-method scipy docs for
    whether it caps iterations or function evaluations.
  * **options** (*Dict* \*\[\**str* *,* *Any* *]*) --

    Per-method options forwarded to `scipy.optimize.minimize`.
    Allowed keys are validated at construction time; an unknown key raises
    `ValueError`.

    Per-method allowed keys:

    * `cobyla`: `rhobeg`, `catol`, `disp`
    * `nelder-mead`: `xatol`, `fatol`, `adaptive`, `maxfev`, `disp`
    * `powell`: `xtol`, `ftol`, `maxfev`, `direc`, `disp`
    * `cobyqa`: `rhobeg`, `final_tr_radius`, `disp`

    `maxiter` is intentionally excluded; pass it via the top-level field.
  * **type** (*Literal* *\[* *'scipy'* *]*)

### Notes

Final result selection: scipy methods can wander after they have found a
good point. Haiqu therefore returns the best-so-far parameters tracked
across the optimization, not the final scipy iterate.

Function-evaluation cap: `nelder-mead` and `powell` accept `maxfev`
in their options dict in addition to `maxiter`. `cobyla` and `cobyqa`
do not expose a separate function-evaluation cap.

### Example

```python theme={null}
>>> from haiqu.sdk.qml import ScipyOptimizerOptions
>>> ScipyOptimizerOptions(method="cobyla", maxiter=200, options={"rhobeg": 0.5})
>>> ScipyOptimizerOptions(method="powell", maxiter=500, options={"xtol": 1e-6})
>>> ScipyOptimizerOptions(method="nelder-mead", options={"adaptive": True})
```
