Passing arguments to fsolve - python

I'm solving a nonlinear equation with many constants.
I created a function for solving like:
def terminalV(Vt, data):
from numpy import sqrt
ro_p, ro, D_p, mi, g = (i for i in data)
y = sqrt((4*g*(ro_p - ro)*D_p)/(3*C_d(Re(data, Vt))*ro)) - Vt
return y
Then I want to do:
data = (1800, 994.6, 0.208e-3, 8.931e-4, 9.80665)
Vt0 = 1
Vt = fsolve(terminalV, Vt0, args=data)
But fsolve is unpacking data and passing too many arguments to terminalV function, so I get:
TypeError: terminalV() takes exactly 2 arguments (6 given)
So, my question can I somehow pass a tuple to the function called by fsolve()?

The problem is that you need to use an asterisk to tell your function to repack the tuple. The standard way to pass arguments as a tuple is the following:
from numpy import sqrt # leave this outside the function
from scipy.optimize import fsolve
# here it is V
def terminalV(Vt, *data):
ro_p, ro, D_p, mi, g = data # automatic unpacking, no need for the 'i for i'
return sqrt((4*g*(ro_p - ro)*D_p)/(3*C_d(Re(data, Vt))*ro)) - Vt
data = (1800, 994.6, 0.208e-3, 8.931e-4, 9.80665)
Vt0 = 1
Vt = fsolve(terminalV, Vt0, args=data)
Without fsolve, i.e., if you just want to call terminalV on its own, for example if you want to see its value at Vt0, then you must unpack data with a star:
data = (1800, 994.6, 0.208e-3, 8.931e-4, 9.80665)
Vt0 = 1
terminalV(Vt0, *data)
Or pass the values individually:
terminalV(Vt0, 1800, 994.6, 0.208e-3, 8.931e-4, 9.80665)

Like so:
Vt = fsolve(terminalV, Vt0, args=[data])

Related

Pass function that takes kwargs to solve_ivp

For a class, I am using solve_ivp to solve a differential equation function that I customize using keyword arguments. Here is my differential equation function integrate and the function sineCurrent I pass to it to help generate the differential equation. I already know that calling sineCurrent within integrate works. My issue arises when I try to pass integrate and by extension sineCurrent to solve_ivp. My class requires that they all remain separate functions.
def integrate(time,**kwargs):
func = kwargs['current']
V = kwargs['voltage']
Cm = 0.2 # membrane capacitance in nF
R = 100 # membrane resistance in mega-ohms
V_rest = -60 # resting membrane voltage in mV
I = func(time,**kwargs) # passing arguments current input function
s = I*R
dVdt = (V_rest-V+s)/(Cm*R)
return dVdt
def sineCurrent(time,**kwargs):
# Passing frequency and current arguments, setting defaults to f=0.5 kHz and I=2 nA
defaultKwargs = { 'freq': float("0.5"), 'Imax': float("2"),}
kwargs = { **defaultKwargs, **kwargs }
freq = kwargs['freq']
Imax = kwargs['Imax']
# Error message if time vector not included
if len(time) == 0:
return "sineCurrent requires at least one input parameter, time"
# Calculating sineCurrent
I = Imax*np.sin(2*pi*freq*time)
return I
I tried several methods to pass integrate to solve_ivp. First, I tried using the basicsolve_ivp syntax.
t = np.arange(-10,100,0.1)
V_init= np.array([50])
soln = solve_ivp(integrate,t,current=sineCurrent,[t[0], t[-1]],V_init)
This yielded the error SyntaxError: positional argument follows keyword argument. Next I tried using a lambda function.
t = np.arange(-10,100,0.1)
V_init= np.array([50])
soln = solve_ivp(integrate=lambda t,current=sineCurrent: [t[0], t[-1]],V_init)
This led to the same error message as before. So I moved the keyword arguments to the end.
t = np.arange(-10,100,0.1)
V_init= np.array([50])
soln = solve_ivp(integrate,[t[0], t[-1]],V_init,args=(t,current=sineCurrent,voltage=V_init))
This led to the error message SyntaxError: invalid syntax. I am at a loss as to what to try next. I know that my integrate function can produce an np.ndarray when arguments are passed to it properly, shown below, but I cannot figure out how to pass integrate to solve_ivp.
dVdt = integrate(t,current=sineCurrent,voltage=V_init)

Input arguments for MATLAB Engine function

I'm trying to use MATLAB engine to call a MATLAB function in Python, but I'm having some problems. After manage to deal with NumPy arrays as input in the function, now I have some error from MATLAB:
MatlabExecutionError: Undefined function 'simple_test' for input
arguments of type 'int64'.
My Python code is:
import numpy as np
import matlab
import matlab.engine
eng = matlab.engine.start_matlab()
eng.cd()
Nn = 30
x= 250*np.ones((1,Nn))
y= 100*np.ones((1,Nn))
z = 32
xx = matlab.double(x.tolist())
yy = matlab.double(y.tolist())
Output = eng.simple_test(xx,yy,z,nargout=4)
A = np.array(Output[0]).astype(float)
B = np.array(Output[1]).astype(float)
C = np.array(Output[2]).astype(float)
D = np.array(Output[3]).astype(float)
and the Matlab function is:
function [A,B,C,D] = simple_test(x,y,z)
A = 3*x+2*y;
B = x*ones(length(x),length(x));
C = ones(z);
D = x*y';
end
Is a very simple example but I'm not able to run it!
I know the problem is in the z variable, because when I define z=32 the error is the one I mentioned, and when I change for z=32. the error changes to
MatlabExecutionError: Undefined function 'simple_test' for input
arguments of type 'double'.
but I don't know how to define z.

