Repository: lmfit/lmfit-py

Issue:
Regression: lmfit imports OptimizeResult with scipy 0.13
Importing lmfit 0.8.2 when scipy 0.13.3 is installed gives:

```
ImportError: cannot import name 'OptimizeResult'
```

I initially got the error when building the docs on ReadTheDocs for another package (FRETBursts) that depends on lmfit (see [error](https://readthedocs.org/builds/fretbursts/2040067/)).

ReadTheDocs has scipy 0.13.3.



Discussion:
Does 0.13.3 have the minimize function? If so we just have add OptimizeResult to 'lmfit/_differentialevolution.py'.

Yes, scipy 0.13 it does have `minimize` but not `OptimizeResult`

I can make a PR for this in the next couple of days.


Resolving patch:
diff --git a/lmfit/_differentialevolution.py b/lmfit/_differentialevolution.py
--- a/lmfit/_differentialevolution.py
+++ b/lmfit/_differentialevolution.py
@@ -4,7 +4,7 @@
 """
 from __future__ import division, print_function, absolute_import
 import numpy as np
-from scipy.optimize import OptimizeResult, minimize
+from scipy.optimize import minimize
 from scipy.optimize.optimize import _status_message
 import numbers
 
@@ -13,6 +13,60 @@
 _MACHEPS = np.finfo(np.float64).eps
 
 
+#------------------------------------------------------------------------------
+# scipy.optimize does not contain OptimizeResult until 0.14. Include here as a
+# fix for scipy < 0.14.
+
+class OptimizeResult(dict):
+    """ Represents the optimization result.
+    Attributes
+    ----------
+    x : ndarray
+        The solution of the optimization.
+    success : bool
+        Whether or not the optimizer exited successfully.
+    status : int
+        Termination status of the optimizer. Its value depends on the
+        underlying solver. Refer to `message` for details.
+    message : str
+        Description of the cause of the termination.
+    fun, jac, hess, hess_inv : ndarray
+        Values of objective function, Jacobian, Hessian or its inverse (if
+        available). The Hessians may be approximations, see the documentation
+        of the function in question.
+    nfev, njev, nhev : int
+        Number of evaluations of the objective functions and of its
+        Jacobian and Hessian.
+    nit : int
+        Number of iterations performed by the optimizer.
+    maxcv : float
+        The maximum constraint violation.
+    Notes
+    -----
+    There may be additional attributes not listed above depending of the
+    specific solver. Since this class is essentially a subclass of dict
+    with attribute accessors, one can see which attributes are available
+    using the `keys()` method.
+    """
+    def __getattr__(self, name):
+        try:
+            return self[name]
+        except KeyError:
+            raise AttributeError(name)
+
+    __setattr__ = dict.__setitem__
+    __delattr__ = dict.__delitem__
+
+    def __repr__(self):
+        if self.keys():
+            m = max(map(len, list(self.keys()))) + 1
+            return '\n'.join([k.rjust(m) + ': ' + repr(v)
+                              for k, v in self.items()])
+        else:
+            return self.__class__.__name__ + "()"
+#------------------------------------------------------------------------------
+
+
 def differential_evolution(func, bounds, args=(), strategy='best1bin',
                            maxiter=None, popsize=15, tol=0.01,
                            mutation=(0.5, 1), recombination=0.7, seed=None,
diff --git a/lmfit/minimizer.py b/lmfit/minimizer.py
--- a/lmfit/minimizer.py
+++ b/lmfit/minimizer.py
@@ -539,8 +539,14 @@ def scalar_minimize(self, method='Nelder-Mead', **kws):
                                  'are required for each parameter')
             bounds = [(-np.pi / 2., np.pi / 2.)] * len(self.vars)
             fmin_kws['bounds'] = bounds
+            
+            # in scipy 0.14 this can be called directly from scipy_minimize
+            # When minimum scipy is 0.14 the following line and the else
+            # can be removed.
+            ret = _differential_evolution(self.penalty, self.vars, **fmin_kws)
+        else:
+            ret = scipy_minimize(self.penalty, self.vars, **fmin_kws)
 
-        ret = scipy_minimize(self.penalty, self.vars, **fmin_kws)
         xout = ret.x
         self.message = ret.message
 


Test patch: