Skip to content
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

An extended RZZ pass for unbounded parameters #2072

Draft
wants to merge 19 commits into
base: main
Choose a base branch
from
Draft

Conversation

yaelbh
Copy link
Collaborator

@yaelbh yaelbh commented Dec 4, 2024

Closes #2032.
The transpiler pass of #2043 is extended to accommodate unbounded parameters, including parameter expressions.
Many thanks to Liran Shirizly for the idea.
The tests are currently failing - I'm investigating - opening in draft mode.

@yaelbh yaelbh marked this pull request as draft December 4, 2024 15:15
@yaelbh yaelbh marked this pull request as ready for review December 5, 2024 18:15
@yaelbh
Copy link
Collaborator Author

yaelbh commented Dec 5, 2024

All the bugs have been fixed, it's not in draft mode anymore but ready for review.

@yaelbh yaelbh requested a review from wshanks December 5, 2024 18:23
@yaelbh
Copy link
Collaborator Author

yaelbh commented Dec 6, 2024

Is there a risk that the pass will run again and again, forever?
Converting back to draft until this question is clarified.

@yaelbh yaelbh marked this pull request as draft December 6, 2024 05:11
Copy link
Collaborator

@wshanks wshanks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very clever, Yael and Liran.

I think the concern about running forever is coming from the use of the modified variable in a loop? That variable is just used to determine whether to reuse the original data or new data and not to restart the loop, so I think it is okay.

I have a couple concerns about this PR, but they may not be issues:

  1. Should we be concerned about the blowing up in size of the input circuit?

    The simple circuit with a single rzz with angle p goes from having a data attribute like:

    [CircuitInstruction(operation=Instruction(name='rzz', num_qubits=2, num_clbits=0, params=[Parameter(p)]), qubits=(Qubit(QuantumRegister(2, 'q'), 0), Qubit(QuantumRegister(2, 'q'), 1)), clbits=())]

    To one like:

    [CircuitInstruction(operation=Instruction(name='global_phase', num_qubits=0, num_clbits=0, params=[ParameterExpression(1.5707963267949*(1 - sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5)))*((sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) + 1.5707963267949) + 1)*sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) + 1.5707963267949)**2/2 - sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) + 1.5707963267949)**2 + 1)*sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5))**2 + 1.5707963267949*(sign(-p + 12.5663706143592*floor(0.0795774715459477*p) + 9.42477796076938) + 1)*((sign(p - 12.5663706143592*floor(0.0795774715459477*p) - 3.14159265358979) + 1)*sign(p - 12.5663706143592*floor(0.0795774715459477*p) - 3.14159265358979)**2/2 - sign(p - 12.5663706143592*floor(0.0795774715459477*p) - 3.14159265358979)**2 + 1)*sign(-p + 12.5663706143592*floor(0.0795774715459477*p) + 9.42477796076938)**2 - 0.785398163397448*(sign(-p + 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949) + 1)*((sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979) + 1)*sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979)**2/2 - sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979)**2 + 1)*sign(-p + 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949)**2 - 0.785398163397448*(sign(-p + 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979) + 1)*((sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949) + 1)*sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949)**2/2 - sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949)**2 + 1)*sign(-p + 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979)**2)]), qubits=(), clbits=()), CircuitInstruction(operation=Instruction(name='rz', num_qubits=1, num_clbits=0, params=[ParameterExpression(1.5707963267949*(sign(-p + 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949) + 1)*((sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979) + 1)*sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979)**2/2 - sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979)**2 + 1)*sign(-p + 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949)**2 + 1.5707963267949*(sign(-p + 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979) + 1)*((sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949) + 1)*sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949)**2/2 - sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949)**2 + 1)*sign(-p + 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979)**2)]), qubits=(Qubit(QuantumRegister(2, 'q'), 0),), clbits=()), CircuitInstruction(operation=Instruction(name='rx', num_qubits=1, num_clbits=0, params=[ParameterExpression(1.5707963267949*(1 - sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5)))*((sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) + 1.5707963267949) + 1)*sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) + 1.5707963267949)**2/2 - sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) + 1.5707963267949)**2 + 1)*sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5))**2 + 1.5707963267949*(sign(-p + 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979) + 1)*((sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949) + 1)*sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949)**2/2 - sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949)**2 + 1)*sign(-p + 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979)**2)]), qubits=(Qubit(QuantumRegister(2, 'q'), 0),), clbits=()), CircuitInstruction(operation=Instruction(name='rz', num_qubits=1, num_clbits=0, params=[ParameterExpression(1.5707963267949*(sign(-p + 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949) + 1)*((sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979) + 1)*sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979)**2/2 - sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979)**2 + 1)*sign(-p + 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949)**2 + 1.5707963267949*(sign(-p + 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979) + 1)*((sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949) + 1)*sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949)**2/2 - sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949)**2 + 1)*sign(-p + 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979)**2)]), qubits=(Qubit(QuantumRegister(2, 'q'), 1),), clbits=()), CircuitInstruction(operation=Instruction(name='rzz', num_qubits=2, num_clbits=0, params=[ParameterExpression(-(1 - sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5)))*(p - 6.28318530717959*floor(0.159154943091895*p + 0.5))*((sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) + 1.5707963267949) + 1)*sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) + 1.5707963267949)**2/2 - sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) + 1.5707963267949)**2 + 1)*sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5))**2/2 + (p - 6.28318530717959*floor(0.159154943091895*p + 0.5))*(sign(-p + 6.28318530717959*floor(0.159154943091895*p + 0.5) + 1.5707963267949) + 1)*((sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5)) + 1)*sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5))**2/2 - sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5))**2 + 1)*sign(-p + 6.28318530717959*floor(0.159154943091895*p + 0.5) + 1.5707963267949)**2/2 + (sign(-p + 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949) + 1)*(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979)*((sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979) + 1)*sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979)**2/2 - sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979)**2 + 1)*sign(-p + 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949)**2/2 + (sign(-p + 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979) + 1)*(-p + 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979)*((sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949) + 1)*sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949)**2/2 - sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949)**2 + 1)*sign(-p + 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979)**2/2)]), qubits=(Qubit(QuantumRegister(2, 'q'), 0), Qubit(QuantumRegister(2, 'q'), 1)), clbits=()), CircuitInstruction(operation=Instruction(name='rx', num_qubits=1, num_clbits=0, params=[ParameterExpression(1.5707963267949*(1 - sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5)))*((sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) + 1.5707963267949) + 1)*sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) + 1.5707963267949)**2/2 - sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) + 1.5707963267949)**2 + 1)*sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5))**2 + 1.5707963267949*(sign(-p + 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979) + 1)*((sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949) + 1)*sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949)**2/2 - sign(p - 6.28318530717959*floor(0.159154943091895*p + 0.5) - 1.5707963267949)**2 + 1)*sign(-p + 6.28318530717959*floor(0.159154943091895*p + 0.5) + 3.14159265358979)**2)]), qubits=(Qubit(QuantumRegister(2, 'q'), 0),), clbits=())]

    Perhaps in the weighing of pros and cons, this large expression is better than not being able to use parametrized circuits with rzz or needing to wrap parameter values in some external method.

  2. Do we need to be careful about adding a link between the rzz gate and the rx gate? In practice, they were added at the same time and so rx is always present when rzz is. The first version of the pass just added an assumption of there being an XGate though, not RXGate. Perhaps it is fine the way it is. Or should there be a check that the backend supports RXGate as well to be safe?

  3. I wonder if the parameterized and numerical code paths should be more aligned? Perhaps not, things are readable the way they are. The parameterized path uses a single function adding on parts as it goes while the numerical one has four separate functions. The parameterized version doesn't have the option of splitting up like the numerical one does.

