French
Languages
English
Bengali
French
German
Japanese
Korean
Portuguese
Spanish
Tamil

Note

Cette page a été générée à partir de tutorials/operators/02_gradients_framework.ipynb.

Le framework de gradient de Qiskit

Le framework de gradient permet d’évaluer des gradients quantiques ainsi que des fonctions qui en dépendent. En plus des gradients standards de premier ordre des valeurs d’espérance de la forme

\[\langle\psi\left(\theta\right)|\hat{O}\left(\omega\right)|\psi\left(\theta\right)\rangle\]

Le framework de gradient permet également l’évaluation des gradients du second ordre (Hessiens) ainsi que la Quantum Fisher Information (QFI) d’états quantiques \(|\psi\left(\theta\right)\rangle\).

gradient_framework.png

Imports

[1]:
#General imports
import numpy as np

#Operator Imports
from qiskit.opflow import Z, X, I, StateFn, CircuitStateFn, SummedOp
from qiskit.opflow.gradients import Gradient, NaturalGradient, QFI, Hessian

#Circuit imports
from qiskit.circuit import QuantumCircuit, QuantumRegister, Parameter, ParameterVector, ParameterExpression
from qiskit.circuit.library import EfficientSU2

Gradients de premier ordre

Given a parameterized quantum state \(|\psi\left(\theta\right)\rangle = V\left(\theta\right)|\psi\rangle\) with input state \(|\psi\rangle\), parametrized Ansatz \(V\left(\theta\right)\), and observable \(\hat{O}\left(\omega\right)=\sum_{i}\omega_i\hat{O}_i\), we want to compute…

Gradients relatifs aux paramètres de l’opérateur de mesure

Le gradient d’une espérance relativement à un coefficient de l’observable \(\hat{O}(\omega)\), c’est à dire.

\[\frac{\partial\langle\psi\left(\theta\right)|\hat{O}\left(\omega\right)|\psi\left(\theta\right)\rangle}{\partial\omega_i} = \langle\psi\left(\theta\right)|\hat{O}_i\left(\omega\right)|\psi\left(\theta\right)\rangle.\]

Tout d’abord, nous définissons un état quantique \(|\psi\left (\theta\right)\rangle\) et un Hamiltonien \(H\) agissant comme un observable. Ensuite, l’état et l’Hamiltonien sont intégrés dans un objet définissant la valeur de l’espérance

\[\langle\psi\left(\theta\right)|H|\psi\left(\theta\right)\rangle.\]
[2]:
# Instantiate the quantum state
a = Parameter('a')
b = Parameter('b')
q = QuantumRegister(1)
qc = QuantumCircuit(q)
qc.h(q)
qc.rz(a, q[0])
qc.rx(b, q[0])

# Instantiate the Hamiltonian observable
H = (2 * X) + Z

# Combine the Hamiltonian observable and the state
op = ~StateFn(H) @ CircuitStateFn(primitive=qc, coeff=1.)

# Print the operator corresponding to the expectation value
print(op)
ComposedOp([
  OperatorMeasurement(2.0 * X
  + 1.0 * Z),
  CircuitStateFn(
        ┌───┐┌───────┐┌───────┐
  q0_0: ┤ H ├┤ RZ(a) ├┤ RX(b) ├
        └───┘└───────┘└───────┘
  )
])

Nous construisons une liste des paramètres pour lesquels nous cherchons à évaluer le gradient. Cette liste et la valeur de l’espérance de l’opérateur sont utilisés pour créer l’opérateur qui représente le gradient.

[3]:
params = [a, b]

# Define the values to be assigned to the parameters
value_dict = {a: np.pi / 4, b: np.pi}

# Convert the operator and the gradient target params into the respective operator
grad = Gradient().convert(operator = op, params = params)

