Repository: statsmodels/statsmodels

Issue:
fit_regularized returns the string 'True' in mle_retvals object for 'coverged'
When fitting a logit model using the fit_regularized option I notice that mle_retvals dictionary returns the string 'True' instead of the boolean True after a successful convergence. Discovered this error when trying to deal with some exception handling.



Discussion:
The relevant line is here:

https://github.com/statsmodels/statsmodels/blob/master/statsmodels/base/l1_slsqp.py#L102

also here:

https://github.com/statsmodels/statsmodels/blob/f3077577a983fc16217c77d98618510e7eebd66c/statsmodels/base/l1_cvxopt.py#L125

I believe everywhere else in statsmodels, `converged` holds a boolean, but here it holds a string.  The use of the string 'True' was probably done intentionally to maintain type consistency. 

It should probably be changed for consistency with other models.   


Resolving patch:
diff --git a/statsmodels/base/l1_cvxopt.py b/statsmodels/base/l1_cvxopt.py
--- a/statsmodels/base/l1_cvxopt.py
+++ b/statsmodels/base/l1_cvxopt.py
@@ -122,11 +122,12 @@ def F(x=None, z=None):
         gopt = float('nan')  # Objective is non-differentiable
         hopt = float('nan')
         iterations = float('nan')
-        converged = 'True' if results['status'] == 'optimal'\
-            else results['status']
+        converged = (results['status'] == 'optimal')
+        warnflag = results['status']
         retvals = {
             'fopt': fopt, 'converged': converged, 'iterations': iterations,
-            'gopt': gopt, 'hopt': hopt, 'trimmed': trimmed}
+            'gopt': gopt, 'hopt': hopt, 'trimmed': trimmed,
+            'warnflag': warnflag}
     else:
         x = np.array(results['x']).ravel()
         params = x[:k_params]
diff --git a/statsmodels/base/l1_slsqp.py b/statsmodels/base/l1_slsqp.py
--- a/statsmodels/base/l1_slsqp.py
+++ b/statsmodels/base/l1_slsqp.py
@@ -99,13 +99,15 @@ def fit_l1_slsqp(
     if full_output:
         x_full, fx, its, imode, smode = results
         fopt = func(np.asarray(x_full))
-        converged = 'True' if imode == 0 else smode
+        converged = (imode == 0)
+        warnflag = str(imode) + ' ' + smode
         iterations = its
         gopt = float('nan')     # Objective is non-differentiable
         hopt = float('nan')
         retvals = {
             'fopt': fopt, 'converged': converged, 'iterations': iterations,
-            'gopt': gopt, 'hopt': hopt, 'trimmed': trimmed}
+            'gopt': gopt, 'hopt': hopt, 'trimmed': trimmed,
+            'warnflag': warnflag}
 
     ### Return
     if full_output:


Test patch:
diff --git a/statsmodels/discrete/tests/test_discrete.py b/statsmodels/discrete/tests/test_discrete.py
--- a/statsmodels/discrete/tests/test_discrete.py
+++ b/statsmodels/discrete/tests/test_discrete.py
@@ -833,6 +833,8 @@ def test_basic_results(self):
         assert_almost_equal(self.res1.bic, self.res2.bic, DECIMAL_4)
         assert_almost_equal(self.res1.pvalues, self.res2.pvalues, DECIMAL_4)
 
+        assert_(self.res1.mle_retvals['converged'] is True)
+
 
 class CompareL11D(CompareL1):
     """
@@ -858,6 +860,14 @@ def setup_class(cls):
                 trim_mode='auto', auto_trim_tol=0.01)
         cls.res2 = Logit(data.endog, data.exog).fit(disp=0, tol=1e-15)
 
+    def test_converged(self):
+        res = self.res1.model.fit_regularized(
+                method="l1", alpha=0, disp=0, acc=1e-15, maxiter=1,
+                trim_mode='auto', auto_trim_tol=0.01)
+
+        # see #2857
+        assert_(res.mle_retvals['converged'] is False)
+
 
 class TestL1AlphaZeroProbit(CompareL11D):
     # Compares l1 model with alpha = 0 to the unregularized model.