Uncomputation
Uncomputation is the process of reversing the effects of quantum operations, restoring the state of qubits to their initial state and disentangling them from other qubits. Failing to properly uncompute intermediate results can lead to incorrect final measurement results and wasted resources. In Qmod, intermediate results are typically stored in local variables, which are scoped inside a function and are inaccessible outside of it. Hence, local variables must be used in a way that enables subsequent uncomputation. Specifically, their interactions with other objects must be restrictive to be subsequently disentangled from them. In Qmod, local variables are automatically uncomputed, lifting the burden of manually uncomputing intermediate results. When finer-grained control is required, allocate the variable inside the within block of a within-apply statement. Such variables are subject to strict rules that guarantee their correct uncomputation. In this way, Qmod abstracts away the implementation details of efficient quantum storage management and prevents a class of functional bugs that are very difficult to detect. A fully manual uncomputation can be achieved by using free, as variables which are explicitly freed are not subject to these rules. To enable these capabilities, quantum operations are classified into arbitrary functions and permutation-only functions. In addition, each of an operation’s parameters is classified as either const or non-const.- permutation: A quantum operation is a permutation if it maps computational-basis states to computational-basis states (with possible phase shifts). Such an operation neither introduces nor destroys quantum superpositions, and its computation can be described classically.
- const: A parameter of a quantum operation is constant if it is immutable up to a phase. That is, the magnitudes of its computational-basis state components remain unchanged, while their phases may shift.
qperm. const parameters are explicity declared using the keyword
const. See more under Function Declarations.
Examples
Examples
Zis a permutation and its parameter is const as it merely flips the phase of the state.Xis a permutation but its parameter is not const as it flips between and .His not a permutation and its parameter is not const, as it introduces superposition.SWAPis a permutation but its two parameters are not const, as it swaps between and .
Quantum operations classification
Each quantum operation is classified as either an arbitrary mutation of the quantum state or strictly a permutation of computational-basis states, according to the following rules:- A function call is a permutation if the callee is declared as
qperm. - A numeric assignment (both out-of-place and in-place) is a permutation.
- Amplitude-encoding assignment is non-permutation.
- Phase statement is a permutation.
- A compound statement is a permutation if its body contains only permutation operations. For example, a control statement is a permutation if all statements inside its then and else clauses are permutations.
- Bind statement is considered a permutation, as are
allocateandfree.
- An argument in a function call is classified according to the modifier used in the matching parameter declaration.
- Use as right-value quantum expression is const. Right-value expressions
occur in the following contexts:
- On the right-hand side of a numeric assignment or amplitude-encoding assignment.
- As the condition in a control statement.
- As the argument of a phase statement.
- Use as left-value expression in numeric assignments (both out-of-place and in-place) and amplitude-encoding assignments is non-const.
- Arguments to a bind statement are treated specially: they are not immediately classified, but are instead bound together and assigned a joint classification of either const or non-const.
Enforcement of function classification
Generally, aqperm function is restricted to use only permutation
operations, and a const parameter is restricted to const use contexts.
Violation of these restrictions results in a compilation error.
However, flexibility is often required in the implementation of lower-level
building blocks. The cumulative effect of the function on the quantum
parameters in these cases satisfies its declared restrictions, but individual
operations violate it. Well known examples are the implementation of Toffoli gate,
and arithmetic addition in the Fourier bases. Both are permutation-only operations
taken as a whole, but internally use Hadamard gates and rotations.
It is not scalable to validate the correct cumulative effect of a description
automatically in the general case. However, you can suppress the fine-grained
enforcement of function classification with the disable_perm_check and
disable_const_checks specifiers using the following syntax:
- Python
- Native
The decorators
@qfunc and @qperm have the optional parameters
disable_perm_check and disable_const_checks declared thus -disable_perm_check: bool = Falsedisable_const_checks: Union[list[str], bool] = Falsedisable_const_checks may contain a list of parameter names, or a boolean
to disable the checks for all const parameters.disable_perm_check is used, the compiler does not enforce the usage of
non-permutation operations. When disable_const_checks is used, the compiler
does not enforce use context restrictions on the listed parameters (or all
parameters if none specified).
Examples
Example 1 - Correct use of permutation and const parameters
In the example below, functionfoo is declared qperm, and its first
parameter param1 is declared const. The definition of foo is consistent
with these declarations. Note that the restriction on param1 is carried over
to its use in the lambda expression passed to apply_to_all.
- Python
- Native
Example 2 - Incorrect use of permutation and const parameters
The example below demonstrates violations of the restrictions on the use of parameters and operations. As in the previous example, the functionfoo is
declared qperm and its first parameter param1 is declared const. However,
the use of param1 violates the restriction, and the function uses a
non-permutation operation as well.
- Python
- Native
Example 3 - Disabling permutation check
In the example below, functionmy_cx implements the CX operation using a
simple equivalence - applying phase flip in the Hadamard basis. The cumulative
operation on the quantum state is a permutation, but individual calls to H are
not. The disable_perm_check is used to suppress compiler errors in this case.
- Python
- Native
Example 4 - Disabling permutation check and const checks
In the example below, functionmy_z implements the Z operation using a
simple equivalence - applying bit flip in the Hadamard basis. The cumulative
operation on the quantum state is a permutation, but individual calls to H are
not. Also, the cumulative operation on the tgt is const, but individual
calls are not.
The disable_perm_check and disable_const_checks are used to suppress
compiler errors in this case.
- Python
- Native
Semantics of uncomputation
When a variable is initialized inside the within block of a within-apply statement, it is returned to its uninitialized state after the statement completes. The newly allocated quantum object is uncomputed, and its qubits are reclaimed by the compiler for subsequent use. Likewise, a variable declared locally within a function is only accessible inside the function’s scope. The quantum object allocated inside the function may be uncomputed at some later point and its qubits reclaimed. Local variables are considered uncomputation candidates if they remain initialized at the end of the function scope in which they were declared. Variables initialized inside a within block of a within-apply statement are also considered uncomputation candidates in the scope of the statement. The following rules guarantee that uncomputation candidates can be handled correctly:- An uncomputation candidate must not be used in a non-permutation operation.
- A variable becomes a dependency of an uncomputation candidate when used in an operation together with the candidate variable, and the latter is used in a non-const context. From that point, the dependency variable is subject to the same rules as the candidate variable, while the latter is in scope.
- An auto-uncomputation candidate must not be used in an operation together with a non-local variable if both use contexts are non-const.
- An auto-uncomputation candidate must not become a dependency of any variable that already (directly or indirectly) depends on it — that is, circular dependencies are not allowed.
- A variable initialized inside a within block of a within-apply statement can only be used in const contexts inside the apply block.
Examples
Example 1 - Automatic uncomputation
The example below demonstrates the use of a local variable and its automatic uncomputation. Variableaux is initialized as the left-value expression of an assignment statement, which is a permutation
operation. Subsequently, it is used as the condition of a control
statement, which is a const context. Both uses are valid, and the variable is
automatically uncomputed and freed correctly at the end of the function.
- Python
- Native
aux is uncomputed and reused across the multiple calls to foo,
as can be seen in the visualization:

