In [1]:
import qutip as qt
import numpy as np
from qutip import QobjEvo
%load_ext cython
In [2]:
N = 5
destroy, create, Id = qt.destroy(N), qt.create(N), qt.qeye(N)
def exp_i(t,args):
return np.exp(-1j*t)
def cos_w(t,args):
return np.cos(args["w"]*t)
tlist = np.linspace(0,10,10000)
tlistlog = np.logspace(-3,1,10000)
# state vector as np array
vec = np.arange(N)*.5+.5j
vec_super = np.arange(N**2)*.5+.5j
mat_c = (np.arange(N**2)*.5+.5j).reshape((5,5))
mat_f = np.asfortranarray(mat_c*1.)
# Construct QobjEvo of all type
td_cte1 = QobjEvo(Id)
td_cte2 = QobjEvo([Id])
td_func = QobjEvo([Id,[create,exp_i],[destroy,cos_w]],args={"w":2})
td_str = QobjEvo([Id,[create,"exp(-1j*t)"],[destroy,"cos(w*t)"]],args={'w':2.})
td_array = QobjEvo([Id,[create,np.exp(-1j*tlist)],[destroy,np.cos(2*tlist)]],tlist=tlist)
td_array_log = QobjEvo([Id,[create,np.exp(-1j*tlistlog)],[destroy,np.cos(2*tlistlog)]],tlist=tlistlog)
td_super = qt.liouvillian(td_func, c_ops=td_cte1)
In [3]:
from qutip.cy.spmatfuncs import spmv, cy_expect_rho_vec, cy_expect_psi
In [4]:
spmv(td_func(2).data, vec) == td_func.mul_vec(2,vec)
Out[4]:
In [5]:
print(td_func(2).data * mat_c == td_func.mul_mat(2,mat_c))
mat_c.flags
Out[5]:
In [6]:
print(td_func(2).data * mat_f == td_func.mul_mat(2,mat_f))
mat_f.flags
Out[6]:
In [7]:
cy_expect_psi(td_str(2).data, vec, 0) == td_str.expect(2, vec, 0)
Out[7]:
In [8]:
cy_expect_rho_vec(td_super(2).data, vec_super, 0) == td_super.expect(2, vec_super, 0)
Out[8]:
In [9]:
def coeff_state(t, args):
return np.max(args["psi"])*args["w"]
td_state = QobjEvo([Id, [destroy, coeff_state]],args={"w":1, "psi=vec":qt.basis(N,0)})
In [10]:
td_state(2,state=vec)
Out[10]:
In [11]:
td_str.compiled = False
print("Before compilation")
%timeit td_str(2, data=True)
%timeit td_str.mul_vec(2,vec)
%timeit td_str.mul_mat(2,mat_c)
%timeit td_str.mul_mat(2,mat_f)
%timeit td_str.expect(2,vec,0)
td_str.compile()
print("After compilation")
%timeit td_str(2, data=True)
%timeit td_str.mul_vec(2,vec)
%timeit td_str.mul_mat(2,mat_c)
%timeit td_str.mul_mat(2,mat_f)
%timeit td_str.expect(2,vec,0)
In [12]:
def multiply(qobj, b, factor = 3.):
return qobj*b*factor
print(td_func.apply(multiply,2)(2) == td_func(2)*6)
print(td_func.apply(multiply,2,factor=2)(2) == td_func(2)*4)
In [13]:
def rescale_time_and_scale(f_original, time_scale, factor=2.):
def f(t, *args, **kwargs):
return f_original(time_scale*t, *args, **kwargs)*factor
return f
print(td_func.apply_decorator(rescale_time_and_scale, 2)(2) == td_func(4)*2-Id)
print(td_func.apply_decorator(rescale_time_and_scale, 3, factor=3)(2) ==
td_func(6)*3.0 - 2*Id)
QobjEvo based on string and np.array are changed to a function then the decorator is applied. There are option so that the type of coefficient stay unchanged:
str_mod : change the string -> str_mod[0] + str + str_mod[1]
inplace_np : modify the array (array[i] = decorator(lambda v: v)(array[i]))
*any modification that rescale the time will not work properly
Decorator can cause problem when used in parallel. (function cannot be pickled error)
In [14]:
td_func_1 = QobjEvo([[create,exp_i]],args={"w":2})
td_str_1 = QobjEvo([[create,"exp(-1j*t)"]],args={'w':2.})
td_array_1 = QobjEvo([[create,np.exp(-1j*tlist)]],tlist=tlist)
def square_qobj(qobj):
return qobj*qobj
def square_f(f_original):
def f(t, *args, **kwargs):
return f_original(t, *args, **kwargs)**2
return f
t1 = td_func_1.apply(square_qobj).apply_decorator(square_f)
print(t1(2) == td_func_1(2)*td_func_1(2))
print((t1.ops[0][2]))
t1 = td_str_1.apply(square_qobj).apply_decorator(square_f)
print(t1(2) == td_str_1(2)*td_str_1(2))
print("str not updated:", (t1.ops[0][2]))
t1 = td_str_1.apply(square_qobj).apply_decorator(square_f, str_mod=["(",")**2"])
print(t1(2) == td_str_1(2)*td_str_1(2))
print("str updated:",(t1.ops[0][2]))
t1 = td_array_1.apply(square_qobj).apply_decorator(square_f)
print(t1(2) == td_array_1(2)*td_array_1(2))
print("array not updated:",(t1.ops[0][2]))
t1 = td_array_1.apply(square_qobj).apply_decorator(square_f, inplace_np=1)
print(t1(2) == td_array_1(2)*td_array_1(2))
print("array updated:",(t1.ops[0][2]))
In [15]:
small = qt.destroy(2)
def f1(t,args):
return np.sin(t)
def f2(t,args):
return np.cos(args["w"]*t)
def f3(t,args):
return np.sin(args["w"]*t)
def f4(t,args):
return np.cos(t)
In [16]:
td_redoundance = QobjEvo([qt.qeye(2),[small,"sin(t)"],[small,"cos(w*t)"],[small,"sin(w*t)"],
[small,"cos(t)"]],args={'w':2.})
td_redoundance1 = QobjEvo([qt.qeye(2),[small,"sin(t)"],[small,"cos(w*t)"],[small,"sin(w*t)"],
[small,"cos(t)"]],args={'w':2.})
td_redoundance2 = QobjEvo([qt.qeye(2),[small,f1],[small,f2],[small,f3],[small,f4]],args={'w':2.})
td_redoundance3 = QobjEvo([qt.qeye(2),[small,np.sin(tlist)],[small,np.cos(2*tlist)],
[small,np.sin(2*tlist)],[small,np.cos(tlist)]],tlist=tlist)
td_redoundance4 = QobjEvo([qt.qeye(2),[small,f1],[small,"cos(w*t)"],
[small,np.sin(2*tlist)],[small,"cos(t)"]],args={'w':2.},tlist=tlist)
td_redoundance1.compress()
print(td_redoundance1.to_list())
print(len(td_redoundance1.ops))
print(td_redoundance(1.) == td_redoundance1(1.))
td_redoundance2.compress()
print(len(td_redoundance2.ops))
print(td_redoundance(1.) == td_redoundance2(1.))
td_redoundance3.compress()
print(len(td_redoundance3.ops))
print(td_redoundance(1.) == td_redoundance3(1.))
td_redoundance4.compress()
print(len(td_redoundance4.ops))
print(td_redoundance(1.) == td_redoundance4(1.))
In [17]:
td_redoundance_v2 = QobjEvo([qt.qeye(2),[qt.qeye(2),"sin(t)"],[small,"sin(t)"],[qt.create(2),"sin(t)"]])
td_redoundance_v2.compress()
td_redoundance_v2.to_list()
Out[17]:
The cython object can be 'cimported'.
cdef class CQobjEvo:
cdef void _mul_vec(self, double t, complex* vec, complex* out)
cdef void _mul_matf(self, double t, complex* mat, complex* out,int nrow, int ncols)
cdef void _mul_matc(self, double t, complex* mat, complex* out,int nrow, int ncols)
cdef complex _expect(self, double t, complex* vec, int isherm)
cdef complex _expect_super(self, double t, complex* rho, int isherm)
In [18]:
%%cython
from qutip.cy.cqobjevo cimport CQobjEvo
cimport numpy as np
def rhs_call_from_cy(CQobjEvo qobj, double t, np.ndarray[complex, ndim=1] vec, np.ndarray[complex, ndim=1] out):
qobj._mul_vec(t,&vec[0],&out[0])
def expect_call_from_cy(CQobjEvo qobj, double t, np.ndarray[complex, ndim=1] vec, int isherm):
return qobj._expect(t,&vec[0])
def rhs_cdef_timing(CQobjEvo qobj, double t, np.ndarray[complex, ndim=1] vec, np.ndarray[complex, ndim=1] out):
cdef int i
for i in range(10000):
qobj._mul_vec(t,&vec[0],&out[0])
def expect_cdef_timing(CQobjEvo qobj, double t, np.ndarray[complex, ndim=1] vec, int isherm):
cdef complex aa = 0.
cdef int i
for i in range(10000):
aa = qobj._expect(t, &vec[0])
return aa
def rhs_def_timing(qobj, double t, np.ndarray[complex, ndim=1] vec, complex[::1] out):
cdef int i
for i in range(10000):
out = qobj.mul_vec(t,vec)
def expect_def_timing(qobj, double t, np.ndarray[complex, ndim=1] vec, int isherm):
cdef complex aa = 0.
cdef int i
for i in range(10000):
aa = qobj.expect(t, vec)
return aa
In [19]:
td_str.compile()
print(expect_call_from_cy(td_str.compiled_qobjevo, 2, vec, 0) - td_str.expect(2,vec,0))
%timeit expect_def_timing(td_str.compiled_qobjevo, 2, vec, 0)
%timeit expect_cdef_timing(td_str.compiled_qobjevo, 2, vec, 0)
In [20]:
out = np.zeros(N,dtype=np.complex128)
rhs_call_from_cy(td_str.compiled_qobjevo, 2, vec, out)
print( [a - b for a,b in zip(out, td_str.mul_vec(2,vec))])
%timeit rhs_def_timing(td_str.compiled_qobjevo, 2, vec, out)
%timeit rhs_cdef_timing(td_str.compiled_qobjevo, 2, vec, out)
# Most of the time gained is from allocating the out vector, not the
In [21]:
td_cte = QobjEvo([Id])
td_cte.compile()
out = np.zeros(N,dtype=np.complex128)
rhs_call_from_cy(td_cte.compiled_qobjevo, 2, vec, out)
print( [a - b for a,b in zip(out, td_cte.mul_vec(2,vec))])
%timeit rhs_def_timing(td_cte.compiled_qobjevo, 2, vec, out)
%timeit rhs_cdef_timing(td_cte.compiled_qobjevo, 2, vec, out)
In [22]:
td_str.compiled = False
print(td_str.compile(code=True))
In [23]:
qt.about()
In [ ]: