python - Replacing complex-valued arguments of generic function object by two real-valued arguments -
curve fitting tools such in scipy tend assume parameters of model functions real-valued. when fitting model depends on complex-valued parameters complex-valued data set, 1 therefore first has create version of model, in each complex parameter replaced 2 real ones.
first, simple example:
# original function, representing model a,b may complex-valued def f(x, a, b): return a+b*x # modified function, complex parameters have been replaced 2 real ones def f_r(x, a_r, a_i, b_r, b_i): return f(x, a_r + 1j*a_i, b_r+1j*b_i) print( f(1,2+3j,4+5j) == f_r(1,2,3,4,5) )
note: output of model still complex-valued, can taken care of appropriately defining residual function.
now, instead of having write new code every function f
, have "function factory" pass function object f
list of booleans is_complex
specifying arguments of f
assumed complex-valued (and therefore need replaced 2 real-valued arguments). list of booleans e.g. inferred initial values provided f
.
i new kind of problem, looked around on web , came across decorator module. before going generic case, here example above using functionmaker
class:
import decorator def f(x, a, b): return a+b*x f_r = decorator.functionmaker.create( 'f_r(x, a_r, a_i, b_r, b_i)', 'return f(x, a_r + 1j*a_i, b_r + 1j*b_i)', dict(f=f))
for generic case, 1 can imagine synthesize 2 strings passed function maker:
import decorator import inspect def f(x, a, b): return a+b*x def fmaker(f,is_complex): argspec = inspect.getargspec(f) args = argspec.args[:] fname = f.func_name s1 = "{}_r(".format(fname) s2 = "return f(" arg, cplx in zip(args, is_complex): if not cplx: s1 += "{},".format(arg) s2 += "{},".format(arg) else: s1 += "{}_r,".format(arg) s1 += "{}_i,".format(arg) s2 += "{}_r+1j*{}_i,".format(arg,arg) s1 += ')' s2 += ')' return decorator.functionmaker.create(s1,s2,dict(f=f)) is_complex = [false, true, true] f_r = fmaker(f,is_complex) # prints argspec(args=['x', 'a_r', 'a_i', 'b_r', 'b_i'], varargs=none, keywords=none, defaults=()) print(inspect.getargspec(f_r)) print( f(1,2+3j,4+5j) == f_r(1,2,3,4,5) )
this seems solve problem.
my question is: reasonable way of doing this? there better/simpler ways in python?
p.s. not computer scientist, if using technical terms incorrectly, please feel free revise.
you not have nasty string based generation, can use basic function closures create wrapper:
def complex_unroll(f, are_complex): # function have access are_complex , f through python closure # *args give access parameters list def g(*args, **kwargs): # new_args stores new list of parameters, complex ones new_args = [] # arg_id iterator used keep track in original list arg_id = 0 is_complex in are_complex: if is_complex: # if request complex unroll, merge 2 consequtive params new_args.append(args[arg_id] + 1j*args[arg_id+1]) # , move iterator 2 slots arg_id += 2 else: # otherwise, copy argument new_args.append(args[arg_id]) arg_id += 1 # return call original function f new args return f(*new_args, **kwargs) # our unroll function returns newly designed function g return g
and now
def f(x, a, b): return a+b*x def f_r(x, a_r, a_i, b_r, b_i): return f(x, a_r + 1j*a_i, b_r+1j*b_i) f_u = complex_unroll(f, [false, true, true]) print f(1,2+3j,4+5j) print f_r(1,2,3,4,5) print f_u(1,2,3,4,5) f_u2 = complex_unroll(f, [true, true, true]) print f_u2(1,0,2,3,4,5)
works desired.
why prefer path compared proposed 1 in question?
- it not use any additional modules/libraries, basic mechanism of python's dealing arguments , closures. in particular solution reflection, analyzes defined function, quite complex operation compared try obtain.
- it handles named arguments fine, if have
f(x, a, b, flag)
, can still useg = complex_unroll(f, [false, true, true])
, callg(0, 0, 0, 0, 0, flag = true)
, fail in code. add support this, though.
Comments
Post a Comment