Repository: PennyLaneAI/pennylane
Pull request #3282 (2022-11-07 22:29:33)

Remove components ops from queue instead of updating metadata
PennyLane has a variety of nested operations, such as `Tensor`, `Hamiltonian`, `SymbolicOp`, and `CompositeOp`, as well as functions that create new operations out of old operations, such as `map_wires` and `simplify`.  

Currently, we mark the original op as "owned" and remove it later during a processing step:

```
>>> with qml.tape.QuantumTape() as tape:
...     op = qml.adjoint(qml.PauliX(0), lazy=True)
>>> tape.get_info(op.base)
{'owner': Adjoint(PauliX(wires=[0]))}
>>> tape.operations
[Adjoint(PauliX(wires=[0]))]
```

Because of the metadata, the original operation is not processed into the final circuit object during `qml.tape.QuantumTape._process_queue`. 

This PR explores immediately removing nested operations from the queue instead of merely marking them for later removal. Under this PR,

```
>>> with qml.queuing.AnnotatedQueue() as q:
...     op = qml.adjoint(qml.PauliX(0), lazy=True)
>>> q.queue
[Adjoint(PauliX(wires=[0]))]
```

This new behaviour seems more straightforward and leads to a cleaner queue. None of this behaviour should be exposed to users and it is mostly hidden from developers.

It would be a breaking change from accepted practice and may have unforeseen consequences.