One minor thing -- this is not related to this PR, but while looking at it I noticed the circuit drawings in the quad method docstrings are not aligned properly. Perhaps that could be incidentally fixed here as well.

@yaelbh
Copy link
Collaborator Author

yaelbh commented Dec 8, 2024

For (2), I expect other translation passes to translate rx (or X in the numeric case) to basis gates, if they are not already basis gates. This is related to my infinite loop concern. I still have to learn how the pass manager works, but I imagine that the passes run and rerun until a fixed point. If this is the case, suppose that rx is not a basis gate. A pass modifies the DAG circuit to use basis gates instead, and, since the DAG circuit has been modified, our rzz pass runs again. Which generates new rx gates, and so on. Note that this issue does not apply to the numeric case, where only the first execution of the rzz pass modifies the DAG circuit.

For (1), I'm more concerned than you, to the point that I estimate that this will PR eventually will not be able to merge, because of the circuit size. This is certainly something that must be checked.

@yaelbh
Copy link
Collaborator Author

yaelbh commented Dec 8, 2024

So the pass managers are just lists of passes that get to run sequentially, not involving loops and fixed points. I suggest at minimum to check if rx a basis gate, and skip the pass otherwise. We can also, in this case, create an alternative circuit without rx gates, but I find it unnecessary, since at least for now rx is present in all the backends that support fractional gates.

@@ -46,7 +46,7 @@ class FoldRzzAngle(TransformationPass):
with angle of arbitrary real numbers.
"""

def __init__(self, target: Optional[Union[Target, list[str]]] = None):
def __init__(self, target: Optional[Union[Target, List[str]]] = None):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you prefer, you can use from __future__ import annotations at the top of the import statements and then the new style will work for type hints. Doing this causes the type hints to be converted into strings instead of being evaluated as objects (so it doesn't make the list[...] syntax actually work on Python 3.9; if you try to use it outside of an annotation, there will be an error).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't mind, do you have a preference?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are inconsistent enough in this repo that I don't think it matters much in individual PRs like this, it would be more effective to have a PR that unifies across the repo and then we start being strict. My preference is to eventually settle on the new-style, in which case this would be Target | list[str] | None.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a strong preference though I do think it is nice to avoid the typing imports when possible. I mainly wanted to point the option out since I saw the commit message "old style typing required by CI".

@ihincks
Copy link
Collaborator

ihincks commented Dec 10, 2024

Should we be concerned about the blowing up in size of the input circuit?

Thanks for writing this example out explicitly, @wshanks . I don't have any feedback at the moment, just a question and some comments.

First of all, I haven't yet looked at the implementation or any math notes, but based on the gates in the printout, I assume this is implementing something the following case logic (?):

  • θ in [0, pi/2]: rzz(θ)
  • θ in [pi/2, pi]: (z \kron z) . rzz(θ-pi/2)
  • θ in [-pi/2, 0]: (x \kron I) . rzz(-θ) . (x \kron I) + global phase
  • θ in [-pi, -pi/2]: (x \kron i) . (z \kron z) . rzz(-θ-pi/2) . (x \kron I) + global phase

If so, I guess it's not surprising that implementing case logic with parameter expressions end up rather complicated. If there were many thousands of these inside of an input, it would cause bottlenecks various places in the stack.

To me, one of the main sticking points is where and how the user opts-in to the two extra rx gates.

@yaelbh
Copy link
Collaborator Author

yaelbh commented Dec 10, 2024

The logic is described in a clear manner at the doc strings of the methods quad1 - quad4. Note that the rzz angle must always be valid. The rx gates come from the fact that X is only sometimes present (so rx angle is either 0 or pi), the user is not involved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Transpilation can result in rzz gates with invalid angles
3 participants