tensorflow / quantum

Hybrid Quantum-Classical Machine Learning in TensorFlow
https://www.tensorflow.org/quantum
Apache License 2.0
1.77k stars 560 forks source link

Having two PQC layers with the training circuits in between. #672

Open L-P-B opened 2 years ago

L-P-B commented 2 years ago

Hello,

I'm pretty new to TFQ. I have a basic question.

Say I have a set of simple quantum circuits that are my training circuits - T

I want a QNN that consists of two parameterized quantum circuits (PQC_1 and PQC_2) , one before and one after the action of the circuits in T. Where PQC_1 and PQC_2 take different parameters.

In summary, I want to be able to train a QNN with the structure: PQC_1 -> T -> PQC_2 -> Measure.

In TFQ it appears that the tfq.layers.PQC structure must always take readout operators, and so this is not fit to my purpose.

Also when I try to use tfq.layers.AddCircuit the parameters do not seem to be stored in the way I want for optimization.

Is there a simple way to construct something of the above form?

Thanks!

lockwo commented 2 years ago

There are probably several ways to code this, but two immediately come to mind. If T is of a constant circuit structure (or is a small finite set of structures) that simply vary by the parameters, that is basically the same as a data re-uploading PQC. There are several implementation of this in TFQ, one example can be found in the tutorials section (https://www.tensorflow.org/quantum/tutorials/quantum_reinforcement_learning). If your T circuits are inconsistent, then you could just use the expectation layer (https://www.tensorflow.org/quantum/api_docs/python/tfq/layers/Expectation) and create the full circuits yourself. See a trivial example of that below:

import tensorflow as tf
import tensorflow_quantum as tfq
import cirq
import sympy
import numpy as np

qubits = [cirq.GridQubit(0, i) for i in range(2)]

T = cirq.Circuit()
T += cirq.H(qubits[0])
T += cirq.CNOT(qubits[0], qubits[1])

params = sympy.symbols("q0:4")

PQC_1 = cirq.Circuit()
PQC_1 += cirq.ry(params[0]).on(qubits[0])
PQC_1 += cirq.ry(params[1]).on(qubits[1])

PQC_2 = cirq.Circuit()
PQC_2 += cirq.ry(params[2]).on(qubits[0])
PQC_2 += cirq.ry(params[3]).on(qubits[1])

full_circuit = PQC_1 + T + PQC_2

expectation_layer = tfq.layers.Expectation()

ops = [cirq.Z(qubits[0]) * cirq.Z(qubits[1])]
weights = tf.Variable(initial_value=np.random.uniform(0, 2 * np.pi, (1, 4)), trainable=True, dtype=tf.float32)
opt = tf.keras.optimizers.Adam(learning_rate=0.08)

for i in range(20):
  with tf.GradientTape() as tape:
    energy = expectation_layer(full_circuit, symbol_names=params, symbol_values=weights, operators=ops)
  grads = tape.gradient(energy, weights)
  opt.apply_gradients(zip([grads], [weights]))
  print(i, energy.numpy()[0][0])
L-P-B commented 2 years ago

Thanks so much!

lockwo commented 1 year ago

No problem! Any updates on this issue or can it be closed?