Passing extra arguments to broyden1 - python

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]

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.

Passing string to Fortran DLL using ctypes and Python

I am trying to load a DLL in Python 2.7 using ctypes. The DLL was written using Fortran and has multiple subroutines in it. I was able to successfully set up couple of the exported functions that that long and double pointers as arguments.
import ctypes as C
import numpy as np
dll = C.windll.LoadLibrary('C:\\Temp\\program.dll')
_cp_from_t = getattr(dll, "CP_FROM_T")
_cp_from_t.restype = C.c_double
_cp_from_t.argtypes = [C.POINTER(C.c_longdouble),
np.ctypeslib.ndpointer(C.c_longdouble)]
# Mixture Rgas function
_mix_r = getattr(dll, "MIX_R")
_mix_r.restype = C.c_double
_mix_r.argtypes = [np.ctypeslib.ndpointer(dtype=C.c_longdouble)]
def cp_from_t(composition, temp):
""" Calculates Cp in BTU/lb/R given a fuel composition and temperature.
:param composition: numpy array containing fuel composition
:param temp: temperature of fuel
:return: Cp
:rtype : float
"""
return _cp_from_t(C.byref(C.c_double(temp)), composition)
def mix_r(composition):
"""Return the gas constant for a given composition.
:rtype : float
:param composition: numpy array containing fuel composition
"""
return _mix_r(composition)
# At this point, I can just pass a numpy array as the composition and I can get the
# calculated values without a problem
comps = np.array([0, 0, 12.0, 23.0, 33.0, 10, 5.0])
temp = 900.0
cp = cp_from_t(comps, temp)
rgas = mix_r(comps)
So far, so good.
The problem arises when I try another subroutine called Function2 which needs some strings as input. The strings are all fixed length (255) and they also ask for the length of each of the string parameters.
The function is implemented in Fortran as follows:
Subroutine FUNCTION2(localBasePath,localTempPath,InputFileName,Model,DataArray,ErrCode)
!DEC$ ATTRIBUTES STDCALL,REFERENCE, ALIAS:'FUNCTION2',DLLEXPORT :: FUNCTION2
Implicit None
Character *255 localBasePath,localTempPath,InputFileName
Integer *4 Model(20), ErrCode(20)
Real *8 DataArray(900)
The function prototype in Python is set up as follows
function2 = getattr(dll, 'FUNCTION2')
function2.argtypes = [C.POINTER(C.c_char_p), C.c_long,
C.POINTER(C.c_char_p), C.c_long,
C.POINTER(C.c_char_p), C.c_long,
np.ctypeslib.ndpointer(C.c_long , flags='F_CONTIGUOUS'),
np.ctypeslib.ndpointer(C.c_double, flags='F_CONTIGUOUS'),
np.ctypeslib.ndpointer(C.c_long, flags='F_CONTIGUOUS')]
And I call it using:
base_path = "D:\\Users\\xxxxxxx\\Documents\\xxxxx\\".ljust(255)
temp_path = "D:\\Users\\xxxxxxx\\Documents\\xxxxx\\temp".ljust(255)
inp_file = "inp.txt".ljust(255)
function2(C.byref(C.c_char_p(base_path)),
C.c_long(len(base_path)),
C.byref(C.c_char_p(temp_dir)),
C.c_long(len(temp_dir))),
C.byref(C.c_char_p(inp_file)),
C.c_long(len(inp_file)),
model_array,
data_array,
error_array)
The strings are essentially paths. The function Function2 does not recognize the paths and proves an error message with some non-readable characters at the end, such as:
forrtl: severe (43): file name specification error, unit 16, D:\Users\xxxxxxx\Documents\xxxxx\ωa.
What I wanted the function to receive was D:\Users\xxxxxxx\Documents\xxxxx\. Obviously, the strings are not passed correctly.
I have read that Python uses NULL terminated strings. Can that be a problem while passing strings to a Fortran dll? If so, how do I get around it?
Any recommendations?
Following comment from #eryksun, I made the following changes to make it work.
Changed the argtypes to:
function2 = getattr(dll, 'FUNCTION2')
function2.argtypes = [C.c_char_p, C.c_long,
C.c_char_p, C.c_long,
C.c_char_p, C.c_long,
np.ctypeslib.ndpointer(C.c_long , flags='F_CONTIGUOUS'),
np.ctypeslib.ndpointer(C.c_double, flags='F_CONTIGUOUS'),
np.ctypeslib.ndpointer(C.c_long, flags='F_CONTIGUOUS')]
And instead of passing the string as byref, I changed it to the following.
base_path = "D:\\Users\\xxxxxxx\\Documents\\xxxxx\\".ljust(255)
temp_path = "D:\\Users\\xxxxxxx\\Documents\\xxxxx\\temp".ljust(255)
inp_file = "inp.txt".ljust(255)
function2(base_path, len(base_path), temp_dir, len(temp_dir), inp_file, len(inp_file),
model_array, data_array, error_array)
It was sufficient to pass the values directly.

Passing arguments to fsolve

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])

Using Theano.scan with shared variables

I want to calculate the sumproduct of two arrays in Theano. Both arrays are declared as shared variables and are the result of prior computations. Reading the tutorial, I found out how to use scan to compute what I want using 'normal' tensor arrays, but when I tried to adapt the code to shared arrays I got the error message TypeError: function() takes at least 1 argument (1 given). (See minimal running code example below)
Where is the mistake in my code? Where is my misconception? I am also open to a different approach for solving my problem.
Generally I would prefer a version which takes the shared variables directly, because in my understanding, converting the arrays first back to Numpy arrays and than again passing them to Theano, would be wasteful.
Error message producing sumproduct code using shared variables:
import theano
import theano.tensor as T
import numpy
a1 = [1,2,4]
a2 = [3,4,5]
Ta1_shared = theano.shared(numpy.array(a1))
Ta2_shared = theano.shared(numpy.array(a2))
outputs_info = T.as_tensor_variable(numpy.asarray(0, 'float64'))
Tsumprod_result, updates = theano.scan(fn=lambda Ta1_shared, Ta2_shared, prior_value:
prior_value + Ta1_shared * Ta2_shared,
outputs_info=outputs_info,
sequences=[Ta1_shared, Ta2_shared])
Tsumprod_result = Tsumprod_result[-1]
Tsumprod = theano.function(outputs=Tsumprod_result)
print Tsumprod()
Error message:
TypeError: function() takes at least 1 argument (1 given)
Working sumproduct code using non-shared variables:
import theano
import theano.tensor as T
import numpy
a1 = [1, 2, 4]
a2 = [3, 4, 5]
Ta1 = theano.tensor.vector("a1")
Ta2 = theano.tensor.vector("coefficients")
outputs_info = T.as_tensor_variable(numpy.asarray(0, 'float64'))
Tsumprod_result, updates = theano.scan(fn=lambda Ta1, Ta2, prior_value:
prior_value + Ta1 * Ta2,
outputs_info=outputs_info,
sequences=[Ta1, Ta2])
Tsumprod_result = Tsumprod_result[-1]
Tsumprod = theano.function(inputs=[Ta1, Ta2], outputs=Tsumprod_result)
print Tsumprod(a1, a2)
You need to change the compilation line to this one:
Tsumprod = theano.function([], outputs=Tsumprod_result)
theano.function() always need a list of inputs. If the function take 0 input, like in this case, you need to give an empty list for the inputs.

Categories