The documentation for qml.QnodeCollection says all qnodes have to share the same parameters. I am looking to get some more insight to how this works.
I am running a pytorch experiment with several of the same qnodes that take in the same input vector. I want each circuit to have its own unique randomly distributed weights (i.e separate calls of torch.randn), and also for each circuit to update its own weights throughout training. Is this possible?
Seem to have a problem with the weights no longer training now. Is there anything different I should be doing? For reference, my code roughly follows the pennylane transfer learning tutorial.
class Generator_Quantum(nn.Module):
def __init__(self, n_qubits, q_depth, q_delta=0.1):
"""
This is the quantum generator as described in https://arxiv.org/pdf/2010.06201.pdf
"""
super().__init__()
self.q_params = nn.ParameterList([nn.Parameter(q_delta * torch.randn(q_depth * n_qubits)) for i in range(8)])
self.n_qubits = n_qubits
self.q_depth = q_depth
# Spread of the random parameters for the paramaterised quantum gates
self.q_delta = q_delta
device = qml.device('lightning.qubit', wires=self.n_qubits)
# This is just a class with a simple circuit function - obtained by quantum_sim.circuit()
self.quantum_sim = QuantumSim(n_qubits, q_depth)
self.qnodes = qml.QNodeCollection(
[qml.QNode(self.quantum_sim.circuit, device, interface="torch") for i in range(8)]
)
def forward(self, noise):
q_out = torch.Tensor(0, 8* (2**self.n_qubits))
q_out = q_out.to(device)
# Apply the quantum circuit to each element of the batch and append to q_out
for elem in noise:
patched_array = np.empty((0, 2**self.n_qubits))
for p, qnode in zip(self.q_params, self.qnodes):
q_out_elem = qnode(elem, p).float().detach().cpu().numpy()
patched_array = np.append(patched_array, q_out_elem)
patched_tensor = torch.Tensor(patched_array).to(device).reshape(1, 8* (2**self.n_qubits))
q_out = torch.cat((q_out, patched_tensor))
return q_out
Let me explain this line q_out = torch.Tensor(0, 8* (2**self.n_qubits))
The 2 ** self.n_qubits comes from the circuit output being qml.probs() on all qubits. The 8 is because I am trying to have 8 lightning.qubits run at the same time for a concatenated output.
On each forward pass, a single vector ‘noise’ of size n_qubits, is passed to each 8 nodes.
`for elem in noise:`
This for loops over the batch
I hope I made it clear… if not I can provide more code or explanations if I forgot to define anything.
Hi @James_Ellis. Based on the functions you posted above, it is not easy to find out why the weights cannot be trained. Could you please post the full working version of your code which provides actual output? Thanks.
Hi @James_Ellis. Thank you for sharing the code. I had a brief look at it, and I can reproduce the issue with the weight not training, although I don’t know exactly why that is. There are a lot of things going on, and it’d take quite a bit of effort to understand and pinpoint the exact issue.
It would be best if you could try to find out exactly what part of the code that causes this and attempt to create a minimal example where it still happens e.g. just training a smaller QNodeCollection directly, with/without torch. If the issue still persist, feel free to let us know and we can have a closer look at it.