Skip to content

Base classes

Backend

Bases: ABC

Base class for different emulation backends. Forces backends to implement a run method.

run(sequence, config) abstractmethod

Emulates the given sequence.

PARAMETER DESCRIPTION
sequence

a Pulser sequence to simulate

TYPE: Sequence

config

the config. Should be of the appropriate type for the backend

TYPE: BackendConfig

RETURNS DESCRIPTION
Results

the simulation results

Source code in emu_base/base_classes/backend.py
@abstractmethod
def run(self, sequence: Sequence, config: BackendConfig) -> Results:
    """
    Emulates the given sequence.

    Args:
        sequence: a Pulser sequence to simulate
        config: the config. Should be of the appropriate type for the backend

    Returns:
        the simulation results
    """
    pass

BackendConfig

The base backend configuration.

PARAMETER DESCRIPTION
observables

a list of callbacks to compute observables

TYPE: list['Callback'] | None DEFAULT: None

with_modulation

if True, run the sequence with hardware modulation

TYPE: bool DEFAULT: False

noise_model

The pulser.NoiseModel to use in the simulation.

TYPE: NoiseModel DEFAULT: None

interaction_matrix

When specified, override the interaction terms in the Hamiltonian. This corresponds to the \(U_{ij}\) terms in the documentation. Must be symmetric.

TYPE: list[list[float]] | None DEFAULT: None

interaction_cutoff

set interaction coefficients smaller than this to 0. This can improve the memory profile of the application for some backends.

TYPE: float DEFAULT: 0.0

log_level

The output verbosity. Should be one of the constants from logging.

TYPE: int DEFAULT: INFO

log_file

a path to a file where to store the log, instead of printing to stdout

TYPE: Path | None DEFAULT: None

Examples:

>>> observables = [BitStrings(400, 100)] #compute 100 bitstrings at 400ns
>>> noise_model = pulser.noise_model.NoiseModel()
>>> interaction_matrix = [[1 for _ in range(nqubits)] for _ in range(nqubits)]
>>> interaction_cutoff = 2.0 #this will turn off all the above interactions again
>>> log_level = logging.warn
Source code in emu_base/base_classes/config.py
def __init__(
    self,
    *,
    # "Callback" is a forward type reference because of the circular import otherwise.
    observables: list["Callback"] | None = None,  # type: ignore # noqa: F821
    with_modulation: bool = False,
    noise_model: NoiseModel = None,
    interaction_matrix: list[list[float]] | None = None,
    interaction_cutoff: float = 0.0,
    log_level: int = logging.INFO,
    log_file: pathlib.Path | None = None,
):
    if observables is None:
        observables = []
    self.callbacks = (
        observables  # we can add other types of callbacks, and just stack them
    )
    self.with_modulation = with_modulation
    self.noise_model = noise_model

    if interaction_matrix is not None and (
        not isinstance(interaction_matrix, list)
        or not isinstance(interaction_matrix[0], list)
    ):
        raise ValueError(
            "Interaction matrix must be provided as a Python list of lists of floats"
        )

    self.interaction_matrix = interaction_matrix
    self.interaction_cutoff = interaction_cutoff
    self.logger = logging.getLogger("global_logger")
    if log_file is None:
        logging.basicConfig(
            level=log_level, format="%(message)s", stream=sys.stdout, force=True
        )  # default to stream = sys.stderr
    else:
        logging.basicConfig(
            level=log_level,
            format="%(message)s",
            filename=str(log_file),
            filemode="w",
            force=True,
        )
    if noise_model is not None and (
        noise_model.runs != 1
        or noise_model.samples_per_run != 1
        or noise_model.runs is not None
        or noise_model.samples_per_run is not None
    ):
        self.logger.warning(
            "Warning: The runs and samples_per_run values of the NoiseModel are ignored!"
        )

Results

This class contains emulation results. Since the results written by an emulator are defined through callbacks, the contents of this class are not known a-priori.

get_result(name, time)

get the given result at the given time

PARAMETER DESCRIPTION
name

name of the result to get

TYPE: str

time

time in ns at which to get the result

TYPE: int

RETURNS DESCRIPTION
Any

the result

Source code in emu_base/base_classes/results.py
def get_result(self, name: str, time: int) -> Any:
    """
    get the given result at the given time

    Args:
        name: name of the result to get
        time: time in ns at which to get the result

    Returns:
        the result

    """
    return self._results[name][time]

get_result_names()

get a list of results present in this object

Args:

RETURNS DESCRIPTION
list[str]

list of results by name

Source code in emu_base/base_classes/results.py
def get_result_names(self) -> list[str]:
    """
    get a list of results present in this object

    Args:

    Returns:
        list of results by name

    """
    return list(self._results.keys())

get_result_times(name)

get a list of times for which the given result has been stored

PARAMETER DESCRIPTION
name

name of the result to get times of

TYPE: str

RETURNS DESCRIPTION
list[int]

list of times in ns

Source code in emu_base/base_classes/results.py
def get_result_times(self, name: str) -> list[int]:
    """
    get a list of times for which the given result has been stored

    Args:
        name: name of the result to get times of

    Returns:
        list of times in ns

    """
    return list(self._results[name].keys())

State

Bases: ABC

Base class enforcing an API for quantum states. Each backend will implement its own type of state, and the below methods.

__add__(other) abstractmethod

Computes the sum of two states.

PARAMETER DESCRIPTION
other

the other state

TYPE: State

RETURNS DESCRIPTION
State

the summed state

Source code in emu_base/base_classes/state.py
@abstractmethod
def __add__(self, other: State) -> State:
    """
    Computes the sum of two states.

    Args:
        other: the other state

    Returns:
        the summed state
    """
    pass

__rmul__(scalar) abstractmethod

Scale the state by a scale factor.

PARAMETER DESCRIPTION
scalar

the scale factor

TYPE: complex

RETURNS DESCRIPTION
State

the scaled state

Source code in emu_base/base_classes/state.py
@abstractmethod
def __rmul__(self, scalar: complex) -> State:
    """
    Scale the state by a scale factor.

    Args:
        scalar: the scale factor

    Returns:
        the scaled state
    """
    pass

from_state_string(*, basis, nqubits, strings, **kwargs) abstractmethod staticmethod

Construct a state from the pulser abstract representation https://www.notion.so/pasqal/Abstract-State-and-Operator-Definition For a list of existing bases, see https://pulser.readthedocs.io/en/stable/conventions.html

PARAMETER DESCRIPTION
basis

A tuple containing the basis states.

TYPE: Iterable[str]

nqubits

the number of qubits.

TYPE: int

strings

A dictionary mapping state strings to complex or floats amplitudes

TYPE: dict[str, complex]

RETURNS DESCRIPTION
State

the state in whatever format the backend provides.

Examples:

>>> afm_string_state = {"rrr": 1.0 / math.sqrt(2), "ggg": 1.0 / math.sqrt(2)}
>>> afm_state = State.from_state_string(
>>>     basis=("r", "g"), nqubits=3, strings=afm_string_state
>>> )
Source code in emu_base/base_classes/state.py
@staticmethod
@abstractmethod
def from_state_string(
    *, basis: Iterable[str], nqubits: int, strings: dict[str, complex], **kwargs: Any
) -> State:
    """
    Construct a state from the pulser abstract representation
    <https://www.notion.so/pasqal/Abstract-State-and-Operator-Definition>
    For a list of existing bases, see
    <https://pulser.readthedocs.io/en/stable/conventions.html>

    Args:
        basis: A tuple containing the basis states.
        nqubits: the number of qubits.
        strings: A dictionary mapping state strings to complex or floats amplitudes

    Returns:
        the state in whatever format the backend provides.

    Examples:
        >>> afm_string_state = {"rrr": 1.0 / math.sqrt(2), "ggg": 1.0 / math.sqrt(2)}
        >>> afm_state = State.from_state_string(
        >>>     basis=("r", "g"), nqubits=3, strings=afm_string_state
        >>> )
    """
    pass

inner(other) abstractmethod

Compute the inner product between this state and other. Note that self is the left state in the inner product, so this function is linear in other, and anti-linear in self

PARAMETER DESCRIPTION
other

the other state

TYPE: State

RETURNS DESCRIPTION
float | complex

inner product

Source code in emu_base/base_classes/state.py
@abstractmethod
def inner(self, other: State) -> float | complex:
    """
    Compute the inner product between this state and other.
    Note that self is the left state in the inner product,
    so this function is linear in other, and anti-linear in self

    Args:
        other: the other state

    Returns:
        inner product
    """
    pass

sample(num_shots, p_false_pos=0.0, p_false_neg=0.0) abstractmethod

Sample bitstrings from the state, taking into account error rates.

PARAMETER DESCRIPTION
num_shots

how many bitstrings to sample

TYPE: int

p_false_pos

the rate at which a 0 is read as a 1

TYPE: float DEFAULT: 0.0

p_false_neg

the rate at which a 1 is read as a 0

