Qaching#
- qache(*func, **kwargs)[source]#
This decorator allows you to mark a function as “reusable”. Reusable here means that the jasp expression of this function will be cached and reused in the next calls (if the function is called with the same signature).
A qached function therefore has to be traced by the Python interpreter only once and after that the function can be called without any Python-interpreter induced delay. This can significantly speed up the compilation process.
Using the
qache
decorator not only improves the compilation speed but also enables the compiler to speed up transformation processes.Warning
Two important rules apply to the
qache
decorator to adhere to the functional programming paradigm.It is illegal to have a qached function return a QuantumVariable that has been passed as an argument to the function.
It is illegal to modify traced attributes of QuantumVariables that have been passed as an argument to the function.
See the examples section for representatives of these cases.
- Parameters:
- funccallable
The function to be qached.
- Returns:
- qached_functioncallable
A function that will be traced on it’s first execution and retrieved from the cache in any other call.
Examples
We create a simple function that is qached. To simulate an expensive compilation task we insert a
time.sleep
command.import time from qrisp import * from qrisp.jasp import qache @qache def inner_function(qv): h(qv[0]) cx(qv[0], qv[1]) res_bl = measure(qv[0]) # Simulate demanding compilation procedure by calling time.sleep(1) return res_bl def main(): a = QuantumVariable(2) b = QuantumFloat(2) bl_0 = inner_function(a) bl_1 = inner_function(b) bl_2 = inner_function(a) bl_3 = inner_function(b) return bl_0 & bl_1 & bl_2 & bl_3 # Measure the time required for tracing t0 = time.time() jaspr = make_jaspr(main)() print(time.time() - t0) # 2.0225703716278076
Even though
inner_function
has been called 4 times, we only see a delay of 2 seconds. This is because the function has been called with two different quantum types, implying it has been traced twice and recalled from the cache twice. We take a look at the Jaspr.>>> print(jaspr) let inner_function = { lambda ; a:QuantumCircuit b:QubitArray. let c:Qubit = jasp.get_qubit b 0 d:QuantumCircuit = jasp.h a c e:Qubit = jasp.get_qubit b 1 f:QuantumCircuit = jasp.cx d c e g:QuantumCircuit h:bool[] = jasp.measure f c in (g, h) } in let inner_function1 = { lambda ; i:QuantumCircuit j:QubitArray k:i32[] l:bool[]. let m:Qubit = jasp.get_qubit j 0 n:QuantumCircuit = jasp.h i m o:Qubit = jasp.get_qubit j 1 p:QuantumCircuit = jasp.cx n m o q:QuantumCircuit r:bool[] = jasp.measure p m in (q, r) } in { lambda ; s:QuantumCircuit. let t:QuantumCircuit u:QubitArray = jasp.create_qubits s 2 v:QuantumCircuit w:QubitArray = jasp.create_qubits t 2 x:QuantumCircuit y:bool[] = pjit[name=inner_function jaxpr=inner_function] v u z:QuantumCircuit ba:bool[] = pjit[name=inner_function jaxpr=inner_function1] x w 0 False bb:QuantumCircuit bc:bool[] = pjit[name=inner_function jaxpr=inner_function] z u bd:QuantumCircuit be:bool[] = pjit[ name=inner_function jaxpr=inner_function1 ] bb w 0 False bf:bool[] = and y ba bg:bool[] = and bf bc bh:bool[] = and bg be bi:QuantumCircuit = jasp.reset bd u bj:QuantumCircuit = jasp.delete_qubits bi u in (bj, bh) }
As expected, we see three different function definitions:
The first one describes
inner_function
called with a QuantumVariable. For this kind of signature only theQubitArray
is required.The second one describes
inner_function
called with QuantumFloat. Additionally to theQubitArray
, the.exponent
and.signed
attribute are also passed to the function.The third function definition is
outer_function
, which calls the previously defined functions.
Illegal functions
We will now demonstrate what type of functions can not be qached.
@qache def inner_function(qv): h(qv[0]) return qv @jaspify def main(): qf_0 = QuantumFloat(2) qf_1 = inner_function(qf_0) return measure(qf_1) main() # Yields: Exception: Found parameter QuantumVariable within returned results
inner_function
returns a QuantumVariable that has been passed as an argument and can therefore not be qached.The second case of an illegal functions is a function that tries to modify a traced attribute of a
QuantumVariable
that has been passed as an argument. A traced attribute is for instance theexponent
attribute of QuantumFloat.@qache def inner_function(qf): qf.exponent += 1 @jaspify def main(): qf = QuantumFloat(2) inner_function(qf) main() # Yields: Exception: Found in-place parameter modification of QuantumVariable qf