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

Odd behaviour with shadowing variables #840

Open
bhayat-quantinuum opened this issue Mar 6, 2025 · 2 comments
Open

Odd behaviour with shadowing variables #840

bhayat-quantinuum opened this issue Mar 6, 2025 · 2 comments

Comments

@bhayat-quantinuum
Copy link

bhayat-quantinuum commented Mar 6, 2025

from guppylang import guppy
from guppylang.std.builtins import array, py, result
from guppylang.std.quantum import discard_array, measure_array, qubit, h, rz, rx, x, cx
from guppylang.hresult import HShots


def build_fun() :
    j = 1

    @guppy
    def fun2(i : int, arr : array[qubit, 3]) -> None:
        x(arr[i])

    @guppy 
    def main() -> None:
        reg = array(qubit() for _ in range(3))
        for i in range(50):
            j = i + 1
            if j == 51:
                x(reg[1])

        fun2(py(j), reg)

        [c0, c1, c2] = measure_array(reg)

        result("c[0]", c0)
        result("c[1]", c1)
        result("c[2]", c2)

    return main.compile()

Running the above results in error:

Error: Python error (at /home/abhayat/repos/QTDA-Algs-Guppy/Guppy-code/src/test.py:25:12)
   | 
23 |                 x(reg[1])
24 | 
25 |         fun2(py(j), reg)
   |             ^ Error occurred while evaluating this expression

Traceback printed below:

Traceback (most recent call last):
  File "<string>",
line 1, in <module>
NameError: name 'j' is not defined

Guppy compilation failed due to 1 previous error

If I change the name of j to anything else, the code runs fine. This seems a little odd / buggy since the local j is out of scope by the time the compile time one is called.

It seems to me that it should be possible to use py(j) even when the local j is in scope, because the py(...) function should be enough for the compiler to differentiate between the two?

@mark-koch
Copy link
Collaborator

the local j is out of scope by the time the compile time one is called.

Note that we're following Python's scoping semantics. For example the following code would be fine

@guppy
def foo() -> int:
    while True:
        j = 1
        if condition():
            break

    # Use of j outside loop is legal since the compiler can 
    # prove that it is definitely assigned
    return j

So the j in your example is at least potentially still in scope.

It seems to me that it should be possible to use py(j) even when the local j is in scope, because the py(...) function should be enough for the compiler to differentiate between the two?

This feels a bit dangerous as it might confuse users:

j = 0

@guppy
def foo() -> int:
    j = 1
    return py(j)  # Making this evaluate to 0 seems counter intuitive to me

I'd prefer to force users to be explicit and give them an error pointing to the fact that they're using a Guppy variable:

Error: Not compile-time evaluatable (at test.py:26:14)
   | 
24 | def foo() -> int:
25 |     j = 1
26 |     return py(j)  # Making this evaluate to 0 seems counter intuitive to me
   |               ^ Guppy variable `j` cannot be accessed in a compile-time
   |                 `py(...)` expression

But the error you're getting in your example is clearly worse! We should be outputting the same message as above.

The problem is that given a decorated function f, we use the f.__closure__ and f.__code__.co_freevars attributes of the CPython implementation to figure out which external variables are used by the function and make those available to the execution of the py(...) expression. However, Python doesn't treat j as a global variable since it is assigned in the function. Therefore, we don't catch it that case. But maybe there is a way around that...

@bhayat-quantinuum
Copy link
Author

j = 0

@guppy
def foo() -> int:
    j = 1
    return py(j)  # Making this evaluate to 0 seems counter intuitive to me

I actually don't find this counter intuitive, since my mind reads py(j) as python_j, but perhaps that is just me.

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

No branches or pull requests

2 participants