Opt.step function and 'numpy.float64 object not callable' error

Hi Pennylane Team

I am having trouble with the opt.step function. Everything else appears to be working fine. Can anyone spot the problem please?

→ numpy.float64’ object is not callable

Influenza A virus (A/California/07/2009(H1N1)) genome assembly ViralMultiSegProj274766 - NCBI - NLM This is the download link for the Influenza A virus

Human immunodeficiency virus 1 genome assembly ViralProj15476 - NCBI - NLM This is the download link for HIV 1

import pennylane as qml
from pennylane import numpy as np
from pennylane.optimize import NesterovMomentumOptimizer
import random as rn

def readGenome(filename):
    genome = ''
    with open(filename, 'r') as f:
        for line in f:
            # ignore header line with genome information
            if not line[0] == '>':
                genome += line.rstrip()
    return genome

Influenza_seq=readGenome('virus_data/GCF_001343785.1_ViralMultiSegProj274766_genomic.fna')
HIV_1_seq=readGenome('virus_data/GCF_000864765.1_ViralProj15476_genomic.fna')

'''

This is a test version of the new DNA encoder
The classifier has to distinguish between Influenza virus and HIV 
Snippets of 128 bases length are used for the training

'''

Influenza_A_list=[]
for i in range(0,len(Influenza_seq),128):
    Influenza_A_list.append(Influenza_seq[i:i+128])
rn.shuffle(Influenza_A_list)  

HIV_1_list=[]
for i in range(0,len(HIV_1_seq),128):
    HIV_1_list.append(HIV_1_seq[i:i+128])
rn.shuffle(HIV_1_list)  


#create training and validation sets

train1=Influenza_A_list[:50]
train2=HIV_1_list[:50]

train=[[train1[i],0] for i in range(50)]   # add labels to each list element 0 for Influenza and 1 for HIV
train+=[[train2[i],1] for i in range(50)] 

rn.shuffle(train) # randomise the 100 sequence-label pairs 

# train can be assessed by train[index][0] for DNA sequence and train[index][1] for the label

def DNA_encoder(seq):

    '''
    Complex encoding of a DNA sequence
    The function returns a list of complex numbers
    '''
    
    DNA_list=list(seq)
    encode_DNA={'A':'-1','T':'1','C':'-1j','G':'1j', 'R':'-1+j'}   # R is an ambiguous result which could either be A or G
    encoded_seq=[complex(encode_DNA[base]) for base in DNA_list]
    return encoded_seq

''' variational quantum circuit

    the circuit uses alternating RQ and RX gates and entangling CNOT gates
'''


qubits=7
layers=5



dev = qml.device("lightning.qubit", wires=range(qubits))
@qml.qnode(dev, interface="autograd")



def circuit(encoded_seq, qubits,layers, weights):

    phi=weights.reshape(qubits,layers)   # not sure if this is a good idea but this makes it easier to update the weights
    
    qml.AmplitudeEmbedding(features=encoded_seq, wires=range(qubits), normalize=True, pad_with=0.)
    
    for l in range(layers):
        
        for q in range(qubits):
            
        # alternating Ry and Rz gates
            
            if q%2!=0:    #odd gate
                qml.RZ(phi[q,l], wires=q)
            else:
                qml.RY(phi[q,l], wires=q)
                
        for q in range(qubits-1):
                
            # entangling layer - connect neighbouring wires and the last and first one
            
            qml.CNOT(wires=[q,q+1])
            
        qml.CNOT(wires=[qubits-1,0])
        
    exp_vals=qml.expval(qml.PauliZ(0))
    
    return exp_vals
        
     
            

weights=0.01*np.random.random(size =qubits*layers, requires_grad=True)  


#print(circuit(encoded_seq,qubits,layers,weights))


def square_loss(x,y):
    return (y-x)**2      # we are just comparing one label at a time


