quri_parts.core.operator package#



quri_parts.core.operator.commutator(op1: Operator, op2: Operator) Operator#

Returns the commutator of op1 and op2 :math:`[text{op1},


quri_parts.core.operator.truncate(op: Operator, atol: float = 1e-08) Operator#

Returns truncated operator by eliminating terms whose coefficients are smaller than atol.

quri_parts.core.operator.is_hermitian(op: Operator, atol: float = 1e-08) bool#

Returns True if given operator is hermitian.

quri_parts.core.operator.is_ops_close(op1: Operator, op2: Operator, rtol: float = 1e-09, atol: float = 0.0) bool#

Returns True if two operators are close to each other.

quri_parts.core.operator.PAULI_IDENTITY = frozenset({})#

PauliLabel used as an identity.

quri_parts.core.operator.pauli_label(p: str | PauliLabelProvider | Iterable[tuple[int, int]]) PauliLabel#

Create a PauliLabel.

The argument can be one of the followings:

  • A string representation of a Pauli string.

  • A PauliLabelProvider, an object providing a qubit index list and a SinglePauli list.

  • An Iterable of tuple[int, int], pairs of a qubit index and a SinglePauli.

quri_parts.core.operator.pauli_name(p: int) str#

Returns the name of Pauli matrix for a SinglePauli (int)

quri_parts.core.operator.pauli_product(pauli1: PauliLabel, pauli2: PauliLabel) tuple[PauliLabel, complex]#

Returns the product of two PauliLabels.

Note that this function returns a tuple (PauliLabel, phase) since the multiplication of two Pauli operators can yield a Pauli operator multiplied by the imaginary unit (e.g., XY = iZ).

class quri_parts.core.operator.PauliLabel#

Bases: frozenset[tuple[int, int]]

PauliLabel represents a label for a Pauli string, e.g. X0 Y1 Z2.

It represents only a “label” of a multi-qubit Pauli term and does not hold a coefficient or a phase factor. This class is basically a frozenset of pairs of a qubit index and a SinglePauli, and also can be used as a key for a dict (Hashable).


>>> label = pauli_label({(0, SinglePauli.X), (1, SinglePauli.Y), (2, SinglePauli.Z)})
>>> label
PauliLabel({(0, <SinglePauli.X: 1>), (1, <SinglePauli.Y: 2>), (2, <SinglePauli.Z: 3>)})
>>> str(label)
'X0 Y1 Z2'
>>> for pair in label:
...     print(pair)
(0, <SinglePauli.X: 1>)
(1, <SinglePauli.Y: 2>)
(2, <SinglePauli.Z: 3>)
>>> label.pauli_at(1)
<SinglePauli.Y: 2>
>>> 1 in label.qubit_indices()
>>> for i in label.qubit_indices():
...     print(f"index: {i}, operator: {label.pauli_at(i)}")
index: 0, operator: 1
index: 1, operator: 2
index: 2, operator: 3

You can construct a PauliLabel in one of the following ways:

  • With a tuples of a qubit index (int) and a Pauli matrix label (int)
    >>> pauli_label({(0, 1), (1, 2), (2, 3)})
  • With a tuples of a qubit index (int) and a Pauli matrix label (SinglePauli)
    >>> pauli_label({(0, SinglePauli.X), (1, SinglePauli.Y), (2, SinglePauli.Z)})
  • With a string representation of a Pauli string (Note that string parsing has a significant performance overhead)
    >>> pauli_label("X0 Y1 Z2")
  • From a qubit index list and a matrix label (SinglePauli) list
    >>> PauliLabel.from_index_and_pauli_list(
            [0, 1, 2], [SinglePauli.X, SinglePauli.Y, SinglePauli.Z]
  • From an object providing a qubit index list and a matrix label list (PauliLabelProvider)
    >>> pauli_label(obj) # obj has get_index_list and get_pauli_id_list methods

Note that the following code does not raise an error but creates an invalid PauliLabel. To avoid this kind of error, it is recommended to use a factory function pauli_label() instead of the original constructor PauliLabel().

>>> PauliLabel("X0 Y1 Z2") # Should be pauli_label("X0 Y1 Z2") or PauliLabel.from_str("X0 Y1 Z2")
PauliLabel({' ', 'Z', '0', '1', 'X', 'Y', '2'})

The string input should be given as a list of single pauli terms separated by white spaces. Each single pauli term consists of a Pauli matrix label (X, Y or Z) and a qubit index (int), possibly separated by white spaces.

Valid string input examples:

  • X0 Y1 Z2

  • X 0 Y 1 Z 2

Invalid string input examples:

  • X0 Y1 A2 (Invalid Pauli matrix label A)

  • X0Y1Z2 (No spaces between each term)

  • X0 Y1 Z1 (Duplicate qubit indices)

  • X0 Y Z2 (No qubit index specified)

  • X0 1 Z2 (No Pauli matrix label specified)

Order of the terms is irrelevant when comparing two PauliLabel’s:

>>> pauli_label("X0 Y1 Z2") == pauli_label("X0 Z2 Y1")

Performance tip: An index access by pauli_at() involves an iteration inside it, so it is inefficient when you want to iterate over all single Pauli terms in a PauliLabel. Instead of doing this:

>>> for i in range(len(label)):
...     p = label.pauli_at(i)
...     # Do something with p

the following is faster:

>>> for i, p in label:
...     # Do something with p
qubit_indices() Collection[int]#

Returns a Collection of qubit indices on which Pauli matrices act.

Note that this collection does not have a definite ordering.

pauli_at(index: int) int | None#

Returns a Pauli matrix at the qubit index index.

Returns None if no Pauli matrix acts on the index.

property index_and_pauli_id_list: tuple[Sequence[int], Sequence[int]]#

A pair of a list of indices of qubits on which the Pauli matrices act, and a list of the Pauli matrices in the order corresponding to the index list.

static from_index_and_pauli_list(index: Collection[int], pauli: Collection[int]) PauliLabel#

Create a PauliLabel from a list of qubit indices and a list of Pauli matrices.

static from_str(pauli_label_str: str) PauliLabel#

Create a PauliLabel from a string representation of a Pauli String.

static of(pauli: PauliLabelProvider) PauliLabel#

Create a PauliLabel from an object providing a qubit index list and a SinglePauli list.

class quri_parts.core.operator.Operator#

Bases: dict[PauliLabel, complex]

Operator represents the set of single-term operators as a dict[PauliLabel, coefficient].

Coefficients can not only be real values but also complex values since Operator represents a general operator including non-Hermitian one.


>>> op = Operator({pauli_label("X0"): 0.1j})
>>> op
{PauliLabel({(0, <SinglePauli.X: 1>)}): 0.1j}
>>> op[pauli_label("X1 Y2")] = 0.2
>>> op
{PauliLabel({(0, <SinglePauli.X: 1>)}): 0.1j, PauliLabel({(1, <SinglePauli.X: 1>), (2, <SinglePauli.Y: 2>)}): 0.2}
>>> str(op)
'0.1j*X0 + 0.2*X1 Y2'
>>> for pauli, coef in op.items():
...     print(f"Pauli: {pauli}, coefficient: {coef}")
Pauli: X0, coefficient: 0.1j
Pauli: X1 Y2, coefficient: 0.2
>>> op.constant
>>> op[PAULI_IDENTITY] = 2.0
>>> op
{PauliLabel({(0, <SinglePauli.X: 1>)}): 0.1j, PauliLabel({(1, <SinglePauli.X: 1>), (2, <SinglePauli.Y: 2>)}): 0.2, PauliLabel(): 2.0}
>>> op.constant
>>> op.constant = 1.0
>>> op.constant
You can add a single-term operator by add_term() method.
>>> operator.add_term(PauliLabel({(1, 1)}), 1.0)
By accessing by key, the coefficient is replaced with a new value.
>>> operator = Operator({pauli_label("X0"): 0.1j})
>>> op
{PauliLabel({(0, <SinglePauli.X: 1>)}): 0.1j}
>>> operator[pauli_label("X0")] = 0.1
>>> op
{PauliLabel({(0, <SinglePauli.X: 1>)}): 0.1}
add_term(pauli_label: PauliLabel, coef: complex) None#

Method for adding single-term operator.

copy() Operator#

Returns the copy of itself.

property n_terms: int#

Number of all terms.

property constant: complex#

Constant value.

Note that the constant value is a coefficient of an identity pauli term (PAULI_IDENTITY).

hermitian_conjugated() Operator#
class quri_parts.core.operator.SinglePauli(value)#

Bases: IntEnum

An integer enumeration representing Pauli matrices X, Y, Z acting on a single qubit.

X = 1#
Y = 2#
Z = 3#
quri_parts.core.operator.transition_amp_comp_basis(op_binary_repr: Mapping[int, Sequence[tuple[complex, int]]], m: int, n: int) complex#

Returns the transition amplitude \(\langle m|O|n\rangle\) of the operator \(O\)

quri_parts.core.operator.transition_amp_representation(operator: Operator) Mapping[int, Sequence[tuple[complex, int]]]#

Returns a binary representation _TransitionAmplitudeRepresentation, which is the special representation designed for calculating tansition amplitudes efficiently.

quri_parts.core.operator.zero() Operator#

Returns zero (empty) operator, which always return zero expectation value for all states.

quri_parts.core.operator.trotter_suzuki_decomposition(op: Operator, param: complex, order: int) list[ExponentialSinglePauli]#

Trotter-Suzuki decomposition [1], a recursive formula of the approximation that decomposes an exponential function of the sum of Pauli operators into a product of the exponential function of Pauli operators. The explicit formula for the sum of Pauli operator \(A=\sum_i A_i\) is given as follows:

\[\begin{split}S_{2k}(x)&=[S_{2k-2}(p_kx)]^2S_{2k-2}((1-4p_k)x)][S_{2k-2}(p_kx)]^2,\\ S_2(x)&=\prod_{j=1}^me^{A_j x /2}\prod_{j'=m}^1 e^{A_{j'} x /2},\\ p_k&=(4-4^{1/(2k-1)})^{-1},\end{split}\]

where \(k\) is an order of this decomposition and \(A_i\) is the Pauli operator.

  • op – An operator on the exponential.

  • param – The overall coefficient of the exponential. This can be not only a real but also a complex number.

  • order – The order of the Trotter-Suzuki decomposition. An integer that satisfies >= 1.


List of ExponentialSinglePauli.


[1]: M. Suzuki, Fractal decomposition of exponential operators with applications to many-body theories and Monte Carlo simulation, Phys. Lett. 146 319-323, 1990