1.12. Numerics
The numerical intensive calculations are encapsulated by the OperatorMatrix
class, which can encode quantum states or operators in a matrix representation, meaning that each object must have two dimensions. The class was already introduced earlier, but now we would like to illuminate the computational efficiency for advanced users.
A frequently used and computationally complex operation is the calculation of the matrix potential, which is used to solve partial differential equations or first order like Schroedingers equation or a master equation in lindblad form. For example an \(X_{\pi/2}\) rotation on the bloch sphere is given by a unitary:
which can be calculated using several numeric methods. The matrix exponential and its frechet derivative are usually calculated together, for example by spectral decomposition:
[1]:
import numpy as np
from qopt.matrix import DenseOperator
sigma_x = DenseOperator.pauli_x()
print(sigma_x.dexp(tau=.25j * np.pi,
method='spectral',
compute_expm=True,
direction=sigma_x))
(DenseOperator with data:
array([[7.07106781e-01-2.22044605e-16j, 5.55111512e-17+7.07106781e-01j],
[5.55111512e-17+7.07106781e-01j, 7.07106781e-01+0.00000000e+00j]]), DenseOperator with data:
array([[-5.55360367e-01-1.14514159e-16j, 2.88907583e-16+5.55360367e-01j],
[ 2.75869449e-16+5.55360367e-01j, -5.55360367e-01-1.88672737e-16j]]))
Aternatively, the scipy method expm_frechet
can be used:
[2]:
print(sigma_x.dexp(tau=.25j * np.pi,
method='Frechet',
compute_expm=True,
direction=sigma_x))
(DenseOperator with data:
array([[0.70710678+0.j , 0. +0.70710678j],
[0. +0.70710678j, 0.70710678+0.j ]]), DenseOperator with data:
array([[-0.55536037+0.j , 0. +0.55536037j],
[ 0. +0.55536037j, -0.55536037+0.j ]]))
The derivative can also be calculated by finite differences using the method approx:
[3]:
print(sigma_x.dexp(tau=.25j * np.pi,
method='approx',
compute_expm=True,
direction=sigma_x))
(DenseOperator with data:
array([[7.07106781e-01-2.22044605e-16j, 5.55111512e-17+7.07106781e-01j],
[5.55111512e-17+7.07106781e-01j, 7.07106781e-01+0.00000000e+00j]]), DenseOperator with data:
array([[-5.55359092e-01+2.22044605e-06j, -5.55111512e-07+5.55362423e-01j],
[-5.55111512e-07+5.55362423e-01j, -5.55361312e-01+0.00000000e+00j]]))
And we can also explicitly calculate a number of terms in the expansion serias of the exponential function:
[4]:
print(sigma_x.dexp(tau=.25j * np.pi,
method='first_order',
compute_expm=True,
direction=sigma_x))
(DenseOperator with data:
array([[7.07106781e-01-2.22044605e-16j, 5.55111512e-17+7.07106781e-01j],
[5.55111512e-17+7.07106781e-01j, 7.07106781e-01+0.00000000e+00j]]), DenseOperator with data:
array([[0.+0.j , 0.+0.78539816j],
[0.+0.78539816j, 0.+0.j ]]))
[5]:
print(sigma_x.dexp(tau=.25j * np.pi,
method='second_order',
compute_expm=True,
direction=sigma_x))
(DenseOperator with data:
array([[7.07106781e-01-2.22044605e-16j, 5.55111512e-17+7.07106781e-01j],
[5.55111512e-17+7.07106781e-01j, 7.07106781e-01+0.00000000e+00j]]), DenseOperator with data:
array([[-0.61685028+0.j , 0. +0.78539816j],
[ 0. +0.78539816j, -0.61685028+0.j ]]))
[6]:
print(sigma_x.dexp(tau=.25j * np.pi,
method='third_order',
compute_expm=True,
direction=sigma_x))
(DenseOperator with data:
array([[7.07106781e-01-2.22044605e-16j, 5.55111512e-17+7.07106781e-01j],
[5.55111512e-17+7.07106781e-01j, 7.07106781e-01+0.00000000e+00j]]), DenseOperator with data:
array([[-0.61685028+0.j , 0. +0.54316163j],
[ 0. +0.54316163j, -0.61685028+0.j ]]))
[6]: