Source code for kosmos.ml.models.vqc.circuit.pennylane_circuit
import pennylane as qml
import torch
from pennylane.measurements import ExpectationMP
from kosmos.circuit_runner.pennylane_runner import PennyLaneRunner
from kosmos.ml.models.vqc.circuit.circuit import ParameterizedCircuit
from kosmos.ml.models.vqc.encoding.encoding import VQCEncoding
from kosmos.ml.typing import TensorMapping
[docs]
class PennyLaneParameterizedCircuit(ParameterizedCircuit):
"""Parameterized quantum circuit using PennyLane."""
def __init__( # noqa: PLR0913
self,
circuit_runner: PennyLaneRunner,
encoding: VQCEncoding,
num_layers: int,
weight_mapping_func: TensorMapping | None,
input_mapping_func: TensorMapping | None,
output_scaling_parameter: torch.Tensor | None,
bias_parameter: torch.Tensor | None,
*,
data_reuploading: bool,
) -> None:
"""Initialize the circuit.
Args:
circuit_runner (PennyLaneRunner): The PennyLane circuit runner.
encoding (VQCEncoding): The VQC encoding.
num_layers (int): The number of variational layers.
weight_mapping_func (TensorMapping | None): The mapping function for the weights.
input_mapping_func (TensorMapping | None): The mapping function for the inputs.
output_scaling_parameter (torch.Tensor | None): The output scaling parameter.
bias_parameter (torch.Tensor | None): The bias parameter.
data_reuploading (bool): Whether to use data re-uploading.
"""
super().__init__(
circuit_runner,
encoding,
num_layers,
weight_mapping_func,
input_mapping_func,
output_scaling_parameter,
bias_parameter,
data_reuploading=data_reuploading,
)
self.circuit_runner = circuit_runner
self.circuit_runner.configure_qnode(self.num_qubits, self._circuit)
def _circuit(self, weights: torch.Tensor, x: torch.Tensor) -> list[ExpectationMP]:
"""Circuit definition.
Args:
weights (torch.Tensor): Weights tensor.
x (torch.Tensor): Input tensor.
Returns:
list[ExpectationMP]: List of Z-expectation measurement processes.
"""
if not self.data_reuploading:
self.encoding.apply_operation(x, wires=range(self.num_qubits))
for w in weights:
if self.data_reuploading:
self.encoding.apply_operation(x, wires=range(self.num_qubits))
qml.StronglyEntanglingLayers(w.unsqueeze(0), wires=range(self.num_qubits))
return [qml.expval(qml.PauliZ(i)) for i in range(self.output_dim)]
[docs]
def expect_z(self, weights: torch.Tensor, x: torch.Tensor) -> torch.Tensor:
"""Execute the circuit and calculate Z expectation values.
Args:
weights (torch.Tensor): Weights tensor.
x (torch.Tensor): Input tensor.
Returns:
torch.Tensor: Z expectation values.
"""
return qml.math.stack(self.circuit_runner.execute(weights, x))
[docs]
def forward_circuit(self, x: torch.Tensor, weights: torch.Tensor) -> torch.Tensor:
"""Compute model outputs for inputs and weights.
Args:
x (torch.Tensor): Input tensor.
weights (torch.Tensor): Weights tensor.
Returns:
torch.Tensor: Output tensor.
"""
outputs = []
mapped_weights = self.weight_mapping_func(weights)
for model_input in x:
mapped_input = self.input_mapping_func(model_input)
circuit_out = self.expect_z(mapped_weights, mapped_input)
outputs.append(circuit_out.to(torch.float32))
output = torch.stack(outputs)
if self.output_scaling_parameter is not None:
output = self.output_scaling_parameter * output
if self.bias_parameter is not None:
output = output + self.bias_parameter
return output