r"""
Contains methods to functions to create and/or manipulate quantum operators
.. currentmodule:: quanguru.QuantumToolbox.operators
Functions
---------
.. autosummary::
identity
.. autosummary::
number
destroy
create
.. autosummary::
sigmaz
sigmay
sigmax
sigmap
sigmam
.. autosummary::
Jp
Jm
Jx
Jy
Jz
Js
.. autosummary::
displacement
squeeze
.. autosummary::
parityEXP
paritySUM
.. autosummary::
compositeOp
operatorPow
.. |c| unicode:: U+2705
.. |x| unicode:: U+274C
.. |w| unicode:: U+2000
======================= ================== ============== ================ ===============
**Function Name** **Docstrings** **Examples** **Unit Tests** **Tutorials**
======================= ================== ============== ================ ===============
`identity` |w| |w| |w| |c| |w| |w| |c| |w| |w| |c| |w| |w| |x|
`number` |w| |w| |w| |c| |w| |w| |c| |w| |w| |c| |w| |w| |x|
`destroy` |w| |w| |w| |c| |w| |w| |c| |w| |w| |c| |w| |w| |x|
`create` |w| |w| |w| |c| |w| |w| |c| |w| |w| |c| |w| |w| |x|
`sigmaz` |w| |w| |w| |c| |w| |w| |c| |w| |w| |c| |w| |w| |x|
`sigmay` |w| |w| |w| |c| |w| |w| |c| |w| |w| |c| |w| |w| |x|
`sigmax` |w| |w| |w| |c| |w| |w| |c| |w| |w| |c| |w| |w| |x|
`sigmap` |w| |w| |w| |c| |w| |w| |c| |w| |w| |c| |w| |w| |x|
`sigmam` |w| |w| |w| |c| |w| |w| |c| |w| |w| |c| |w| |w| |x|
`Jp` |w| |w| |w| |c| |w| |w| |c| |w| |w| |c| |w| |w| |x|
`Jm` |w| |w| |w| |c| |w| |w| |c| |w| |w| |c| |w| |w| |x|
`Jx` |w| |w| |w| |c| |w| |w| |c| |w| |w| |x| |w| |w| |x|
`Jy` |w| |w| |w| |c| |w| |w| |c| |w| |w| |x| |w| |w| |x|
`Jz` |w| |w| |w| |c| |w| |w| |c| |w| |w| |c| |w| |w| |x|
`Js` |w| |w| |w| |c| |w| |w| |c| |w| |w| |c| |w| |w| |x|
`displacement` |w| |w| |w| |c| |w| |w| |c| |w| |w| |c| |w| |w| |x|
`squeeze` |w| |w| |w| |c| |w| |w| |c| |w| |w| |x| |w| |w| |x|
`parityEXP` |w| |w| |w| |c| |w| |w| |c| |w| |w| |x| |w| |w| |x|
`paritySUM` |w| |w| |w| |c| |w| |w| |c| |w| |w| |x| |w| |w| |x|
`compositeOp` |w| |w| |w| |c| |w| |w| |c| |w| |w| |x| |w| |w| |x|
`operatorPow` |w| |w| |w| |c| |w| |w| |c| |w| |w| |x| |w| |w| |x|
======================= ================== ============== ================ ===============
""" #pylint:disable=too-many-lines
from typing import Callable
import scipy.sparse as sp # type: ignore
import scipy.linalg as linA # type: ignore
from scipy.sparse.linalg import expm # type: ignore
import numpy as np # type: ignore
from .linearAlgebra import tensorProd, _matPower
from .customTypes import Matrix #pylint: disable=relative-beyond-top-level
# do not delete these
# from typing import Callable, TypeVar
# from numpy import ndarray
# from scipy.sparse import spmatrix
# These type aliases are used in type hinting of below methods
# Matrix = TypeVar('Matrix', spmatrix, ndarray) # Type which is either spmatrix or nparray (created using TypeVar)
[docs]def number(dimension: int, sparse: bool = True) -> Matrix:
r"""
Creates the (bosonic) number :math:`\hat{n} := a^{\dagger}a` operator (in Fock basis).
Parameters
----------
dimension : int
dimension of the Hilbert space
sparse : bool
if True(False), the returned Matrix type will be sparse(array)
Returns
-------
Matrix
number operator
Examples
--------
>>> number(dimension=3, sparse=False)
[[0 0 0]
[0 1 0]
[0 0 2]]
>>> print(number(3))
(0, 0) 0
(1, 1) 1
(2, 2) 2
"""
data = list(range(dimension))
rows = range(0, dimension)
columns = range(0, dimension)
n = sp.csc_matrix((data, (rows, columns)), shape=(dimension, dimension))
return n if sparse else n.toarray()
[docs]def destroy(dimension: int, sparse: bool = True) -> Matrix:
r"""
Creates the bosonic `annihilation` :math:`\hat{a}` operator (in Fock basis).
Parameters
----------
dimension : int
dimension of the Hilbert space
sparse : bool
if True(False), the returned Matrix type will be sparse(array)
Returns
-------
Matrix
bosonic `annihilation` operator
Examples
--------
>>> print(destroy(dimension=3))
(0, 1) 1.0
(1, 2) 1.4142135623730951
>>> destroy(3, sparse=False)
[[0. 1. 0. ]
[0. 0. 1.41421356]
[0. 0. 0. ]]
"""
data = [np.sqrt(i+1) for i in range(dimension-1)]
rows = range(0, dimension-1)
columns = range(1, dimension)
n = sp.csc_matrix((data, (rows, columns)), shape=(dimension, dimension))
return n if sparse else n.toarray()
[docs]def create(dimension: int, sparse: bool = True) -> Matrix:
r"""
Creates the bosonic `creation` :math:`\hat{a}^{\dagger}` operator (in Fock basis).
Parameters
----------
dimension : int
dimension of the Hilbert space
sparse : bool
if True(False), the returned Matrix type will be sparse(array)
Returns
-------
Matrix
bosonic `creation` operator
Examples
--------
>>> print(create(3))
(1, 0) 1.0
(2, 1) 1.4142135623730951
>>> create(3, sparse=False)
[[0. 0. 0. ]
[1. 0. 0. ]
[0. 1.41421356 0. ]]
"""
data = [np.sqrt(i+1) for i in range(dimension-1)]
rows = range(1, dimension)
columns = range(0, dimension-1)
n = sp.csc_matrix((data, (rows, columns)), shape=(dimension, dimension))
return n if sparse else n.toarray()
[docs]def identity(dimension: int, sparse: bool = True) -> Matrix:
r"""
Creates the identity operator :math:`\mathbb{I}`.
Parameters
----------
dimension : int
dimension of the Hilbert space
sparse : bool
if True(False), the returned Matrix type will be sparse(array)
Returns
-------
Matrix
identity operator
Examples
--------
>>> print(identity(3))
(0, 0) 1.0
(1, 1) 1.0
(2, 2) 1.0
>>> identity(3, sparse=False)
[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
"""
return sp.identity(dimension, format="csc") if sparse else np.identity(dimension)
[docs]def sigmaz(sparse: bool = True) -> Matrix:
r"""
Creates the `Pauli` (sigma z) :math:`\hat{\sigma}_{z} := \begin{bmatrix} 1, \ \ 0 \\ 0, -1 \end{bmatrix}` operator.
Parameters
----------
sparse : bool
if True(False), the returned Matrix type will be sparse(array)
Returns
-------
Matrix
`Pauli` sigma z operator
Examples
--------
>>> sigmaz(sparse=False)
[[ 1 0]
[ 0 -1]]
>>> print(sigmaz())
(0, 0) 1
(1, 1) -1
"""
data = [1, -1]
rows = [0, 1]
columns = [0, 1]
n = sp.csc_matrix((data, (rows, columns)), shape=(2, 2))
return n if sparse else n.toarray()
[docs]def sigmay(sparse: bool = True) -> Matrix:
r"""
Creates the `Pauli` (sigma y) :math:`\hat{\sigma}_{y} := \begin{bmatrix} 0, -i \\ i,\ \ 0 \end{bmatrix}` operator.
Parameters
----------
sparse : bool
if True(False), the returned Matrix type will be sparse(array)
Returns
-------
Matrix
`Pauli` sigma y operator
Examples
--------
>>> sigmay(sparse=False)
[[0.+0.j 0.-1.j]
[0.+1.j 0.+0.j]]
>>> print(sigmay())
(1, 0) 1j
(0, 1) (-0-1j)
"""
data = [-1j, 1j]
rows = [0, 1]
columns = [1, 0]
n = sp.csc_matrix((data, (rows, columns)), shape=(2, 2))
return n if sparse else n.toarray()
[docs]def sigmax(sparse: bool = True) -> Matrix:
r"""
Creates the `Pauli` (sigma x) :math:`\hat{\sigma}_{x} := \begin{bmatrix} 0, 1 \\ 1, 0 \end{bmatrix}` operator.
Parameters
----------
sparse : bool
if True(False), the returned Matrix type will be sparse(array)
Returns
-------
Matrix
`Pauli` sigma x operator
Examples
--------
>>> sigmax(sparse=False)
[[0 1]
[1 0]]
>>> print(sigmax())
(1, 0) 1
(0, 1) 1
"""
data = [1, 1]
rows = [0, 1]
columns = [1, 0]
n = sp.csc_matrix((data, (rows, columns)), shape=(2, 2))
return n if sparse else n.toarray()
[docs]def sigmap(sparse: bool = True) -> Matrix:
r"""
Creates the `Pauli` (sigma +) :math:`\hat{\sigma}_{+} := \frac{1}{2}(\hat{\sigma}_{x} +i\hat{\sigma}_{y}) =
\begin{bmatrix} 0, 1 \\ 0, 0 \end{bmatrix}` operator.
Parameters
----------
sparse : bool
if True(False), the returned Matrix type will be sparse(array)
Returns
-------
Matrix
`Pauli` sigma + operator
Examples
--------
>>> sigmap(sparse=False)
[[0 1]
[0 0]]
>>> print(sigmap())
(0, 1) 1
"""
data = [1]
rows = [0]
columns = [1]
n = sp.csc_matrix((data, (rows, columns)), shape=(2, 2))
return n if sparse else n.toarray()
[docs]def sigmam(sparse: bool = True) -> Matrix:
r"""
Creates the `Pauli` (sigma -) :math:`\hat{\sigma}_{-} := \frac{1}{2}(\hat{\sigma}_{x} - i\hat{\sigma}_{y}) =
\begin{bmatrix} 0, 0 \\ 1, 0 \end{bmatrix}` operator.
Parameters
----------
sparse : bool
if True(False), the returned Matrix type will be sparse(array)
Returns
-------
Matrix
`Pauli` sigma - operator
Examples
--------
>>> sigmam(sparse=False)
[[0 0]
[1 0]]
>>> print(sigmam())
(1, 0) 1
"""
data = [1]
rows = [1]
columns = [0]
n = sp.csc_matrix((data, (rows, columns)), shape=(2, 2))
return n if sparse else n.toarray()
[docs]def Jp(j: float, sparse: bool = True, isDim: bool = False) -> Matrix:
r"""
Creates the angular momentum (spin) `raising` operator
:math:`\hat{J}_{+} := \frac{1}{2}(\hat{J}_{x}+i\hat{J}_{y})` for a given spin quantum number j.
NOTE This is a direct matrix construction, i.e. it does not use the Jx and Jy functions, and this function is used
in Jx and Jy implementations
Parameters
----------
j : int or float
integer or half-integer spin quantum number, or the dimension (then spin quantum number = (d-1)/2)
sparse : bool
if True(False), the returned Matrix type will be sparse(array)
isDim : bool
boolean for whether j is spin quantum number of dimension
Returns
-------
Matrix
Angular momentum (spin) raising operator
Examples
--------
>>> Jp(j=2, sparse=False)
[[0. 2. 0. 0. 0. ]
[0. 0. 2.44948974 0. 0. ]
[0. 0. 0. 2.44948974 0. ]
[0. 0. 0. 0. 2. ]
[0. 0. 0. 0. 0. ]]
>>> print(Jp(j=2))
(0, 1) 2.0
(1, 2) 2.449489742783178
(2, 3) 2.449489742783178
(3, 4) 2.0
>>> Jp(j=5, sparse=False, isDim=True)
[[0. 2. 0. 0. 0. ]
[0. 0. 2.44948974 0. 0. ]
[0. 0. 0. 2.44948974 0. ]
[0. 0. 0. 0. 2. ]
[0. 0. 0. 0. 0. ]]
>>> print(Jp(j=5, isDim=True))
(0, 1) 2.0
(1, 2) 2.449489742783178
(2, 3) 2.449489742783178
(3, 4) 2.0
"""
d = int((2*j) + 1)
if isDim:
d = int(j)
j = round((d-1)/2, 1)
m = [j-i for i in range(d)]
data = [np.sqrt((j+m[i])*(j-m[i]+1)) for i in range(len(m) - 1)]
rows = range(0, d-1)
columns = range(1, d)
n = sp.csc_matrix((data, (rows, columns)), shape=(d, d))
return n if sparse else n.toarray()
[docs]def Jm(j: float, sparse: bool = True, isDim: bool = False) -> Matrix:
r"""
Creates the angular momentum (spin) `lowering` operator
:math:`\hat{J}_{-} := \frac{1}{2}(\hat{J}_{x}-i\hat{J}_{y})` for a given spin quantum number j.
NOTE This is a direct matrix construction, i.e. it does not use the Jx and Jy functions, and this function is used
in Jx and Jy implementations
Parameters
----------
j : int or float
integer or half-integer spin quantum number, or the dimension (then spin quantum number = (d-1)/2)
sparse : bool
if True(False), the returned Matrix type will be sparse(array)
isDim : bool
boolean for whether j is spin quantum number of dimension
Returns
-------
Matrix
Angular momentum (spin) lowering operator
Examples
--------
>>> Jm(j=2, isDim=False, sparse=False)
[[0. 0. 0. 0. 0. ]
[2. 0. 0. 0. 0. ]
[0. 2.44948974 0. 0. 0. ]
[0. 0. 2.44948974 0. 0. ]
[0. 0. 0. 2. 0. ]]
>>> print(Jm(j=2, isDim=False))
(1, 0) 2.0
(2, 1) 2.449489742783178
(3, 2) 2.449489742783178
(4, 3) 2.0
>>> Jm(j=5, sparse=False, isDim=True)
[[0. 0. 0. 0. 0. ]
[2. 0. 0. 0. 0. ]
[0. 2.44948974 0. 0. 0. ]
[0. 0. 2.44948974 0. 0. ]
[0. 0. 0. 2. 0. ]]
>>> print(Jm(j=5, isDim=True))
(1, 0) 2.0
(2, 1) 2.449489742783178
(3, 2) 2.449489742783178
(4, 3) 2.0
"""
d = int((2*j) + 1)
if isDim:
d = int(j)
j = round((d-1)/2, 1)
m = [j-i for i in range(d)]
data = [np.sqrt((j+m[i])*(j-m[i]+1)) for i in range(len(m) - 1)]
rows = range(1, d)
columns = range(0, d-1)
n = sp.csc_matrix((data, (rows, columns)), shape=(d, d))
return n if sparse else n.toarray()
[docs]def Jx(j: float, sparse: bool = True, isDim: bool = False) -> Matrix:
r"""
Creates the angular momentum (spin) `X` operator :math:`\hat{J}_{x}` for a given spin quantum number j.
NOTE This function uses the definition :math:`\hat{J}_{x} =\frac{1}{2}(\hat{J}_{p}+\hat{J}_{m})` and calls Jp and Jm
There are no test for this method, it rely on Jp and Jm
Parameters
----------
j : int or float
integer or half-integer spin quantum number, or the dimension (then spin quantum number = (d-1)/2)
sparse : bool
if True(False), the returned Matrix type will be sparse(array)
isDim : bool
boolean for whether j is spin quantum number of dimension
Returns
-------
Matrix
Angular momentum (spin) X operator
Examples
--------
>>> Jx(j=2, isDim=False, sparse=False)
[[0. 1. 0. 0. 0. ]
[1. 0. 1.22474487 0. 0. ]
[0. 1.22474487 0. 1.22474487 0. ]
[0. 0. 1.22474487 0. 1. ]
[0. 0. 0. 1. 0. ]]
>>> print(Jx(j=2, isDim=False))
(1, 0) 1.0
(0, 1) 1.0
(2, 1) 1.224744871391589
(1, 2) 1.224744871391589
(3, 2) 1.224744871391589
(2, 3) 1.224744871391589
(4, 3) 1.0
(3, 4) 1.0
>>> Jx(j=5, sparse=False, isDim=True)
[[0. 1. 0. 0. 0. ]
[1. 0. 1.22474487 0. 0. ]
[0. 1.22474487 0. 1.22474487 0. ]
[0. 0. 1.22474487 0. 1. ]
[0. 0. 0. 1. 0. ]]
>>> print(Jx(j=5, isDim=True))
(1, 0) 1.0
(0, 1) 1.0
(2, 1) 1.224744871391589
(1, 2) 1.224744871391589
(3, 2) 1.224744871391589
(2, 3) 1.224744871391589
(4, 3) 1.0
(3, 4) 1.0
"""
n = 0.5*(Jp(j, isDim=isDim) + Jm(j, isDim=isDim))
return n if sparse else n.toarray()
[docs]def Jy(j: float, sparse: bool = True, isDim: bool = False) -> Matrix:
r"""
Creates the angular momentum (spin) `Y` operator :math:`\hat{J}_{y}` for a given spin quantum number j.
NOTE This function uses the definition :math:`\hat{J}_{y}=\frac{1}{2j}(\hat{J}_{p}-\hat{J}_{m})` and calls Jp and Jm
There are no test for this method, it rely on Jp and Jm
Parameters
----------
j : int or float
integer or half-integer spin quantum number, or the dimension (then spin quantum number = (d-1)/2)
sparse : bool
if True(False), the returned Matrix type will be sparse(array)
isDim : bool
boolean for whether j is spin quantum number of dimension
Returns
-------
Matrix
Angular momentum (spin) Y operator
Examples
--------
>>> Jy(j=2, isDim=False, sparse=False)
[[0.+0.j 0.-1.j 0.+0.j 0.+0.j 0.+0.j ]
[0.+1.j 0.+0.j 0.-1.22474487j 0.+0.j 0.+0.j ]
[0.+0.j 0.+1.22474487j 0.+0.j 0.-1.22474487j 0.+0.j ]
[0.+0.j 0.+0.j 0.+1.22474487j 0.+0.j 0.-1.j ]
[0.+0.j 0.+0.j 0.+0.j 0.+1.j 0.+0.j ]]
>>> print(Jy(j=2, isDim=False))
(1, 0) 1j
(0, 1) -1j
(2, 1) 1.224744871391589j
(1, 2) -1.224744871391589j
(3, 2) 1.224744871391589j
(2, 3) -1.224744871391589j
(4, 3) 1j
(3, 4) -1j
>>> Jy(j=5, sparse=False, isDim=True)
[[0.+0.j 0.-1.j 0.+0.j 0.+0.j 0.+0.j ]
[0.+1.j 0.+0.j 0.-1.22474487j 0.+0.j 0.+0.j ]
[0.+0.j 0.+1.22474487j 0.+0.j 0.-1.22474487j 0.+0.j ]
[0.+0.j 0.+0.j 0.+1.22474487j 0.+0.j 0.-1.j ]
[0.+0.j 0.+0.j 0.+0.j 0.+1.j 0.+0.j ]]
>>> print(Jy(j=5, isDim=True))
(1, 0) 1j
(0, 1) -1j
(2, 1) 1.224744871391589j
(1, 2) -1.224744871391589j
(3, 2) 1.224744871391589j
(2, 3) -1.224744871391589j
(4, 3) 1j
(3, 4) -1j
"""
n = (1/(2j))*(Jp(j, isDim=isDim) - Jm(j, isDim=isDim))
return n if sparse else n.toarray()
[docs]def Jz(j: float, sparse: bool = True, isDim: bool = False) -> Matrix:
r"""
Creates the angular momentum (spin) `Z` operator :math:`\hat{J}_{z}` for a given spin quantum number j.
Parameters
----------
j : int or float
integer or half-integer spin quantum number, or the dimension (then spin quantum number = (d-1)/2)
sparse : bool
if True(False), the returned Matrix type will be sparse(array)
isDim : bool
boolean for whether j is spin quantum number of dimension
Returns
-------
Matrix
Angular momentum (spin) Z operator
Examples
--------
>>> Jz(j=2, isDim=False, sparse=False)
[[ 2 0 0 0 0]
[ 0 1 0 0 0]
[ 0 0 0 0 0]
[ 0 0 0 -1 0]
[ 0 0 0 0 -2]]
>>> print(Jz(j=2, isDim=False))
(0, 0) 2
(1, 1) 1
(2, 2) 0
(3, 3) -1
(4, 4) -2
>>> Jz(j=5, sparse=False, isDim=True)
[[ 2. 0. 0. 0. 0.]
[ 0. 1. 0. 0. 0.]
[ 0. 0. 0. 0. 0.]
[ 0. 0. 0. -1. 0.]
[ 0. 0. 0. 0. -2.]]
>>> print(Jz(j=5, isDim=True))
(0, 0) 2.0
(1, 1) 1.0
(2, 2) 0.0
(3, 3) -1.0
(4, 4) -2.0
"""
d = int((2*j) + 1)
if isDim:
d = int(j)
j = round((d-1)/2, 1)
data = [j-i for i in range(d)]
rows = range(0, d)
columns = range(0, d)
n = sp.csc_matrix((data, (rows, columns)), shape=(d, d))
return n if sparse else n.toarray()
[docs]def Js(j: float, sparse: bool = True, isDim: bool = False) -> Matrix:
r"""
Creates the total angular momentum (spin) operator
:math:`\hat{J}_{s} := \hat{J}_{x}^{2} + \hat{J}_{y}^{2}+ \hat{J}_{z}^{2}` for a given spin quantum number j.
NOTE This function is direct implementation of the defition, meaning it uses Jx, Jy, and Jz functions
Parameters
----------
j : int or float
integer or half-integer spin quantum number, or the dimension (then spin quantum number = (d-1)/2)
sparse : bool
if True(False), the returned Matrix type will be sparse(array)
isDim : bool
boolean for whether j is spin quantum number of dimension
Returns
-------
Total angular momentum (spin) operator
Examples
--------
>>> Js(j=2, isDim=False, sparse=False)
[[6.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j]
[0.+0.j 6.+0.j 0.+0.j 0.+0.j 0.+0.j]
[0.+0.j 0.+0.j 6.+0.j 0.+0.j 0.+0.j]
[0.+0.j 0.+0.j 0.+0.j 6.+0.j 0.+0.j]
[0.+0.j 0.+0.j 0.+0.j 0.+0.j 6.+0.j]]
>>> print(Js(j=2, isDim=False))
(0, 0) (6+0j)
(1, 1) (6+0j)
(2, 2) (5.999999999999999+0j)
(3, 3) (6+0j)
(4, 4) (6+0j)
>>> Js(j=5, sparse=False, isDim=True)
[[6.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j]
[0.+0.j 6.+0.j 0.+0.j 0.+0.j 0.+0.j]
[0.+0.j 0.+0.j 6.+0.j 0.+0.j 0.+0.j]
[0.+0.j 0.+0.j 0.+0.j 6.+0.j 0.+0.j]
[0.+0.j 0.+0.j 0.+0.j 0.+0.j 6.+0.j]]
>>> print(Js(j=5, isDim=True))
(0, 0) (6+0j)
(1, 1) (6+0j)
(2, 2) (5.999999999999999+0j)
(3, 3) (6+0j)
(4, 4) (6+0j)
"""
n = (Jx(j, isDim=isDim)@Jx(j, isDim=isDim)) + (Jy(j, isDim=isDim)@Jy(j, isDim=isDim))\
+ (Jz(j, isDim=isDim)@Jz(j, isDim=isDim))
return n if sparse else n.toarray()
[docs]def displacement(alpha: complex, dim: int, sparse: bool = True) -> Matrix:
r"""
Creates the displacement operator :math:`\hat{D}(\alpha) := e^{\alpha a^{\dagger} - \alpha^{*}a}`
for a given displacement parameter :math:`\alpha`.
NOTE can be implemented without matrix exponentiation
Parameters
----------
alpha : complex
complex number, the displacement parameter
dim : int
dimension of the Hilbert space
sparse : bool
if True(False), the returned Matrix type will be sparse(array)
Returns
-------
Matrix
Displacement operator
Examples
--------
>>> displacement(alpha=1j, dim=4, sparse=False)
[[ 0.60605894+0.j 0. +0.6100857j -0.41242505+0.j 0. -0.30065525j]
[ 0. +0.6100857j 0.02280184+0.j 0. +0.34204129j -0.71434114+0.j]
[-0.41242505+0.j 0. +0.34204129j -0.56045527+0.j 0. +0.63150869j]
[ 0. -0.30065525j -0.71434114+0.j 0. +0.63150869j 0.02280184+0.j]]
>>> print(displacement(alpha=1j, dim=4))
(0, 0) (0.6060589372864117+0j)
(1, 0) 0.610085698426889j
(2, 0) (-0.41242505189886125+0j)
(3, 0) (-0-0.3006552538647247j)
(0, 1) 0.610085698426889j
(1, 1) (0.02280183542861441+0j)
(2, 1) 0.3420412936689465j
(3, 1) (-0.7143411442030587+0j)
(0, 2) (-0.4124250518988613+0j)
(1, 2) 0.34204129366894637j
(2, 2) (-0.5604552664291825+0j)
(3, 2) 0.6315086890322961j
(0, 3) -0.3006552538647247j
(1, 3) (-0.7143411442030586+0j)
(2, 3) 0.6315086890322962j
(3, 3) (0.02280183542861464+0j)
"""
oper = (alpha * create(dim)) - (np.conj(alpha) * destroy(dim))
n = expm(oper)
return n if sparse else n.A
[docs]def squeeze(alpha: complex, dim: int, sparse: bool = True) -> Matrix:
r"""
Creates the squeezing operator :math:`\hat{S}(\alpha) := e^{\frac{1}{2}(\alpha^{*}a^{2} - \alpha a^{\dagger 2})}`
for a given squeezing parameter :math:`\alpha`.
NOTE can be implemented without matrix exponentiation
Parameters
----------
alpha : complex
complex number, the squeezing parameter
dim : int
dimension of the Hilbert space
sparse : bool
if True(False), the returned Matrix type will be sparse(array)
Returns
-------
Matrix
Squeezing operator
Examples
--------
>>> squeeze(alpha=1j, dim=4, sparse=False)
[[0.7602446 +0.j 0. +0.j 0. -0.64963694j 0. +0.j ]
[0. +0.j 0.33918599+0.j 0. +0.j 0. -0.94071933j]
[0. -0.64963694j 0. +0.j 0.7602446 +0.j 0. +0.j ]
[0. +0.j 0. -0.94071933j 0. +0.j 0.33918599+0.j ]]
>>> print(squeeze(alpha=1j, dim=4))
(0, 0) (0.7602445970756301+0j)
(2, 0) -0.6496369390800625j
(1, 1) (0.3391859889869473+0j)
(3, 1) -0.940719333741444j
(0, 2) -0.6496369390800625j
(2, 2) (0.7602445970756302+0j)
(1, 3) -0.9407193337414442j
(3, 3) (0.33918598898694713+0j)
"""
oper = -(alpha * (create(dim)@create(dim))) + (np.conj(alpha) * (destroy(dim)@destroy(dim)))
n = expm(0.5*oper)
return n if sparse else n.A
[docs]def parityEXP(HamiltonianCavity: Matrix) -> Matrix:
r"""
Creates a parity operator :math:`\hat{P}(\hat{H}) := e^{i\pi\hat{H}}` by exponenting a given Hamiltonian
:math:`\hat{H}`.
Keeps sparse/array as sparse/array.
Parameters
----------
HamiltonianCavity : Matrix
dimension of the Hilbert space
Returns
-------
Matrix
Parity operator
Examples
--------
>>> ham = number(dimension=5, sparse=False)
>>> parityEXP(HamiltonianCavity=ham) # returns an array since ham is an array
[[ 1.+0.0000000e+00j 0.+0.0000000e+00j 0.+0.0000000e+00j 0.+0.0000000e+00j 0.+0.0000000e+00j]
[ 0.+0.0000000e+00j -1.+1.2246468e-16j 0.+0.0000000e+00j 0.+0.0000000e+00j 0.+0.0000000e+00j]
[ 0.+0.0000000e+00j 0.+0.0000000e+00j 1.-2.4492936e-16j 0.+0.0000000e+00j 0.+0.0000000e+00j]
[ 0.+0.0000000e+00j 0.+0.0000000e+00j 0.+0.0000000e+00j -1.+3.6739404e-16j 0.+0.0000000e+00j]
[ 0.+0.0000000e+00j 0.+0.0000000e+00j 0.+0.0000000e+00j 0.+0.0000000e+00j 1.-4.8985872e-16j]]
>>> ham = number(dimension=5)
>>> print(parityEXP(HamiltonianCavity=ham)) # returns a sparse since ham is a sparse
(0, 0) (1+0j)
(0, 1) 0j
(1, 1) (-1+1.2246467991473532e-16j)
(1, 2) -0j
(2, 2) (1-2.4492935982947064e-16j)
(2, 3) 0j
(3, 3) (-1+3.6739403974420594e-16j)
(3, 4) -0j
(4, 4) (1-4.898587196589413e-16j)
"""
sparse = sp.isspmatrix(HamiltonianCavity)
parEX = ((1j * np.pi) * HamiltonianCavity)
return expm(parEX) if sparse else linA.expm(parEX)
[docs]def paritySUM(dimension: int, sparse: bool = True) -> Matrix:
r"""
Creates a parity operator by explicitly placing alternating +/- into a matrix.
Parameters
----------
dimension : int
dimension of the Hilbert space
sparse : bool
if True(False), the returned Matrix type will be sparse(array)
Returns
-------
Matrix
Parity operator
Examples
--------
>>> paritySUM(dimension=5, sparse=False)
[[ 1. 0. 0. 0. 0.]
[ 0. -1. 0. 0. 0.]
[ 0. 0. 1. 0. 0.]
[ 0. 0. 0. -1. 0.]
[ 0. 0. 0. 0. 1.]]
>>> print(paritySUM(dimension=5))
(0, 0) 1.0
(1, 1) -1.0
(2, 2) 1.0
(3, 3) -1.0
(4, 4) 1.0
"""
a = np.empty((dimension,))
a[::2] = 1
a[1::2] = -1
data = a
rows = range(0, dimension)
columns = range(0, dimension)
n = sp.csc_matrix((data, (rows, columns)), shape=(dimension, dimension))
return n if sparse else n.toarray()
[docs]def compositeOp(operator: Matrix, dimB: int = 1, dimA: int = 1) -> Matrix:
r"""
Creates a composite operator
:math:`\hat{O}_{comp} = \mathbb{I}_{dimB\times dimB}\otimes\hat{O}_{single}\otimes\mathbb{I}_{dimA\times dimA}`
,ie tensor product with identities of dimensions before dimB and after dimA
NOTE simply calls and returns :func:`tensorProd <quanguru.QuantumToolbox.linearAlgebra.tensorProd>`
Parameters
----------
operator : Matrix
operator of a sub-system
dimB : int
(total) dimension of the systems that appear `before` in the tensor product order
dimA : int
(total) dimension of the systems that appear `after` in the tensor product order
Returns
-------
Matrix
sub-system operator in the extended Hilbert space
Examples
--------
>>> compositeOp(operator=sigmaz(), dimB=0, dimA=2).A
[[ 1. 0. 0. 0.]
[ 0. 1. 0. 0.]
[ 0. 0. -1. 0.]
[ 0. 0. 0. -1.]]
>>> compositeOp(operator=sigmaz(), dimB=2, dimA=0).A
[[ 1. 0. 0. 0.]
[ 0. -1. 0. 0.]
[ 0. 0. 1. 0.]
[ 0. 0. 0. -1.]]
"""
return tensorProd(*[a for a in [dimB, operator, dimA] if ((not isinstance(a, int)) or (a > 1))])
[docs]def operatorPow(op: Callable, dim: int, power: int, sparse: bool = True) -> Matrix:
r"""
Creates a quantum operator for given function reference `op` and raises to a `power`.
Parameters
----------
op : Callable
reference to the function (in here) for the operator
dim : int
dimension of the Hilbert space
power : int
power that the operator to be raised
sparse : bool
if True(False), the returned Matrix type will be sparse(array)
Returns
-------
Matrix
an operator raised to a power
Examples
--------
>>> operatorPow(op=sigmax, dim=2, power=2, sparse=False)
[[1 0]
[0 1]]
>>> print(operatorPow(op=sigmax, dim=2, power=2))
(0, 0) 1
(1, 1) 1
>>> operatorPow(op=sigmax, dim=2, power=3, sparse=False)
[[0 1]
[1 0]]
>>> print(operatorPow(op=sigmax, dim=2, power=3))
(1, 0) 1
(0, 1) 1
"""
try:
opPow = _matPower(op(dim, sparse), power)
except: # pylint: disable=bare-except # noqa: E722
opPow = _matPower(op(sparse), power)
return opPow