Uncomputation in Qmod
Decomposing complex computations into simpler, reusable building blocks is fundamental to quantum computation, just as it is in classical computing. Doing so requires each functional block to manage its intermediate results locally. This is the role of local variables in programming languages. In quantum computing, however, this is not straightforward at all: intermediate results cannot simply be discarded, because they are often entangled with the function’s external outputs.
Entanglement is a form of correlation between variables unique to quantum computation. If two variables are entangled, measuring one gives information about the other. More subtly, operating on a variable can have different observable effects when it is entangled with another. Thus, if a function leaves its output entangled with a local variable, an internal implementation detail “leaks out” in a way the caller cannot anticipate or handle. For instance, the caller cannot reverse the function because they do not have access to the full state on which it depends.
The creators of Silq introduced language semantics that enabled automatic uncomputation. They showed that for a variable in a local scope to be disentangled from variables in an outer scope, it must be manipulated only with “lifted” operations: quantum counterparts of classical functions whose inputs remain constant. This requirement can be slightly relaxed, allowing for the introduction of relative phases, but the core idea remains intact.
Qmod, much like conventional programming languages, uses local variables within quantum functions to perform intermediate computations. These variables must be correctly uncomputed to ensure that the function produces the intended results and that auxiliary qubits can be reused. The Qmod compiler automatically inserts the necessary uncomputation steps. It also enforces the conditions that make uncomputation possible, specifically, that the manipulation of local variables corresponds to a classical transformation.
In Qmod, an operation that modifies its quantum parameters classically is called a permutation. Such an operation only permutes the amplitudes of computational-basis states (more precisely, their magnitudes). It can entangle quantum variables, but cannot introduce or destroy superposition. A local quantum variable can only be modified using permutations. This is, unless the programmer chooses to manually uncompute the variable (and call free() when they are done) or opt out entirely (by calling drop()).
Here is a simple example illustrating the use of a local variable and its automatic uncomputation:
@qfunc
def foo(qn: QNum, res: QBit):
aux = QBit()
aux |= qn > 1 # assignment of an expression is a permutation
control(aux, lambda: RX(pi/2, res))
@qfunc
def main(qn: Output[QNum], res: Output[QBit]):
allocate(2, qn)
hadamard_transform(qn)
allocate(res)
foo(qn, res)
foo(qn, res)
In function foo, the local variable aux is used to store an intermediate result (line 4). Subsequently, the result is used to modify parameter res (line 5). Assignment of a quantum expression is a permutation - aux is in the computational-basis state or as a classical function of the parameter qn. Hence, it is safe for the compiler to inject the logic to disentangle aux from qn and return it to its initial state. Function foo is then called twice from main.
When we synthesize this description, the quantum program visualization shows additional operations that the compiler injected to reverse the computation of aux. We can see that indeed, the same qubit is returned to the pool of available clean qubits and reused in the second call to foo.

You can find more details and many examples in the Qmod Language Reference. To see how these concepts are applied in algorithms and applications, explore the Classiq library on GitHub.
Decomposing complex computations into simpler, reusable building blocks is fundamental to quantum computation, just as it is in classical computing. Doing so requires each functional block to manage its intermediate results locally. This is the role of local variables in programming languages. In quantum computing, however, this is not straightforward at all: intermediate results cannot simply be discarded, because they are often entangled with the function’s external outputs.
Entanglement is a form of correlation between variables unique to quantum computation. If two variables are entangled, measuring one gives information about the other. More subtly, operating on a variable can have different observable effects when it is entangled with another. Thus, if a function leaves its output entangled with a local variable, an internal implementation detail “leaks out” in a way the caller cannot anticipate or handle. For instance, the caller cannot reverse the function because they do not have access to the full state on which it depends.
The creators of Silq introduced language semantics that enabled automatic uncomputation. They showed that for a variable in a local scope to be disentangled from variables in an outer scope, it must be manipulated only with “lifted” operations: quantum counterparts of classical functions whose inputs remain constant. This requirement can be slightly relaxed, allowing for the introduction of relative phases, but the core idea remains intact.
Qmod, much like conventional programming languages, uses local variables within quantum functions to perform intermediate computations. These variables must be correctly uncomputed to ensure that the function produces the intended results and that auxiliary qubits can be reused. The Qmod compiler automatically inserts the necessary uncomputation steps. It also enforces the conditions that make uncomputation possible, specifically, that the manipulation of local variables corresponds to a classical transformation.
In Qmod, an operation that modifies its quantum parameters classically is called a permutation. Such an operation only permutes the amplitudes of computational-basis states (more precisely, their magnitudes). It can entangle quantum variables, but cannot introduce or destroy superposition. A local quantum variable can only be modified using permutations. This is, unless the programmer chooses to manually uncompute the variable (and call free() when they are done) or opt out entirely (by calling drop()).
Here is a simple example illustrating the use of a local variable and its automatic uncomputation:
@qfunc
def foo(qn: QNum, res: QBit):
aux = QBit()
aux |= qn > 1 # assignment of an expression is a permutation
control(aux, lambda: RX(pi/2, res))
@qfunc
def main(qn: Output[QNum], res: Output[QBit]):
allocate(2, qn)
hadamard_transform(qn)
allocate(res)
foo(qn, res)
foo(qn, res)
In function foo, the local variable aux is used to store an intermediate result (line 4). Subsequently, the result is used to modify parameter res (line 5). Assignment of a quantum expression is a permutation - aux is in the computational-basis state or as a classical function of the parameter qn. Hence, it is safe for the compiler to inject the logic to disentangle aux from qn and return it to its initial state. Function foo is then called twice from main.
When we synthesize this description, the quantum program visualization shows additional operations that the compiler injected to reverse the computation of aux. We can see that indeed, the same qubit is returned to the pool of available clean qubits and reused in the second call to foo.

You can find more details and many examples in the Qmod Language Reference. To see how these concepts are applied in algorithms and applications, explore the Classiq library on GitHub.