# qml.AdaptiveOptimizer¶

class AdaptiveOptimizer(param_steps=10, stepsize=0.5)[source]

Bases: object

Optimizer for building fully trained quantum circuits by adding gates adaptively.

Quantum circuits can be built by adding gates adaptively. The adaptive optimizer implements an algorithm that grows and optimizes an input quantum circuit by selecting and adding gates from a user-defined collection of operators. The algorithm starts by adding all the gates to the circuit and computing the circuit gradients with respect to the gate parameters. The algorithm then retains the gate which has the largest gradient and optimizes its parameter. The process of growing the circuit can be repeated until the computed gradients converge to zero within a given threshold. The optimizer returns the fully trained and adaptively-built circuit. The adaptive optimizer can be used to implement algorithms such as ADAPT-VQE.

Parameters
• param_steps (int) – number of steps for optimizing the parameter of a selected gate

• stepsize (float) – step size for optimizing the parameter of a selected gate

Example

This examples shows an implementation of the ADAPT-VQE algorithm for building an adaptive circuit for the $$\text{H}_3^+$$ cation.

>>> import pennylane as qml
>>> from pennylane import numpy as np


The molecule is defined and the Hamiltonian is computed with:

>>> symbols = ["H", "H", "H"]
>>> geometry = np.array([[0.01076341, 0.04449877, 0.0],
...                      [0.98729513, 1.63059094, 0.0],
...                      [1.87262415, -0.00815842, 0.0]], requires_grad=False)
>>> H, qubits = qml.qchem.molecular_hamiltonian(symbols, geometry, charge = 1)


The collection of gates to grow the circuit adaptively contains all single and double excitations:

>>> n_electrons = 2
>>> singles, doubles = qml.qchem.excitations(n_electrons, qubits)
>>> singles_excitations = [qml.SingleExcitation(0.0, x) for x in singles]
>>> doubles_excitations = [qml.DoubleExcitation(0.0, x) for x in doubles]
>>> operator_pool = doubles_excitations + singles_excitations


An initial circuit preparing the Hartree-Fock state and returning the expectation value of the Hamiltonian is defined:

>>> hf_state = qml.qchem.hf_state(n_electrons, qubits)
>>> dev = qml.device("default.qubit", wires=qubits)
>>> @qml.qnode(dev)
... def circuit():
...     qml.BasisState(hf_state, wires=range(qubits))
...     return qml.expval(H)


The optimizer is instantiated and then the circuit is created and optimized adaptively:

>>> opt = AdaptiveOptimizer()
>>> for i in range(len(operator_pool)):
...     circuit, energy, gradient = opt.step_and_cost(circuit, operator_pool, drain_pool=True)
...     print('Energy:', energy)
...     print(qml.draw(circuit)())
...     print('Largest Gradient:', gradient)
...     print()
...     if gradient < 1e-3:
...         break

Energy: -1.246549938420637
0: ─╭BasisState(M0)─╭G²(0.20)─┤ ╭<𝓗>
1: ─├BasisState(M0)─├G²(0.20)─┤ ├<𝓗>
2: ─├BasisState(M0)─│─────────┤ ├<𝓗>
3: ─├BasisState(M0)─│─────────┤ ├<𝓗>
4: ─├BasisState(M0)─├G²(0.20)─┤ ├<𝓗>
5: ─╰BasisState(M0)─╰G²(0.20)─┤ ╰<𝓗>
Largest Gradient: 0.14399872776755085

Energy: -1.2613740231529604
0: ─╭BasisState(M0)─╭G²(0.20)─╭G²(0.19)─┤ ╭<𝓗>
1: ─├BasisState(M0)─├G²(0.20)─├G²(0.19)─┤ ├<𝓗>
2: ─├BasisState(M0)─│─────────├G²(0.19)─┤ ├<𝓗>
3: ─├BasisState(M0)─│─────────╰G²(0.19)─┤ ├<𝓗>
4: ─├BasisState(M0)─├G²(0.20)───────────┤ ├<𝓗>
5: ─╰BasisState(M0)─╰G²(0.20)───────────┤ ╰<𝓗>
Largest Gradient: 0.1349349562423238

Energy: -1.2743971719780331
0: ─╭BasisState(M0)─╭G²(0.20)─╭G²(0.19)──────────┤ ╭<𝓗>
1: ─├BasisState(M0)─├G²(0.20)─├G²(0.19)─╭G(0.00)─┤ ├<𝓗>
2: ─├BasisState(M0)─│─────────├G²(0.19)─│────────┤ ├<𝓗>
3: ─├BasisState(M0)─│─────────╰G²(0.19)─╰G(0.00)─┤ ├<𝓗>
4: ─├BasisState(M0)─├G²(0.20)────────────────────┤ ├<𝓗>
5: ─╰BasisState(M0)─╰G²(0.20)────────────────────┤ ╰<𝓗>
Largest Gradient: 0.00040841755397108586

 step(circuit, operator_pool[, params_zero]) Update the circuit with one step of the optimizer. step_and_cost(circuit, operator_pool[, …]) Update the circuit with one step of the optimizer, return the corresponding objective function value prior to the step, and return the maximum gradient
step(circuit, operator_pool, params_zero=True)[source]

Update the circuit with one step of the optimizer.

Parameters
• circuit (QNode) – user-defined circuit returning an expectation value

• operator_pool (list[Operator]) – list of the gates to be used for adaptive optimization

• params_zero (bool) – flag to initiate circuit parameters at zero

Returns

the optimized circuit

Return type

QNode

step_and_cost(circuit, operator_pool, drain_pool=False, params_zero=True)[source]

Update the circuit with one step of the optimizer, return the corresponding objective function value prior to the step, and return the maximum gradient

Parameters
• circuit (QNode) – user-defined circuit returning an expectation value

• operator_pool (list[Operator]) – list of the gates to be used for adaptive optimization

• drain_pool (bool) – flag to remove selected gates from the operator pool

• params_zero (bool) – flag to initiate circuit parameters at zero

Returns

the optimized circuit, the objective function output prior to the step, and the largest gradient

Return type

tuple[QNode, float, float]