BaseDevice#

class pennylane_calculquebec.base_device.BaseDevice(wires=None, shots=None, client=None, processing_config=None)#

Bases: Device

Abstract base class for MonarQ-compatible PennyLane devices.

Subclasses must implement machine_name and _measure(). Concrete implementations are MonarqDevice, MonarqSim, and MonarqBackup.

Supported measurements: CountsMP, ProbabilityMP, ExpectationMP.

Initialize the base device and optionally connect to the API.

Parameters:
  • wires (int or Iterable) – number of wires or iterable of wire labels. Defaults to None.

  • shots (int or Sequence[int]) – default number of shots per execution. Defaults to None.

  • client (ApiClient, optional) – credentials used to authenticate with the Thunderhead API. When None, no API connection is established (useful for simulation-only use cases).

  • processing_config (ProcessingConfig, optional) – custom transpilation pipeline. When None, the subclass is expected to provide a default configuration.

author

capabilities

A DeviceCapabilities object describing the capabilities of the backend device.

config_filepath

A device can use a toml file to specify the capabilities of the backend device.

machine_name

Name of the target quantum hardware.

measurement_methods

Mapping from measurement class name to the post-processing callable that converts raw counts into the expected output format.

name

The name of the device or set of devices.

observables

pennylane_requires

processing_config

Return the active processing configuration.

realm

shots

Default shots for execution workflows containing this device.

tracker

A Tracker that can store information about device executions, shots, batches, intermediate results, or any additional device dependent information.

wires

The device wires.

author = 'CalculQuebec'#
capabilities: DeviceCapabilities | None = None#

A DeviceCapabilities object describing the capabilities of the backend device.

config_filepath: str | None = None#

A device can use a toml file to specify the capabilities of the backend device. If this is provided, the file will be loaded into a DeviceCapabilities object assigned to the capabilities attribute.

machine_name#

Name of the target quantum hardware.

Returns:

identifier of the machine used for job submission

Return type:

str

Raises:

NotImplementedError – must be overridden by concrete subclasses

measurement_methods: dict = {'CountsMP': <function BaseDevice.<lambda>>, 'ExpectationMP': <function compute_expval>, 'ProbabilityMP': <function counts_to_probs>}#

Mapping from measurement class name to the post-processing callable that converts raw counts into the expected output format.

Type:

dict

name#

The name of the device or set of devices.

This property can either be the name of the class, or an alias to be used in the device() constructor, such as "default.qubit" or "lightning.qubit".

observables = {'PauliZ'}#
pennylane_requires = '>=0.36.0'#
processing_config#

Return the active processing configuration.

Returns:

the pre- and post-processing pipeline configuration

Return type:

ProcessingConfig

realm = 'calculqc'#
shots#

Default shots for execution workflows containing this device.

Note that the device itself should always pull shots from the provided QuantumTape and its shots, not from this property. This property is used to provide a default at the start of a workflow.

tracker: Tracker = <pennylane.devices.tracker.Tracker object>#

A Tracker that can store information about device executions, shots, batches, intermediate results, or any additional device dependent information.

A plugin developer can store information in the tracker by:

# querying if the tracker is active
if self.tracker.active:

    # store any keyword: value pairs of information
    self.tracker.update(executions=1, shots=self._shots, results=results)

    # Calling a user-provided callback function
    self.tracker.record()
wires#

The device wires.

Note that wires are optional, and the default value of None means any wires can be used. If a device has wires defined, they will only be used for certain features. This includes:

  • Validation of tapes being executed on the device

  • Defining the wires used when evaluating a state() measurement

compute_derivatives(circuits[, execution_config])

Calculate the jacobian of either a single or a batch of circuits on the device.

compute_jvp(circuits, tangents[, ...])

The jacobian vector product used in forward mode calculation of derivatives.

compute_vjp(circuits, cotangents[, ...])

The vector jacobian product used in reverse-mode differentiation.

eval_jaxpr(jaxpr, consts, *args[, ...])

An experimental method for natively evaluating PLXPR.

execute(circuits[, execution_config])

Execute one or more pre-processed quantum circuits.

execute_and_compute_derivatives(circuits[, ...])

Compute the results and jacobians of circuits at the same time.

