Skip to content

DifferentiableBackend

DifferentiableBackend(backend, diff_mode=DiffMode.AD, **psr_args)

Bases: Module

A class to abstract the operations done by the autodiff engine

PARAMETER DESCRIPTION
backend

An instance of the QuantumBackend type perform execution.

TYPE: Backend

diff_mode

A differentiable mode supported by the differentiation engine.

TYPE: DiffMode DEFAULT: AD

**psr_args

Arguments that will be passed on to DifferentiableExpectation.

TYPE: int | float | None DEFAULT: {}

Source code in qadence/backends/pytorch_wrapper.py
def __init__(
    self,
    backend: QuantumBackend,
    diff_mode: DiffMode = DiffMode.AD,
    **psr_args: int | float | None,
) -> None:
    super().__init__()

    self.backend = backend
    self.diff_mode = diff_mode
    self.psr_args = psr_args
    # TODO: Add differentiable overlap calculation
    self._overlap: Callable = None  # type: ignore [assignment]

expectation(circuit, observable, param_values={}, state=None, protocol=None, endianness=Endianness.BIG)

Compute the expectation value of a given observable.

PARAMETER DESCRIPTION
circuit

A backend native quantum circuit to be executed.

TYPE: ConvertedCircuit

observable

A backend native observable to compute the expectation value from.

TYPE: list[ConvertedObservable] | ConvertedObservable

param_values

A dict of values for symbolic substitution.

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

state

An initial state.

TYPE: Tensor | None DEFAULT: None

protocol

A shot-based measurement protocol.

TYPE: Measurements | None DEFAULT: None

endianness

Endianness of the state.

TYPE: Endianness DEFAULT: BIG

RETURNS DESCRIPTION
Tensor

A tensor of expectation values.

Source code in qadence/backends/pytorch_wrapper.py
def expectation(
    self,
    circuit: ConvertedCircuit,
    observable: list[ConvertedObservable] | ConvertedObservable,
    param_values: dict[str, Tensor] = {},
    state: Tensor | None = None,
    protocol: Measurements | None = None,
    endianness: Endianness = Endianness.BIG,
) -> Tensor:
    """Compute the expectation value of a given observable.

    Arguments:
        circuit: A backend native quantum circuit to be executed.
        observable: A backend native observable to compute the expectation value from.
        param_values: A dict of values for symbolic substitution.
        state: An initial state.
        protocol: A shot-based measurement protocol.
        endianness: Endianness of the state.

    Returns:
        A tensor of expectation values.
    """
    observable = observable if isinstance(observable, list) else [observable]
    differentiable_expectation = DifferentiableExpectation(
        backend=self.backend,
        circuit=circuit,
        observable=observable,
        param_values=param_values,
        state=state,
        protocol=protocol,
        endianness=endianness,
    )

    if self.diff_mode == DiffMode.AD:
        expectation = differentiable_expectation.ad
    else:
        try:
            fns = get_gpsr_fns()
            psr_fn = fns[self.diff_mode]
        except KeyError:
            raise ValueError(f"{self.diff_mode} differentiation mode is not supported")
        expectation = partial(differentiable_expectation.psr, psr_fn=psr_fn, **self.psr_args)
    return expectation()

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

Run on the underlying backend.

Source code in qadence/backends/pytorch_wrapper.py
def run(
    self,
    circuit: ConvertedCircuit,
    param_values: dict = {},
    state: Tensor | None = None,
    endianness: Endianness = Endianness.BIG,
) -> Tensor:
    """Run on the underlying backend."""
    return self.backend.run(
        circuit=circuit, param_values=param_values, state=state, endianness=endianness
    )

sample(circuit, param_values, state=None, n_shots=1, endianness=Endianness.BIG)

Sample bitstring from the registered circuit.

PARAMETER DESCRIPTION
circuit

A backend native quantum circuit to be executed.

TYPE: ConvertedCircuit

param_values

The values of the parameters after embedding

TYPE: dict[str, Tensor]

n_shots

The number of shots. Defaults to 1.

TYPE: int DEFAULT: 1

RETURNS DESCRIPTION
list[Counter]

An iterable with all the sampled bitstrings

Source code in qadence/backends/pytorch_wrapper.py
def sample(
    self,
    circuit: ConvertedCircuit,
    param_values: dict[str, Tensor],
    state: Tensor | None = None,
    n_shots: int = 1,
    endianness: Endianness = Endianness.BIG,
) -> list[Counter]:
    """Sample bitstring from the registered circuit.

    Arguments:
        circuit: A backend native quantum circuit to be executed.
        param_values: The values of the parameters after embedding
        n_shots: The number of shots. Defaults to 1.

    Returns:
        An iterable with all the sampled bitstrings
    """
    with torch.no_grad():
        return self.backend.sample(
            circuit=circuit,
            param_values=param_values,
            state=state,
            n_shots=n_shots,
            endianness=endianness,
        )

DifferentiableExpectation dataclass

A handler for differentiating expectation estimation using various engines.

construct_rules(circuit, observable, psr_fn, **psr_args) staticmethod

Create a mapping between parameters and PSR functions.

Source code in qadence/backends/pytorch_wrapper.py
@staticmethod
def construct_rules(
    circuit: QuantumCircuit,
    observable: list[AbstractBlock],
    psr_fn: Callable,
    **psr_args: int | float | None,
) -> dict[str, Callable]:
    """Create a mapping between parameters and PSR functions."""

    uuid_to_eigs = uuid_to_eigen(circuit.block)
    # We currently rely on implicit ordering to match the PSR to the parameter,
    # because we want to cache PSRs.

    param_to_psr = OrderedDict()
    for param_id, eigenvalues in uuid_to_eigs.items():
        if eigenvalues is None:
            raise ValueError(
                f"Eigenvalues are not defined for param_id {param_id}\n"
                # f"of type {type(block)}.\n"
                "PSR cannot be defined in that case."
            )

        param_to_psr[param_id] = psr_fn(eigenvalues, **psr_args)
    for obs in observable:
        for param_id, _ in uuid_to_eigen(obs).items():
            # We need the embedded fixed params of the observable in the param_values dict
            # to be able to call expectation. Since torch backward requires
            # a list of param_ids and values of equal length, we need to pass them to PSR too.
            # Since they are constants their gradients are 0.
            param_to_psr[param_id] = lambda x: torch.tensor([0.0], requires_grad=False)
    return param_to_psr

PSRExpectation

Bases: Function

Overloads the PyTorch AD system to perform parameter shift rule on quantum circuits.