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

Support arithmetic on measurement results #10

Closed
rmshaffer opened this issue May 8, 2024 · 6 comments · Fixed by #27
Closed

Support arithmetic on measurement results #10

rmshaffer opened this issue May 8, 2024 · 6 comments · Fixed by #27
Assignees
Labels
enhancement New feature or request good first issue Good for newcomers

Comments

@rmshaffer
Copy link
Contributor

rmshaffer commented May 8, 2024

Users may commonly want to do arithmetic on measurement results (for example: syndrome calculation in quantum error correction). Today measure() returns a BitVar which cannot be operated on without explicitly casting to IntVar, like:

a1 = aq.IntVar(measure(1))
a0 = aq.IntVar(measure(0))
syndrome = 2*a1 + a0

We should make this easier so that users can simply do something like:

syndrome = 2*measure(1) + measure(0)

which implicitly does the same thing as above.

As a bonus, we should also allow conversion of a BitVar register to an IntVar simply by using a Pythonic conversion to int, such as:

syndrome = int(measure([0,1]))

which is an even simpler way to express the same as above.

@rmshaffer rmshaffer converted this from a draft issue May 8, 2024
@rmshaffer rmshaffer added enhancement New feature or request good first issue Good for newcomers labels May 8, 2024
@abidart
Copy link
Contributor

abidart commented Jun 5, 2024

Hello @rmshaffer, thank you for adding this issue to UnitaryHack! I am trying to tackle this as part of the challenge.

I have a quick question: if I understand correctly, a call to int() should always return an int, even if I write a custom __int__ method for the class. However, we are using BitVars and IntVars because, at this stage, we do not have access to what the measurements will result in.

Instead of using a call to int(), would it be appropriate to do something like the following, expecting syndrome to be an IntVar?

syndrome = measure([0,1]).to_int()

Thank you for your time!

@rmshaffer
Copy link
Contributor Author

@abidart Thanks, this is a very good point. So the AutoQASM/AutoGraph model for this would be to:
(1) add a new converter which converts int() calls to something like ag__.int_cast(). There's an example here which converts assignment statements to ag__.assign_stmt(tar_name_, val_) calls.
(2) implement int_cast() in the operators module - just as assign_stmt is implemented here. This int_cast() operator would return an IntVar in the case where the argument is an aq type (e.g. by calling aq_types.is_qasm_type()), and otherwise it would just fall back to the python int() function. (an example of this type of logic here)
(3) add the new converter to the list of converters in the transpiler here

This allows keeping the Pythonic syntax (where the user can just code as if they are dealing with native Python types), but also keeps the implementation code clean and doesn't force us to write a custom __int__ function which returns an IntVar (which, as you say, would be unnatural).

Let me know if I can clarify any of this - although this change might touch several files, there should hopefully be enough existing examples to follow.

@abidart
Copy link
Contributor

abidart commented Jun 7, 2024

Thank you very much! I have a follow-up question. In the current AQ version,

a1 = aq.IntVar(measure(1))
a0 = aq.IntVar(measure(0))
syndrome = 2*a1 + a0

compiles to:

OPENQASM 3.0;
qubit[2] __qubits__;
bit __bit_0__;
__bit_0__ = measure __qubits__[1];
int[32] a1 = __bit_0__;
bit __bit_2__;
__bit_2__ = measure __qubits__[0];
int[32] a0 = __bit_2__;

I am trying to get syndrome = int(measure([0,1])) to compile to the same thing. My initial approach was to add a transformer for typecasts in the transpiler, precisely in line 140. I thought that transforming the typecasting calls might need to come before the call tree gets transformed, otherwise, it gets a lot more difficult to iterate over the "bits" in the BitVar object returned by a call to measure. However, this results in the following instructions which look quite different.

OPENQASM 3.0;
qubit[2] __qubits__;
bit[2] __bit_0__ = "00";
__bit_0__[0] = measure __qubits__[0];
__bit_0__[1] = measure __qubits__[1];

Also, the original three lines still compile to the same instructions and all unit tests pass (tox -e unit-tests), so I do not think the changes I made impacted anything outside calls to int.

Do you think I should move the transformer to (or towards) the end of the transformer calls? Otherwise, given that I followed the steps from your previous message, do the instructions resulting from the int call make you think of something I might be missing/doing wrong? Here are some key snippets from my operator and converter files in case it can be helpful:

operator

    if aq_types.is_qasm_type(args_):
        if args_.size is not None and args_.size > 1 and isinstance(args_, aq_types.BitVar):
            typecasted_var = aq_types.IntVar(args_[0])
            for index in range(1, args_.size):
                typecasted_var += aq_types.IntVar(args_[index]) * 2**index
            return typecasted_var
        else:
            aq_types.IntVar(args_)
    else:
        return type_(*args_)

converter

        if (
            len(node.args) > 1
            and hasattr(node.args[1], "func")
            and hasattr(node.args[1].func, "id")
            and node.args[1].func.id == "int"
        ):
            new_node = templates.replace(
                template,
                type_=node.args[1].func.id,
                args_=node.args[1].args,
                original=node,
            )
            # new_node is a list of gast.Expr with only one element, 
            # whose value attribute is a gast.Call
            new_node = new_node[0].value  

Thanks again for your time and patience 🙏

@rmshaffer
Copy link
Contributor Author

Hey @abidart, thanks for looking into this! The easiest way for me to help would be if you push your changes to a branch or even open a draft PR here so that I can easily pull your code and run it - would you mind doing that?

@abidart
Copy link
Contributor

abidart commented Jun 7, 2024

I just opened a draft PR!

@rmshaffer
Copy link
Contributor Author

@abidart Thanks! I've left a few comments on the PR, we can continue the discussion there.

@rmshaffer rmshaffer linked a pull request Jun 12, 2024 that will close this issue
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers
Development

Successfully merging a pull request may close this issue.

2 participants