execute_and_compute_jvp(circuits, tangents)

Execute a batch of circuits and compute their jacobian vector products.

execute_and_compute_vjp(circuits, cotangents)

Calculate both the results and the vector jacobian product used in reverse-mode differentiation.

jaxpr_jvp(jaxpr, args, tangents[, ...])

An experimental method for computing the results and jvp for PLXPR.

preprocess([execution_config])

Build the PennyLane transform program that preprocesses circuits before execution.

preprocess_transforms([execution_config])

Returns the transform program to preprocess a circuit for execution.

setup_execution_config([config, circuit])

Sets up an ExecutionConfig that configures the execution behaviour.

supports_derivatives([execution_config, circuit])

Determine whether or not a device provided derivative is potentially available.

supports_jvp([execution_config, circuit])

Whether or not a given device defines a custom jacobian vector product.

supports_vjp([execution_config, circuit])

Whether or not a given device defines a custom vector jacobian product.

compute_derivatives(circuits: QuantumScript | Sequence[QuantumScript], execution_config: ExecutionConfig | None = None)#

Calculate the jacobian of either a single or a batch of circuits on the device.

Parameters:
  • circuits (Union[QuantumTape, Sequence[QuantumTape]]) – the circuits to calculate derivatives for

  • execution_config (ExecutionConfig) – a datastructure with all additional information required for execution

Returns:

The jacobian for each trainable parameter

Return type:

Tuple

Execution Config:

The execution config has gradient_method and order property that describes the order of differentiation requested. If the requested method or order of gradient is not provided, the device should raise a NotImplementedError. The supports_derivatives() method can pre-validate supported orders and gradient methods.

Return Shape:

If a batch of quantum scripts is provided, this method should return a tuple with each entry being the gradient of each individual quantum script. If the batch is of length 1, then the return tuple should still be of length 1, not squeezed.

compute_jvp(circuits: QuantumScript | Sequence[QuantumScript], tangents: tuple[Number, ...], execution_config: ExecutionConfig | None = None)#

The jacobian vector product used in forward mode calculation of derivatives.

Parameters:
  • circuits (Union[QuantumTape, Sequence[QuantumTape]]) – the circuit or batch of circuits

  • tangents (tensor-like) – Gradient vector for input parameters.

  • execution_config (ExecutionConfig) – a datastructure with all additional information required for execution

Returns:

A numeric result of computing the jacobian vector product

Return type:

Tuple

Definition of jvp:

If we have a function with jacobian:

\[\vec{y} = f(\vec{x}) \qquad J_{i,j} = \frac{\partial y_i}{\partial x_j}\]

The Jacobian vector product is the inner product with the derivatives of \(x\), yielding only the derivatives of the output \(y\):

\[\text{d}y_i = \Sigma_{j} J_{i,j} \text{d}x_j\]

Shape of tangents:

The tangents tuple should be the same length as circuit.get_parameters() and have a single number per parameter. If a number is zero, then the gradient with respect to that parameter does not need to be computed.

compute_vjp(circuits: QuantumScript | Sequence[QuantumScript], cotangents: tuple[Number, ...], execution_config: ExecutionConfig | None = None)#

The vector jacobian product used in reverse-mode differentiation.

Parameters:
  • circuits (Union[QuantumTape, Sequence[QuantumTape]]) – the circuit or batch of circuits

  • cotangents (Tuple[Number, Tuple[Number]]) – Gradient-output vector. Must have shape matching the output shape of the corresponding circuit. If the circuit has a single output, cotangents may be a single number, not an iterable of numbers.

  • execution_config (ExecutionConfig) – a datastructure with all additional information required for execution

Returns:

A numeric result of computing the vector jacobian product

Return type:

tensor-like

Definition of vjp:

If we have a function with jacobian:

\[\vec{y} = f(\vec{x}) \qquad J_{i,j} = \frac{\partial y_i}{\partial x_j}\]

The vector jacobian product is the inner product of the derivatives of the output y with the Jacobian matrix. The derivatives of the output vector are sometimes called the cotangents.

\[\text{d}x_i = \Sigma_{i} \text{d}y_i J_{i,j}\]

Shape of cotangents:

The value provided to cotangents should match the output of execute().