def cost(encoded_seq, label, weights):
    prediction = circuit(encoded_seq, qubits, layers, weights)
    return square_loss(label, prediction)
    
opt = NesterovMomentumOptimizer(0.01)

  
for iter in range(1):
    # Update the weights by one optimizer step

    weights= opt.step(cost(DNA_encoder(train[iter][0]), train[iter][1], weights), weights)

    
#fig, ax=qml.draw_mpl(circuit, style="pennylane")(encoded_seq,qubits,layers,phi)
#fig.show()   



Full error message below:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[17], line 74
     68 opt = NesterovMomentumOptimizer(0.01)
     71 for iter in range(1):
     72     # Update the weights by one optimizer step
---> 74     weights= opt.step(cost(DNA_encoder(train[iter][0]), train[iter][1], weights), weights)

File ~/anaconda3/envs/Pennylane_new/lib/python3.11/site-packages/pennylane/optimize/gradient_descent.py:93, in GradientDescentOptimizer.step(self, objective_fn, grad_fn, *args, **kwargs)
     75 def step(self, objective_fn, *args, grad_fn=None, **kwargs):
     76     """Update trainable arguments with one step of the optimizer.
     77 
     78     Args:
   (...)
     90         If single arg is provided, list [array] is replaced by array.
     91     """
---> 93     g, _ = self.compute_grad(objective_fn, args, kwargs, grad_fn=grad_fn)
     94     new_args = self.apply_grad(g, args)
     96     # unwrap from list if one argument, cleaner return

File ~/anaconda3/envs/Pennylane_new/lib/python3.11/site-packages/pennylane/optimize/nesterov_momentum.py:76, in NesterovMomentumOptimizer.compute_grad(self, objective_fn, args, kwargs, grad_fn)
     73         shifted_args[index] = args[index] - self.momentum * self.accumulation[index]
     75 g = get_gradient(objective_fn) if grad_fn is None else grad_fn
---> 76 grad = g(*shifted_args, **kwargs)
     77 forward = getattr(g, "forward", None)
     79 grad = (grad,) if len(trainable_indices) == 1 else grad

File ~/anaconda3/envs/Pennylane_new/lib/python3.11/site-packages/pennylane/_grad.py:165, in grad.__call__(self, *args, **kwargs)
    162     self._forward = self._fun(*args, **kwargs)
    163     return ()
--> 165 grad_value, ans = grad_fn(*args, **kwargs)  # pylint: disable=not-callable
    166 self._forward = ans
    168 return grad_value

File ~/anaconda3/envs/Pennylane_new/lib/python3.11/site-packages/autograd/wrap_util.py:20, in unary_to_nary.<locals>.nary_operator.<locals>.nary_f(*args, **kwargs)
     18 else:
     19     x = tuple(args[i] for i in argnum)
---> 20 return unary_operator(unary_f, x, *nary_op_args, **nary_op_kwargs)

File ~/anaconda3/envs/Pennylane_new/lib/python3.11/site-packages/pennylane/_grad.py:183, in grad._grad_with_forward(fun, x)
    177 @staticmethod
    178 @unary_to_nary
    179 def _grad_with_forward(fun, x):
    180     """This function is a replica of ``autograd.grad``, with the only
    181     difference being that it returns both the gradient *and* the forward pass
    182     value."""
--> 183     vjp, ans = _make_vjp(fun, x)  # pylint: disable=redefined-outer-name
    185     if vspace(ans).size != 1:
    186         raise TypeError(
    187             "Grad only applies to real scalar-output functions. "
    188             "Try jacobian, elementwise_grad or holomorphic_grad."
    189         )

File ~/anaconda3/envs/Pennylane_new/lib/python3.11/site-packages/autograd/core.py:10, in make_vjp(fun, x)
      8 def make_vjp(fun, x):
      9     start_node = VJPNode.new_root()
---> 10     end_value, end_node =  trace(start_node, fun, x)
     11     if end_node is None:
     12         def vjp(g): return vspace(x).zeros()

File ~/anaconda3/envs/Pennylane_new/lib/python3.11/site-packages/autograd/tracer.py:10, in trace(start_node, fun, x)
      8 with trace_stack.new_trace() as t:
      9     start_box = new_box(x, t, start_node)
---> 10     end_box = fun(start_box)
     11     if isbox(end_box) and end_box._trace == start_box._trace:
     12         return end_box._value, end_box._node

File ~/anaconda3/envs/Pennylane_new/lib/python3.11/site-packages/autograd/wrap_util.py:15, in unary_to_nary.<locals>.nary_operator.<locals>.nary_f.<locals>.unary_f(x)
     13 else:
     14     subargs = subvals(args, zip(argnum, x))
---> 15 return fun(*subargs, **kwargs)

TypeError: 'numpy.float64' object is not callable


Name: PennyLane
Version: 0.35.1
Summary: PennyLane is a cross-platform Python library for quantum computing, quantum machine learning, and quantum chemistry. Train a quantum computer the same way as a neural network.
Home-page: GitHub - PennyLaneAI/pennylane: PennyLane is a cross-platform Python library for differentiable programming of quantum computers. Train a quantum computer the same way as a neural network.
Author:
Author-email:
License: Apache License 2.0
Location: /Users/brah/anaconda3/envs/Pennylane_new/lib/python3.11/site-packages
Requires: appdirs, autograd, autoray, cachetools, networkx, numpy, pennylane-lightning, requests, rustworkx, scipy, semantic-version, toml, typing-extensions
Required-by: PennyLane_Lightning

Platform info: macOS-12.6.3-arm64-arm-64bit
Python version: 3.11.8
Numpy version: 1.26.4
Scipy version: 1.12.0
Installed devices:

  • default.clifford (PennyLane-0.35.1)
  • default.gaussian (PennyLane-0.35.1)
  • default.mixed (PennyLane-0.35.1)
  • default.qubit (PennyLane-0.35.1)
  • default.qubit.autograd (PennyLane-0.35.1)
  • default.qubit.jax (PennyLane-0.35.1)
  • default.qubit.legacy (PennyLane-0.35.1)
  • default.qubit.tf (PennyLane-0.35.1)
  • default.qubit.torch (PennyLane-0.35.1)
  • default.qutrit (PennyLane-0.35.1)
  • null.qubit (PennyLane-0.35.1)
  • lightning.qubit (PennyLane_Lightning-0.35.1)

Hey @Quvance,

I don’t feel 100% comfortable that I’m downloading the correct data from the links you put in your post. Can you create some dummy data for us to work with instead? That’ll make it easier for me to copy-paste and replicate the issue on my side :slight_smile:

No problem - these are the first 10 training elements:

