Repository: scipy/scipy

Issue:
`OptimizeResult.message` from `L-BFGS-B` is a bytes, not a str.
The doc for `OptimizeResult` says that its `message` attribute will be a string.

For `L-BFGS-B`, it is a bytes object.   That makes it harder to serialize the `OptimizeResult`.

#### Reproducing code example:

```
from scipy.optimize import minimize, rosen
x0 = [1.3, 0.7, 0.8, 1.9, 1.2]
for method in ('Nelder-Mead', 'Powell',  'BFGS',
               'TNC', 'COBYLA', 'SLSQP', 'L-BFGS-B'):
    res = minimize(rosen, x0, method=method, tol=1e-6)
    print(method, type(res.message), res.message)
```

prints out

```
Nelder-Mead <class 'str'> Optimization terminated successfully.
Powell <class 'str'> Optimization terminated successfully.
BFGS <class 'str'> Desired error not necessarily achieved due to precision loss.
TNC <class 'str'> Converged (|f_n-f_(n-1)| ~= 0)
COBYLA <class 'str'> Maximum number of function evaluations has been exceeded.
SLSQP <class 'str'> Optimization terminated successfully
L-BFGS-B <class 'bytes'> b'CONVERGENCE: REL_REDUCTION_OF_F_<=_FACTR*EPSMCH'
```

#### Error message:


N/A

#### Scipy/Numpy/Python version information:
```
>>>import sys, scipy, numpy; print(scipy.__version__, numpy.__version__, sys.version_info)
1.5.2 1.19.1 sys.version_info(major=3, minor=8, micro=5, releaselevel='final', serial=0)
```

`OptimizeResult.message` from `L-BFGS-B` is a bytes, not a str.
The doc for `OptimizeResult` says that its `message` attribute will be a string.

For `L-BFGS-B`, it is a bytes object.   That makes it harder to serialize the `OptimizeResult`.

#### Reproducing code example:

```
from scipy.optimize import minimize, rosen
x0 = [1.3, 0.7, 0.8, 1.9, 1.2]
for method in ('Nelder-Mead', 'Powell',  'BFGS',
               'TNC', 'COBYLA', 'SLSQP', 'L-BFGS-B'):
    res = minimize(rosen, x0, method=method, tol=1e-6)
    print(method, type(res.message), res.message)
```

prints out

```
Nelder-Mead <class 'str'> Optimization terminated successfully.
Powell <class 'str'> Optimization terminated successfully.
BFGS <class 'str'> Desired error not necessarily achieved due to precision loss.
TNC <class 'str'> Converged (|f_n-f_(n-1)| ~= 0)
COBYLA <class 'str'> Maximum number of function evaluations has been exceeded.
SLSQP <class 'str'> Optimization terminated successfully
L-BFGS-B <class 'bytes'> b'CONVERGENCE: REL_REDUCTION_OF_F_<=_FACTR*EPSMCH'
```

#### Error message:


N/A

#### Scipy/Numpy/Python version information:
```
>>>import sys, scipy, numpy; print(scipy.__version__, numpy.__version__, sys.version_info)
1.5.2 1.19.1 sys.version_info(major=3, minor=8, micro=5, releaselevel='final', serial=0)
```



Discussion:

Resolving patch:
diff --git a/scipy/optimize/lbfgsb.py b/scipy/optimize/lbfgsb.py
--- a/scipy/optimize/lbfgsb.py
+++ b/scipy/optimize/lbfgsb.py
@@ -392,6 +392,7 @@ def _minimize_lbfgsb(fun, x0, args=(), jac=None, bounds=None,
     n_corrs = min(n_bfgs_updates, maxcor)
     hess_inv = LbfgsInvHessProduct(s[:n_corrs], y[:n_corrs])
 
+    task_str = task_str.decode()
     return OptimizeResult(fun=f, jac=g, nfev=sf.nfev,
                           njev=sf.ngev,
                           nit=n_iterations, status=warnflag, message=task_str,


Test patch:
diff --git a/scipy/optimize/tests/test_optimize.py b/scipy/optimize/tests/test_optimize.py
--- a/scipy/optimize/tests/test_optimize.py
+++ b/scipy/optimize/tests/test_optimize.py
@@ -810,7 +810,7 @@ def __call__(self, x):
         assert_almost_equal(res.fun, c.fun)
         assert_equal(res.status, 1)
         assert_(res.success is False)
-        assert_equal(res.message.decode(),
+        assert_equal(res.message,
                      'STOP: TOTAL NO. of ITERATIONS REACHED LIMIT')
 
     def test_minimize_l_bfgs_b(self):
@@ -1891,9 +1891,12 @@ def test_attributes_present(self):
                 if method in skip and attribute in skip[method]:
                     continue
 
-                assert_(hasattr(res, attribute))
+                assert hasattr(res, attribute)
                 assert_(attribute in dir(res))
 
+            # gh13001, OptimizeResult.message should be a str
+            assert isinstance(res.message, str)
+
 
 def f1(z, *params):
     x, y = z