# Print the operator corresponding to the Gradient
print(grad)
ListOp([
  SummedOp([
    ComposedOp([
      OperatorMeasurement(Z),
      CircuitStateFn(
            ┌───┐┌─────────────────────────┐┌───────┐┌───┐
      q0_0: ┤ H ├┤ RZ(a + 1.5707963267949) ├┤ RX(b) ├┤ H ├
            └───┘└─────────────────────────┘└───────┘└───┘
      )
    ]),
    -1.0 * ComposedOp([
      OperatorMeasurement(Z),
      CircuitStateFn(
            ┌───┐┌─────────────────────────┐┌───────┐┌───┐
      q0_0: ┤ H ├┤ RZ(a - 1.5707963267949) ├┤ RX(b) ├┤ H ├
            └───┘└─────────────────────────┘└───────┘└───┘
      )
    ]),
    0.5 * ComposedOp([
      OperatorMeasurement(Z),
      CircuitStateFn(
            ┌───┐┌─────────────────────────┐┌───────┐
      q0_0: ┤ H ├┤ RZ(a + 1.5707963267949) ├┤ RX(b) ├
            └───┘└─────────────────────────┘└───────┘
      )
    ]),
    -0.5 * ComposedOp([
      OperatorMeasurement(Z),
      CircuitStateFn(
            ┌───┐┌─────────────────────────┐┌───────┐
      q0_0: ┤ H ├┤ RZ(a - 1.5707963267949) ├┤ RX(b) ├
            └───┘└─────────────────────────┘└───────┘
      )
    ])
  ]),
  SummedOp([
    ComposedOp([
      OperatorMeasurement(Z),
      CircuitStateFn(
            ┌───┐┌───────┐┌─────────────────────────┐┌───┐
      q0_0: ┤ H ├┤ RZ(a) ├┤ RX(b + 1.5707963267949) ├┤ H ├
            └───┘└───────┘└─────────────────────────┘└───┘
      )
    ]),
    -1.0 * ComposedOp([
      OperatorMeasurement(Z),
      CircuitStateFn(
            ┌───┐┌───────┐┌─────────────────────────┐┌───┐
      q0_0: ┤ H ├┤ RZ(a) ├┤ RX(b - 1.5707963267949) ├┤ H ├
            └───┘└───────┘└─────────────────────────┘└───┘
      )
    ]),
    0.5 * ComposedOp([
      OperatorMeasurement(Z),
      CircuitStateFn(
            ┌───┐┌───────┐┌─────────────────────────┐
      q0_0: ┤ H ├┤ RZ(a) ├┤ RX(b + 1.5707963267949) ├
            └───┘└───────┘└─────────────────────────┘
      )
    ]),
    -0.5 * ComposedOp([
      OperatorMeasurement(Z),
      CircuitStateFn(
            ┌───┐┌───────┐┌─────────────────────────┐
      q0_0: ┤ H ├┤ RZ(a) ├┤ RX(b - 1.5707963267949) ├
            └───┘└───────┘└─────────────────────────┘
      )
    ])
  ])
])

Il ne reste alors plus qu’à attribuer des valeurs aux paramètres et à calculer le gradient associé.

[4]:
# Assign the parameters and evaluate the gradient
grad_result = grad.assign_parameters(value_dict).eval()
print('Gradient', grad_result)
Gradient [(-1.414213562373094+0j), (-0.7071067811865474+0j)]

Gradients relatifs aux paramètres de l’état quantique

Gradient d’une espérance relativement à un état paramétré \(|\psi\left(\theta\right)\rangle\), i.e.

\[\frac{\partial\langle\psi\left(\theta\right)|\hat{O}\left(\omega\right)|\psi\left(\theta\right)\rangle}{\partial\theta}\]

relativement à des probabilités d’échantillonnage d’un paramètre d’un état \(|\psi\left(\theta\right)\rangle\), c’est-à-dire

\[\frac{\partial p_i}{\partial\theta} = \frac{\partial\langle\psi\left(\theta\right)|i\rangle\langle i |\psi\left(\theta\right)\rangle}{\partial\theta}.\]

Un gradient relatif à un paramètre d’état quantique peut être évalué de différentes manières, chacune ayant ses avantages et inconvénients.

[5]:
# Define the Hamiltonian with fixed coefficients
H = 0.5 * X - 1 * Z
# Define the parameters w.r.t. we want to compute the gradients
params = [a, b]
# Define the values to be assigned to the parameters
value_dict = { a: np.pi / 4, b: np.pi}

# Combine the Hamiltonian observable and the state into an expectation value operator
op = ~StateFn(H) @ CircuitStateFn(primitive=qc, coeff=1.)
print(op)
ComposedOp([
  OperatorMeasurement(0.5 * X
  - 1.0 * Z),
  CircuitStateFn(
        ┌───┐┌───────┐┌───────┐
  q0_0: ┤ H ├┤ RZ(a) ├┤ RX(b) ├
        └───┘└───────┘└───────┘
  )
])

Calcul par décalage de paramètre

