Repository: scipy/scipy

Issue:
Division by zero in _trustregion.py when x0 is exactly equal to the minimum.
When using the `trust_ncg` minimisation algorithm with analytic solutions for the first (`jac`) and second (`hess`) order derivatives, and the initial guess `x0` is exactly equal to the minimum, then `jac_mag` is zero at that point, which causes an unhandled division by zero in `get_boundaries_intersections` in`BaseQuadraticSubproblem` in `_trustregion.py`.

It happens rarely, since the probability of two floating point numbers being exactly equal is low.

Here's a screenshot of my debugger when it happens:

![image](https://user-images.githubusercontent.com/10025342/38453271-1de5fa58-3a53-11e8-84d9-4f3557dea1cd.png)

### Reproducing code example & error message

```
import scipy.optimize
import numpy as np


def fun(x):
    return x * x


def jac(x):
    return 2 * x


def hess(x):
    return 2


results = scipy.optimize.minimize(fun=fun, x0=0, method='trust-ncg', jac=jac, hess=hess, options={
    # 'disp': True,
    # 'gtol': gtol,
    # 'max_trust_radius': 1.,
    # 'initial_trust_radius': 0.5,
})

print results

```

If you change `x0` from `0` to `x0=0 + np.finfo(float).eps`, the output is as expected:
```
     fun: array([0.])
    hess: 2
     jac: array([0.])
 message: 'Optimization terminated successfully.'
    nfev: 2
    nhev: 1
     nit: 1
    njev: 2
  status: 0
 success: True
       x: array([0.])

```

Otherwise, we get:

```
/home/dolf/anaconda2/lib/python2.7/site-packages/scipy/optimize/_trustregion.py:93: RuntimeWarning: invalid value encountered in double_scalars
  ta = -aux / (2*a)
/home/dolf/anaconda2/lib/python2.7/site-packages/scipy/optimize/_trustregion.py:94: RuntimeWarning: divide by zero encountered in double_scalars
  tb = -2*c / aux
/home/dolf/anaconda2/lib/python2.7/site-packages/scipy/optimize/_trustregion_ncg.py:100: RuntimeWarning: invalid value encountered in multiply
  pb = z + tb * d
     fun: array([0.])
    hess: 2
     jac: array([0.])
 message: 'Optimization terminated successfully.'
    nfev: 2
    nhev: 1
     nit: 1
    njev: 1
  status: 0
 success: True
       x: array([0.])
```

Which is still the correct answer, but I think the special case should be handled internally by scipy, without giving a warning to the user.

### Scipy/Numpy/Python version information:
```
('1.0.0', '1.14.0', sys.version_info(major=2, minor=7, micro=14, releaselevel='final', serial=0))
```




Discussion:
Thank you a lot for reporting the error.

I will try to take a look at it when I have some time. It seems to be something easy to fix.

Resolving patch:
diff --git a/scipy/optimize/_trustregion.py b/scipy/optimize/_trustregion.py
--- a/scipy/optimize/_trustregion.py
+++ b/scipy/optimize/_trustregion.py
@@ -178,7 +178,8 @@ def _minimize_trust_region(fun, x0, args=(), jac=None, hess=None, hessp=None,
     k = 0
 
     # search for the function min
-    while True:
+    # do not even start if the gradient is small enough
+    while m.jac_mag >= gtol:
 
         # Solve the sub-problem.
         # This gives us the proposed step relative to the current position


Test patch:
diff --git a/scipy/optimize/tests/test_trustregion.py b/scipy/optimize/tests/test_trustregion.py
--- a/scipy/optimize/tests/test_trustregion.py
+++ b/scipy/optimize/tests/test_trustregion.py
@@ -85,8 +85,22 @@ def test_solver_concordance(self):
             assert_(len(r_dogleg['allvecs']) < len(r_ncg['allvecs']))
 
     def test_trust_ncg_hessp(self):
-        for x0 in (self.easy_guess, self.hard_guess):
+        for x0 in (self.easy_guess, self.hard_guess, self.x_opt):
             r = minimize(rosen, x0, jac=rosen_der, hessp=rosen_hess_prod,
                          tol=1e-8, method='trust-ncg')
             assert_allclose(self.x_opt, r['x'])
 
+    def test_trust_ncg_start_in_optimum(self):
+        r = minimize(rosen, x0=self.x_opt, jac=rosen_der, hess=rosen_hess,
+                     tol=1e-8, method='trust-ncg')
+        assert_allclose(self.x_opt, r['x'])
+
+    def test_trust_krylov_start_in_optimum(self):
+        r = minimize(rosen, x0=self.x_opt, jac=rosen_der, hess=rosen_hess,
+                     tol=1e-8, method='trust-krylov')
+        assert_allclose(self.x_opt, r['x'])
+
+    def test_trust_exact_start_in_optimum(self):
+        r = minimize(rosen, x0=self.x_opt, jac=rosen_der, hess=rosen_hess,
+                     tol=1e-8, method='trust-exact')
+        assert_allclose(self.x_opt, r['x'])