eval_jaxpr(jaxpr: jax.extend.core.Jaxpr, consts: list[int | float | bool | complex | bytes | list | tuple | ndarray | generic | ArrayBox | InterfaceTensor], *args, execution_config: ExecutionConfig | None = None, shots: Shots = Shots(total_shots=None, shot_vector=())) list[int | float | bool | complex | bytes | list | tuple | ndarray | generic | ArrayBox | InterfaceTensor]#

An experimental method for natively evaluating PLXPR. See the capture module for more details.

Parameters:
  • jaxpr (jax.extend.core.Jaxpr) – Pennylane variant jaxpr containing quantum operations and measurements

  • consts (list[TensorLike]) – the closure variables consts corresponding to the jaxpr

  • *args (TensorLike) – the variables to use with the jaxpr.

Keyword Arguments:
  • execution_config (Optional[ExecutionConfig]) – a data structure with additional information required for execution

  • shots (Shots) – the number of shots to use for the evaluation

Returns:

the result of evaluating the jaxpr with the given parameters.

Return type:

list[TensorLike]

execute(circuits: ~pennylane.tape.tape.QuantumTape | list[~pennylane.tape.tape.QuantumTape], execution_config=<class 'pennylane.devices.execution_config.ExecutionConfig'>)#

Execute one or more pre-processed quantum circuits.

Iterates over the provided tapes and delegates each one to _measure(). A single tape is returned as a scalar result; a list of tapes is returned as a list.

Parameters:
  • circuits (QuantumTape or list[QuantumTape]) – the circuit(s) to execute

  • execution_config (ExecutionConfig) – execution parameters. Defaults to ExecutionConfig.

Returns:

measurement result(s) in the format determined by the measurement type (counts, probabilities, or expectation value)

Return type:

any or list[any]

execute_and_compute_derivatives(circuits: QuantumScript | Sequence[QuantumScript], execution_config: ExecutionConfig | None = None)#

Compute the results and jacobians of circuits at the same time.

Parameters:
  • circuits (Union[QuantumTape, Sequence[QuantumTape]]) – the circuits or batch of circuits

  • execution_config (ExecutionConfig) – a datastructure with all additional information required for execution

Returns:

A numeric result of the computation and the gradient.

Return type:

tuple

See execute() and compute_derivatives() for more information about return shapes and behaviour. If compute_derivatives() is defined, this method should be as well.

This method can be used when the result and execution need to be computed at the same time, such as during a forward mode calculation of gradients. For certain gradient methods, such as adjoint diff gradients, calculating the result and gradient at the same can save computational work.

execute_and_compute_jvp(circuits: QuantumScript | Sequence[QuantumScript], tangents: tuple[Number, ...], execution_config: ExecutionConfig | None = None)#

Execute a batch of circuits and compute their jacobian vector products.

Parameters:
  • circuits (Union[QuantumTape, Sequence[QuantumTape]]) – circuit or batch of circuits

  • tangents (tensor-like) – Gradient vector for input parameters.

  • execution_config (ExecutionConfig) – a datastructure with all additional information required for execution

Returns:

A numeric result of execution and of computing the jacobian vector product

Return type:

Tuple, Tuple

See also

execute() and compute_jvp()

execute_and_compute_vjp(circuits: QuantumScript | Sequence[QuantumScript], cotangents: tuple[Number, ...], execution_config: ExecutionConfig | None = None)#

Calculate both the results and the vector jacobian product used in reverse-mode differentiation.

Parameters:
  • circuits (Union[QuantumTape, Sequence[QuantumTape]]) – the circuit or batch of circuits to be executed

  • cotangents (Tuple[Number, Tuple[Number]]) – Gradient-output vector. Must have shape matching the output shape of the corresponding circuit. If the circuit has a single output, cotangents may be a single number, not an iterable of numbers.

  • execution_config (ExecutionConfig) – a datastructure with all additional information required for execution

Returns:

the result of executing the scripts and the numeric result of computing the vector jacobian product

Return type:

Tuple, Tuple

See also

execute() and compute_vjp()

jaxpr_jvp(jaxpr: jax.extend.core.Jaxpr, args, tangents, execution_config: ExecutionConfig | None = None)#

An experimental method for computing the results and jvp for PLXPR. See the capture module for more details.

Parameters:
  • jaxpr (jax.extend.core.Jaxpr) – Pennylane variant jaxpr containing quantum operations and measurements

  • args (Sequence[TensorLike]) – the consts followed by the normal arguments

  • tangents (Sequence[TensorLike]) – the tangents corresponding to args. May contain jax.interpreters.ad.Zero.

Keyword Arguments:

execution_config (Optional[ExecutionConfig]) – a data structure with additional information required for execution

Returns:

the results and jacobian vector products

Return type:

Sequence[TensorLike], Sequence[TensorLike]

>>> qml.capture.enable()
>>> import jax
>>> closure_var = jax.numpy.array(0.5)
>>> def f(x):
...     qml.RX(closure_var, 0)
...     qml.RX(x, 1)
...     return qml.expval(qml.Z(0)), qml.expval(qml.Z(1))
>>> jaxpr = jax.make_jaxpr(f)(1.2)
>>> args = (closure_var, 1.2)
>>> zero = jax.interpreters.ad.Zero(jax.core.ShapedArray((), float))
>>> tangents = (zero, 1.0)
>>> config = qml.devices.ExecutionConfig(gradient_method="adjoint")
>>> dev = qml.device('default.qubit', wires=2)
>>> res, jvps = dev.jaxpr_jvp(jaxpr.jaxpr, args, tangents, execution_config=config)
>>> res
[Array(0.87758255, dtype=float32), Array(0.36235774, dtype=float32)]
>>> jvps
[Array(0., dtype=float32), Array(-0.932039, dtype=float32)]
preprocess(execution_config=<class 'pennylane.devices.execution_config.ExecutionConfig'>) Tuple[TransformProgram, ExecutionConfig]#

Build the PennyLane transform program that preprocesses circuits before execution.

The transform program applies all pre-processing steps defined in processing_config (decomposition, placement, routing, optimisation, …).

Parameters:

execution_config (ExecutionConfig) – parameters describing the execution. Defaults to ExecutionConfig.

Returns:

the transform program and the (potentially updated) execution config.

Return type:

tuple[TransformProgram, ExecutionConfig]

preprocess_transforms(execution_config: ExecutionConfig | None = None) TransformProgram#

Returns the transform program to preprocess a circuit for execution.

Parameters:

execution_config (ExecutionConfig) – The execution configuration object

Returns:

A transform program that is called before execution

Return type:

TransformProgram

The transform program is composed of a list of individual transforms, which may include:

  • Decomposition of operations and measurements to what is supported by the device.

  • Splitting a circuit with measurements of non-commuting observables or Hamiltonians into multiple executions.

  • Splitting a circuit with batched parameters into multiple executions.

  • Validation of wires, measurements, and observables.

  • Gradient specific preprocessing, such as making sure trainable operators have generators.

Example

All transforms that are part of the preprocessing transform program need to respect the transform contract defined in pennylane.transform().

from pennylane.tape import QuantumScriptBatch
from pennylane.typing import PostprocessingFn

@qml.transform
def my_preprocessing_transform(tape: qml.tape.QuantumScript) -> tuple[QuantumScriptBatch, PostprocessingFn]:
    # e.g. valid the measurements, expand the tape for the hardware execution, ...

    def blank_processing_fn(results):
        return results[0]

    return [tape], processing_fn

A transform program can hold an arbitrary number of individual transforms:

def preprocess(self, config):
    program = TransformProgram()
    program.add_transform(my_preprocessing_transform)
    return program

See also

transform() and TransformProgram

setup_execution_config(config: ExecutionConfig | None = None, circuit: QuantumScript | None = None) ExecutionConfig#

Sets up an ExecutionConfig that configures the execution behaviour.

The execution config stores information on how the device should perform the execution, as well as how PennyLane should interact with the device. See ExecutionConfig for all available options and what they mean.

An ExecutionConfig is constructed from arguments passed to the QNode, and this method allows the device to update the config object based on device-specific requirements or preferences. See execution_config for more details.

Parameters:
  • config (ExecutionConfig) – The initial ExecutionConfig object that describes the parameters needed to configure the execution behaviour.

  • circuit (QuantumScript) – The quantum circuit to customize the execution config for.

Returns:

The updated ExecutionConfig object

Return type:

ExecutionConfig

supports_derivatives(execution_config: ExecutionConfig | None = None, circuit: QuantumScript | None = None) bool#

Determine whether or not a device provided derivative is potentially available.

Default behaviour assumes first order device derivatives for all circuits exist if compute_derivatives() is overriden.

Parameters:
  • execution_config (ExecutionConfig) – A description of the hyperparameters for the desired computation.

  • circuit (None, QuantumTape) – A specific circuit to check differentation for.

Returns:

Bool

The device can support multiple different types of “device derivatives”, chosen via execution_config.gradient_method. For example, a device can natively calculate "parameter-shift" derivatives, in which case compute_derivatives() will be called for the derivative instead of execute() with a batch of circuits.

>>> config = ExecutionConfig(gradient_method="parameter-shift")
>>> custom_device.supports_derivatives(config)
True

In this case, compute_derivatives() or execute_and_compute_derivatives() will be called instead of execute() with a batch of circuits.

If circuit is not provided, then the method should return whether or not device derivatives exist for any circuit.

Example:

For example, the Python device will support device differentiation via the adjoint differentiation algorithm if the order is 1 and the execution occurs with no shots (shots=None).

>>> config = ExecutionConfig(derivative_order=1, gradient_method="adjoint")
>>> dev.supports_derivatives(config)
True
>>> circuit_analytic = qml.tape.QuantumScript([qml.RX(0.1, wires=0)], [qml.expval(qml.Z(0))], shots=None)
>>> dev.supports_derivatives(config, circuit=circuit_analytic)
True
>>> circuit_finite_shots = qml.tape.QuantumScript([qml.RX(0.1, wires=0)], [qml.expval(qml.Z(0))], shots=10)
>>> dev.supports_derivatives(config, circuit = circuit_fintite_shots)
False
>>> config = ExecutionConfig(derivative_order=2, gradient_method="adjoint")
>>> dev.supports_derivatives(config)
False

Adjoint differentiation will only be supported for circuits with expectation value measurements. If a circuit is provided and it cannot be converted to a form supported by differentiation method by preprocess(), then supports_derivatives should return False.

>>> config = ExecutionConfig(derivative_order=1, shots=None, gradient_method="adjoint")
>>> circuit = qml.tape.QuantumScript([qml.RX(2.0, wires=0)], [qml.probs(wires=(0,1))])
>>> dev.supports_derivatives(config, circuit=circuit)
False

If the circuit is not natively supported by the differentiation method but can be converted into a form that is supported, it should still return True. For example, Rot gates are not natively supported by adjoint differentation, as they do not have a generator, but they can be compiled into operations supported by adjoint differentiation. Therefore this method may reproduce compilation and validation steps performed by preprocess().

>>> config = ExecutionConfig(derivative_order=1, shots=None, gradient_method="adjoint")
>>> circuit = qml.tape.QuantumScript([qml.Rot(1.2, 2.3, 3.4, wires=0)], [qml.expval(qml.Z(0))])
>>> dev.supports_derivatives(config, circuit=circuit)
True

Backpropagation:

This method is also used be to validate support for backpropagation derivatives. Backpropagation is only supported if the device is transparent to the machine learning framework from start to finish.

>>> config = ExecutionConfig(gradient_method="backprop")
>>> python_device.supports_derivatives(config)
True
>>> cpp_device.supports_derivatives(config)
False
supports_jvp(execution_config: ExecutionConfig | None = None, circuit: QuantumScript | None = None) bool#

Whether or not a given device defines a custom jacobian vector product.

Parameters:
  • execution_config (ExecutionConfig) – A description of the hyperparameters for the desired computation.

  • circuit (None, QuantumTape) – A specific circuit to check differentation for.

Default behaviour assumes this to be True if compute_jvp() is overridden.

supports_vjp(execution_config: ExecutionConfig | None = None, circuit: QuantumScript | None = None) bool#

Whether or not a given device defines a custom vector jacobian product.

Parameters:
  • execution_config (ExecutionConfig) – A description of the hyperparameters for the desired computation.

  • circuit (None, QuantumTape) – A specific circuit to check differentation for.

Default behaviour assumes this to be True if compute_vjp() is overridden.