# Defining Operations including qml.ctrl raises problems

**URL:** <https://discuss.pennylane.ai/t/defining-operations-including-qml-ctrl-raises-problems/9126>\
**Category:** PennyLane Help\
**Created:** [November 28, 2025, 11:29am UTC](https://discuss.pennylane.ai/t/defining-operations-including-qml-ctrl-raises-problems/9126 "2025-11-28T11:29:16Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![Pia](https://avatars.discourse-cdn.com/v4/letter/p/6a8cbe/32.png) [@Pia](https://discuss.pennylane.ai/u/Pia)\
**Post date:** [November 28, 2025, 11:29am UTC](https://discuss.pennylane.ai/t/defining-operations-including-qml-ctrl-raises-problems/9126/1 "2025-11-28T11:29:16Z")

</div>

Dear Pennylane Team,

I am currently trying to implement a quantum circuit composed of various Operations and Operations of Operations. However, I face trouble as soon as I want to include the command qml.ctrl() in any of my Operator-definitions.

Let me explain the issue with a minimal example:  
If I define the Operator like this:

```auto
class controlled_H_gate(Operation):
    num_wires = AnyWires
    grad_method = "A"

    def __init__ (self,wires, id=None):
        super(). __init__ (wires=wires, id=id) # calls the init of the operator class

    @staticmethod
    def compute_decomposition(wires):
        n_wires_1 = wires[0:1]
        n_aux_1 = wires[1:2]
        return qml.ctrl(qml.H, control = n_aux_1, control_values=[0])(wires=n_wires_1[0:1])

```

and want to call it within a quantum circuit

```auto
n = 2
dev_test = qml.device("default.qubit", wires=n)

@qml.qnode(dev_test)
def test_circuit():
    w = dev_test.wires
    controlled_H_gate(wires=w)
    return qml.state()

```

it will run without an error **but the operator is apperently not applied** as the output-state and a verification with

```auto
fig, ax = qml.draw_mpl(test_circuit, level=“device”)()
plt.show()

```

shows.  
If I modify my circuit, by returning a list (as I would usually do), i.e.:

```auto
class controlled_H_gate(Operation):
    num_wires = AnyWires
    grad_method = "A"

    def __init__ (self,wires, id=None):
        super(). __init__ (wires=wires, id=id) # calls the init of the operator class

    @staticmethod
    def compute_decomposition(wires):
        n_wires_1 = wires[0:1]
        n_aux_1 = wires[1:2]
        return [qml.ctrl(qml.H, control = n_aux_1, control_values=[0])(wires=n_wires_1[0:1])]

```

and then try ot run the quantum circuit I get the error message:

```auto
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[110], line 10
      7 controlled_H_gate(wires=w)
      8 return qml.state()
---> 10 print( test_circuit())
     11 fig, ax = qml.draw_mpl(test_circuit, level="device")() #, level="device"
     12 plt.show()

File ~\Desktop\TrainingProject\jax-env\lib\site-packages\pennylane\workflow\qnode.py:882, in QNode. __call__ (self, *args, **kwargs)
    879 from ._capture_qnode import capture_qnode # pylint: disable=import-outside-toplevel
    881 return capture_qnode(self, *args, **kwargs)
--> 882 return self._impl_call(*args, **kwargs)

File ~\Desktop\TrainingProject\jax-env\lib\site-packages\pennylane\workflow\qnode.py:855, in QNode._impl_call(self, *args, **kwargs)
    852 # Calculate the classical jacobians if necessary
    853 self._transform_program.set_classical_component(self, args, kwargs)
--> 855 res = qml.execute(
    856 (tape,),
    857 device=self.device,
    858 diff_method=self.diff_method,
    859 interface=self.interface,
    860 transform_program=self._transform_program,
    861 gradient_kwargs=self.gradient_kwargs,
    862 **self.execute_kwargs,
    863 )
    864 res = res[0]
    866 # convert result to the interface in case the qfunc has no parameters

File ~\Desktop\TrainingProject\jax-env\lib\site-packages\pennylane\workflow\execution.py:239, in execute(tapes, device, diff_method, interface, transform_program, grad_on_execution, cache, cachesize, max_diff, device_vjp, postselect_mode, mcm_method, gradient_kwargs, mcm_config, config, inner_transform)
    234 transform_program, inner_transform = _setup_transform_program(
    235 transform_program, device, config, cache, cachesize
    236 )
    238 #### Executing the configured setup #####
--> 239 tapes, post_processing = transform_program(tapes)
    241 if transform_program.is_informative:
    242 return post_processing(tapes)

File ~\Desktop\TrainingProject\jax-env\lib\site-packages\pennylane\transforms\core\transform_program.py:643, in TransformProgram. __call__ (self, *args, **kwargs)
    641 if type(args[0]). __name__ == "Jaxpr":
    642 return self.__call_jaxpr(*args, **kwargs)
--> 643 return self.__call_tapes(*args, **kwargs)

File ~\Desktop\TrainingProject\jax-env\lib\site-packages\pennylane\transforms\core\transform_program.py:580, in TransformProgram.__call_tapes(self, tapes)
    578 if argnums is not None:
    579 tape.trainable_params = argnums[j]
--> 580 new_tapes, fn = transform(tape, *targs, **tkwargs)
    581 execution_tapes.extend(new_tapes)
    583 fns.append(fn)

File ~\Desktop\TrainingProject\jax-env\lib\site-packages\pennylane\devices\preprocess.py:401, in decompose(tape, stopping_condition, stopping_condition_shots, skip_initial_state_prep, decomposer, name, error)
    398 return (tape,), null_postprocessing
    399 try:
--> 401 new_ops = [
    402 final_op
    403 for op in tape.operations[len(prep_op) :]
    404 for final_op in _operator_decomposition_gen(
    405 op,
    406 stopping_condition,
    407 decomposer=decomposer,
    408 name=name,
    409 error=error,
    410 )
    411 ]
    412 except RecursionError as e:
    413 raise error(
    414 "Reached recursion limit trying to decompose operations. "
    415 "Operator decomposition may have entered an infinite loop."
    416 ) from e

File ~\Desktop\TrainingProject\jax-env\lib\site-packages\pennylane\devices\preprocess.py:401, in <listcomp>(.0)
    398 return (tape,), null_postprocessing
    399 try:
--> 401 new_ops = [
    402 final_op
    403 for op in tape.operations[len(prep_op) :]
    404 for final_op in _operator_decomposition_gen(
    405 op,
    406 stopping_condition,
    407 decomposer=decomposer,
    408 name=name,
    409 error=error,
    410 )
    411 ]
    412 except RecursionError as e:
    413 raise error(
    414 "Reached recursion limit trying to decompose operations. "
    415 "Operator decomposition may have entered an infinite loop."
    416 ) from e

File ~\Desktop\TrainingProject\jax-env\lib\site-packages\pennylane\devices\preprocess.py:72, in _operator_decomposition_gen(op, acceptance_function, decomposer, max_expansion, current_depth, name, error)
     67 raise error(
     68 f"Operator {op} not supported with {name} and does not provide a decomposition."
     69 ) from e
     71 for sub_op in decomp:
---> 72 yield from _operator_decomposition_gen(
     73 sub_op,
     74 acceptance_function,
     75 decomposer=decomposer,
     76 max_expansion=max_expansion,
     77 current_depth=current_depth,
     78 name=name,
     79 error=error,
     80 )

File ~\Desktop\TrainingProject\jax-env\lib\site-packages\pennylane\devices\preprocess.py:60, in _operator_decomposition_gen(op, acceptance_function, decomposer, max_expansion, current_depth, name, error)
     58 if max_expansion is not None and max_expansion <= current_depth:
     59 max_depth_reached = True
---> 60 if acceptance_function(op) or max_depth_reached:
     61 yield op
     62 else:

File ~\Desktop\TrainingProject\jax-env\lib\site-packages\pennylane\devices\default_qubit.py:61, in stopping_condition(op)
     59 def stopping_condition(op: qml.operation.Operator) -> bool:
     60 """Specify whether or not an Operator object is supported by the device."""
---> 61 if op.name == "QFT" and len(op.wires) >= 6:
     62 return False
     63 if op.name == "GroverOperator" and len(op.wires) >= 13:

AttributeError: 'list' object has no attribute 'name'

```

If I instead remove the ctrl, everything works as expected.  
I am well aware, that for this simple example I do not need to include the control in the operator itself, but in my more complicated-quite-nested circuit it would be very helpful.

**Is there a way how to include ctrl in an Operation-definition correctly?**

Many thanks and best regards,  
Pia

---

<div class="post-metadata">

**Author:** ![CatalinaAlbornoz](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/catalinaalbornoz/32/1196_2.png) [@CatalinaAlbornoz](https://discuss.pennylane.ai/u/CatalinaAlbornoz)\
**Post date:** [November 29, 2025, 12:36am UTC](https://discuss.pennylane.ai/t/defining-operations-including-qml-ctrl-raises-problems/9126/2 "2025-11-29T00:36:39Z")

</div>

Hi @Pia ,

Thank you for bringing this up. I think you may have uncovered a hidden bug. I’ve forwarded it to our team, so I hope we can find a workaround that we can share. I’ll let you know what we find next week.

---

<div class="post-metadata">

**Author:** ![christina](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/christina/32/583_2.png) [@christina](https://discuss.pennylane.ai/u/christina)\
**Post date:** [December 1, 2025, 2:25pm UTC](https://discuss.pennylane.ai/t/defining-operations-including-qml-ctrl-raises-problems/9126/3 "2025-12-01T14:25:13Z")

</div>

Thanks for asking this @Pia ,

We have two ways of creating controlled operations in pennylane.

1. `ctrl(op, control_wires)`: which takes in an operation and returns a controlled operation
2. `ctrl(qfunc, control_wires)(*args, **kwargs)`, which takes a quantum function and returns a _quantum function_ that returns nothing.

The quantum function just “queues” operations, instead of actually returning them. But for decompositions, we take the actual list output of the method and don’t care about the queueing behaviour.

If you switch to using the first method, where an operator goes and and an operator goes out, you should be able to get the decomposition to work.

```auto
op = qml.H(wires=n_wires_1[0])
return return [qml.ctrl(op, control = n_aux_1, control_values=[0])]

```

---

<div class="post-metadata">

**Author:** ![Pia](https://avatars.discourse-cdn.com/v4/letter/p/6a8cbe/32.png) [@Pia](https://discuss.pennylane.ai/u/Pia)\
**Post date:** [December 2, 2025, 3:30pm UTC](https://discuss.pennylane.ai/t/defining-operations-including-qml-ctrl-raises-problems/9126/4 "2025-12-02T15:30:44Z")

</div>

I see 🙂 thank you!

> [@christina](#):
>
> ```auto
> op = qml.H(wires=n_wires_1[0])
> return return [qml.ctrl(op, control = n_aux_1, control_values=[0])]
> 
> ```

That works! Thanks a lot 🙂
