|
import pennylane as qml |
|
import torch |
|
import torch.nn as nn |
|
from torch.nn.parameter import Parameter |
|
|
|
|
|
def encode(n_qubits, inputs): |
|
for wire in range(n_qubits): |
|
qml.RX(inputs[wire], wires=wire) |
|
|
|
|
|
def layer(n_qubits, y_weight, z_weight): |
|
for wire, y_weight in enumerate(y_weight): |
|
qml.RY(y_weight, wires=wire) |
|
for wire, z_weight in enumerate(z_weight): |
|
qml.RZ(z_weight, wires=wire) |
|
for wire in range(n_qubits): |
|
qml.CZ(wires=[wire, (wire + 1) % n_qubits]) |
|
|
|
|
|
def measure(n_qubits): |
|
return [qml.expval(qml.PauliZ(wire)) for wire in range(n_qubits)] |
|
|
|
|
|
def get_model(n_qubits, n_layers, data_reupload): |
|
|
|
|
|
dev = qml.device("default.qubit", wires=n_qubits) |
|
shapes = { |
|
"y_weights": (n_layers, n_qubits), |
|
"z_weights": (n_layers, n_qubits) |
|
} |
|
|
|
@qml.qnode(dev, interface='torch') |
|
def circuit(inputs, y_weights, z_weights): |
|
for layer_idx in range(n_layers): |
|
if (layer_idx == 0) or data_reupload: |
|
encode(n_qubits, inputs) |
|
layer(n_qubits, y_weights[layer_idx], z_weights[layer_idx]) |
|
return measure(n_qubits) |
|
|
|
model = qml.qnn.TorchLayer(circuit, shapes) |
|
|
|
return model |
|
|
|
|
|
class QuantumNet(nn.Module): |
|
def __init__(self, n_layers, w_input, w_output, data_reupload): |
|
super(QuantumNet, self).__init__() |
|
self.n_qubits = 2 |
|
self.n_actions = 3 |
|
self.data_reupload = data_reupload |
|
self.q_layers = get_model(n_qubits=self.n_qubits, n_layers=n_layers, data_reupload=data_reupload) |
|
|
|
|
|
self.layer1 = nn.Linear(2, 3) |
|
|
|
if w_input: |
|
self.w_input = Parameter(torch.Tensor(self.n_qubits)) |
|
nn.init.normal_(self.w_input) |
|
else: |
|
self.register_parameter("w_input", None) |
|
if w_output: |
|
self.w_output = Parameter(torch.Tensor(self.n_actions)) |
|
nn.init.normal_(self.w_output, mean=90.0) |
|
else: |
|
self.register_parameter("w_output", None) |
|
|
|
def forward(self, inputs): |
|
if self.w_input is not None: |
|
inputs = inputs * self.w_input |
|
inputs = torch.atan(inputs) |
|
q_outputs = self.q_layers(inputs) |
|
q_outputs = (1 + q_outputs) / 2 |
|
|
|
outputs = self.layer1(q_outputs) |
|
|
|
if self.w_output is not None: |
|
outputs = outputs * self.w_output |
|
else: |
|
outputs = 90 * outputs |
|
return outputs |
|
|