Phase-space Representation of Stabilizer States#

A stabilizer state \(\ketbra{\psi}{\psi}\) can be uniquely defined by the set of its stabilizers, i.e. those unitary operators \(U\) that have \(\psi\) as an eigenstate with eigenvalue \(1\). In general, \(n\)-qubit stabilizer states are stabilized by \(d = 2^n\) Pauli operators on said \(n\) qubits. However, it is known that the set of \(d\) Paulis can be generated by only \(n\) unique members of the set. In that case, indeed, the number of operators needed to represent a stabilizer state reduces to \(n\). Each one of these \(n\) Pauli generators takes \(2n + 1\) bits to specify, yielding a \(n(2n+1)\) total number of bits needed. In particular, Aaronson and Gottesman (2004) demonstrated that the application of Clifford gates on stabilizer states can be efficiently simulated in this representation at the cost of storing the generators of the destabilizers, in addition to the stabilizers.

A \(n\)-qubit stabilizer state is uniquely defined by a symplectic matrix of the form

../../_images/symplectic_matrix.png

where \((x_{kl},z_{kl})\) are the bits encoding the \(n\)-qubits Pauli generator as

\[P_{k} = \bigotimes_{l=1}^{n} \, i^{x_{kl} \, \oplus \, z_{kl}} \, X_{l}^{x_{kl}} \, Z_{l}^{z_{kl}}.\]

The qibo.quantum_info.clifford.Clifford object is in charge of storing the phase-space representation of a stabilizer state. This object is automatically created after the execution of a Clifford circuit through the qibo.backends.clifford.CliffordBackend, but it can also be created by directly passing a symplectic matrix to the constructor.

The generators of the stabilizers can be extracted with the qibo.quantum_info.clifford.Clifford.generators() method, or the complete set of \(d = 2^{n}\) stabilizers operators can be extracted through the qibo.quantum_info.clifford.Clifford.stabilizers() method.

generators, phases = clifford.generators()
stabilizers = clifford.stabilizers()

The destabilizers can be extracted analogously with qibo.quantum_info.clifford.Clifford.destabilizers().

We provide integration with the stim package. It is possible to run Clifford circuits using stim as a platform:

from qibo.backends import CliffordBackend
from qibo.quantum_info import Clifford, random_clifford

clifford_backend = CliffordBackend(platform="stim")

circuit = random_clifford(nqubits)
result = clifford_backend.execute_circuit(circuit)

## Note that the execution above is equivalent to the one below

result = Clifford.from_circuit(circuit, platform="stim")
class qibo.quantum_info.clifford.Clifford(data, nqubits=None, measurements=None, nshots=1000, platform=None, _backend=None, _measurement_gate=None, _samples=None)[source]#

Object storing the results of a circuit execution with the qibo.backends.clifford.CliffordBackend.

Parameters:
  • data (ArrayLike or qibo.models.circuit.Circuit) – If ArrayLike, it is the symplectic matrix of the stabilizer state in phase-space representation. If qibo.models.circuit.Circuit, it is a circuit composed only of Clifford gates and computational-basis measurements.

  • nqubits (int, optional) – number of qubits of the state.

  • measurements (list, optional) – list of measurements gates qibo.gates.M. Defaults to None.

  • nshots (int, optional) – number of shots used for sampling the measurements. Defaults to \(1000\).

  • platform (str, optional) –

    platform to use in the execution of the qibo.backends.CliffordBackend. It accepts "numpy", "numba", "cupy", and "stim" (see stim). If None, defaults to the corresponding platform from the current backend. Defaults to None.

  • _backend (CliffordBackend | None) –

  • _measurement_gate (M | None) –

  • _samples (_Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes] | None) –

classmethod from_circuit(circuit, initial_state=None, nshots=1000, platform=None)[source]#

Allows to create a qibo.quantum_info.clifford.Clifford object by executing the input circuit.

Parameters:
  • circuit (qibo.models.circuit.Circuit) – Clifford circuit to run.

  • initial_state (ArrayLike, optional) – symplectic matrix of the initial state. If None, defaults to the symplectic matrix of the zero state. Defaults to None.

  • nshots (int, optional) – number of measurement shots to perform if circuit has measurement gates. Defaults to \(10^{3}\).

  • platform (str, optional) –

    engine to use in the execution of the qibo.backends.CliffordBackend. It accepts "numpy", "numba", "cupy", and "stim" (see stim). If None, defaults to the corresponding platform from the current backend. Defaults to None.

