Skip to content

API specification

The emu-sv API is based on the specification here. Concretely, the classes are as follows:

SVBackend

Bases: EmulatorBackend

A backend for emulating Pulser sequences using state vectors and sparse matrices. Noisy simulation is supported by solving the Lindblad equation and using effective noise channel or jump operators

PARAMETER DESCRIPTION
sequence

The sequence to be executed.

TYPE: Sequence

config

Configuration for the SV backend.

TYPE: EmulationConfig | None DEFAULT: None

mimic_qpu

Whether to enforce Register constrains imposed by the device in the Sequence.

TYPE: bool DEFAULT: False

Source code in pulser/backend/abc.py
def __init__(
    self,
    sequence: pulser.Sequence,
    *,
    config: EmulationConfig | None = None,
    mimic_qpu: bool = False,
) -> None:
    """Initializes the backend."""
    super().__init__(sequence, mimic_qpu=mimic_qpu)
    self._config = self.validate_config(config or self.default_config)

    noise_model = self._config.noise_model

    if noise_model is not None:
        is_dmm_channel = any(
            isinstance(ch, DMM)
            for ch in self._sequence.declared_channels.values()
        )

        if (
            is_dmm_channel
            and "register" in noise_model.noise_types
            and noise_model.detuning_map_spot_waist is None
        ):
            raise ValueError(
                "Combining register noise with a DMM requires"
                "`detuning_map_spot_waist` to be defined. If not defined,"
                "atom thermal motion can lead to non-physical effects."
            )

    if (
        self._config.prefer_device_noise_model
        and self._sequence.device.noise_model is not None
        and self._sequence.device.noise_model.runs is not None
        and self._sequence.device.noise_model.runs
        != self._config.n_trajectories
    ):
        config = self._config
        warnings.warn(
            f"'{sequence.device.noise_model.runs=}' is being "
            f"ignored; '{config.n_trajectories=}' will be used instead.",
            stacklevel=2,
        )

run()

Emulates the given sequence.

RETURNS DESCRIPTION
Results | list[Results]

the simulation results

Source code in emu_sv/sv_backend.py
def run(self) -> Results | list[Results]:
    """
    Emulates the given sequence.

    Returns:
        the simulation results
    """
    assert isinstance(self._config, SVConfig)
    assert self._sequence._in_ising, "Only Rydberg interaction is currently supported"
    pulser_data = PulserData(
        sequence=self._sequence, config=self._config, dt=self._config.dt
    )
    results = []
    for sequence_data in pulser_data.get_sequences():
        results.append(self._run_from_sequence_data(sequence_data, self._config))
    return Results.aggregate(results)

SVConfig

Bases: EmulationConfig

The configuration of the emu-sv SVBackend. The kwargs passed to this class are passed on to the base class. See the API for that class for a list of available options.

PARAMETER DESCRIPTION
dt

the timestep size that the solver uses. Note that observables are only calculated if the evaluation_times are divisible by dt.

TYPE: float DEFAULT: 10.0

max_krylov_dim

the size of the krylov subspace that the Lanczos algorithm maximally builds

TYPE: int DEFAULT: 100

krylov_tolerance

the Lanczos algorithm uses this as the convergence tolerance

TYPE: float DEFAULT: 1e-10

gpu

whether to run the simulation on GPU - if gpu = True, use 1 GPU to store the state. (causes errors if True when GPU not available) - if gpu = False, use CPU to run the entire simulation. - if gpu = None (the default value), the backend internally chooses 1 GPU.

TYPE: bool | None DEFAULT: None

interaction_cutoff

Set interaction coefficients below this value to 0. Potentially improves runtime and memory consumption.

TYPE: float DEFAULT: 0.0

log_level

How much to log. Set to logging.WARN to get rid of the timestep info.

TYPE: int DEFAULT: INFO

log_file

If specified, log to this file rather than stout.

TYPE: Path | None DEFAULT: None

solver

The solver to use in the presence of Lindblad noise. By default it will use Monte Carlo quantum jumps if shot noise is present, and the Lindblad equation with density matrices otherwise.

TYPE: Solver DEFAULT: DEFAULT

