~jrjohansson/qutip/master

« back to all changes in this revision

Viewing changes to qutip/mcsolve.py

  • Committer: Paul Nation
  • Date: 2011-04-21 04:46:56 UTC
  • Revision ID: git-v1:dd4c966b490aa468dfbd28cef66694df4bf235c8

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#This file is part of QuTIP.
 
2
#
 
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.
 
7
#
 
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.
 
12
#
 
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/>.
 
15
#
 
16
# Copyright (C) 2011, Paul D. Nation & Robert J. Johansson
 
17
#
 
18
###########################################################################
 
19
from multiprocessing import Pool,cpu_count
 
20
from scipy import *
 
21
from scipy.integrate import *
 
22
import scipy.linalg as la
 
23
from qobj import *
 
24
from expect import *
 
25
import sys,os,time
 
26
from Counter import *
 
27
from istests import *
 
28
 
 
29
def mcsolve(Heff,psi0,tlist,ntraj,collapse_ops,expect_ops):
 
30
    mc=MC_class(Heff,psi0,tlist,ntraj,collapse_ops,expect_ops)
 
31
    mc.run()
 
32
    if mc.num_collapse==0 and mc.num_expect==0:
 
33
        return mc.psi_out
 
34
    elif mc.num_collapse==0 and mc.num_expect!=0:
 
35
        return mc.expect_out
 
36
    elif mc.num_collapse!=0 and mc.num_expect==0:
 
37
        return mc.collapse_times_out,mc.which_op_out
 
38
    elif mc.num_collapse!=0 and mc.num_expect!=0:
 
39
        return mc.expect_out
 
40
       
 
41
 
 
42
 
 
43
######---Monte-Carlo class---######
 
44
class MC_class():
 
45
    def __init__(self,Heff,psi0,tlist,ntraj,collapse_ops,expect_ops):
 
46
        self.times=tlist
 
47
        self.ntraj=ntraj
 
48
        self.num_times=len(tlist)
 
49
        self.collapse_ops=collapse_ops
 
50
        self.num_collapse=len(collapse_ops)
 
51
        self.expect_ops=expect_ops
 
52
        self.num_expect=len(expect_ops)
 
53
        self.Hdata=-1.0j*Heff.data# extract matrix and multiply Heff by -1j so user doesn't have to.
 
54
        self.psi_in=psi0.full() #need dense matrix as input to ODE solver.
 
55
        self.psi_dims=psi0.dims
 
56
        self.psi_shape=psi0.shape
 
57
        if self.num_collapse==0 and self.num_expect==0:
 
58
            self.psi_out=array([qobj() for k in xrange(self.num_times)])#preallocate array of qobjs
 
59
        elif self.num_collapse==0 and self.num_expect!=0:#no collpase expectation values
 
60
            self.expect_out=[]
 
61
            self.isher=isherm(self.expect_ops)#checks if expectation operators are hermitian
 
62
            for jj in xrange(self.num_expect):#expectation operators evaluated at initial conditions
 
63
                if self.isher[jj]==1:
 
64
                    self.expect_out.append(zeros(self.num_times))
 
65
                else:
 
66
                    self.expect_out.append(zeros(self.num_times,dtype=complex))
 
67
        elif self.num_collapse!=0:
 
68
            self.bar=Counter(self.ntraj)#create GUI element if avaliable
 
69
            #extract matricies from collapse operators
 
70
            self.norm_collapse_data=array([(op.dag()*op).data for op in self.collapse_ops])
 
71
            self.collapse_ops_data=array([op.data for op in self.collapse_ops])
 
72
            #preallocate #ntraj arrays for state vectors, collapse times, and which operator
 
73
            self.collapse_times_out=zeros((self.ntraj),dtype=ndarray)
 
74
            self.which_op_out=zeros((self.ntraj),dtype=ndarray)
 
75
            if self.num_expect==0:# if no expectation operators, preallocate #ntraj arrays for state vectors
 
76
                self.psi_out=zeros((self.ntraj),dtype=ndarray)
 
77
            else: #preallocate array of lists for expectation values
 
78
                self.isher=isherm(self.expect_ops)
 
79
                self.expect_out=[[] for x in xrange(self.ntraj)]
 
80
    #------------------------------------------------------------------------------------
 
81
    def callback(self,results):
 
82
        r=results[0]
 
83
        if self.num_expect==0:#output state-vector
 
84
            self.psi_out[r]=array([qobj(psi,dims=self.psi_dims,shape=self.psi_shape) for psi in results[1]])
 
85
        else:#output expectation values
 
86
            self.expect_out[r]=results[1]
 
87
        self.collapse_times_out[r]=results[2]
 
88
        self.which_op_out[r]=results[3]
 
89
        self.bar.update()
 
90
    #########################
 
91
    def run(self):
 
92
        if self.num_collapse==0:
 