Example 2 - Illegal use of local variable
The example below demonstrates illegal use of a local variable in a function which outputs only one qubit of a Bell pair, making it impossible to uncompute the other qubit. Specifically, the local variableq2 undergoes a non-permutation
operation, which is flagged as an error.
- Python
- Native
Example 3 - Illegal use of local variable due to parameter mutation
The example below demonstrates an illegal use of a local variableaux, as it is used as an argument
in a call to function x_transform together with the variable p, where both parameters
of x_transform are declared non-const.
- Python
- Native
Example 4 - Illegal use of local variable due to circular dependency
The example below demonstrates an illegal use of the local variableaux, as
the two calls to the function CX create a circular dependency between it and
the variable p.
- Python
- Native
Example 5 - Correct uncomputation in within-apply
The example below demonstrates the use of a local variable initialized inside a within block of a within-apply statement. Variableaux is initialized as
the left-value expression of an assignment statement, which is a permutation
operation. In the apply block, it is used as the condition of a control
statement, which is a const context. Both uses are valid, and the variable is
uncomputed and freed correctly after the within-apply statement.
- Python
- Native
Example 6 - Illegal use of local variable in within-apply
The code below is a modification of Example 1 above, with a couple of lines added to demonstrate violations of the rules for correct use of a variable initialized inside the within block of a within-apply statement. Here, variableaux is also used as the argument of function H in the within
block. This is an arbitrarily non-permutation operation (indeed H introduces
superposition between computational-basis states). In addition, aux is used
as the argument of function X in the apply block, which is non-const
context. Both uses are illegal, and both are reported as errors by the
compiler.
- Python
- Native
Example 7 - Illegal use of dependent variable in within-apply
The following example demonstrates a violation of the rules for correct use of a dependent variable inside a within-apply statement. Here, variableaux is
initialized inside a within block, and is subsequently entangled with q1
which is not an uncomputation candidate. From that point, the same restrictions
that hold for aux apply to q1. Therefore, using it as an argument to
function H is illegal, and is reported as an error. Indeed, if foo would
execute as specified, aux would not be uncomputed correctly.
- Python
- Native