名称
wnnlp — 制約つき非線形最適化パッケージ
文法
#include “wnnlp.h”
#define WN_EQ_COMPARISON 1
#define WN_GT_COMPARISON 2
#define WN_LT_COMPARISON 3
typedef struct wn_linear_constraint_type_struct
{
…
int size;
int *vars;
int comparison_type;
double *weights;
double rhs;
} *wn_linear_constraint_type;
typedef struct wn_nonlinear_constraint_type_struct
{
…
int size;
int *vars;
int comparison_type;
double offset;
ptr client_data;
double (*pfunction)(int size,double *values,ptr client_data);
void (*pgradient)(double *grad,int size,double *values,ptr client_data);
} *wn_nonlinear_constraint_type;
void wn_nlp_conj_method
(
int &code,
double &val_min,
double solution_vect[],
double delta_vect[],
wn_nonlinear_constraint_type objective,
wn_sll constraint_list,
int num_vars,
int conj_iterations,
int offset_iterations,
double offset_adjust_rate
)
void wn_make_linear_constraint
(
wn_linear_constraint_type &constraint,
int size,double rhs,int comparison_type
)
void wn_make_nonlinear_constraint
(
wn_nonlinear_constraint_type &constraint,
int size,int comparison_type
)
extern int wn_nlp_verbose;
説明
このパッケージは一般制約を条件として多変数に関する一般関数の最適化に役に立ちます。最小にする関数と制約条件は、線形かまたは非線形です。非線型の場合は、連続していてかつ微分可能でなくてはなりません。制約条件は、等式かまたは不等式が可能です。
最適化アルゴリズムはおよそ以下の通りです。制約条件と目的関数は、制約なしマスタ目的関数を作成するために結合されます。制約条件は、違反される各制約条件のためのマスタ目的関数に二次のペナルティ関数を追加することによって、扱われます。マスタ目的関数は、共役勾配山登りアルゴリズムを使用することで最適化されています(wnconjを見てく
ださい)。結果として起こるソリューションはたぶん制約条件のいくつかに違反します。違反された制約条件を条件に合うようにソリューションを押すため、違反された制約条件にオフセットが追加されます。共役勾配手続きは繰り返されます。この全体のサイクルは収束するまで繰り返されます。
すべてがうまく行くなら、アルゴリズムは局所的な最小値に一点に集まります。この局所的最小値は、大域的な最小値であるとは限らず、また良い局所的最小値とも限りません。目的関数か制約関数のどちらかが微分可能でないなら、アルゴリズムはまさしくそのサブ最適解でしばしば立ち往生します、そして、この場合は警告はされません。
目的関数と制約関数の適切なスケーリングは、アルゴリズムの速くて正確な収束を確実にするために重要です。関数と変数がスケーリングされるべきであるので、ソリューションの領域のすべての変数に関するすべての関数の偏微分がほぼ+1と-1にあります。いつも非常に大きいか、またはいつも非常に小さい部分は、この領域にあるようにスケーリング
されるべきです。
「wn_nlp_conj_method」は制約つき非線型最小化問題を解決します。「objective」は最小にするための線形か非線形目的関数を指定します。「constrain_list」は線形、そして非線形制約条件の単独で繋がっているリストです。「objective」と一緒に「constrain_list」は解決すべき課題を包括します。タイプ「wn_linear_constrain_type」と「wn_nonlinear_constrain_type」のオブジェクトでそれらを構成しなければなりません。「num_vars」は問題の変数の数です。「objective」はwn_linear_constrain_type型であることができます。その場合はキャストを使用してlint警告を避けてください。
「solution_vect」は呼び出し側がソリューションに可能な限り近いベクトルを含むように初期化するべきであるベクトルです。終了の後に、「solution_vect」はソリューションを含みます。「val_min」は「solution_vect」に関する目的関数の値に設定されます。
「code」は実行のリターンステータスを含んでいます。「code」の潜在値に関する詳細に関しては「診断」というセクションを見てください。
「conj_iterations」は共役勾配アルゴリズムでの繰返し数を指定します。それを小さくし過ぎると、一貫性のない行動と非収束がもたらされます。それを大きくし過ぎると、CPU時間が浪費されます。しばしば「len」と同じ値に「conj_iterations」を設定するとうまくいきます。「offset_iterations」は制約条件オフセットを調整するために繰返し数を指定します。しばしば、10や20のような数はうまくいきます。「offset_adjust_rate」は、制約条件オフセットがどれくらい積極的に調整されるかを指定します。1は最も多く積極的に、0が調整を全く意味しないということです。「offset_adjust_rate」は(0,1]の範囲に常にあるべきです。線形問題やまたは非線形でも「穏やかに」非線形の問題では1は通常うまくいきます。大きすぎる値を使用すると不安定な非収束をもたらします。あまりに小さな値を使用すると、CPU時間を浪費します。「delta_vect」は呼び出し側が非線型制約条件のすべてのためのシンボリックな勾配を提供しない場合に、数値勾配を計算するためのデルタ値のベクトルです。すべての非線型制約条件にシンボリックな勾配があるなら、「delta_vect」をNULLに設定してください。数値微分関数はあなたのマシンのdouble型の数値精度の中で区別可能であるように、「delta_vect」に割り当てる値は十分大きくすべきです。が、微分中に高次のテーラー展開項が引き起こすエラーを最小にするため、限りなく小さくすべきです。
「wn_make_linear_constraint」は以下の形式の線形制約か目的関数を作ります。
weights[0]*solution_vector[vars[0]]+
weights[1]*solution_vector[vars[1]]+
…
weights[size-1]*solution_vector[vars[size-1]] 「rhs」
「size」は制約条件にかかわる変数の数を指定します。「rhs」は制約条件右手側です。制約条件が目的関数として使用されるなら「rhs」は無視されます。「comparison_type」はWN_EQ_COMPARISON、WN_GT_COMPARISON、またはWN_LT_COMPARISONのうちのひとつでなければなりません。目的関数が作成された場合は「comparison_type」の値はWN_EQ_COMPARISONでなければなりません。線形制約を作成した後に、呼び出し側は線形制約のベクトル「weights」と「vars」を設定しなければなりません。「vars」はサイズ「size」の整数アレイです。各エントリは制約条件に含まれている変数のインデックスを指定します。「weights」はサイズ「size」の2倍のアレイです。各エントリは「vars」の対応する変数に係数を指定します。
「wn_make_nonlinear_constraint」が非線型制約条件か目的を作ります。「size」は制約条件にかかわる変数の数を指定します。「comparison_type」はWN_EQ_COMPARISON、WN_GT_COMPARISON、WN_LT_COMPARISONのうちのひとつでなければなりません。目的関数が作成された場合は「comparison_type」の値はWN_EQ_COMPARISONでなければなりません。非線型制約条件を作成した後は、呼び出し側は「vars」、”pfunction”、ことによると”pgradient”、および「client_data」を設定しなければなりません。
「vars」はサイズ「size」の整数アレイです。各エントリは制約条件に含まれている変数のインデックスを指定します。”pfunction”は「vars」における、変数の非線型関数へのポインタです。実装された制約条件は以下のどれかです。
(*pfunction)(…) >= 0
(*pfunction)(…) < = 0
(*pfunction)(…) == 0
これは「comparison_type」の値に依存します。
“pgradient”は”pfunction”の勾配を「grad」に置きます。”pfunction”の勾配を理解するのに煩わされたくないなら、”pgradient”を設定しないでください。ドメイン値を「delta_vect」の量だけ差分をとるため”pfunction”を呼び出すことで、勾配は数値的に計算されます。これは遅い場合があります。「client_data」は制約条件で関連するべきユーザによって定義されたデータへのポインタです。
「wn_nlp_verbose」は「wn_nlp_conj_method」で出力する状態情報の量を制御します。0は全く出力しません。1はいくらか出力します。2はさらに出力します。 3は最も出力します。
収束問題
あなたが、最適化機構によって見つけられたソリューションが制約的なスペースの外にあるのを観測しているなら、目的関数を変更するのが必要であるかもしれません。基本的な考え方が目的関数を変更することであるので、それは碁の無限制約的な領域のどんな外部もしません。この変更は、2次ペナルティ関数であることを容認します。(各制約条件のために、加えられます) その結果、効くのに、最適化機構が制約的な領域の中でソリューションを見つけるので、それは、より簡単になります。
変更された目的関数は以下の性質を満たさなければなりません:
1) 変更された目的関数は制約的な領域の外の無限まで行きません。
2) 変更された目的関数は制約的な領域の境界で連続しています。
3) 変更された目的のスロープは制約的な領域の境界で連続しています。
目的関数が制約的な領域の境界に値+無限を持っているとき、この規則へのただ一つの例外は起こります。
例えば、F(x)がxがある目的関数であると仮定してください、> 0 制約条件とあなたが新しいFnew(x)を作成したがっているのに従って、それはF(x)よりよく一点に集まります。 x=0でs0がF(x)(dF/dx)のスロープの値であることをさせてください。そして、
定義
Fnew(x) = (x > 0.0) ? F(x) : ((x > -1.0) ? (s0*x) : (-s0))
は、あなたの収束問題を改良するかもしれません。
乗法、分割、log()、pow()を使用する複雑な目的関数によって、制約条件限界におけるこれらの演算子と関数の振る舞いの分析が収束を改良するのに必要であるかもしれません。以下に、これらの演算子と関数がどうそれらへの収束問題を避けるのを助けることができる収束問題と変更を引き起こす場合があるかに関するいくつかの簡単な例があります。
除算演算子の例
目的関数を
F(x, ….) = … + 1/x
とする。制約は、
x > 0
「1/x」項がFにある収束問題はxが反対側から0に近づくのに応じてFが無限まで行くということです。 xがプラス面から0に近づくのに応じてFが+ 無限まで行くのを観測してください。したがって、私たちは1/xを以下の関数に取り替えるべきです。
double my_reciprical(double x)
{
if(x > 0.0)
{
/* 強制的な領域の中で */
return(1.0/x);
}
else
{
/* 強制的な領域の外で: マッチ
強制的な領域の境界の関数値 */
return(FLT_MAX);
}
}
乗算演算子の例
目的関数は
F(x, y, ….) = …. + x*y
とする。制約は
x > 0
y > 0
とする。収束問題は以下の通りです。
1) いつ、x> 0とy手法無限、Fは無限まで行きます。
2) いつ、y> 0とx手法無限、Fは無限まで行きます。
乗算結果がマイナス無限まで行くのを防ぐために、そして、境界における関数値とスロープを保存するために、私たちは乗算関数を本関数に取り替えます。
double my_multiply(double x,double y)
{
double prod;
prod = x*y;
/* この判断命令は外で実施します。
領域だけを抑制して、制約的な領域の境界における
目的関数値とスロープを変わりがないままにします。*/
if(!(prod >= -1.0))
{
prod = -1.0;
}
return(prod);
}
除算演算子の例
目的関数を
F(x, y, …) = …. + x/y
とする。制約を
x > 0
y > 0
「x/y」項がある2収束冊がFにあります:
1) そして、yがプラス面から0に近づくのに従って、Fは+ 無限まで行きます(x>0であるなら)。
2) いつ、y> 無限Fが無限まで行かせる0とx手法
注意してください
x/y = x*(1/y)
私たちが分割に取って代わる上記の例を使用できるように
double my_divide(double x,double y)
{
return(my_multiply(x,my_reciprical(y)))
}
LOG関数の例
目的関数を
F(x,….) = …. + log(x)
とし、制約を
x > 1
とするとき、収束問題は以下の通りです。
1) log関数はx< =0のために定義されません。
2) xがプラス面のために0に近づくとき、Fは無限まで行きます。
制約条件xの限界=1において、log(x)が0とdlog(x)/d x=1と等しいのを観測してください。私たちがlog(x)を本関数に置き換えることによって、より良い収束性Fを作成できるように
double my_log(double x)
{
if(x > 1.0)
{
return(log(x))
}
else if(x > 0.0)
{
/* 関数値にマッチしてください、そして、境界で坂になってください。
制約的な領域について */
return(1.0*(x-1.0));
}
else
{
/* x=0で関数値にマッチしなさい、ただし、無限まで行くのを止めてください。*/
return(-1.0);
}
}
POW関数例
目的関数を
F(x,y,….) = …. + pow(x,y)
とし、制約を
x > 0
y > 0
とすると、収束問題は以下の通りです。
1. x<0、Fは虚数(むしろC言語では未定義)
2. y<0とx–>0、Fが+ 無限か-無限まで行くか、または未定義であるときに
したがって、私たちはpow(…)を本関数に取り替えます。
double my_pow(double x,double y)
{
if(x > 0.0)
{
return(pow(x,y));
}
else
{
/* 強制的な領域の境界で関数値にマッチしてください。
スロープがx=0で+ 無限と等しいので、スロープにマッチするのは
不可能です。
*/
return(0.0);
}
}
資源
wn_nlp_conj_method runs with
time = conj_iterations*offset_iterations*
stack memory = 1
dynamic memory = len
もちろん、この数字はおよそ時間の無は良い収束を達成するのが必要でないと言います。これは大いに解決されていることにおける問題、適切に「conj_iterations」、「offset_iterations」、および「offset_adjust_rate」を調整することにおけるユーザの技能、および問題の規模に依存します。
問題が合理的に振る舞って、よくスケーリングされるなら、1つは、通常、変数”len”の数の注文について何かに「conj_iterations」を設定して、「offset_iterations」を10や20のような何らかの数に設定できます。
作者は、ワークステーション時間の数時間後に1万の変数と5万の制約条件に関する非線型問題を解決するのにこのルーチンを使用し成功しました。また、このルーチンが大きい線形計画問題を解決できますが、数字の精度は、より専門化しているLPアルゴリズムのものほど良くはありません。
診断
コード=WN_SUCCESSは、最適条件が見つかり、正常終了したことを意味します。
コード=WN_SUBOPTIMALは、最適条件の前に終了したことを意味します。
コード=WN_UNBOUNDEDは、関数の制限がないことを意味します。
例題
「wnlib/acc/conjdir/examples.c.nlp」というファイルは、最小領域バッファリング問題を解決するために”wnnlp”という非線型制約つき最適化パッケージを使用します。以下に問題定式化があります。
問題の定式化:
選択せよ w[i] (1 < = i <= num_vars) 以下を最小化する目的で。
sum_over(i){ w[i] }
以下の制約条件を満足しながら。
for_all(i) { w[i] >= 0 }
sum_over(0< =i<=NUM_VARIABLE_BUFFERS){ w[i+1]/w[i] } <= MAX_DELAY
w[0] == DRIVER_BUFFER_SIZE
w[NUM_VARIABLE_BUFFERS+1] == RECEIVER_BUFFER_SIZE
バグ
他に参照すべき
wnconj, wnsplx, wnsll
著者
Will Naylor