Skip to content

Loading and running QASM2 files in Bloqade via PyQrack gives None #262

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

Open
johnzl-777 opened this issue May 12, 2025 · 6 comments
Open
Labels
bug Something isn't working enhancement New feature or request

Comments

@johnzl-777
Copy link
Contributor

johnzl-777 commented May 12, 2025

This something I noticed a couple times from ETH Zurich hackathon user workflows. I can see this being reflective of a greater user base as well.

One of the things users like to do is take a standard QASM2 file and load it into Bloqade, then apply our noise model onto it and pass it into PyQrack. The problem is, even if the QASM2 file has an explicit measure statement at the end, there's no real way of saying "return" the results.

To be a bit more precise, here's the code:

from bloqade import qasm2, pyqrack
from bloqade.qasm2.parse.lowering import QASM2
from pathlib import Path


method = QASM2(qasm2.main).loadfile(Path("test.qasm"))
method.print()

device = pyqrack.PyQrack(dynamic_qubits=True)
result = device.run(method)

And the QASM2 file, measurement and all

// Generated from Cirq v1.4.1

OPENQASM 2.0;
include "qelib1.inc";


// Qubits: [q(0), q(1), q(2), q(3), q(4), q(5), q(6)]
qreg q[7];
creg c[7];


h q[1];
h q[2];
h q[3];
cx q[6],q[5];
cx q[1],q[0];
cx q[2],q[4];
cx q[3],q[5];
cx q[2],q[0];
cx q[1],q[5];
cx q[6],q[4];
cx q[2],q[6];
cx q[3],q[4];
cx q[3],q[0];
cx q[1],q[6];

measure q -> c;

In the resulting SSA from the QASM2 load, the return is set to None by default:

...
  │ %52 = qasm2.expr.constant.int 6 : !py.int
  │ %53 = qasm2.core.qreg.get(reg=%1, idx=%52) : !py.Qubit
  │       qasm2.uop.CX(ctrl=%51, qarg=%53)
  │       qasm2.core.measure(qarg=%1, carg=%3)
  │ %54 = func.const.none() : !py.NoneType
  │       func.return %54

So the result is just nothing on execution.

There would have to be some way to set the return to either a qreg or creg.

Some off-the-bat ideas that might fix this and my immediate feelings:

  • Have some special annotation in the QASM2 file itself, like a comment that says return q or return c
    • I think it's pretty obnoxious for a user to have to jump into the text file and it feels very fragile
  • Indicate during loading what the return is via argument (would require some behind-the-scenes manipulation which I have a rough idea of)
    • For example, maybe something like ...loadfile(path, return_obj: "q"), where the string would have to line up with what's in the QASM file
    • I imagine there could be some analysis pass to gather all the SSA values for the creg and qreg's in the program, then some rewrite to plug in/change the return value to something from the analysis. I'm a little concerned this enters "modify return type" territory that I remember @Roger-luo being against but I can't think of another way around it.
    • On a subsequent larger thought, would it be desirable for a user to load in a QASM2 file into a Bloqade method and have the ability to change the contents of the method? Or is that too much flexibility?
@johnzl-777 johnzl-777 added bug Something isn't working enhancement New feature or request labels May 12, 2025
@weinbe58
Copy link
Member

For example, maybe something like ...loadfile(path, return_obj: "q"), where the string would have to line up with what's in the QASM file

At some point we had the first option implemented but it got removed in #242

I imagine there could be some analysis pass to gather all the SSA values for the creg and qreg's in the program, then some rewrite to plug in/change the return value to something from the analysis. I'm a little concerned this enters "modify return type" territory that I remember @Roger-luo being against but I can't think of another way around it.

This isn't modifying the return type because in QASM2 there is only a global scope while here we're mapping that global scope into a function scope so we have the freedom to return something if we want.

On a subsequent larger thought, would it be desirable for a user to load in a QASM2 file into a Bloqade method and have the ability to change the contents of the method? Or is that too much flexibility?

I am not sure what you mean here, the IR is already mutable.

@Roger-luo
Copy link
Member

At some point we had the first option implemented but it got removed in #242

I think this is tricky, but we do decide to add back the support of inserting a return value that was removed in #242 I think @david-pl proposed the use case where one wants to return a classical register. Was there an issue tracking this? or not yet?

On a subsequent larger thought, would it be desirable for a user to load in a QASM2 file into a Bloqade method and have the ability to change the contents of the method? Or is that too much flexibility?

What's the use case?

@david-pl
Copy link
Collaborator

I'm confused: it seems that @johnzl-777 is still using the old API, which is on the latest release (v0.2.3). That's fine, since it's the one which was used for the hackathon. But: that one still has the kwarg, right?

You can just do method = QASM2(qasm2.main).loadfile(Path("test.qasm"), returns="q"), or doesn't that work?

The kwarg has been removed by @Roger-luo in the in the new API that is qasm2.loadfile (currently on main but not released). There's no issue tracking this, but yes, I'd like to add this back before releasing v0.3.0.

@johnzl-777
Copy link
Contributor Author

johnzl-777 commented May 13, 2025

I'm confused: it seems that @johnzl-777 is still using the old API, which is on the latest release (v0.2.3). That's fine, since it's the one which was used for the hackathon. But: that one still has the kwarg, right?

You can just do method = QASM2(qasm2.main).loadfile(Path("test.qasm"), returns="q"), or doesn't that work?

You're quite right, that is definitely still there and works on my end. Would have saved a great deal of pain during the hackathon, I accept responsibility for not seeing this functionality earlier. I know there's plenty of conversation about improving documentation, I feel like this is definitely something worth making very explicit when we get there.

On a subsequent larger thought, would it be desirable for a user to load in a QASM2 file into a Bloqade method and have the ability to change the contents of the method? Or is that too much flexibility?

What's the use case?

I admit I didn't have something concrete, in fact I remember some issue (I can't seem to find it) that probably covers the use case I'm thinking of where you have some gates in your program you want to quickly replace with some other gate (say, take all instances of Hadamard and plug in Sqrt(Y)).

I know we think of it as a rewrite on the SSA level but I wonder if there would be any benefit having an API on a higher level for users that don't want to touch the SSA IR and only want to deal with QASM2. For example, cirq has "transformers" (https://quantumcomputing.stackexchange.com/a/24069) that fall in line with what I was thinking of.

At this case this is probably a separate issue and I don't feel too strongly about the matter (as of yet, at least). I only thought about this because I thought there would have to be some clever rewrite to do what I wanted to fix this issue but as David points out, it's been sitting under my nose the whole time 😅

I think the issue is addressed, perhaps some sub-issues might be worth it to remind us to make the returns functionality in loads explicit to users or if the higher-level gate substitute functionality is worth tracking. I defer to your guys' opinions on the matter now

@Roger-luo
Copy link
Member

I think having some rewrite rule like transformer would be nice. The question is what would be the design exactly? This should indeed be a separate issue (and will be a good one).

@david-pl
Copy link
Collaborator

FYI, added back returns in #266

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants