Repository: scipy/scipy

Issue:
minimize() should have friendlier args=
This is a bit newbie-unfriendly:

```
>>> def f(params, data):
...     return (...)
>>> minimize(f, x0, args=mydata)
...
TypeError: can only concatenate tuple (not "list") to tuple
```

This could use similar logic as in `scipy.integrate.quad`, which has

```
if not isinstance(args, tuple):
    args = (args,)
```



Discussion:
fwiw, I personally never use `args=` but I use `functools.partial` instead.  But I understand that we need to keep `args=` and that suggesting that newbies use `functools.partial` would fall into the category of "now we have _two_ problems!"

The example problem in `minimize()` docstring is also not good, since it uses a predefined `rosen` function and so it does not actually show how to define proper objective functions.

Similarly, the optimization example in the optimization tutorial is not as good as it should be, because it uses multidimensional rosenbrock function which is too complicated to understand.

Sorry if I've asked this before or if it's too off-topic, but is there an analogous way to check docs/tutorials changes that is as simple and foolproof as using `runtests.py` for code changes?  For code changes I can be sure that I'm using the version of scipy checked out in git, and that I'm not spamming up my git directories with .pyc files and other partial build remnants which may cause problems in the future, and with ccache and `-s` flags I can know within a few seconds whether my code changes passed the unit tests.  For docs building instructions, I see "The easy way to build the documentation is to run `python setup.py build_sphinx` This will first build Scipy in-place, and then generate documentation for it" but this way of checking docs/tutorials changes seems to be missing some of the nice features that `runtests.py` provides for code changes.

Duplicate of #3503 

Working on it


Resolving patch:
diff --git a/scipy/optimize/_minimize.py b/scipy/optimize/_minimize.py
--- a/scipy/optimize/_minimize.py
+++ b/scipy/optimize/_minimize.py
@@ -328,6 +328,9 @@ def minimize(fun, x0, args=(), method=None, jac=None, hess=None,
 
     """
 
+    if not isinstance(args, tuple):
+        args = (args,)
+
     if method is None:
         # Select automatically
         if constraints:
@@ -553,6 +556,9 @@ def minimize_scalar(fun, bracket=None, bounds=None, args=(),
     -2.0000002026
 
     """
+    if not isinstance(args, tuple):
+        args = (args,)
+
     if callable(method):
         meth = "_custom"
     else:
diff --git a/scipy/optimize/_root.py b/scipy/optimize/_root.py
--- a/scipy/optimize/_root.py
+++ b/scipy/optimize/_root.py
@@ -144,6 +144,9 @@ def root(fun, x0, args=(), method='hybr', jac=None, tol=None, callback=None,
     >>> sol.x
     array([ 0.8411639,  0.1588361])
     """
+    if not isinstance(args, tuple):
+        args = (args,)
+
     meth = method.lower()
     if options is None:
         options = {}


Test patch:
diff --git a/scipy/optimize/tests/test__root.py b/scipy/optimize/tests/test__root.py
--- a/scipy/optimize/tests/test__root.py
+++ b/scipy/optimize/tests/test__root.py
@@ -38,3 +38,10 @@ def dfunc(z):
             assert_(sol2.success, msg)
             assert_(abs(func(sol1.x)).max() < abs(func(sol2.x)).max(),
                     msg)
+
+    def test_minimize_scalar_coerce_args_param(self):
+        # github issue #3503
+        def func(z, f=1):
+            x, y = z
+            return np.array([x**3 - 1, y**3 - f])
+        root(func, [1.1, 1.1], args=1.5)
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
@@ -586,6 +586,18 @@ def cons(x):
         assert_allclose(sol_3.x, 5, atol=1e-8)
         assert_allclose(sol_4.x, 2, atol=1e-8)
 
+    def test_minimize_coerce_args_param(self):
+        # github issue #3503
+        def Y(x, c):
+            return np.sum((x-c)**2)
+
+        def dY_dx(x, c=None):
+            return 2*(x-c)
+
+        c = np.array([3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5])
+        xinit = np.random.randn(len(c))
+        optimize.minimize(Y, xinit, jac=dY_dx, args=(c), method="BFGS")
+
 
 class TestLBFGSBBounds(TestCase):
     """ Tests for L-BFGS-B with bounds """
@@ -768,6 +780,10 @@ def custmin(fun, bracket, args=(), maxfev=None, stepsize=0.1,
                                        options=dict(stepsize=0.05))
         assert_allclose(res.x, self.solution, atol=1e-6)
 
+    def test_minimize_scalar_coerce_args_param(self):
+        # github issue #3503
+        optimize.minimize_scalar(self.fun, args=1.5)
+
 
 class TestNewtonCg(object):
     def test_rosenbrock(self):