filter_functions.util module

This module provides various helper functions.

Functions

abs2()

Absolute value squared

get_indices_from_identifiers()

The indices of a subset of identifiers within a list of identifiers.

tensor()

Fast, flexible tensor product of an arbitrary number of inputs using einsum()

tensor_insert()

For an array that is known to be a tensor product, insert arrays at a given position in the product chain

tensor_merge()

For two arrays that are tensor products of known dimensions, merge them at arbitary positions in the product chain

tensor_transpose()

For a tensor product, transpose the order of the constituents in the product chain

mdot()

Multiple matrix product

adot()

Accumulated matrix product

remove_float_errors()

Set entries whose absolute value is below a certain threshold to zero

oper_equiv()

Determine if two vectors or operators are equal up to a global phase

dot_HS()

Hilbert-Schmidt inner product

get_sample_frequencies()

Get frequencies with typical infrared and ultraviolet cutoffs for a PulseSequence

progressbar()

A progress bar for loops. Uses tqdm if available and a simple custom one if not.

hash_array_along_axis()

Return a list of hashes along a given axis

all_array_equal()

Check if all arrays in an iterable are equal

Exceptions

CalculationError

Exception raised if trying to fetch the pulse correlation function when it was not computed during concatenation

abs2(x: ndarray) ndarray[source]

Fast function to calculate the absolute value squared,

\[|\cdot|^2 := \Re(\cdot)^2 + \Im(\cdot)^2\]

Equivalent to:

np.abs(x)**2
all_array_equal(it: Iterable) bool[source]

Return True if all array elements of it are equal by hashing the bytes representation of each array. Note that this is not thread-proof.

dot_HS(U: ndarray | Qobj, V: ndarray | Qobj, eps: float | None = None) float | complex | ndarray[source]

Return the Hilbert-Schmidt inner product of U and V,

\[\langle U, V\rangle_\mathrm{HS} := \mathrm{tr}(U^\dagger V).\]
Parameters:
U, V: qutip.Qobj or ndarray

Objects to compute the inner product of.

eps: float

The floating point precision. The result is rounded to abs(int(np.log10(eps))) decimals if eps > 0.

Returns:
result: float, complex

The result rounded to precision eps.

Examples

>>> U, V = paulis[1:3]
>>> dot_HS(U, V)
0.0
>>> dot_HS(U, U)
2.0
get_sample_frequencies(pulse: PulseSequence, n_samples: int = 300, spacing: str = 'log', include_quasistatic: bool = False, omega_min: float | None = None, omega_max: float | None = None) ndarray[source]

Get n_samples sample frequencies spaced ‘linear’ or ‘log’.

The ultraviolet cutoff is taken to be one order of magnitude larger than the timescale of the pulse tau. In the case of log spacing, the values are clipped in the infrared at two orders of magnitude below the timescale of the pulse.

Parameters:
pulse: PulseSequence

The pulse to get frequencies for.

n_samples: int, optional

The number of frequency samples. Default is 300.

spacing: str, optional

The spacing of the frequencies. Either ‘log’ or ‘linear’, default is ‘log’.

include_quasistatic: bool, optional

Include zero frequency. Default is False.

omega_min, omega_max: float, optional

Minimum and maximum angular frequencies included (DC notwithstanding). Default to \(2\pi\times 10^{-2}/\tau\) and \(2\pi\times 10^{+1}/\Delta t_{\mathrm{min}}\).

Returns:
omega: ndarray

The angular frequencies.

hash_array_along_axis(arr: ndarray, axis: int = 0) List[int][source]

Return the hashes of arr along the first axis

mdot(arr: Sequence[ndarray], axis: int = 0) ndarray[source]

Multiple matrix products along axis.

oper_equiv(psi: ndarray | Qobj, phi: ndarray | Qobj, eps: float | None = None, normalized: bool = False) Tuple[bool, float][source]

Checks whether psi and phi are equal up to a global phase, i.e.

\[|\psi\rangle = e^{i\chi}|\phi\rangle \Leftrightarrow \langle \phi|\psi\rangle = e^{i\chi},\]

and returns the phase. If the first return value is false, the second is meaningless in this context. psi and phi can also be operators.

Parameters:
psi, phi: qutip.Qobj or array_like

Vectors or operators to be compared

eps: float

The tolerance below which the two objects are treated as equal, i.e., the function returns True if abs(1 - modulus) <= eps.

normalized: bool

Flag indicating if psi and phi are normalized with respect to the Hilbert-Schmidt inner product dot_HS().

Examples

>>> psi = paulis[1]
>>> phi = paulis[1]*np.exp(1j*1.2345)
>>> oper_equiv(psi, phi)
(True, 1.2345)
progressbar(iterable: Iterable, *args, **kwargs)[source]

Progress bar for loops. Uses tqdm.

Usage:

for i in progressbar(range(10)):
    do_something()
remove_float_errors(arr: ndarray, eps_scale: float | None = None) ndarray[source]

Clean up arr by removing floating point numbers smaller than the dtype’s precision multiplied by eps_scale. Treats real and imaginary parts separately.

Obviously only works for arrays with norm ~1.

tensor(*args, rank: int = 2, optimize: bool | str = False) ndarray[source]

Fast, flexible tensor product using einsum. The product is taken over the last rank axes and broadcast over the remaining axes which thus need to follow numpy broadcasting rules. Note that vectors are treated as rank 2 tensors with shape (1, x) or (x, 1).

For example, the following shapes are compatible:

  • rank == 2 (e.g. matrices or vectors):

    (a, b, c, d, d), (a, b, c, e, e) -> (a, b, c, d*e, d*e)
    (a, b, c), (a, d, e) -> (a, b*d, c*e)
    (a, b), (c, d, e) -> (c, a*d, b*e)
    (1, a), (b, 1, c) -> (b, 1, a*c)
    
  • rank == 1:

    (a, b), (a, c) -> (a, b*c)
    (a, b, 1), (a, c) -> (a, b, c)
    
Parameters:
args: array_like

The elements of the tensor product

rank: int, optional (default: 2)

The rank of the tensors. E.g., for a Kronecker product between two matrices rank == 2. The remaining axes are broadcast over.

optimize: bool|str, optional (default: False)

Optimize the tensor contraction order. Passed through to numpy.einsum().

See also

numpy.kron

NumPy tensor product.

tensor_insert

Insert array at given position in tensor product chain.

tensor_merge

Merge tensor product chains.

tensor_transpose

Transpose the order of a tensor product chain.

Examples

>>> Z = np.diag([1, -1])
>>> np.array_equal(tensor(Z, Z), np.kron(Z, Z))
True
>>> A, B = np.arange(2), np.arange(2, 5)
>>> tensor(A, B, rank=1)
array([[0, 0, 0, 2, 3, 4]])
>>> args = np.random.randn(4, 10, 3, 2)
>>> result = tensor(*args, rank=1)
>>> result.shape == (10, 3, 2**4)
True
>>> result = tensor(*args, rank=2)
>>> result.shape == (10, 3**4, 2**4)
True
>>> A, B = np.random.randn(1, 3), np.random.randn(3, 4)
>>> result = tensor(A, B)
>>> result.shape == (1*3, 3*4)
True
>>> A, B = np.random.randn(3, 1, 2), np.random.randn(2, 2, 2)
>>> try:
...     result = tensor(A, B, rank=2)
... except ValueError as err:  # cannot broadcast over axis 0
...     print(err)
Incompatible shapes (3, 1, 2) and (2, 2, 2) for tensor product of rank 2.
>>> result = tensor(A, B, rank=3)
>>> result.shape == (3*2, 1*2, 2*2)
True
tensor_insert(arr: ndarray, *args, pos: int | Sequence[int], arr_dims: Sequence[Sequence[int]], rank: int = 2, optimize: bool | str = False) ndarray[source]

For a tensor product arr, insert args into the product chain at pos. E.g, if \(\verb|arr|\equiv A\otimes B\otimes C\) and \(\verb|pos|\equiv 2\), the result will be the tensor product

\[A\otimes B\otimes\left[\bigotimes_{X\in\verb|args|}X\right] \otimes C.\]

This function works in a similar way to numpy.insert() and the following would be functionally equivalent in the case that the constituent tensors of the product arr are known:

>>> tensor_insert(tensor(*arrs, rank=rank), *args, pos=pos, arr_dims=...,
...               rank=rank)
>>> tensor(*np.insert(arrs, pos, args, axis=0), rank=rank)
Parameters:
arr: ndarray

The tensor product in whose chain the other args should be inserted

*args: ndarray

The tensors to be inserted in the product chain

pos: int|sequence of ints

The position(s) at which the args are inserted in the product chain. If an int and len(args) > 1, it is repeated so that all args are inserted in a row. If a sequence, it should indicate the indices in the original tensor product chain that led to arr before which args should be inserted.

arr_dims: array_like, shape (rank, n_const)

The last rank dimensions of the n_const constituent tensors of the tensor product arr as a list of lists with the list at position i containing the i-th relevant dimension of all args. Since the remaing axes are broadcast over, their shape is irrelevant.

For example, if arr = tensor(a, b, c, rank=2) and a,b,c have shapes (2, 3, 4), (5, 2, 2, 1), (2, 2), arr_dims = [[3, 2, 2], [4, 1, 2]].

rank: int, optional (default: 2)

The rank of the tensors. E.g., for a Kronecker product between two vectors, rank == 1, and between two matrices rank == 2. The remaining axes are broadcast over.

optimize: bool|str, optional (default: False)

Optimize the tensor contraction order. Passed through to numpy.einsum().

See also

numpy.insert

NumPy array insertion with similar syntax.

numpy.kron

NumPy tensor product.

tensor_insert

Insert array at given position in tensor product chain.

tensor_merge

Merge tensor product chains.

tensor_transpose

Transpose the order of a tensor product chain.

Examples

>>> I, X, Y, Z = paulis
>>> arr = tensor(X, I)
>>> r = tensor_insert(arr, Y, Z, arr_dims=[[2, 2], [2, 2]], pos=0)
>>> np.allclose(r, tensor(Y, Z, X, I))
True
>>> r = tensor_insert(arr, Y, Z, arr_dims=[[2, 2], [2, 2]], pos=1)
>>> np.allclose(r, tensor(X, Y, Z, I))
True
>>> r = tensor_insert(arr, Y, Z, arr_dims=[[2, 2], [2, 2]], pos=2)
>>> np.allclose(r, tensor(X, I, Y, Z))
True

Other ranks and different dimensions:

>>> from numpy.random import randn
>>> A, B, C = randn(2, 3, 1, 2), randn(2, 2, 2, 2), randn(3, 2, 1)
>>> arr = tensor(A, C, rank=3)
>>> r = tensor_insert(arr, B, pos=1, rank=3,
...                   arr_dims=[[3, 3], [1, 2], [2, 1]])
>>> np.allclose(r, tensor(A, B, C, rank=3))
True
>>> arrs, args = randn(2, 2, 2), randn(2, 2, 2)
>>> arr_dims = [[2, 2], [2, 2]]
>>> r = tensor_insert(tensor(*arrs), *args, pos=(0, 1), arr_dims=arr_dims)
>>> np.allclose(r, tensor(args[0], arrs[0], args[1], arrs[1]))
True
>>> r = tensor_insert(tensor(*arrs), *args, pos=(0, 0), arr_dims=arr_dims)
>>> np.allclose(r, tensor(*args, *arrs))
True
>>> r = tensor_insert(tensor(*arrs), *args, pos=(1, 2), arr_dims=arr_dims)
>>> np.allclose(r, tensor(*np.insert(arrs, (1, 2), args, axis=0)))
True
tensor_merge(arr: ndarray, ins: ndarray, pos: Sequence[int], arr_dims: Sequence[Sequence[int]], ins_dims: Sequence[Sequence[int]], rank: int = 2, optimize: bool | str = False) ndarray[source]

For two tensor products arr and ins, merge ins into the product chain at indices pos. E.g, if \(\verb|arr|\equiv A\otimes B\otimes C\), \(\verb|ins|\equiv D\otimes E\), and \(\verb|pos|\equiv [1, 2]\), the result will be the tensor product

\[A\otimes D\otimes B\otimes E\otimes C.\]

This function works in a similar way to numpy.insert() and tensor_insert().

Parameters:
arr: ndarray

The tensor product in whose chain the other args should be inserted

ins: ndarray

The tensor product to be inserted in the product chain

pos: sequence of ints

The positions at which the constituent tensors of ins are inserted in the product chain. Should indicate the indices in the original tensor product chain that led to arr before which the constituents of ins should be inserted.

arr_dims: array_like, shape (rank, n_const)

The last rank dimensions of the n_const constituent tensors of the tensor product arr as a list of lists with the list at position i containing the i-th relevant dimension of all args. Since the remaing axes are broadcast over, their shape is irrelevant.

For example, if arr = tensor(a, b, c, rank=2) and a,b,c have shapes (2, 3, 4), (5, 2, 2, 1), (2, 2), arr_dims = [[3, 2, 2], [4, 1, 2]].

ins_dims: array_like, shape (rank, n_const)

The last rank dimensions of the n_const constituent tensors of the tensor product ins as a list of lists with the list at position i containing the i-th relevant dimension of ins. Since the remaing axes are broadcast over, their shape is irrelevant.

rank: int, optional (default: 2)

The rank of the tensors. E.g., for a Kronecker product between two vectors, rank == 1, and between two matrices rank == 2. The remaining axes are broadcast over.

optimize: bool|str, optional (default: False)

Optimize the tensor contraction order. Passed through to numpy.einsum().

See also

numpy.insert

NumPy array insertion with similar syntax.

numpy.kron

NumPy tensor product.

tensor

Fast tensor product with broadcasting.

tensor_insert

Insert array at given position in tensor product chain.

tensor_transpose

Transpose the order of a tensor product chain.

Examples

>>> I, X, Y, Z = paulis
>>> arr = tensor(X, Y, Z)
>>> ins = tensor(I, I)
>>> r1 = tensor_merge(arr, ins, pos=[1, 2], arr_dims=[[2]*3, [2]*3],
...                   ins_dims=[[2]*2, [2]*2])
>>> np.allclose(r1, tensor(X, I, Y, I, Z))
True
>>> r2 = tensor_merge(ins, arr, pos=[0, 1, 2], arr_dims=[[2]*2, [2]*2],
...                   ins_dims=[[2]*3, [2]*3])
>>> np.allclose(r1, r2)
True

tensor_insert() can provide the same functionality in some cases:

>>> arr = tensor(Y, Z)
>>> ins = tensor(I, X)
>>> r1 = tensor_merge(arr, ins, pos=[0, 0], arr_dims=[[2]*2, [2]*2],
...                   ins_dims=[[2]*2, [2]*2])
>>> r2 = tensor_insert(arr, I, X, pos=[0, 0], arr_dims=[[2]*2, [2]*2])
>>> np.allclose(r1, r2)
True

Also tensors of rank other than 2 and numpy broadcasting are supported:

>>> arr = np.random.randn(2, 10, 3, 4)
>>> ins = np.random.randn(2, 10, 3, 2)
>>> r = tensor_merge(tensor(*arr, rank=1), tensor(*ins, rank=1), [0, 1],
...                  arr_dims=[[4, 4]], ins_dims=[[2, 2]], rank=1)
>>> np.allclose(r, tensor(ins[0], arr[0], ins[1], arr[1], rank=1))
True
tensor_transpose(arr: ndarray, order: Sequence[int], arr_dims: Sequence[Sequence[int]], rank: int = 2) ndarray[source]

Transpose the order of a tensor product chain.

Parameters:
arr: ndarray

The tensor product whose chain should be reordered.

order: sequence of ints

The transposition order. If arr == tensor(A, B) and order == (1, 0), the result will be tensor(B, A).

arr_dims: array_like, shape (rank, n_const)

The last rank dimensions of the n_const constituent tensors of the tensor product arr as a list of lists with the list at position i containing the i-th relevant dimension of all args. Since the remaing axes are broadcast over, their shape is irrelevant.

For example, if arr = tensor(a, b, c, rank=2) and a,b,c have shapes (2, 3, 4), (5, 2, 2, 1), (2, 2), arr_dims = [[3, 2, 2], [4, 1, 2]].

rank: int, optional (default: 2)

The rank of the tensors. E.g., for a Kronecker product between two vectors, rank == 1, and between two matrices rank == 2. The remaining axes are broadcast over.

Returns:
transposed_arr: ndarray

The tensor product arr with its order transposed according to order

See also

numpy.insert

NumPy array insertion with similar syntax.

numpy.kron

NumPy tensor product.

tensor

Fast tensor product with broadcasting.

tensor_insert

Insert array at given position in tensor product chain.

tensor_merge

Merge tensor product chains.

Examples

>>> I, X, Y, Z = paulis
>>> arr = tensor(X, Y, Z)
>>> transposed = tensor_transpose(arr, [1, 2, 0], arr_dims=[[2, 2, 2]]*2)
>>> np.allclose(transposed, tensor(Y, Z, X))
True