Repository: PennyLaneAI/pennylane
Pull request #7927 (2025-07-18 22:14:11)

Removing Observable
**Context:**
Remove `Observable`, `AdjointObs`, `AdjointOpObs`, `PowObs`, `PowOpObs`

Note that in the past, the inheritance is organized as:
Observable -> Operator
Operation -> Operator

And in the deprecated `Observable`, the logic for the queue property is such that there's possibility for an `Operator` to be both `Observable` and `Operation`, and once the latter condition is satisfied one immediately has `_queue_category='_ops'`

**Description of the Change:**
Removed:
 - [x] `Observable`
 - [x] `AdjointObs`
 - [x] `AdjointOpObs`
 - [x] `PowObs`
 - [x] `PowOpObs`
 - [x] Non-parametric Ops for qubits: `Hadamard`, `PauliX/Y/Z` become pure `Operation`s now
 - [x] Observables `SparseHamiltonian` and `Projector` has `_queue_category = None`; `BasisStateProjector` has `_ops` instead
 - [x] qutrit observable `GellMann` inherits from `Operator` instead; no more further modification needed since already done before

**Benefits:**
Much more satisfaction with quite less redundancy

**Possible Drawbacks:**
Any other packages within ecosys depending on this class?

**Related GitHub Issues:**
[sc-95838]

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/7927?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 99.68%. Comparing base [(`d4aa27b`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/d4aa27bd0c081afb3e41293fd157c35705067c1c?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) to head [(`f7ad2ba`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/f7ad2ba48336c352c4ae88808da5096ab7a3bc51?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).
> Report is 1 commits behind head on master.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #7927      +/-   ##
==========================================
- Coverage   99.68%   99.68%   -0.01%     
==========================================
  Files         544      543       -1     
  Lines       55471    55432      -39     
==========================================
- Hits        55295    55256      -39     
  Misses        176      176              
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/7927?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>

Patch:
diff --git a/doc/development/deprecations.rst b/doc/development/deprecations.rst
index f4f705ddf..5bf772966 100644
--- a/doc/development/deprecations.rst
+++ b/doc/development/deprecations.rst
@@ -39,27 +39,6 @@ Pending deprecations
   - Deprecated in v0.43
   - Will be removed in v0.44
 
-* ``qml.operation.Observable`` and the accompanying ``Observable.compare`` methods are deprecated. At this point, ``Observable`` only
-  provides a default value of ``is_hermitian=True`` and prevents the object from being processed into a tape. Instead of inheriting from
-  ``Observable``, operator developers should manually set ``is_hermitian = True`` and update the ``queue`` function to stop it from being
-  processed into the circuit.
-
-  .. code-block:: python
-
-      class MyObs(Operator):
-      
-          is_hermitian = True
-
-          def queue(self, context=qml.QueuingManager):
-              return self
-
-  To check if an operator is likely to be hermitian, the ``op.is_hermitian`` property can be checked.
-
-  ``qml.equal`` and ``op1 == op2`` should be used to compare instances instead of ``op1.compare(op2)``.
-
-  - Deprecated in v0.42
-  - Will be removed in v0.43
-
 * The boolean functions provided by ``pennylane.operation`` are deprecated. See below for alternate code to
   use instead.
   These include ``not_tape``, ``has_gen``, ``has_grad_method``,  ``has_multipar``, ``has_nopar``, ``has_unitary_gen``,
@@ -137,6 +116,27 @@ for details on how to port your legacy code to the new system. The following fun
 Completed deprecation cycles
 ----------------------------
 
+* ``qml.operation.Observable`` and the accompanying ``Observable.compare`` methods are deprecated. At this point, ``Observable`` only
+  provides a default value of ``is_hermitian=True`` and prevents the object from being processed into a tape. Instead of inheriting from
+  ``Observable``, operator developers should manually set ``is_hermitian = True`` and update the ``queue`` function to stop it from being
+  processed into the circuit.
+
+  .. code-block:: python
+
+      class MyObs(Operator):
+      
+          is_hermitian = True
+
+          def queue(self, context=qml.QueuingManager):
+              return self
+
+  To check if an operator is likely to be hermitian, the ``op.is_hermitian`` property can be checked.
+
+  ``qml.equal`` and ``op1 == op2`` should be used to compare instances instead of ``op1.compare(op2)``.
+
+  - Deprecated in v0.42
+  - Removed in v0.43
+
 * ``qml.operation.WiresEnum``, ``qml.operation.AllWires``, and ``qml.operation.AnyWires`` are deprecated. If an operation can act
   on any number of wires ``Operator.num_wires = None`` should be used instead. This is the default, and does not need
   to be overridden unless the operator developer wants to validate that the correct number of wires is passed.
diff --git a/doc/introduction/interfaces.rst b/doc/introduction/interfaces.rst
index 257149bd5..fe92907f3 100644
--- a/doc/introduction/interfaces.rst
+++ b/doc/introduction/interfaces.rst
@@ -195,6 +195,7 @@ PennyLane offers a built-in Jax-based implementation compatible with ``jax.jit``
     :nosignatures:
 
     ~pennylane.QNGOptimizerQJIT
+    ~pennylane.MomentumQNGOptimizerQJIT
 
 :html:`</div>`
 
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 147c716bb..295b51150 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -4,6 +4,47 @@
 
 <h3>New features since last release</h3>
 
+* Leveraging quantum just-in-time compilation to optimize parameterized hybrid workflows with the momentum
+  quantum natural gradient optimizer is now possible with the new :class:`~.MomentumQNGOptimizerQJIT` optimizer.
+  [(#7606)](https://github.com/PennyLaneAI/pennylane/pull/7606)
+
+  Similar to the :class:`~.QNGOptimizerQJIT` optimizer, :class:`~.MomentumQNGOptimizerQJIT` offers a
+  `qml.qjit`-compatible analogue to the existing :class:`~.MomentumQNGOptimizer` with an Optax-like interface:
+
+  ```python
+  import pennylane as qml
+  import jax.numpy as jnp
+
+  dev = qml.device("lightning.qubit", wires=2)
+
+  @qml.qnode(dev)
+  def circuit(params):
+      qml.RX(params[0], wires=0)
+      qml.RY(params[1], wires=1)
+      return qml.expval(qml.Z(0) + qml.X(1))
+
+  opt = qml.MomentumQNGOptimizerQJIT(stepsize=0.1, momentum=0.2)
+
+  @qml.qjit
+  def update_step_qjit(i, args):
+      params, state = args
+      return opt.step(circuit, params, state)
+
+  @qml.qjit
+  def optimization_qjit(params, iters):
+      state = opt.init(params)
+      args = (params, state)
+      params, state = qml.for_loop(iters)(update_step_qjit)(args)
+      return params
+  ```
+
+  ```pycon
+  >>> params = jnp.array([0.1, 0.2])
+  >>> iters = 1000
+  >>> optimization_qjit(params=params, iters=iters)
+  Array([ 3.14159265, -1.57079633], dtype=float64)
+  ```
+
 <h3>Improvements 🛠</h3>
 
 <h4>OpenQASM-PennyLane interoperability</h4>
@@ -85,6 +126,11 @@
 
 <h3>Breaking changes 💔</h3>
 
+* `qml.operation.Observable` and the corresponding `Observable.compare` have been removed, as
+  PennyLane now depends on the more general `Operator` interface instead. The
+  `Operator.is_hermitian` property can instead be used to check whether or not it is highly likely
+  that the operator instance is Hermitian.
+
 * `qml.operation.WiresEnum`, `qml.operation.AllWires`, and `qml.operation.AnyWires` have been removed. Setting `Operator.num_wires = None` (the default)
   should instead indicate that the `Operator` does not need wire validation.
   [(#7911)](https://github.com/PennyLaneAI/pennylane/pull/7911)
@@ -168,6 +214,10 @@
 
 <h3>Bug fixes 🐛</h3>
 
+* Fixes issue related to :func:`~.transforms.to_zx` adding the support for
+  `Toffoli` and `CCZ` gates conversion into their ZX-graph representation.
+  [(#7899)](https://github.com/PennyLaneAI/pennylane/pull/7899)
+
 * `get_best_diff_method` now correctly aligns with `execute` and `construct_batch` logic in workflows.
   [(#7898)](https://github.com/PennyLaneAI/pennylane/pull/7898)
 
@@ -190,6 +240,7 @@ Utkarsh Azad,
 Joey Carter,
 Yushao Chen,
 Marcus Edwards,
+Simone Gasperini,
 David Ittah,
 Erick Ochoa,
 Mudit Pandey,
diff --git a/pennylane/_deprecated_observable.py b/pennylane/_deprecated_observable.py
deleted file mode 100644
index a95e212fa..000000000
--- a/pennylane/_deprecated_observable.py
+++ /dev/null
@@ -1,98 +0,0 @@
-# Copyright 2025 Xanadu Quantum Technologies Inc.
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-
-#     http://www.apache.org/licenses/LICENSE-2.0
-
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""
-This module contains the deprecated Observable class to allow for deprecation warnings on access to the class.
-"""
-import warnings
-from typing import Union
-
-import pennylane as qml
-
-from .operation import Operation, Operator
-
-
-class Observable(Operator):
-    """Base class representing observables.
-
-    .. warning::
-
-        ``qml.operation.Observable`` is now deprecated. A generic operator can be used anywhere an ``Observable``
-        can, and is less restrictive.  To preserve prior ``Observable`` default behavior, an operator can override
-        ``Operator.queue()`` with empty behavior, and set ``is_hermitian = True`` manually:
-
-        .. code-block:: python
-
-            class MyObs(Operator):
-
-                is_hermitian = True
-
-                def queue(self, context=qml.QueuingManager):
-                    return self
-
-
-
-    Args:
-        params (tuple[tensor_like]): trainable parameters
-        wires (Iterable[Any] or Any): Wi're label(s) that the operator acts on.
-            If not given, args[-1] is interpreted as wires.
-        id (str): custom label given to an operator instance,
-            can be useful for some applications where the instance has to be identified
-    """
-
-    @property
-    def _queue_category(self):
-        return "_ops" if isinstance(self, Operation) else None
-
-    @property
-    def is_hermitian(self) -> bool:
-        """All observables must be hermitian"""
-        return True
-
-    def compare(self, other: Union["Observable", "qml.ops.LinearCombination"]) -> bool:
-        r"""Compares with another :class:`~Observable`, to determine if they are equivalent.
-
-        .. warning::
-
-            This method is deprecated. ``qml.equal`` or ``op1 == op2`` should be used instead.
-
-        Observables are equivalent if they represent the same operator
-        (their matrix representations are equal), and they are defined on the same wires.
-
-        .. Warning::
-
-            The compare method does **not** check if the matrix representation
-            of a :class:`~.Hermitian` observable is equal to an equivalent
-            observable expressed in terms of Pauli matrices.
-            To do so would require the matrix form to be calculated, which would
-            drastically increase runtime.
-
-        Returns:
-            (bool): True if equivalent.
-
-        **Examples**
-
-        >>> ob1 = qml.X(0) @ qml.Identity(1)
-        >>> ob2 = qml.Hamiltonian([1], [qml.X(0)])
-        >>> ob1.compare(ob2)
-        True
-        >>> ob1 = qml.X(0)
-        >>> ob2 = qml.Hermitian(np.array([[0, 1], [1, 0]]), 0)
-        >>> ob1.compare(ob2)
-        False
-        """
-        warnings.warn(
-            "The compare method is deprecated and will be removed in v0.43."
-            " op1 == op2 or qml.equal should be used instead.",
-            qml.exceptions.PennyLaneDeprecationWarning,
-        )
-        return qml.equal(self, other)
diff --git a/pennylane/operation.py b/pennylane/operation.py
index bd626e843..80d843c84 100644
--- a/pennylane/operation.py
+++ b/pennylane/operation.py
@@ -110,7 +110,6 @@ Operator Types
 
     ~Operator
     ~Operation
-    ~Observable
     ~CV
     ~CVObservable
     ~CVOperation
@@ -2575,17 +2574,6 @@ def gen_is_multi_term_hamiltonian(obj):
 
 def __getattr__(name):
     """To facilitate StatePrep rename"""
-    if name == "Observable":
-        from ._deprecated_observable import Observable  # pylint: disable=import-outside-toplevel
-
-        warnings.warn(
-            "Observable is deprecated and will be removed in v0.43. "
-            "A generic Operator class should be used instead. "
-            "If defining an Operator, set the is_hermitian property to True. "
-            "If checking if an Operator is Hermitian, check the is_hermitian property. ",
-            PennyLaneDeprecationWarning,
-        )
-        return Observable
     if name == "StatePrep":
         return StatePrepBase
     raise AttributeError(f"module 'pennylane.operation' has no attribute '{name}'")
diff --git a/pennylane/ops/op_math/adjoint.py b/pennylane/ops/op_math/adjoint.py
index 565bb5b4d..df78d1c80 100644
--- a/pennylane/ops/op_math/adjoint.py
+++ b/pennylane/ops/op_math/adjoint.py
@@ -19,7 +19,6 @@ from functools import lru_cache, partial
 from typing import overload
 
 import pennylane as qml
-from pennylane._deprecated_observable import Observable
 from pennylane.capture.autograph import wraps
 from pennylane.compiler import compiler
 from pennylane.math import conj, moveaxis, transpose
@@ -312,17 +311,6 @@ class Adjoint(SymbolicOp):
         >>> op.grad_method
         'A'
 
-        If the base class is an ``Observable`` instead, the ``Adjoint`` will be an ``Observable`` as
-        well.
-
-        >>> op = Adjoint(1.0 * qml.X(0))
-        >>> isinstance(op, qml.operation.Observable)
-        True
-        >>> isinstance(op, qml.operation.Operation)
-        False
-        >>> Adjoint(qml.X(0)) @ qml.Y(1)
-        (Adjoint(X(0))) @ Y(1)
-
     """
 
     resource_keys = {"base_class", "base_params"}
@@ -340,21 +328,13 @@ class Adjoint(SymbolicOp):
         """Returns an uninitialized type with the necessary mixins.
 
         If the ``base`` is an ``Operation``, this will return an instance of ``AdjointOperation``.
-        If ``Observable`` but not ``Operation``, it will be ``AdjointObs``.
-        And if both, it will be an instance of ``AdjointOpObs``.
 
         """
 
         if isinstance(base, Operation):
-            if isinstance(base, Observable):
-                return object.__new__(AdjointOpObs)
-
             # not an observable
             return object.__new__(AdjointOperation)
 
-        if isinstance(base, Observable):
-            return object.__new__(AdjointObs)
-
         return object.__new__(Adjoint)
 
     def __init__(self, base=None, id=None):
@@ -491,22 +471,4 @@ class AdjointOperation(Adjoint, Operation):
         return -1 * self.base.generator()
 
 
-class AdjointObs(Adjoint, Observable):
-    """A child of :class:`~.Adjoint` that also inherits from :class:`~.Observable`."""
-
-    def __new__(cls, *_, **__):
-        return object.__new__(cls)
-
-
-# TODO: Remove when PL supports pylint==3.3.6 (it is considered a useless-suppression) [sc-91362]
-# pylint: disable=too-many-ancestors
-class AdjointOpObs(AdjointOperation, Observable):
-    """A child of :class:`~.AdjointOperation` that also inherits from :class:`~.Observable."""
-
-    def __new__(cls, *_, **__):
-        return object.__new__(cls)
-
-
 AdjointOperation._primitive = Adjoint._primitive  # pylint: disable=protected-access
-AdjointObs._primitive = Adjoint._primitive  # pylint: disable=protected-access
-AdjointOpObs._primitive = Adjoint._primitive  # pylint: disable=protected-access
diff --git a/pennylane/ops/op_math/linear_combination.py b/pennylane/ops/op_math/linear_combination.py
index 0dacf9adc..bee71b4c0 100644
--- a/pennylane/ops/op_math/linear_combination.py
+++ b/pennylane/ops/op_math/linear_combination.py
@@ -16,7 +16,6 @@ LinearCombination class
 """
 import itertools
 import numbers
-import warnings
 
 # pylint: disable=too-many-arguments,protected-access
 from copy import copy
@@ -313,58 +312,6 @@ class LinearCombination(Sum):
         coeffs, ops, pr = self._simplify_coeffs_ops(self.coeffs, self.ops, self.pauli_rep, cutoff)
         return LinearCombination(coeffs, ops, _pauli_rep=pr)
 
-    def compare(self, other):
-        r"""Determines mathematical equivalence between operators
-
-        ``LinearCombination`` and other operators are equivalent if they mathematically represent the same operator
-        (their matrix representations are equal), acting on the same wires.
-
-        .. Warning::
-
-            This method does not compute explicit matrices but uses the underlyding operators and coefficients for comparisons. When both operators
-            consist purely of Pauli operators, and therefore have a valid ``op.pauli_rep``, the comparison is cheap.
-            When that is not the case (e.g. one of the operators contains a ``Hadamard`` gate), it can be more expensive as it involves mathematical simplification of both operators.
-
-        Returns:
-            (bool): True if equivalent.
-
-        **Examples**
-
-        >>> H = qml.ops.LinearCombination(
-        ...     [0.5, 0.5],
-        ...     [qml.PauliZ(0) @ qml.PauliY(1), qml.PauliY(1) @ qml.PauliZ(0) @ qml.Identity("a")]
-        ... )
-        >>> obs = qml.PauliZ(0) @ qml.PauliY(1)
-        >>> print(H.compare(obs))
-        True
-
-        >>> H1 = qml.ops.LinearCombination([1, 1], [qml.PauliX(0), qml.PauliZ(1)])
-        >>> H2 = qml.ops.LinearCombination([1, 1], [qml.PauliZ(0), qml.PauliX(1)])
-        >>> H1.compare(H2)
-        False
-
-        >>> ob1 = qml.ops.LinearCombination([1], [qml.PauliX(0)])
-        >>> ob2 = qml.Hermitian(np.array([[0, 1], [1, 0]]), 0)
-        >>> ob1.compare(ob2)
-        False
-        """
-        warnings.warn(
-            "The compare method is deprecated and will be removed in v0.43."
-            " op1 == op2 or qml.equal should be used instead.",
-            qml.exceptions.PennyLaneDeprecationWarning,
-        )
-        if isinstance(other, (Operator)):
-            if (pr1 := self.pauli_rep) is not None and (pr2 := other.pauli_rep) is not None:
-                pr1.simplify()
-                pr2.simplify()
-                return pr1 == pr2
-
-            op1 = self.simplify()
-            op2 = other.simplify()
-            return qml.equal(op1, op2)
-
-        raise ValueError("Can only compare a LinearCombination and an Operator.")
-
     def __matmul__(self, other: Operator) -> Operator:
         """The product operation between Operator objects."""
         if isinstance(other, LinearCombination):
diff --git a/pennylane/ops/op_math/pow.py b/pennylane/ops/op_math/pow.py
index 5f8c1fd0d..1828b2de3 100644
--- a/pennylane/ops/op_math/pow.py
+++ b/pennylane/ops/op_math/pow.py
@@ -21,7 +21,6 @@ from scipy.linalg import fractional_matrix_power
 
 import pennylane as qml
 from pennylane import math as qmlmath
-from pennylane._deprecated_observable import Observable
 from pennylane.operation import (
     AdjointUndefinedError,
     DecompositionUndefinedError,
@@ -163,15 +162,10 @@ class Pow(ScalarSymbolicOp):
         """
 
         if isinstance(base, Operation):
-            if isinstance(base, Observable):
-                return object.__new__(PowOpObs)
 
             # not an observable
             return object.__new__(PowOperation)
 
-        if isinstance(base, Observable):
-            return object.__new__(PowObs)
-
         return object.__new__(Pow)
 
     def __init__(self, base=None, z=1, id=None):
@@ -430,20 +424,3 @@ class PowOperation(Pow, Operation):
     @property
     def control_wires(self):
         return self.base.control_wires
-
-
-class PowObs(Pow, Observable):
-    """A child class of ``Pow`` that also inherits from ``Observable``."""
-
-    def __new__(cls, *_, **__):
-        return object.__new__(cls)
-
-
-# pylint: disable=too-many-ancestors
-class PowOpObs(PowOperation, Observable):
-    """A child class of ``Pow`` that inherits from both
-    ``Observable`` and ``Operation``.
-    """
-
-    def __new__(cls, *_, **__):
-        return object.__new__(cls)
diff --git a/pennylane/ops/qubit/non_parametric_ops.py b/pennylane/ops/qubit/non_parametric_ops.py
index f38055ea9..234b5b880 100644
--- a/pennylane/ops/qubit/non_parametric_ops.py
+++ b/pennylane/ops/qubit/non_parametric_ops.py
@@ -25,7 +25,6 @@ from scipy import sparse
 
 import pennylane as qml
 from pennylane import math
-from pennylane._deprecated_observable import Observable
 from pennylane.decomposition import (
     add_decomps,
     adjoint_resource_rep,
@@ -47,7 +46,7 @@ from pennylane.wires import Wires, WiresLike
 INV_SQRT2 = 1 / qml.math.sqrt(2)
 
 
-class Hadamard(Observable, Operation):
+class Hadamard(Operation):
     r"""Hadamard(wires)
     The Hadamard operator
 
@@ -65,6 +64,7 @@ class Hadamard(Observable, Operation):
     """
 
     is_hermitian = True
+    _queue_category = "_ops"
 
     num_wires = 1
     """int: Number of wires that the operator acts on."""
@@ -305,7 +305,7 @@ def _controlled_hadamard(wires, control_wires, work_wires, work_wire_type, **__)
 add_decomps("C(Hadamard)", flip_zero_control(_controlled_hadamard))
 
 
-class PauliX(Observable, Operation):
+class PauliX(Operation):
     r"""
     The Pauli X operator
 
@@ -337,6 +337,7 @@ class PauliX(Observable, Operation):
     batch_size = None
 
     _queue_category = "_ops"
+    is_hermitian = True
 
     @property
     def pauli_rep(self):
@@ -588,7 +589,7 @@ def _controlled_x_decomp(
 add_decomps("C(PauliX)", _controlled_x_decomp)
 
 
-class PauliY(Observable, Operation):
+class PauliY(Operation):
     r"""
     The Pauli Y operator
 
@@ -845,7 +846,7 @@ def _controlled_y_decomp(*_, wires, control_wires, work_wires, work_wire_type, *
 add_decomps("C(PauliY)", flip_zero_control(_controlled_y_decomp))
 
 
-class PauliZ(Observable, Operation):
+class PauliZ(Operation):
     r"""
     The Pauli Z operator
 
@@ -863,6 +864,7 @@ class PauliZ(Observable, Operation):
     """
 
     is_hermitian = True
+    _queue_category = "_ops"
     num_wires = 1
     num_params = 0
     """int: Number of trainable parameters that the operator depends on."""
diff --git a/pennylane/ops/qubit/observables.py b/pennylane/ops/qubit/observables.py
index 6a78d8702..acaa50aff 100644
--- a/pennylane/ops/qubit/observables.py
+++ b/pennylane/ops/qubit/observables.py
@@ -24,15 +24,14 @@ import numpy as np
 from scipy.sparse import csr_matrix, spmatrix
 
 import pennylane as qml
-from pennylane._deprecated_observable import Observable
-from pennylane.operation import Operation
+from pennylane.operation import Operation, Operator
 from pennylane.typing import TensorLike
 from pennylane.wires import Wires, WiresLike
 
 from .matrix_ops import QubitUnitary
 
 
-class Hermitian(Observable):
+class Hermitian(Operator):
     r"""
     An arbitrary Hermitian observable.
 
@@ -262,7 +261,7 @@ class Hermitian(Observable):
         return self.compute_diagonalizing_gates(self.eigendecomposition["eigvec"], self.wires)
 
 
-class SparseHamiltonian(Observable):
+class SparseHamiltonian(Operator):
     r"""
     A Hamiltonian represented directly as a sparse matrix in Compressed Sparse Row (CSR) format.
 
@@ -405,7 +404,7 @@ class SparseHamiltonian(Observable):
         return H
 
 
-class Projector(Observable):
+class Projector(Operator):
     r"""Projector(state, wires, id=None)
     Observable corresponding to the state projector :math:`P=\ket{\phi}\bra{\phi}`.
 
@@ -453,6 +452,7 @@ class Projector(Observable):
     is_hermitian = True
     name = "Projector"
     num_params = 1
+    _queue_category = None
     """int: Number of trainable parameters that the operator depends on."""
 
     ndim_params = (1,)
@@ -505,6 +505,7 @@ class BasisStateProjector(Projector, Operation):
     :math:`\phi` denotes a basis state."""
 
     grad_method = None
+    _queue_category = "_ops"
 
     # The call signature should be the same as Projector.__new__ for the positional
     # arguments, but with free key word arguments.
diff --git a/pennylane/ops/qutrit/observables.py b/pennylane/ops/qutrit/observables.py
index d69f2cbc5..5b3c455d6 100644
--- a/pennylane/ops/qutrit/observables.py
+++ b/pennylane/ops/qutrit/observables.py
@@ -17,7 +17,7 @@ This submodule contains the qutrit quantum observables.
 import numpy as np
 
 import pennylane as qml
-from pennylane._deprecated_observable import Observable
+from pennylane.operation import Operator
 from pennylane.ops.qubit import Hermitian
 from pennylane.ops.qutrit import QutritUnitary
 
@@ -150,7 +150,7 @@ class THermitian(Hermitian):
         return [QutritUnitary(eigenvectors.conj().T, wires=wires)]
 
 
-class GellMann(Observable):
+class GellMann(Operator):
     r"""
     The Gell-Mann observables for qutrits
 
diff --git a/pennylane/optimize/__init__.py b/pennylane/optimize/__init__.py
index 8ffe29ced..b549b3c5a 100644
--- a/pennylane/optimize/__init__.py
+++ b/pennylane/optimize/__init__.py
@@ -33,6 +33,7 @@ from .rotosolve import RotosolveOptimizer
 from .shot_adaptive import ShotAdaptiveOptimizer
 from .spsa import SPSAOptimizer
 from .qng_qjit import QNGOptimizerQJIT
+from .momentum_qng_qjit import MomentumQNGOptimizerQJIT
 
 # Optimizers to display in the docs
 __all__ = [
@@ -52,4 +53,5 @@ __all__ = [
     "ShotAdaptiveOptimizer",
     "SPSAOptimizer",
     "QNGOptimizerQJIT",
+    "MomentumQNGOptimizerQJIT",
 ]
diff --git a/pennylane/optimize/momentum_qng.py b/pennylane/optimize/momentum_qng.py
index caaf7b2ed..8744a3e3a 100644
--- a/pennylane/optimize/momentum_qng.py
+++ b/pennylane/optimize/momentum_qng.py
@@ -81,6 +81,8 @@ class MomentumQNGOptimizer(QNGOptimizer):
         - `QAOA <https://github.com/borbysh/Momentum-QNG/blob/main/QAOA_depth4.ipynb>`__
         - `VQE <https://github.com/borbysh/Momentum-QNG/blob/main/portfolio_optimization.ipynb>`__
 
+        See :class:`~.MomentumQNGOptimizerQJIT` for an Optax-like and ``jax.jit``/``qml.qjit``-compatible implementation.
+
     Keyword Args:
         stepsize=0.01 (float): the user-defined hyperparameter :math:`\eta`
         momentum=0.9 (float): the user-defined hyperparameter :math:`\rho`
diff --git a/pennylane/optimize/momentum_qng_qjit.py b/pennylane/optimize/momentum_qng_qjit.py
new file mode 100644
index 000000000..0280e244a
--- /dev/null
+++ b/pennylane/optimize/momentum_qng_qjit.py
@@ -0,0 +1,124 @@
+# Copyright 2018-2025 Xanadu Quantum Technologies Inc.
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+#     http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Quantum natural gradient optimizer with momentum for Jax/Catalyst interface"""
+
+from pennylane import math
+
+from .qng_qjit import QNGOptimizerQJIT
+
+
+class MomentumQNGOptimizerQJIT(QNGOptimizerQJIT):
+    r"""Optax-like and ``jax.jit``/``qml.qjit``-compatible implementation of the :class:`~.MomentumQNGOptimizer`,
+    a generalized Quantum Natural Gradient (QNG) optimizer considering a discrete-time Langevin equation
+    with QNG force.
+
+    For more theoretical details, see the :class:`~.MomentumQNGOptimizer` documentation.
+
+    .. note::
+
+        Please be aware of the following:
+
+        - As with ``MomentumQNGOptimizer``, ``MomentumQNGOptimizerQJIT`` supports a single QNode to encode the objective function.
+
+        - ``MomentumQNGOptimizerQJIT`` does not support any QNode with multiple arguments. A potential workaround
+          would be to combine all parameters into a single objective function argument.
+
+        - ``MomentumQNGOptimizerQJIT`` does not work correctly if there is any classical processing in the QNode circuit
+          (e.g., ``2 * theta`` as a gate parameter).
+
+    Parameters:
+        stepsize (float): the stepsize hyperparameter (default value: 0.01).
+        momentum (float): the momentum coefficient hyperparameter (default value: 0.9).
+        approx (str): approximation method for the metric tensor (default value: "block-diag").
+
+            - If ``None``, the full metric tensor is computed
+
+            - If ``"block-diag"``, the block-diagonal approximation is computed, reducing
+              the number of evaluated circuits significantly
+
+            - If ``"diag"``, the diagonal approximation is computed, slightly
+              reducing the classical overhead but not the quantum resources
+              (compared to ``"block-diag"``)
+
+        lam (float): metric tensor regularization to be applied at each optimization step (default value: 0).
+
+    **Example:**
+
+    Consider a hybrid workflow to optimize an objective function defined by a quantum circuit.
+    To make the entire workflow faster, the update step and the whole optimization
+    can be just-in-time compiled using the :func:`~.qjit` decorator:
+
+    .. code-block:: python
+
+        import pennylane as qml
+        import jax.numpy as jnp
+
+        dev = qml.device("lightning.qubit", wires=2)
+
+        @qml.qnode(dev)
+        def circuit(params):
+            qml.RX(params[0], wires=0)
+            qml.RY(params[1], wires=1)
+            return qml.expval(qml.Z(0) + qml.X(1))
+
+        opt = qml.MomentumQNGOptimizerQJIT(stepsize=0.1, momentum=0.2)
+
+        @qml.qjit
+        def update_step_qjit(i, args):
+            params, state = args
+            return opt.step(circuit, params, state)
+
+        @qml.qjit
+        def optimization_qjit(params, iters):
+            state = opt.init(params)
+            args = (params, state)
+            params, state = qml.for_loop(iters)(update_step_qjit)(args)
+            return params
+
+    >>> params = jnp.array([0.1, 0.2])
+    >>> iters = 1000
+    >>> optimization_qjit(params=params, iters=iters)
+    Array([ 3.14159265, -1.57079633], dtype=float64)
+
+    Make sure you are using the ``lightning.qubit`` device along with ``qml.qjit``.
+    """
+
+    def __init__(self, stepsize=0.01, momentum=0.9, approx="block-diag", lam=0):
+        super().__init__(stepsize, approx, lam)
+        self.momentum = momentum
+
+    def init(self, params):
+        """Return the initial state of the optimizer. This state is always initialized as an
+        array of zeros with the same shape and type of the given array of parameters.
+
+        Args:
+            params (array): QNode parameters
+
+        Returns:
+            array: initial state of the optimizer
+        """
+        # pylint:disable=no-self-use
+        return math.zeros_like(params)
+
+    def _apply_grad(self, mt, grad, params, state):
+        """Update the optimizer's state and the array of parameters for a single optimization
+        step according to the Quantum Natural Gradient algorithm with momentum.
+        """
+        shape = math.shape(grad)
+        grad_flat = math.flatten(grad)
+        update_flat = math.linalg.pinv(mt) @ grad_flat
+        update = math.reshape(update_flat, shape)
+        state = self.momentum * state + self.stepsize * update
+        new_params = params - state
+        return new_params, state
diff --git a/pennylane/optimize/qng_qjit.py b/pennylane/optimize/qng_qjit.py
index 7bd04744a..f49109fba 100644
--- a/pennylane/optimize/qng_qjit.py
+++ b/pennylane/optimize/qng_qjit.py
@@ -78,12 +78,13 @@ class QNGOptimizerQJIT:
     Array([ 3.14159265, -1.57079633], dtype=float64)
 
     Make sure you are using the ``lightning.qubit`` device along with ``qml.qjit`` with ``autograph`` enabled.
+    Using ``qml.qjit`` on the whole workflow with ``autograph`` not enabled may lead to a substantial increase
+    in compilation time and no runtime benefits.
 
-    Using the ``jax.jit`` decorator for the entire workflow is not recommended since it
-    may lead to a significative compilation time and no runtime benefits.
-    However, ``jax.jit`` can be used with the ``default.qubit`` device to just-in-time
-    compile the ``step`` (or ``step_and_cost``) method of the optimizer.
-    For example:
+    The ``jax.jit`` decorator should not be used on the entire workflow.
+    However, it can be used with the ``default.qubit`` device to just-in-time
+    compile the ``step`` (or ``step_and_cost``) method of the optimizer, leading
+    to a significative increase in runtime performance:
 
     .. code-block:: python
 
@@ -163,13 +164,6 @@ class QNGOptimizerQJIT:
 
         Returns:
             tuple: (new parameters values, new optimizer's state)
-
-        .. note::
-
-            Since the Quantum Natural Gradient (QNG) algorithm doesn't actually require any particular state,
-            the ``state`` object is never really updated in this case. However, it is carried over the
-            optimization to match the ``optax``-like interface for all Jax-based quantum-specific optimizers.
-
         """
         mt = self._get_metric_tensor(qnode, params, **kwargs)
         grad = self._get_grad(qnode, params, **kwargs)
@@ -188,13 +182,6 @@ class QNGOptimizerQJIT:
 
         Returns:
             tuple: (new parameters values, new optimizer's state, objective function value)
-
-        .. note::
-
-            Since the Quantum Natural Gradient (QNG) algorithm doesn't actually require any particular state,
-            the ``state`` object is never really updated in this case. However, it is carried over the
-            optimization to match the ``optax``-like interface for all Jax-based quantum-specific optimizers.
-
         """
         mt = self._get_metric_tensor(qnode, params, **kwargs)
         cost, grad = self._get_value_and_grad(qnode, params, **kwargs)
diff --git a/pennylane/transforms/zx/converter.py b/pennylane/transforms/zx/converter.py
index 688065962..5a2d2b397 100644
--- a/pennylane/transforms/zx/converter.py
+++ b/pennylane/transforms/zx/converter.py
@@ -29,6 +29,52 @@ from pennylane.typing import PostprocessingFn
 from pennylane.wires import Wires
 
 
+def _toffoli_clifford_t_decomp(wires):
+    """Return the explicit Clifford+T decomposition of the Toffoli gate,
+    replacing Adjoint(T) with PhaseShift(-π/4)."""
+
+    return [
+        qml.Hadamard(wires=wires[2]),
+        qml.CNOT(wires=[wires[1], wires[2]]),
+        qml.PhaseShift(-np.pi / 4, wires=wires[2]),
+        qml.CNOT(wires=[wires[0], wires[2]]),
+        qml.T(wires=wires[2]),
+        qml.CNOT(wires=[wires[1], wires[2]]),
+        qml.PhaseShift(-np.pi / 4, wires=wires[2]),
+        qml.CNOT(wires=[wires[0], wires[2]]),
+        qml.T(wires=wires[2]),
+        qml.T(wires=wires[1]),
+        qml.CNOT(wires=[wires[0], wires[1]]),
+        qml.Hadamard(wires=wires[2]),
+        qml.T(wires=wires[0]),
+        qml.PhaseShift(-np.pi / 4, wires=wires[1]),
+        qml.CNOT(wires=[wires[0], wires[1]]),
+    ]
+
+
+def _ccz_clifford_t_decomp(wires):
+    """Return the explicit Clifford+T decomposition of the CCZ gate,
+    replacing Adjoint(T) with PhaseShift(-π/4)."""
+
+    return [
+        qml.CNOT(wires=[wires[1], wires[2]]),
+        qml.PhaseShift(-np.pi / 4, wires=wires[2]),
+        qml.CNOT(wires=[wires[0], wires[2]]),
+        qml.T(wires=wires[2]),
+        qml.CNOT(wires=[wires[1], wires[2]]),
+        qml.PhaseShift(-np.pi / 4, wires=wires[2]),
+        qml.CNOT(wires=[wires[0], wires[2]]),
+        qml.T(wires=wires[2]),
+        qml.T(wires=wires[1]),
+        qml.CNOT(wires=[wires[0], wires[1]]),
+        qml.Hadamard(wires=wires[2]),
+        qml.T(wires=wires[0]),
+        qml.PhaseShift(-np.pi / 4, wires=wires[1]),
+        qml.CNOT(wires=[wires[0], wires[1]]),
+        qml.Hadamard(wires=wires[2]),
+    ]
+
+
 class VertexType:  # pylint: disable=too-few-public-methods
     """Type of a vertex in the graph.
 
@@ -254,6 +300,13 @@ def to_zx(tape, expand_measurements=False):
         It is a PennyLane adapted and reworked `circuit_to_graph <https://github.com/Quantomatic/pyzx/blob/master/pyzx/circuit/graphparser.py>`_
         function.
 
+    .. note::
+
+        Prior to being added to the graph, Toffoli and CCZ gates are replaced by particular decompositions. These decompositions
+        are described in detail in: J. Welch, A. Bocharov, and K. Svore, “Efficient Approximation of Diagonal Unitaries over the Clifford+T Basis,”
+        Quantum information & computation, vol. 16, Dec. 2014, doi: 10.26421/QIC16.1-2-6.
+        This is necessary because Toffoli and CCZ gates are not directly supported in PyZX.
+
         Copyright (C) 2018 - Aleks Kissinger and John van de Wetering
     """
     # If it is a simple operation just transform it to a tape
@@ -284,17 +337,22 @@ def _to_zx_transform(
 
     # Dictionary of gates (PennyLane to PyZX circuit)
     gate_types = {
-        "PauliX": pyzx.circuit.gates.NOT,
-        "PauliZ": pyzx.circuit.gates.Z,
+        "X": pyzx.circuit.gates.NOT,
+        "Y": pyzx.circuit.gates.Y,
+        "Z": pyzx.circuit.gates.Z,
         "S": pyzx.circuit.gates.S,
         "T": pyzx.circuit.gates.T,
         "Hadamard": pyzx.circuit.gates.HAD,
         "RX": pyzx.circuit.gates.XPhase,
+        "RY": pyzx.circuit.gates.YPhase,
         "RZ": pyzx.circuit.gates.ZPhase,
         "PhaseShift": pyzx.circuit.gates.ZPhase,
         "SWAP": pyzx.circuit.gates.SWAP,
         "CNOT": pyzx.circuit.gates.CNOT,
+        "CY": pyzx.circuit.gates.CY,
         "CZ": pyzx.circuit.gates.CZ,
+        "CRX": pyzx.circuit.gates.CRX,
+        "CRY": pyzx.circuit.gates.CRY,
         "CRZ": pyzx.circuit.gates.CRZ,
         "CH": pyzx.circuit.gates.CHAD,
         "CCZ": pyzx.circuit.gates.CCZ,
@@ -331,17 +389,12 @@ def _to_zx_transform(
         )
 
         expanded_operations = []
-
-        # Define specific decompositions
         for op in mapped_tape.operations:
-            if op.name == "RY":
-                theta = op.data[0]
-                decomp = [
-                    qml.RX(np.pi / 2, wires=op.wires),
-                    qml.RZ(theta + np.pi, wires=op.wires),
-                    qml.RX(np.pi / 2, wires=op.wires),
-                    qml.RZ(3 * np.pi, wires=op.wires),
-                ]
+            if op.name == "Toffoli":
+                decomp = _toffoli_clifford_t_decomp(op.wires)
+                expanded_operations.extend(decomp)
+            elif op.name == "CCZ":
+                decomp = _ccz_clifford_t_decomp(op.wires)
                 expanded_operations.extend(decomp)
             else:
                 expanded_operations.append(op)


Test patch:
diff --git a/tests/io/qasm_interpreter/test_interpreter.py b/tests/io/qasm_interpreter/test_interpreter.py
index 1b1acdef2..6e5b649e9 100644
--- a/tests/io/qasm_interpreter/test_interpreter.py
+++ b/tests/io/qasm_interpreter/test_interpreter.py
@@ -42,7 +42,7 @@ from pennylane import (
 )
 from pennylane.measurements import MeasurementValue, MidMeasureMP
 from pennylane.ops import Adjoint, Controlled, ControlledPhaseShift, MultiControlledX
-from pennylane.ops.op_math.pow import PowOperation, PowOpObs
+from pennylane.ops.op_math.pow import PowOperation
 from pennylane.wires import Wires
 
 try:
@@ -1401,7 +1401,7 @@ class TestGates:
             RX(0.5, wires=["q0"]),
             RY(0.2, wires=["q0"]),
             Adjoint(RX(0.5, wires=["q0"])),
-            PowOpObs(PauliX(wires=["q0"]), 2),
+            PowOperation(PauliX(wires=["q0"]), 2),
             CNOT(wires=["q1", "q0"]),
         ]
 
diff --git a/tests/ops/functions/conftest.py b/tests/ops/functions/conftest.py
index c969b6592..602e0b926 100644
--- a/tests/ops/functions/conftest.py
+++ b/tests/ops/functions/conftest.py
@@ -22,11 +22,10 @@ import numpy as np
 import pytest
 
 import pennylane as qml
-from pennylane._deprecated_observable import Observable
 from pennylane.exceptions import DeviceError
 from pennylane.operation import Channel, Operation, Operator, StatePrepBase
-from pennylane.ops.op_math.adjoint import Adjoint, AdjointObs, AdjointOperation, AdjointOpObs
-from pennylane.ops.op_math.pow import PowObs, PowOperation, PowOpObs
+from pennylane.ops.op_math.adjoint import Adjoint, AdjointOperation
+from pennylane.ops.op_math.pow import PowOperation
 from pennylane.templates.subroutines.trotter import TrotterizedQfunc
 
 
@@ -171,12 +170,9 @@ These operators need to break PL conventions, and each one's reason is specified
 
 _ABSTRACT_OR_META_TYPES = {
     Adjoint,
-    AdjointOpObs,
     AdjointOperation,
-    AdjointObs,
     Operator,
     Operation,
-    Observable,
     Channel,
     qml.ops.Projector,
     qml.ops.SymbolicOp,
@@ -190,9 +186,7 @@ _ABSTRACT_OR_META_TYPES = {
     StatePrepBase,
     qml.resource.ResourcesOperation,
     qml.resource.ErrorOperation,
-    PowOpObs,
     PowOperation,
-    PowObs,
     qml.StatePrep,
     qml.FromBloq,
     qml.allocation.Allocate,  # no integer wires
diff --git a/tests/ops/functions/test_equal.py b/tests/ops/functions/test_equal.py
index 029c81843..095a92b33 100644
--- a/tests/ops/functions/test_equal.py
+++ b/tests/ops/functions/test_equal.py
@@ -1885,19 +1885,6 @@ class TestSymbolicOpComparison:
         assert qml.equal(op1, op2, check_interface=False, check_trainability=False)
         assert not qml.equal(op1, op2, check_interface=False, check_trainability=True)
 
-    def test_adjoint_observable_with_non_adjoint(self):
-        """Test that comparing an adjoint observable with a non-adjoint operation returns False"""
-
-        base = qml.X(0)
-        adj_op = qml.adjoint(base)
-
-        # if we get rid of Observable or stop having adjoint(Observable) be an Observable,
-        # this test is no longer relevant. Ref: https://github.com/PennyLaneAI/pennylane/pull/7107/
-        with pytest.warns(qml.exceptions.PennyLaneDeprecationWarning):
-            assert isinstance(adj_op, qml.operation.Observable)
-
-        assert not qml.equal(adj_op, qml.exp(base, 2))
-
     @pytest.mark.parametrize(("wire1", "wire2", "res"), WIRES)
     def test_conditional_base_operator_wire_comparison(self, wire1, wire2, res):
         """Test that equal compares operator wires for Conditional operators"""
diff --git a/tests/ops/op_math/test_adjoint.py b/tests/ops/op_math/test_adjoint.py
index 0676ad33b..b8412c8ed 100644
--- a/tests/ops/op_math/test_adjoint.py
+++ b/tests/ops/op_math/test_adjoint.py
@@ -48,8 +48,6 @@ class TestInheritanceMixins:
         assert isinstance(op, Adjoint)
         assert isinstance(op, qml.operation.Operator)
         assert not isinstance(op, qml.operation.Operation)
-        with pytest.warns(qml.exceptions.PennyLaneDeprecationWarning):
-            assert not isinstance(op, qml.operation.Observable)
         assert not isinstance(op, AdjointOperation)
 
         # checking we can call `dir` without problems
@@ -70,41 +68,12 @@ class TestInheritanceMixins:
         assert isinstance(op, Adjoint)
         assert isinstance(op, qml.operation.Operator)
         assert isinstance(op, qml.operation.Operation)
-        with pytest.warns(qml.exceptions.PennyLaneDeprecationWarning):
-            assert not isinstance(op, qml.operation.Observable)
         assert isinstance(op, AdjointOperation)
 
         # check operation-specific properties made it into the mapping
         assert "grad_recipe" in dir(op)
         assert "control_wires" in dir(op)
 
-    def test_observable(self):
-        """Test that when the base is an Observable, Adjoint will also inherit from Observable."""
-
-        with pytest.warns(qml.exceptions.PennyLaneDeprecationWarning):
-            # pylint: disable=too-few-public-methods
-            class CustomObs(qml.operation.Observable):
-                num_wires = 1
-                num_params = 0
-
-        base = CustomObs(wires=0)
-        ob = Adjoint(base)
-
-        assert isinstance(ob, Adjoint)
-        assert isinstance(ob, qml.operation.Operator)
-        assert not isinstance(ob, qml.operation.Operation)
-        with pytest.warns(qml.exceptions.PennyLaneDeprecationWarning):
-            assert isinstance(ob, qml.operation.Observable)
-        assert not isinstance(ob, AdjointOperation)
-
-        # Check some basic observable functionality
-        with pytest.warns(qml.exceptions.PennyLaneDeprecationWarning):
-            assert ob.compare(ob)
-        assert isinstance(1.0 * ob @ ob, qml.ops.Prod)
-
-        # check the dir
-        assert "grad_recipe" not in dir(ob)
-
     @pytest.mark.parametrize(
         "op",
         (
@@ -1003,19 +972,6 @@ class TestAdjointConstructorOutsideofQueuing:
         assert isinstance(out, qml.RX)
         assert out.data == (-x,)
 
-    def test_observable(self):
-        """Test providing a preconstructed Observable outside of a queuing context."""
-
-        base = 1.0 * qml.PauliX(0)
-        obs = adjoint(base)
-
-        assert isinstance(obs, Adjoint)
-        with pytest.warns(qml.exceptions.PennyLaneDeprecationWarning):
-            assert isinstance(base, qml.operation.Observable) == isinstance(
-                obs, qml.operation.Observable
-            )
-        assert obs.base is base
-
     def test_single_op_function(self):
         """Test the transform on a single op as a callable outside of a queuing context."""
         x = 1.234
diff --git a/tests/ops/op_math/test_linear_combination.py b/tests/ops/op_math/test_linear_combination.py
index 1d815d078..180afb432 100644
--- a/tests/ops/op_math/test_linear_combination.py
+++ b/tests/ops/op_math/test_linear_combination.py
@@ -708,54 +708,6 @@ class TestLinearCombination:
         (qml.ops.LinearCombination([1.0], [X(0) @ X(1)]), X(0) @ X(1)),
     )
 
-    @pytest.mark.parametrize("H, op", COMPARE_WITH_OPS)
-    def test_compare_to_simple_ops(self, H, op):
-        with pytest.raises(qml.exceptions.PennyLaneDeprecationWarning):
-            assert H.compare(op)
-
-    def test_compare_raises_error(self):
-        op = qml.ops.LinearCombination([], [])
-        with pytest.raises(qml.exceptions.PennyLaneDeprecationWarning):
-            with pytest.raises(ValueError, match="Can only compare a LinearCombination"):
-                op.compare(0)
-
-    @pytest.mark.xfail
-    def test_compare_gell_mann(self):
-        """Tests that the compare method returns the correct result for LinearCombinations
-        with qml.GellMann present."""
-        H1 = qml.ops.LinearCombination([1], [qml.GellMann(wires=2, index=2)])
-        H2 = qml.ops.LinearCombination(
-            [1], [qml.GellMann(wires=2, index=1) @ qml.GellMann(wires=1, index=2)]
-        )
-        H3 = qml.ops.LinearCombination([1], [qml.GellMann(wires=2, index=1)])
-        H4 = qml.ops.LinearCombination(
-            [1], [qml.GellMann(wires=2, index=1) @ qml.GellMann(wires=1, index=3)]
-        )
-
-        assert H1.compare(qml.GellMann(wires=2, index=2))
-        assert H1.compare(qml.GellMann(wires=2, index=1))
-        assert H1.compare(H3)
-        assert H2.compare(qml.GellMann(wires=2, index=1) @ qml.GellMann(wires=1, index=2))
-        assert not H2.compare(qml.GellMann(wires=2, index=2) @ qml.GellMann(wires=1, index=2))
-        assert not H2.compare(H4)
-
-    def test_LinearCombination_equal_error(self):
-        """Tests that the correct error is raised when compare() is called on invalid type"""
-
-        H = qml.ops.LinearCombination([1], [Z(0)])
-        with pytest.warns(qml.exceptions.PennyLaneDeprecationWarning):
-            with pytest.raises(
-                ValueError,
-                match=r"Can only compare a LinearCombination and an Operator.",
-            ):
-                _ = H.compare([[1, 0], [0, -1]])
-
-    @pytest.mark.parametrize(("H1", "H2", "res"), equal_LinearCombinations)
-    def test_LinearCombination_equal(self, H1, H2, res):
-        """Tests that equality can be checked between LinearCombinations"""
-        with pytest.warns(qml.exceptions.PennyLaneDeprecationWarning):
-            assert H1.compare(H2) == res
-
     @pytest.mark.parametrize(("H1", "H2", "H"), add_LinearCombinations)
     def test_LinearCombination_add(self, H1, H2, H):
         """Tests that LinearCombinations are added correctly"""
diff --git a/tests/ops/op_math/test_pow_op.py b/tests/ops/op_math/test_pow_op.py
index 449ee9ab1..de1a4de2b 100644
--- a/tests/ops/op_math/test_pow_op.py
+++ b/tests/ops/op_math/test_pow_op.py
@@ -122,8 +122,6 @@ class TestInheritanceMixins:
         assert isinstance(op, Pow)
         assert isinstance(op, qml.operation.Operator)
         assert not isinstance(op, qml.operation.Operation)
-        with pytest.warns(qml.exceptions.PennyLaneDeprecationWarning):
-            assert not isinstance(op, qml.operation.Observable)
         assert not isinstance(op, PowOperation)
 
         # checking we can call `dir` without problems
@@ -143,38 +141,12 @@ class TestInheritanceMixins:
         assert isinstance(op, Pow)
         assert isinstance(op, qml.operation.Operator)
         assert isinstance(op, qml.operation.Operation)
-        with pytest.warns(qml.exceptions.PennyLaneDeprecationWarning):
-            assert not isinstance(op, qml.operation.Observable)
         assert isinstance(op, PowOperation)
 
         # check operation-specific properties made it into the mapping
         assert "grad_recipe" in dir(op)
         assert "control_wires" in dir(op)
 
-    def test_observable(self, power_method):
-        """Test that when the base is an Observable, Pow will also inherit from Observable."""
-
-        with pytest.warns(qml.exceptions.PennyLaneDeprecationWarning):
-
-            class CustomObs(qml.operation.Observable):
-                num_wires = 1
-                num_params = 0
-
-        base = CustomObs(wires=0)
-        ob: Pow = power_method(base=base, z=-1.2)
-
-        assert isinstance(ob, Pow)
-        assert isinstance(ob, qml.operation.Operator)
-        assert not isinstance(ob, qml.operation.Operation)
-        assert not isinstance(ob, PowOperation)
-
-        # Check some basic observable functionality
-        with pytest.warns(qml.exceptions.PennyLaneDeprecationWarning):
-            assert ob.compare(ob)
-
-        # check the dir
-        assert "grad_recipe" not in dir(ob)
-
 
 @pytest.mark.parametrize("power_method", [Pow, pow_using_dunder_method, qml.pow])
 class TestInitialization:
diff --git a/tests/optimize/test_momentum_qng_qjit.py b/tests/optimize/test_momentum_qng_qjit.py
new file mode 100644
index 000000000..0643de8af
--- /dev/null
+++ b/tests/optimize/test_momentum_qng_qjit.py
@@ -0,0 +1,240 @@
+# Copyright 2018-2025 Xanadu Quantum Technologies Inc.
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+#     http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Test Jax-based Catalyst-compatible Momentum-QNG optimizer"""
+
+from functools import partial
+
+import numpy as np
+import pytest
+
+import pennylane as qml
+
+dev_names = (
+    "default.qubit",
+    "lightning.qubit",
+)
+
+
+def circuit(params):
+    """Simple circuit to use for testing."""
+    qml.RX(params[0], wires=0)
+    qml.RY(params[1], wires=1)
+    return qml.expval(qml.Z(wires=0))
+
+
+class TestBasics:
+    """Test basic properties of the MomentumQNGOptimizerQJIT."""
+
+    def test_initialization_default(self):
+        """Test that initializing MomentumQNGOptimizerQJIT with default values works."""
+        opt = qml.MomentumQNGOptimizerQJIT()
+        assert opt.stepsize == 0.01
+        assert opt.momentum == 0.9
+        assert opt.approx == "block-diag"
+        assert opt.lam == 0
+
+    def test_initialization_custom(self):
+        """Test that initializing MomentumQNGOptimizerQJIT with custom values works."""
+        opt = qml.MomentumQNGOptimizerQJIT(stepsize=0.05, momentum=0.8, approx="diag", lam=1e-9)
+        assert opt.stepsize == 0.05
+        assert opt.momentum == 0.8
+        assert opt.approx == "diag"
+        assert opt.lam == 1e-9
+
+    def test_init_zero_state(self):
+        """Test that the MomentumQNGOptimizerQJIT state is initialized to an array of zeros."""
+        opt = qml.MomentumQNGOptimizerQJIT()
+        params = np.array([[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]])
+        state = opt.init(params)
+        assert np.all(state == np.zeros_like(params))
+
+
+class TestOptimize:
+    """Test basic optimization integration."""
+
+    @pytest.mark.jax
+    @pytest.mark.parametrize("dev_name", dev_names)
+    def test_step_and_cost(self, dev_name):
+        """Test that the step and step_and_cost methods are returning the correct result."""
+        import jax.numpy as jnp
+
+        @qml.qnode(qml.device(dev_name))
+        def circ(params):
+            qml.RX(params[0], wires=0)
+            qml.RY(params[1], wires=0)
+            return qml.expval(qml.PauliZ(0))
+
+        opt = qml.MomentumQNGOptimizerQJIT(stepsize=0.1, momentum=0.4)
+        params = jnp.array([0.11, 0.412])
+        state = jnp.array([[-0.31, 0.842]])
+
+        new_params1, state1 = opt.step(circ, params, state)
+        new_params2, state2, cost = opt.step_and_cost(circ, params, state)
+
+        exp_params = qml.numpy.array(params)
+        exp_state = np.array([-0.31, 0.842])
+        exp_cost = circ(exp_params)
+        exp_mt = np.array([0.25, (np.cos(exp_params[0]) ** 2) / 4])
+        exp_state = opt.momentum * exp_state + opt.stepsize * qml.grad(circ)(exp_params) / exp_mt
+        exp_params -= exp_state
+
+        assert np.allclose(new_params1, exp_params)
+        assert np.allclose(new_params2, exp_params)
+        assert np.allclose(state1, exp_state)
+        assert np.allclose(state2, exp_state)
+        assert np.allclose(cost, exp_cost)
+
+    @pytest.mark.jax
+    @pytest.mark.parametrize("dev_name", dev_names)
+    def test_step_and_cost_with_gen_hamiltonian(self, dev_name):
+        """Test that the step and step_and_cost methods are returning the correct result
+        when the generator of an operator is a Hamiltonian."""
+        import jax.numpy as jnp
+
+        @qml.qnode(qml.device(dev_name, wires=4))
+        def circ(params):
+            qml.DoubleExcitation(params[0], wires=[0, 1, 2, 3])
+            qml.RY(params[1], wires=0)
+            return qml.expval(qml.PauliZ(0))
+
+        opt = qml.MomentumQNGOptimizerQJIT(stepsize=0.1, momentum=0.4)
+        params = jnp.array([0.11, 0.412])
+        state = jnp.array([[-0.31, 0.842]])
+
+        new_params1, state1 = opt.step(circ, params, state)
+        new_params2, state2, cost = opt.step_and_cost(circ, params, state)
+
+        exp_params = qml.numpy.array(params)
+        exp_state = np.array([-0.31, 0.842])
+        exp_cost = circ(exp_params)
+        exp_mt = np.array([1 / 16, 1 / 4])
+        exp_state = opt.momentum * exp_state + opt.stepsize * qml.grad(circ)(exp_params) / exp_mt
+        exp_params -= exp_state
+
+        assert np.allclose(new_params1, exp_params)
+        assert np.allclose(new_params2, exp_params)
+        assert np.allclose(state1, exp_state)
+        assert np.allclose(state2, exp_state)
+        assert np.allclose(cost, exp_cost)
+
+    @pytest.mark.jax
+    @pytest.mark.parametrize("dev_name", dev_names)
+    def test_qubit_rotations_circuit(self, tol, dev_name):
+        """Test that a simple qubit rotations circuit gets optimized correctly, checking params and cost at each step."""
+        import jax.numpy as jnp
+
+        @qml.qnode(qml.device(dev_name))
+        def circ(params):
+            qml.RX(params[0], wires=0)
+            qml.RY(params[1], wires=0)
+            return qml.expval(qml.PauliZ(0))
+
+        def grad(params):
+            """Returns the gradient of the above circuit."""
+            da = -np.sin(params[0]) * np.cos(params[1])
+            db = -np.cos(params[0]) * np.sin(params[1])
+            return np.array([da, db])
+
+        opt = qml.MomentumQNGOptimizerQJIT(stepsize=0.2, momentum=0.3)
+        params = jnp.array([0.011, 0.012])
+        state = opt.init(params)
+
+        exp_params = np.array([0.011, 0.012])
+        exp_state = np.zeros_like(exp_params)
+
+        num_steps = 30
+        for _ in range(num_steps):
+            params, state, cost = opt.step_and_cost(circ, params, state)
+
+            exp_cost = circ(exp_params)
+            exp_mt = np.array([0.25, (np.cos(exp_params[0]) ** 2) / 4])
+            exp_state = opt.momentum * exp_state + opt.stepsize * grad(exp_params) / exp_mt
+            exp_params -= exp_state
+
+            assert np.allclose(cost, exp_cost, atol=tol, rtol=0)
+            assert np.allclose(state, exp_state, atol=tol, rtol=0)
+            assert np.allclose(params, exp_params, atol=tol, rtol=0)
+
+        assert np.allclose(circ(params), -1)
+
+    @pytest.mark.jax
+    def test_jit(self):
+        """Test optimizer compatibility with jax.jit compilation."""
+        import jax
+        import jax.numpy as jnp
+
+        device = qml.device("default.qubit", wires=2)
+        qnode = qml.QNode(circuit, device=device)
+
+        opt = qml.MomentumQNGOptimizerQJIT()
+        params = jnp.array([0.1, 0.2])
+        state = opt.init(params)
+
+        new_params1, state1 = opt.step(qnode, params, state)
+        new_params2, state2, cost = opt.step_and_cost(qnode, params, state)
+
+        step = jax.jit(partial(opt.step, qnode))
+        step_and_cost = jax.jit(partial(opt.step_and_cost, qnode))
+        new_params1_jit, state1_jit = step(params, state)
+        new_params2_jit, state2_jit, cost_jit = step_and_cost(params, state)
+
+        # check params have been updated
+        assert not np.allclose(params, new_params1)
+        assert not np.allclose(state, state1)
+        assert not np.allclose(params, new_params2)
+        assert not np.allclose(state, state2)
+
+        # check jitted results match
+        assert np.allclose(new_params1, new_params1_jit)
+        assert np.allclose(state1, state1_jit)
+        assert np.allclose(new_params2, new_params2_jit)
+        assert np.allclose(state2, state2_jit)
+        assert np.allclose(cost, cost_jit)
+
+    @pytest.mark.jax
+    @pytest.mark.catalyst
+    @pytest.mark.external
+    def test_qjit(self):
+        """Test optimizer compatibility with qml.qjit compilation."""
+        import jax.numpy as jnp
+
+        pytest.importorskip("catalyst")
+
+        device = qml.device("lightning.qubit", wires=2)
+        qnode = qml.QNode(circuit, device=device)
+
+        opt = qml.MomentumQNGOptimizerQJIT()
+        params = jnp.array([0.1, 0.2])
+        state = opt.init(params)
+
+        new_params1, state1 = opt.step(qnode, params, state)
+        new_params2, state2, cost = opt.step_and_cost(qnode, params, state)
+
+        step = qml.qjit(partial(opt.step, qnode))
+        step_and_cost = qml.qjit(partial(opt.step_and_cost, qnode))
+        new_params1_qjit, state1_qjit = step(params, state)
+        new_params2_qjit, state2_qjit, cost_qjit = step_and_cost(params, state)
+
+        # check params and state have been updated
+        assert not np.allclose(params, new_params1)
+        assert not np.allclose(state, state1)
+        assert not np.allclose(params, new_params2)
+        assert not np.allclose(state, state2)
+
+        # check qjitted results match
+        assert np.allclose(new_params1, new_params1_qjit)
+        assert np.allclose(state1, state1_qjit)
+        assert np.allclose(new_params2, new_params2_qjit)
+        assert np.allclose(state2, state2_qjit)
+        assert np.allclose(cost, cost_qjit)
diff --git a/tests/test_operation.py b/tests/test_operation.py
index d4b2f665a..790b160d2 100644
--- a/tests/test_operation.py
+++ b/tests/test_operation.py
@@ -938,42 +938,17 @@ class TestObservableConstruction:
     def test_construction_with_wires_pos_arg(self):
         """Test that the wires can be given as a positional argument"""
 
-        with pytest.warns(qml.exceptions.PennyLaneDeprecationWarning):
+        class DummyObserv(qml.operation.Operator):
+            r"""Dummy custom observable"""
 
-            class DummyObserv(qml.operation.Observable):
-                r"""Dummy custom observable"""
+            _queue_category = None
 
-                num_wires = 1
-                grad_method = None
+            num_wires = 1
+            grad_method = None
 
         ob = DummyObserv([1])
         assert ob.wires == qml.wires.Wires(1)
 
-    def test_observable_is_not_operation_but_operator(self):
-        """Check that the Observable class inherits from an Operator, not from an Operation"""
-
-        with pytest.warns(qml.exceptions.PennyLaneDeprecationWarning):
-            assert issubclass(qml.operation.Observable, qml.operation.Operator)
-
-        with pytest.warns(qml.exceptions.PennyLaneDeprecationWarning):
-            assert not issubclass(qml.operation.Observable, qml.operation.Operation)
-
-    def test_observable_is_operation_as_well(self):
-        """Check that the Observable class inherits from an Operator class as well"""
-
-        with pytest.warns(qml.exceptions.PennyLaneDeprecationWarning):
-
-            class DummyObserv(qml.operation.Observable, qml.operation.Operation):
-                r"""Dummy custom observable"""
-
-                num_wires = 1
-                grad_method = None
-
-        assert issubclass(DummyObserv, qml.operation.Operator)
-        with pytest.warns(qml.exceptions.PennyLaneDeprecationWarning):
-            assert issubclass(DummyObserv, qml.operation.Observable)
-        assert issubclass(DummyObserv, qml.operation.Operation)
-
     def test_tensor_n_multiple_modes(self):
         """Checks that the TensorN operator was constructed correctly when
         multiple modes were specified."""
@@ -1027,13 +1002,11 @@ class TestObservableConstruction:
     def test_id(self):
         """Test that the id attribute of an observable can be set."""
 
-        with pytest.warns(qml.exceptions.PennyLaneDeprecationWarning):
-
-            class DummyObserv(qml.operation.Observable):
-                r"""Dummy custom observable"""
+        class DummyObserv(qml.operation.Operator):
+            r"""Dummy custom observable"""
 
-                num_wires = 1
-                grad_method = None
+            num_wires = 1
+            grad_method = None
 
         op = DummyObserv(1.0, wires=0, id="test")
         assert op.id == "test"
@@ -1041,10 +1014,8 @@ class TestObservableConstruction:
     def test_raises_if_no_wire_is_given(self):
         """Test that an error is raised if no wire is passed at initialization."""
 
-        with pytest.warns(qml.exceptions.PennyLaneDeprecationWarning):
-
-            class DummyObservable(qml.operation.Observable):
-                num_wires = 1
+        class DummyObservable(qml.operation.Operator):
+            num_wires = 1
 
         with pytest.raises(Exception, match="Must specify the wires *"):
             DummyObservable()
@@ -1052,16 +1023,14 @@ class TestObservableConstruction:
     def test_is_hermitian(self):
         """Test that the id attribute of an observable can be set."""
 
-        with pytest.warns(qml.exceptions.PennyLaneDeprecationWarning):
-
-            class DummyObserv(qml.operation.Observable):
-                r"""Dummy custom observable"""
+        class DummyObserv(qml.operation.Operator):
+            r"""Dummy custom observable"""
 
-                num_wires = 1
-                grad_method = None
+            num_wires = 1
+            grad_method = None
 
         op = DummyObserv(wires=0)
-        assert op.is_hermitian is True
+        assert op.is_hermitian is False
 
 
 class TestOperatorIntegration:
diff --git a/tests/transforms/test_zx.py b/tests/transforms/test_zx.py
index 733cbc0f9..c8a6f82db 100644
--- a/tests/transforms/test_zx.py
+++ b/tests/transforms/test_zx.py
@@ -26,31 +26,30 @@ from pennylane.tape import QuantumScript
 from pennylane.transforms import TransformError
 
 pyzx = pytest.importorskip("pyzx")
-
 pytestmark = pytest.mark.external
-
 supported_operations = [
-    qml.PauliX(wires=0),
-    qml.PauliZ(wires=0),
-    qml.Hadamard(wires=0),
+    qml.X(wires=0),
+    qml.Y(wires=0),
+    qml.Z(wires=0),
     qml.S(wires=0),
     qml.T(wires=0),
+    qml.Hadamard(wires=0),
     qml.SWAP(wires=[0, 1]),
     qml.CNOT(wires=[0, 1]),
+    qml.CY(wires=[0, 1]),
     qml.CZ(wires=[0, 1]),
     qml.CH(wires=[0, 1]),
-]
-
-supported_operations_params = [
     qml.RX(0.3, wires=0),
+    qml.RY(0.3, wires=0),
     qml.RZ(0.3, wires=0),
+    qml.PhaseShift(0.3, wires=0),
+    qml.CRX(0.3, wires=[0, 1]),
+    qml.CRY(0.3, wires=[0, 1]),
     qml.CRZ(0.3, wires=[0, 1]),
+    qml.Toffoli(wires=[0, 1, 2]),
+    qml.CCZ(wires=[0, 1, 2]),
 ]
 
-expanded_operations = [qml.PauliY(wires=0), qml.PhaseShift(0.3, wires=0), qml.RY(0.3, wires=0)]
-
-non_diagram_like_operations = [qml.CCZ(wires=[0, 1, 2]), qml.Toffoli(wires=[0, 1, 2])]
-
 decompose_phases = [True, False]
 qscript = [True, False]
 
@@ -81,7 +80,7 @@ class TestConvertersZX:
 
     @pytest.mark.parametrize("script", qscript)
     @pytest.mark.parametrize("operation", supported_operations)
-    def test_supported_operation_no_params(self, operation, script):
+    def test_supported_operations(self, operation, script):
         """Test to convert the script to a ZX graph and back for supported operations."""
 
         I = qml.math.eye(2 ** len(operation.wires))
@@ -118,109 +117,11 @@ class TestConvertersZX:
 
         assert qml.math.allclose(mat_product, I)
 
-    @pytest.mark.parametrize("script", qscript)
-    @pytest.mark.parametrize("operation", supported_operations_params)
-    def test_supported_operation_params(self, operation, script):
-        """Test to convert the script to a ZX graph and back for supported operations with parameters."""
-
-        if script:
-            qs = QuantumScript([operation])
-        else:
-            qs = operation
-
-        I = qml.math.eye(2 ** len(operation.wires))
-
-        matrix_qscript = qml.matrix(qs, wire_order=qs.wires)
-
-        zx_g = qml.transforms.to_zx(qs)
-
-        matrix_zx = zx_g.to_matrix()
-
-        assert isinstance(zx_g, pyzx.graph.graph_s.GraphS)
-        # Check whether the two matrices are each others conjugate transposes
-        mat_product = qml.math.dot(matrix_qscript, qml.math.conj(matrix_zx.T))
-        # Remove global phase
-        mat_product /= mat_product[0, 0]
-
-        assert qml.math.allclose(mat_product, I)
-
-        qscript_back = qml.transforms.from_zx(zx_g)
-        assert isinstance(qscript_back, qml.tape.QuantumScript)
-
-        matrix_qscript_back = qml.matrix(qscript_back, wire_order=list(range(len(qs.wires))))
-
-        # Check whether the two matrices are each others conjugate transposes
-        mat_product = qml.math.dot(matrix_qscript, qml.math.conj(matrix_qscript_back.T))
-        # Remove global phase
-        mat_product /= mat_product[0, 0]
-
-        assert qml.math.allclose(mat_product, I)
-
-    @pytest.mark.parametrize("script", qscript)
-    @pytest.mark.parametrize("operation", expanded_operations)
-    def test_operation_need_expansion(self, operation, script):
-        """Test to convert the script to a ZX graph and back for operations that needs expansions."""
-
-        if script:
-            qs = QuantumScript([operation])
-        else:
-            qs = operation
-
-        I = qml.math.eye(2 ** len(operation.wires))
-
-        matrix_qscript = qml.matrix(qs, wire_order=qs.wires)
-
-        zx_g = qml.transforms.to_zx(qs)
-
-        matrix_zx = zx_g.to_matrix()
-
-        assert isinstance(zx_g, pyzx.graph.graph_s.GraphS)
-        # Check whether the two matrices are each others conjugate transposes
-        mat_product = qml.math.dot(matrix_qscript, qml.math.conj(matrix_zx.T))
-        # Remove global phase
-        mat_product /= mat_product[0, 0]
-
-        assert qml.math.allclose(mat_product, I)
-
-        qscript_back = qml.transforms.from_zx(zx_g)
-        assert isinstance(qscript_back, qml.tape.QuantumScript)
-
-        matrix_qscript_back = qml.matrix(qscript_back, wire_order=list(range(len(qs.wires))))
-
-        # Check whether the two matrices are each others conjugate transposes
-        mat_product = qml.math.dot(matrix_qscript, qml.math.conj(matrix_qscript_back.T))
-        # Remove global phase
-        mat_product /= mat_product[0, 0]
-
-        assert qml.math.allclose(mat_product, I)
-
-    @pytest.mark.parametrize("operation", non_diagram_like_operations)
-    def test_non_diagram_like_op(self, operation):
-        """Test operations that result in a non diagram like circuit."""
-
-        I = qml.math.eye(2 ** len(operation.wires))
-
-        qs = QuantumScript([operation], [])
-        matrix_qscript = qml.matrix(qs, wire_order=qs.wires)
-
-        zx_g = qml.transforms.to_zx(qs)
-        assert isinstance(zx_g, pyzx.graph.graph_s.GraphS)
-
-        matrix_zx = zx_g.to_matrix()
-        # Check whether the two matrices are each others conjugate transposes
-        mat_product = qml.math.dot(matrix_qscript, qml.math.conj(matrix_zx.T))
-        # Remove global phase
-        mat_product /= mat_product[0, 0]
-        assert qml.math.allclose(mat_product, I)
-
-        with pytest.raises(QuantumFunctionError, match="Graph doesn't seem circuit like"):
-            qml.transforms.from_zx(zx_g)
-
     @pytest.mark.parametrize("decompose", decompose_phases)
     def test_circuit(self, decompose):
         """Test a simple circuit."""
 
-        I = qml.math.eye(2**2)
+        I = qml.math.eye(2**3)
 
         operations = [
             qml.RZ(5 / 4 * np.pi, wires=0),
@@ -234,6 +135,8 @@ class TestConvertersZX:
             qml.CNOT(wires=[0, 1]),
             qml.CNOT(wires=[1, 0]),
             qml.SWAP(wires=[0, 1]),
+            qml.Toffoli(wires=[0, 1, 2]),
+            qml.CCZ(wires=[0, 1, 2]),
         ]
 
         qs = QuantumScript(operations, [])