Étant donné un Hermitien \(g\) ayant deux valeurs propres uniques \(\pm r\) qui génère une porte quantique paramétrée

\[G(\theta)= e^{-i\theta g}.\]

Les gradients quantiques peuvent être calculés par décalage des paramètres dépendant de la valeur propre \(r\). Toutes les portes quantique paramétrées « standard » de Qiskit <https://github.com/Qiskit/qiskit-terra/tree/master/qiskit/circuit/library/standard_gates> __ peuvent être décalées d’un angle \(\pi/2\), c’est-à-dire,

\[\frac{\partial\langle\psi\left(\theta\right)|\hat{O}\left(\omega\right)|\psi\left(\theta\right)\rangle}{\partial\theta} = \left(\langle\psi\left(\theta+\pi/2\right)|\hat{O}\left(\omega\right)|\psi\left(\theta+\pi/2\right)\rangle -\langle\psi\left(\theta-\pi/2\right)|\hat{O}\left(\omega\right)|\psi\left(\theta-\pi/2\right)\rangle\right) / 2.\]

Les gradients utilisant une distribution de probabilités peuvent être calculés de manière similaire.

[6]:
# Convert the expectation value into an operator corresponding to the gradient w.r.t. the state parameters using
# the parameter shift method.
state_grad = Gradient(grad_method='param_shift').convert(operator=op, params=params)
# Print the operator corresponding to the gradient
print(state_grad)
# Assign the parameters and evaluate the gradient
state_grad_result = state_grad.assign_parameters(value_dict).eval()
print('State gradient computed with parameter shift', state_grad_result)
ListOp([
  SummedOp([
    0.25 * ComposedOp([
      OperatorMeasurement(Z),
      CircuitStateFn(
            ┌───┐┌─────────────────────────┐┌───────┐┌───┐
      q0_0: ┤ H ├┤ RZ(a + 1.5707963267949) ├┤ RX(b) ├┤ H ├
            └───┘└─────────────────────────┘└───────┘└───┘
      )
    ]),
    -0.25 * ComposedOp([
      OperatorMeasurement(Z),
      CircuitStateFn(
            ┌───┐┌─────────────────────────┐┌───────┐┌───┐
      q0_0: ┤ H ├┤ RZ(a - 1.5707963267949) ├┤ RX(b) ├┤ H ├
            └───┘└─────────────────────────┘└───────┘└───┘
      )
    ]),
    -0.5 * ComposedOp([
      OperatorMeasurement(Z),
      CircuitStateFn(
            ┌───┐┌─────────────────────────┐┌───────┐
      q0_0: ┤ H ├┤ RZ(a + 1.5707963267949) ├┤ RX(b) ├
            └───┘└─────────────────────────┘└───────┘
      )
    ]),
    0.5 * ComposedOp([
      OperatorMeasurement(Z),
      CircuitStateFn(
            ┌───┐┌─────────────────────────┐┌───────┐
      q0_0: ┤ H ├┤ RZ(a - 1.5707963267949) ├┤ RX(b) ├
            └───┘└─────────────────────────┘└───────┘
      )
    ])
  ]),
  SummedOp([
    0.25 * ComposedOp([
      OperatorMeasurement(Z),
      CircuitStateFn(
            ┌───┐┌───────┐┌─────────────────────────┐┌───┐
      q0_0: ┤ H ├┤ RZ(a) ├┤ RX(b + 1.5707963267949) ├┤ H ├
            └───┘└───────┘└─────────────────────────┘└───┘
      )
    ]),
    -0.25 * ComposedOp([
      OperatorMeasurement(Z),
      CircuitStateFn(
            ┌───┐┌───────┐┌─────────────────────────┐┌───┐
      q0_0: ┤ H ├┤ RZ(a) ├┤ RX(b - 1.5707963267949) ├┤ H ├
            └───┘└───────┘└─────────────────────────┘└───┘
      )
    ]),
    -0.5 * ComposedOp([
      OperatorMeasurement(Z),
      CircuitStateFn(
            ┌───┐┌───────┐┌─────────────────────────┐
      q0_0: ┤ H ├┤ RZ(a) ├┤ RX(b + 1.5707963267949) ├
            └───┘└───────┘└─────────────────────────┘
      )
    ]),
    0.5 * ComposedOp([
      OperatorMeasurement(Z),
      CircuitStateFn(
            ┌───┐┌───────┐┌─────────────────────────┐
      q0_0: ┤ H ├┤ RZ(a) ├┤ RX(b - 1.5707963267949) ├
            └───┘└───────┘└─────────────────────────┘
      )
    ])
  ])
])
State gradient computed with parameter shift [(-0.35355339059327356+0j), (0.7071067811865472+0j)]

