Repository: scipy/scipy

Issue:
Bug: setting iprint=0 hides all output from fmin_l_bfgs_b, but it should print convergence info from final iteration.
`scipy.optimize.fmin_l_bfgs_b` provides an option to print a report after the optimisation procedure with the keyword argument `iprint=0`. However, this option does not work, as demonstrated by the code below. This bug is likely caused by how the display option (required for the minimize wrapper) is implemented, as it modifies the same behaviour as `iprint`, but in a different manner. If `display=0`, then all output from `scipy.optimize.fmin_l_bfgs_b` is suppressed, whereas we should set `iprint=-1` for the same behaviour.

The `display` argument is handled in both `scipy.optimize.fmin_l_bfgs_b` and `scipy.optimize._minimize_lbfgsb`, and this seems to cause the problem. It should be sufficient to handle the `display` argument in `scipy.optimize._minimize_lbfgsb`.

We can provide a pull request to fix this bug.

#### Reproducing code example:
This code should display convergence information of the final iteration, but it does not.
```python
import scipy.optimize as optimize
from scipy.optimize.optimize import rosen, rosen_der
import numpy as np

result = optimize.fmin_l_bfgs_b(rosen, np.array([4, 3]), rosen_der, iprint=0, bounds=((3, 5), (3, 5)))
```

#### Scipy/Numpy/Python version information:
```
import sys, scipy, numpy; print(scipy.__version__, numpy.__version__, sys.version_info)
```
--> 1.5.2 1.18.5 sys.version_info(major=3, minor=8, micro=6, releaselevel='final', serial=0)




Discussion:

Resolving patch:
diff --git a/scipy/optimize/_lbfgsb_py.py b/scipy/optimize/_lbfgsb_py.py
--- a/scipy/optimize/_lbfgsb_py.py
+++ b/scipy/optimize/_lbfgsb_py.py
@@ -183,8 +183,6 @@ def fmin_l_bfgs_b(func, x0, fprime=None, args=(),
         jac = fprime
 
     # build options
-    if disp is None:
-        disp = iprint
     opts = {'disp': disp,
             'iprint': iprint,
             'maxcor': m,


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
@@ -1065,6 +1065,40 @@ def custmin(fun, x0, args=(), maxfev=None, stepsize=0.1,
                                 options=dict(stepsize=0.05))
         assert_allclose(res.x, 1.0, rtol=1e-4, atol=1e-4)
 
+    @pytest.mark.xfail(reason="output not reliable on all platforms")
+    def test_gh13321(self, capfd):
+        # gh-13321 reported issues with console output in fmin_l_bfgs_b;
+        # check that iprint=0 works.
+        kwargs = {'func': optimize.rosen, 'x0': [4, 3],
+                  'fprime': optimize.rosen_der, 'bounds': ((3, 5), (3, 5))}
+
+        # "L-BFGS-B" is always in output; should show when iprint >= 0
+        # "At iterate" is iterate info; should show when iprint >= 1
+
+        optimize.fmin_l_bfgs_b(**kwargs, iprint=-1)
+        out, _ = capfd.readouterr()
+        assert "L-BFGS-B" not in out and "At iterate" not in out
+
+        optimize.fmin_l_bfgs_b(**kwargs, iprint=0)
+        out, _ = capfd.readouterr()
+        assert "L-BFGS-B" in out and "At iterate" not in out
+
+        optimize.fmin_l_bfgs_b(**kwargs, iprint=1)
+        out, _ = capfd.readouterr()
+        assert "L-BFGS-B" in out and "At iterate" in out
+
+        # `disp is not None` overrides `iprint` behavior
+        # `disp=0` should suppress all output
+        # `disp=1` should be the same as `iprint = 1`
+
+        optimize.fmin_l_bfgs_b(**kwargs, iprint=1, disp=False)
+        out, _ = capfd.readouterr()
+        assert "L-BFGS-B" not in out and "At iterate" not in out
+
+        optimize.fmin_l_bfgs_b(**kwargs, iprint=-1, disp=True)
+        out, _ = capfd.readouterr()
+        assert "L-BFGS-B" in out and "At iterate" in out
+
     def test_gh10771(self):
         # check that minimize passes bounds and constraints to a custom
         # minimizer without altering them.