Repository: qutip/qutip

Issue:
Tests failing (possibly) due to scipy Deprecation in power-gmres solver
I can't quite figure out if this is simply due to the DeprecationWarnings or something else but all tests are failing due to `atol` not being set for the gmres solver. I will try to set `atol` and check if the tests pass.

Error log: https://api.travis-ci.org/v3/job/379109482/log.txt.
```
scipy/sparse/linalg/isolve/iterative.py:2: DeprecationWarning: scipy.sparse.linalg.bicgstab called without specifying `atol`. The default value will be changed in a future release. For compatibility, specify a value for `atol` explicitly, e.g., ``bicgstab(..., atol=0)``, or to retain the old behavior ``bicgstab(..., atol='legacy')``


```

```
..................................................
======================================================================
ERROR: Steady state: Thermal qubit - power-gmres solver
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/shahnawaz/miniconda3/lib/python3.6/site-packages/nose/case.py", line 197, in runTest
    self.test(*self.arg)
  File "/Users/shahnawaz/dev/qutip/qutip/tests/test_steadystate.py", line 145, in test_qubit_power_gmres
    rho_ss = steadystate(H, c_op_list, method='power-gmres')
  File "/Users/shahnawaz/dev/qutip/qutip/steadystate.py", line 298, in steadystate
    return _steadystate_power(A, ss_args)
  File "/Users/shahnawaz/dev/qutip/qutip/steadystate.py", line 863, in _steadystate_power
    v = v / la.norm(v, np.inf)
  File "/Users/shahnawaz/miniconda3/lib/python3.6/site-packages/scipy/linalg/misc.py", line 137, in norm
    a = np.asarray_chkfinite(a)
  File "/Users/shahnawaz/miniconda3/lib/python3.6/site-packages/numpy/lib/function_base.py", line 1233, in asarray_chkfinite
    "array must not contain infs or NaNs")
ValueError: array must not contain infs or NaNs

----------------------------------------------------------------------
Ran 561 tests in 576.175s

FAILED (SKIP=19, errors=1)
```


Discussion:

Resolving patch:
diff --git a/qutip/steadystate.py b/qutip/steadystate.py
--- a/qutip/steadystate.py
+++ b/qutip/steadystate.py
@@ -55,8 +55,8 @@
 from qutip.sparse import sp_permute, sp_bandwidth, sp_reshape, sp_profile
 
 from qutip.superoperator import liouvillian, vec2mat
-from qutip.sparse import (sp_permute, sp_bandwidth, sp_reshape, 
-                            sp_profile)
+from qutip.sparse import (sp_permute, sp_bandwidth, sp_reshape,
+                          sp_profile)
 from qutip.cy.spmath import zcsr_kron
 from qutip.graph import reverse_cuthill_mckee, weighted_bipartite_matching
 from qutip import (mat2vec, tensor, identity, operator_to_vector)
@@ -80,29 +80,33 @@ def _empty_info_dict():
     def_info = {'perm': [], 'solution_time': None,
                 'residual_norm': None,
                 'solver': None, 'method': None}
-    
+
     return def_info
 
+
 def _default_steadystate_args():
     def_args = {'sparse': True, 'use_rcm': False,
-                'use_wbm': False, 'weight': None, 'use_precond': False, 
-                'all_states': False, 'M': None, 'x0': None, 'drop_tol': 1e-4, 
-                'fill_factor': 100, 'diag_pivot_thresh': None, 'maxiter': 1000, 
-                'tol': 1e-12, 'permc_spec': 'COLAMD', 'ILU_MILU': 'smilu_2', 
-                'restart': 20, 'return_info': False, 'info': _empty_info_dict(), 
+                'use_wbm': False, 'weight': None, 'use_precond': False,
+                'all_states': False, 'M': None, 'x0': None, 'drop_tol': 1e-4,
+                'fill_factor': 100, 'diag_pivot_thresh': None, 'maxiter': 1000,
+                'tol': 1e-12, 'matol': 1e-15, 'mtol': None,
+                'permc_spec': 'COLAMD', 'ILU_MILU': 'smilu_2',
+                'restart': 20, 'return_info': False,
+                'info': _empty_info_dict(),
                 'verbose': False, 'solver': 'scipy'}
 
     return def_args
-    
+
+
 def _mkl_steadystate_args():
     def_args = {'max_iter_refine': 10,
                 'scaling_vectors': True,
                 'weighted_matching': True,
-                'return_info': False, 'info': _empty_info_dict(), 
+                'return_info': False, 'info': _empty_info_dict(),
                 'verbose': False, 'solver': 'mkl',
                 'use_rcm': False,
                 'use_wbm': False, 'weight': None,
-                'tol': 1e-12,
+                'tol': 1e-12, 'matol': 1e-15, 'mtol': None,
                 'maxiter': 1000}
 
     return def_args
@@ -127,7 +131,7 @@ def steadystate(A, c_op_list=[], method='direct', solver=None, **kwargs):
     solver : str {None, 'scipy', 'mkl'}
         Selects the sparse solver to use.  Default is auto-select
         based on the availability of the MKL library.
-    
+
     method : str {'direct', 'eigen', 'iterative-gmres',
                   'iterative-lgmres', 'iterative-bicgstab', 'svd', 'power',
                   'power-gmres', 'power-lgmres', 'power-bicgstab'}
@@ -164,13 +168,13 @@ def steadystate(A, c_op_list=[], method='direct', solver=None, **kwargs):
 
     max_iter_refine : int {10}
         MKL ONLY. Max. number of iterative refinements to perform.
-    
+
     scaling_vectors : bool {True, False}
         MKL ONLY.  Scale matrix to unit norm columns and rows.
-    
+
     weighted_matching : bool {True, False}
         MKL ONLY.  Use weighted matching to better condition diagonal.
-    
+
     x0 : ndarray, optional
         ITERATIVE ONLY. Initial guess for solution vector.
 
@@ -180,6 +184,13 @@ def steadystate(A, c_op_list=[], method='direct', solver=None, **kwargs):
     tol : float, optional, default=1e-12
         ITERATIVE ONLY. Tolerance used for terminating solver.
 
+    mtol : float, optional, default=None
+        ITERATIVE 'power' methods ONLY. Tolerance for lu solve method.
+        If None given then `max(0.1*tol, 1e-15)` is used
+
+    matol : float, optional, default=1e-15
+        ITERATIVE ONLY. Absolute tolerance for lu solve method.
+
     permc_spec : str, optional, default='COLAMD'
         ITERATIVE ONLY. Column ordering used internally by superLU for the
         'direct' LU decomposition method. Options include 'COLAMD' and
