Skip to content

Abstract backend

Backend(name, supports_ad, support_bp, supports_adjoint, is_remote, with_measurements, native_endianness, engine, with_noise, config) dataclass

Bases: ABC

The abstract class that defines the interface for the backends.

ATTRIBUTE DESCRIPTION
name

backend unique string identifier

TYPE: BackendName

supports_ad

whether or not the backend has a native autograd

TYPE: bool

supports_bp

whether or not the backend has a native backprop

TYPE: bool

supports_adjoint

Does the backend support native adjoint differentation.

TYPE: bool

is_remote

whether computations are executed locally or remotely on this backend, useful when using cloud platforms where credentials are needed for example.

TYPE: bool

with_measurements

whether it supports counts or not

TYPE: bool

with_noise

whether to add realistic noise or not

TYPE: bool

native_endianness

The native endianness of the backend

TYPE: Endianness

engine

The underlying (native) automatic differentiation engine of the backend.

TYPE: Engine

circuit(circuit) abstractmethod

Converts an abstract QuantumCircuit to the native backend representation.

PARAMETER DESCRIPTION
circuit

A circuit, for example: QuantumCircuit(2, X(0))

TYPE: QuantumCircuit

RETURNS DESCRIPTION
ConvertedCircuit

A converted circuit c. You can access the original, arbstract circuit via c.abstract

ConvertedCircuit

and the converted (or backend native) circuit via c.native.

Source code in qadence/backend.py
@abstractmethod
def circuit(self, circuit: QuantumCircuit) -> ConvertedCircuit:
    """Converts an abstract `QuantumCircuit` to the native backend representation.

    Arguments:
        circuit: A circuit, for example: `QuantumCircuit(2, X(0))`

    Returns:
        A converted circuit `c`. You can access the original, arbstract circuit via `c.abstract`
        and the converted (or backend *native*) circuit via `c.native`.
    """
    raise NotImplementedError

convert(circuit, observable=None)

Convert an abstract circuit and an optional observable to their native representation.

Additionally, this function constructs an embedding function which maps from user-facing parameters to device parameters (read more on parameter embedding here).

Source code in qadence/backend.py
def convert(
    self, circuit: QuantumCircuit, observable: list[AbstractBlock] | AbstractBlock | None = None
) -> Converted:
    """Convert an abstract circuit and an optional observable to their native representation.

    Additionally, this function constructs an embedding function which maps from
    user-facing parameters to device parameters (read more on parameter embedding
    [here][qadence.blocks.embedding.embedding]).
    """

    def check_observable(obs_obj: Any) -> AbstractBlock:
        if isinstance(obs_obj, QubitOperator):
            from qadence.blocks.manipulate import from_openfermion

            assert len(obs_obj.terms) > 0, "Make sure to give a non-empty qubit hamiltonian"

            return from_openfermion(obs_obj)

        elif isinstance(obs_obj, (CompositeBlock, PrimitiveBlock, ScaleBlock)):
            from qadence.blocks.utils import block_is_qubit_hamiltonian

            assert block_is_qubit_hamiltonian(
                obs_obj
            ), "Make sure the QubitHamiltonian consists only of Pauli operators X, Y, Z, I"
            return obs_obj
        raise TypeError(
            "qubit_hamiltonian should be a Pauli-like AbstractBlock or a QubitOperator"
        )

    conv_circ = self.circuit(circuit)
    circ_params, circ_embedding_fn = embedding(
        conv_circ.abstract.block, self.config._use_gate_params, self.engine
    )
    params = circ_params
    if observable is not None:
        observable = observable if isinstance(observable, list) else [observable]
        conv_obs = []
        obs_embedding_fn_list = []

        for obs in observable:
            obs = check_observable(obs)
            c_obs = self.observable(obs, max(circuit.n_qubits, obs.n_qubits))
            obs_params, obs_embedding_fn = embedding(
                c_obs.abstract, self.config._use_gate_params, self.engine
            )
            params.update(obs_params)
            obs_embedding_fn_list.append(obs_embedding_fn)
            conv_obs.append(c_obs)

        def embedding_fn_dict(a: dict, b: dict) -> dict:
            embedding_dict = circ_embedding_fn(a, b)
            for o in obs_embedding_fn_list:
                embedding_dict.update(o(a, b))
            return embedding_dict

        return Converted(conv_circ, conv_obs, embedding_fn_dict, params)

    def embedding_fn(a: dict, b: dict) -> dict:
        return circ_embedding_fn(a, b)

    return Converted(conv_circ, None, embedding_fn, params)

expectation(circuit, observable, param_values={}, state=None, measurement=None, noise=None, mitigation=None, endianness=Endianness.BIG) abstractmethod

Compute the expectation value of the circuit with the given observable.

PARAMETER DESCRIPTION
circuit

A converted circuit as returned by backend.circuit.

TYPE: ConvertedCircuit

param_values

Already embedded parameters of the circuit. See embedding for more info.

TYPE: ParamDictType DEFAULT: {}

state

Initial state.

TYPE: ArrayLike | None DEFAULT: None

measurement

Optional measurement protocol. If None, use exact expectation value with a statevector simulator.

TYPE: Measurements | None DEFAULT: None

noise

A noise model to use.

TYPE: Noise | None DEFAULT: None

endianness

Endianness of the resulting bit strings.

TYPE: Endianness DEFAULT: BIG

Source code in qadence/backend.py
@abstractmethod
def expectation(
    self,
    circuit: ConvertedCircuit,
    observable: list[ConvertedObservable] | ConvertedObservable,
    param_values: ParamDictType = {},
    state: ArrayLike | None = None,
    measurement: Measurements | None = None,
    noise: Noise | None = None,
    mitigation: Mitigations | None = None,
    endianness: Endianness = Endianness.BIG,
) -> ArrayLike:
    """Compute the expectation value of the `circuit` with the given `observable`.

    Arguments:
        circuit: A converted circuit as returned by `backend.circuit`.
        param_values: _**Already embedded**_ parameters of the circuit. See
            [`embedding`][qadence.blocks.embedding.embedding] for more info.
        state: Initial state.
        measurement: Optional measurement protocol. If None, use
            exact expectation value with a statevector simulator.
        noise: A noise model to use.
        endianness: Endianness of the resulting bit strings.
    """
    raise NotImplementedError

observable(observable, n_qubits) abstractmethod

Converts an abstract observable (which is just an AbstractBlock) to the native backend.

representation.

PARAMETER DESCRIPTION
observable

An observable.

TYPE: AbstractBlock

n_qubits

Number of qubits the observable covers. This is typically circuit.n_qubits.

TYPE: int

RETURNS DESCRIPTION
ConvertedObservable

A converted observable o. You can access the original, arbstract observable via

ConvertedObservable

o.abstract and the converted (or backend native) observable via o.native.

Source code in qadence/backend.py
@abstractmethod
def observable(self, observable: AbstractBlock, n_qubits: int) -> ConvertedObservable:
    """Converts an abstract observable (which is just an `AbstractBlock`) to the native backend.

    representation.

    Arguments:
        observable: An observable.
        n_qubits: Number of qubits the observable covers. This is typically `circuit.n_qubits`.

    Returns:
        A converted observable `o`. You can access the original, arbstract observable via
        `o.abstract` and the converted (or backend *native*) observable via `o.native`.
    """
    raise NotImplementedError

run(circuit, param_values={}, state=None, endianness=Endianness.BIG, *args, **kwargs)

Run a circuit and return the resulting wave function.

PARAMETER DESCRIPTION
circuit

A converted circuit as returned by backend.circuit.

TYPE: ConvertedCircuit

param_values

Already embedded parameters of the circuit. See embedding for more info.

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

state

Initial state.

TYPE: Tensor | None DEFAULT: None

endianness

Endianness of the resulting wavefunction.

TYPE: Endianness DEFAULT: BIG

RETURNS DESCRIPTION
ArrayLike

A list of Counter objects where each key represents a bitstring

ArrayLike

and its value the number of times it has been sampled from the given wave function.

Source code in qadence/backend.py
def run(
    self,
    circuit: ConvertedCircuit,
    param_values: dict[str, ArrayLike] = {},
    state: Tensor | None = None,
    endianness: Endianness = Endianness.BIG,
    *args: Any,
    **kwargs: Any,
) -> ArrayLike:
    """Run a circuit and return the resulting wave function.

    Arguments:
        circuit: A converted circuit as returned by `backend.circuit`.
        param_values: _**Already embedded**_ parameters of the circuit. See
            [`embedding`][qadence.blocks.embedding.embedding] for more info.
        state: Initial state.
        endianness: Endianness of the resulting wavefunction.

    Returns:
        A list of Counter objects where each key represents a bitstring
        and its value the number of times it has been sampled from the given wave function.
    """
    raise NotImplementedError

run_dm(circuit, noise, param_values={}, state=None, endianness=Endianness.BIG) abstractmethod

Run a circuit and return the resulting the density matrix.

TODO: Temporary method for the purposes of noise model implementation. To be removed in a later refactoring.

PARAMETER DESCRIPTION
circuit

A converted circuit as returned by backend.circuit.

TYPE: ConvertedCircuit

param_values

Already embedded parameters of the circuit. See embedding for more info.

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

state

Initial state.

TYPE: Tensor | None DEFAULT: None

endianness

Endianness of the resulting density matrix.

TYPE: Endianness DEFAULT: BIG

RETURNS DESCRIPTION
Tensor

A list of Counter objects where each key represents a bitstring

Tensor

and its value the number of times it has been sampled from the given wave function.

Source code in qadence/backend.py
@abstractmethod
def run_dm(
    self,
    circuit: ConvertedCircuit,
    noise: Noise,
    param_values: dict[str, ArrayLike] = {},
    state: Tensor | None = None,
    endianness: Endianness = Endianness.BIG,
) -> Tensor:
    """Run a circuit and return the resulting the density matrix.

    TODO: Temporary method for the purposes of noise model implementation.
    To be removed in a later refactoring.

    Arguments:
        circuit: A converted circuit as returned by `backend.circuit`.
        param_values: _**Already embedded**_ parameters of the circuit. See
            [`embedding`][qadence.blocks.embedding.embedding] for more info.
        state: Initial state.
        endianness: Endianness of the resulting density matrix.

    Returns:
        A list of Counter objects where each key represents a bitstring
        and its value the number of times it has been sampled from the given wave function.
    """
    raise NotImplementedError

sample(circuit, param_values={}, n_shots=1000, state=None, noise=None, mitigation=None, endianness=Endianness.BIG) abstractmethod

Sample bit strings.

PARAMETER DESCRIPTION
circuit

A converted circuit as returned by backend.circuit.

TYPE: ConvertedCircuit

param_values

Already embedded parameters of the circuit. See embedding for more info.

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

n_shots

Number of shots to sample.

TYPE: int DEFAULT: 1000

state

Initial state.

TYPE: ArrayLike | None DEFAULT: None

noise

A noise model to use.

TYPE: Noise | None DEFAULT: None

mitigation

An error mitigation protocol to apply.

TYPE: Mitigations | None DEFAULT: None

endianness

Endianness of the resulting bit strings.

TYPE: Endianness DEFAULT: BIG

Source code in qadence/backend.py
@abstractmethod
def sample(
    self,
    circuit: ConvertedCircuit,
    param_values: dict[str, Tensor] = {},
    n_shots: int = 1000,
    state: ArrayLike | None = None,
    noise: Noise | None = None,
    mitigation: Mitigations | None = None,
    endianness: Endianness = Endianness.BIG,
) -> list[Counter]:
    """Sample bit strings.

    Arguments:
        circuit: A converted circuit as returned by `backend.circuit`.
        param_values: _**Already embedded**_ parameters of the circuit. See
            [`embedding`][qadence.blocks.embedding.embedding] for more info.
        n_shots: Number of shots to sample.
        state: Initial state.
        noise: A noise model to use.
        mitigation: An error mitigation protocol to apply.
        endianness: Endianness of the resulting bit strings.
    """
    raise NotImplementedError

BackendConfiguration(_use_gate_params=True, use_sparse_observable=False, use_gradient_checkpointing=False, use_single_qubit_composition=False, transpilation_passes=None) dataclass

available_options()

Return as a string the available fields with types of the configuration.

RETURNS DESCRIPTION
str

a string with all the available fields, one per line

TYPE: str

Source code in qadence/backend.py
def available_options(self) -> str:
    """Return as a string the available fields with types of the configuration.

    Returns:
        str: a string with all the available fields, one per line
    """
    conf_msg = ""
    for _field in fields(self):
        if not _field.name.startswith("_"):
            conf_msg += (
                f"Name: {_field.name} - Type: {_field.type} - Default value: {_field.default}\n"
            )
    return conf_msg

get_param_name(blk)

Return parameter names for the current backend.

Depending on which backend is in use this function returns either UUIDs or expressions of parameters.

Source code in qadence/backend.py
def get_param_name(self, blk: AbstractBlock) -> Tuple[str, ...]:
    """Return parameter names for the current backend.

    Depending on which backend is in use this
    function returns either UUIDs or expressions of parameters.
    """
    param_ids: Tuple
    # FIXME: better type hiearchy?
    types = (TimeEvolutionBlock, ParametricBlock, ConstantAnalogRotation, InteractionBlock)
    if not isinstance(blk, types):
        raise TypeError(f"Can not infer param name from {type(blk)}")
    else:
        if self._use_gate_params:
            param_ids = tuple(blk.parameters.uuids())
        else:
            param_ids = tuple(map(stringify, blk.parameters.expressions()))
    return param_ids