Source code for lambeq.training.tket_model

# Copyright 2021-2024 Cambridge Quantum Computing Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Tket Model
==========
Module based on a quantum backend, using `tket`.

"""
from __future__ import annotations

from typing import Any

import numpy as np

from lambeq.backend.quantum import Diagram as Circuit, Id, Measure
from lambeq.backend.tensor import Diagram
from lambeq.training.quantum_model import QuantumModel


[docs]class TketModel(QuantumModel): """Model based on `tket`. This can run either shot-based simulations of a quantum pipeline or experiments run on quantum hardware using `tket`. """
[docs] def __init__(self, backend_config: dict[str, Any]) -> None: """Initialise TketModel based on the `t|ket>` backend. Other Parameters ---------------- backend_config : dict Dictionary containing the backend configuration. Must include the fields `backend`, `compilation` and `shots`. Raises ------ KeyError If `backend_config` is not provided or has missing fields. """ super().__init__() fields = ('backend', 'compilation', 'shots') missing_fields = [f for f in fields if f not in backend_config] if missing_fields: raise KeyError('Missing arguments in backend configuation. ' f'Missing arguments: {missing_fields}.') self.backend_config = backend_config self.rng = np.random.default_rng()
def _randint(self, low: int = -1 << 63, high: int = (1 << 63)-1) -> int: return self.rng.integers(low, high, dtype=np.int64)
[docs] def get_diagram_output(self, diagrams: list[Diagram]) -> np.ndarray: """Return the prediction for each diagram using t|ket>. Parameters ---------- diagrams : list of :py:class:`~lambeq.backend.quantum.Diagram The :py:class:`Circuits <lambeq.backend.quantum.Diagram>` to be evaluated. Raises ------ ValueError If `model.weights` or `model.symbols` are not initialised. Returns ------- np.ndarray Resulting array. """ if len(self.weights) == 0 or not self.symbols: raise ValueError('Weights and/or symbols not initialised. ' 'Instantiate through ' '`TketModel.from_diagrams()` first, ' 'then call `initialise_weights()`, or load ' 'from pre-trained checkpoint.') measured = [diagram >> Id().tensor(*[Measure()] * len(diagram.cod)) for diagram in diagrams] # noqa: E501 measured = self._fast_subs(measured, self.weights) tensors = Circuit.eval( *measured, # type: ignore[arg-type] **self.backend_config, seed=self._randint() ) self.backend_config['backend'].empty_cache() # lambeq evals a single diagram into a single result # and not a list of results if len(diagrams) == 1: result = self._normalise_vector(tensors) return result.reshape(1, *result.shape) return np.array([self._normalise_vector(t) for t in tensors])
[docs] def forward(self, x: list[Diagram]) -> np.ndarray: """Perform default forward pass of a lambeq quantum model. In case of a different datapoint (e.g. list of tuple) or additional computational steps, please override this method. Parameters ---------- x : list of :py:class:`~lambeq.backend.quantum.Diagram` The :py:class:`Circuits <lambeq.backend.quantum.Diagram>` to be evaluated. Returns ------- np.ndarray Array containing model's prediction. """ return self.get_diagram_output(x)