kwargs

arguments that are passed to the base class

TYPE: Any DEFAULT: {}

Examples:

gpu = True
dt = 1.0 #this will impact the runtime
krylov_tolerance = 1e-8 #the simulation will be faster, but less accurate
SVConfig(gpu=gpu, dt=dt, krylov_tolerance=krylov_tolerance,
    with_modulation=True) #the last arg is taken from the base class
Source code in emu_sv/sv_config.py
def __init__(
    self,
    *,
    dt: float = 10.0,
    max_krylov_dim: int = 100,
    krylov_tolerance: float = 1e-10,
    gpu: bool | None = None,
    interaction_cutoff: float = 0.0,
    log_level: int = logging.INFO,
    log_file: pathlib.Path | None = None,
    solver: Solver = Solver.DEFAULT,
    **kwargs: Any,
):
    super().__init__(
        solver=solver,
        dt=dt,
        max_krylov_dim=max_krylov_dim,
        gpu=gpu,
        krylov_tolerance=krylov_tolerance,
        interaction_cutoff=interaction_cutoff,
        log_level=log_level,
        log_file=log_file,
        **kwargs,
    )

    self.monkeypatch_observables()
    logger = init_logging(log_level, log_file)

    if (self.noise_model.runs != 1 and self.noise_model.runs is not None) or (
        self.noise_model.samples_per_run != 1
        and self.noise_model.samples_per_run is not None
    ):
        logger.warning(
            "Warning: The runs and samples_per_run "
            "values of the NoiseModel are ignored!"
        )

StateVector

Bases: DenseState

Represents a quantum state vector in a computational basis.

This class extends the State class to handle state vectors, providing various utilities for initialization, normalization, manipulation, and measurement. The state vector must have a length that is a power of 2, representing 2ⁿ basis states for n qubits.

PARAMETER DESCRIPTION
data

1D tensor representation of a state vector.

TYPE: Tensor

gpu

store the vector on GPU if True and a GPU is present, otherwise on CPU

TYPE: bool DEFAULT: True

eigenstates

sequence of eigenstates used as basis only qubit basis are supported (default: ('r','g'))

TYPE: Sequence[Eigenstate] DEFAULT: ('r', 'g')

Source code in emu_sv/dense_state.py
def __init__(
    self,
    data: torch.Tensor,
    *,
    gpu: bool = True,
    eigenstates: Sequence[Eigenstate] = ("r", "g"),
):
    assert data.dim() == 1 or data.dim() == 2
    super().__init__(eigenstates=eigenstates)

    device = "cuda" if gpu and DEVICE_COUNT > 0 else "cpu"
    self.data = data.to(dtype=dtype, device=device)

__add__(other)

Sum of two state vectors

PARAMETER DESCRIPTION
other

the vector to add to this vector

TYPE: State

RETURNS DESCRIPTION
StateVector

The summed state

Source code in emu_sv/state_vector.py
def __add__(self, other: State) -> StateVector:
    """Sum of two state vectors

    Args:
        other: the vector to add to this vector

    Returns:
        The summed state
    """
    assert isinstance(other, StateVector), "`Other` state can only be a StateVector"
    assert (
        self.eigenstates == other.eigenstates
    ), f"`Other` state has basis {other.eigenstates} != {self.eigenstates}"
    return StateVector(
        self.data + other.data,
        gpu=self.data.is_cuda,
        eigenstates=self.eigenstates,
    )

__rmul__(scalar)

Scalar multiplication

PARAMETER DESCRIPTION
scalar

the scalar to multiply with

TYPE: complex

RETURNS DESCRIPTION
StateVector

The scaled state

Source code in emu_sv/state_vector.py
def __rmul__(self, scalar: complex) -> StateVector:
    """Scalar multiplication

    Args:
        scalar: the scalar to multiply with

    Returns:
        The scaled state
    """
    return StateVector(
        scalar * self.data,
        gpu=self.data.is_cuda,
        eigenstates=self.eigenstates,
    )

inner(other)

Compute . The type of other must be StateVector.

PARAMETER DESCRIPTION
other

the other state

TYPE: State

RETURNS DESCRIPTION
Tensor