fixed point iteration over list (colebrook)

I need to calculate the friction factor using the Colebrook-Equation, but unlike most cases it needs to be over a length, so the Reynolds number is a list, not a float.
I basically used what this guy this guy recommended (fixed point iteration), specially because it will be part of a bigger programm which wasn't very fast when I did it in Matlab.
For my interpretation of that code I oriented myself at the example of the fixed_point documentation, because there they also pass arguments in form of lists into the function (or actually the solver). v is a list with 1000 elements.
k = 0.000045
d = 0.03
reynolds = d/10**(-6)*v
def f(x, Re):
LHS = -2*np.log10((2.51/(Re*np.sqrt(x))) + (k/(3.71*d)))
return 1/LHS**2
x0 = [0.02]*1000
Re = np.array(reynolds)
result = fixed_point(f,x0, args = Re)
print(result)
I made sure that the starting value and the Reynolds-argument have the same length and I still get an error message.
File " ... ", line 72, in <module>
result = fixed_point(f,x0, args = Re)
...
TypeError: f() takes 2 positional arguments but 1001 were given
I feel like I'm missing something fundamentally here although I'm sitting on it longer than this problem should be taking me.
Thanks for you help,

Passing extra arguments to broyden1

Im trying to execute scipy broyden1 function with extra parameters (called "data" in the example), here is the code:
data = [radar_wavelen, satpos, satvel, ellipsoid_semimajor_axis, ellipsoid_semiminor_axis, srange]
target_xyz = broyden1(Pixlinexyx_2Bsolved, start_xyz, args=data)
def Pixlinexyx_2Bsolved(target, *data):
radar_wavelen, satpos, satvel, ellipsoid_semimajor_axis, ellipsoid_semiminor_axis, srange = data
print target
print radar_wavelen, satpos, satvel, ellipsoid_semimajor_axis, ellipsoid_semiminor_axis, srange
Pixlinexyx_2Bsolved is the function whose root I want to find.
start_xyz is initial guess of the solution:
start_xyz = [4543557.208584103, 1097477.4119051248, 4176990.636060918]
And data is this list containing a lot of numbers, that will be used inside the Pixlinexyx_2Bsolved function:
data = [0.056666, [5147114.2523595653, 1584731.770061729, 4715875.3525346108], [5162.8213179936156, -365.24378919717839, -5497.6237250296626], 6378144.0430000005, 6356758.789000001, 850681.12442702544]
When I call the function broyden1 (as in the second line of example code) I get the next error:
target_xyz = broyden1(Pixlinexyx_2Bsolved, start_xyz, args=data)
File "<string>", line 5, in broyden1
TypeError: __init__() got an unexpected keyword argument 'args'
What I'm doing wrong?
Now, seeing the documentation of fsolve, it seems to be able to get extra args in the callable func... Here is a similar question as mine.
There is a similar question at scipy's issue-tracker including a solution using python's functools-module (here: PEP 309 -- Partial Function Application
).
Small example based on the above link and the original problem from the docs:
import numpy as np
import scipy.optimize
""" No external data """
def F(x):
return np.cos(x) + x[::-1] - [1, 2, 3, 4]
x = scipy.optimize.broyden1(F, [1,1,1,1], f_tol=1e-14)
print(x)
""" External data """
from functools import partial
def G(data, x):
return np.cos(x) + x[::-1] - data
data = [1,2,3,4]
G_partial = partial(G, data)
x = scipy.optimize.broyden1(G_partial, [1,1,1,1], f_tol=1e-14)
print(x)
Out
[ 4.04674914 3.91158389 2.71791677 1.61756251]
[ 4.04674914 3.91158389 2.71791677 1.61756251]

solving non linear problems in python

in last equation i need to solve for q. Here is the problem from miranda feckler , I need to develop equivalent python code If my function is based on many variables and i need to solve non linear root finding problem for only one variable then how will i write-
when i write all the three variable, I get following error
TypeError: 'numpy.ndarray' object is not callable
and when i write only one of variables-
i get error-
TypeError: resid() missing 2 required positional arguments: 'p' and 'phi'
can anyone tell me my mistake and a better code for this.
broyden1(resid(co, p_node, q), co)
breaks because the term resid(co, p_node, q) gets evaluated (returning an array) before passing into the function.
broyden1(resid, co)
breaks because when broyden1 evaluates it calls resid(co) which is clearly not well defined. You want to be able to pass the initial guess as a single object (e.g. a tuple) in broyden1, so a simple solution is to just redefine resid to take in a tuple instead of three sepearate arguments, like so:
def resid(arg):
c,p,phi = arg
return p + (phi * c) * ((-1 / eta) * (p ** (eta + 1))) \
- alpha * (np.sqrt(np.abs(phi * c))) - (phi * c) ** 2
c1 = scipy.optimize.broyden1(resid, (co, p_node, q))

Categories