93
            if self.ntraj!=1:#check if ntraj!=1 which is pointless for no collapse operators
 
94
                print 'No collapse operators specified.\nRunning a single trajectory only.\n'
 
95
            if self.num_expect==0:# return psi QOBJ at each requested time 
 
96
                self.psi_out=no_collapse_psi_out(self.Hdata,self.psi_in,self.times,self.num_times,self.psi_dims,self.psi_shape,self.psi_out)
 
97
            else:# return expectation values of requested operators
 
98
                self.expect_out=no_collapse_expect_out(self.Hdata,self.psi_in,self.times,self.expect_ops,self.num_expect,self.num_times,self.psi_dims,self.psi_shape,self.expect_out,self.isher)
 
99
        elif self.num_collapse!=0:
 
100
            args=(self.Hdata,self.psi_in,self.times,self.num_times,self.num_collapse,self.collapse_ops_data,self.norm_collapse_data,self.num_expect,self.expect_ops,self.isher)
 
101
            pool=Pool(processes=cpu_count())
 
102
            for nt in xrange(0,self.ntraj):
 
103
                pool.apply_async(mc_alg_evolve,args=(nt,args),callback=self.callback)
 
104
            pool.close()
 
105
            pool.join()
 
106
            self.bar.finish() #stop GUI if running
 
107
 
 
108
 
 
109
 
 
110
######---return psi at requested times for no collapse operators---######
 
111
def no_collapse_psi_out(Hdata,psi_in,tlist,num_times,psi_dims,psi_shape,psi_out):
 
112
    def RHS(t,psi):#define RHS of ODE
 
113
            return Hdata*psi #cannot use dot(a,b) since mat is matrix and not array.
 
114
    ODE=ode(RHS)
 
115
    ODE.set_integrator('zvode',method='adams',nsteps=500,atol=1e-6) #initialize ODE solver for RHS
 
116
    ODE.set_initial_value(psi_in,tlist[0]) #set initial conditions
 
117
    psi_out[0]=qobj(psi_in,dims=psi_dims,shape=psi_shape)
 
118
    for k in xrange(1,num_times):
 
119
        ODE.integrate(tlist[k],step=0) #integrate up to tlist[k]
 
120
        if ODE.successful():
 
121
            psi_out[k]=qobj(ODE.y/la.norm(ODE.y),dims=psi_dims,shape=psi_shape)
 
122
        else:
 
123
            raise ValueError('Error in ODE solver')
 
124
    return psi_out
 
125
#------------------------------------------------------------------------
 
126
 
 
127
 
 
128
######---return expectation values at requested times for no collapse operators---######
 
129
def no_collapse_expect_out(Hdata,psi_in,tlist,expect_ops,num_expect,num_times,psi_dims,psi_shape,expect_out,isher):
 
130
    ######---Define RHS of ODE---##############
 
131
    def RHS(t,psi):
 
132
            return Hdata*psi #cannot use dot(a,b) since mat is matrix and not array.
 
133
    ######-------------------------------------
 
134
    ODE=ode(RHS)
 
135
    ODE.set_integrator('zvode',method='adams',nsteps=500,atol=1e-6) #initialize ODE solver for RHS
 
136
    ODE.set_initial_value(psi_in,tlist[0]) #set initial conditions
 
137
    for jj in xrange(num_expect):
 
138
        expect_out[jj][0]=mc_expect(expect_ops[jj],psi_in,isher[jj])
 
139
    for k in xrange(1,num_times):
 
140
        ODE.integrate(tlist[k],step=0) #integrate up to tlist[k]
 
141
        if ODE.successful():
 
142
            state=ODE.y/la.norm(ODE.y)
 
143
            for jj in xrange(num_expect):
 
144
                expect_out[jj][k]=mc_expect(expect_ops[jj],state,isher[jj])
 
145
        else:
 
146
            raise ValueError('Error in ODE solver')
 
147
    return expect_out #return times and expectiation values
 
148
#------------------------------------------------------------------------
 
149
 
 
150
 
 
151
######---single-trajectory for monte-carlo---###########           
 
152
def mc_alg_evolve(nt,args):
 
153
    """
 
154
    Monte-Carlo algorithm returning state-vector at times tlist[k] for a single trajectory
 
155
    """
 
156
    Hdata,psi_in,tlist,num_times,num_collapse,collapse_ops_data,norm_collapse_data,num_expect,expect_ops,isher=args
 
157
    def RHS(t,psi):
 
158
            return Hdata*psi #cannot use dot(a,b) since mat is matrix and not array.
 
159
    if num_expect==0:
 
160
        psi_out=array([zeros((len(psi_in),1),dtype=complex) for k in xrange(num_times)])#preallocate real array of qobjs
 
161
        psi_out[0]=psi_in
 
162
    else:
 
163
        expect_out=[]
 
164
        for i in isher:
 
165
            if i==1:#preallocate real array of zeros
 
