Often one is interested in the output of a given function as a single-parameter is varied. For instance, we can calculate the steady-state response of our system as the driving frequency is varied. In cases such as this, where each iteration is independent of the others, we can speedup the calculation by performing the iterations in parallel. In QuTiP, parallel computations may be performed using the qutip.parfor (parallel-for-loop) function.
To use the parfor function we need to define a function of one or more variables, and the range over which these variable are to be iterated. For example:
In [2]: def func1(x): return x, x**2, x**3
In [3]: [a,b,c] = parfor(func1, range(10))
In [4]: print(a)
[0 1 2 3 4 5 6 7 8 9]
In [5]: print(b)
[ 0 1 4 9 16 25 36 49 64 81]
In [6]: print(c)
[ 0 1 8 27 64 125 216 343 512 729]
One can also use a single output variable as:
In [7]: x = parfor(func1, range(10))
In [8]: print(x[0])
[0 1 2 3 4 5 6 7 8 9]
In [9]: print(x[1])
[ 0 1 4 9 16 25 36 49 64 81]
In [10]: print(x[2])
[ 0 1 8 27 64 125 216 343 512 729]
The qutip.parfor function is not limited to just numbers, but also works for a variety of outputs:
In [11]: def func2(x): return x, Qobj(x), 'a' * x
In [12]: [a, b, c] = parfor(func2, range(5))
In [13]: print(a)
[0 1 2 3 4]
In [14]: print(b)
[ Quantum object: dims = [[1], [1]], shape = [1, 1], type = oper, isherm = True
Qobj data =
[[ 0.]]
Quantum object: dims = [[1], [1]], shape = [1, 1], type = oper, isherm = True
Qobj data =
[[ 1.]]
Quantum object: dims = [[1], [1]], shape = [1, 1], type = oper, isherm = True
Qobj data =
[[ 2.]]
Quantum object: dims = [[1], [1]], shape = [1, 1], type = oper, isherm = True
Qobj data =
[[ 3.]]
Quantum object: dims = [[1], [1]], shape = [1, 1], type = oper, isherm = True
Qobj data =
[[ 4.]]]
In [15]: print(c)
['' 'a' 'aa' 'aaa' 'aaaa']
Note
New in QuTiP 3.
One can also define functions with multiple input arguments and even keyword arguments:
In [16]: def sum_diff(x , y, z=0): return x + y, x - y, z
In [17]: parfor(sum_diff, [1, 2, 3], [4, 5, 6], z=5)
Out[17]: [array([5, 7, 9]), array([-3, -3, -3]), array([5, 5, 5])]
Note that the keyword arguments can be anything you like, but the keyword values are not iterated over. The keyword argument num_cpus is reserved as it sets the number of CPU’s used by parfor. By default, this value is set to the total number of physical processors on your system. You can change this number to a lower value, however setting it higher than the number of CPU’s will cause a drop in performance.
Parfor is also useful for repeated tasks such as generating plots corresponding to the dynamical evolution of your system, or simultaneously simulating different parameter configurations.
Note
New in QuTiP 3.
When QuTiP is used with IPython interpreter, there is an alternative parallel for-loop implementation in the QuTiP module qutip.ipynbtools, see qutip.ipynbtools.parfor. The advantage of this parfor implementation is based on IPythons powerful framework for parallelization, so the compute processes are not confined to run on the same host as the main process.
Note
New in QuTiP 3.
New to QuTiP version 3 is the option to run computations in parallel on the cloud computing platform provided by PiCloud. You must have their software installed on your machine, and an active account, for this function to work. Note that, at present, the picloud software is only available for Python version 2.7. Using the picloud function is very similar to using parfor, however the picloud function does not accept any keyword arguments:
>>> from qutip.picloud import *
>>> def add(x, y): return x + y
>>> picloud(add, [10, 20, 30], [5, 6, 7])
[15, 26, 37]