OperatorRepr

class pulser.backend.OperatorRepr

Bases: Operator

Define a backend-independent quantum operator representation.

Allows the user to define a quantum operator with the dedicated class method from_operator_repr, which requires:

  • eigenstates: The basis states (e.g., (‘r’, ‘g’)).

  • n_qudits: Number of qudits in the system.

  • operations: A sequence of tuples weight, tensor operators on each qudit, as described in from_operator_repr.

The created operator, supports de/serialization methods for remote backend execution.

Examples

>>> eigenstates = ("r", "g")
>>> n_qudits = 4
>>> # define X,Y,Z
>>> X = {"gr": 1.0, "rg": 1.0}
>>> Y = {"gr": 1.0j, "rg": -1.0j}
>>> Z = {"rr": 1.0, "gg": -1.0}
>>> # build for example 0.5*X0Y1X2Z3
>>> operations = [
>>>     (
>>>         0.5,
>>>         [
>>>             (X, [0, 2]), # acts on qudit 0 and 2
>>>             (Y, [1]),
>>>             (Z, [3]),
>>>         ],
>>>     )
>>> ]
>>> op = OperatorRepr.from_operator_repr(
>>>     eigenstates=eigenstates,
>>>     n_qudits=n_qudits,
>>>     operations=operations
>>> )

Methods

apply_to

apply_to not implemented in OperatorRepr.

expect

expect not implemented in OperatorRepr.

from_operator

Creates a new operator of this type from another operator.

from_operator_repr

Create an operator from the operator representation.

from_pauli_string

Create the operator for a Pauli string.

Signatures

apply_to(state, /)

apply_to not implemented in OperatorRepr.

Return type:

TypeVar(StateType, bound= State)

expect(state, /)

expect not implemented in OperatorRepr.

Return type:

None

classmethod from_operator(operator)

Creates a new operator of this type from another operator.

By default, the operator is converted through its abstract representation, which is only possible if it was created via from_operator_repr(). If the operator is already an instance of this type, a shallow copy of it is returned instead, so it does not need to be serializable.

Subclasses can override this method to support other conversions (e.g. directly from an operator type they know).

Parameters:

operator (Operator) – The operator to convert.

Return type:

TypeVar(OperatorType, bound= Operator)

Returns:

A new operator of this type, equivalent to the given one.

Raises:

TypeError – If the operator can’t be converted to this type.

classmethod from_operator_repr(*, eigenstates, n_qudits, operations)

Create an operator from the operator representation.

Only operators constructed with this method are allowed in remote backend.

The full operator representation (FullOp) is a weigthed sum of tensor operators (TensorOp), written as a sequence of coefficient and tensor operator pairs, ie

FullOp = Sequence[tuple[ScalarType, TensorOp]]

Each TensorOp is itself a sequence of qudit operators (QuditOp) applied to mutually exclusive sets of qudits (represented by their indices), ie

TensorOp = Sequence[tuple[QuditOp, Collection[int]]]

Qudits without an associated QuditOp are applied the identity operator.

Finally, each QuditOp is represented as weighted sum of pre-defined single-qudit operators. It is given as a mapping between a string representation of the single-qudit operator and its respective coefficient, ie

QuditOp = Mapping[str, ScalarType]

By default, it identifies strings "ij" as single-qudit operators, where i and j are eigenstates that denote |i><j|.

Parameters:
  • eigenstates (Sequence[Literal['u', 'd', 'r', 'g', 'h', 'x'] | Literal['0', '1']]) – The eigenstates to use.

  • n_qudits (int) – How many qudits there are in the system.

  • operations (Sequence[tuple[TypeVar(ArgScalarType), Sequence[tuple[Mapping[str, TypeVar(ArgScalarType)], Collection[int]]]]]) – The full operator representation.

Return type:

TypeVar(OperatorType, bound= Operator)

Returns:

The constructed operator.

Examples

>>> eigenstates = ("r", "g")
>>> n_qudits = 4
>>> # define X,Y,Z
>>> X = {"gr": 1.0, "rg": 1.0}
>>> Y = {"gr": 1.0j, "rg": -1.0j}
>>> Z = {"rr": 1.0, "gg": -1.0}
>>> # build for example 0.5*X0Y1X2Z3 - j0.2*Z1Y3 + (-0.1+j4.0)*X2
>>> operations = [
>>>     (
>>>         0.5,
>>>         [
>>>             (X, [0, 2]), # acts on qudit 0 and 2
>>>             (Y, [1]),
>>>             (Z, [3]),
>>>         ],
>>>     ),
>>>     (
>>>         -0.2j,
>>>         [
>>>             (Z, [1]),
>>>             (Y, [3]),
>>>         ],
>>>     ),
>>>     (
>>>         -0.1 + 4.0j,
>>>         [
>>>             (X, [2]),
>>>         ],
>>>     ),
>>> ]
>>> config_class = your_pulser_backend.config_type
>>> operator_class = config_class.operator_type
>>> op = operator_class.from_operator_repr(
>>>     eigenstates=eigenstates,
>>>     n_qudits=n_qudits,
>>>     operations=operations
>>> )
classmethod from_pauli_string(*, eigenstates, n_qudits, paulis)

Create the operator for a Pauli string.

A Pauli string is a product of Pauli matrices acting on different qudits, e.g. σ^z_1 σ^x_3 σ^z_10. Qudits without an associated Pauli matrix are applied the identity. The resulting operator is built with from_operator_repr(), so it is supported by every backend (including remote ones) and can be given to the Expectation observable to compute its expectation value.

The Pauli matrices are defined following the order of the eigenstates. For eigenstates (e0, e1), they are

  • σ^x = |e0><e1| + |e1><e0|

  • σ^y = -i|e0><e1| + i|e1><e0|

  • σ^z = |e0><e0| - |e1><e1|

which matches the conventions of pulser-simulation for both the ground-rydberg (("r", "g")) and XY (("0", "1")) bases.

The leakage state "x" (added to the eigenstates when the noise model includes leakage) is not one of the two states the Pauli matrices act on, so all Pauli matrices act as zero on it. As such, population in the leakage state does not contribute to the expectation value of a Pauli string. Bases with more than two eigenstates besides "x" are not supported.

Parameters:
  • eigenstates (Sequence[Literal['u', 'd', 'r', 'g', 'h', 'x'] | Literal['0', '1']]) – The eigenstates to use. Must contain exactly two eigenstates, besides the optional leakage state "x".

  • n_qudits (int) – How many qudits there are in the system.

  • paulis (Mapping[int, str]) – A mapping between the index of a qudit and the Pauli matrix applied to it, given as one of "X", "Y" or "Z" (case-insensitive). The qudit indices follow the order of the qubit IDs in the Sequence’s register (i.e. seq.register.qubit_ids).

Return type:

TypeVar(OperatorType, bound= Operator)

Returns:

The operator for the Pauli string.

Example

>>> # σ^z_1 σ^x_3 σ^z_10 in a system of 12 qubits
>>> operator_class = your_pulser_backend.config_type.operator_type
>>> op = operator_class.from_pauli_string(
>>>     eigenstates=("r", "g"),
>>>     n_qudits=12,
>>>     paulis={1: "Z", 3: "X", 10: "Z"},
>>> )
>>> # To compute its expectation value during the emulation
>>> Expectation(op, tag_suffix="zxz")