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

transformations: add qref/qssa conversions #2770

Merged
merged 6 commits into from
Jun 26, 2024
Merged

Conversation

alexarice
Copy link
Collaborator

Built on top of #2768

Adds two passes which convert back and forth between the qssa and qref dialects. Also adds filecheck tests that test each pass and roundtrip of the passes

I think the convert-qref-to-qssa is particularly hacky, with the saving the old operands of the operation into a tuple, running replace on the operands and then inserting back the old ones. Would be keen to hear better ways to achieve the same behaviour

@alexarice alexarice added the transformations Changes or adds a transformatio label Jun 24, 2024
@alexarice alexarice self-assigned this Jun 24, 2024
Copy link

codecov bot commented Jun 24, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 89.80%. Comparing base (ea6f4d9) to head (45ea1f4).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2770      +/-   ##
==========================================
+ Coverage   89.79%   89.80%   +0.01%     
==========================================
  Files         376      378       +2     
  Lines       48079    48181     +102     
  Branches     7370     7372       +2     
==========================================
+ Hits        43172    43269      +97     
- Misses       3757     3762       +5     
  Partials     1150     1150              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Collaborator

@AntonLydike AntonLydike left a comment

Choose a reason for hiding this comment

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

Would love to see docstrings on the patterns and/or passes that explain what they do!

Comment on lines 41 to 56
@property
@abstractmethod
def is_gate(self) -> bool:
"""
Is this operation a gate
"""
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you add a docstring to explain why you need this helper?

Copy link
Member

Choose a reason for hiding this comment

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

I agree, also a more explicit description of what a gate is in this context.

# Create replacement operation
new_op: QssaBase = op.ssa_op()
# For gates there are no results to replace
new_results = () if new_op.is_gate else new_op.results
Copy link
Collaborator

Choose a reason for hiding this comment

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

Couldn't this check be replaced with a check of "has the new_op results that are a qubit"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

is_gate should be equivalent to "The qubit-typed operands match one-to-one with the qubit-typed results".

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This property could be removed and replaced by an actual check of this. Essentially the gates are the things we actually need to modify in the transform as they switch between having results and having no results depending on the dialect. Alloc and measure on the other hand are basically unchanged between dialects (apart from their types)

Copy link
Collaborator

@n-io n-io left a comment

Choose a reason for hiding this comment

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

Looks good, I'd suggest to separate out dialect and transform code

@property
def is_gate(self) -> bool:
return False

res: VarOpResult = var_result_def(qubit)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Style comment: Can you move res before the methods please

Copy link
Collaborator

Choose a reason for hiding this comment

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

@AntonLydike I maybe incorrectly believed that this would cause some style check to fail, since it is done this way across all xdsl ops I've seen.

@@ -70,6 +94,17 @@ def print(self, printer: Printer):
class HGateOp(QRefBase):
name = "qref.h"

def ssa_op(self) -> qssa.HGateOp:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Best to keep dialect conversion functions in the converter rather than on the dialect.

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 am unsure if this is desirable, as I don't see a way around having a big if isinstance chain in the converter without doing it this way. Another advantage of this method is that if a new operation is added, then the implementation of these two functions is forced

Copy link
Collaborator

@n-io n-io Jun 25, 2024

Choose a reason for hiding this comment

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

You could match on the specific op, create a new one, and move any shared code to a helper function., e.g. something along the lines of:

class ConvertHGateToQssaPattern(RewritePattern):
    @op_type_rewrite_pattern
    def match_and_rewrite(self, op: qref.HGateOp, rewriter: PatternRewriter):
        replace_with_ssa(
            op,
            qssa.HGateOp.create(
                operands=op.operands,
                result_types=(qssa.qubit,),
                attributes=op.attributes,
            ),
            rewriter,
        )

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This still runs into the problem of not forcing the conversion to be written for new ops, though I guess a test could be written that ensures this

Copy link
Member

Choose a reason for hiding this comment

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

I kind of like the current setup. The only thing I might suggest is to have a QuantumGate trait instead of is_gate() but even that seems like a nit, and like less good a way to guarantee that each op will have to implement these methods when it's added. It also feels like the two dialects are coupled in a pretty fundamental way, which seems like an ok design decision, making the abstract method style of conversion quite natural. I'm not sure in what situation in the future we would regret merging this PR as-is.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This still runs into the problem of not forcing the conversion to be written for new ops, though I guess a test could be written that ensures this

This could also be a feature. Forcing a conversion to be made also implies an unwritten guarantee that there will always be a valid 1:1 conversion, and that neither of these dialects will ever hold concepts that are not directly convertible, now or in the future. I just wanted to draw attention to the fact that this tight coupling might be a rather committal decision. I don't fully know the plans for these dialects, so if you're happy then please go ahead and merge.

@@ -85,6 +120,17 @@ def __init__(self, input: SSAValue):
class CNotGateOp(QRefBase):
name = "qref.cnot"

def ssa_op(self) -> qssa.CNotGateOp:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Likewise

@@ -102,6 +148,17 @@ def __init__(self, in1: SSAValue, in2: SSAValue):
class CZGateOp(QRefBase):
name = "qref.cz"

def ssa_op(self) -> qssa.CZGateOp:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Likewise

@@ -119,6 +176,17 @@ def __init__(self, in1: SSAValue, in2: SSAValue):
class MeasureOp(QRefBase):
name = "qref.measure"

def ssa_op(self) -> qssa.MeasureOp:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Likewise

name = "qssa.alloc"

def ref_op(self) -> qref.QRefAllocOp:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Likewise

name = "qssa.h"

def ref_op(self) -> qref.HGateOp:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Likewise

name = "qssa.cnot"

def ref_op(self) -> qref.CNotGateOp:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Likewise

name = "qssa.cz"

def ref_op(self) -> qref.CZGateOp:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Likewise

name = "qssa.measure"

def ref_op(self) -> qref.MeasureOp:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Likewise

Base automatically changed from alexarice/qref-dialect to main June 25, 2024 09:25
@alexarice alexarice force-pushed the alexarice/qref-transforms branch from 08fbe7e to 45ea1f4 Compare June 25, 2024 09:29
# For gates we replace any other occurences of the original operands
# with the results of the new operation

old_operands = tuple(new_op.operands)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@math-fehr I requested a review to see if you knew a better way to do this section, but feel free to ignore if you're too busy

@alexarice alexarice requested review from AntonLydike and n-io June 25, 2024 10:21
Copy link
Collaborator

@AntonLydike AntonLydike left a comment

Choose a reason for hiding this comment

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

Good stuff!

Comment on lines +51 to +56
"""
Is this operation a gate?
Qref gates represent standard quantum logic gates.
They should have no results.
The results of the generated gate must be rewired when converting to the qssa dialect.
"""
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍 Nice and concise docstring!

Comment on lines -32 to +34
class QubitBase(IRDLOperation, ABC):
pass
class QssaBase(IRDLOperation, ABC):
"""
Copy link
Collaborator

Choose a reason for hiding this comment

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

I like this renaming, big fan 💪


def apply(self, ctx: MLContext, op: builtin.ModuleOp) -> None:
PatternRewriteWalker(
ConvertQRefToQssaPattern(), apply_recursively=False
Copy link
Collaborator

Choose a reason for hiding this comment

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

Supernit: I'm 99% sure you don't need to specify apply_recursively=False here as it should not impact the result at all.

Every non-standard option specified unnecessarily introduces reviewer overhead.

Copy link
Member

Choose a reason for hiding this comment

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

I disagree, it has a performance impact, and should be added whenever the rewrites don't insert operations that could be recursively matched.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

You are probably right, think I was worried about things being converted in the "wrong" order which could maybe cause verification problems (I'm not actually sure when the verifier is run)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The tests still succeed without it, so I guess it's just a performance thing

Copy link
Collaborator

@n-io n-io left a comment

Choose a reason for hiding this comment

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

If we're happy with a tight coupling of these dialects (see discussion above) I'm happy to approve.

@alexarice alexarice merged commit 9d9bbdd into main Jun 26, 2024
10 checks passed
@alexarice alexarice deleted the alexarice/qref-transforms branch June 26, 2024 10:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
transformations Changes or adds a transformatio
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants