> ## Documentation Index
> Fetch the complete documentation index at: https://prod-mint.classiq.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Within-apply

The *within-apply* statement performs the common quantum pattern $U^{\dagger} V U$. It
operates on two nested statement blocks, the *within* block and the *apply* blocks,
and evaluates the sequence - *within*, *apply*, and *invert(within)*. Under conditions
described below, quantum objects that are allocated and prepared by the *within* block
are subsequently uncomputed and released.

## Syntax

<Tabs>
  <Tab title="Python">
    [comment]: DO_NOT_TEST

    ```python theme={null}
    def within_apply(within: Callable, apply: Callable) -> None:
        pass
    ```
  </Tab>

  <Tab title="Native">
    **within** **\{** *within-statements* **}** **apply** **\{** *apply-statements* **}**
  </Tab>
</Tabs>

## Semantics

* Unlike the case with other statements, the nested blocks of *within-apply* may initialize
  outer context variables.
* Variables that are initialized inside the *within* block of a *within-apply* statement,
  are returned to their uninitialized state after the statement completes.
* All quantum objects allocated directly or indirectly under the *within* block
  are uncomputed, and their qubits are reclaimed for subsequent use after the statement
  completes.
* In addition to the general restriction of local variables to *permutable* use contexts,
  variables initialized inside the *within* block and their dependents can only be
  used in *const* contexts inside the *apply* block. See more on uncomputation rules
  under [Uncomputation](/qmod-reference/language-reference/uncomputation).
* The application of the *within* block and its inverse are not subjected to redundant control logic in
  the case where the *within-apply* statement as a whole is subject to control.
* The application of the *within* block and its inverse are guaranteed to be strictly equivalent, including
  in the case where *within* block involves non-deterministic implementation decisions by the
  synthesis engine.

## Examples

### Example 1

The following example demonstrates how auxiliary qubits get used, uncomputed, and reused
at different steps of a computation, when scoped inside a *within-apply* statement.
Actual reuse is a decision the synthesis engine takes to satisfy width constraints.

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from classiq import *


    @qfunc
    def main(res: Output[QBit]):
        allocate(res)
        ctrl = QNum()
        within_apply(lambda: assign(3, ctrl), lambda: CCX(ctrl, res))
        within_apply(lambda: assign(2, ctrl), lambda: CCX(ctrl, res))
    ```
  </Tab>

  <Tab title="Native">
    ```
    qfunc main(output res: qbit) {
      allocate(res);
      ctrl: qnum;
      within {
        ctrl = 3;
      } apply {
        CCX(ctrl, res);
      }
      within {
        ctrl = 2;
      } apply {
        CCX(ctrl, res);
      }
    }
    ```
  </Tab>
</Tabs>

Note how variable `ctrl` is initialized in the
*within* block, prior to being used for the `CCX` in the *apply* block.
Outside the *within-apply* statement the variable is reset to its uninitialized state,
and used again in the same way.

Visualizing the resulting quantum program, you can see how the same two auxiliary
qubits are reused across the two steps of the circuit, because of width optimization.

<img src="https://mintcdn.com/classiq/sA-J-h8chQJV9OAG/qmod-reference/language-reference/statements/resources/within_apply.png?fit=max&auto=format&n=sA-J-h8chQJV9OAG&q=85&s=02d28d7ac926d626181f412419008efd" alt="within_apply.png" width="1176" height="136" data-path="qmod-reference/language-reference/statements/resources/within_apply.png" />

### Example 2

The code snippet below demonstrates the implementation of the phase kickback pattern
for an arbitrary quantum predicate. Function `my_phase_oracle` takes as parameter a
function that flips a qubit on the states of interest. Variable `aux` is prepared
in the $|1\rangle$ state and passed as the target to function `my_cond_phase_flip`. The
cumulative effect of `my_cond_phase_flip` is a conditional $\pi$ phase on `target`, controlled
on the states of interest. Since `aux` is subsequently uncomputed and released, a
relative $\pi$ phase remains between the states of interest and all others in the
superposition.

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from classiq import *


    @qperm(disable_perm_check=True, disable_const_checks=True)
    def my_cond_phase_flip(predicate: QPerm[QBit], target: Const[QBit]):
        H(target)
        predicate(target)
        H(target)


    @qperm
    def my_phase_oracle(predicate: QPerm[QBit]):
        aux = QBit()
        within_apply(
            lambda: (allocate(aux), X(aux)), lambda: my_cond_phase_flip(predicate, aux)
        )
    ```
  </Tab>

  <Tab title="Native">
    ```
    @disable_perm_check
    @disable_const_checks
    qperm my_cond_phase_flip(predicate: qperm (qbit), const target: qbit) {
      H(target);
      predicate(target);
      H(target);
    }

    qperm my_phase_oracle(predicate: qperm (qbit)) {
      aux: qbit;
      within {
        allocate(aux);
        X(aux);
      } apply {
        my_cond_phase_flip(predicate, aux);
      }
    }
    ```
  </Tab>
</Tabs>

Note that `my_cond_phase_flip` declares parameter `target` as `const` because, taken as
a whole, the function only applies phase changes to it. But because the implementation
uses non-cost operations, we disable const checks. For more on enforcement
of parameter restrictions see [Uncomputation](/qmod-reference/language-reference/uncomputation).

### Example 3

The code snippet below demonstrates the use of *within-apply* to define the Grover operator,
avoiding redundant control logic when called in higher-level contexts (for example, when
used as the unitary operand in a phase-estimation flow):

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from classiq import *


    @qfunc
    def my_grover_operator(
        oracle: QCallable[QArray[QBit]],
        space_transform: QCallable[QArray[QBit]],
        target: QArray[QBit],
    ):
        oracle(target)
        within_apply(
            lambda: invert(lambda: space_transform(target)),
            lambda: reflect_about_zero(target),
        )
    ```
  </Tab>

  <Tab title="Native">
    ```
    qfunc my_grover_operator(oracle: qfunc (qbit[]), space_transform: qfunc (qbit[]), target: qbit[]) {
      oracle(target);
      within {
        invert {
          space_transform(target);
        }
      } apply {
        reflect_about_zero(target);
      }
    }
    ```
  </Tab>
</Tabs>
