scipy fmin operands could not be broadcast together with shapes - python

i'm trying to learn about optimization in Python so i've written some code to test out the fmin function.
However i keep receiving the following error:
ValueError: operands could not be broadcast together with shapes (1,2) (100,)
I can tell the issue is to do with the dimensions of my arguments but I'm not sure how to rectify it. Rather than a lambda function I also tried to def a function but I still get the same error.
I'm sure it's something pretty basic but I can't seem to understand it. Any help wold be greatly appreciated!
import numpy as np
import pandas as pd
from scipy.stats.distributions import norm
from scipy.optimize import fmin
x = np.random.normal(size=100)
norm_1 = lambda theta,x: -(np.log(norm.pdf(x,theta[0],theta[1]))).sum()
def norm_2(theta,x):
mu = theta[0]
sigma = theta[1]
ll = np.log(norm.pdf(x,mu,sigma)).sum()
return -ll
fmin(norm_1,np.array([0,1]),x)
fmin(norm_2,np.array([0,1]),x)

The docs for fmin say:
Definition: fmin(func, x0, args=(), xtol=0.0001, ftol=0.0001, maxiter=None, maxfun=None, full_output=0, disp=1, retall=0, callback=None)
...
args : tuple, optional
Extra arguments passed to func, i.e. ``f(x,*args)``.
Therefore, the third argument, args, should be a tuple:
In [45]: fmin(norm_1,np.array([0,1]),(x,))
Warning: Maximum number of function evaluations has been exceeded.
Out[45]: array([-0.02405078, 1.0203125 ])
(x, ) is a tuple containing one element, x.
The docs say f(x, *args) gets called. Which mean in your case
norm_1(np.array([0,1]), *(x,))
will get called, which is equivalent to
norm_1(np.array([0,1]), x)

Related

pasing arguments in global optimization in scipy.optimize

I am trying to solve an optimization problem with scipy global optimization. I am using differential evolution.
code
def objective(x,*args):
x = np.append(x,args)
res = MLmodel.predict(x)
return res
fun_history = []
x_values = []
def callback(x,convergence):
fobj = objective(x)
x_values.append(x)
fun_history.append(fobj)
bounds = [(5.5,8.8),(29,40)]
load = (50,)
res = optimize.differential_evolution(objective,bounds,args=load,disp=True,callback = callback)
My objective function takes these three parameters as input and gives an output. I want to optimize on only the first two parameters. So the third parameter I am passing as an argument.
When I run the optimizer I get an error after the first run, which says
ValueError: operands could not be broadcast together with shapes (1,2) (3,) (1,2)
I guess the argument is not being appended to the x value from the second run.
Can someone help me with how to solve this problem?

using scipy solve_ivp for a function with extra arguments

