Open Shuhul24 opened 1 year ago
To apply it on a |0> state you can just define an tensor in_ = tfq.convert_to_tensor([cirq.Circuit()])
then do PQC(in_)
. You can input arbitrary circuits (I think they have to be serializable though) as an input as well (not just an empty circuit).
Thanks for the reply! I have a piece of code that I implemented:
gen_params = sympy.symbols("a0:13")
def encode_circuit_gen(params):
qubits = [cirq.GridQubit(1, 0), cirq.GridQubit(1, 1)]
circuit = cirq.Circuit()
circuit.append(cirq.H(cirq.GridQubit(1, 0)))
circuit.append(cirq.H(cirq.GridQubit(1, 1)))
circuit.append(cirq.Rx(rads=params[0]).on(cirq.GridQubit(1, 0)))
circuit.append(cirq.Rx(rads=params[1]).on(cirq.GridQubit(1, 1)))
return circuit
class QuantumLayer(tf.keras.layers.Layer):
def __init__(self) -> None:
super().__init__()
ops = [cirq.Z(cirq.GridQubit(1, 0)), cirq.Z(cirq.GridQubit(1, 1))]
qubits = [cirq.GridQubit(1, 0), cirq.GridQubit(1, 1)]
encoded = encode_circuit_gen(gen_enc_param)
ansatz_circuit = ansatz_gen(qubits, gen_params)
circuit = encoded + ansatz_circuit
self.quantum_operation = tfq.layers.ControlledPQC(circuit, ops,
differentiator=tfq.differentiators.ParameterShift())
self.quantum_weights = tf.Variable(initial_value = np.random.uniform(-np.pi, np.pi, len(gen_params)), dtype="float32", trainable=True)
self.circuit_tensor = tfq.convert_to_tensor([cirq.Circuit()])
def call(self, inputs):
circuit_batch_dim = tf.gather(tf.shape(inputs), 0)
tiled_b = tf.tile(tf.expand_dims(self.quantum_weights, 0), [circuit_batch_dim, 1])
quantum_inputs = tf.concat([inputs, tiled_b], axis=1)
tiled_circuits = tf.tile(self.circuit_tensor, [circuit_batch_dim])
quantum_output = self.quantum_operation([tiled_circuits, quantum_inputs])
return quantum_output
Is this circuit taking in |0> state or an encoded part? This has been taken from https://github.com/lockwo/quantum_computation/blob/master/TFQ/RL_QVC/atari_qddqn.py and https://github.com/lockwo/quantum_computation/blob/master/TFQ/RL_QVC/policies.py
Good to see my code be useful to others. The input flow goes circuit_tensor
-> encoded
-> ansatz_circuit
-> Z, Z
ops. The circuit tensor input is the |0> state. It looks like the layer input (i.e. the input to the call function) are the encoding parameters.
Thanks for the reply! I have updated my above code as follows:
class QuantumLayer(tf.keras.layers.Layer):
def __init__(self, batch_size) -> None:
super().__init__()
ops = [cirq.Z(cirq.GridQubit(1, 0)), cirq.Z(cirq.GridQubit(1, 1))]
qubits = [cirq.GridQubit(1, 0), cirq.GridQubit(1, 1)]
# encoded = encode_circuit_gen(gen_enc_param)
ansatz_circuit = ansatz_gen(qubits, gen_params)
circuit = ansatz_circuit
self.batch_size = batch_size
self.quantum_operation = tfq.layers.ControlledPQC(circuit, ops)
# differentiator=tfq.differentiators.ParameterShift())
self.quantum_weights = tf.Variable(initial_value = np.random.uniform(-np.pi, np.pi, len(gen_params)), dtype="float32", trainable=True)
self.circuit_tensor = tfq.convert_to_tensor([cirq.Circuit()])
def call(self):
batch = tf.constant(self.batch_size)
tiled = tf.tile(tf.expand_dims(self.quantum_weights, 0), [batch, 1])
# circuit_batch_dim = tf.gather(tf.shape(inputs), 0)
# tiled_b = tf.tile(tf.expand_dims(self.quantum_weights, 0), [circuit_batch_dim, 1])
# quantum_inputs = tf.concat([inputs, tiled_b], axis=1)
circuits = tf.tile(self.circuit_tensor, [batch])
quantum_output = self.quantum_operation([circuits, tiled])
return quantum_output
In this I haven't taken anything input, and the initial states, I suppose, are to be |0>. But the error I am coming up is when I call the function inside tf.keras.Sequential
layer.
quantum = tf.keras.Sequential([
QuantumLayer(1),
])
print(quantum)
But the output comes out to be:
python <keras.engine.sequential.Sequential object at 0x7f40337cfe80>
which is not what I desire. I need the measured values to be in the output rather than this keras.engine.sequential.Sequential
. Is this possible anyhow?
You have to actually call it (not just print what it is). So do quantum() rather than quantum.
After doing quantum()
, I'm getting an error:
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
[<ipython-input-30-9ac82edd80fb>](https://localhost:8080/#) in <module>
2 Generator(1),
3 ])
----> 4 generator()
1 frames
[/usr/local/lib/python3.8/dist-packages/keras/engine/base_layer.py](https://localhost:8080/#) in _split_out_first_arg(self, args, kwargs)
3074 inputs = kwargs.pop(self._call_fn_args[0])
3075 else:
-> 3076 raise ValueError(
3077 'The first argument to `Layer.call` must always be passed.')
3078 return inputs, args, kwargs
ValueError: The first argument to `Layer.call` must always be passed.
Is there something that I am missing?
Are you doing quantum or quantum()? It seems like the object is being printed, not the result of the call.
After doing quantum(), I'm getting an error:
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
[<ipython-input-30-9ac82edd80fb>](https://localhost:8080/#) in <module>
2 Generator(1),
3 ])
----> 4 generator()
1 frames
[/usr/local/lib/python3.8/dist-packages/keras/engine/base_layer.py](https://localhost:8080/#) in _split_out_first_arg(self, args, kwargs)
3074 inputs = kwargs.pop(self._call_fn_args[0])
3075 else:
-> 3076 raise ValueError(
3077 'The first argument to `Layer.call` must always be passed.')
3078 return inputs, args, kwargs
ValueError: The first argument to `Layer.call` must always be passed.
Is there something that I am missing?
It looks like TF models require an input to all call functions (makes sense, TF tries to make a compute graph mapping inputs to outputs) and it doesn't like that you aren't passing anything when calling
Well, in my case batch_size
is the input that I am using. Following is the tweaked code:
class QuantumLayer(tf.keras.layers.Layer):
def __init__(self) -> None:
super(QuantumLayer, self).__init__()
ops = [cirq.Z(cirq.GridQubit(1, 0)), cirq.Z(cirq.GridQubit(1, 1))]
qubits = [cirq.GridQubit(1, 0), cirq.GridQubit(1, 1)]
# encoded = encode_circuit_gen(gen_enc_param)
ansatz_circuit = ansatz_gen(qubits, gen_params)
circuit = ansatz_circuit
self.quantum_operation = tfq.layers.ControlledPQC(circuit, ops)
# differentiator=tfq.differentiators.ParameterShift())
self.quantum_weights = tf.Variable(initial_value = np.random.uniform(-np.pi, np.pi, NUM_GPARAMS), dtype="float32", trainable=True)
self.circuit_tensor = tfq.convert_to_tensor([cirq.Circuit()])
def call(self, batch_size):
batch = tf.constant(batch_size)
tiled = tf.tile(tf.expand_dims(self.quantum_weights, 0), [batch, 1])
# circuit_batch_dim = tf.gather(tf.shape(inputs), 0)
# tiled_b = tf.tile(tf.expand_dims(self.quantum_weights, 0), [circuit_batch_dim, 1])
# quantum_inputs = tf.concat([inputs, tiled_b], axis=1)
circuits = tf.tile(self.circuit_tensor, [batch])
quantum_output = self.quantum_operation([circuits, tiled])
return quantum_output
Here, in the call function, I have applied batch_size
which is what I am taking as an input (which isn't necessary, yet Layer.call
needs an input argument). Yet I'm getting an error.
Can I use tfq.layers.ControlledPQC
as a variable and then apply tape.gradient
and optimizer.apply_gradient
?
You can apply it to the trainable variables of a ControlledPQC, yes
Cool. Will take this into consideration and try to implement it in an another way. Thanks a lot for the replies!
I have got one doubt that is kinda creating a problem. Say that I have built a circuit on 2 qubits, which are, cirq.GridQubit(1,0)
and cirq.GridQubit(1, 1)
. Consider that the circuit consists a bunch of rotation operations and CNOT gates in it. Now, I have applied this quantum circuit through tfq.layers.ControlledPQC
and for the observable part I have applied cirq.Z(cirq.GridQubit(1,0))
. Does this observable means that it will measure the expectation value in |0>,|1> basis AFTER all the operations (rotations and entangling gates) has occured or BEFORE all these operations. Because in the MNIST example they have considered the readout
at cirq.GridQubit(-1, -1)
which kind of creates a doubt that the observable will be giving the same output as in the MNIST example?
It is after, the ControlledPQC circuit is applied after the input then the result is generated
I am using
ControlledPQC
andPQC
for a couple of times in quantum machine learning. My question is that since I don't have any encoding data to be integrated into the quantum circuit, how can I just apply aControlledPQC
orPQC
on just states that initialized as 0 states. In tutorials ofPQC
, they have been using some classical data and then encoding them and then applying thetfq.layers.PQC
and finally compiling all these intotf.keras.Sequential
. But in my case, I have input as qubits initialized as states? Is there any way to applyPQC
in this case?Edit: I came across
tfq.layers.Expectation
which takes a circuit and a bunch of parameters as an input. But I don't get to know how to usetfq.layers.Expectation
epoch-wise on a bunch of data? Is there any sample of code available where I can apply it on a very small data, saytf.random.normal
? How cantfq.layers.Expectation
be used astf.keras.layers.Layer
class?