.. _evaluation: Basics: evals, join, identity, and QExpr-returning functions ************************************************************ FLEQ provides three namespaces, ``qexpr``, ``qlist`` and ``datalist``, which are available in the header files ``qexpr.h``, ``qlist.h``, and ``datalist.h``, respectively. The functions described below will be scoped by their appropriate namespace, where this scoping can be avoided using the typical C++ keywords ``using namespace ``. The simplest quantum kernel expressions are basic primitives representing single quantum gates. ``QExpr qexpr::_(Args... args)`` For each primitive gate ``(Args...)`` in the |Intel| Quantum SDK (see |DGR|), there is a corresponding primitive quantum kernel expression ``_(Args...)``. The arguments of ``_`` are the same as those of ````. Other basic primitives include ``QExpr qexpr::identity()`` An identity or no-op quantum kernel expression. ``QExpr qexpr::global_phase(double angle)`` A quantum kernel expression that applies a global phase but otherwise has no effect. The distinction between ``identity`` and ``global_phase`` is primarily relevant to unitary control of a ``QExpr``; see :ref:`unitary-control`. The ``angle`` argument to ``global_phase()`` can be dynamic--it need not be resolvable at compile-time. Two quantum kernel expressions ``e1`` and ``e2`` can be combined together in several equivalent ways: ``QExpr qexpr::join(QExpr e1, QExpr e2)`` Returns the sequential composition of ``e1`` followed by ``e2``. In other words, when evaluated, ``qexpr::join(e1,e2)`` will execute the logic associated with ``e1`` followed by the logic associated with ``e2``. ``e1 + e2`` Shorthand for ``qexpr::join(e1, e2)``. ``e2 * e1`` Combines the quantum kernel expressions in *composition order*, meaning that it evaluates its second argument first. Composition order is natural when thinking in terms of matrix multiplication or function composition. Equivalent to ``qexpr::join(e1,e2)``. The third core component of ``QExpr`` functionality are the evaluation functions introduced in :ref:`intro-evaluation`. ``void qexpr::eval_hold(QExpr e)`` Executes the quantum instructions represented by the quantum kernel expression ``e``. It guarantees that the quantum state will be maintained after execution of the instructions. ``void qexpr::eval_release(QExpr e)`` Executes the quantum instructions represented by ``e`` under the assumption that the quantum state need not be preserved after execution. It is the direct analog to the ``release_quantum_state`` directive for ordinary ``quantum_kernel`` functions; see the |DGR| for details. When an evaluation function is called inside a classical function ``f()``, the compiler treats ``f()`` itself as a ``quantum_kernel`` function. This helps lift some of the limitations placed on conventional quantum kernel functions. For one, multiple evaluation calls, or even a single evaluation call in the case of :ref:`branching` and :ref:`bind`, can result in multiple quantum basic blocks. In addition, local qubits, which can only be declared inside ``quantum_kernel`` functions, can now be declared in functions that make evaluation calls to quantum kernel expressions. See :ref:`local_qubits` for more details. A quantum kernel expression can be constructed inline inside an evaluation call, such as .. code-block:: C++ qexpr::eval_hold(qexpr::_PrepZ(q) + qexpr::_H(q)); Alternatively, ordinary C++ functions can return a quantum kernel expression of type ``QExpr``, which can then be evaluated. .. code-block:: C++ QExpr prep_plus_qexpr(qbit& q) { return qexpr::_PrepZ(q) + qexpr::_H(q); } int main() { ... qexpr::eval_hold(prep_plus_qexpr(q)); ... } ``QExpr``-returning functions do come with some limitations and special features: * A function returning a ``QExpr`` must have a single return statement; this is enforced by the FLEQ compilation stage of the |Intel| Quantum Compiler (IQC). If the user desires branching or conditional ``QExpr`` returns, they should use the ``cIf`` functionality described in :ref:`branching`. * Traditional C++ branching and looping is allowed under the same constraints as a ``quantum_kernel`` function for classical data, but generally is not encouraged. Best practice is to use ``cIf`` (:ref:`branching`) and recursion (:ref:`recursion`). There are known cases where traditional branching and FLEQ are incompatible, in particular: * FLEQ does not support loops that are bound by FLEQ functions such as the size of a ``QList`` or ``DataList``. This is because classical loop unrolling comes before FLEQ processing in compilation (see :ref:`compilation-overview`). For example, the following function results in a compiler error: .. code-block:: C++ quantum_kernel void prepAll_qlist_BAD(qlist::QList qs) { for (int i=0; i