Grad only applies to real scalar-output functions

Hi,
i have the following quantum circuit. I got the error

“TypeError: Grad only applies to real scalar-output functions. Try jacobian, elementwise_grad or holomorphic_grad.”

while trying to optimize the cost function. What can’t i do this?
Thank you in advance.

n_qubits = 4 # 4 actions for lunar lander 
device = qml.device("default.qubit", wires=n_qubits)

@qml.qnode(device)
def qcircuit(weights, input = None):
    AngleEmbedding(input, wires=range(n_qubits))
    SEL(weights, wires=range(n_qubits))
    
    return [qml.expval(qml.PauliZ(wires=i)) for i in range(n_qubits)]

def softmax(x):
    return [np.exp(i) / np.sum(np.exp(i)) for i in x]

def one_hot(labels, num_labels):
    one_hot_diag_matix = np.identity(num_labels)
    return [one_hot_diag_matix[i] for i in labels]

def cross_entropy_loss(dist, labels, num_labels):
    p = softmax(dist)
    one_hot_labels = one_hot(labels,num_labels)
    
    results = []
    for pd,l in zip(p,one_hot_labels):
        results.append(-np.sum([l[i]*np.log(pd[i]) for i in range(num_labels)]))

    return np.mean(results)

def cost(weights, labels, inputs):
    predictions = [qcircuit(weights, input=i) for i in inputs]
    return cross_entropy_loss(predictions, labels, inputs.shape[1])


max_steps = 60
opt = qml.AdamOptimizer(0.3)
nlayers = 1

np.random.seed(42)

inputs = np.random.random((10,4))
output = np.random.choice([0,1],10)
weights = 2*np.pi*np.random.random((nlayers,n_qubits,3))

for step in range(max_steps):
    weights = opt.step(lambda w: cost(w,output,inputs), weights)
    print("{}\n{}".format(step,weights))
    print("Cost at step {} ---> {}".format(cost(weights,output, inputs)))

Hi @Andre_Sequeira!

I am able to get your code running by replacing the np.mean(result) with np.mean(np.array(result)). For the autodifferentiation of np.mean to work, the input should (as far as I can tell) always be a NumPy array.

The full code is below:

import pennylane as qml
from pennylane import numpy as np

n_qubits = 4 # 4 actions for lunar lander
device = qml.device("default.qubit", wires=n_qubits)

@qml.qnode(device)
def qcircuit(weights, input = None):
    qml.templates.AngleEmbedding(input, wires=range(n_qubits))
    qml.templates.StronglyEntanglingLayers(weights, wires=range(n_qubits))

    return [qml.expval(qml.PauliZ(wires=i)) for i in range(n_qubits)]

def softmax(x):
    return [np.exp(i) / np.sum(np.exp(i)) for i in x]

def one_hot(labels, num_labels):
    one_hot_diag_matix = np.identity(num_labels)
    return [one_hot_diag_matix[i] for i in labels]

def cross_entropy_loss(dist, labels, num_labels):
    p = softmax(dist)
    one_hot_labels = one_hot(labels,num_labels)

    results = []
    for pd,l in zip(p,one_hot_labels):
        results.append(-np.sum([l[i]*np.log(pd[i]) for i in range(num_labels)]))

    return np.mean(np.array(results))

def cost(weights, labels, inputs):
    predictions = [qcircuit(weights, input=i) for i in inputs]
    return cross_entropy_loss(predictions, labels, inputs.shape[1])


max_steps = 60
opt = qml.AdamOptimizer(0.3)
nlayers = 1

np.random.seed(42)

inputs = np.random.random((10,4))
output = np.random.choice([0,1],10)
weights = qml.init.strong_ent_layers_normal(n_layers=nlayers, n_wires=n_qubits)

for step in range(max_steps):
    weights, cost_value = opt.step_and_cost(lambda w: cost(w,output,inputs), weights)
    print("{}\n{}".format(step,weights))
    print("Cost at step {} ---> {}".format(step, cost_value))

Let me know if this works!

Note that I also made an additional change; I replaced opt.step with opt.step_and_cost(), which will avoid the overhead of computing the cost function twice. This is a new feature of PennyLane v0.13, released last week.

Hi @josh, thank you so much for your help. I can run the code now :smiley:

Amazing ! We were all been hoping for this for a while now. Keep on the good work!

3 Likes