Stopping_condition in default qubits causing issues with generators

I’m having some trouble with using private static methods in my class definitions to help break down the decomposition in a simpler way.

class U8Gate(Operation):
    '''
    U8 gate, an 82-parameter generalized 3-qubit gate.
    '''
    num_params = 1 #  for pennylane internals, see nparams for actual number of parameters
    nparams = 82 # number of parameters
    num_wires = 3
    grad_method = "A"
    name = "U8"

    def __init__(self, theta, wires):
        if len(theta) != 82:
            raise ValueError("U8gate expects 82 parameters")
        super().__init__(theta, wires)

    @staticmethod
    def _ngate(theta, wires):
        '''
        Returns the N gate, a 3-parameter specialized 3-qubit gate.
        '''
        yield from [
            qml.RZ(-pnp.pi/2, wires=wires[1]),
            qml.Hadamard(wires=wires[2]),
            qml.CNOT(wires=wires[1::-1]),
            qml.CNOT(wires=wires[1:]),
            qml.RY(2*theta[0], wires=wires[1]),
            qml.CNOT(wires=wires[:2]),
            qml.RY(-2*theta[1], wires=wires[1]),
            qml.CNOT(wires=wires[:2]),
            qml.CNOT(wires=wires[1:]),
            qml.Hadamard(wires=wires[2]),
            qml.RZ(pnp.pi/2, wires=wires[1]),
            qml.CNOT(wires=wires[1::-1]),
            qml.CNOT(wires=wires[::2]),
            qml.CNOT(wires=wires[1:]),
            qml.RZ(2*theta[2], wires=wires[2]),
            qml.CNOT(wires=wires[1:]),
            qml.CNOT(wires=wires[::2])
        ]
    
    @staticmethod
    def _mgate(theta, wires):
        '''
        Returns the M gate, a 4-parameter specialized 3-qubit gate.
        '''
        yield from [
            qml.CNOT(wires=wires[2::-2]),
            qml.CNOT(wires=wires[:2]),
            qml.CNOT(wires=wires[2:0:-1]),
            qml.RZ(-pnp.pi/2, wires=wires[2]),
            qml.RY(2*theta[0], wires=wires[2]),
            qml.CNOT(wires=wires[1:]),
            qml.RY(-2*theta[1], wires=wires[2]),
            qml.CNOT(wires=wires[1:]),
            qml.RZ(pnp.pi/2, wires=wires[2]),
            qml.CNOT(wires=wires[2::-2]),
            qml.CNOT(wires=wires[:2]),
            qml.CNOT(wires=wires[1::-1]),
            qml.Hadamard(wires=wires[2]),
            qml.CNOT(wires=wires[::2]),
            qml.RZ(2*theta[2], wires=wires[2]),
            qml.CNOT(wires=wires[::2]),
            qml.RZ(2 * theta[3], wires=wires[2]),
            qml.CNOT(wires=wires[1::-1]),
            qml.Hadamard(wires=wires[2])
        ]
   
    def decomposition(self):
        '''
        Returns the decomposition of the U8 gate, as described in https://doi.org/10.48550/arXiv.quant-ph/0401178
        '''
        theta = self.parameters[0]
        wires = self.wires
        yield from [
            U4Gate(theta[:15], wires=wires[:2]),
            qml.U3(theta[15], theta[16], theta[17], wires=wires[2]),
            self._ngate(theta[18:21], wires=wires),
            U4Gate(theta[21:36], wires=wires[:2]),
            qml.U3(theta[36], theta[37], theta[38], wires=wires[2]),
            self._mgate(theta[39:43], wires=wires),
            U4Gate(theta[43:58], wires=wires[:2]),
            qml.U3(theta[58], theta[59], theta[60], wires=wires[2]),
            self._ngate(theta[61:64], wires=wires),
            U4Gate(theta[64:79], wires=wires[:2]),
            qml.U3(theta[79], theta[80], theta[81], wires=wires[2])
        ]

When i try to call this code in a circuit, i get an attribute error(generator has no attribute name) in this part of default_qubit.py:

def stopping_condition(op: qml.operation.Operator) -> bool:
    """Specify whether or not an Operator object is supported by the device."""
    if op.name == "QFT" and len(op.wires) >= 6:
        return False
    if op.name == "GroverOperator" and len(op.wires) >= 13:
        return False
    if op.name == "Snapshot":
        return True
    if op.__class__.__name__ == "Pow" and qml.operation.is_trainable(op):
        return False

    return op.has_matrix

when op = <generator object U8Gate._ngate at 0x7f6c763ba150>, since I haven’t written a whole different class for ngate and mgate. I’m wondering if this is intended behaviour since this is the only use of op.name i’ve seen in the source code like this, or if there’s a way to go about this without creating a whole new class.

Hey @somearthling, welcome back!

Custom operators need to provide one of generator , parameter_frequencies or grad_recipe. This might be at the heart of the issue.

Check out these resources for more information:

Let me know if this helps!

hey isaac,

not sure how that would affect this, the error I’m getting has to do with op.name being called on a generator object instead of unpacking then calling. I can’t seem to find documentation for any of the above, except Operation.generator. I have defined a grad_method for my operator, though

Okay! I’ll try and replicate the problem :slight_smile:. Can you show me what U4Gate is? I think that’s the only thing that I’m missing from your example.

U4Gate is a subclass of qml.operation.Operator, with a decomposition method defined, acting on 2 gates. I think for this example can be any 2-qubit gate. I recently started making my custom gate decompositions return generators, and I’m running into these kind of issues quite a bit(in another case, using qml.ctrl on this U8gate has the effect of checking length of a generator object)

Can you show me the code for it please :grin:? It would help me be able to run your code and try to reproduce your error :slight_smile: