Skip to content

Conversation

@david-pl
Copy link
Collaborator

This implements the simple dialect group as discussed in #531.

However, it also contains a draft (RFC) for validating kernels, using the above as an example.

I thought a bit about it and in the end decided to split the validation into two parts:

  1. A ValidationAnalysis pass with an appropriate lattice (could be extended to warnings, etc.) that checks for errors in the IR.
  2. A simple KernelValidation class, that gathers up the errors and throws them as kirin.ir.ValidationError with the appropriate source info pointing to the line of the error. I went back and forth as to how to implement this, since it is a bit similar to interpretation. However, in the end I just decided to go with a very simple dataclass, since it is sufficiently different from an interpreter or a rewrite.

One thing that's not great and I'm not sure how to improve: we probably don't want the ValidationAnalysis to implement a lot of methods via method tables. For example, the gemini.logical dialect doesn't support scf.IfElse. However, if we want to re-use the ValidationAnalysis elsewhere, then registering a MethodTable for scf with a method for scf.IfElse might fail validation in kernels where this should be fine. At the same time, we might want to share validation between different kernels.

The way I "solved" this for now is to define a new analysis pass just for the logical dialect that inherits from the generic ValidationAnalysis. If we go this route, then the ValidationAnalysis basically just defines the lattice and a fallback method.

There's also some TODOs left here, e.g. how to display multiple errors, but I thought I'd get comments as soon as possible.

@david-pl david-pl added the rfc Request for Comments label Oct 21, 2025
@codecov
Copy link

codecov bot commented Oct 21, 2025

Codecov Report

❌ Patch coverage is 90.79755% with 15 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/bloqade/validation/kernel_validation.py 63.15% 14 Missing ⚠️
src/bloqade/gemini/groups.py 96.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@github-actions
Copy link
Contributor

github-actions bot commented Oct 21, 2025

☂️ Python Coverage

current status: ✅

Overall Coverage

Lines Covered Coverage Threshold Status
9648 8515 88% 0% 🟢

New Files

File Coverage Status
src/bloqade/gemini/_init_.py 100% 🟢
src/bloqade/gemini/analysis/_init_.py 100% 🟢
src/bloqade/gemini/analysis/logical_validation/init.py 100% 🟢
src/bloqade/gemini/analysis/logical_validation/analysis.py 100% 🟢
src/bloqade/gemini/analysis/logical_validation/impls.py 100% 🟢
src/bloqade/gemini/groups.py 96% 🟢
src/bloqade/validation/_init_.py 100% 🟢
src/bloqade/validation/analysis/_init_.py 100% 🟢
src/bloqade/validation/analysis/analysis.py 100% 🟢
src/bloqade/validation/analysis/lattice.py 100% 🟢
src/bloqade/validation/kernel_validation.py 63% 🟢
TOTAL 96% 🟢

Modified Files

File Coverage Status
src/bloqade/squin/gate/stmts.py 100% 🟢
TOTAL 100% 🟢

updated for commit: 2e4631d by action🐍

Copy link
Member

@weinbe58 weinbe58 left a comment

Choose a reason for hiding this comment

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

For the base class you don't need to specify any keys at all and those can be overwritten by the child class so we can use the method table keys to compose method tables in the different verification analysis passes.

Comment on lines 21 to 28
# TODO: this is actually tricky, since qalloc calls qubit.new multiple times and we have to make sure qalloc is only called once
# but it can technically contain many qubit.new calls
# if interp.has_allocated_qubits:
# raise ir.ValidationError(
# stmt, "Can only allocate qubits once in a logical Gemini program!"
# )

# interp.has_allocated_qubits = True
Copy link
Member

Choose a reason for hiding this comment

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

Actually what matters is if qubit are allocated after the first gate so maybe add a flag for if there ways already a gate operation

Copy link
Member

Choose a reason for hiding this comment

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

Actually I'm not even sure if its a problem if there are allocations after the first gate.

Copy link
Contributor

Choose a reason for hiding this comment

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

Wouldn't it be sufficient for Gemini to ensure that at most n qubits are allocated? In principle it would not make a difference where in the squin kernel qalloc is called, would it? On the machine, all qubits are always prepared at the very beginning.

Copy link
Member

Choose a reason for hiding this comment

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

yes exactly.

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're right. The only problem here would be conditional allocation. But since the dialect won't support if statements, we can lift basically all qubit allocations to the top of the kernel.

