I am working on a toy problem of regression using QNN circuit given in strawberry fields’ website. There, we have used tensorflow’s apply_gradient method, that follows your parameter in the forward pass (from 10th line to 7th).
for i in range(1000):
# reset the engine if it has already been executed
if eng.run_progs:
eng.reset()
with tf.GradientTape() as tape:
loss, fid, ket, trace = cost(weights)
# one repetition of the optimization
gradients = tape.gradient(loss, weights)
opt.apply_gradients(zip([gradients], [weights]))
where cost function is
def cost(weights):
# Create a dictionary mapping from the names of the Strawberry Fields
# symbolic gate parameters to the TensorFlow weight values.
mapping = {p.name: w for p, w in zip(sf_params.flatten(), tf.reshape(weights, [-1]))}
# run the engine
state = eng.run(qnn, args=mapping).state
ket = state.ket()
difference = tf.reduce_sum(tf.abs(ket - target_state))
fidelity = tf.abs(tf.reduce_sum(tf.math.conj(ket) * target_state)) ** 2
return difference, fidelity, ket, tf.math.real(state.trace())
We can see that weights are given in the circuit as arg in eng.run(). This works well and good until I have to add X values (regression AX=Y) as parameters to the circuit. But I don’t want the gradients with respect to those X values.
I have done the following for encoding of X values into the circuit
with qnn.context as q:
for i in range(N):
ops.Dgate(x[0][i],0) | q[i] # this is where I am encoding.
for k in range(layers):
layer(sf_params[k], q)
and a little tweaking in the cost function as follows
def cost(param): # X_target should be scalar and X 1-D
#mapping = {p.name: w for p, w in zip(sf_params.flatten(), tf.reshape(params[i], [-1]))}
a=np.concatenate([sf_params.flatten(),x.flatten()])
b=tf.convert_to_tensor(np.concatenate([ np.reshape(param, [-1]),X[0]]))
mapping = {p.name: w for p, w in zip(a,b)}
# all of this is just to include x values in mapping.
pred = eng.run(qnn, args=mapping).state
Y = pred.quad_expectation(0,phi=0.)[0]
diff = tf.abs(tf.cast(Y,dtype=tf.float64)-y[0])
return diff
and main looks like
opt = tf.keras.optimizers.Adam(learning_rate = lr)
cost_progress = []
best_fid = 0
# same old thing
eng = sf.Engine(backend='tf',backend_options={"cutoff_dim":cutoff})
qnn = sf.Program(N)
sf_params = np.arange(num_params).reshape(params.shape).astype(str)
sf_params = np.array([qnn.params(*i) for i in sf_params])
# a mere modification
x= np.arange(num_params,N+num_params).astype(str)
x= np.array([qnn.params(*i) for i in [x]])
y = tf.constant(y)
X = tf.constant(X)
with qnn.context as q:
for i in range(N):
ops.Dgate(x[0][i],0) | q[i]
for k in range(layers):
layer(sf_params[k], q)
#ops.MeasureX | q[0],q[1]
cost_progress=[]
for i in range(reps):
if eng.run_progs:
eng.reset()
with tf.GradientTape() as tape:
tape.watch(params)
loss = cost(params)
# one repetition of the optimization
gradients = tape.gradient(loss, params)
print(i, loss ,params, gradients)
opt.apply_gradients(zip([gradients], [params]))
cost_progress.append(loss)
This code is unable to calculate the gradients, it gives gradients= NONE, when I print them. And I think, it is only due to the above complexity of the code. So, what is the efficient way to do all this, so that my optimizer can understand my cost function.
Exact error is the following
ValueError: No gradients provided for any variable: ([‘Variable:0’],). Provided grads_and_vars
is ((None, <tf.Variable ‘Variable:0’ shape=(1, 14) dtype=float32, numpy=
array([[ 1.6708091e-02, 2.2838793e-05, -5.2734390e-03, -6.9317231e-03,
-3.0103172e-03, 3.6114827e-03, -1.0168127e-02, 6.7833858e-03,
1.1122092e-02, 1.8201470e-02, -8.2193566e-03, -6.1826045e-03,
1.2012760e-02, 1.0526734e-02]], dtype=float32)>),).