Unexpected recursion error qml minimization

I’m using pennylane qml to minimize a cost function involving certain circuits. I’m unexpectedly running into a ‘max recursion depth error’. Here is my code:

from scipy import linalg
%pip install pennylane --upgrade
import pennylane as qml
import numpy as np


qbts=3
layers=3
params=layers*qbts + 2
opt = qml.optimize.AdagradOptimizer()
all_wires = [i for i in range(qbts)]
ansatz_wires = all_wires[1:]
dev = qml.device("default.qubit", analytic=True, wires=qbts+1)
@qml.template
def ansatz(params):
    param_idx = 0
    for i in ansatz_wires:
        qml.RY(params[param_idx], wires=i)
        param_idx=param_idx+1
    
    for _ in range(layers-1):
        for i in range(qbts):
            if i+1 == qbts:
                qml.CNOT(wires=[i,0])
            else:
                qml.CNOT(wires=[i,i+1])

        for i in range(qbts):
            qml.RY(params[param_idx], wires=i)
            param_idx=param_idx+1
@qml.template
def Cadder():
    adder_matrix = np.diag(np.ones(2**qbts), -1)
    adder_matrix[0,-1] = 1
    
    c_adder = linalg.block_diag(np.diag(np.ones(2**qbts)),adder_matrix)
    
    qml.QubitUnitary(c_adder,wires=all_wires)

@qml.qnode(dev)
def circuit(l2,l1,U):
    qml.Hadamard(0)
    
    ansatz(l1)
    for u in U[::-1]:
        if u == 'a': Cadder()
    qml.inv(ansatz(l2))
    
    qml.Hadamard(0)
    return qml.expval(qml.PauliZ(0))

def cost(l2,l1,U):
    psi = circuit(l2[2:],l1[2:],U)
    psi = [np.round(_,decimals=5) for _ in psi]
    return np.linalg.norm(np.array(psi)-np.linalg.norm(fx))

g0 = .02
def CostFunctionReal(l_new, l_old, g0=g0):
    l2 = l_new[2:]
    l1 = l_old[2:]
    R1=circuit(l2,l1,[ ])
    R2=circuit(l2,l1,['a']) #******* Issue caused by this line
    return R1 + R2
def costMin(l_old):
    return lambda l_new:CostFunctionReal(l_new,l_old)

x0 = 4*np.pi*np.random.rand(params)
x = opt.step(costMin(x0), x0)

As you can see, by the commented code, the issue is caused when I use the Cadder() circuit in the cost function CostFunctionReal(…) when setting the R2 term. Your feedback on fixing this error would be much appreciated. Thanks.

Hey @matp!

I managed to get it to work by setting U as a keyword argument:

from scipy import linalg
import pennylane as qml
from pennylane import numpy as np

qbts=3
layers=3
params=layers*qbts + 2
opt = qml.optimize.AdagradOptimizer()

all_wires = [i for i in range(qbts)]
ansatz_wires = all_wires[1:]

adder_matrix = np.diag(np.ones(2**qbts - 1), -1)
adder_matrix[0,-1] = 1

c_adder = linalg.block_diag(np.diag(np.ones(2**qbts)),adder_matrix)

dev = qml.device("default.qubit", analytic=True, wires=qbts+1)

@qml.template
def ansatz(params):
    param_idx = 0
    for i in ansatz_wires:
        qml.RY(params[param_idx], wires=i)
        param_idx=param_idx+1
    
    for _ in range(layers-1):
        for i in range(qbts):
            if i+1 == qbts:
                qml.CNOT(wires=[i,0])
            else:
                qml.CNOT(wires=[i,i+1])

        for i in range(qbts):
            qml.RY(params[param_idx], wires=i)
            param_idx=param_idx+1
            
@qml.template
def Cadder():
    qml.QubitUnitary(adder_matrix, wires=all_wires)

@qml.qnode(dev)
def circuit(l2,l1,U=None):
    qml.Hadamard(0)
    
    ansatz(l1)
    for u in U[::-1]:
        if u == 'a':
            Cadder()
    qml.inv(ansatz(l2))
    
    qml.Hadamard(0)
    return qml.expval(qml.PauliZ(0))

def cost(l2,l1,U):
    psi = circuit(l2[2:],l1[2:],U)
    psi = [np.round(_,decimals=5) for _ in psi]
    return np.linalg.norm(np.array(psi)-np.linalg.norm(fx))

l1 = np.random.random(params)
l2 = np.random.random(params)

c1 = circuit(l2, l1, U=[])
c2 = circuit(l2, l1, U=["a"])

print(c1, c2)

You can see in the code above that we use def circuit(l2,l1,U=None), which allows the QNode to not worry about differentiating with respect to U. I think having a list of strings was confusing autograd, which is the default interface for supporting differentiation of the QNode.

Some other changes in the above:

  • Had to user adder_matrix rather than c_adder as the unitary in Cadder since c_adder was of too big dimension
  • You should do from pennylane import numpy as np so that numpy functionality can keep track of the gradient (see here)

Hope this helps!

Thank you very much, I was able to solve the issue thanks to your feedback!

1 Like