TYPE: float DEFAULT: 0.0

RETURNS DESCRIPTION
Counter[str]

the measured bitstrings, by count

Source code in emu_base/base_classes/state.py
@abstractmethod
def sample(
    self, num_shots: int, p_false_pos: float = 0.0, p_false_neg: float = 0.0
) -> Counter[str]:
    """
    Sample bitstrings from the state, taking into account error rates.

    Args:
        num_shots: how many bitstrings to sample
        p_false_pos: the rate at which a 0 is read as a 1
        p_false_neg: the rate at which a 1 is read as a 0

    Returns:
        the measured bitstrings, by count
    """
    pass

Operator

Bases: ABC

__add__(other) abstractmethod

Computes the sum of two operators.

PARAMETER DESCRIPTION
other

the other operator

TYPE: Operator

RETURNS DESCRIPTION
Operator

the summed operator

Source code in emu_base/base_classes/operator.py
@abstractmethod
def __add__(self, other: Operator) -> Operator:
    """
    Computes the sum of two operators.

    Args:
        other: the other operator

    Returns:
       the summed operator
    """
    pass

__matmul__(other) abstractmethod

Compose two operators. The ordering is that self is applied after other.

PARAMETER DESCRIPTION
other

the operator to compose with self

TYPE: Operator

RETURNS DESCRIPTION
Operator

the composed operator

Source code in emu_base/base_classes/operator.py
@abstractmethod
def __matmul__(self, other: Operator) -> Operator:
    """
    Compose two operators. The ordering is that
    self is applied after other.

    Args:
        other: the operator to compose with self

    Returns:
        the composed operator
    """
    pass

__mul__(other) abstractmethod

Apply the operator to a state

PARAMETER DESCRIPTION
other

the state to apply this operator to

TYPE: State

RETURNS DESCRIPTION
State

the resulting state

Source code in emu_base/base_classes/operator.py
@abstractmethod
def __mul__(self, other: State) -> State:
    """
    Apply the operator to a state

    Args:
        other: the state to apply this operator to

    Returns:
        the resulting state
    """
    pass

__rmul__(scalar) abstractmethod

Scale the operator by a scale factor.

PARAMETER DESCRIPTION
scalar

the scale factor

TYPE: complex

RETURNS DESCRIPTION
Operator

the scaled operator

Source code in emu_base/base_classes/operator.py
@abstractmethod
def __rmul__(self, scalar: complex) -> Operator:
    """
    Scale the operator by a scale factor.

    Args:
        scalar: the scale factor

    Returns:
        the scaled operator
    """
    pass

expect(state) abstractmethod

Compute the expectation value of self on the given state.

PARAMETER DESCRIPTION
state

the state with which to compute

TYPE: State

RETURNS DESCRIPTION
float | complex

the expectation

Source code in emu_base/base_classes/operator.py
@abstractmethod
def expect(self, state: State) -> float | complex:
    """
    Compute the expectation value of self on the given state.

    Args:
        state: the state with which to compute

    Returns:
        the expectation
    """

from_operator_string(basis, nqubits, operations, operators={}, /, **kwargs) abstractmethod staticmethod

Create an operator in the backend-specific format from the pulser abstract representation https://www.notion.so/pasqal/Abstract-State-and-Operator-Definition By default it supports strings 'ij', where i and j in basis, to denote |i><j|, but additional symbols can be defined in operators For a list of existing bases, see https://pulser.readthedocs.io/en/stable/conventions.html

PARAMETER DESCRIPTION
basis

the eigenstates in the basis to use

TYPE: Iterable[str]

nqubits

how many qubits there are in the state

TYPE: int

operations

which bitstrings make up the state with what weight

TYPE: FullOp

operators

additional symbols to be used in operations

TYPE: dict[str, QuditOp] DEFAULT: {}

RETURNS DESCRIPTION
Operator

the operator in whatever format the backend provides.

Examples:

>>> basis = {"r", "g"} #rydberg basis
>>> nqubits = 3 #or whatever
>>> x = {"rg": 1.0, "gr": 1.0}
>>> z = {"gg": 1.0, "rr": -1.0}
>>> operators = {"X": x, "Z": z} #define X and Z as conveniences
>>>
>>> operations = [ # 4 X1X + 3 1Z1
>>>     (
>>>         1.0,
>>>         [
>>>             ({"X": 2.0}, [0, 2]),
>>>             ({"Z": 3.0}, [1]),
>>>         ],
>>>     )
>>> ]
>>> op = Operator.from_operator_string(basis, nqubits, operations, operators)
Source code in emu_base/base_classes/operator.py
@staticmethod
@abstractmethod
def from_operator_string(
    basis: Iterable[str],
    nqubits: int,
    operations: FullOp,
    operators: dict[str, QuditOp] = {},
    /,
    **kwargs: Any,
) -> Operator:
    """
    Create an operator in the backend-specific format from the
    pulser abstract representation
    <https://www.notion.so/pasqal/Abstract-State-and-Operator-Definition>
    By default it supports strings 'ij', where i and j in basis,
    to denote |i><j|, but additional symbols can be defined in operators
    For a list of existing bases, see
    <https://pulser.readthedocs.io/en/stable/conventions.html>

    Args:
        basis: the eigenstates in the basis to use
        nqubits: how many qubits there are in the state
        operations: which bitstrings make up the state with what weight
        operators: additional symbols to be used in operations

    Returns:
        the operator in whatever format the backend provides.

    Examples:
        >>> basis = {"r", "g"} #rydberg basis
        >>> nqubits = 3 #or whatever
        >>> x = {"rg": 1.0, "gr": 1.0}
        >>> z = {"gg": 1.0, "rr": -1.0}
        >>> operators = {"X": x, "Z": z} #define X and Z as conveniences
        >>>
        >>> operations = [ # 4 X1X + 3 1Z1
        >>>     (
        >>>         1.0,
        >>>         [
        >>>             ({"X": 2.0}, [0, 2]),
        >>>             ({"Z": 3.0}, [1]),
        >>>         ],
        >>>     )
        >>> ]
        >>> op = Operator.from_operator_string(basis, nqubits, operations, operators)
    """
    pass

Callback

Bases: ABC

The callback base class that can be subclassed to add new kinds of results to the Results object returned by the Backend

PARAMETER DESCRIPTION
evaluation_times

the times at which to add a result to Results

TYPE: set[int]

Source code in emu_base/base_classes/callback.py
def __init__(self, evaluation_times: set[int]):
    """
    The callback base class that can be subclassed to add new kinds of results
    to the Results object returned by the Backend

    Args:
        evaluation_times: the times at which to add a result to Results
    """
    self.evaluation_times = evaluation_times

default_aggregation_type property

Defines how to combine by default multiple values from different simulation results. None means no default, therefore aggregator function is always user-provided.

name abstractmethod property

The name of the observable, can be used to index into the Results object. Some Callbacks might have multiple instances, such as a callback to compute a fidelity on some given state. In that case, this method could make sure each instance has a unique name.

RETURNS DESCRIPTION
str

the name of the callback

__call__(config, t, state, H, result)

This function is called after each time step performed by the emulator. By default it calls apply to compute a result and put it in result if t in self.evaluation_times. It can be overloaded to define any custom behaviour for a Callback.

PARAMETER DESCRIPTION
config

the config object passed to the run method

TYPE: BackendConfig

t

the current time in ns

TYPE: int

state

the current state

TYPE: State

H

the Hamiltonian at this time

TYPE: Operator

result

the results object

TYPE: Results

Source code in emu_base/base_classes/callback.py
def __call__(
    self, config: BackendConfig, t: int, state: State, H: Operator, result: "Results"
) -> None:
    """
    This function is called after each time step performed by the emulator.
    By default it calls apply to compute a result and put it in `result`
    if `t` in `self.evaluation_times`.
    It can be overloaded to define any custom behaviour for a `Callback`.

    Args:
        config: the config object passed to the run method
        t: the current time in ns
        state: the current state
        H: the Hamiltonian at this time
        result: the results object
    """
    if t in self.evaluation_times:
        value_to_store = self.apply(config, t, state, H)
        result.store(callback=self, time=t, value=value_to_store)

apply(config, t, state, H) abstractmethod

This method must be implemented by subclasses. The result of this method gets put in the Results object.

PARAMETER DESCRIPTION
config

the config object passed to the run method

TYPE: BackendConfig

t

the current time in ns

TYPE: int

state

the current state

TYPE: State

H

the Hamiltonian at this time

TYPE: Operator

RETURNS DESCRIPTION
Any

the result to put in Results

Source code in emu_base/base_classes/callback.py
@abstractmethod
def apply(self, config: BackendConfig, t: int, state: State, H: Operator) -> Any:
    """
    This method must be implemented by subclasses. The result of this method
    gets put in the Results object.

    Args:
        config: the config object passed to the run method
        t: the current time in ns
        state: the current state
        H: the Hamiltonian at this time

    Returns:
        the result to put in Results
    """
    pass