2016-05-28 15 views
0

scipyなどの曲線適合ツールは、モデル関数のパラメータが実数であると想定する傾向があります。 複素数値のパラメータに依存するモデルを複素数値のデータセットにフィッティングする場合、最初にモデルのバージョンを作成しなければなりません。各モデルでは、各複素パラメータが2つの実数パラメータに置き換えられます。汎用関数オブジェクトの複素数値引数を2つの実数値引数で置き換える

まず、簡単な例:

# original function, representing a model where a,b may be complex-valued 
def f(x, a, b): 
    return a+b*x 

# modified function, complex parameters have been replaced by two 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)) 

注:モデルの出力は、依然として複素数値であるが、これは容易に適切残留関数を定義しての世話をすることができます。

さて、代わりにすべての機能fのための新しいコードを記述するので、私はfの引数がにあるブール値のリストis_complex指定と一緒に関数オブジェクトfを渡す「機能の工場」を持っていると思います(したがって、2つの実数引数で置き換える必要があります)。 このブール値のリストは、例えば次のようにすることができる。 fと共に提供される初期値から推定される。

私はこの種の問題に慣れていないので、ウェブ上を見て、decorator moduleを見つけました。一般的なケースに行く前に、ここでFunctionmakerクラスを使用して、上からの例です:

:一般的なケースでは

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

、一つは今の機能メーカに渡される2つの文字列を合成するために想像することができます

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(" 
    for 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)) 

これは問題を解決すると思われます。

私の質問です:これはこれを行う妥当な方法ですか?より良い/より簡単な方法は、Pythonでですか?

P.S.私はコンピュータ科学者ではないので、技術用語を誤って使用している場合は、自由に改訂してください。

答えて

0

あなたは単にラッパーを作成するための基本的な関数クロージャを使用することができ、任意の厄介な文字列ベースの生成を行う必要はありません。

def complex_unroll(f, are_complex): 

    # This function will have access to are_complex and f through python closure 
    # *args give us access to all parameters as a list 
    def g(*args, **kwargs): 
     # new_args stores new list of parameters, the complex ones 
     new_args = [] 
     # arg_id is iterator used to keep track where are we in the original list 
     arg_id = 0 
     for is_complex in are_complex:   
      if is_complex: 
       # if we request complex unroll, we merge two consequtive params 
       new_args.append(args[arg_id] + 1J*args[arg_id+1]) 
       # and move iterator 2 slots 
       arg_id += 2 
      else: 
       # otherwise, just copy the argument 
       new_args.append(args[arg_id]) 
       arg_id += 1 
     # finally we return a call to original function f with new args 
     return f(*new_args, **kwargs) 

    # our unroll function returns a newly designed function g 
    return g 

し、必要に応じて、今

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) 

作品。

なぜこのパスは、質問に提案されたものと比べて好きですか?

  • それは、どの追加のモジュール/ライブラリ、引数とクロージャとPythonの取引だけの非常に基本的なメカニズムを使用していません。特に、あなたのソリューションは反射を行い、定義された機能を分析します。これは、取得しようとしているものに比べてかなり複雑な操作です。
  • 名前付き引数はうまく処理されるため、f(x, a, b, flag)がある場合は、g = complex_unroll(f, [False, True, True])を使用してg(0, 0, 0, 0, 0, flag = True)を呼び出しても問題はありません。あなたはこれのためのサポートを追加することができます。
+0

ありがとうございます。このソリューションは本当にはるかに簡単です(後で表示されます)。あなたの関数 'f_u2'の評価のスピードについてコメントできますか(確かに、私の質問の一部ではありませんでした)?ここで私が見ることのできる唯一の不利な点は、関数f_u2が呼び出されるたびに新しい引数リストを構築するオーバーヘッドです(そして、最適化問題を解決するときに頻繁に呼び出されるかもしれません)。 –

+0

オーバーヘッドは、私とあなたの両方のソリューションの場合、ほぼ同じです。ジャンプするには、関数を呼び出す関数があります。 f_u2対f_uの場合、オーバヘッドはメモリ割り当てのみから来るので、非常に小さくする必要があります。これはf変数自体の内部での合理的な操作(ローカル変数の定義など)よりも小さくなければなりません。つまり、効率を求めているなら、Pythonではなく評価の中で単一のループのためにcythonを使うような、より大きな "咬合"を取ることができます。しかし、多くのパラメータがある場合、私の解決策は遅くなるかもしれません。 – lejlot