Skip to content
This repository was archived by the owner on Jan 12, 2024. It is now read-only.
This repository was archived by the owner on Jan 12, 2024. It is now read-only.

Qubit allocation without new block scope #14

@bamarsha

Description

@bamarsha

Suggestion

We propose creating an analogue of let for qubits: a statement that allocates qubits and binds them to a name for the remainder of the current scope, after which the name is no longer accessible and the qubits are released. The statement does not require a new block scope, unlike the current using and borrowing statements.

Considerations

Allocating qubits in Q# with using and borrowing statements requires creating a new block scope. The block makes the lifetime of the qubits explicit, and discourages holding on to qubits for longer than needed, but at the cost of increased block nesting and indentation. Additionally, it is very common for a using statement block to be the last statement in the enclosing scope, in which case the explicit scope did not actually help to shorten the lifetime of the allocated qubits.

The explicit block also makes it cumbersome to create more than one qubit variable at a time. If one using statement per variable is used, a new indentation level is required for each variable. More commonly, tuple destructuring is used to create more than one variable in a single assignment, but this becomes difficult to read as the number of variables increases or as the complexity of the qubit initializer expression increases.

A let-like qubit allocation statement is needed to simplify these use cases. Because using and borrowing statements are the only way to allocate qubits, there is no workaround currently in Q#. An alternative proposal that would only address the many variable case could be to allow multiple using statements in a row followed by only one scope:

using (q1 = Qubit())
using (q2 = Qubit()) {
    // ... use both q1 and q2 ...
}

But this still requires at least one new block scope in all cases, unlike the original suggestion.

Context

We can consider two possible naming conventions for the new statements:

  1. Reuse the same keywords as the block statements: using and borrowing.
  2. By analogy to let, create the new keywords use and borrow.

Reusing the keywords avoids a breaking change, but creates a slight inconsistency:

using q1 = Qubit();
borrowing q2 = Qubit();
letting q3 = q1; // wrong
let q3 = q1;     // right

If we use new keywords, we need to decide if we keep the original keywords for the block statements (supporting two parallel sets of keywords indefinitely), or if we rename those too:

use (q = Qubit()) {
    // ...
}
borrow (q = Qubit()) {
    // ...
}

Examples

Example 1: Current syntax.

// One variable.
using (q = Qubit()) {
    // ...
}

// Two variables with separate statements.
using (q1 = Qubit()) {
    using (q2 = Qubit()) {
        // ...
    }
}

// Two variables with tuple destructuring.
using ((q1, q2) = (Qubit(), Qubit())) {
    // ...
}

Example 2: Proposed syntax with new keywords.

// One variable.
use q = Qubit();
// ...

// Two variables with separate statements.
use q1 = Qubit();
use q2 = Qubit();
// ...

// Two variables with tuple destructuring.
use (q1, q2) = (Qubit(), Qubit());
// ...

Affidavit (please fill out)

Please add ticks by placing a cross in the box:

  • I have searched both open and closed suggestions and proposals on this site and believe this is not a duplicate.
  • I believe that the spirit of this suggestion is aligned with the design principles and general vision for Q#.

Please tick all that apply:

  • This is not a breaking change to the Q# language design
  • I or my organization would be willing to help implement and/or test this

Metadata

Metadata

Assignees

Labels

ApprovedInPrincipleIssues that resulted in a proposal that is ready for implementation

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions