- 
                Notifications
    You must be signed in to change notification settings 
- Fork 706
RZ Phase gradient method transform #8213
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
          
     Merged
      
      
    
  
     Merged
                    Changes from all commits
      Commits
    
    
            Show all changes
          
          
            47 commits
          
        
        Select commit
          Hold shift + click to select a range
      
      7fed855
              
                move intermediate reps to mainline pennylane
              
              
                Qottmann 0a847a1
              
                Merge branch 'master' of https://github.com/PennyLaneAI/pennylane
              
              
                Qottmann 7c4179c
              
                RZ Phrase Gradient transform
              
              
                Qottmann 028407b
              
                RZ Phrase Gradient transform
              
              
                Qottmann 50d637f
              
                tests
              
              
                Qottmann c79180b
              
                fix global phase
              
              
                Qottmann fcf3284
              
                be a bit more clever about global phases
              
              
                Qottmann 7c6c9d4
              
                Merge branch 'master' of https://github.com/PennyLaneAI/pennylane int…
              
              
                Qottmann 193df79
              
                remove unrelated things
              
              
                Qottmann 5ed02f6
              
                docs
              
              
                Qottmann 790b611
              
                global phase
              
              
                Qottmann 50dcd72
              
                global phase
              
              
                Qottmann de14a67
              
                move queueing manager
              
              
                Qottmann 43339b5
              
                Apply suggestions from code review
              
              
                Qottmann 556473e
              
                update
              
              
                Qottmann 241494c
              
                Merge branch 'phase-gradient' of https://github.com/PennyLaneAI/penny…
              
              
                Qottmann 319ca1a
              
                Merge branch 'master' of https://github.com/PennyLaneAI/pennylane int…
              
              
                Qottmann a64700c
              
                run hooks
              
              
                Qottmann 377e480
              
                Merge branch 'master' of https://github.com/PennyLaneAI/pennylane int…
              
              
                Qottmann c626561
              
                docs
              
              
                Qottmann 10f6c3d
              
                Merge branch 'master' of https://github.com/PennyLaneAI/pennylane int…
              
              
                Qottmann d6d6155
              
                explain bits
              
              
                Qottmann 0631b8b
              
                Merge branch 'master' of https://github.com/PennyLaneAI/pennylane int…
              
              
                Qottmann 2ddd4a0
              
                docs
              
              
                Qottmann f42074d
              
                docs
              
              
                Qottmann 0585502
              
                docs
              
              
                Qottmann 4f687c9
              
                docs
              
              
                Qottmann 88a24ac
              
                docs
              
              
                Qottmann a67ad30
              
                make it change-op-basis
              
              
                Qottmann acbbb60
              
                Merge branch 'master' of https://github.com/PennyLaneAI/pennylane int…
              
              
                Qottmann adbffd3
              
                angle_wires
              
              
                Qottmann b606f9a
              
                unify global phases
              
              
                Qottmann 2f8ffcf
              
                validate wires
              
              
                Qottmann 6db4f1a
              
                Apply suggestions from code review
              
              
                Qottmann 6d36354
              
                Merge branch 'master' into phase-gradient
              
              
                Qottmann e033a33
              
                docs
              
              
                Qottmann 3206cf3
              
                angilary lol
              
              
                Qottmann a2c9a17
              
                an is actually correct
              
              
                Qottmann 2e67e05
              
                code review docs
              
              
                Qottmann 2117ada
              
                Merge branch 'master' of https://github.com/PennyLaneAI/pennylane int…
              
              
                Qottmann 49ffe1a
              
                changelog
              
              
                Qottmann 9a4bff6
              
                trigger
              
              
                Qottmann d401798
              
                Merge branch 'master' of https://github.com/PennyLaneAI/pennylane int…
              
              
                Qottmann 8533e7e
              
                Apply suggestions from code review
              
              
                Qottmann 2b9a9e0
              
                code review
              
              
                Qottmann b4f6bf0
              
                Merge branch 'phase-gradient' of https://github.com/PennyLaneAI/penny…
              
              
                Qottmann e1234f5
              
                Merge branch 'master' into phase-gradient
              
              
                Qottmann File filter
Filter by extension
Conversations
          Failed to load comments.   
        
        
          
      Loading
        
  Jump to
        
          Jump to file
        
      
      
          Failed to load files.   
        
        
          
      Loading
        
  Diff view