Combinaison linéaire de gradients de matrices unitaires

Toute matrice unitaire peut être écrite comme \(U\left(\omega\right) = e^{iM\left(\omega\right)}\), où \(M\left(\omega\right)\) est une matrice Hermitienne paramétrée. De plus, les matrices Hermitiennes peuvent elles-mêmes être décomposées en une somme pondérée de matrices de Pauli, c’est-à-dire \(M\left(\omega\right) = \sum_pm_p\left(\omega\right)h_p\)\(m_p\left(\omega\right)\in\mathbb{R}\) et \(h_p=\bigotimes\limits_{j=0}^{n-1}\sigma_{j, p}\) pour \(\sigma_{j, p}\in\left\{I, X, Y, Z\right\}\) et agissant sur le \(j^{\text{\\) qubit. Ainsi, les gradients de \(U_k\left(\omega_k\right)\) sont donnés par \begin{equation*} \frac{\partial U_k\left(\omega_k\right)}{\partial\omega_k} = \sum\limits_pi \frac{\partial m_{k,p}\left(\omega_k\right)}{\partial\omega_k}U_k\left(\omega_k\right)h_{k_p}. \end{equation*}

Combiner cette observation avec une structure de circuit présentée dans Simulating physical phenomena by quantum networks permet de calculer le gradient par l’évaluation d’un seul circuit quantique.

[7]:
# Convert the expectation value into an operator corresponding to the gradient w.r.t. the state parameter using
# the linear combination of unitaries method.
state_grad = Gradient(grad_method='lin_comb').convert(operator=op, params=params)

# Print the operator corresponding to the gradient
print(state_grad)

# Assign the parameters and evaluate the gradient
state_grad_result = state_grad.assign_parameters(value_dict).eval()
print('State gradient computed with the linear combination method', state_grad_result)
ListOp([
  SummedOp([
    0.5 * ComposedOp([
      OperatorMeasurement(ZZ) * 2.0,
      CircuitStateFn(
             ┌───┐          ┌───────┐┌───────┐┌───┐
       q0_0: ┤ H ├────────■─┤ RZ(a) ├┤ RX(b) ├┤ H ├
             ├───┤┌─────┐ │ └─┬───┬─┘└───────┘└───┘
      q81_0: ┤ H ├┤ SDG ├─■───┤ H ├────────────────
             └───┘└─────┘     └───┘
      ) * 0.7071067811865476
    ]),
    -1.0 * ComposedOp([
      OperatorMeasurement(ZZ) * 2.0,
      CircuitStateFn(
             ┌───┐          ┌───────┐┌───────┐
       q0_0: ┤ H ├────────■─┤ RZ(a) ├┤ RX(b) ├
             ├───┤┌─────┐ │ └─┬───┬─┘└───────┘
      q82_0: ┤ H ├┤ SDG ├─■───┤ H ├───────────
             └───┘└─────┘     └───┘
      ) * 0.7071067811865476
    ])
  ]),
  SummedOp([
    0.5 * ComposedOp([
      OperatorMeasurement(ZZ) * 2.0,
      CircuitStateFn(
             ┌───┐┌───────┐┌───┐┌───────┐┌───┐
       q0_0: ┤ H ├┤ RZ(a) ├┤ X ├┤ RX(b) ├┤ H ├
             ├───┤└┬─────┬┘└─┬─┘└─┬───┬─┘└───┘
      q83_0: ┤ H ├─┤ SDG ├───■────┤ H ├───────
             └───┘ └─────┘        └───┘
      ) * 0.7071067811865476
    ]),
    -1.0 * ComposedOp([
      OperatorMeasurement(ZZ) * 2.0,
      CircuitStateFn(
             ┌───┐┌───────┐┌───┐┌───────┐
       q0_0: ┤ H ├┤ RZ(a) ├┤ X ├┤ RX(b) ├
             ├───┤└┬─────┬┘└─┬─┘└─┬───┬─┘
      q84_0: ┤ H ├─┤ SDG ├───■────┤ H ├──
             └───┘ └─────┘        └───┘
      ) * 0.7071067811865476
    ])
  ])
])
State gradient computed with the linear combination method [(-0.3535533905932737+0j), (0.7071067811865472+0j)]

Calcul des gradients par différences finies

Contrairement aux autres méthodes, le calcul de gradients par différences finies permet d’obtenir des estimations numériques plutôt que des expressions analytiques. L’implémentation suivante adopte une approche par différence centrale avec \(\epsilon \ll 1\)

\[\frac{\partial\langle\psi\left(\theta\right)|\hat{O}\left(\omega\right)|\psi\left(\theta\right)\rangle}{\partial\theta} \approx \frac{1}{2\epsilon} \left(\langle\psi\left(\theta+\epsilon\right)|\hat{O}\left(\omega\right)|\psi\left(\theta+\epsilon\right)\rangle - \partial\langle\psi\left(\theta-\epsilon\right)|\hat{O}\left(\omega\right)|\psi\left(\theta-\epsilon\right)\rangle\right).\]

Les gradients utilisant une distribution de probabilités peuvent être calculés de manière similaire.

[8]:
# Convert the expectation value into an operator corresponding to the gradient w.r.t. the state parameter using
# the finite difference method.
state_grad = Gradient(grad_method='fin_diff').convert(operator=op, params=params)

# Print the operator corresponding to the gradient
print(state_grad)

# Assign the parameters and evaluate the gradient
state_grad_result = state_grad.assign_parameters(value_dict).eval()
print('State gradient computed with finite difference', state_grad_result)
ListOp([
  SummedOp([
    250000.0 * ComposedOp([
      OperatorMeasurement(Z),
      CircuitStateFn(
            ┌───┐┌────────────────┐┌───────┐┌───┐
      q0_0: ┤ H ├┤ RZ(a + 1.0e-6) ├┤ RX(b) ├┤ H ├
            └───┘└────────────────┘└───────┘└───┘
      )
    ]),
    -250000.0 * ComposedOp([
      OperatorMeasurement(Z),
      CircuitStateFn(
            ┌───┐┌────────────────┐┌───────┐┌───┐
      q0_0: ┤ H ├┤ RZ(a - 1.0e-6) ├┤ RX(b) ├┤ H ├
            └───┘└────────────────┘└───────┘└───┘
      )
    ]),
    -500000.0 * ComposedOp([
      OperatorMeasurement(Z),
      CircuitStateFn(
            ┌───┐┌────────────────┐┌───────┐
      q0_0: ┤ H ├┤ RZ(a + 1.0e-6) ├┤ RX(b) ├
            └───┘└────────────────┘└───────┘
      )
    ]),
    500000.0 * ComposedOp([
      OperatorMeasurement(Z),
      CircuitStateFn(
            ┌───┐┌────────────────┐┌───────┐
      q0_0: ┤ H ├┤ RZ(a - 1.0e-6) ├┤ RX(b) ├
            └───┘└────────────────┘└───────┘
      )
    ])
  ]),
  SummedOp([
    250000.0 * ComposedOp([
      OperatorMeasurement(Z),
      CircuitStateFn(
            ┌───┐┌───────┐┌────────────────┐┌───┐
      q0_0: ┤ H ├┤ RZ(a) ├┤ RX(b + 1.0e-6) ├┤ H ├
            └───┘└───────┘└────────────────┘└───┘
      )
    ]),
    -250000.0 * ComposedOp([
      OperatorMeasurement(Z),
      CircuitStateFn(
            ┌───┐┌───────┐┌────────────────┐┌───┐
      q0_0: ┤ H ├┤ RZ(a) ├┤ RX(b - 1.0e-6) ├┤ H ├
            └───┘└───────┘└────────────────┘└───┘
      )
    ]),
    -500000.0 * ComposedOp([
      OperatorMeasurement(Z),
      CircuitStateFn(
            ┌───┐┌───────┐┌────────────────┐
      q0_0: ┤ H ├┤ RZ(a) ├┤ RX(b + 1.0e-6) ├
            └───┘└───────┘└────────────────┘
      )
    ]),
    500000.0 * ComposedOp([
      OperatorMeasurement(Z),
      CircuitStateFn(
            ┌───┐┌───────┐┌────────────────┐
      q0_0: ┤ H ├┤ RZ(a) ├┤ RX(b - 1.0e-6) ├
            └───┘└───────┘└────────────────┘
      )
    ])
  ])
])
State gradient computed with finite difference [(-0.35355339057345814+0j), (0.707106781149+0j)]

Gradient naturel

A special type of first order gradient is the natural gradient which has proven itself useful in classical machine learning and is already being studied in the quantum context. This quantity represents a gradient that is “rescaled” with the inverse Quantum Fisher Information matrix (QFI)

\[QFI ^{-1} \frac{\partial\langle\psi\left(\theta\right)|\hat{O}\left(\omega\right)|\psi\left(\theta\right)\rangle}{\partial\theta}.\]

Plutôt que d’inverser la QFI, il est aussi possible d’utiliser un solveur des moindres carrés avec ou sans régularisation pour résoudre

\[QFI x = \frac{\partial\langle\psi\left(\theta\right)|\hat{O}\left(\omega\right)|\psi\left(\theta\right)\rangle}{\partial\theta}.\]

Cette implémentation prend en charge les régularisations d’ordre 1 (Lasso) et d’ordre 2 (Ridge) avec une recherche automatisée d’un hyperparamètre convenable par «L-curve corner search» ainsi que deux types de perturbations des éléments diagonaux de la QFI.

La gradient naturel peut être utilisé en lieu et place du gradient standard avec tout optimiseur basé sur les gradients et/ou avec tout solveur d’équations différentielles ordinaires.

[9]:
# Besides the method to compute the circuit gradients resp. QFI, a regularization method can be chosen:
# `ridge` or `lasso` with automatic parameter search or `perturb_diag_elements` or `perturb_diag`
# which perturb the diagonal elements of the QFI.
nat_grad = NaturalGradient(grad_method='lin_comb', qfi_method='lin_comb_full', regularization='ridge').convert(
                           operator=op, params=params)

# Assign the parameters and evaluate the gradient
nat_grad_result = nat_grad.assign_parameters(value_dict).eval()
print('Natural gradient computed with linear combination of unitaries', nat_grad_result)
Natural gradient computed with linear combination of unitaries [-2.62895551  1.31447775]

Hessiens (Gradients de second ordre)

Quatre types de gradients de second ordre sont pris en charge par le framework de gradient.

  1. Les gradients d’une espérance par rapport à un coefficient de l’observable \(\hat{O}\left(\omega\right)\), i.e. \(\frac{\partial^2\langle\psi\left(\theta\right)|\hat{O}\left(\omega\right)|\psi\left(\theta\right)\rangle}{\partial\omega^2}\)

  2. Les gradients d’une espérance par rapport à un paramètre d’un état quantique \(|\psi\left(\theta\right)\rangle\) parameter, c’est-à-dire \(\frac{\partial^2\langle\psi\left(\theta\right)|\hat{O}\left(\omega\right)|\psi\left(\theta\right)\rangle}{\partial\theta^2}\)

  3. Les gradients de probabilités d’échantillonnage par rapport à un paramètre d’un état quantique \(|\psi\left(\theta\right)\rangle\), c’est-à-dire \(\frac{\partial^2 p_i}{\partial\theta^2} = \frac{\partial^2\langle\psi\left(\theta\right)|i\rangle\langle i|\psi\left(\theta\right)\rangle}{\partial\theta^2}\)

  4. Les gradients d’une espérance par rapport à un paramètre d’un état quantique \(|\psi\left(\theta\right)\rangle\) et à un coefficient de l’observable \(\hat{O}\left(\omega\right)\), c’est-à-dire \(\frac{\partial^2\langle\psi\left(\theta\right)|\hat{O}\left(\omega\right)|\psi\left(\theta\right)\rangle}{\partial\theta\partial\omega}\)

Dans la suite, les exemples traiteront des deux premiers types d’Hessien. Les deux autres peuvent être calculés de manière analogue.

Hessiens relatifs aux paramètres d’un observable

Nous définissons une fois de plus un état quantique \(|\psi\left(\theta\right)\rangle\) et un observable Hamiltonien \(H\). L’état quantique et l’Hamiltonien sont alors intégrés dans un objet définissant la valeur de l’espérance

\[\langle\psi\left(\theta\right)|H|\psi\left(\theta\right)\rangle.\]
[10]:
# Instantiate the Hamiltonian observable
H = X

# Instantiate the quantum state with two parameters
a = Parameter('a')
b = Parameter('b')

q = QuantumRegister(1)
qc = QuantumCircuit(q)
qc.h(q)
qc.rz(a, q[0])
qc.rx(b, q[0])

# Combine the Hamiltonian observable and the state
op = ~StateFn(H) @ CircuitStateFn(primitive=qc, coeff=1.)

Maintenant, il nous est possible de choisir les paramètres pour lesquels nous souhaitons calculer les Hessiens. - Étant donné un tuple, la classe Hessian va calculer l’Hessien pour les deux paramètres de ce tuple. - Étant donné une liste, la classe Hessian va calculer l’Hessien pour toutes les combinaisons possibles de tuples de ces paramètres.

Après avoir assigné des valeurs aux paramètres, l’Hessien peut être calculé.

[11]:
# Convert the operator and the hessian target coefficients into the respective operator
hessian = Hessian().convert(operator = op, params = [a, b])

# Define the values to be assigned to the parameters
value_dict = {a: np.pi / 4, b: np.pi/4}

# Assign the parameters and evaluate the Hessian w.r.t. the Hamiltonian coefficients
hessian_result = hessian.assign_parameters(value_dict).eval()
print('Hessian \n', np.real(np.array(hessian_result)))
Hessian
 [[-7.07106781e-01  0.00000000e+00]
 [ 0.00000000e+00 -5.55111512e-17]]

Hessiens relatifs aux paramètres d’états quantiques

[12]:
# Define parameters
params = [a, b]

# Get the operator object representing the Hessian
state_hess = Hessian(hess_method='param_shift').convert(operator=op, params=params)
# Assign the parameters and evaluate the Hessian
hessian_result = state_hess.assign_parameters(value_dict).eval()
print('Hessian computed using the parameter shift method\n', (np.array(hessian_result)))

# Get the operator object representing the Hessian
state_hess = Hessian(hess_method='lin_comb').convert(operator=op, params=params)
# Assign the parameters and evaluate the Hessian
hessian_result = state_hess.assign_parameters(value_dict).eval()
print('Hessian computed using the linear combination of unitaries method\n', (np.array(hessian_result)))

# Get the operator object representing the Hessian using finite difference
state_hess = Hessian(hess_method='fin_diff').convert(operator=op, params=params)
# Assign the parameters and evaluate the Hessian
hessian_result = state_hess.assign_parameters(value_dict).eval()
print('Hessian computed with finite difference\n', (np.array(hessian_result)))
Hessian computed using the parameter shift method
 [[-7.07106781e-01+0.j  0.00000000e+00+0.j]
 [ 0.00000000e+00+0.j -5.55111512e-17+0.j]]
Hessian computed using the linear combination of unitaries method
 [[-7.07106781e-01+0.j  0.00000000e+00+0.j]
 [ 0.00000000e+00+0.j  5.60000000e-17+0.j]]
Hessian computed with finite difference
 [[-7.07122803e-01+0.j  3.05175781e-05+0.j]
 [ 3.05175781e-05+0.j -6.10351562e-05+0.j]]

Quantum Fisher Information (QFI)

La Quantum Fisher Information est un tenseur métrique permettant d’évaluer la capacité de représentation d’un état quantique paramétré \(|\psi\left(\theta\right)\rangle = V\left(\theta\right)|\psi\rangle\) pour un état initial \(|\psi\rangle\) et une Ansatz paramétrée \(V\left(\theta\right)\).

Les coefficients de la QFI pour un état pur sont donnés par

\[QFI_{kl} = 4 * \text{Re}\left[\langle\partial_k\psi|\partial_l\psi\rangle-\langle\partial_k\psi|\psi\rangle\langle\psi|\partial_l\psi\rangle \right].\]

Circuits associés à une QFI

L’évaluation de la QFI associée à un état quantique généré par un circuit quantique paramétré peut être réalisée de différentes manières.

Calcul complet de la QFI par combinaison linéaire

To compute the full QFI, we use a working qubit as well as intercepting controlled gates. See e.g. Variational ansatz-based quantum simulation of imaginary time evolution.

[13]:
# Wrap the quantum circuit into a CircuitStateFn
state = CircuitStateFn(primitive=qc, coeff=1.)

# Convert the state and the parameters into the operator object that represents the QFI
qfi = QFI(qfi_method='lin_comb_full').convert(operator=state, params=params)
# Define the values for which the QFI is to be computed
values_dict = {a: np.pi / 4, b: 0.1}

# Assign the parameters and evaluate the QFI
qfi_result = qfi.assign_parameters(values_dict).eval()
print('full QFI \n', np.real(np.array(qfi_result)))
full QFI
 [[ 1.0000000e+00 -2.0659798e-16]
 [-2.0659798e-16  5.0000000e-01]]

Approximations diagonale et diagonale par blocs

Une approximation diagonale ou diagonale par blocs de la QFI peut être calculée sans utiliser de qubits de travail supplémentaires. Cette implémentation requiert de calculer la décomposition en rotations de Pauli et en portes quantiques non-paramétrées.

[14]:
# Convert the state and the parameters into the operator object that represents the QFI
# and set the approximation to 'block_diagonal'
qfi = QFI('overlap_block_diag').convert(operator=state, params=params)

# Assign the parameters and evaluate the QFI
qfi_result = qfi.assign_parameters(values_dict).eval()
print('Block-diagonal QFI \n', np.real(np.array(qfi_result)))

# Convert the state and the parameters into the operator object that represents the QFI
# and set the approximation to 'diagonal'
qfi = QFI('overlap_diag').convert(operator=state, params=params)

# Assign the parameters and evaluate the QFI
qfi_result = qfi.assign_parameters(values_dict).eval()
print('Diagonal QFI \n', np.real(np.array(qfi_result)))
Block-diagonal QFI
 [[1.  0. ]
 [0.  0.5]]
Diagonal QFI
 [[1.  0. ]
 [0.  0.5]]

Exemple d’application: VQE avec optimisation par gradient

Imports supplémentaires

[15]:
# Execution Imports
from qiskit import Aer
from qiskit.utils import QuantumInstance

# Algorithm Imports
from qiskit.algorithms import VQE
from qiskit.algorithms.optimizers import CG

Le framework de gradient peut également être utilisé par une instance de VQE utilisant un gradient pour son optimisation. Tout d’abord, l’Hamiltonien et la fonction d’onde sont initialisés.

[16]:
from qiskit.opflow import I, X, Z
from qiskit.circuit import QuantumCircuit, ParameterVector
from scipy.optimize import minimize

# Instantiate the system Hamiltonian
h2_hamiltonian = -1.05 * (I ^ I) + 0.39 * (I ^ Z) - 0.39 * (Z ^ I) - 0.01 * (Z ^ Z) + 0.18 * (X ^ X)

# This is the target energy
h2_energy = -1.85727503

# Define the Ansatz
wavefunction = QuantumCircuit(2)
params = ParameterVector('theta', length=8)
it = iter(params)
wavefunction.ry(next(it), 0)
wavefunction.ry(next(it), 1)
wavefunction.rz(next(it), 0)
wavefunction.rz(next(it), 1)
wavefunction.cx(0, 1)
wavefunction.ry(next(it), 0)
wavefunction.ry(next(it), 1)
wavefunction.rz(next(it), 0)
wavefunction.rz(next(it), 1)

# Define the expectation value corresponding to the energy
op = ~StateFn(h2_hamiltonian) @ StateFn(wavefunction)

Il nous est maintenant possible de définir une QuantumInstance sur laquelle VQE s’exécutera ainsi que choisir si l’instance de VQE doit utiliser un Gradient ou un NaturalGradient.

[17]:
grad = Gradient(grad_method='lin_comb')

qi_sv = QuantumInstance(Aer.get_backend('aer_simulator_statevector'),
                        shots=1,
                        seed_simulator=2,
                        seed_transpiler=2)

#Conjugate Gradient algorithm
optimizer = CG(maxiter=50)

# Gradient callable
vqe = VQE(wavefunction, optimizer=optimizer, gradient=grad, quantum_instance=qi_sv)

result = vqe.compute_minimum_eigenvalue(h2_hamiltonian)
print('Result:', result.optimal_value, 'Reference:', h2_energy)
Result: -0.8800000000000001 Reference: -1.85727503
[18]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright

Version Information

Qiskit SoftwareVersion
QiskitNone
Terra0.17.4
Aer0.8.2
IgnisNone
AquaNone
IBM Q ProviderNone
System information
Python3.8.8 (default, Apr 13 2021, 12:59:45) [Clang 10.0.0 ]
OSDarwin
CPUs2
Memory (Gb)12.0
Fri May 28 09:42:47 2021 EDT

This code is a part of Qiskit

© Copyright IBM 2017, 2021.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.

[ ]: