qml.pauli

Overview

This module defines functions and classes for generating and manipulating elements of the Pauli group. It also contains a subpackage pauli/grouping for Pauli-word partitioning functionality used in measurement optimization.

Functions

are_identical_pauli_words(pauli_1, pauli_2)

Performs a check if two Pauli words have the same wires and name attributes.

are_pauli_words_qwc(lst_pauli_words)

Given a list of observables assumed to be valid Pauli observables, determine if they are pairwise qubit-wise commuting.

binary_to_pauli(binary_vector[, wire_map])

Converts a binary vector of even dimension to an Observable instance.

diagonalize_pauli_word(pauli_word)

Transforms the Pauli word to diagonal form in the computational basis.

diagonalize_qwc_groupings(qwc_groupings)

Diagonalizes a list of qubit-wise commutative groupings of Pauli strings.

diagonalize_qwc_pauli_words(qwc_grouping)

Diagonalizes a list of mutually qubit-wise commutative Pauli words.

group_observables(observables[, …])

Partitions a list of observables (Pauli operations and tensor products thereof) into groupings according to a binary relation (qubit-wise commuting, fully-commuting, or anticommuting).

is_pauli_word(observable)

Checks if an observable instance consists only of Pauli and Identity Operators.

is_qwc(pauli_vec_1, pauli_vec_2)

Checks if two Pauli words in the binary vector representation are qubit-wise commutative.

observables_to_binary_matrix(observables[, …])

Converts a list of Pauli words to the binary vector representation and yields a row matrix of the binary vectors.

optimize_measurements(observables[, …])

Partitions then diagonalizes a list of Pauli words, facilitating simultaneous measurement of all observables within a partition.

partition_pauli_group(n_qubits)

Partitions the \(n\)-qubit Pauli group into qubit-wise commuting terms.

pauli_decompose(H[, hide_identity, …])

Decomposes a Hermitian matrix into a linear combination of Pauli operators.

pauli_group(n_qubits[, wire_map])

Generate the \(n\)-qubit Pauli group.

pauli_mult(pauli_1, pauli_2[, wire_map])

Multiply two Pauli words together and return the product as a Pauli word.

pauli_mult_with_phase(pauli_1, pauli_2[, …])

Multiply two Pauli words together, and return both their product as a Pauli word and the global phase.

pauli_sentence(op)

Return the PauliSentence representation of an arithmetic operator or Hamiltonian.

pauli_to_binary(pauli_word[, n_qubits, …])

Converts a Pauli word to the binary vector representation.

pauli_word_prefactor(observable)

If the operator provided is a valid Pauli word (i.e a single term which may be a tensor product of pauli operators), then this function extracts the prefactor.

pauli_word_to_matrix(pauli_word[, wire_map])

Convert a Pauli word from a tensor to its matrix representation.

pauli_word_to_string(pauli_word[, wire_map])

Convert a Pauli word to a string.

qwc_complement_adj_matrix(binary_observables)

Obtains the adjacency matrix for the complementary graph of the qubit-wise commutativity graph for a given set of observables in the binary representation.

qwc_rotation(pauli_operators)

Performs circuit implementation of diagonalizing unitary for a Pauli word.

simplify(h[, cutoff])

Add together identical terms in the Hamiltonian.

string_to_pauli_word(pauli_string[, wire_map])

Convert a string in terms of 'I', 'X', 'Y', and 'Z' into a Pauli word for the given wire map.

Classes

PauliGroupingStrategy(observables[, …])

Class for partitioning a list of Pauli words according to some binary symmetric relation.

PauliSentence

Dictionary representing a linear combination of Pauli words, with the keys as PauliWord instances and the values correspond to coefficients.

PauliWord(mapping)

Immutable dictionary used to represent a Pauli Word, associating wires with their respective operators.

PauliWord and PauliSentence

The single-qubit Pauli group consists of the four single-qubit Pauli operations Identity, PauliX, PauliY , and PauliZ. The \(n\)-qubit Pauli group is constructed by taking all possible \(N\)-fold tensor products of these elements. Elements of the \(n\)-qubit Pauli group are known as Pauli words, and have the form \(P_J = \otimes_{i=1}^{n}\sigma_i^{(J)}\), where \(\sigma_i^{(J)}\) is one of the Pauli operators (PauliX, PauliY, PauliZ) or identity (Identity) acting on the \(i^{th}\) qubit. The full \(n\)-qubit Pauli group has size \(4^n\) (neglecting the four possible global phases that may arise from multiplication of its elements).

PauliWord is a lightweight class which uses a dictionary approach to represent Pauli words. A PauliWord can be instantiated by passing a dictionary of wires and their associated Pauli operators.

>>> from pennylane.pauli import PauliWord
>>> pw1 = PauliWord({0:"X", 1:"Z"})  # X@Z
>>> pw2 = PauliWord({0:"Y", 1:"Z"})  # Y@Z
>>> pw1, pw2
(X(0) @ Z(1), Y(0) @ Z(1))

