QuantumFunctionError using QNGOptimizer with QAOA


I’m trying to implement the QNGOptimizer in a QAOA circuit, but the following error arises when I start the optimization

QuantumFunctionError: Can't generate metric tensor, operation MultiRZ: 1 params, wires [0, 1]has no defined generator

The circuit I’m implementing has the following form

    def circuit(params, **kwargs):
        qml.layer(qaoa_layer, p, params[0], params[1])

where Initial_state() generates our initial state implementing the corresponding Hadamard gates, and qaoa_layer is defined as

def qaoa_layer(gamma, alpha):
    qaoa.cost_layer(gamma, cost_h)
    qaoa.mixer_layer(alpha, mixer_h)

where cost_h and mixer_h come from

cost_h, mixer_h = qaoa.maxcut(G)

And I do the optimization as

opt = qml.QNGOptimizer(stepsize = 0.1)
steps = 50
params = 0.01*np.random.rand(2, p)
for i in range(steps):
    params = opt.step(cost_function, params)
    print("Objective after step {:5d}: {: .7f}".format(i + 1, cost_function(params))

I’ve tried with other optimization methods like Adam or vanilla gradient descent and the optimization works nicely (it might get stucked in a local minima, but no errors appear). However, I don’t know exactly what is going on in this case when calculating the metric tensor. I’ve tried in other similar QAOA circuits where I directly define a function that implements the gates instead of loading them through the qaoa package, and the optimization works.

Thank you very much in advance!

P.D.: This is how I import the different packages

import pennylane as qml
from pennylane import qaoa

Hi @Javier,

Thanks so much for your question! :slight_smile:

The QAOA module indeed uses the MultiRZ operation of PennyLane. The generator referred to in the error message that you are facing is something that is missing for the MultiRZ operation. The generator of the operation is needed to compute the metric tensor of the quantum circuit. This metric tensor is used for optimization with the QNGOptimizer.

We are looking into adding support for the QAOA module when used with the QNGOptimizer and will get back to you on it. :slight_smile:

Hi @Javier, as @antalszava noted, we currently do not support the MultiRZ gate for QNG optimization.

However, one way of forcing it to work now is to temporarily ‘remove’ the capability for default.qubit to accept MultiRZ gates:

dev = qml.device('default.qubit', wires=len(wires))

When executing a QNode on this device, PennyLane will now automatically decompose the MultiRZ gate into Pauli rotations.

Before removing MultiRZ from default.qubit:

 0: ──H──╭RZ(0.006)──╭RZ(0.006)───H──────────RZ(0.017)──H─────────────╭RZ(0.001)──╭RZ(0.001)───H──────────RZ(0.007)──H─────────────╭┤ ⟨Z ⊗ Z⟩
 1: ──H──╰RZ(0.006)──│───────────╭RZ(0.006)──H──────────RZ(0.017)──H──╰RZ(0.001)──│───────────╭RZ(0.001)──H──────────RZ(0.007)──H──╰┤ ⟨Z ⊗ Z⟩
 2: ──H──────────────╰RZ(0.006)──╰RZ(0.006)──H──────────RZ(0.017)──H──────────────╰RZ(0.001)──╰RZ(0.001)──H──────────RZ(0.007)──H───┤

After removing it, we can see that the MultiRZ gate has been decomposed:

 0: ──H──╭X──RZ(0.007)──╭X──╭X──RZ(0.007)──╭X───H──RZ(0.008)───H───────────────────╭X──RZ(0.008)──╭X──╭X──RZ(0.008)──╭X───H──RZ(0.007)───H───────────────────╭┤ ⟨Z ⊗ Z⟩
 1: ──H──╰C─────────────╰C──│──────────────│───╭X──RZ(0.007)──╭X──H──RZ(0.008)──H──╰C─────────────╰C──│──────────────│───╭X──RZ(0.008)──╭X──H──RZ(0.007)──H──╰┤ ⟨Z ⊗ Z⟩
 2: ──H─────────────────────╰C─────────────╰C──╰C─────────────╰C──H──RZ(0.008)──H─────────────────────╰C─────────────╰C──╰C─────────────╰C──H──RZ(0.007)──H───┤
1 Like

Hi @antalszava and @josh, thank you very much for the answers! They were really helpful!

Please @antalszava, keep me updated about the new things with the QNGOptimizer related to the QAOA module.

@josh, thank you very much for the solution, now my code is working nicely with the QNGOptimizer.

Hi again,

I have another minor question. I was trying to implement noise in the system (bit flip operations concretely) using the qml.device("cirq.mixedsimulator") together with the QNGOptimizer. In this case, I didn’t have to implement the “MultiRZ” line @josh suggested. Do you know why? I just want to understand better the qml.device function.

Thank you very much!


Hi @Javier,

Nice find! When executing a QNode, PennyLane checks that the device supports each operation defined in the quantum function. Whenever an unsupported operation is encountered, PennyLane tries to decompose the operation into elementary gates that are pre-defined.

Josh’s suggestion helped with removing the support for the MultiRZ gate from the 'default.qubit' device such that more elementary gates can be used, for which generators are defined.

The "cirq.mixedsimulator" device does not support the MultiRZ gate, so elementary gates are being used by default. The set of operations supported by the device can be checked by looking at the dev.operations dictionary. The reason for no direct support for the MultiRZ gate when using "cirq.mixedsimulator" is that there is no corresponding native Cirq operation that could be used. Therefore, the decomposition of the MultiRZ operation using elementary gates is used instead.

The following is an example of MultiRZ decomposition used internally:

In [1]: qml.MultiRZ.decomposition(0.3, wires=[3,5])                                                                                         
Out[1]: [CNOT(wires=[5, 3]), RZ(0.3, wires=[3]), CNOT(wires=[5, 3])]

The qml.device function is a sort of global loader for every device that was installed with PennyLane and PennyLane plugins. In more details, this loading is achieved through using Python entry points and each device is registered by defining an entry point for the device in the setup.py file.

Hope this helps, let us know if you have further questions! :slight_smile:

1 Like