166
                expect_out.append(zeros(num_times))
 
167
            else:#preallocate complex array of zeros
 
168
                expect_out.append(zeros(num_times,dtype=complex))    
 
169
        for jj in xrange(num_expect):
 
170
            expect_out[jj][0]=mc_expect(expect_ops[jj],psi_in,isher[jj])
 
171
    collapse_times=[] #times at which collapse occurs
 
172
    which_oper=[] # which operator did the collapse
 
173
    random.seed()
 
174
    rand_vals=random.random(2)#first rand is collapse norm, second is which operator
 
175
    ODE=ode(RHS)
 
176
    ODE.set_integrator('zvode',method='adams',nsteps=500,atol=1e-6) #initialize ODE solver for RHS
 
177
    ODE.set_initial_value(psi_in,tlist[0]) #set initial conditions
 
178
    for k in xrange(1,num_times):
 
179
        last_t=ODE.t;last_y=ODE.y
 
180
        step_flag=1
 
181
        while ODE.successful() and ODE.t<tlist[k]:
 
182
            ODE.integrate(tlist[k],step=step_flag) #integrate up to tlist[k], one step at a time.
 
183
            if ODE.t>tlist[k]:
 
184
                ODE.set_integrator('zvode',method='adams',nsteps=500,atol=1e-6,first_step=(tlist[k]-last_t))
 
185
                ODE.set_initial_value(last_y,last_t)
 
186
                step_flag=0
 
187
            else:
 
188
                psi_nrm=la.norm(ODE.y)
 
189
                if psi_nrm<0.0:#safety check for norm<0
 
190
                    psi_nrm,ODE=norm_safety(ODE,tlist,psi_nrm,last_y,last_t)#find non-zero psi norm
 
191
                if psi_nrm<=rand_vals[0]:#collpase has occured
 
192
                    collapse_times.append(ODE.t)
 
193
                    m=0.0
 
194
                    n_dp=array([real(dot(ODE.y.conj().T,op*ODE.y)[0,0]) for op in norm_collapse_data])
 
195
                    dp=sum(n_dp)
 
196
                    for j in xrange(num_collapse):
 
197
                        m+=n_dp[j]/dp
 
198
                        if m>=rand_vals[1]:
 
199
                            which_oper.append(j) #record which operator did collapse
 
200
                            state=collapse_ops_data[j]*ODE.y
 
201
                            psi_nrm=la.norm(state)
 
202
                            state=state/psi_nrm
 
203
                            ODE.y=state
 
204
                            random.seed()
 
205
                            rand_vals=random.random(2)
 
206
                            #last_y=ODE.y;last_t=ODE.t
 
207
                            break #breaks out of for-loop
 
208
                last_t=ODE.t;last_y=ODE.y
 
209
        ###--after while loop--####
 
210
        psi_nrm=la.norm(ODE.y)
 
211
        if num_expect==0:
 
212
            psi_out[k]=ODE.y/psi_nrm
 
213
        else:
 
214
            state=ODE.y/psi_nrm
 
215
            for jj in xrange(num_expect):
 
216
                expect_out[jj][k]=mc_expect(expect_ops[jj],state,isher[jj])
 
217
    if num_expect==0:
 
218
        return nt,psi_out,array(collapse_times),array(which_oper)
 
219
    else:
 
220
        return nt,expect_out,array(collapse_times),array(which_oper)
 
221
#------------------------------------------------------------------------------------------
 
222
 
 
223
 
 
224
 
 
225
######---if psi norm is less than zero---########### 
 
226
def norm_safety(ODE,tlist,psi_nrm,last_y,last_t):
 
227
    print 'wavefunction norm below zero, reducing step size.'
 
228
    ntrys=1
 
229
    while ODE.successful() and la.norm(ODE.y)<0 and ntrys<=10:#reduce step-size by half and integrate again until norm>0 or more than 10 attempts
 
230
        ODE.set_integrator('zvode',method='adams',nsteps=500,atol=1e-6,first_step=(ODE.t-last_t)/2.0)
 
231
        ODE.set_initial_value(last_y,last_t)
 
232
        ODE.integrate(tlist[k],step=1)
 
233
        ntrys+=1
 
234
    psi_nrm=la.norm(ODE.y)
 
235
    if psi_nrm<0:# if norm still <0 return error
 
236
        raise ValueError('State vector norm<0 after reducing ODE step-size 10 times.')
 
237
    ODE.set_integrator('zvode',method='adams',nsteps=500,atol=1e-6)
 
238
    return psi_nrm,ODE
 
239
#------------------------------------------------------------------------
 
240
 
 
241
 
 
242
def mc_expect(oper,state,isherm):
 
243
    if isherm==1:
 
244
        return real(dot(conj(state).T,oper.data*state))
 
245
    else:
 
246
        return complex(dot(conj(state).T,oper.data*state))
 
 
b'\\ No newline at end of file'