the inner product

Source code in emu_sv/state_vector.py
def inner(self, other: State) -> torch.Tensor:
    """
    Compute <self|other>. The type of other must be StateVector.

    Args:
        other: the other state

    Returns:
        the inner product
    """
    assert isinstance(other, StateVector), "Other state must be a StateVector"
    assert self.data.shape == other.data.shape, "States do not have the same shape"

    # by our internal convention inner and norm return to cpu
    return torch.vdot(self.data, other.data.to(self.data.device)).cpu()

make(num_sites, gpu=True) classmethod

Returns a State vector in the ground state |00..0>.

PARAMETER DESCRIPTION
num_sites

the number of qubits

TYPE: int

gpu

whether gpu or cpu

TYPE: bool DEFAULT: True

RETURNS DESCRIPTION
StateVector

The described state

Examples:

StateVector.make(2,gpu=False)

Output:

tensor([1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j], dtype=torch.complex128)

Source code in emu_sv/state_vector.py
@classmethod
def make(cls, num_sites: int, gpu: bool = True) -> StateVector:
    """
    Returns a State vector in the ground state |00..0>.

    Args:
        num_sites: the number of qubits
        gpu: whether gpu or cpu

    Returns:
        The described state

    Examples:
        ```python
        StateVector.make(2,gpu=False)
        ```

        Output:
        ```
        tensor([1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j], dtype=torch.complex128)
        ```
    """

    result = cls.zero(num_sites=num_sites, gpu=gpu)
    result.data[0] = 1.0
    return result

norm()

Returns the norm of the state

RETURNS DESCRIPTION
Tensor

the norm of the state

Source code in emu_sv/state_vector.py
def norm(self) -> torch.Tensor:
    """Returns the norm of the state

    Returns:
        the norm of the state
    """
    nrm: torch.Tensor = torch.linalg.vector_norm(self.data).cpu()
    return nrm

zero(num_sites, gpu=True, eigenstates=('r', 'g')) classmethod

Returns a zero uninitialized "state" vector. Warning, this has no physical meaning as-is!

PARAMETER DESCRIPTION
num_sites

the number of qubits

TYPE: int

gpu

whether gpu or cpu

TYPE: bool DEFAULT: True

RETURNS DESCRIPTION
StateVector

The zero state

Examples:

StateVector.zero(2,gpu=False)

Output:

tensor([0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j], dtype=torch.complex128)

Source code in emu_sv/state_vector.py
@classmethod
def zero(
    cls,
    num_sites: int,
    gpu: bool = True,
    eigenstates: Sequence[Eigenstate] = ("r", "g"),
) -> StateVector:
    """
    Returns a zero uninitialized "state" vector. Warning, this has no physical meaning as-is!

    Args:
        num_sites: the number of qubits
        gpu: whether gpu or cpu

    Returns:
        The zero state

    Examples:
        ```python
        StateVector.zero(2,gpu=False)
        ```

        Output:
        ```
        tensor([0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j], dtype=torch.complex128)
        ```
    """

    vector = torch.zeros(2**num_sites, dtype=dtype)
    return cls(vector, gpu=gpu, eigenstates=eigenstates)

inner

Wrapper around StateVector.inner.

PARAMETER DESCRIPTION
left

StateVector argument

TYPE: StateVector

right

StateVector argument

TYPE: StateVector

RETURNS DESCRIPTION
Tensor

the inner product

Examples:

factor = math.sqrt(2.0)
basis = ("r","g")
nqubits=2
string_state1 = {"gg":1.0,"rr":1.0}
state1 = StateVector.from_state_string(basis=basis,
nqubits=nqubits,strings=string_state1)
string_state2 = {"gr":1.0/factor,"rr":1.0/factor}
state2 = StateVector.from_state_string(basis=basis,
nqubits=nqubits,strings=string_state2)
state1 = StateVector.from_state_amplitudes(eigenstates=basis,
    amplitudes=string_state1)
string_state2 = {"gr":1.0/factor,"rr":1.0/factor}
state2 = StateVector.from_state_amplitudes(eigenstates=basis,
    amplitudes=string_state2)
inner(state1,state2).item()

Output:

(0.49999999144286444+0j)

Source code in emu_sv/state_vector.py
def inner(left: StateVector, right: StateVector) -> torch.Tensor:
    """
    Wrapper around StateVector.inner.

    Args:
        left:  StateVector argument
        right: StateVector argument

    Returns:
        the inner product

    Examples:
        ```python
        factor = math.sqrt(2.0)
        basis = ("r","g")
        nqubits=2
        string_state1 = {"gg":1.0,"rr":1.0}
        state1 = StateVector.from_state_string(basis=basis,
        nqubits=nqubits,strings=string_state1)
        string_state2 = {"gr":1.0/factor,"rr":1.0/factor}
        state2 = StateVector.from_state_string(basis=basis,
        nqubits=nqubits,strings=string_state2)
        state1 = StateVector.from_state_amplitudes(eigenstates=basis,
            amplitudes=string_state1)
        string_state2 = {"gr":1.0/factor,"rr":1.0/factor}
        state2 = StateVector.from_state_amplitudes(eigenstates=basis,
            amplitudes=string_state2)
        inner(state1,state2).item()
        ```

        Output:
        ```
        (0.49999999144286444+0j)
        ```
    """

    assert (left.data.shape == right.data.shape) and (left.data.dim() == 1), (
        "Shape of left.data and right.data should be",
        " the same and both need to be 1D tesnor",
    )
    return left.inner(right)

DenseOperator

Bases: MatrixOperator

DenseOperator in emu-sv uses dense matrices. This class represents a quantum operator backed by a dense PyTorch tensor for state-vector simulation.

PARAMETER DESCRIPTION
matrix

Square complex tensor of shape (2ⁿ, 2ⁿ) representing the operator in the computational basis.

TYPE: Tensor

gpu

If True, place the operator on a CUDA device when available. Default: True (and only 1 GPU).

TYPE: bool DEFAULT: True

Source code in emu_sv/matrix_operator.py
def __init__(
    self,
    matrix: torch.Tensor,
    *,
    gpu: bool = True,
):
    super().__init__()
    device = "cuda" if gpu and DEVICE_COUNT > 0 else "cpu"
    self.data = matrix.to(dtype=dtype, device=device)

__matmul__(other)

Compose two DenseOperators via matrix multiplication.

PARAMETER DESCRIPTION
other

a DenseOperator instance.

TYPE: Operator

RETURNS DESCRIPTION
DenseOperator

A new DenseOperator representing the product self @ other.

Source code in emu_sv/dense_operator.py
def __matmul__(self, other: Operator) -> DenseOperator:
    """
    Compose two DenseOperators via matrix multiplication.

    Args:
        other: a DenseOperator instance.

    Returns:
        A new DenseOperator representing the product `self @ other`.
    """
    assert isinstance(
        other, DenseOperator
    ), "DenseOperator can only be multiplied with a DenseOperator."
    return DenseOperator(self.data @ other.data)

SparseOperator

Bases: MatrixOperator

This operator is used to represent a sparse matrix in CSR (Compressed Sparse Row) format for efficient computation on the emu-sv emulator

PARAMETER DESCRIPTION
matrix

The CSR matrix representation of the operator.

TYPE: Tensor

gpu

If True (by default), run on GPU when available; otherwise fall back to CPU. If False, always run on CPU.

TYPE: bool DEFAULT: True

Source code in emu_sv/matrix_operator.py
def __init__(
    self,
    matrix: torch.Tensor,
    *,
    gpu: bool = True,
):
    super().__init__()
    device = "cuda" if gpu and DEVICE_COUNT > 0 else "cpu"
    self.data = matrix.to(dtype=dtype, device=device)

DensityMatrix

Bases: DenseState

Represents an n-qubit density matrix ρ in the computational (|g⟩, |r⟩) basis. The input should be a square complex tensor with shape (2ⁿ, 2ⁿ), where n is the number of atoms. ρ must be Hermitian, positive semidefinite, and has trace 1. These checks are too expensive to actually perform, so it's up to the user to ensure these constraints are met.

PARAMETER DESCRIPTION
data