Patch:
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 7e78f75b4..7e9bc0d74 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -147,6 +147,10 @@
 * The tape constructed by a QNode is no longer queued to surrounding contexts.
   [(#3509)](https://github.com/PennyLaneAI/pennylane/pull/3509)
 
+* Nested operators like `Tensor`, `Hamiltonian` and `Adjoint` now remove their owned operators
+  from the queue instead of updating their metadata to have an `"owner"`.
+  [(#3282)](https://github.com/PennyLaneAI/pennylane/pull/3282)
+
 <h3>Deprecations</h3>
 
 <h3>Documentation</h3>
diff --git a/pennylane/measurements/classical_shadow.py b/pennylane/measurements/classical_shadow.py
index f53e87ef5..ad16945b6 100644
--- a/pennylane/measurements/classical_shadow.py
+++ b/pennylane/measurements/classical_shadow.py
@@ -386,8 +386,8 @@ class ShadowExpvalMP(MeasurementTransform):
         the observable is not queued"""
         Hs = self.H if isinstance(self.H, Iterable) else [self.H]
         for H in Hs:
-            context.update_info(H, owner=self)
-        context.append(self, owns=Hs)
+            context.remove(H)
+        context.append(self)
 
         return self
 
diff --git a/pennylane/measurements/measurements.py b/pennylane/measurements/measurements.py
index 53473800b..7fe55b893 100644
--- a/pennylane/measurements/measurements.py
+++ b/pennylane/measurements/measurements.py
@@ -415,10 +415,8 @@ class MeasurementProcess(ABC):
     def queue(self, context=qml.QueuingManager):
         """Append the measurement process to an annotated queue."""
         if self.obs is not None:
-            context.update_info(self.obs, owner=self)
-            context.append(self, owns=self.obs)
-        else:
-            context.append(self)
+            context.remove(self.obs)
+        context.append(self)
 
         return self
 
diff --git a/pennylane/operation.py b/pennylane/operation.py
index 0f511c5bd..8187144c2 100644
--- a/pennylane/operation.py
+++ b/pennylane/operation.py
@@ -1883,9 +1883,9 @@ class Tensor(Observable):
                 else:
                     raise ValueError("Can only perform tensor products between observables.")
 
-            context.update_info(o, owner=self)
+            context.remove(o)
 
-        context.append(self, owns=tuple(constituents))
+        context.append(self)
         return self
 
     def __copy__(self):
@@ -1998,16 +1998,14 @@ class Tensor(Observable):
         if QueuingManager.recording() and self not in QueuingManager.active_context():
             QueuingManager.append(self)
 
-        QueuingManager.update_info(self, owns=tuple(self.obs))
-        QueuingManager.update_info(other, owner=self)
+        QueuingManager.remove(other)
 
         return self
 
     def __rmatmul__(self, other):
         if isinstance(other, Observable):
             self.obs[:0] = [other]
-            QueuingManager.update_info(self, owns=tuple(self.obs))
-            QueuingManager.update_info(other, owner=self)
+            QueuingManager.remove(other)
             return self
 
         raise ValueError("Can only perform tensor products between observables.")
@@ -2261,6 +2259,9 @@ class Tensor(Observable):
         Returns:
             ~.Observable: the pruned tensor product of observables
         """
+        if qml.QueuingManager.recording():
+            qml.QueuingManager.remove(self)
+
         if len(self.non_identity_obs) == 0:
             # Return a single Identity as the tensor only contains Identities
             obs = qml.Identity(self.wires[0])
diff --git a/pennylane/ops/functions/map_wires.py b/pennylane/ops/functions/map_wires.py
index 9ef4633cb..67c0e9041 100644
--- a/pennylane/ops/functions/map_wires.py
+++ b/pennylane/ops/functions/map_wires.py
@@ -92,7 +92,7 @@ def map_wires(
             with QueuingManager.stop_recording():
                 new_op = input.map_wires(wire_map=wire_map)
             if replace:
-                QueuingManager.update_info(input, owner=new_op)
+                QueuingManager.remove(input)
             if queue:
                 qml.apply(new_op)
             return new_op
diff --git a/pennylane/ops/functions/simplify.py b/pennylane/ops/functions/simplify.py
index f99a1f624..dff9ea793 100644
--- a/pennylane/ops/functions/simplify.py
+++ b/pennylane/ops/functions/simplify.py
@@ -85,7 +85,7 @@ def simplify(input: Union[Operator, MeasurementProcess, QuantumTape, QNode, Call
         if QueuingManager.recording():
             with QueuingManager.stop_recording():
                 new_op = copy(input.simplify())
-            QueuingManager.update_info(input, owner=new_op)
+            QueuingManager.remove(input)
             return qml.apply(new_op)
         return input.simplify()
 
diff --git a/pennylane/ops/op_math/adjoint.py b/pennylane/ops/op_math/adjoint.py
index 671671ede..2c98db838 100644
--- a/pennylane/ops/op_math/adjoint.py
+++ b/pennylane/ops/op_math/adjoint.py
@@ -129,8 +129,8 @@ def _single_op_eager(op, update_queue=False):
     if op.has_adjoint:
         adj = op.adjoint()
         if update_queue:
-            QueuingManager.update_info(op, owner=adj)
-            QueuingManager.append(adj, owns=op)
+            QueuingManager.remove(op)
+            QueuingManager.append(adj)
         return adj
     return Adjoint(op)
 
diff --git a/pennylane/ops/op_math/composite.py b/pennylane/ops/op_math/composite.py
index fd1541e4a..1dd54c7b3 100644
--- a/pennylane/ops/op_math/composite.py
+++ b/pennylane/ops/op_math/composite.py
@@ -294,8 +294,8 @@ class CompositeOp(Operator):
         """Updates each operator's owner to self, this ensures
         that the operators are not applied to the circuit repeatedly."""
         for op in self:
-            context.update_info(op, owner=self)
-        context.append(self, owns=self.operands)
+            context.remove(op)
+        context.append(self)
         return self
 
     @classmethod
diff --git a/pennylane/ops/op_math/pow.py b/pennylane/ops/op_math/pow.py
index 07a04f2e3..7a62a504c 100644
--- a/pennylane/ops/op_math/pow.py
+++ b/pennylane/ops/op_math/pow.py
@@ -81,16 +81,14 @@ def pow(base, z=1, lazy=True, do_queue=True, id=None):
 
     num_ops = len(pow_ops)
     if num_ops == 0:
-        # needs to be identity (not prod of identities) so device knows to skip
-        pow_op = qml.Identity(base.wires[0], id=id)
+        pow_op = qml.Identity(base.wires, id=id)
     elif num_ops == 1:
         pow_op = pow_ops[0]
     else:
         pow_op = qml.prod(*pow_ops)
 
     if do_queue:
-        QueuingManager.update_info(base, owner=pow_op)
-        QueuingManager.update_info(pow_op, owns=base)
+        QueuingManager.remove(base)
 
     return pow_op
 
diff --git a/pennylane/ops/op_math/prod.py b/pennylane/ops/op_math/prod.py
index e87e07393..368464cea 100644
--- a/pennylane/ops/op_math/prod.py
+++ b/pennylane/ops/op_math/prod.py
@@ -81,8 +81,7 @@ def prod(*ops, do_queue=True, id=None, lazy=True):
 
     if do_queue:
         for op in ops:
-            QueuingManager.update_info(op, owner=ops_simp)
-        QueuingManager.update_info(ops_simp, owns=ops)
+            QueuingManager.remove(op)
 
     return ops_simp
 
diff --git a/pennylane/ops/op_math/sprod.py b/pennylane/ops/op_math/sprod.py
index 169740170..af683d04c 100644
--- a/pennylane/ops/op_math/sprod.py
+++ b/pennylane/ops/op_math/sprod.py
@@ -63,8 +63,7 @@ def s_prod(scalar, operator, lazy=True, do_queue=True, id=None):
     sprod_op = SProd(scalar=scalar * operator.scalar, base=operator.base, do_queue=do_queue, id=id)
 
     if do_queue:
-        QueuingManager.update_info(operator, owner=sprod_op)
-        QueuingManager.update_info(sprod_op, owns=operator)
+        QueuingManager.remove(operator)
 
     return sprod_op
 
diff --git a/pennylane/ops/op_math/sum.py b/pennylane/ops/op_math/sum.py
index f3e596b37..0229c9048 100644
--- a/pennylane/ops/op_math/sum.py
+++ b/pennylane/ops/op_math/sum.py
@@ -67,8 +67,7 @@ def op_sum(*summands, do_queue=True, id=None, lazy=True):
 
     if do_queue:
         for op in summands:
-            QueuingManager.update_info(op, owner=summands_simp)
-        QueuingManager.update_info(summands_simp, owns=summands)
+            QueuingManager.remove(op)
 
     return summands_simp
 
diff --git a/pennylane/ops/op_math/symbolicop.py b/pennylane/ops/op_math/symbolicop.py
index e1b6565ea..0c38c7462 100644
--- a/pennylane/ops/op_math/symbolicop.py
+++ b/pennylane/ops/op_math/symbolicop.py
@@ -111,8 +111,8 @@ class SymbolicOp(Operator):
         return self.base._queue_category  # pylint: disable=protected-access
 
     def queue(self, context=QueuingManager):
-        context.update_info(self.base, owner=self)
-        context.append(self, owns=self.base)
+        context.remove(self.base)
+        context.append(self)
         return self
 
     @property
diff --git a/pennylane/ops/qubit/hamiltonian.py b/pennylane/ops/qubit/hamiltonian.py
index c95a6544f..21c52527e 100644
--- a/pennylane/ops/qubit/hamiltonian.py
+++ b/pennylane/ops/qubit/hamiltonian.py
@@ -645,8 +645,8 @@ class Hamiltonian(Observable):
     def queue(self, context=qml.QueuingManager):
         """Queues a qml.Hamiltonian instance"""
         for o in self.ops:
-            context.update_info(o, owner=self)
-        context.append(self, owns=tuple(self.ops))
+            context.remove(o)
+        context.append(self)
         return self
 
     def map_wires(self, wire_map: dict):
diff --git a/pennylane/qchem/tapering.py b/pennylane/qchem/tapering.py
index f372c40b6..96202b416 100644
--- a/pennylane/qchem/tapering.py
+++ b/pennylane/qchem/tapering.py
@@ -765,7 +765,7 @@ def taper_operation(
         r"""Applies the tapered operation for the specified parameter value whenever
         queing context is active, otherwise returns it as a list."""
         if qml.QueuingManager.recording():
-            qml.QueuingManager.update_info(operation, owner=gen_tapered)
+            qml.QueuingManager.remove(operation)
             for coeff, op in zip(*gen_tapered.terms()):
                 qml.exp(op, 1j * params * coeff)
         else:
diff --git a/pennylane/queuing.py b/pennylane/queuing.py
index 5a90c8c7c..56eb2b582 100644
--- a/pennylane/queuing.py
+++ b/pennylane/queuing.py
@@ -118,22 +118,17 @@ The function queues a copy of the original object if it already in the queue.
 >>> q.queue[0] is q.queue[1]
 False
 
-In the case of operators composed of other operators, each operator in the composite will be queued.
-For example, both ``PauliX`` and ``PauliX`` raised to a power.
+In the case of operators composed of other operators, like with :class:`~.SymbolicOp` and
+:class:`~.CompositeOp`, the new nested operation removes its constituents from the queue.
+Only the operators that will end up in the circuit will remain.
 
 >>> with qml.queuing.AnnotatedQueue() as q:
 ...     base = qml.PauliX(0)
+...     print(q.queue)
 ...     pow_op = base ** 1.5
->>> q.queue
-[PauliX(wires=[0]), PauliX(wires=[0])**1.5]
-
-In this case, each object will have metadata associated with it. At later processing steps, the original
-``PauliX`` will be ignored in favor of the operator that *owns* it.
-
->>> q.get_info(base)
-{'owner': PauliX(wires=[0])**1.5}
->>> q.get_info(base)["owner"] is pow_op
-True
+...     print(q.queue)
+[PauliX(wires=[0])]
+[PauliX(wires=[0])**1.5]
 
 Once the queue is constructed, the :func:`~.process_queue` function converts it into the operations,
 measurements, and state prep present in the final circuit. This step eliminates any object that has an owner.
@@ -359,8 +354,9 @@ class AnnotatedQueue(OrderedDict):
         self[obj] = kwargs
 
     def remove(self, obj):
-        """Remove ``obj`` from the queue.  Raises ``KeyError`` if ``obj`` is not already in the queue."""
-        del self[obj]
+        """Remove ``obj`` from the queue. Passes silently if the object is not in the queue."""
+        if obj in self:
+            del self[obj]
 
     def update_info(self, obj, **kwargs):
         """Update ``obj``'s metadata with ``kwargs`` if it exists in the queue."""
diff --git a/pennylane/templates/subroutines/qpe.py b/pennylane/templates/subroutines/qpe.py
index 748af272a..468483e5c 100644
--- a/pennylane/templates/subroutines/qpe.py
+++ b/pennylane/templates/subroutines/qpe.py
@@ -208,8 +208,8 @@ class QuantumPhaseEstimation(Operation):
         return new_op
 
     def queue(self, context=QueuingManager):
-        context.update_info(self._hyperparameters["unitary"], owner=self)
-        context.append(self, owns=self._hyperparameters["unitary"])
+        context.remove(self._hyperparameters["unitary"])
+        context.append(self)
         return self
 
     @staticmethod


Test patch:
diff --git a/tests/measurements/test_measurements.py b/tests/measurements/test_measurements.py
index 6973ba215..5c9600c87 100644
--- a/tests/measurements/test_measurements.py
+++ b/tests/measurements/test_measurements.py
@@ -127,14 +127,11 @@ class TestStatisticsQueuing:
             A = op(0)
             stat_func(A)
 
-        assert q.queue[:-1] == [A]
-        meas_proc = q.queue[-1]
+        assert len(q.queue) == 1
+        meas_proc = q.queue[0]
         assert isinstance(meas_proc, MeasurementProcess)
         assert meas_proc.return_type == return_type
 
-        assert q.get_info(A) == {"owner": meas_proc}
-        assert q.get_info(meas_proc) == {"owns": (A)}
-
     def test_annotating_tensor_hermitian(self, stat_func, return_type):
         """Test that the return_type related info is updated for a measurement
         when called for an Hermitian observable"""
@@ -145,14 +142,11 @@ class TestStatisticsQueuing:
             Herm = qml.Hermitian(mx, wires=[1])
             stat_func(Herm)
 
-        assert q.queue[:-1] == [Herm]
-        meas_proc = q.queue[-1]
+        assert len(q.queue) == 1
+        meas_proc = q.queue[0]
         assert isinstance(meas_proc, MeasurementProcess)
         assert meas_proc.return_type == return_type
 
-        assert q.get_info(Herm) == {"owner": meas_proc}
-        assert q.get_info(meas_proc) == {"owns": (Herm)}
-
     @pytest.mark.parametrize(
         "op1,op2",
         [
@@ -171,15 +165,11 @@ class TestStatisticsQueuing:
             tensor_op = A @ B
             stat_func(tensor_op)
 
-        assert q.queue[:-1] == [A, B, tensor_op]
-        meas_proc = q.queue[-1]
+        assert len(q.queue) == 1
+        meas_proc = q.queue[0]
         assert isinstance(meas_proc, MeasurementProcess)
         assert meas_proc.return_type == return_type
 
-        assert q.get_info(A) == {"owner": tensor_op}
-        assert q.get_info(B) == {"owner": tensor_op}
-        assert q.get_info(tensor_op) == {"owns": (A, B), "owner": meas_proc}
-
     @pytest.mark.parametrize(
         "op1,op2",
         [
@@ -199,15 +189,12 @@ class TestStatisticsQueuing:
             tensor_op = A @ B
             stat_func(tensor_op)
 
-        assert len(q) == 2
+        assert len(q.queue) == 1
 
-        assert q.queue[0] is tensor_op
-        meas_proc = q.queue[-1]
+        meas_proc = q.queue[0]
         assert isinstance(meas_proc, MeasurementProcess)
         assert meas_proc.return_type == return_type
 
-        assert q.get_info(tensor_op) == {"owns": (A, B), "owner": meas_proc}
-
     def test_not_an_observable(self, stat_func, return_type):
         """Test that a UserWarning is raised if the provided
         argument might not be hermitian."""
diff --git a/tests/ops/functions/test_map_wires.py b/tests/ops/functions/test_map_wires.py
index a2ae67d20..36ce849d7 100644
--- a/tests/ops/functions/test_map_wires.py
+++ b/tests/ops/functions/test_map_wires.py
@@ -81,13 +81,12 @@ class TestMapWiresOperators:
 
     def test_map_wires_with_queuing_and_with_replacing(self):
         """Test the map_wires method while queuing with `queue = True` and `replace=True`."""
-        with qml.queuing.AnnotatedQueue() as q_tape:
+        with qml.queuing.AnnotatedQueue() as q:
             op = build_op()
             m_op = qml.map_wires(op, wire_map=wire_map, queue=True, replace=True)
-        tape = QuantumScript.from_queue(q_tape)
-        assert len(tape.circuit) == 1
-        assert tape.circuit[0] is m_op
-        assert q_tape.get_info(op).get("owner", None) is m_op
+
+        assert len(q) == 1
+        assert q.queue[0] is m_op
 
     def test_map_wires_unsupported_object_raises_error(self):
         """Test that an error is raised when trying to map the wires of an unsupported object."""
diff --git a/tests/ops/functions/test_simplify.py b/tests/ops/functions/test_simplify.py
index ec0832510..d5fe33173 100644
--- a/tests/ops/functions/test_simplify.py
+++ b/tests/ops/functions/test_simplify.py
@@ -58,13 +58,12 @@ class TestSimplifyOperators:
 
     def test_simplify_method_with_queuing(self):
         """Test the simplify method while queuing."""
-        with qml.queuing.AnnotatedQueue() as q_tape:
+        with qml.queuing.AnnotatedQueue() as q:
             op = build_op()
             s_op = qml.simplify(op)
-        tape = QuantumScript.from_queue(q_tape)
-        assert len(tape.circuit) == 1
-        assert tape.circuit[0] is s_op
-        assert q_tape.get_info(op)["owner"] is s_op
+
+        assert len(q) == 1
+        assert q.queue[0] is s_op
 
     def test_simplify_unsupported_object_raises_error(self):
         """Test that an error is raised when trying to simplify an unsupported object."""
diff --git a/tests/ops/op_math/test_adjoint.py b/tests/ops/op_math/test_adjoint.py
index 0b59701b8..1a9075b15 100644
--- a/tests/ops/op_math/test_adjoint.py
+++ b/tests/ops/op_math/test_adjoint.py
@@ -568,22 +568,18 @@ class TestQueueing:
             base = qml.Rot(1.2345, 2.3456, 3.4567, wires="b")
             op = Adjoint(base)
 
-        tape = qml.tape.QuantumScript.from_queue(q)
-        assert q.get_info(base)["owner"] is op
-        assert q.get_info(op)["owns"] is base
-        assert tape.operations == [op]
+        assert base not in q.queue
+        assert len(q) == 1
 
-    def test_queueing_base_defined_outside(self):
+    def test_queuing_base_defined_outside(self):
         """Test that base isn't added to queue if it's defined outside the recording context."""
 
         base = qml.Rot(1.2345, 2.3456, 3.4567, wires="b")
         with qml.queuing.AnnotatedQueue() as q:
             op = Adjoint(base)
 
-        tape = qml.tape.QuantumScript.from_queue(q)
-        assert len(tape) == 1
-        assert q.get_info(op)["owns"] is base
-        assert tape.operations == [op]
+        assert len(q) == 1
+        assert q.queue[0] == op
 
     def test_do_queue_False(self):
         """Test that when `do_queue` is specified, the operation is not queued."""
@@ -591,8 +587,7 @@ class TestQueueing:
         with qml.queuing.AnnotatedQueue() as q:
             op = Adjoint(base, do_queue=False)
 
-        tape = qml.tape.QuantumScript.from_queue(q)
-        assert len(tape) == 0
+        assert len(q) == 0
 
 
 class TestMatrix:
@@ -856,13 +851,8 @@ class TestAdjointConstructorPreconstructedOp:
             base.queue()
             out = adjoint(base)
 
-        tape = qml.tape.QuantumScript.from_queue(q)
-        assert isinstance(out, Adjoint)
-        assert out.base is base
-        assert len(tape) == 1
-        assert len(q) == 2
-        assert q.get_info(base) == {"owner": out}
-        assert q.get_info(out) == {"owns": base}
+        assert len(q) == 1
+        assert q.queue[0] is out
 
     def test_single_op_defined_outside_queue_eager(self):
         """Test if base is defined outside context and the function eagerly simplifies
@@ -871,14 +861,8 @@ class TestAdjointConstructorPreconstructedOp:
         with qml.queuing.AnnotatedQueue() as q:
             out = adjoint(base, lazy=False)
 
-        tape = qml.tape.QuantumScript.from_queue(q)
-        assert isinstance(out, qml.RX)
-        assert out.data == [-1.2]
-        assert len(tape) == 1
-        assert tape[0] is out
-
         assert len(q) == 1
-        assert q.get_info(out) == {"owns": base}
+        assert q.queue[0] is out
 
     def test_single_observable(self):
         """Test passing a single preconstructed observable in a queuing context."""
@@ -887,11 +871,14 @@ class TestAdjointConstructorPreconstructedOp:
             base = qml.PauliX(0) @ qml.PauliY(1)
             out = adjoint(base)
 
-        tape = qml.tape.QuantumScript.from_queue(q)
-        assert len(tape) == 0
+        assert len(q) == 1
+        assert q.queue[0] is out
         assert out.base is base
         assert isinstance(out, Adjoint)
 
+        qs = qml.tape.QuantumScript.from_queue(q)
+        assert len(qs) == 0
+
 
 class TestAdjointConstructorDifferentCallableTypes:
     """Test the adjoint transform on a variety of possible inputs."""
@@ -978,11 +965,8 @@ class TestAdjointConstructorNonLazyExecution:
             base = qml.RX(x, wires="b")
             out = adjoint(base, lazy=False)
 
-        tape = qml.tape.QuantumScript.from_queue(q)
-        assert len(tape) == 1
-        assert out is tape[0]
-        assert q.get_info(base) == {"owner": out}
-        assert q.get_info(out) == {"owns": base}
+        assert len(q) == 1
+        assert q.queue[0] is out
 
         assert isinstance(out, qml.RX)
         assert out.data == [-1.23]
@@ -993,12 +977,8 @@ class TestAdjointConstructorNonLazyExecution:
             base = qml.S(0)
             out = adjoint(base, lazy=False)
 
-        tape = qml.tape.QuantumScript.from_queue(q)
-        assert len(tape) == 1
-        assert out is tape[0]
-        assert len(q) == 2
-        assert q.get_info(base) == {"owner": out}
-        assert q.get_info(out) == {"owns": base}
+        assert len(q) == 1
+        assert q.queue[0] is out
 
         assert isinstance(out, Adjoint)
         assert isinstance(out.base, qml.S)
diff --git a/tests/ops/op_math/test_controlled.py b/tests/ops/op_math/test_controlled.py
index 5b219fccf..2ffece6f3 100644
--- a/tests/ops/op_math/test_controlled.py
+++ b/tests/ops/op_math/test_controlled.py
@@ -606,10 +606,8 @@ class TestQueuing:
             base = qml.Rot(1.234, 2.345, 3.456, wires=2)
             op = Controlled(base, (0, 1))
 
-        tape = qml.tape.QuantumScript.from_queue(q)
-        assert q.get_info(base)["owner"] is op
-        assert q.get_info(op)["owns"] is base
-        assert tape.operations == [op]
+        assert base not in q
+        assert q.queue[0] == op
 
     def test_queuing_base_defined_outside(self):
         """Test that base isn't added to queue if its defined outside the recording context."""
@@ -618,10 +616,8 @@ class TestQueuing:
         with qml.queuing.AnnotatedQueue() as q:
             op = Controlled(base, ("a", "b"))
 
-        tape = qml.tape.QuantumScript.from_queue(q)
         assert len(q) == 1
-        assert q.get_info(op)["owns"] is base
-        assert tape.operations == [op]
+        assert q.queue[0] is op
 
     def test_do_queue_false(self):
         """Test that when `do_queue=False` is specified, the controlled op is not queued."""
diff --git a/tests/ops/op_math/test_pow_op.py b/tests/ops/op_math/test_pow_op.py
index 34e87d50a..3b17a2c2d 100644
--- a/tests/ops/op_math/test_pow_op.py
+++ b/tests/ops/op_math/test_pow_op.py
@@ -59,8 +59,8 @@ class TestConstructor:
         """Test that nonlazy pow returns a single identity if the power decomposes
         to the identity."""
 
-        op = qml.pow(op, 2, lazy=False)
-        assert qml.equal(op, qml.Identity(0))
+        op_new = qml.pow(op, 2, lazy=False)
+        assert qml.equal(op_new, qml.Identity(op.wires))
 
     def test_simplification_multiple_ops(self):
         """Test that when the simplification method returns a list of multiple operators,
@@ -85,8 +85,7 @@ class TestConstructor:
             original_op = qml.PauliX(0)
             new_op = qml.pow(original_op, 0.5, lazy=False)
 
-        assert q[original_op]["owner"] is new_op
-        assert q[new_op]["owns"] is original_op
+        assert original_op not in q.queue
 
 
 @pytest.mark.parametrize("power_method", [Pow, pow_using_dunder_method, qml.pow])
@@ -561,10 +560,8 @@ class TestQueueing:
             base = qml.Rot(1.2345, 2.3456, 3.4567, wires="b")
             op = Pow(base, 1.2)
 
-        tape = qml.tape.QuantumScript.from_queue(q)
-        assert q.get_info(base)["owner"] is op
-        assert q.get_info(op)["owns"] is base
-        assert tape.operations == [op]
+        assert q.queue[0] is op
+        assert len(q) == 1
 
     def test_queueing_base_defined_outside(self):
         """Test that base is added to queue even if it's defined outside the recording context."""
@@ -573,10 +570,8 @@ class TestQueueing:
         with qml.queuing.AnnotatedQueue() as q:
             op = Pow(base, 3.4)
 
-        tape = qml.tape.QuantumScript.from_queue(q)
-        assert len(q.queue) == 1
-        assert q.get_info(op)["owns"] is base
-        assert tape.operations == [op]
+        assert len(q) == 1
+        assert q.queue[0] is op
 
     def test_do_queue_False(self):
         """Test that when `do_queue` is specified, the operation is not queued."""
@@ -584,8 +579,7 @@ class TestQueueing:
         with qml.queuing.AnnotatedQueue() as q:
             op = Pow(base, 4.5, do_queue=False)
 
-        tape = qml.tape.QuantumScript.from_queue(q)
-        assert len(tape) == 0
+        assert len(q) == 0
 
 
 class TestMatrix:
diff --git a/tests/ops/op_math/test_prod.py b/tests/ops/op_math/test_prod.py
index cba54421e..ae83d222a 100644
--- a/tests/ops/op_math/test_prod.py
+++ b/tests/ops/op_math/test_prod.py
@@ -1047,8 +1047,8 @@ class TestWrapperFunc:
             prod1 = prod(qml.S(1), qml.T(1))
             prod2 = prod(qml.S(0), prod1, lazy=False)
 
-        assert q[prod1]["owner"] == prod2
-        assert prod1 in q[prod2]["owns"]
+        assert len(q) == 1
+        assert q.queue[0] is prod2
 
 
 class TestIntegration:
diff --git a/tests/ops/op_math/test_sprod.py b/tests/ops/op_math/test_sprod.py
index 9107fb0de..e26335930 100644
--- a/tests/ops/op_math/test_sprod.py
+++ b/tests/ops/op_math/test_sprod.py
@@ -668,8 +668,8 @@ class TestWrapperFunc:
             sprod1 = s_prod(4, qml.PauliX(0))
             sprod2 = s_prod(3, sprod1, lazy=False)
 
-        assert q[sprod1]["owner"] is sprod2
-        assert q[sprod2]["owns"] is sprod1
+        assert len(q) == 1
+        assert q.queue[0] is sprod2
 
 
 class TestIntegration:
diff --git a/tests/ops/op_math/test_sum.py b/tests/ops/op_math/test_sum.py
index c032bf8bc..450332e6b 100644
--- a/tests/ops/op_math/test_sum.py
+++ b/tests/ops/op_math/test_sum.py
@@ -856,8 +856,8 @@ class TestWrapperFunc:
             sum1 = op_sum(qml.S(1), qml.T(1))
             sum2 = op_sum(qml.S(0), sum1, lazy=False)
 
-        assert q[sum1]["owner"] is sum2
-        assert sum1 in q[sum2]["owns"]
+        assert len(q) == 1
+        assert q.queue[0] is sum2
 
 
 class TestIntegration:
diff --git a/tests/ops/op_math/test_symbolic_op.py b/tests/ops/op_math/test_symbolic_op.py
index 57cdc45e7..0c15800a9 100644
--- a/tests/ops/op_math/test_symbolic_op.py
+++ b/tests/ops/op_math/test_symbolic_op.py
@@ -177,10 +177,9 @@ class TestQueuing:
             base = TempOperator("a")
             op = SymbolicOp(base)
 
-        tape = qml.tape.QuantumScript.from_queue(q)
-        assert q.get_info(base)["owner"] is op
-        assert q.get_info(op)["owns"] is base
-        assert tape.operations == [op]
+        assert base not in q
+        assert q.queue[0] is op
+        assert len(q) == 1
 
     def test_queuing_base_defined_outside(self):
         """Test symbolic op queues without adding base to the queue if it isn't already in the queue."""
@@ -189,10 +188,8 @@ class TestQueuing:
         with qml.queuing.AnnotatedQueue() as q:
             op = SymbolicOp(base)
 
-        tape = qml.tape.QuantumScript.from_queue(q)
         assert len(q) == 1
-        assert q.get_info(op)["owns"] is base
-        assert tape.operations == [op]
+        assert q.queue[0] is op
 
     def test_do_queue_false(self):
         """Test that queuing can be avoided if `do_queue=False`."""
@@ -201,4 +198,4 @@ class TestQueuing:
         with qml.queuing.AnnotatedQueue() as q:
             op = SymbolicOp(base, do_queue=False)
 
-        assert len(q.queue) == 0
+        assert len(q) == 0
diff --git a/tests/ops/qubit/test_hamiltonian.py b/tests/ops/qubit/test_hamiltonian.py
index 4bae82f5c..81e40ded8 100644
--- a/tests/ops/qubit/test_hamiltonian.py
+++ b/tests/ops/qubit/test_hamiltonian.py
@@ -901,29 +901,15 @@ class TestHamiltonian:
     def test_hamiltonian_queue_inside(self):
         """Tests that Hamiltonian are queued correctly when components are instantiated inside the recording context."""
 
-        queue = [
-            qml.Hadamard(wires=1),
-            qml.PauliX(wires=0),
-            qml.PauliX(1),
-            qml.PauliZ(0),
-            qml.PauliZ(2),
-            qml.PauliZ(0) @ qml.PauliZ(2),
-            qml.PauliZ(1),
-            qml.Hamiltonian(
-                [1, 3, 1], [qml.PauliX(1), qml.PauliZ(0) @ qml.PauliZ(2), qml.PauliZ(1)]
-            ),
-        ]
-
         with qml.queuing.AnnotatedQueue() as q:
-            qml.Hadamard(wires=1)
-            qml.PauliX(wires=0)
-            qml.expval(
+            m = qml.expval(
                 qml.Hamiltonian(
                     [1, 3, 1], [qml.PauliX(1), qml.PauliZ(0) @ qml.PauliZ(2), qml.PauliZ(1)]
                 )
             )
 
-        assert np.all([q1.compare(q2) for q1, q2 in zip(q.queue, queue)])
+        assert len(q.queue) == 1
+        assert q.queue[0] is m
 
     def test_terms(self):
         """Tests that the terms representation is returned correctly."""
diff --git a/tests/qchem/test_tapering.py b/tests/qchem/test_tapering.py
index ae9f67acd..78c3414f6 100644
--- a/tests/qchem/test_tapering.py
+++ b/tests/qchem/test_tapering.py
@@ -765,7 +765,8 @@ def test_consistent_taper_ops(operation, op_gen):
     taper_circuit1 = [x for x in tape1.circuit if x.label() != "I"]
     taper_circuit2 = [x for x in tape2.circuit if x.label() != "I"]
 
-    assert len(taper_op1) == len(taper_circuit1) and len(taper_op2) == len(taper_circuit2)
+    assert len(taper_op1) == len(taper_circuit1)
+    assert len(taper_op2) == len(taper_circuit2)
     assert np.all([qml.equal(op1.base, op2.base) for op1, op2 in zip(taper_circuit1, taper_op1)])
     assert np.all([qml.equal(op1.base, op2.base) for op1, op2 in zip(taper_circuit2, taper_op2)])
 
diff --git a/tests/tape/test_tape.py b/tests/tape/test_tape.py
index 33b29ea62..4e397c1d6 100644
--- a/tests/tape/test_tape.py
+++ b/tests/tape/test_tape.py
@@ -80,7 +80,7 @@ class TestConstruction:
         """Test that qubit quantum operations correctly queue"""
         tape, ops, obs = make_tape
 
-        assert len(tape.queue) == 7
+        assert len(tape.queue) == 6
         assert tape.operations == ops
         assert tape.observables == obs
         assert tape.output_dim == 5
@@ -197,7 +197,7 @@ class TestConstruction:
             C = A @ B
             D = qml.expval(C)
 
-        assert len(tape.queue) == 4
+        assert len(tape.queue) == 1
         assert not tape.operations
         assert tape.measurements == [D]
         assert tape.observables == [C]
@@ -221,7 +221,7 @@ class TestConstruction:
             qml.expval(obs[0])
             obs += [qml.probs(wires=[0, "a"])]
 
-        assert len(tape.queue) == 5
+        assert len(tape.queue) == 4
         assert tape.operations == ops
         assert tape.observables == obs
         assert tape.output_dim == 5
diff --git a/tests/test_operation.py b/tests/test_operation.py
index 9d945f9b0..600791f34 100644
--- a/tests/test_operation.py
+++ b/tests/test_operation.py
@@ -1180,8 +1180,6 @@ class TestTensor:
         assert len(q.queue) == 1
         assert q.queue[0] is T
 
-        assert q[T] == {"owns": (op1, op2)}
-
     def test_queuing(self):
         """Test the queuing of a Tensor object."""
 
@@ -1190,14 +1188,8 @@ class TestTensor:
             op2 = qml.PauliY(1)
             T = Tensor(op1, op2)
 
-        assert len(q.queue) == 3
-        assert q.queue[0] is op1
-        assert q.queue[1] is op2
-        assert q.queue[2] is T
-
-        assert q[op1] == {"owner": T}
-        assert q[op2] == {"owner": T}
-        assert q[T] == {"owns": (op1, op2)}
+        assert len(q) == 1
+        assert q.queue[0] is T
 
     def test_queuing_observable_matmul(self):
         """Test queuing when tensor constructed with matmul."""
@@ -1207,10 +1199,8 @@ class TestTensor:
             op2 = qml.PauliY(1)
             t = op1 @ op2
 
-        assert len(q.queue) == 3
-        assert q[op1] == {"owner": t}
-        assert q[op2] == {"owner": t}
-        assert q[t] == {"owns": (op1, op2)}
+        assert len(q) == 1
+        assert q.queue[0] is t
 
     def test_queuing_tensor_matmul(self):
         """Tests the tensor-specific matmul method updates queuing metadata."""
@@ -1223,8 +1213,8 @@ class TestTensor:
             op3 = qml.PauliZ(2)
             t2 = t @ op3
 
-        assert q[t2] == {"owns": (op1, op2, op3)}
-        assert q[op3] == {"owner": t2}
+        assert len(q) == 1
+        assert q.queue[0] is t2
 
     def test_queuing_tensor_matmul_components_outside(self):
         """Tests the tensor-specific matmul method when components are defined outside the
@@ -1238,9 +1228,8 @@ class TestTensor:
             op3 = qml.PauliZ(2)
             t2 = t1 @ op3
 
-        assert len(q) == 2
-        assert q[op3] == {"owner": t2}
-        assert q[t2] == {"owns": (op1, op2, op3)}
+        assert len(q) == 1
+        assert q.queue[0] is t2
 
     def test_queuing_tensor_rmatmul(self):
         """Tests tensor-specific rmatmul updates queuing metatadata."""
@@ -1255,8 +1244,8 @@ class TestTensor:
 
             t2 = op3 @ t1
 
-        assert q[op3] == {"owner": t2}
-        assert q[t2] == {"owns": (op3, op1, op2)}
+        assert len(q.queue) == 1
+        assert q.queue[0] is t2
 
     def test_name(self):
         """Test that the names of the observables are
@@ -1630,12 +1619,12 @@ class TestTensor:
         assert type(O_pruned) == type(expected)
         assert O_pruned.wires == expected.wires
 
-    def test_prune_while_queueing_return_tensor(self):
+    def test_prune_while_queuing_return_tensor(self):
         """Tests that pruning a tensor to a tensor in a tape context registers
         the pruned tensor as owned by the measurement,
         and turns the original tensor into an orphan without an owner."""
 
-        with qml.queuing.AnnotatedQueue() as ann_queue:
+        with qml.queuing.AnnotatedQueue() as q:
             # we assign operations to variables here so we can compare them below
             a = qml.PauliX(wires=0)
             b = qml.PauliY(wires=1)
@@ -1644,51 +1633,23 @@ class TestTensor:
             T_pruned = T.prune()
             m = qml.expval(T_pruned)
 
-        # the pruned tensor became the owner of Paulis
-        assert ann_queue[a]["owner"] == T_pruned
-        assert ann_queue[b]["owner"] == T_pruned
-
-        # the Identity is still owned by the original Tensor
-        assert ann_queue[c]["owner"] == T
-        # the original tensor still owns all three observables
-        # but is not owned by a measurement
-        assert ann_queue[T]["owns"] == (a, b, c)
-        assert not hasattr(ann_queue[T], "owner")
-
-        # the pruned tensor is owned by the measurement
-        # and owns the two Paulis
-        assert ann_queue[T_pruned]["owner"] == m
-        assert ann_queue[T_pruned]["owns"] == (a, b)
-        assert ann_queue[m]["owns"] == T_pruned
+        assert len(q.queue) == 1
+        assert q.queue[0] is m
 
     def test_prune_while_queueing_return_obs(self):
         """Tests that pruning a tensor to an observable in a tape context registers
         the pruned observable as owned by the measurement,
         and turns the original tensor into an orphan without an owner."""
 
-        with qml.queuing.AnnotatedQueue() as ann_queue:
+        with qml.queuing.AnnotatedQueue() as q:
             a = qml.PauliX(wires=0)
             c = qml.Identity(wires=2)
             T = qml.operation.Tensor(a, c)
             T_pruned = T.prune()
             m = qml.expval(T_pruned)
 
-        # the pruned tensor is the Pauli observable
-        assert T_pruned == a
-        # pruned tensor/Pauli is owned by the measurement
-        # since the entry in the dictionary got updated
-        # when the pruned tensor's owner was memorized
-        assert ann_queue[a]["owner"] == m
-        # the Identity is still owned by the original Tensor
-        assert ann_queue[c]["owner"] == T
-
-        # the original tensor still owns both observables
-        # but is not owned by a measurement
-        assert ann_queue[T]["owns"] == (a, c)
-        assert not hasattr(ann_queue[T], "owner")
-
-        # the measurement owns the Pauli/pruned tensor
-        assert ann_queue[m]["owns"] == T_pruned
+        assert len(q.queue) == 1
+        assert q.queue[0] is m
 
     def test_sparse_matrix_no_wires(self):
         """Tests that the correct sparse matrix representation is used."""
diff --git a/tests/test_queuing.py b/tests/test_queuing.py
index a74240258..03dc25acf 100644
--- a/tests/test_queuing.py
+++ b/tests/test_queuing.py
@@ -158,18 +158,10 @@ class TestAnnotatedQueue:
     """Tests for the annotated queue class"""
 
     def test_remove_not_in_queue(self):
-        """Test that remove fails when the object to be removed is not in the queue."""
+        """Test that remove passes silently if the object is not in the queue"""
 
-        with AnnotatedQueue() as q1:
-            op1 = qml.PauliZ(0)
-            op2 = qml.PauliZ(1)
-            q1.append(op1)
-            q1.append(op2)
-
-        with AnnotatedQueue() as q2:
-            q2.append(op1)
-            with pytest.raises(KeyError):
-                q2.remove(op2)
+        q2 = AnnotatedQueue()
+        q2.remove(qml.PauliX(0))
 
     def test_append_qubit_gates(self):
         """Test that gates are successfully appended to the queue."""
@@ -205,7 +197,7 @@ class TestAnnotatedQueue:
             A = qml.PauliZ(0)
             B = qml.PauliY(1)
             tensor_op = qml.operation.Tensor(A, B)
-        assert q.queue == [A, B, tensor_op]
+        assert q.queue == [tensor_op]
         assert tensor_op.obs == [A, B]
 
     def test_append_tensor_ops_overloaded(self):
@@ -216,7 +208,7 @@ class TestAnnotatedQueue:
             A = qml.PauliZ(0)
             B = qml.PauliY(1)
             tensor_op = A @ B
-        assert q.queue == [A, B, tensor_op]
+        assert q.queue == [tensor_op]
         assert tensor_op.obs == [A, B]
 
     def test_get_info(self):
@@ -287,10 +279,7 @@ class TestAnnotatedQueue:
             B = qml.PauliY(1)
             tensor_op = qml.operation.Tensor(A, B)
 
-        assert q.queue == [A, B, tensor_op]
-        assert q.get_info(A) == {"owner": tensor_op}
-        assert q.get_info(B) == {"owner": tensor_op}
-        assert q.get_info(tensor_op) == {"owns": (A, B)}
+        assert q.queue == [tensor_op]
 
 
 test_observables = [