Closed rmshaffer closed 5 months ago
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 BitVar
s and IntVar
s 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!
@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.
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 🙏
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 Thanks! I've left a few comments on the PR, we can continue the discussion there.
Users may commonly want to do arithmetic on measurement results (for example: syndrome calculation in quantum error correction). Today
measure()
returns aBitVar
which cannot be operated on without explicitly casting toIntVar
, like:We should make this easier so that users can simply do something like:
which implicitly does the same thing as above.
As a bonus, we should also allow conversion of a
BitVar
register to anIntVar
simply by using a Pythonic conversion toint
, such as:which is an even simpler way to express the same as above.