The only case that we have to think about are for loops, which are allowed, but restricted to ones that can be unrolled. I'm not sure if the AddressAnalysis can infer the number of qubits in a for loop, however. Technically, since we only support for loops that can be unrolled, it is inferable, but we might just require the user (or previous passes) to unroll before the validation is run. So either

  • make sure the number of qubits is properly inferred in simple (constant iterable) for loops, or
  • error (or warn?) for allocations inside for loops, telling the user to unroll it. This could be done via a boolean flag in the logical dialect group, running the AggressiveUnroll (I think we need to inline anyway).

@david-pl
Copy link
Collaborator Author

For the base class you don't need to specify any keys at all and those can be overwritten by the child class so we can use the method table keys to compose method tables in the different verification analysis passes.

@weinbe58 yes, you're right this should be an abstract class. Originally, I was thinking we'd potentially want to share methods using this single validation analysis (similar to how we only have a single PyQrack interpreter), but since we need to override (or remove) methods at times, this doesn't make much sense.

So, the base class will basically be a template that defines the typing and the lattice along with the single default fallback method of having NoError for a statement. That's it.

@david-pl
Copy link
Collaborator Author

While implementing a validation that checks whether U3 only occurs as the first gate in the kernel, I noticed some limitations of this approach:

  1. Forward analysis doesn't deal well with statements that don't have an associated ResultValue. Since there is no SSAValue associated with it, it doesn't collect the lattice element in the frame. Of course, such statements could still have errors. That means I had to workaround this, simply by collecting a list of "additional errors".
  2. Similarly, statements without a ResultValue don't have source information, meaning the error is less informative.

Since squin.gate statements are all defined as not having a ResultValue, this limits any validation we run on them.

I'm not sure if this is actually an issue on the kirin side of things. @weinbe58 what do you think?

@weinbe58
Copy link
Member

While implementing a validation that checks whether U3 only occurs as the first gate in the kernel, I noticed some limitations of this approach:

  1. Forward analysis doesn't deal well with statements that don't have an associated ResultValue. Since there is no SSAValue associated with it, it doesn't collect the lattice element in the frame. Of course, such statements could still have errors. That means I had to workaround this, simply by collecting a list of "additional errors".
  2. Similarly, statements without a ResultValue don't have source information, meaning the error is less informative.

Since squin.gate statements are all defined as not having a ResultValue, this limits any validation we run on them.

I'm not sure if this is actually an issue on the kirin side of things. @weinbe58 what do you think?

Well since you want to indicate which qubit has the invalid initialization storing the error at the qubit ssa value probably makes more sense anyways.

@david-pl
Copy link
Collaborator Author

Well since you want to indicate which qubit has the invalid initialization storing the error at the qubit ssa value probably makes more sense anyways.

@weinbe58 I'm not so sure. Ideally, I'd like to point to the (invalid) gate statement, not the qubit that is allocated somewhere. Also, how would you deal with multiple errors for a single qubit then?

Generally speaking, I think we should be able to express errors (or any analysis value, really), even for statements that don't have a ResultValue. Unless the statement is pure, it will have some effect that might be relevant in an analysis.

@weinbe58
Copy link
Member

@weinbe58 I'm not so sure. Ideally, I'd like to point to the (invalid) gate statement, not the qubit that is allocated somewhere. Also, how would you deal with multiple errors for a single qubit then?

Generally speaking, I think we should be able to express errors (or any analysis value, really), even for statements that don't have a ResultValue. Unless the statement is pure, it will have some effect that might be relevant in an analysis.

Well you can store the invalid statements in a custom frame. that is associated with the Analysis.

This is similar to how purity is handled in constant prop.

@david-pl
Copy link
Collaborator Author

So, after discussion with @weinbe58 elsewhere: the values in the frame.entries can't store all errors, since the number of values must match the number of ir.ResultValues in a statement.

Instead, we now just append every error we encounter as an kirin.ir.ValidationError in the method table implementation directly and look at that for processing / raising errors.

@david-pl david-pl removed the rfc Request for Comments label Oct 24, 2025
@david-pl david-pl marked this pull request as ready for review October 24, 2025 08:42
@david-pl
Copy link
Collaborator Author

@weinbe58 I'd actually like to conclude this RFC and merge it. In that sense, this is ready for review.

I think the validation for the logical dialect is complete, except for the qubit number validation. However, that requires the address analysis to work, so it's blocked by that. So I'd do that in a separate PR, if you agree.

@david-pl david-pl requested a review from weinbe58 October 24, 2025 08:44
@david-pl david-pl linked an issue Oct 27, 2025 that may be closed by this pull request
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.

Gemini logical target dialect group in bloqade.gemini

4 participants