@@ -230,9 +241,8 @@ def steadystate(A, c_op_list=[], method='direct', solver=None, **kwargs):
     Notes
     -----
     The SVD method works only for dense operators (i.e. small systems).
-    
+
     """
-    
     if solver is None:
         solver = 'scipy'
         if settings.has_mkl:
@@ -241,11 +251,10 @@ def steadystate(A, c_op_list=[], method='direct', solver=None, **kwargs):
     elif solver == 'mkl' and \
             (method not in ['direct', 'power']):
         raise Exception('MKL solver only for direct or power methods.')
-        
+
     elif solver not in ['scipy', 'mkl']:
         raise Exception('Invalid solver kwarg.')
-    
-    
+
     if solver == 'scipy':
         ss_args = _default_steadystate_args()
     elif solver == 'mkl':
@@ -255,15 +264,14 @@ def steadystate(A, c_op_list=[], method='direct', solver=None, **kwargs):
     ss_args['method'] = method
     ss_args['info']['solver'] = ss_args['solver']
     ss_args['info']['method'] = ss_args['method']
-    
+
     for key in kwargs.keys():
         if key in ss_args.keys():
             ss_args[key] = kwargs[key]
         else:
             raise Exception(
                 "Invalid keyword argument '"+key+"' passed to steadystate.")
-    
-    
+
     # Set column perm to NATURAL if using RCM and not specified by user
     if ss_args['use_rcm'] and ('permc_spec' not in kwargs.keys()):
         ss_args['permc_spec'] = 'NATURAL'
@@ -277,7 +285,7 @@ def steadystate(A, c_op_list=[], method='direct', solver=None, **kwargs):
         ss_args['info']['weight'] = ss_args['weight']
 
     if ss_args['method'] == 'direct':
-        if (ss_args['solver']=='scipy' and ss_args['sparse']) \
+        if (ss_args['solver'] == 'scipy' and ss_args['sparse']) \
                 or ss_args['solver'] == 'mkl':
             return _steadystate_direct_sparse(A, ss_args)
         else:
@@ -294,7 +302,7 @@ def steadystate(A, c_op_list=[], method='direct', solver=None, **kwargs):
         return _steadystate_svd_dense(A, ss_args)
 
     elif ss_args['method'] in ['power', 'power-gmres',
-                            'power-lgmres', 'power-bicgstab']:
+                               'power-lgmres', 'power-bicgstab']:
         return _steadystate_power(A, ss_args)
 
     else:
@@ -329,14 +337,12 @@ def _steadystate_LU_liouvillian(L, ss_args, has_mkl=0):
     if has_mkl:
         L = L.data + sp.csr_matrix(
             (ss_args['weight']*np.ones(n), (np.zeros(n), [nn * (n + 1)
-             for nn in range(n)])),
-             shape=(n ** 2, n ** 2))
+             for nn in range(n)])), shape=(n ** 2, n ** 2))
     else:
         form = 'csc'
         L = L.data.tocsc() + sp.csc_matrix(
             (ss_args['weight']*np.ones(n), (np.zeros(n), [nn * (n + 1)
-             for nn in range(n)])),
-             shape=(n ** 2, n ** 2))
+             for nn in range(n)])), shape=(n ** 2, n ** 2))
 
     if settings.debug:
         old_band = sp_bandwidth(L)[0]
@@ -372,22 +378,23 @@ def _steadystate_LU_liouvillian(L, ss_args, has_mkl=0):
             rcm_band = sp_bandwidth(L)[0]
             rcm_pro = sp_profile(L)[0]
             logger.debug('RCM bandwidth: %i' % rcm_band)
-            logger.debug('Bandwidth reduction factor: %f' % 
-                    (old_band/rcm_band))
-            logger.debug('Profile reduction factor: %f' % 
-                    (old_pro/rcm_pro))
+            logger.debug('Bandwidth reduction factor: %f' %
+                         (old_band/rcm_band))
+            logger.debug('Profile reduction factor: %f' %
+                         (old_pro/rcm_pro))
     L.sort_indices()
     return L, perm, perm2, rev_perm, ss_args
 
 
-def steady(L, maxiter=10, tol=1e-12, itertol=1e-15, method='solve', 
-            use_precond=False):
+def steady(L, maxiter=10, tol=1e-12, itertol=1e-15, method='solve',
+           use_precond=False):
     """
     Deprecated. See steadystate instead.
     """
     message = "steady has been deprecated, use steadystate instead"
     warnings.warn(message, DeprecationWarning)
-    return steadystate(L, [], maxiter=maxiter, tol=tol, use_precond=use_precond)
+    return steadystate(L, [], maxiter=maxiter, tol=tol,
+                       use_precond=use_precond)
 
 
 def _steadystate_direct_sparse(L, ss_args):
@@ -406,8 +413,9 @@ def _steadystate_direct_sparse(L, ss_args):
         has_mkl = 1
     else:
         has_mkl = 0
-    
-    L, perm, perm2, rev_perm, ss_args = _steadystate_LU_liouvillian(L, ss_args, has_mkl)
+
+    ss_lu_liouv_list = _steadystate_LU_liouvillian(L, ss_args, has_mkl)
+    L, perm, perm2, rev_perm, ss_args = ss_lu_liouv_list
     if np.any(perm):
         b = b[np.ix_(perm,)]
     if np.any(perm2):
@@ -440,13 +448,13 @@ def _steadystate_direct_sparse(L, ss_args):
                 logger.debug('L NNZ: %i ; U NNZ: %i' % (L_nnz, U_nnz))
                 logger.debug('Fill factor: %f' % ((L_nnz + U_nnz)/orig_nnz))
 
-    else: # Use MKL solver
-        if len(ss_args['info']['perm']) !=0:
+    else:  # Use MKL solver
+        if len(ss_args['info']['perm']) != 0:
             in_perm = np.arange(n**2, dtype=np.int32)
         else:
             in_perm = None
         _direct_start = time.time()
-        v = mkl_spsolve(L, b, perm = in_perm, verbose = ss_args['verbose'],
+        v = mkl_spsolve(L, b, perm=in_perm, verbose=ss_args['verbose'],
                         max_iter_refine=ss_args['max_iter_refine'],
                         scaling_vectors=ss_args['scaling_vectors'],
                         weighted_matching=ss_args['weighted_matching'])
@@ -534,7 +542,7 @@ def _steadystate_eigen(L, ss_args):
     if ss_args['use_rcm']:
         eigvec = eigvec[np.ix_(rev_perm,)]
     _temp = vec2mat(eigvec)
-    data = dense2D_to_fastcsr_fmode(_temp,_temp.shape[0], _temp.shape[1])
+    data = dense2D_to_fastcsr_fmode(_temp, _temp.shape[0], _temp.shape[1])
     data = 0.5 * (data + data.H)
     out = Qobj(data, dims=dims, isherm=True)
     if ss_args['return_info']:
@@ -630,20 +638,49 @@ def _iter_count(r):
 
     # Select iterative solver type
     _iter_start = time.time()
+    # FIXME: These atol keyword except checks can be removed once scipy 1.1
+    # is a minimum requirement
     if ss_args['method'] == 'iterative-gmres':
-        v, check = gmres(L, b, tol=ss_args['tol'], M=ss_args['M'],
-                            x0=ss_args['x0'], restart=ss_args['restart'],
-                            maxiter=ss_args['maxiter'], callback=_iter_count)
+        try:
+            v, check = gmres(L, b, tol=ss_args['tol'], atol=ss_args['matol'],
+                             M=ss_args['M'], x0=ss_args['x0'],
+                             restart=ss_args['restart'],
+                             maxiter=ss_args['maxiter'],
+                             callback=_iter_count)
+        except TypeError as e:
+            if "unexpected keyword argument 'atol'" in str(e):
+                v, check = gmres(L, b, tol=ss_args['tol'],
+                                 M=ss_args['M'], x0=ss_args['x0'],
+                                 restart=ss_args['restart'],
+                                 maxiter=ss_args['maxiter'],
+                                 callback=_iter_count)
 
     elif ss_args['method'] == 'iterative-lgmres':
-        v, check = lgmres(L, b, tol=ss_args['tol'], M=ss_args['M'],
-                          x0=ss_args['x0'], maxiter=ss_args['maxiter'],
-                          callback=_iter_count)
+        try:
+            v, check = lgmres(L, b, tol=ss_args['tol'], atol=ss_args['matol'],
+                              M=ss_args['M'], x0=ss_args['x0'],
+                              maxiter=ss_args['maxiter'],
+                              callback=_iter_count)
+        except TypeError as e:
+            if "unexpected keyword argument 'atol'" in str(e):
+                v, check = lgmres(L, b, tol=ss_args['tol'],
+                                  M=ss_args['M'], x0=ss_args['x0'],
+                                  maxiter=ss_args['maxiter'],
+                                  callback=_iter_count)
 
     elif ss_args['method'] == 'iterative-bicgstab':
-        v, check = bicgstab(L, b, tol=ss_args['tol'], M=ss_args['M'],
-                            x0=ss_args['x0'],
-                            maxiter=ss_args['maxiter'], callback=_iter_count)
+        try:
+            v, check = bicgstab(L, b, tol=ss_args['tol'],
+                                atol=ss_args['matol'],
+                                M=ss_args['M'], x0=ss_args['x0'],
+                                maxiter=ss_args['maxiter'],
+                                callback=_iter_count)
+        except TypeError as e:
+            if "unexpected keyword argument 'atol'" in str(e):
+                v, check = bicgstab(L, b, tol=ss_args['tol'],
+                                    M=ss_args['M'], x0=ss_args['x0'],
+                                    maxiter=ss_args['maxiter'],
+                                    callback=_iter_count)
     else:
         raise Exception("Invalid iterative solver method.")
     _iter_end = time.time()
@@ -736,7 +773,7 @@ def _steadystate_power_liouvillian(L, ss_args, has_mkl=0):
         old_pro = sp_profile(L)[0]
         logger.debug('Original bandwidth: %i' % old_band)
         logger.debug('Original profile: %i' % old_pro)
-    
+
     if ss_args['use_wbm']:
         if settings.debug:
             logger.debug('Calculating Weighted Bipartite Matching ordering...')
@@ -751,7 +788,7 @@ def _steadystate_power_liouvillian(L, ss_args, has_mkl=0):
             wbm_pro = sp_profile(L)[0]
             logger.debug('WBM bandwidth: %i' % wbm_band)
             logger.debug('WBM profile: %i' % wbm_pro)
-    
+
     if ss_args['use_rcm']:
         if settings.debug:
             logger.debug('Calculating Reverse Cuthill-Mckee ordering...')
@@ -766,12 +803,13 @@ def _steadystate_power_liouvillian(L, ss_args, has_mkl=0):
             new_band = sp_bandwidth(L)[0]
             new_pro = sp_profile(L)[0]
             logger.debug('RCM bandwidth: %i' % new_band)
-            logger.debug('Bandwidth reduction factor: %f' % (old_band/new_band))
+            logger.debug('Bandwidth reduction factor: %f'
+                         % (old_band/new_band))
             logger.debug('RCM profile: %i' % new_pro)
-            logger.debug('Profile reduction factor: %f' % (old_pro/new_pro))
+            logger.debug('Profile reduction factor: %f'
+                         % (old_pro/new_pro))
     L.sort_indices()
     return L, perm, perm2, rev_perm, ss_args
-    
 
 
 def _steadystate_power(L, ss_args):
@@ -782,6 +820,9 @@ def _steadystate_power(L, ss_args):
     if settings.debug:
         logger.debug('Starting iterative inverse-power method solver.')
     tol = ss_args['tol']
+    mtol = ss_args['mtol']
+    if mtol is None:
+        mtol = max(0.1*tol, 1e-15)
     maxiter = ss_args['maxiter']
 
     use_solver(assumeSortedIndices=True)
@@ -797,41 +838,44 @@ def _steadystate_power(L, ss_args):
         has_mkl = 1
     else:
         has_mkl = 0
-    L, perm, perm2, rev_perm, ss_args = _steadystate_power_liouvillian(L, 
-                                                ss_args, has_mkl)
+    L, perm, perm2, rev_perm, ss_args = _steadystate_power_liouvillian(L,
+                                                                       ss_args,
+                                                                       has_mkl)
     orig_nnz = L.nnz
     # start with all ones as RHS
     v = np.ones(n, dtype=complex)
     if ss_args['use_rcm']:
         v = v[np.ix_(perm2,)]
-    
+
     # Do preconditioning
     if ss_args['solver'] == 'scipy':
         if ss_args['M'] is None and ss_args['use_precond'] and \
-                ss_args['method'] in ['power-gmres', 
-                                    'power-lgmres', 'power-bicgstab']:
-            ss_args['M'], ss_args = _iterative_precondition(L, int(np.sqrt(n)), ss_args)
+                ss_args['method'] in ['power-gmres',
+                                      'power-lgmres',
+                                      'power-bicgstab']:
+            ss_args['M'], ss_args = _iterative_precondition(L, int(np.sqrt(n)),
+                                                            ss_args)
             if ss_args['M'] is None:
                 warnings.warn("Preconditioning failed. Continuing without.",
                               UserWarning)
-    
+
     ss_iters = {'iter': 0}
 
     def _iter_count(r):
         ss_iters['iter'] += 1
         return
-    
+
     _power_start = time.time()
     # Get LU factors
     if ss_args['method'] == 'power':
         if ss_args['solver'] == 'mkl':
-            lu = mkl_splu(L,max_iter_refine=ss_args['max_iter_refine'],
-                        scaling_vectors=ss_args['scaling_vectors'],
-                        weighted_matching=ss_args['weighted_matching'])
-        else: 
+            lu = mkl_splu(L, max_iter_refine=ss_args['max_iter_refine'],
+                          scaling_vectors=ss_args['scaling_vectors'],
+                          weighted_matching=ss_args['weighted_matching'])
+        else:
             lu = splu(L, permc_spec=ss_args['permc_spec'],
-              diag_pivot_thresh=ss_args['diag_pivot_thresh'],
-              options=dict(ILU_MILU=ss_args['ILU_MILU']))
+                      diag_pivot_thresh=ss_args['diag_pivot_thresh'],
+                      options=dict(ILU_MILU=ss_args['ILU_MILU']))
 
             if settings.debug and _scipy_check:
                 L_nnz = lu.L.nnz
@@ -840,26 +884,60 @@ def _iter_count(r):
                 logger.debug('Fill factor: %f' % ((L_nnz+U_nnz)/orig_nnz))
 
     it = 0
-    _tol = max(ss_args['tol']/10, 1e-15) # Should make this user accessible
+    # FIXME: These atol keyword except checks can be removed once scipy 1.1
+    # is a minimum requirement
     while (la.norm(L * v, np.inf) > tol) and (it < maxiter):
-        
+        check = 0
         if ss_args['method'] == 'power':
             v = lu.solve(v)
         elif ss_args['method'] == 'power-gmres':
-            v, check = gmres(L, v, tol=_tol, M=ss_args['M'],
-                                x0=ss_args['x0'], restart=ss_args['restart'],
-                                maxiter=ss_args['maxiter'], callback=_iter_count)
+            try:
+                v, check = gmres(L, v, tol=mtol, atol=ss_args['matol'],
+                                 M=ss_args['M'], x0=ss_args['x0'],
+                                 restart=ss_args['restart'],
+                                 maxiter=ss_args['maxiter'],
+                                 callback=_iter_count)
+            except TypeError as e:
+                if "unexpected keyword argument 'atol'" in str(e):
+                    v, check = gmres(L, v, tol=mtol,
+                                     M=ss_args['M'], x0=ss_args['x0'],
+                                     restart=ss_args['restart'],
+                                     maxiter=ss_args['maxiter'],
+                                     callback=_iter_count)
+
         elif ss_args['method'] == 'power-lgmres':
-            v, check = lgmres(L, v, tol=_tol, M=ss_args['M'],
-                              x0=ss_args['x0'], maxiter=ss_args['maxiter'],
-                              callback=_iter_count)
+            try:
+                v, check = lgmres(L, v, tol=mtol, atol=ss_args['matol'],
+                                  M=ss_args['M'], x0=ss_args['x0'],
+                                  maxiter=ss_args['maxiter'],
+                                  callback=_iter_count)
+            except TypeError as e:
+                if "unexpected keyword argument 'atol'" in str(e):
+                    v, check = lgmres(L, v, tol=mtol,
+                                      M=ss_args['M'], x0=ss_args['x0'],
+                                      maxiter=ss_args['maxiter'],
+                                      callback=_iter_count)
+
         elif ss_args['method'] == 'power-bicgstab':
-            v, check = bicgstab(L, v, tol=_tol, M=ss_args['M'],
-                                x0=ss_args['x0'],
-                                maxiter=ss_args['maxiter'], callback=_iter_count)
+            try:
+                v, check = bicgstab(L, v, tol=mtol, atol=ss_args['matol'],
+                                    M=ss_args['M'], x0=ss_args['x0'],
+                                    maxiter=ss_args['maxiter'],
+                                    callback=_iter_count)
+            except TypeError as e:
+                if "unexpected keyword argument 'atol'" in str(e):
+                    v, check = bicgstab(L, v, tol=mtol,
+                                        M=ss_args['M'], x0=ss_args['x0'],
+                                        maxiter=ss_args['maxiter'],
+                                        callback=_iter_count)
         else:
             raise Exception("Invalid iterative solver method.")
-            
+        if check > 0:
+            raise Exception("{} failed to find solution in "
+                            "{} iterations.".format(ss_args['method'],
+                                                    check))
+        if check < 0:
+            raise Exception("Breakdown in {}".format(ss_args['method']))
         v = v / la.norm(v, np.inf)
         it += 1
     if ss_args['method'] == 'power' and ss_args['solver'] == 'mkl':
@@ -868,7 +946,7 @@ def _iter_count(r):
             ss_args['info']['max_iter_refine'] = ss_args['max_iter_refine']
             ss_args['info']['scaling_vectors'] = ss_args['scaling_vectors']
             ss_args['info']['weighted_matching'] = ss_args['weighted_matching']
-    
+
     if it >= maxiter:
         raise Exception('Failed to find steady state after ' +
                         str(maxiter) + ' iterations')
@@ -890,7 +968,9 @@ def _iter_count(r):
     else:
         data = data / la.norm(v)
 
-    data = dense2D_to_fastcsr_fmode(vec2mat(data), rhoss.shape[0], rhoss.shape[0])
+    data = dense2D_to_fastcsr_fmode(vec2mat(data),
+                                    rhoss.shape[0],
+                                    rhoss.shape[0])
     rhoss.data = 0.5 * (data + data.H)
     rhoss.isherm = True
     if ss_args['return_info']:
@@ -934,7 +1014,7 @@ def build_preconditioner(A, c_op_list=[], **kwargs):
         Tells the preconditioner what type of Liouvillian to build for
         iLU factorization.  For direct iterative methods use 'iterative'.
         For power iterative methods use 'power'.
-    
+
     permc_spec : str, optional, default='COLAMD'
         Column ordering used internally by superLU for the
         'direct' LU decomposition method. Options include 'COLAMD' and
@@ -991,12 +1071,14 @@ def build_preconditioner(A, c_op_list=[], **kwargs):
 
     n = int(np.sqrt(L.shape[0]))
     if ss_args['method'] == 'iterative':
-        L, perm, perm2, rev_perm, ss_args = _steadystate_LU_liouvillian(L, ss_args)
+        ss_list = _steadystate_LU_liouvillian(L, ss_args)
+        L, perm, perm2, rev_perm, ss_args = ss_list
     elif ss_args['method'] == 'power':
-        L, perm, perm2, rev_perm, ss_args = _steadystate_power_liouvillian(L, ss_args)
+        ss_list = _steadystate_power_liouvillian(L, ss_args)
+        L, perm, perm2, rev_perm, ss_args = ss_list
     else:
         raise Exception("Invalid preconditioning method.")
-    
+
     M, ss_args = _iterative_precondition(L, n, ss_args)
 
     if ss_args['return_info']:
@@ -1004,6 +1086,7 @@ def build_preconditioner(A, c_op_list=[], **kwargs):
     else:
         return M
 
+
 def _pseudo_inverse_dense(L, rhoss, w=None, **pseudo_args):
     """
     Internal function for computing the pseudo inverse of an Liouvillian using