Square complex tensor of shape (2ⁿ, 2ⁿ), Hermitian with trace 1, that represents the state in the computational basis.

TYPE: Tensor

gpu

If True, place the operator on a CUDA device when available. Default: True.

TYPE: bool DEFAULT: True

eigenstates

sequence of eigenstates used as basis only qubit basis are supported (default: ('r','g'))

TYPE: Sequence[Eigenstate] DEFAULT: ('r', 'g')

Source code in emu_sv/dense_state.py
def __init__(
    self,
    data: torch.Tensor,
    *,
    gpu: bool = True,
    eigenstates: Sequence[Eigenstate] = ("r", "g"),
):
    assert data.dim() == 1 or data.dim() == 2
    super().__init__(eigenstates=eigenstates)

    device = "cuda" if gpu and DEVICE_COUNT > 0 else "cpu"
    self.data = data.to(dtype=dtype, device=device)

from_state_vector(state) classmethod

Convert a state vector to a density matrix. This function takes a state vector |ψ❭ and returns the corresponding density matrix ρ = |ψ❭❬ψ| representing the pure state |ψ❭.

Examples:

bell_state_vec = 0.7071 * torch.tensor([1.0, 0.0, 0.0, 1.0j],
dtype=torch.complex128)
bell_state = StateVector(bell_state_vec, gpu=False)
density = DensityMatrix.from_state_vector(bell_state)
print(density.data)

Output:

tensor([[0.5000+0.0000j, 0.0000+0.0000j, 0.0000+0.0000j, 0.0000-0.5000j],
       [0.0000+0.0000j, 0.0000+0.0000j, 0.0000+0.0000j, 0.0000+0.0000j],
       [0.0000+0.0000j, 0.0000+0.0000j, 0.0000+0.0000j, 0.0000+0.0000j],
       [0.0000+0.5000j, 0.0000+0.0000j, 0.0000+0.0000j, 0.5000+0.0000j]],
      dtype=torch.complex128)

Source code in emu_sv/density_matrix_state.py
@classmethod
def from_state_vector(cls, state: StateVector) -> DensityMatrix:
    """
    Convert a state vector to a density matrix.
    This function takes a state vector |ψ❭ and returns the corresponding
    density matrix ρ = |ψ❭❬ψ| representing the pure state |ψ❭.

    Examples:
        ```python
        bell_state_vec = 0.7071 * torch.tensor([1.0, 0.0, 0.0, 1.0j],
        dtype=torch.complex128)
        bell_state = StateVector(bell_state_vec, gpu=False)
        density = DensityMatrix.from_state_vector(bell_state)
        print(density.data)
        ```

        Output:
        ```
        tensor([[0.5000+0.0000j, 0.0000+0.0000j, 0.0000+0.0000j, 0.0000-0.5000j],
               [0.0000+0.0000j, 0.0000+0.0000j, 0.0000+0.0000j, 0.0000+0.0000j],
               [0.0000+0.0000j, 0.0000+0.0000j, 0.0000+0.0000j, 0.0000+0.0000j],
               [0.0000+0.5000j, 0.0000+0.0000j, 0.0000+0.0000j, 0.5000+0.0000j]],
              dtype=torch.complex128)
        ```
    """

    return cls(torch.outer(state.data, state.data.conj()), gpu=state.data.is_cuda)

make(n_atoms, gpu=True) classmethod

Creates the density matrix of the ground state |000...0>

Source code in emu_sv/density_matrix_state.py
@classmethod
def make(cls, n_atoms: int, gpu: bool = True) -> DensityMatrix:
    """Creates the density matrix of the ground state |000...0>"""
    result = torch.zeros(2**n_atoms, 2**n_atoms, dtype=dtype)
    result[0, 0] = 1.0
    return cls(result, gpu=gpu)

norm()

Returns the norm of the state

RETURNS DESCRIPTION
Tensor

the norm of the state

Source code in emu_sv/density_matrix_state.py
def norm(self) -> torch.Tensor:
    """Returns the norm of the state

    Returns:
        the norm of the state
    """
    nrm: torch.Tensor = torch.trace(self.data).real.cpu()
    return nrm