qml.pauli.PauliVSpace

class PauliVSpace(generators, dtype=<class 'float'>, tol=None)[source]

Bases: object

Class representing the linearly independent basis of a vector space.

The main purpose of this class is to store and process M, which is a dictionary-of-keys (DOK) style sparse representation of the set of basis vectors. You can think of it as the numpy-equivalent of a PauliSentence: each PauliWord (key of PauliSentence) represents one row of M with the coefficient (value of PauliSentence). For example the set of 3 linearly independent generators X(0) + X(1), X(0) + X(2), X(0) + 0.5 * Y(0) can be represented as

[
    [1, 1, 1],
    [1, 0, 0],
    [0, 1, 0],
    [0, 0, 0.5]
]

where each column represents one sentence, and each row represents the coefficient of the respective word in the sentence. To make sense of this representation one additionally needs to keep track of the mapping between keys and rows. In this case we have

pw_to_idx = {
    X(0) : 0,
    X(1) : 1,
    X(2) : 2,
    Y(0) : 3
}

where we have set the numbering based on appearance in the list of generators. This mapping is in general not unique.

Parameters
  • generators (Iterable[Union[PauliWord, PauliSentence, Operator]]) – Operators that span the vector space.

  • dtype (type) – dtype of the underlying DOK sparse matrix M. Default is float.

  • tol (float) – Numerical tolerance for the linear independence check. If the norm of the projection of the candidate vector onto \(M^\perp\) is greater than tol, then it is deemed to be linearly independent.

Example

Take a linearly dependent set of operators and span the PauliVSpace.

ops = [
    X(0) @ X(1) + Y(0) @ Y(1),
    X(0) @ X(1),
    Y(0) @ Y(1)
]

vspace = PauliVSpace(ops)

It automatically detects that the third operator is linearly dependent on the former two, so it does not add the third operator to the basis.

>>> vspace.basis
[1.0 * X(0) @ X(1)
 + 1.0 * Y(0) @ Y(1),
 1.0 * X(0) @ X(1)]

We can also retrospectively add operators.

>>> vspace.add(qml.X(0))
[1.0 * X(0) @ X(1)
 + 1.0 * Y(0) @ Y(1),
 1.0 * X(0) @ X(1),
 1.0 * X(0)]

Again, checks of linear independence are always performed. So in the following example no operator is added.

>>> vspace.add(Y(0) @ Y(1))
[1.0 * X(0) @ X(1)
 + 1.0 * Y(0) @ Y(1),
 1.0 * X(0) @ X(1),
 1.0 * X(0)]

basis

List of basis operators of PauliVSpace

basis

List of basis operators of PauliVSpace

add(other[, tol])

Adding Pauli sentences if they are linearly independent.

is_independent(pauli_sentence[, tol])

Check if the pauli_sentence is linearly independent of the basis of PauliVSpace.

add(other, tol=None)[source]

Adding Pauli sentences if they are linearly independent.

Parameters
  • other (List[PauliWord, PauliSentence, Operator]) – List of candidate operators to add to the PauliVSpace, if they are linearly independent.

  • tol (float) – Numerical tolerance for linear independence check. Defaults to 1e-15.

Returns

New basis vectors after adding the linearly independent ones from other.

Return type

List

Example

We can generate a PauliVSpace and add a linearly independent operator to its basis.

>>> ops = [X(0), X(1)]
>>> vspace = qml.pauli.PauliVSpace(ops)
>>> vspace.add(Y(0))
>>> vspace
[1.0 * X(0), 1.0 * X(1), 1.0 * Y(0)]

We can add a list of operators at once. Only those that are linearly dependent with the current PauliVSpace are added.

>>> vspace.add([Z(0), X(0)])
[1.0 * X(0), 1.0 * X(1), 1.0 * Y(0), 1.0 * Z(0)]
is_independent(pauli_sentence, tol=None)[source]

Check if the pauli_sentence is linearly independent of the basis of PauliVSpace.

Parameters
  • pauli_sentence (~.PauliSentence) – Candidate Pauli sentence to check against the PauliVSpace basis for linear independence.

  • tol (float) – Numerical tolerance for linear independence check. Defaults to 1e-15.

Returns

whether pauli_sentence was linearly independent

Return type

bool

Example

>>> ops = [X(0), X(1)]
>>> vspace = PauliVSpace([op.pauli_rep for op in ops])
>>> vspace.is_independent(X(0).pauli_rep)
False
>>> vspace.is_independent(Y(0).pauli_rep)
True