Diff view
There are no files selected for viewing
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,215 @@ | ||
| # 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. | ||
| """ | ||
| A transform for decomposing RZ rotations using a phase gradient catalyst state. | ||
| """ | ||
| import numpy as np | ||
|  | ||
| import pennylane as qml | ||
| from pennylane.queuing import QueuingManager | ||
| from pennylane.tape import QuantumScript, QuantumScriptBatch | ||
| from pennylane.transforms import transform | ||
| from pennylane.typing import PostprocessingFn | ||
| from pennylane.wires import Wires | ||
|  | ||
|  | ||
| def _binary_repr_int(phi, precision): | ||
| # Reasoning for +1e-10 term: | ||
| # due to the division by pi, we obtain 14.999.. instead of 15 for, e.g., (1, 1, 1, 1) pi | ||
| # at the same time, we want to floor off any additional floats when converting to the desired precision, | ||
| # e.g. representing (1, 1, 1, 1) with only 3 digits we want to obtain (1, 1, 1) | ||
| # so overall we floor but make sure we add a little term to not accidentally write 14 when the result is 14.999.. | ||
| return int(np.floor(2**precision * phi / (2 * np.pi) + 1e-10)) | ||
|  | ||
|  | ||
| @QueuingManager.stop_recording() | ||
| def _rz_phase_gradient( | ||
| phi: float, wire: Wires, angle_wires: Wires, phase_grad_wires: Wires, work_wires: Wires | ||
|         
                  Qottmann marked this conversation as resolved.
              Show resolved
            Hide resolved | ||
| ) -> tuple[QuantumScriptBatch, PostprocessingFn]: | ||
| """Function that transforms the RZ gate to the phase gradient circuit | ||
| The precision is implicitly defined by the length of ``angle_wires`` | ||
| Note that the global phases are collected and added as one big global phase in the main function | ||
| """ | ||
|  | ||
| precision = len(angle_wires) | ||
| # BasisEmbedding can handle integer inputs, no need to actually translate to binary | ||
| binary_int = _binary_repr_int(-phi, precision) | ||
|  | ||
| compute_op = qml.ctrl(qml.BasisEmbedding(features=binary_int, wires=angle_wires), control=wire) | ||
| target_op = qml.SemiAdder(angle_wires, phase_grad_wires, work_wires) | ||
|  | ||
| return qml.change_op_basis(compute_op, target_op, compute_op) | ||
|         
                  Qottmann marked this conversation as resolved.
              Show resolved
            Hide resolved | ||
|  | ||
|  | ||
| @transform | ||
| def rz_phase_gradient( | ||
|         
                  Qottmann marked this conversation as resolved.
              Show resolved
            Hide resolved | ||
| tape: QuantumScript, angle_wires: Wires, phase_grad_wires: Wires, work_wires: Wires | ||
| ) -> tuple[QuantumScriptBatch, PostprocessingFn]: | ||
| r"""Quantum function transform to decompose all instances of :class:`~.RZ` gates into additions | ||
|         
                  Qottmann marked this conversation as resolved.
              Show resolved
            Hide resolved | ||
| using a phase gradient resource state. | ||
|  | ||
| For example, an :class:`~.RZ` gate with angle :math:`\phi = (0 \cdot 2^{-1} + 1 \cdot 2^{-2} + 0 \cdot 2^{-3}) 2\pi` | ||
| is translated into the following routine, where the angle is conditionally prepared on the ``angle_wires`` in binary | ||
| and added to a ``phase_grad_wires`` register semi-inplace via :class:`~.SemiAdder`. | ||
|  | ||
| .. code-block:: | ||
|  | ||
| target: ─RZ(ϕ)─ = ────╭●──────────────╭●────exp(iϕ/2)─┤ | ||
| ang_0: ────├|0⟩─╭SemiAdder─├|0⟩────────────┤ | ||
| ang_1: ────├|1⟩─├SemiAdder─├|1⟩────────────┤ | ||
| ang_2: ────╰|0⟩─├SemiAdder─╰|0⟩────────────┤ | ||
| phg_0: ─────────├SemiAdder─────────────────┤ | ||
| phg_1: ─────────├SemiAdder─────────────────┤ | ||
| phg_2: ─────────╰SemiAdder─────────────────┤ | ||
|  | ||
| For this routine to work, the provided ``phase_grad_wires`` need to hold a phase gradient | ||
| state :math:`|\nabla Z\rangle = \frac{1}{\sqrt{2^n}} \sum_{m=0}^{2^n-1} e^{2 \pi i \frac{m}{2^n}} |m\rangle`. | ||
| Because this state is not modified and can be re-used at a later stage, the transform does not prepare it but | ||
| rather assumes it has been prepared on those wires at an earlier stage. | ||
|  | ||
|  | ||
| Note that :class:`~.SemiAdder` requires additional ``work_wires`` (not shown in the diagram) for the semi-in-place addition | ||
| :math:`\text{SemiAdder}|x\rangle_\text{ang} |y\rangle_\text{phg} = |x\rangle_\text{ang} |x + y\rangle_\text{phg}`. | ||
|  | ||
| More details can be found on page 4 in `arXiv:1709.06648 <https://arxiv.org/abs/1709.06648>`__ | ||
| and Figure 17a in `arXiv:2211.15465 <https://arxiv.org/abs/2211.15465>`__ (a generalization to | ||
| multiplexed :class:`~.RZ` rotations is provided in Figure 4 in | ||
| `arXiv:2409.07332 <https://arxiv.org/abs/2409.07332>`__). | ||
|  | ||
| Note that technically, this circuit realizes :class:`~.PhaseShift`, i.e. :math:`R_\phi(\phi) = R_Z(\phi) e^{i\phi/2}`. | ||
| The additional global phase is taken into account in the decomposition. | ||
|  | ||
| Args: | ||
| tape (QNode or QuantumTape or Callable): A quantum circuit containing :class:`~.RZ` gates. | ||
| angle_wires (Wires): The qubits that conditionally load the angle :math:`\phi` of | ||
| the :class:`~.RZ` gate in binary as a multiple of :math:`2\pi`. | ||
| The length of the ``angle_wires`` implicitly determines the precision | ||
| with which the angle is represented. | ||
| E.g., :math:`(2^{-1} + 2^{-2} + 2^{-3}) 2\pi` is exactly represented by three bits as ``111``. | ||
| phase_grad_wires (Wires): The catalyst qubits with a phase gradient state prepared on them. | ||
| Needs to be at least the length of ``angle_wires`` and will only | ||
| use the first ``len(angle_wires)``. | ||
| work_wires (Wires): Additional work wires to realize the :class:`~.SemiAdder` between the ``angle_wires`` and | ||
| ``phase_grad_wires``. Needs to be at least ``b-1`` wires, where ``b=len(phase_grad_wires)`` is | ||
| the precision of the angle :math:`\phi`. | ||
|  | ||
| Returns: | ||
| qnode (QNode) or quantum function (Callable) or tuple[List[QuantumTape], function]: The transformed circuit as described in :func:`qml.transform <pennylane.transform>`. | ||
|  | ||
| **Example** | ||
|  | ||
| .. code-block:: python | ||
|  | ||
| from functools import partial | ||
|  | ||
| import numpy as np | ||
|  | ||
| import pennylane as qml | ||
| from pennylane.transforms.rz_phase_gradient import rz_phase_gradient | ||
|  | ||
| precision = 3 | ||
| phi = (1 / 2 + 1 / 4 + 1 / 8) * 2 * np.pi | ||
| wire = "targ" | ||
| angle_wires = [f"ang_{i}" for i in range(precision)] | ||
| phase_grad_wires = [f"phg_{i}" for i in range(precision)] | ||
| work_wires = [f"work_{i}" for i in range(precision - 1)] | ||
| wire_order = [wire] + angle_wires + phase_grad_wires + work_wires | ||
|  | ||
|  | ||
| def phase_gradient(wires): | ||
| # prepare phase gradient state | ||
| qml.X(wires[-1]) | ||
| qml.QFT(wires) | ||
|  | ||
|  | ||
| @partial( | ||
| rz_phase_gradient, | ||
| angle_wires=angle_wires, | ||
| phase_grad_wires=phase_grad_wires, | ||
| work_wires=work_wires, | ||
| ) | ||
| @qml.qnode(qml.device("default.qubit")) | ||
| def rz_circ(phi, wire): | ||
|         
                  Qottmann marked this conversation as resolved.
              Show resolved
            Hide resolved | ||
| phase_gradient(phase_grad_wires) # prepare phase gradient state | ||
|  | ||
| qml.Hadamard(wire) # transform rotation | ||
| qml.RZ(phi, wire) | ||
| qml.Hadamard(wire) # transform rotation | ||
|  | ||
| return qml.probs(wire) | ||
|  | ||
|  | ||
| In this example we perform the rotation of an angle of :math:`\phi = (0.111)_2 2\pi`. Because phase shifts | ||
| are trivial on computational basis states, we transform the :math:`R_Z` rotation to :math:`R_X = H R_Z H` via two | ||
| :class:`~.Hadamard` gates. | ||
|  | ||
| Note that for the transform to work, we need to also prepare a phase gradient state on the ``phase_grad_wires``. | ||
|  | ||
| Overall, the full circuit looks like the following: | ||
|  | ||
| >>> print(qml.draw(rz_circ, wire_order=wire_order)(phi, wire)) | ||
|         
                  Qottmann marked this conversation as resolved.
              Show resolved
            Hide resolved | ||
| targ: ──H──────╭(|Ψ⟩)@SemiAdder@(|Ψ⟩)──H─╭GlobalPhase(2.75)─┤ Probs | ||
| ang_0: ─────────├(|Ψ⟩)@SemiAdder@(|Ψ⟩)────├GlobalPhase(2.75)─┤ | ||
| ang_1: ─────────├(|Ψ⟩)@SemiAdder@(|Ψ⟩)────├GlobalPhase(2.75)─┤ | ||
| ang_2: ─────────├(|Ψ⟩)@SemiAdder@(|Ψ⟩)────├GlobalPhase(2.75)─┤ | ||
| phg_0: ────╭QFT─├(|Ψ⟩)@SemiAdder@(|Ψ⟩)────├GlobalPhase(2.75)─┤ | ||
| phg_1: ────├QFT─├(|Ψ⟩)@SemiAdder@(|Ψ⟩)────├GlobalPhase(2.75)─┤ | ||
| phg_2: ──X─╰QFT─├(|Ψ⟩)@SemiAdder@(|Ψ⟩)────├GlobalPhase(2.75)─┤ | ||
| work_0: ─────────├(|Ψ⟩)@SemiAdder@(|Ψ⟩)────├GlobalPhase(2.75)─┤ | ||
| work_1: ─────────╰(|Ψ⟩)@SemiAdder@(|Ψ⟩)────╰GlobalPhase(2.75)─┤ | ||
|  | ||
| The additional work wires are required by the :class:`~.SemiAdder`. | ||
| Executing the circuit, we get the expected result: | ||
|  | ||
| >>> rz_circ(phi, wire) | ||
| array([0.85355339, 0.14644661]) | ||
|  | ||
| """ | ||
|  | ||
| if len(phase_grad_wires) < len(angle_wires): | ||
| raise ValueError( | ||
| f"phase_grad_wires needs to be at least as large as angle_wires. Got {len(phase_grad_wires)} phase_grad_wires, which is fewer than the {len(angle_wires)} angle wires." | ||
| ) | ||
|  | ||
| operations = [] | ||
| global_phases = [] | ||
| for op in tape.operations: | ||
| if isinstance(op, qml.RZ): | ||
| wire = op.wires | ||
| phi = op.parameters[0] | ||
| global_phases.append(phi / 2) | ||
|  | ||
| operations.append( | ||
| _rz_phase_gradient( | ||
|         
                  Qottmann marked this conversation as resolved.
              Show resolved
            Hide resolved | ||
| phi, | ||
| wire, | ||
| angle_wires=angle_wires, | ||
| phase_grad_wires=phase_grad_wires, | ||
| work_wires=work_wires, | ||
| ) | ||
| ) | ||
| else: | ||
| operations.append(op) | ||
|  | ||
| operations.append(qml.GlobalPhase(sum(global_phases))) | ||
|  | ||
| new_tape = tape.copy(operations=operations) | ||
|  | ||
| def null_postprocessing(results): | ||
| """A postprocesing function returned by a transform that only converts the batch of results | ||
| into a result for a single ``QuantumTape``. | ||
| """ | ||
| return results[0] | ||
|  | ||
| return [new_tape], null_postprocessing | ||
      
      Oops, something went wrong.
        
    
  
  Add this suggestion to a batch that can be applied as a single commit.
  This suggestion is invalid because no changes were made to the code.
  Suggestions cannot be applied while the pull request is closed.
  Suggestions cannot be applied while viewing a subset of changes.
  Only one suggestion per line can be applied in a batch.
  Add this suggestion to a batch that can be applied as a single commit.
  Applying suggestions on deleted lines is not supported.
  You must change the existing code in this line in order to create a valid suggestion.
  Outdated suggestions cannot be applied.
  This suggestion has been applied or marked resolved.
  Suggestions cannot be applied from pending reviews.
  Suggestions cannot be applied on multi-line comments.
  Suggestions cannot be applied while the pull request is queued to merge.
  Suggestion cannot be applied right now. Please check back later.
  
    
  
    
Uh oh!
There was an error while loading. Please reload this page.