I am trying to solve an initial value problem with solve_ivp. The problem is that my function f(x,y)=dy/dx contain extra arguments. I tried to use this:
Pass args for solve_ivp (new SciPy ODE API)
but it keeps giving me errors. Here is the code:
from numpy import arange
import math
import numpy as np
from scipy.integrate import solve_ivp
from numpy import pi
sigmav = 1.0e-9
Mpl = 1.22e19
ms=100.0
gg=100.0
gs=106.75
def Yeq(x):
return 0.145*(gg/gs)*(x)**(3/2)*np.exp(-x)
def ss(x,m_dm):
return (2/45)*((pi)**2)*gs*(m_dm**3/x**3)
def hubb(x,m_dm):
return 1.66*(gg**(1/2))*(m_dm**2/Mpl)*(1/x**2)
def derivada(x,yl,m_dm,σv):
return -(σv*ss(x,m_dm)*((yl**2)-(Yeq(x)**2)))/(x*hubb(x,m_dm))
xx=np.logspace(np.log10(3),3,100)
y00 = Yeq(xx[0])
x00 = xx[0]
sigmav = 1.7475e-9
ms=100.0
solucion1 = solve_ivp(fun=lambda x, y: derivada(x, y, args=(ms,sigmav),[np.min(xx),np.max(xx)],[y00],method='BDF',rtol=1e-10,atol=1e-10))
When I try to run the cell for solucion1, it returns the following:
File "<ipython-input-17-afcf6c3782a9>", line 6
solucion1 = solve_ivp(fun=lambda x, y: derivada(x, y, args=(ms,sigmav),[np.min(xx),np.max(xx)],[y00],method='BDF',rtol=1e-10,atol=1e-10))
^
SyntaxError: positional argument follows keyword argument
It must be something very easy but I am just starting to work with python, so please help me with this.
You simply mis-placed a closing parenthesis. As derivada has no named args, remove the args statement there, the lambda expression already binds these additional parameters. Also, the passed function is not a named argument
solucion1 = solve_ivp(lambda x, y: derivada(x, y, ms, sigmav),[np.min(xx),np.max(xx)], [y00], method='BDF', rtol=1e-10, atol=1e-10)
or in the form that you cite from the github discussion, where the parameter list is constructed elsewhere
args = ( ms, sigmav )
solucion1 = solve_ivp(lambda x, y: derivada(x, y, *args),[np.min(xx),np.max(xx)], [y00], method='BDF', rtol=1e-10, atol=1e-10)
The error message gives the answer: all positional arguments need to come before keyword arguments. In your case, I'd suggest using partial:
from functools import partial
f = partial(derivada, m_dm=ms, σv=sigmav)
solucion1 = solve_ivp(f, [np.min(xx),np.max(xx)],[y00],method='BDF',rtol=1e-10,atol=1e-10)
This issue has been resolved in SciPy 1.4+.
https://github.com/scipy/scipy/issues/8352#issuecomment-619243137
You should be able to add extra arguments to the function as you did with odeint, lambda function not required. I think this is what you intended for the solution
solucion1 = solve_ivp(fun=derivada,
t_span=[np.min(xx),np.max(xx)],
y0=[y00],
args=(ms,sigmav),
method='BDF',
rtol=1e-10,
atol=1e-10)

Find the minimum of a 2d interpolation

I'm trying to find the minimum of a 2d interpolation. I"m really stuck on trying to find a way to appropriately pass the data to the optimizer,
here is the code I have so far:
import scipy
from scipy.interpolate import interp2d
a_ca_energy_interp = interp2d(a, c_a, Energy)
def run_2d_params(params, func):
a, b = params
return func(a, b)
scipy.optimize.fmin(run_2d_params, np.array([1.60,6.075]),
args=a_ca_energy_interp)
Which throws the error:
TypeError: can only concatenate tuple (not "interp2d") to tuple
args must be a tuple, even if it is only one argument:
scipy.optimize.fmin(run_2d_params, np.array([1.60,6.075]),
args=(a_ca_energy_interp, ))

TypeError: deriv() takes 2 positional arguments but 4 were given

I'm getting the above error. I understand in principle what it means, but can't really see how it applies to my code
#project starts here
import numpy as np
import scipy.integrate
import matplotlib.pyplot as plt
from numpy import pi
from scipy.integrate import odeint
def deriv(cond,t):
for q in range (0,N):
i=6*q
dydt[i]=cond[i+3]
dydt[i+1]=cond[i+4]
dydt[i+2]=cond[i+5]
r=sqrt((cond[i])**2 +(cond[i+1])**2 +(cond[i+2])**2)
dydt[i+3]=-G*M*cond[i]/(r**3)
dydt[i+4]=-G*M*cond[i+1]/(r**3)
dydt[i+5]=-G*M*cond[i+2]/(r**3)
return dydt
G=1
M=1
N=12
vmag=((G*M)/(2))**(0.5)
theta = np.linspace(0,2*pi,N)
x=2*np.cos(theta)
y=2*np.sin(theta)
vx=-vmag*np.sin(theta)
vy=vmag*np.cos(theta)
z=np.zeros(N)
vz=np.zeros(N)
t=np.linspace(0,30,100)
cond=list(item for group in zip(x,y,z,vx,vy,vz) for item in group)
sln=odeint(deriv, cond, t, args=(G,M))
Any ideas where it is coming from? I feel like I have given the correct number of arguments.
You are sending 4 arguments to deriv. Per the odeint docs, you have a function deriv whose first two arguments must be y and t. When you call odeint(deriv,cond,t,...) the cond and t are automatically sent as the first two arguments to deriv. All you need to do is to make deriv(cond,t,G,M).
If you look documantation for odeint [1], you will see that your fucntion to call in oneint must be in form func(y, t0, ...). So when you call odeint(deriv, cond, t, args=(G,M)) it actually call your function as deriv(cond,t,G,m). But your function takes just 2 argument.
[1] http://docs.scipy.org/doc/scipy-0.17.0/reference/generated/scipy.integrate.odeint.html

Error: [only length-1 arrays can be converted to Python scalars] when changing variable order

Dear Stackoverflow Community,
I am very new to Python and to programming in general, so please don't get mad when I don't get your answers and ask again.
I am trying to fit a curve to experimental data with scipy.optimization.curve_fit. This is my code:
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as nm
from __future__ import division
import cantera as ct
from matplotlib.backends.backend_pdf import PdfPages
import math as ma
import scipy.optimize as so
R = 8.314
T = nm.array([700, 900, 1100, 1300, 1400, 1500, 1600, 1700])
k = nm.array([289, 25695, 763059, 6358040, 14623536, 30098925, 56605969, 98832907])
def func(A, E, T):
return A*ma.exp(-E/(R*T))
popt, pcov = so.curve_fit(func, T, k)
Now this code works for me, but if I change the function to:
def func(T, A, E)
and keep the rest I get:
TypeError: only length-1 arrays can be converted to Python scalars
Also I am not really convinced by the Parameter solution of the first one.
Can anyone tell me what happens when you change the variable order?
I got the same problem and found the cause and its solution:
The problem lies on the implementation of Scipy. After the optimal parameter has been found, Scipy calls your function with the input array xdata as first argument. That is, it calls func(xdata, *args), and the function complains with a type error because xdata is not an scalar. For example:
from math import erf
erf([1, 2]) # TypeError
erf(np.array([1, 2])) # TypeError
To avoid the error, you can add custom code for supporting arrays, or better, as suggested in the answer of Joris, use numpy functions because they have support for scalars and arrays.
If the math function is not in numpy , like erf or any custom function you coded, then I recommend you instead of doing from math import erf, to do as follows:
from math import erf as math_erf # only supports scalars
import numpy as np
erf = np.vectorize(math_erf) # adds array support
def fit_func(t,s):
return 0.5*(1.0-erf(t/(np.sqrt(2)*s)))
X = np.linspace(-5,5,1000)
Y = np.array([fit_func(x,1) for x in X])
curve_fit(fit_func, X, Y)
The curve_fit function from scipy does not handle very well embedded functions from the math module. When you change the exponential function to the numpy exponential function you don't get the error:
def func(A, E, T):
return A*np.exp(-E/(R*T))
I wonder whether you data shows an exponential decay of rate. The mathematical model may not be the most suitable one.
See the doc string of curve_fit
f : callable
The model function, f(x, ...). It must take the independent variable as the first argument and the parameters to fit as separate remaining arguments.
since your formula is essentially: k=A*ma.exp(-E/(R*T)), the right order of parameters in func should be (T, A, E) or (T, E, A).
Regarding the order of A and E, they don't really matter. If you flip them, the result will get flipped as well:
>>> def func(T, A, E):
return A*ma.exp(-E/(R*T))
>>> so.curve_fit(func, T, k)
(array([ 8.21449078e+00, -5.86499656e+04]), array([[ 6.07720215e+09, 4.31864058e+12],
[ 4.31864058e+12, 3.07102992e+15]]))
>>> def func(T, E, A):
return A*ma.exp(-E/(R*T))
>>> so.curve_fit(func, T, k)
(array([ -5.86499656e+04, 8.21449078e+00]), array([[ 3.07102992e+15, 4.31864058e+12],
[ 4.31864058e+12, 6.07720215e+09]]))
I didn't get your typeerror at all.

Categories