Quantum Function Injection#
If you managed to read this far, you realized already that every quantum gate operates in-place on it’s qubits. While in-place functions are an established paradigm in classical computing, it would be tempting to just forbid out-of-place functions within Qrisp. This would however require all variable declarations to be executed before anything happens and thus inevitably result in unreadable and cluttered code.
Out-of-place functions also have their place in quantum and the injection operator manages to bridge the two worlds. In particular this structure allows the programmer to write/test/maintain quantum functions with out-of-place syntax but the user to use them as in-place functions, when required.
The injection operator is called via the <<
symbol and transforms function given as the second operand into an in-place function operating on the first operand:
from qrisp import mcx, QuantumBool
# Create a simple out-of-place function, computing the
# AND value of two inputs
def AND(a, b):
res = QuantumBool()
mcx([a, b], res)
return res
injection_target = QuantumBool()
# Perform some computation on the injection target
injection_target.flip()
# We can now inject the target into the function
injected_function = (injection_target << AND)
# Calling this function will now operate on injection_target instead of allocating a new QuantumBool
a = QuantumBool()
b = QuantumBool()
injected_function(a, b)
print(injection_target.qs)
# QuantumCircuit:
# ---------------
# ┌───┐┌───┐
# injection_target.0: ┤ X ├┤ X ├
# └───┘└─┬─┘
# a.0: ───────■──
# │
# b.0: ───────■──
# Live QuantumVariables:
# ----------------------
# QuantumBool injection_target
# QuantumBool a
# QuantumBool b
Another example - the injection operator can be used for manual uncomputation of out-of-place arithmetic.
from qrisp import QuantumFloat, z, invert
a = QuantumFloat(5)
b = QuantumFloat(5)
a[:] = 3
b[:] = 4
# The multiplication operator is an out-of-place function
c = a*b
print(c)
# Yields: {12: 1.0}
# In an actual algorithm, something would be done now.
# After that, we can do manual uncomputation by injecting
# the multiplication function into the result and
# inverting the whole process.
mult_func = lambda x, y : x*y
with invert():
(c << mult_func)(a, b)
print(c)
# Yields: {0: 1.0}
# c can now be deallocated.
c.delete()
Warning
The operator can only be used to inject QuantumVariables
of matching size - injecting a size 10 QuantumFloat
into a QuantumBool
will raise an error. When called within Jasp, the operator is restricted to quantum functions, where the return value has never been sliced.