The purpose of this class is to efficiently compute products of Pauli words and obtain the matrix representation.

>>> pw1 @ pw2
1j * Z(0)
>>> pw1.to_mat(wire_order=[0, 1])
array([[ 0,  0,  1,  0],
       [ 0,  0,  0, -1],
       [ 1,  0,  0,  0],
       [ 0, -1,  0,  0]])

The PauliSentence class represents linear combinations of Pauli words. Using a similar dictionary based approach we can efficiently add, multiply and extract the matrix of operators in this representation.

>>> ps1 = PauliSentence({pw1: 1.2, pw2: 0.5j})
>>> ps2 = PauliSentence({pw1: -1.2})
>>> ps1
1.2 * X(0) @ Z(1)
+ 0.5j * Y(0) @ Z(1)
>>> ps1 + ps2
0.0 * X(0) @ Z(1)
+ 0.5j * Y(0) @ Z(1)
>>> ps1 @ ps2
-1.44 * I
+ (-0.6+0j) * Z(0)
>>> (ps1 + ps2).to_mat(wire_order=[0, 1])
array([[ 0. +0.j,  0. +0.j,  0.5+0.j,  0. +0.j],
       [ 0. +0.j,  0. +0.j,  0. +0.j, -0.5+0.j],
       [-0.5+0.j,  0. +0.j,  0. +0.j,  0. +0.j],
       [ 0. +0.j,  0.5+0.j,  0. +0.j,  0. +0.j]])

We can intuitively use Pauli arithmetic to construct Hamiltonians consisting of PauliWord and PauliSentence objects like the spin-1/2 XXZ model Hamiltonian,

\[H_\text{XXZ} = \sum_j [J^\bot (X_j X_{j+1} + Y_j Y_{j+1}) + J^\text{ZZ} Z_j Z_{j+1} + h Z_j].\]

Here we look at the simple topology of a one-dimensional chain with periodic boundary conditions (i.e. qubit number \(n \equiv 0\) for pythonic numbering of wires, e.g. [0, 1, 2, 3] for n=4). In code we can do this via the following example with 4 qubits.

n = 4
J_orthogonal = 1.5
ops = [
    J_orthogonal * (PauliWord({i:"X", (i+1)%n:"X"}) + PauliWord({i:"Y", (i+1)%n:"Y"}))
    for i in range(n)
]

J_zz = 0.5
ops += [J_zz * PauliWord({i:"Z", (i+1)%n:"Z"}) for i in range(n)]

h = 2.
ops += [h * PauliWord({i:"Z"}) for i in range(n)]

H = sum(ops)

We can also displace the Hamiltonian by an arbitrary amount. Here, for example, such that the ground state energy is 0.

>>> H = H - np.min(np.linalg.eigvalsh(H.to_mat()))

Graph colouring

A module for heuristic algorithms for colouring Pauli graphs.

A Pauli graph is a graph where vertices represent Pauli words and edges denote if a specified symmetric binary relation (e.g., commutation) is satisfied for the corresponding Pauli words. The graph-colouring problem is to assign a colour to each vertex such that no vertices of the same colour are connected, using the fewest number of colours (lowest “chromatic number”) as possible.

Functions

largest_first(binary_observables, adj)

Performs graph-colouring using the Largest Degree First heuristic.

recursive_largest_first(binary_observables, adj)

Performs graph-colouring using the Recursive Largest Degree First heuristic.

Grouping observables

Pauli words can be used for expressing a qubit Hamiltonian. A qubit Hamiltonian has the form \(H_{q} = \sum_{J} C_J P_J\) where \(C_{J}\) are numerical coefficients, and \(P_J\) are Pauli words.

A list of Pauli words can be partitioned according to certain grouping strategies. As an example, the group_observables() function partitions a list of observables (Pauli operations and tensor products thereof) into groupings according to a binary relation (e.g., qubit-wise commuting):

>>> observables = [qml.PauliY(0), qml.PauliX(0) @ qml.PauliX(1), qml.PauliZ(1)]
>>> obs_groupings = group_observables(observables)
>>> obs_groupings
[[PauliX(wires=[0]) @ PauliX(wires=[1])],
 [PauliY(wires=[0]), PauliZ(wires=[1])]]

The \(C_{J}\) coefficients for each \(P_J\) Pauli word making up a Hamiltonian can also be specified along with further options, such as the Pauli-word grouping method (e.g., qubit-wise commuting) and the underlying graph-colouring algorithm (e.g., recursive largest first) used for creating the groups of observables:

>>> obs = [qml.PauliY(0), qml.PauliX(0) @ qml.PauliX(1), qml.PauliZ(1)]
>>> coeffs = [1.43, 4.21, 0.97]
>>> obs_groupings, coeffs_groupings = group_observables(obs, coeffs, 'qwc', 'rlf')
>>> obs_groupings
[[PauliX(wires=[0]) @ PauliX(wires=[1])],
 [PauliY(wires=[0]), PauliZ(wires=[1])]]
>>> coeffs_groupings
[[4.21], [1.43, 0.97]]