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

Reassignment/swapping of borrowed variables #609

Open
acl-cqc opened this issue Nov 1, 2024 · 1 comment
Open

Reassignment/swapping of borrowed variables #609

acl-cqc opened this issue Nov 1, 2024 · 1 comment

Comments

@acl-cqc
Copy link
Contributor

acl-cqc commented Nov 1, 2024

Here are a couple of example programs that fail type-checking at the moment:

@guppy
def foo(q: qubit) -> None:
  measure(q)
  q = qubit()

@guppy
def bar(q1: qubit, q2: qubit, a: angle, b: bool) -> None:
  if b:
    q1, q2 = q2, q1
  h(q1)
  rz(q2)
  cx(q1, q2)

One way would be if we had an intrinsic swap(q1: qubit, q2: qubit) -> None:

@guppy
def foo(q: qubit) -> None:
  q2 = qubit()
  swap(q,q2) # newly-allocated qubit is now in `q`
  measure(q2)

@guppy
def bar(q1: qubit, q2: qubit, a: angle, b: bool) -> None:
  if b:
    swap(q1, q2)
  h(q1)
  rz(q2)
  cx(q1, q2)

The foo here is a bit awkward - this is akin to Rust std::swap on &muts - but perhaps OK.
bar seems fine, but supposes that the swap of q1,q2 should be propagated back to the caller - which makes sense if we realize that borrowing is actually transfer of ownership and back again from the same variable. If we didn't want to do this, we might try:

@guppy(module)
def bar2(q1: qubit, q2: qubit, a: angle, b: bool) -> None:
        (r1, r2) = (q1,q2) if b else (q2, q1)
        h(r1)
        rz(r2, a)
        cx(r1, r2)

but this won't compile - you'd have to refactor into an inner function:

@guppy(module)
def test(q1: qubit, q2: qubit, a: angle, b: bool) -> None:
    def inner(r1: qubit, r2: qubit, a: angle) -> None:
        h(r1)
        rz(r2, a)
        cx(r1, r2)
    if b:
        inner(q1, q2, a)
    else:
        inner(q2, q1, a)

Which works ok. (One could imagine def inner(t: tuple[qubit, qubit], a: angle) and then calling inner((q1,q2) if b else (q2,q1),a) but I'm not sure whether that works, haven't figured out how to use tuples yet.)

A further enhancement would thus be to allow variables r1,r2 in bar2 above to borrow q1/q2, i.e., for borrowing to occur over the lifetime of a local variable (i.e. a section of the CFG) rather than only/always over the lifetime of a function call. This would (significantly) complicate the checker and perhaps also require new syntax, however (in some sense equivalent to defining that local function but without the possibility of indirect call). For discussion, perhaps something like

with ((q1, q2) if b else (q2, q1)) as (r1,r2): # Univariate `with (q1 if b else q2)` straightforward, but tuples??
  h(r1)
  rz(r2, a)
  cx(r1, r2)
# r1,r2 now gone (out-of-scope/consumed); q1,q2 restored
@acl-cqc
Copy link
Contributor Author

acl-cqc commented Nov 1, 2024

Note the swap version (where the caller sees the qubits being swapped too) is straightforward via the quantum_functional interface: for example:

    def test(q1: qubit @owned, q2: qubit @owned, a: angle, b:bool) -> tuple[qubit, qubit]:
        (r1,r2) = (q1,q2) if b else (q2, q1) # Renaming to r1/r2 only for clarity, no need to
        r1 = h(r1)
        r2 = rz(r2, a)
        return cx(r1, r2)

so as an alternative to swap one might consider ways of calling into quantum_functional from the "inout" world (the other way around being easy)

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

1 participant