Repository: scipy/scipy

Issue:
BUG: if zero derivative at root, then Newton fails with RuntimeWarning
As indicated in [this discussion](../pull/8357#discussion_r192612946) by @pvanmulbregt

>... if the derivative is 0 at the root? E.g. `f(x)=x**3 - x**2`, starting at `x0=0` (immediately at the root), ... It looks like this might be considered a failure


### Reproducing code example:
```
from scipy import optimize

# use secant
r = optimize.newton(lambda x: x**3 - x**2, x0=0)
# -1.3552527156068808e-20  close enough to zero to be the correct answer

# now try Newton
r = optimize.newton(lambda x: x**3 - x**2, x0=0, fprime=lambda x: 3*x**2 - 2*x)
# raises RuntimeWarning but solution is zero

# or Halley's
r = optimize.newton(lambda x: x**3 - x**2, x0=0, fprime=lambda x: 3*x**2 - 2*x, fprime2=lambda x: 6*x - 2)
# also raises RuntimeWarning even though solution should be zero
```

### Error message:
```
~/miniconda/lib/python3.6/site-packages/scipy/optimize/zeros.py:169: RuntimeWarning: derivative was zero.
  warnings.warn(msg, RuntimeWarning)
```

### Scipy/Numpy/Python version information:
```
1.1.0 1.14.3 sys.version_info(major=3, minor=6, micro=5, releaselevel='final', serial=0)
```




Discussion:

Resolving patch:
diff --git a/scipy/optimize/zeros.py b/scipy/optimize/zeros.py
--- a/scipy/optimize/zeros.py
+++ b/scipy/optimize/zeros.py
@@ -3,7 +3,7 @@
 import warnings
 
 from . import _zeros
-from numpy import finfo, sign, sqrt
+from numpy import finfo
 
 _iter = 100
 _xtol = 2e-12
@@ -191,14 +191,18 @@ def newton(func, x0, fprime=None, args=(), tol=1.48e-8, maxiter=50,
     if fprime is not None:
         # Newton-Raphson method
         for itr in range(maxiter):
+            # first evaluate fval
+            fval = func(p0, *args)
+            funcalls += 1
+            # If fval is 0, a root has been found, then terminate
+            if fval == 0:
+                return _results_select(full_output, (p0, funcalls, itr, _ECONVERGED))
             fder = fprime(p0, *args)
             funcalls += 1
             if fder == 0:
                 msg = "derivative was zero."
                 warnings.warn(msg, RuntimeWarning)
                 return _results_select(full_output, (p0, funcalls, itr + 1, _ECONVERR))
-            fval = func(p0, *args)
-            funcalls += 1
             newton_step = fval / fder
             if fprime2 is None:
                 # Newton step


Test patch:
diff --git a/scipy/optimize/tests/test_zeros.py b/scipy/optimize/tests/test_zeros.py
--- a/scipy/optimize/tests/test_zeros.py
+++ b/scipy/optimize/tests/test_zeros.py
@@ -97,7 +97,7 @@ def test_newton_full_output(self):
                     x, r = zeros.newton(f1, x0, maxiter=iters, disp=True, **kwargs)
 
     def test_deriv_zero_warning(self):
-        func = lambda x: x**2
+        func = lambda x: x**2 - 2.0
         dfunc = lambda x: 2*x
         assert_warns(RuntimeWarning, cc.newton, func, 0.0, dfunc)
 
@@ -169,3 +169,36 @@ def f_2(x, *a):
     y = zeros.newton(f, z, args=coeffs, fprime=f_1, fprime2=f_2, tol=1e-6)
     # (-0.75000000000000078+1.1989578808281789j)
     assert_allclose(f(y, *coeffs), 0, atol=1e-6)
+
+
+# this test should **not** raise a RuntimeWarning
+def test_gh8904_zeroder_at_root_fails():
+    """Test that Newton or Halley don't warn if zero derivative at root"""
+
+    # a function that has a zero derivative at it's root
+    def f_zeroder_root(x):
+        return x**3 - x**2
+
+    # should work with secant
+    r = zeros.newton(f_zeroder_root, x0=0)
+    assert_allclose(r, 0, atol=zeros._xtol, rtol=zeros._rtol)
+
+    # 1st derivative
+    def fder(x):
+        return 3 * x**2 - 2 * x
+
+    # should work with newton and halley
+    r = zeros.newton(f_zeroder_root, x0=0, fprime=fder)
+    assert_allclose(r, 0, atol=zeros._xtol, rtol=zeros._rtol)
+    r = zeros.newton(f_zeroder_root, x0=0, fprime=fder,
+                     fprime2=lambda x: 6*x - 2)
+    assert_allclose(r, 0, atol=zeros._xtol, rtol=zeros._rtol)
+
+    # also test that if a root is found we do not raise RuntimeWarning even if
+    # the derivative is zero, EG: at x = 0.5, then fval = -0.125 and
+    # fder = -0.25 so the next guess is 0.5 - (-0.125/-0.5) = 0 which is the
+    # root, but if the solver continued with that guess, then it will calculate
+    # a zero derivative, so it should return the root w/o RuntimeWarning
+    r = zeros.newton(f_zeroder_root, x0=0.5, fprime=fder)
+    assert_allclose(r, 0, atol=zeros._xtol, rtol=zeros._rtol)
+    # doesn't apply to halley