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.

compute_partition_indices(observables[, …])

Computes the partition indices of a list of observables using a specified grouping type and graph colouring method.

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 into a matrix where each row is the binary vector (symplectic) representation of the observables.

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_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 (symplectic) 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.

PauliVSpace(generators[, dtype, tol])

Class representing the linearly independent basis of a vector space.

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]]

For a larger example of how grouping can be used with PennyLane, check out the Measurement Optimization demo.

Dynamical Lie Algebras

PennyLane provides support for working with dynamical Lie algebras (DLA) of Pauli operators. See our introduction to Dynamical Lie Algebras for quantum practitioners.

lie_closure(generators[, max_iterations, …])

Compute the dynamical Lie algebra from a set of generators.

structure_constants(g[, pauli])

Compute the structure constants that make up the adjoint representation of a Lie algebra.

center(g[, pauli])

A function to compute the center of a Lie algebra.