train=[['GAACTGGGAAATTGTGAAAATTCAATGGTCACAAGATCCCACAATGTTATACAACAAAATGGAATTTGAACCATTTCAGTCTCTTGTCCCTAAGGCAACCAGAAGCCGGTACAGTGGATTCGTAAGGA'
,0],
['ACCCCGAGGGGACCCGACAGGCCCGAAGGAATAGAAGAAGAAGGTGGAGAGAGAGACAGAGACAGATCCATTCGATTAGTGAACGGATCCTTGGCACTTATCTGGGACGATCTGCGGAGCCTGTGCCT'
,1],
['GCACGGAGATGTCGCTGAGAGGGATAAGAGTCAGCAAAATGGGAGTAGATGAATACTCCAGCACGGAGAGAGTGGTAGTGAGTATTGACCGATTTTTAAGGGTTAGAGATCAAAGAGGGAACGTACTA'
,0],
['AAGCTTGAACAGTCTGGGCTCCCAGTAGGGGGCAATGAAAAGAAGGCCAAACTGGCAAATGTTGTGAGAAAGATGATGACTAATTCACAAGACACAGAGATTTCTTTCACAATCACTGGGGACAACAC'
,0],
['TACAGCCTATAGTGCTGCCAGAAAAAGACAGCTGGACTGTCAATGACATACAGAAGTTAGTGGGGAAATTGAATTGGGCAAGTCAGATTTACCCAGGGATTAAAGTAAGGCAATTATGTAAACTCCTT'
,1],
['GGTATATAAAATTATTCATAATGATAGTAGGAGGCTTGGTAGGTTTAAGAATAGTTTTTGCTGTACTTTCTATAGTGAATAGAGTTAGGCAGGGATATTCACCATTATCGTTTCAGACCCACCTCCCA'
,1],
['TGGATGACAGAAACCTTGTTGGTCCAAAATGCGAACCCAGATTGTAAGACTATTTTAAAAGCATTGGGACCAGCGGCTACACTAGAAGAAATGATGACAGCATGTCAGGGAGTAGGAGGACCCGGCCA'
,1],
['TTCCTGAAGTCTGCTTAAAATGGGAGCTAATGGATGATGATTATCGGGGAAGACTTTGTAATCCCCTGAATCCCTTTGTCAGTCATAAAGAGATTGATTCTGTAAACAATGCTGTGGTAATGCCAGCC'
,0],
['ATGGAGAGAATAAAAGAACTGAGAGATCTAATGTCGCAGTCCCGCACTCGCGAGATACTCACTAAGACCACTGTGGACCATATGGCCATAATCAAAAAGTACACATCAGGAAGGCAAGAGAAGAACCC'
,0],
['AATTGTAGAATCTGGTGACCCGAATGCACTATTGAAGCACCGATTTGAGATAATTGAAGGAAGAGACCGAATCATGGCCTGGACAGTGGTGAACAGTATATGTAACACAACAGGGGTAGAGAAGCCTA'
,0]]

Ah! The issue is your optimization step:

    weights= opt.step(cost(DNA_encoder(train[iter][0]), train[iter][1], weights), weights)

The step method of optimizers needs to be a function, not the result of a function (which ends up being a float in this case, and that’s what’s causing the error). PennyLane is trying to call your first argument, expecting that it’s a function, and it can’t.

This should help:

for iter in range(1):
    # Update the weights by one optimizer step

    weights= opt.step(cost, DNA_encoder(train[iter][0]), train[iter][1], weights)

Let me know if this helps!

This works for only one iteration. I get a reshape error for more than 2 iterations because the step function gets confused by the other arguments.

The solution I have found is to take the DNA_encoder and the label out from step function like so:

for iter in range(10):
    # Update the weights by one optimizer step

    encoded_seq=DNA_encoder(train[iter][0])
    label=train[iter][1]

    weights= opt.step(cost, weights)

Apologies! One issue is that DNA_encoder should return a numpy object:

def DNA_encoder(seq):

    '''
    Complex encoding of a DNA sequence
    The function returns a list of complex numbers
    '''
    
    DNA_list=list(seq)
    encode_DNA={'A':'-1','T':'1','C':'-1j','G':'1j', 'R':'-1+j'}   # R is an ambiguous result which could either be A or G
    encoded_seq= np.array([complex(encode_DNA[base]) for base in DNA_list])
    return encoded_seq

In general, there are a few areas in your code that use operations that aren’t native to numpy, which can cause type errors like what you’re seeing (for reference: Autograd ArrayBox type intermediate output from optimizer - #3 by leongfy). Let me know if this helps you debug!

1 Like

Thank you so much Isaac.

It is working now in principle except it does not converge. I will close this thread for now and mark it as resolved.

The other issues I am currently trying to figure out relates to the right cost function most suited for a binary classification problem and using perhaps an artificial data set to see if this encoding scheme can actually work in practise. I may open up another request at a later time but in the meantime I will do some further tests.

Berend

Awesome! Glad to hear you’re on the right path :slight_smile:. Do let us know if you run into anymore blockers!