77from scipy import sparse as sp
88
99try :
10- from . import short_iterative_lanczos_cython as _sil_cython
10+ from .short_iterative_lanczos_cython import _lanczos_timeprop_cython
1111
1212 have_cython_backend = True
1313except ImportError :
14- _sil_cython = None
14+ _lanczos_timeprop_cython = None
1515 have_cython_backend = False
1616
1717try :
@@ -57,6 +57,17 @@ def _matvec(H, phi):
5757 return H @ phi
5858
5959
60+ def _as_hfun (H ):
61+ """Return an in-place Hfun(t, phi, Hphi) callable for H."""
62+ if callable (H ):
63+ return H
64+
65+ H_f = H .dot if hasattr (H , "dot" ) else H .__matmul__
66+ def Hfun (t , phi , Hphi ):
67+ Hphi [:] = H_f (phi )
68+
69+ return Hfun
70+
6071def _qobj_state_io (phi0 ):
6172 outdims = phi0 .dims
6273 outshape = phi0 .full ().shape
@@ -73,17 +84,7 @@ class _lanczos_timeprop_reference:
7384 def __init__ (self , H , maxsteps , target_convg , debug = 0 , do_full_order = False ):
7485 if have_qutip and isinstance (H , qutip .Qobj ):
7586 H = _qobj_to_matrix (H )
76-
77- if not callable (H ):
78- # time-independent operator
79- # assume it supports dot or matmul for matrix-vector multiplication
80- def Hfun (t , phi , Hphi ):
81- Hphi [:] = _matvec (H , phi )
82- return Hphi
83-
84- self .Hfun = Hfun
85- else :
86- self .Hfun = H
87+ self .Hfun = _as_hfun (H )
8788
8889 self .maxsteps = maxsteps
8990 self .target_convg = target_convg
@@ -95,6 +96,7 @@ def Hfun(t, phi, Hphi):
9596
9697 self .curr_coeff = zeros (maxsteps + 1 , dtype = complex )
9798 self .prev_coeff = self .curr_coeff .copy ()
99+ self .breakdown_tol = 1e-14
98100
99101 def propagate (self , phi0 , ts , maxHT = None ):
100102 phi0 = np .asarray (phi0 ).view (normdotndarray )
@@ -129,6 +131,7 @@ def _step(self, t, HT):
129131 prev_coeff = self .prev_coeff
130132 debug = self .debug
131133 Hfun = self .Hfun
134+ max_lanczos_steps = min (self .maxsteps , phia [0 ].shape [0 ])
132135
133136 HT_done = HT
134137
@@ -140,9 +143,12 @@ def _step(self, t, HT):
140143 # doesn't converge at first step
141144 curr_coeff [:] = 0.0
142145
143- for step in range (1 , self .maxsteps + 1 ):
146+ convg = np .inf
147+ exact_complete = False
148+
149+ for step in range (1 , max_lanczos_steps + 1 ):
144150 # set |phia(step)> to H|phia(step-1)>
145- phia [ step ] = Hfun (t , phia [step - 1 ], phia [step ])
151+ Hfun (t , phia [step - 1 ], phia [step ])
146152 prefacs [step ] = prefacs [step - 1 ]
147153 phinorm = prefacs [step ] * phia [step ].norm ()
148154 # phinorm = sqrt(<q(step-1)|H H|q(step-1)>)
@@ -177,18 +183,23 @@ def _step(self, t, HT):
177183 # i.e. to prefac = 1.d0 / sqrt(<phi|phi>)
178184 phinorm = phia [step ].norm ()
179185 beta [step - 1 ] = prefacs [step ] * phinorm
180- prefacs [step ] = 1.0 / phinorm
181- if abs (log10 (prefacs [step ])) > 4.0 :
182- phia [step ] *= prefacs [step ]
186+ if phinorm <= self .breakdown_tol :
183187 prefacs [step ] = 1.0
184- if abs (beta [step - 1 ]) < 1e-2 and debug > 2 :
185- print ("WARNING! beta[%d]=%g is very small - there seems to be a linearly dependent vector!" % (step , beta [step - 1 ]))
186- if debug > 1 :
187- # check if new vector is orthogonal to all others
188- for ii in range (step ):
189- dotpr = prefacs [ii ] * prefacs [step ] * phia [ii ].dot (phia [step ])
190- if abs (dotpr ) > 1e-12 :
191- print ("WARNING! vectors not orthogonal. dotpr(%d,%d) = %g" % (ii , step , dotpr ))
188+ phia [step ][:] = 0.0
189+ exact_complete = True
190+ else :
191+ prefacs [step ] = 1.0 / phinorm
192+ if abs (log10 (prefacs [step ])) > 4.0 :
193+ phia [step ] *= prefacs [step ]
194+ prefacs [step ] = 1.0
195+ if abs (beta [step - 1 ]) < 1e-2 and debug > 2 :
196+ print ("WARNING! beta[%d]=%g is very small - there seems to be a linearly dependent vector!" % (step , beta [step - 1 ]))
197+ if debug > 1 :
198+ # check if new vector is orthogonal to all others
199+ for ii in range (step ):
200+ dotpr = prefacs [ii ] * prefacs [step ] * phia [ii ].dot (phia [step ])
201+ if abs (dotpr ) > 1e-12 :
202+ print ("WARNING! vectors not orthogonal. dotpr(%d,%d) = %g" % (ii , step , dotpr ))
192203
193204 # check convergence
194205 prev_coeff [:] = curr_coeff [:]
@@ -201,6 +212,9 @@ def _step(self, t, HT):
201212 if debug > 5 :
202213 print ("convg:" , convg )
203214
215+ if exact_complete or step == max_lanczos_steps :
216+ break
217+
204218 if not self .do_full_order and convg < self .target_convg :
205219 break
206220
@@ -209,7 +223,7 @@ def _step(self, t, HT):
209223 print (beta [: step - 1 ])
210224
211225 # if convergence was reached in lanczos_loop, convg < target_convg, and this loop is never entered
212- while convg > self .target_convg :
226+ while ( not exact_complete ) and convg > self .target_convg :
213227 # error (~convg) should be O(HT**maxsteps)
214228 # convg = a * HT**maxsteps
215229 # target_convg = a * HT_new**maxsteps
@@ -258,40 +272,35 @@ def _is_csr_matrix(H):
258272 return csr_array is not None and isinstance (H , csr_array )
259273
260274
261- def _select_backend (H , backend = None ):
262- if backend is None :
263- backend = "auto"
264- else :
265- backend = backend .strip ().lower ()
275+ def _select_backend (H , backend ):
276+ backend = backend .strip ().lower ()
266277
267278 if backend == "python" :
268279 return "python"
269280
270281 if backend == "cython" :
271282 if not have_cython_backend :
272283 raise ValueError ("backend='cython' requested but Cython backend extension is not available." )
273- if _is_dense_matrix (H ) or _is_csr_matrix (H ):
274- return "cython"
275- raise ValueError ("backend='cython' requested but Hamiltonian type is unsupported for cython backend." )
284+ return "cython"
276285
277286 if backend != "auto" :
278287 raise ValueError ("Unknown backend value '%s'. Valid values are 'python', 'cython', 'auto'." % backend )
279288
280- if callable (H ):
281- return "python"
282- if have_cython_backend and (_is_dense_matrix (H ) or _is_csr_matrix (H )):
289+ if have_cython_backend :
283290 return "cython"
284291 return "python"
285292
286293
287294class lanczos_timeprop :
288- def __init__ (self , H , maxsteps , target_convg , debug = 0 , do_full_order = False , backend = None ):
295+ def __init__ (self , H , maxsteps , target_convg , debug = 0 , do_full_order = False , backend = "auto" ):
289296 if have_qutip and isinstance (H , qutip .Qobj ):
290297 H = _qobj_to_matrix (H )
291298 self .backend = _select_backend (H , backend )
292299 if self .backend == "cython" :
293- self ._impl = _sil_cython .CythonLanczosPropagator (H , maxsteps , target_convg , debug ,
294- do_full_order )
300+ if not (_is_dense_matrix (H ) or _is_csr_matrix (H )):
301+ H = _as_hfun (H )
302+
303+ self ._impl = _lanczos_timeprop_cython (H , maxsteps , target_convg , debug , do_full_order )
295304 else :
296305 self ._impl = _lanczos_timeprop_reference (H , maxsteps , target_convg , debug , do_full_order )
297306
@@ -320,6 +329,6 @@ def __getattr__(self, name):
320329 return getattr (self ._impl , name )
321330
322331
323- def sesolve_lanczos (H , phi0 , ts , maxsteps , target_convg , maxHT = None , debug = 0 , do_full_order = False , backend = None ):
332+ def sesolve_lanczos (H , phi0 , ts , maxsteps , target_convg , maxHT = None , debug = 0 , do_full_order = False , backend = "auto" ):
324333 prop = lanczos_timeprop (H , maxsteps , target_convg , debug , do_full_order , backend )
325334 return prop .propagate (phi0 , ts , maxHT )
0 commit comments