Data Structures#
All the data structures of PauliArray rely on the fact that a Pauli string of \(n\) qubits can be encoded into two vectors of \(n\) binary components \(\mathbf{z}\) and \(\mathbf{x}\) used in the following definition
where the exponentiation by a vector is to be interpreted as a tensor product
with \(z_q\) the \(q\) th components of the bit string \(\mathbf{z}\). The dot product \(\mathbf{z} \cdot \mathbf{x}\) counts the number of \(\hat{Y}\) operators so the factor \((-i)^{\mathbf{z} \cdot \mathbf{x}}\) compensates the factors coming from \(\hat{Z}\hat{X} = i\hat{Y}\).
PauliArray#
The fundamental data structure PauliArray
represents a \(d\)-dimension array of Pauli strings. It uses two arrays of the same shape of bit strings \(\mathbf{z}\) and \(\mathbf{x}\) to store this information such that each Pauli string in the PauliArray is given by
The \(d\)-dimension arrays of bit strings \(\mathbf{b}_{ij\ldots k}\) are stored as \((d+1)\)-dimension arrays \(\mathsf{b}\) using numpy.ndarray[bool]
where the last hidden dimension is along the length of the Pauli strings and is of size of \(n\). The elements of these arrays are related to the bits of the bit string such that
It can be initialized by providing two numpy.ndarray[bool]
of the same shape.
import numpy as np
from pauliarray import PauliArray
num_qubits = 4
z_strings = np.tri(num_qubits, k=-1, dtype=bool)
x_strings = np.eye(num_qubits, dtype=bool)
paulis = PauliArray(z_strings, x_strings)
A convenient initialization method using Pauli string labels is also available. This uses the little-endian labelling convention by default.
paulis = PauliArray.from_labels(["IIIX", "IIXZ", "IXZZ", "XZZZ"])
Multidimensional PauliArray
are also supported. The following code creates a (4, 2)
4-qubit PauliArray
.
num_qubits = 4
z_strings = np.zeros((num_qubits, 2, num_qubits), dtype=bool)
x_strings = np.zeros((num_qubits, 2, num_qubits), dtype=bool)
z_strings[:, 0, :] = np.tri(num_qubits, k=-1, dtype=bool)
x_strings[:, 0, :] = np.eye(num_qubits, dtype=bool)
z_strings[:, 1, :] = np.tri(num_qubits, k=0, dtype=bool)
x_strings[:, 1, :] = np.eye(num_qubits, dtype=bool)
paulis = PauliArray(z_strings, x_strings)
This can also be achieved with labels.
paulis = PauliArray.from_labels(
[
["IIIX", "IIIY"],
["IIXZ", "IIYZ"],
["IXZZ", "IYZZ"],
["XZZZ", "YZZZ"],
]
)
WeightedPauliArray#
A WeightedPauliArray
is obtained by assigning a complex number to each Pauli string in a PauliArray
It can be initialized by providing a PauliArray
and a numpy.ndarray[complex]
. Both arrays should have the same shape or at leat be broadcastable.
from pauliarray import WeightedPauliArray
num_qubits = 4
z_strings = np.tri(num_qubits, k=-1, dtype=bool)
x_strings = np.eye(num_qubits, dtype=bool)
paulis = PauliArray(z_strings, x_strings)
weights = np.array([1, 2, 3, 4], dtype=complex)
wpaulis = WeightedPauliArray(paulis, weights)
Other initialization methods such as from_labels_and_weights
and from_z_strings_and_x_strings_and_weights
also exists for convenience.
Operator#
Any \(n\)-qubits operator \(\hat{O}\) can be decomposed on the basis of Pauli strings of length \(n\)
Therefore an Operator
is simply a sum over a one-dimensional WeightedPauliArray
. It can be initialized by simply providing a one-dimensional WeightedPauliArray
.
from pauliarray import Operator
operator = Operator(wpaulis)
OperatorArrayType1#
It is possible to define an array of operators by using a multidimensional WeightedPauliArray
and assigning its last dimension as the summation axis
All the operators in this type of operator array have the same number of Pauli strings.
It can be initialized by providing a WeightedPauliArray
. The last dimension is associated to the summation.
from pauliarray import OperatorArrayType1
paulis = PauliArray.from_labels(
[
["IIIX", "IIIY"],
["IIXZ", "IIYZ"],
["IXZZ", "IYZZ"],
["XZZZ", "YZZZ"],
]
)
wpaulis = WeightedPauliArray(paulis, 0.5)
operators = OperatorArrayType1(wpaulis)
Other initialization methods such as from_pauli_array
and from_weighted_pauli_array
allow to specify the summation axis (or axes), while from_operator_list
and from_operator_ndarray
can assemble multiple Operator
into an OperatorArrayType1
.