1
#This file is part of QuTIP.
3
# QuTIP is free software: you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation, either version 3 of the License, or
6
# (at your option) any later version.
8
# QuTIP is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with QuTIP. If not, see <http://www.gnu.org/licenses/>.
16
# Copyright (C) 2011, Paul D. Nation & Robert J. Johansson
18
###########################################################################
20
import scipy.sparse as sp
21
import scipy.linalg as la
26
# Quantum Object class. Describes the composition of a quantum system and
27
# implements common operations.
33
@brief Quantum object class.
34
requires: scipy, scipy.sparse.csr_matrix, scipy.linalg
36
################## Define qobj class #################
37
__array_priority__=100 #sets QOBJ priority above numpy arrays
38
def __init__(self,inpt=array([[0]]),dims=[[],[]],shape=[]):
40
qobj constructor. Optionally takes the dimension array and/or
41
shape array as arguments.
43
@param array Data for vector/matrix representation of the quantum object
47
if isinstance(inpt,qobj):#if input is already QOBJ then return identical copy
48
self.data=sp.csr_matrix(inpt.data) #make sure matrix is sparse (safety check)
57
else:#if input is NOT QOBJ
58
#if input is int, float, or complex then convert to array
59
if isinstance(inpt,(int,float,complex)):
61
#case where input is array or sparse
62
if (isinstance(inpt,ndarray)) or (isinstance(inpt,sp.csr_matrix)):
63
self.data=sp.csr_matrix(inpt) #data stored as space array
65
self.dims=[[inpt.shape[0]],[inpt.shape[1]]] #list of object dimensions
69
self.shape=[inpt.shape[0],inpt.shape[1]] # list of matrix dimensions
72
elif isinstance(inpt,list):# case where input is not array or sparse, i.e. a list
73
if len(array(inpt).shape)==1:#if list has only one dimension (i.e [5,4])
75
else:#if list has two dimensions (i.e [[5,4]])
77
self.data=sp.csr_matrix(inpt)
79
self.dims=[[inpt.shape[0]],[inpt.shape[1]]]
83
self.shape=[inpt.shape[0],inpt.shape[1]]
87
##### Definition of PLUS with qobj on LEFT (ex. qobj+4) #########
88
def __add__(self, other): #defines left addition for qobj class
89
if classcheck(other)=='eseries':
90
return other.__radd__(self)
92
if prod(other.shape)==1 and prod(self.shape)!=1: #case for scalar quantum object
93
dat=array(other.full())[0][0]
95
out.data=self.data+dat*sp.csr_matrix(ones(self.shape))
99
elif prod(self.shape)==1 and prod(other.shape)!=1:#case for scalar quantum object
100
dat=array(self.full())[0][0]
102
out.data=dat*sp.csr_matrix(ones(other.shape))+other.data
104
out.shape=other.shape
106
elif self.dims!=other.dims:
107
raise TypeError('Incompatible quantum object dimensions')
108
elif self.shape!=other.shape:
109
raise TypeError('Matrix shapes do not match')
110
else:#case for matching quantum objects
112
out.data=self.data+other.data
117
##### Definition of PLUS with qobj on RIGHT (ex. 4+qobj) ###############
118
def __radd__(self,other): #defines left addition for qobj class
121
##### Definition of SUBTRACTION with qobj on LEFT (ex. qobj-4) #########
122
def __sub__(self,other):
125
##### Definition of SUBTRACTION with qobj on RIGHT (ex. 4-qobj) #########
126
def __rsub__(self,other):
129
##### Definition of Multiplication with qobj on left (ex. qobj*5) #########
130
def __mul__(self,other):
131
if isinstance(other,qobj): #if both are quantum objects
132
if prod(other.shape)==1 and prod(self.shape)!=1:#case for scalar quantum object
133
dat=array(other.data.todense())[0][0]
135
out.data=dat * (sp.csr_matrix(ones(self.shape))*self.data)
139
elif prod(self.shape)==1 and prod(other.shape)!=1:#take care of right mul as well for scalar qobjs
140
dat=array(self.data.todense())[0][0]
142
out.data=dat*sp.csr_matrix(ones(other.shape))*other.data
144
out.shape=other.shape
146
elif self.shape[1]==other.shape[0] and self.dims[1]==other.dims[0]:
148
out.data=self.data*other.data
149
out.dims = [self.dims[0], other.dims[1]]
150
out.shape = [self.shape[0], other.shape[1]]
153
raise TypeError("Incompatible qobj shapes")
155
if isinstance(other, list): # if other is a list, do element-wise multiplication
158
prod_list.append(self * item)
161
if classcheck(other)=='eseries':
162
return other.__rmul__(self)
164
if isinstance(other,(int,float,complex)): #if other is int,float,complex
166
out.data=self.data*other
171
raise TypeError("Incompatible object for multiplication")
173
##### Definition of Multiplication with qobj on right (ex. 5*qobj) #########
174
def __rmul__(self,other):
175
if isinstance(other,qobj): #if both are quantum objects
176
if prod(other.shape)==1 and prod(self.shape)!=1:#case for scalar quantum object
177
dat=array(other.data.todense())[0][0]
179
out.data=dat * (sp.csr_matrix(ones(self.shape))*self.data)
183
elif prod(self.shape)==1 and prod(other.shape)!=1:#take care of right mul as well for scalar qobjs
184
dat=array(self.data.todense())[0][0]
186
out.data=dat*sp.csr_matrix(ones(other.shape))*other.data
188
out.shape=other.shape
190
elif self.shape[1]==other.shape[0] and self.dims[1]==other.dims[0]:
192
out.data=other.data * self.data
194
out.shape=[self.shape[0],other.shape[1]]
197
raise TypeError("Incompatible qobj shapes")
199
if isinstance(other, list): # if other is a list, do element-wise multiplication
202
prod_list.append(item * self)
205
if classcheck(other)=='eseries':
206
return other.__mul__(self)
208
if isinstance(other,(int,float,complex)): #if other is int,float,complex
210
out.data=other*self.data
215
raise TypeError("Incompatible object for multiplication")
217
##### Definition of Division with qobj on left (ex. qobj / sqrt(2)) #########
218
def __div__(self,other):
219
if isinstance(other,qobj): #if both are quantum objects
220
raise TypeError("Incompatible qobj shapes [division with qobj not implemented]")
221
#if isinstance(other,qobj): #if both are quantum objects
222
# raise TypeError("Incompatible qobj shapes [division with qobj not implemented]")
223
if isinstance(other,(int,float,complex)): #if other is int,float,complex
225
out.data=self.data/other
230
raise TypeError("Incompatible object for division")
240
#return "Quantum object: ", dimensions = " + str(self.shape) + "\n" + str(self.data)
241
print "Quantum object: " + "dims = " + str(self.dims) + ", shape = " + str(self.shape)
246
#####---functions acting on quantum objects---######################
248
#returns Adjont (dagger) of operator
250
out.data=self.data.T.conj()
251
out.dims=[self.dims[1],self.dims[0]]
252
out.shape=[self.shape[1],self.shape[0]]
255
#returns norm of quantum object
256
return la.norm(self.full())
258
#returns trace of quantum object
259
return sum(diag(self.full()))
261
#returns dense matrix form of quantum object data
262
if isinstance(self.data, ndarray):
264
return array(self.data.todense())
266
if self.dims[0][0]==self.dims[1][0]:
269
raise TypeError('Invalid operand for matrix exponential');
272
##############################################################################
275
# functions acting on QOBJ class
279
if not isinstance(inqobj,qobj): #checks for qobj
280
raise TypeError("Input is not a quantum object")
282
outqobj.data=inqobj.data.T.conj()
283
outqobj.dims=[inqobj.dims[1],inqobj.dims[0]]
284
outqobj.shape=[inqobj.shape[1],inqobj.shape[0]]
290
out.dims=[A.dims[1],A.dims[0]]
291
out.shape=[A.shape[1],A.shape[0]]
296
if isinstance(ops,qobj):
300
for k in range(len(ops)):
301
if (ops[k].dag()-ops[k]).norm()/ops[k].norm()>1e-12:#any non-zero elements
307
##############################################################################
310
# some functions for increased compatibility with quantum optics toolbox:
315
if isinstance(obj,qobj):
318
raise TypeError("Incompatible object for dims (not a qobj)")
322
from scipy import shape as shp
323
if isinstance(inpt,qobj):
328
##############################################################################
331
# check for class type (ESERIES,FSERIES)
335
def classcheck(inpt):
337
Checks for ESERIES and FSERIES class types
339
from eseries import eseries
340
if isinstance(inpt,eseries):
346
########################################################################
348
#############################
353
apows= [[] for jj in xrange(int(ceil((m+1)/2)))]
354
apows[0]=sp.eye(n,n).tocsr()
356
for jj in xrange(2,int(ceil((m+1)/2))):
357
apows[jj]=apows[jj-1]*apows[1]
358
U=sp.lil_matrix(zeros((n,n))).tocsr(); V=sp.lil_matrix(zeros((n,n))).tocsr()
359
for jj in xrange(m,0,-2):
360
U=U+c[jj]*apows[jj/2]
362
for jj in xrange(m-1,-1,-2):
363
V=V+c[jj]*apows[(jj+1)/2]
364
F=la.solve((-U+V).todense(),(U+V).todense())
365
return sp.lil_matrix(F).tocsr()
370
U = A*(A6*(c[13]*A6+c[11]*A4+c[9]*A2)+c[7]*A6+c[5]*A4+c[3]*A2+c[1]*sp.eye(n,n).tocsr())
371
V = A6*(c[12]*A6 + c[10]*A4 + c[8]*A2)+ c[6]*A6 + c[4]*A4 + c[2]*A2 + c[0]*sp.eye(n,n).tocsr()
372
F=la.solve((-U+V).todense(),(U+V).todense())
373
return sp.lil_matrix(F).tocsr()
374
#################################
375
A=qo.data #extract Qobj data (sparse matrix)
376
m_vals=array([3,5,7,9,13])
377
theta=array([0.01495585217958292,0.2539398330063230,0.9504178996162932,2.097847961257068,5.371920351148152],dtype=float)
378
normA=la.norm(A.todense(),1)
380
for ii in xrange(len(m_vals)):
385
t,s=frexp(normA/theta[-1])
391
return qobj(F,dims=qo.dims,shape=qo.shape)
396
return array([120, 60, 12, 1])
398
return array([30240, 15120, 3360, 420, 30, 1])
400
return array([17297280, 8648640, 1995840, 277200, 25200, 1512, 56, 1])
402
return array([17643225600, 8821612800, 2075673600, 302702400, 30270240,2162160, 110880, 3960, 90, 1])
404
return array([64764752532480000, 32382376266240000, 7771770303897600,1187353796428800, 129060195264000, 10559470521600,670442572800, 33522128640, 1323241920,40840800, 960960, 16380, 182, 1])