@@ -1017,32 +1100,35 @@ def _pseudo_inverse_dense(L, rhoss, w=None, **pseudo_args):
     I = np.identity(N * N)
     P = np.kron(np.transpose(rho_vec), tr_vec)
     Q = I - P
-    
+
     if w is None:
         L = L
     else:
         L = 1.0j*w*spre(tr_mat)+L
-    
+
     if pseudo_args['method'] == 'direct':
         try:
             LIQ = np.linalg.solve(L.full(), Q)
         except:
             LIQ = np.linalg.lstsq(L.full(), Q)[0]
-         
+
         R = np.dot(Q, LIQ)
-     
+
         return Qobj(R, dims=L.dims)
 
     elif pseudo_args['method'] == 'numpy':
-        return Qobj(np.dot(Q,np.dot(np.linalg.pinv(L.full()),Q)), dims=L.dims)
+        return Qobj(np.dot(Q, np.dot(np.linalg.pinv(L.full()), Q)),
+                    dims=L.dims)
 
     elif pseudo_args['method'] == 'scipy':
-        #return Qobj(la.pinv(L.full()), dims=L.dims)
-        return Qobj(np.dot(Q,np.dot(la.pinv(L.full()),Q)), dims=L.dims)
+        # return Qobj(la.pinv(L.full()), dims=L.dims)
+        return Qobj(np.dot(Q, np.dot(la.pinv(L.full()), Q)),
+                    dims=L.dims)
 
     elif pseudo_args['method'] == 'scipy2':
-        #return Qobj(la.pinv2(L.full()), dims=L.dims)
-        return Qobj(np.dot(Q,np.dot(la.pinv2(L.full()),Q)), dims=L.dims)
+        # return Qobj(la.pinv2(L.full()), dims=L.dims)
+        return Qobj(np.dot(Q, np.dot(la.pinv2(L.full()), Q)),
+                    dims=L.dims)
 
     else:
         raise ValueError("Unsupported method '%s'. Use 'direct' or 'numpy'" %
@@ -1065,17 +1151,15 @@ def _pseudo_inverse_sparse(L, rhoss, w=None, **pseudo_args):
     P = zcsr_kron(rhoss_vec.data, tr_op_vec.data.T)
     I = sp.eye(N*N, N*N, format='csr')
     Q = I - P
-    
-  
+
     if w is None:
-        L =  1.0j*(1e-15)*spre(tr_op) + L
+        L = 1.0j*(1e-15)*spre(tr_op) + L
     else:
         if w != 0.0:
             L = 1.0j*w*spre(tr_op) + L
         else:
-            L =  1.0j*(1e-15)*spre(tr_op) + L
-        
-        
+            L = 1.0j*(1e-15)*spre(tr_op) + L
+
     if pseudo_args['use_rcm']:
         perm = reverse_cuthill_mckee(L.data)
         A = sp_permute(L.data, perm, perm)
@@ -1084,30 +1168,27 @@ def _pseudo_inverse_sparse(L, rhoss, w=None, **pseudo_args):
         if ss_args['solver'] == 'scipy':
             A = L.data.tocsc()
             A.sort_indices()
-        
-        
-    
+
     if pseudo_args['method'] == 'splu':
         if settings.has_mkl:
             A = L.data.tocsr()
             A.sort_indices()
-            LIQ = mkl_spsolve(A,Q.toarray())
+            LIQ = mkl_spsolve(A, Q.toarray())
         else:
-       
-            lu = sp.linalg.splu(A, permc_spec=pseudo_args['permc_spec'],
-                            diag_pivot_thresh=pseudo_args['diag_pivot_thresh'],
-                            options=dict(ILU_MILU=pseudo_args['ILU_MILU']))
+            pspec = pseudo_args['permc_spec']
+            diag_p_thresh = pseudo_args['diag_pivot_thresh']
+            pseudo_args = pseudo_args['ILU_MILU']
+            lu = sp.linalg.splu(A, permc_spec=pspec,
+                                diag_pivot_thresh=diag_p_thresh,
+                                options=dict(ILU_MILU=pseudo_args))
             LIQ = lu.solve(Q.toarray())
-         
-            
+
     elif pseudo_args['method'] == 'spilu':
-        
         lu = sp.linalg.spilu(A, permc_spec=pseudo_args['permc_spec'],
-                             fill_factor=pseudo_args['fill_factor'], 
+                             fill_factor=pseudo_args['fill_factor'],
                              drop_tol=pseudo_args['drop_tol'])
         LIQ = lu.solve(Q.toarray())
 
-
     else:
         raise ValueError("unsupported method '%s'" % method)
 
@@ -1123,21 +1204,23 @@ def _pseudo_inverse_sparse(L, rhoss, w=None, **pseudo_args):
 def pseudo_inverse(L, rhoss=None, w=None, sparse=True,  **kwargs):
     """
     Compute the pseudo inverse for a Liouvillian superoperator, optionally
-    given its steady state density matrix (which will be computed if not given).
+    given its steady state density matrix (which will be computed if not
+    given).
 
     Returns
     -------
     L : Qobj
         A Liouvillian superoperator for which to compute the pseudo inverse.
-    
-    
+
+
     rhoss : Qobj
         A steadystate density matrix as Qobj instance, for the Liouvillian
         superoperator L.
-        
+
     w : double
-        frequency at which to evaluate pseudo-inverse.  Can be zero for dense systems
-        and large sparse systems. Small sparse systems can fail for zero frequencies.
+        frequency at which to evaluate pseudo-inverse.  Can be zero for dense
+        systems and large sparse systems. Small sparse systems can fail for
+        zero frequencies.
 
     sparse : bool
         Flag that indicate whether to use sparse or dense matrix methods when
@@ -1155,20 +1238,21 @@ def pseudo_inverse(L, rhoss=None, w=None, sparse=True,  **kwargs):
     -------
     R : Qobj
         Returns a Qobj instance representing the pseudo inverse of L.
-    
+
     Note
     ----
     In general the inverse of a sparse matrix will be dense.  If you
     are applying the inverse to a density matrix then it is better to
     cast the problem as an Ax=b type problem where the explicit calculation
-    of the inverse is not required. See page 67 of "Electrons in nanostructures"
-    C. Flindt, PhD Thesis available online:
-    http://orbit.dtu.dk/fedora/objects/orbit:82314/datastreams/file_4732600/content
-    
+    of the inverse is not required. See page 67 of "Electrons in
+    nanostructures" C. Flindt, PhD Thesis available online:
+    http://orbit.dtu.dk/fedora/objects/orbit:82314/datastreams/
+    file_4732600/content
+
     Note also that the definition of the pseudo-inverse herein is different
-    from numpys pinv() alone, as it includes pre and post projection onto 
+    from numpys pinv() alone, as it includes pre and post projection onto
     the subspace defined by the projector Q.
-    
+
     """
     pseudo_args = _default_steadystate_args()
     for key in kwargs.keys():
@@ -1178,17 +1262,20 @@ def pseudo_inverse(L, rhoss=None, w=None, sparse=True,  **kwargs):
             raise Exception(
                 "Invalid keyword argument '"+key+"' passed to pseudo_inverse.")
     if 'method' not in kwargs.keys():
-        pseudo_args['method']='splu'
-        
+        pseudo_args['method'] = 'splu'
+
     # Set column perm to NATURAL if using RCM and not specified by user
     if pseudo_args['use_rcm'] and ('permc_spec' not in kwargs.keys()):
         pseudo_args['permc_spec'] = 'NATURAL'
-    
+
     if rhoss is None:
         rhoss = steadystate(L, **pseudo_args)
 
     if sparse:
-        return _pseudo_inverse_sparse(L,rhoss, w=w,  **pseudo_args)
+        return _pseudo_inverse_sparse(L, rhoss, w=w, **pseudo_args)
     else:
-        pseudo_args['method'] = pseudo_args['method'] if pseudo_args['method'] != 'splu' else 'direct'
-        return _pseudo_inverse_dense(L, rhoss, w=w, **pseudo_args)
\ No newline at end of file
+        if pseudo_args['method'] != 'splu':
+            pseudo_args['method'] = pseudo_args['method']
+        else:
+            pseudo_args['method'] = 'direct'
+        return _pseudo_inverse_dense(L, rhoss, w=w, **pseudo_args)
diff --git a/qutip/stochastic.py b/qutip/stochastic.py
--- a/qutip/stochastic.py
+++ b/qutip/stochastic.py
@@ -167,13 +167,13 @@ class StochasticSolverOptions:
 
     solver : string
         Name of the solver method to use for solving the stochastic
-        equations. Valid values are: 
+        equations. Valid values are:
         1/2 order algorithms: 'euler-maruyama', 'fast-euler-maruyama',
-        'pc-euler' is a predictor-corrector method which is more 
+        'pc-euler' is a predictor-corrector method which is more
         stable than explicit methods,
         1 order algorithms: 'milstein', 'fast-milstein', 'platen',
         'milstein-imp' is semi-implicit Milstein method,
-        3/2 order algorithms: 'taylor15', 
+        3/2 order algorithms: 'taylor15',
         'taylor15-imp' is semi-implicit Taylor 1.5 method.
         Implicit methods can adjust tolerance via args = {'tol':value},
         default is {'tol':1e-6}
@@ -312,10 +312,10 @@ def ssesolve(H, psi0, times, sc_ops=[], e_ops=[], _safe_mode=True, **kwargs):
         e_ops = [e for e in e_ops.values()]
     else:
         e_ops_dict = None
-    
+
     if _safe_mode:
         _solver_safety_check(H, psi0, sc_ops, e_ops)
-    
+
     sso = StochasticSolverOptions(H=H, state0=psi0, times=times,
                                   sc_ops=sc_ops, e_ops=e_ops, **kwargs)
 
@@ -383,7 +383,7 @@ def ssesolve(H, psi0, times, sc_ops=[], e_ops=[], _safe_mode=True, **kwargs):
     return res
 
 
-def smesolve(H, rho0, times, c_ops=[], sc_ops=[], e_ops=[], 
+def smesolve(H, rho0, times, c_ops=[], sc_ops=[], e_ops=[],
             _safe_mode=True ,**kwargs):
     """
     Solve stochastic master equation. Dispatch to specific solvers
@@ -442,7 +442,7 @@ def smesolve(H, rho0, times, c_ops=[], sc_ops=[], e_ops=[],
         e_ops = [e for e in e_ops.values()]
     else:
         e_ops_dict = None
-    
+
     if _safe_mode:
         _solver_safety_check(H, rho0, c_ops+sc_ops, e_ops)
 
@@ -536,7 +536,7 @@ def smesolve(H, rho0, times, c_ops=[], sc_ops=[], e_ops=[],
                     sso.rhs = _rhs_rho_milstein_homodyne_two_fast
                 else:
                     sso.rhs = _rhs_rho_milstein_homodyne_fast
-                 
+
         elif sso.solver == 'taylor15':
             sso.generate_A_ops = _generate_A_ops_simple
             sso.generate_noise = _generate_noise_Taylor_15
@@ -561,9 +561,9 @@ def smesolve(H, rho0, times, c_ops=[], sc_ops=[], e_ops=[],
                 else:
                     raise Exception("Only one stochastic operator is supported")
             else:
-                raise Exception("Only homodyne is available") 
+                raise Exception("Only homodyne is available")
 
-        elif sso.solver == 'taylor15-imp':  
+        elif sso.solver == 'taylor15-imp':
             sso.generate_A_ops = _generate_A_ops_implicit
             sso.generate_noise = _generate_noise_Taylor_15
             if sso.args == None:
@@ -1568,14 +1568,14 @@ def _generate_A_ops_simple(sc, L, dt):
     out = []
     out += temp
     out += [tempL]
-    
+
     out1 = [out]
     # the following hack is required for compatibility with old A_ops
     out1 += [[] for n in range(A_len - 1)]
 
     return out1
-    
-    
+
+
 def _generate_A_ops_implicit(sc, L, dt):
     """
     pre-compute superoperator operator combinations that are commonly needed
@@ -1590,7 +1590,7 @@ def _generate_A_ops_implicit(sc, L, dt):
     out += temp
     out += [sp.eye(L.shape[0], format='csr') - 0.5*dt*tempL]
     out += [tempL]
-    
+
     out1 = [out]
     # the following hack is required for compatibility with old A_ops
     out1 += [[] for n in range(A_len - 1)]
@@ -1620,24 +1620,24 @@ def _generate_noise_Taylor_15(sc_len, N_store, N_substeps, d2_len, dt):
     """
     generate noise terms for the strong Taylor 1.5 scheme
     """
-    U1 = np.random.randn(sc_len, N_store, N_substeps, 1) 
-    U2 = np.random.randn(sc_len, N_store, N_substeps, 1) 
-    dW = U1 * np.sqrt(dt) 
-    dZ = 0.5 * dt**(3./2) * (U1 + 1./np.sqrt(3) * U2) 
+    U1 = np.random.randn(sc_len, N_store, N_substeps, 1)
+    U2 = np.random.randn(sc_len, N_store, N_substeps, 1)
+    dW = U1 * np.sqrt(dt)
+    dZ = 0.5 * dt**(3./2) * (U1 + 1./np.sqrt(3) * U2)
 
     if sc_len == 1:
-        noise = np.vstack([ dW, 0.5 * (dW * dW - dt), dZ, dW * dt - dZ, 0.5 * (1./3. * dW**2 - dt) * dW ])                    
-    
+        noise = np.vstack([ dW, 0.5 * (dW * dW - dt), dZ, dW * dt - dZ, 0.5 * (1./3. * dW**2 - dt) * dW ])
+
     elif sc_len == 2:
-        noise = np.vstack([ dW, 0.5 * (dW**2 - dt), dZ, dW * dt - dZ, 0.5 * (1./3. * dW**2 - dt) * dW] 
+        noise = np.vstack([ dW, 0.5 * (dW**2 - dt), dZ, dW * dt - dZ, 0.5 * (1./3. * dW**2 - dt) * dW]
                     + [[dW[n] * dW[m] for (n, m) in np.ndindex(sc_len, sc_len) if n < m]]  # Milstein
                     + [[0.5 * dW[n] * (dW[m]**2 - dt) for (n, m) in np.ndindex(sc_len, sc_len) if n != m]])
 
     #else:
-        #noise = np.vstack([ dW, 0.5 * (dW**2 - dt), dZ, dW * dt - dZ, 0.5 * (1./3. * dW**2 - dt) * dW] 
+        #noise = np.vstack([ dW, 0.5 * (dW**2 - dt), dZ, dW * dt - dZ, 0.5 * (1./3. * dW**2 - dt) * dW]
                     #+ [[dW[n] * dW[m] for (n, m) in np.ndindex(sc_len, sc_len) if n > m]]  # Milstein
                     #+ [[0.5 * dW[n] * (dW[m]**2 - dt) for (n, m) in np.ndindex(sc_len, sc_len) if n != m]]
-                    #+ [[dW[n] * dW[m] * dW[k] for (n, m, k) in np.ndindex(sc_len, sc_len, sc_len) if n>m>k]])  
+                    #+ [[dW[n] * dW[m] * dW[k] for (n, m, k) in np.ndindex(sc_len, sc_len, sc_len) if n>m>k]])
     else:
         raise Exception("too many stochastic operators")
 
@@ -2030,12 +2030,12 @@ def _rhs_rho_taylor_15_one(L, rho_t, t, A, dt, ddW, d1, d2,
     drho_t += b * dW[0]
     drho_t += Lb * dW[1] # Milstein term
 
-    # new terms: 
+    # new terms:
     drho_t += A[-1] * b * dW[2]
     drho_t += (A[0] * a - TrAa * rho_t - e0 * a - TrAb * b) * dW[3]
     drho_t += A[-1] * a * (0.5 * dt*dt)
-    drho_t += (A[0] * Lb - TrALb * rho_t - (2 * TrAb) * b - e0 * Lb) * dW[4] 
-        
+    drho_t += (A[0] * Lb - TrALb * rho_t - (2 * TrAb) * b - e0 * Lb) * dW[4]
+
     return rho_t + drho_t
 
 #include _rhs_rho_Taylor_15_two#
@@ -2043,15 +2043,15 @@ def _rhs_rho_taylor_15_one(L, rho_t, t, A, dt, ddW, d1, d2,
 def _rhs_rho_milstein_implicit(L, rho_t, t, A, dt, ddW, d1, d2, args):
     """
     Drift implicit Milstein (theta = 1/2, eta = 0)
-    Wang, X., Gan, S., & Wang, D. (2012). 
-    A family of fully implicit Milstein methods for stiff stochastic differential 
-    equations with multiplicative noise. 
+    Wang, X., Gan, S., & Wang, D. (2012).
+    A family of fully implicit Milstein methods for stiff stochastic differential
+    equations with multiplicative noise.
     BIT Numerical Mathematics, 52(3), 741–772.
     """
-    
+
     dW = ddW[:, 0]
     A = A[0]
-    
+
 
     #reusable operators and traces
     a = A[-1] * rho_t * (0.5 * dt)
@@ -2059,22 +2059,30 @@ def _rhs_rho_milstein_implicit(L, rho_t, t, A, dt, ddW, d1, d2, args):
     b = A[0] * rho_t - e0 * rho_t
     TrAb = cy_expect_rho_vec(A[0], b, 1)
 
-    drho_t = b * dW[0] 
+    drho_t = b * dW[0]
     drho_t += a
     drho_t += (A[0] * b - TrAb * rho_t - e0 * b) * dW[1] # Milstein term
     drho_t += rho_t
 
-    v, check = sp.linalg.bicgstab(A[-2], drho_t, x0 = drho_t + a, tol=args['tol'])
+    #FIXME: This atol keyword except check can be removed once scipy 1.1
+    #       is a minimum requirement
+    try:
+        v, check = sp.linalg.bicgstab(A[-2], drho_t, x0=drho_t + a,
+                                      tol=args['tol'], atol='legacy')
+    except TypeError as e:
+        if "unexpected keyword argument 'atol'" in str(e):
+            v, check = sp.linalg.bicgstab(A[-2], drho_t, x0=drho_t + a,
+                                          tol=args['tol'])
 
     return v
-    
+
 def _rhs_rho_taylor_15_implicit(L, rho_t, t, A, dt, ddW, d1, d2, args):
     """
     Drift implicit Taylor 1.5 (alpha = 1/2, beta = doesn't matter)
     Chaptert 12.2 Eq. (2.18) in Numerical Solution of Stochastic Differential Equations
     By Peter E. Kloeden, Eckhard Platen
     """
-    
+
     dW = ddW[:, 0]
     A = A[0]
 
@@ -2087,29 +2095,36 @@ def _rhs_rho_taylor_15_implicit(L, rho_t, t, A, dt, ddW, d1, d2, args):
     TrALb = cy_expect_rho_vec(A[0], Lb, 1)
     TrAa = cy_expect_rho_vec(A[0], a, 1)
 
-    drho_t = b * dW[0] 
+    drho_t = b * dW[0]
     drho_t += Lb * dW[1] # Milstein term
     xx0 = (drho_t + a * dt) + rho_t #starting vector for the linear solver (Milstein prediction)
     drho_t += (0.5 * dt) * a
 
-    # new terms: 
+    # new terms:
     drho_t += A[-1] * b * (dW[2] - 0.5*dW[0]*dt)
     drho_t += (A[0] * a - TrAa * rho_t - e0 * a - TrAb * b) * dW[3]
 
     drho_t += (A[0] * Lb - TrALb * rho_t - (2 * TrAb) * b - e0 * Lb) * dW[4]
     drho_t += rho_t
 
-    v, check = sp.linalg.bicgstab(A[-2], drho_t, x0 = xx0, tol=args['tol'])
-
+    #FIXME: This atol keyword except check can be removed once scipy 1.1
+    #       is a minimum requirement
+    try:
+        v, check = sp.linalg.bicgstab(A[-2], drho_t, x0=xx0,
+                                      tol=args['tol'], atol='legacy')
+    except TypeError as e:
+        if "unexpected keyword argument 'atol'" in str(e):
+            v, check = sp.linalg.bicgstab(A[-2], drho_t, x0=xx0,
+                                          tol=args['tol'])
     return v
-    
+
 def _rhs_rho_pred_corr_homodyne_single(L, rho_t, t, A, dt, ddW, d1, d2,
                                            args):
     """
     1/2 predictor-corrector scheme for homodyne detection with 1 stochastic operator
     """
     dW = ddW[:, 0]
-    
+
     #predictor
 
     d_vec = (A[0][0] * rho_t).reshape(-1, len(rho_t))
@@ -2125,7 +2140,7 @@ def _rhs_rho_pred_corr_homodyne_single(L, rho_t, t, A, dt, ddW, d1, d2,
     pred_rho_t += rho_t
 
     a_pred -= ((d_vec[1] - e[1] * rho_t) - (2.0 * e[0]) * b_pred) * (0.5 * dt)
-    
+
     #corrector
 
     d_vec = (A[0][0] * pred_rho_t).reshape(-1, len(rho_t))


Test patch:
diff --git a/qutip/tests/test_steadystate.py b/qutip/tests/test_steadystate.py
--- a/qutip/tests/test_steadystate.py
+++ b/qutip/tests/test_steadystate.py
@@ -114,7 +114,7 @@ def test_qubit_power():
         c_op_list.append(np.sqrt(rate) * sm)
         rate = gamma1 * n_th
         c_op_list.append(np.sqrt(rate) * sm.dag())
-        rho_ss = steadystate(H, c_op_list, method='power')
+        rho_ss = steadystate(H, c_op_list, method='power', mtol=1e-5)
         p_ss[idx] = expect(sm.dag() * sm, rho_ss)
 
     p_ss_analytic = np.exp(-1.0 / wth_vec) / (1 + np.exp(-1.0 / wth_vec))
@@ -135,14 +135,13 @@ def test_qubit_power_gmres():
     p_ss = np.zeros(np.shape(wth_vec))
 
     for idx, wth in enumerate(wth_vec):
-
         n_th = 1.0 / (np.exp(1.0 / wth) - 1)  # bath temperature
         c_op_list = []
         rate = gamma1 * (1 + n_th)
         c_op_list.append(np.sqrt(rate) * sm)
         rate = gamma1 * n_th
         c_op_list.append(np.sqrt(rate) * sm.dag())
-        rho_ss = steadystate(H, c_op_list, method='power-gmres')
+        rho_ss = steadystate(H, c_op_list, method='power-gmres', mtol=1e-1)
         p_ss[idx] = expect(sm.dag() * sm, rho_ss)
 
     p_ss_analytic = np.exp(-1.0 / wth_vec) / (1 + np.exp(-1.0 / wth_vec))
@@ -308,7 +307,7 @@ def test_ho_power():
         c_op_list.append(np.sqrt(rate) * a)
         rate = gamma1 * n_th
         c_op_list.append(np.sqrt(rate) * a.dag())
-        rho_ss = steadystate(H, c_op_list, method='power')
+        rho_ss = steadystate(H, c_op_list, method='power', mtol=1e-5)
         p_ss[idx] = np.real(expect(a.dag() * a, rho_ss))
 
     p_ss_analytic = 1.0 / (np.exp(1.0 / wth_vec) - 1)
@@ -334,7 +333,8 @@ def test_ho_power_gmres():
         c_op_list.append(np.sqrt(rate) * a)
         rate = gamma1 * n_th
         c_op_list.append(np.sqrt(rate) * a.dag())
-        rho_ss = steadystate(H, c_op_list, method='power-gmres',use_precond=1)
+        rho_ss = steadystate(H, c_op_list, method='power-gmres', mtol=1e-1,
+                             use_precond=1)
         p_ss[idx] = np.real(expect(a.dag() * a, rho_ss))
 
     p_ss_analytic = 1.0 / (np.exp(1.0 / wth_vec) - 1)
@@ -469,11 +469,11 @@ def test_driven_cavity_power():
     H = Omega * (a.dag() + a)
     c_ops = [np.sqrt(Gamma) * a]
 
-    rho_ss = steadystate(H, c_ops, method='power')
+    rho_ss = steadystate(H, c_ops, method='power', mtol=1e-5,)
     rho_ss_analytic = coherent_dm(N, -1.0j * (Omega)/(Gamma/2))
 
     assert_((rho_ss - rho_ss_analytic).norm() < 1e-4)
-    
+
 
 def test_driven_cavity_power_gmres():
     "Steady state: Driven cavity - power-gmres solver"
@@ -486,7 +486,8 @@ def test_driven_cavity_power_gmres():
     H = Omega * (a.dag() + a)
     c_ops = [np.sqrt(Gamma) * a]
     M = build_preconditioner(H, c_ops, method='power')
-    rho_ss = steadystate(H, c_ops, method='power-gmres', M=M, use_precond=1)
+    rho_ss = steadystate(H, c_ops, method='power-gmres', M=M, mtol=1e-1,
+                         use_precond=1)
     rho_ss_analytic = coherent_dm(N, -1.0j * (Omega)/(Gamma/2))
     assert_((rho_ss - rho_ss_analytic).norm() < 1e-4)