qrisp.gate_wrap#

gate_wrap(*args, permeability=None, is_qfree=None, name=None, verify=False)[source]#

Decorator to bundle up the quantum instructions of a function into a single gate object. Bundled gate objects can help debugging as it allows for a more clear QuantumCircuit visualisation.

Furthermore, bundling up functions is relevant for Qrisps uncomputation algorithm. When bundling up for uncomputation, this decorator provides the means to annotate the gate objects with information about its permeability and qfree-ness. For further information about these concepts check the uncomputation documentation. Specifying this information allows to skip the computationally costly automatic determination at runtime.

Note that the specified information is not checked for correctness as this would defy the purpose.

A shorthand for gate_wrap(permeability = "args", is_qfree = True) is the lifted decorator.

Warning

Using gate_wrap without specifying permeability and qfree-ness on functions processing a lot of qubits, can causes long compile times, since the unitaries of these gates have to be determined numerically.

Warning

Incorrect information about permeability and qfree-ness can yield incorrect compilation results. If you are unsure, use the verify keyword on a small scale first.

Parameters
permeabilitystring or list, optional

Specify the permeability behavior of the function. When given “args”, it is assumed that the gate is permeable only on the qubits of the arguments. When given “full”, it is assumed that the gate is permeable on every qubit it acts on (i.e. also the result). When given a list of integers it is assumed, that the gate is permeable on the qubits of the arguments corresponding to the integers. The default is None.

is_qfreebool, optional

Specify the qfree-ness of the function. The default is None.

namestring, optional

String which will be used for naming the gate object. The default is None.

verifybool, optional

If set to True, the specified information about permeability and qfree-ness will be checked numerically. The default is False.

Examples

We create a simple function wrapping up multiple gates:

from qrisp import QuantumVariable, cx, x, h, z, gate_wrap

@gate_wrap
def example_function(a, b):

    cx(a,b)
    x(a)
    cx(b,a)
    h(b)

a = QuantumVariable(3)
b = QuantumVariable(3)

example_function(a, b)
>>> print(a.qs)
QuantumCircuit:
--------------
     ┌───────────────────┐
b.0: ┤0                  ├
     │                   │
b.1: ┤1                  ├
     │                   │
b.2: ┤2                  ├
     │  example_function │
a.0: ┤3                  ├
     │                   │
a.1: ┤4                  ├
     │                   │
a.2: ┤5                  ├
     └───────────────────┘
Live QuantumVariables:
---------------------
QuantumVariable a
QuantumVariable b
>>> print(a.qs.transpile())
     ┌───┐                    ┌───┐
b.0: ┤ X ├─────────────────■──┤ H ├──────────
     └─┬─┘┌───┐            │  └───┘┌───┐
b.1: ──┼──┤ X ├────────────┼────■──┤ H ├─────
       │  └─┬─┘┌───┐       │    │  └───┘┌───┐
b.2: ──┼────┼──┤ X ├───────┼────┼────■──┤ H ├
       │    │  └─┬─┘┌───┐┌─┴─┐  │    │  └───┘
a.0: ──■────┼────┼──┤ X ├┤ X ├──┼────┼───────
            │    │  ├───┤└───┘┌─┴─┐  │
a.1: ───────■────┼──┤ X ├─────┤ X ├──┼───────
                 │  ├───┤     └───┘┌─┴─┐
a.2: ────────────■──┤ X ├──────────┤ X ├─────
                    └───┘          └───┘

In the next example, we create a function that performs no quantum gates and specify that it is qfree and permeable on the second argument but not on the first.

from qrisp import QuantumCircuit

@gate_wrap(permeability = [1], is_qfree = True)
def example_function(arg_0, arg_1):

    res = QuantumVariable(1)

    #Append an identity gate
    res.qs.append(QuantumCircuit(3).to_gate(), [arg_0, arg_1, res])

    return res

qv_0 = QuantumVariable(1)
qv_1 = QuantumVariable(1)

res = example_function(qv_0, qv_1)
>>> print(qv_0.qs)
QuantumCircuit:
--------------
        ┌───────────────────┐
qv_0.0: ┤0                  ├
        │                   │
qv_1.0: ┤1 example_function ├
        │                   │
 res.0: ┤2                  ├
        └───────────────────┘
Live QuantumVariables:
---------------------
QuantumVariable qv_0
QuantumVariable qv_1
QuantumVariable res
>>> qv_1.uncompute()
>>> print(qv_0.qs)
QuantumCircuit:
--------------
        ┌───────────────────┐
qv_0.0: ┤0                  ├
        │                   │
qv_1.0: ┤1 example_function ├
        │                   │
 res.0: ┤2                  ├
        └───────────────────┘
Live QuantumVariables:
---------------------
QuantumVariable qv_0
QuantumVariable res

Since arg_1 is marked as permeable, there are no further gates required for uncomputation. The situation is different for the other two QuantumVariables, where #the qubits are not marked as permeable.

>>> qv_0.uncompute(do_it = False)
>>> res.uncompute()
>>> print(qv_0.qs)
QuantumCircuit:
--------------
        ┌───────────────────┐┌──────────────────────┐
qv_0.0: ┤0                  ├┤0                     ├
        │                   ││                      │
qv_1.0: ┤1 example_function ├┤1 example_function_dg ├
        │                   ││                      │
 res.0: ┤2                  ├┤2                     ├
        └───────────────────┘└──────────────────────┘
Live QuantumVariables:
---------------------