Returns:

Object storing the result of the circuit execution.

Return type:

qibo.quantum_info.clifford.Clifford

to_circuit(algorithm='AG04', **kwargs)[source]#

Converts symplectic matrix into a Clifford circuit.

Parameters:
Returns:

circuit composed of Clifford gates.

Return type:

qibo.models.circuit.Circuit

generators(return_array=False)[source]#

Extracts the generators of stabilizers and destabilizers.

Parameters:

return_array (bool, optional) – If True returns the generators as ArrayLike. If False, their representation as strings is returned. Defaults to False.

Returns:

Generators and their corresponding phases, respectively.

Return type:

(list, list)

stabilizers(symplectic=False, return_array=False)[source]#

Extracts the stabilizers of the state.

Parameters:
  • symplectic (bool, optional) – If True, returns the rows of the symplectic matrix that correspond to the \(n\) generators of the \(2^{n}\) total stabilizers, independently of return_array.

  • return_array (bool, optional) – To be used when symplectic = False. If True returns the stabilizers as ArrayLike. If False, returns stabilizers as strings. Defaults to False.

Returns:

Stabilizers of the state.

Return type:

(ArrayLike or list)

destabilizers(symplectic=False, return_array=False)[source]#

Extracts the destabilizers of the state.

Parameters:
  • symplectic (bool, optional) – If True, returns the rows of the symplectic matrix that correspond to the \(n\) generators of the \(2^{n}\) total destabilizers, independently of return_array.

  • return_array (bool, optional) – To be used when symplectic = False. If True returns the destabilizers as ArrayLike. If False, their representation as strings is returned. Defaults to False.

Returns:

Destabilizers of the state.

Return type:

(ArrayLike or list)

state()[source]#

Builds the density matrix representation of the state.

Note

This method is inefficient in runtime and memory for a large number of qubits.

Returns:

Density matrix of the state.

Return type:

ArrayLike

property measurement_gate: M#

Single measurement gate containing all measured qubits.

Useful for sampling all measured qubits at once when simulating.

samples(binary=True, registers=False)[source]#

Returns raw measurement samples.

Parameters:
  • binary (bool, optional) – If True, returns samples in binary form. If False, returns samples in decimal form. Defaults to True.

  • registers (bool, optional) – If True, groups samples according to registers. Defaults to False.

Returns:

If binary is True

samples are returned in binary form as a tensor of shape (nshots, n_measured_qubits).

If binary is False

samples are returned in decimal form as a tensor of shape (nshots,).

If registers is True

samples are returned in a dict where the keys are the register names and the values are the samples tensors for each register.

If registers is False

a single tensor is returned which contains samples from all the measured qubits, independently of their registers.

Return type:

_Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes] | dict

frequencies(binary=True, registers=False)[source]#

Returns the frequencies of measured samples.

Parameters:
  • binary (bool, optional) – If True, return frequency keys in binary form. If False, return frequency keys in decimal form. Defaults to True.

  • registers (bool, optional) – If True, groups frequencies according to registers. Defaults to False.

Returns:

A collections.Counter where the keys are the observed values and the values the corresponding frequencies, that is the number of times each measured value/bitstring appears.

If binary is True

the keys of the collections.Counter are in binary form, as strings of \(0\) and \(1\).

If binary is False

the keys of the collections.Counter are integers.

If registers is True

a dict of collections.Counter is returned where keys are the name of each register.

If registers is False

a single collections.Counter is returned which contains samples from all the measured qubits, independently of their registers.

Return type:

_Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes] | dict

probabilities(qubits=None)[source]#

Computes the probabilities of the selected qubits from the measured samples.

Parameters:

qubits (tuple or list, optional) – Qubits for which to compute the probabilities.

Returns:

Measured probabilities.

Return type:

ArrayLike

copy(deep=False)[source]#

Returns copy of qibo.quantum_info.clifford.Clifford object.

Parameters:

deep (bool, optional) – If True, creates another copy in memory. Defaults to False.

Returns:

copy of original Clifford object.

Return type:

qibo.quantum_info.clifford.Clifford