簡単に使用できるように、GUI を使用してより視覚的なプログラムに必要なコマンド ライン コードを転送しようとしています。元のコードは C++ であったため、Visual Studio Express 2012 で利用可能な Visual C++ を
使用していますが、オブジェクトを処理する "新しい"
マネージ C++/CLI の方法を理解するのに問題があります。CLI とマネージ C++ を初めて使用するので
、誰かが私が間違っていることと、なぜそれが機能しないのかを説明できるかどうか疑問に思っていました。ここ
で、コードと問題について説明します。
このプログラムは本質的に最適化プログラムです。
- システムには複数のボックス (モード) があり、各モードには、そのタイプに応じて、その動作と外部
励起
への応答方法を制御するいくつかの数値係数があります。 - プログラムは、ボックスの数と各ボックスのタイプを指定するようにユーザーに求めます。
次に、システム応答と実験的に得られた応答との差を最小化する数値係数を見つけようとします。
そのため、UI には、ユーザーが実験結果ファイルを開き、
モードの数を指定し、各モードの種類を指定する手段があります。次に、ユーザーは
、バックグラウンドワーカーを開始する開始ボタンをクリックして処理機能を開始できます。
MSDNに示されている例に従って、作業を実行するクラスを作成しました。
ref class curveFit
{
public: ref class CurrentState{
public:
int percentage;
int iterationNo;
int stage;
bool done;
multimode systemModel;
};
public:
int modes;
int returncode;
array<double> ^expExcitations;
array<double> ^expResults;
multimode systemModel;
private:
void fcn(int, int, double*, double*, int*);
double totalError(std::vector<double> &);
public:
delegate void fcndelegate(int, int, double*, double*, int*);
public:
curveFit(void);
curveFit^ fit(System::ComponentModel::BackgroundWorker^, System::ComponentModel::DoWorkEventArgs^, Options^);
};
multimode
は単なるコンテナ クラス、つまりさまざまなボックスのリストです。
ref class multimode
{
private:
Collections::Generic::List<genericBoxModel ^>^ models;
int modes;
public:
multimode(void);
multimode(const multimode%);
int modeNo(void);
void Add(genericBoxModel^);
void Clear();
genericBoxModel^ operator[](int);
multimode% operator=(const multimode%);
double result(double);
bool isValid();
std::vector<double> MapData();
void MapData(std::vector<double> &);
};
multimode::multimode(void)
{
models = gcnew Collections::Generic::List<genericBoxModel ^>();
modes = 0;
}
multimode::multimode(const multimode% rhs)
{
models = gcnew Collections::Generic::List<genericBoxModel ^>();
for(int ind = 0; ind < rhs.modes; ind++)
models->Add(rhs.models[ind]);
modes = rhs.modes;
}
int multimode::modeNo(void)
{
return modes;
}
void multimode::Add(genericBoxModel^ model)
{
models->Add(model);
modes++;
}
void multimode::Clear()
{
models->Clear();
modes = 0;
}
genericBoxModel^ multimode::operator[](int ind)
{
return models[ind];
}
multimode% multimode::operator=(const multimode% rhs)
{
models->Clear();
for(int ind = 0; ind < rhs.modes; ind++)
models->Add(rhs.models[ind]);
modes = rhs.modes;
return *this;
}
double multimode::result(double excitation)
{
double temp = 0.0;
for(int ind = 0; ind < modes; ind++)
temp += models[ind]->result(excitation);
return temp;
}
bool multimode::isValid()
{
bool isvalid = true;
if(modes < 1)
return false;
for(int ind = 0; ind < modes; ind++)
isvalid = (isvalid && models[ind]->isValid());
return isvalid;
}
std::vector<double> multimode::fullMap()
{
//Map the model coefficients to a vector of doubles
...
}
void multimode::fullMap(std::vector<double> &data)
{
//Map a vector of doubles to the model coefficients
...
}
genericBoxModel
すべてのボックス モデルのベースとなる抽象クラスです。
このcurvefit::fit
関数は、渡されたオプションに基づいて最適化を行います。
curveFit^ curveFit::fit(System::ComponentModel::BackgroundWorker^ worker, System::ComponentModel::DoWorkEventArgs^ e, Options^ opts)
{
fcndelegate^ del = gcnew fcndelegate(this, &curveFit::fcn);
std::vector<double> data;
CurrentState^ state = gcnew CurrentState;
state->done = false;
state->stage = 0;
state->percentage = 0;
state->systemModel = systemModel;
worker->ReportProgress(state->percentage, state);
switch(opts->optimizationMethod)
{
case 0:
while(iterationNo < maxIterations)
{
data = systemModel.MapData();
OptimizationMethod0::step(some_parameters, data, (optmethods::costfunction)Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(del).ToPointer());
systemModel.MapData(data);
iterationNo++;
state->percentage = 0;
state->systemModel = systemModel;
worker->ReportProgress(state->percentage, state);
}
...
}
}
最新のステップの結果を画面に表示できるように、状態内でシステムモデルを渡して
いますが、これは機能しませんが、それは別の問題です:-)
開始ボタンはcurvefit::fit
、システム モデルを初期化した後に関数を呼び出します。
private: System::Void btnStart_Click(System::Object^ sender, System::EventArgs^ e) {
systemModel.Clear();
for(int mode = 0; mode < modes; mode++)
{
switch(model)
{
case 0:
systemModel.Add(gcnew model0);
systemModel[mode]->coefficients[0] = 100.0 / double(mode + 1);
...
break;
...
}
}
btnStart->Enabled = false;
stStatusText->Text = "Calculating!";
Application::UseWaitCursor = true;
curveFit^ cf = gcnew curveFit;
fitCurve->RunWorkerAsync(cf);
}
private: System::Void fitCurve_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) {
System::ComponentModel::BackgroundWorker^ worker;
worker = dynamic_cast<System::ComponentModel::BackgroundWorker^>(sender);
curveFit^ cf = safe_cast<curveFit^>(e->Argument);
cf->expExcitations = gcnew array<double>(expExcitations.Count);
expExcitations.CopyTo(cf->expExcitations);
cf->expResults = gcnew array<double>(expResults.Count);
expResults.CopyTo(cf->expResults);
cf->systemModel = systemModel;
cf->modes = modes;
e->Result = cf->fit(worker, e, options);
}
これは完璧に機能します!しかし、最適化プロセスをより速く、より
成功させるために、以前の最適化の結果を次の実行の最初の推測として使用したかった
(可能であれば):
multimode oldmodel(systemModel);
systemModel.Clear();
for(int mode = 0; mode < modes; mode++)
{
switch(model)
{
case 0:
if(mode < oldmodel.modeNo() && oldmodel.isValid() && (oldmodel[mode]->model == 0))
systemModel.Add(oldmodel[mode]);
else
{
systemModel.Add(gcnew model0);
systemModel[mode]->coefficients[0] = 100.0 / double(mode + 1);
...
}
break;
...
さて、私の問題は、この変更後、メッセージが正しく渡されないように見えることです
。最初に開始ボタンをクリックすると、すべてが正常に機能します
が、それ以降、ステートメントsystemModel.Add(oldmodel[mode]);
が実行された場合、
結果はそのままです最初の推測と同じで、fit
関数が呼び出された後に更新されません。
では、なぜこれらの 2 行 (Add(oldmodel[mode])
とAdd(gcnew model0)
)で
このように異なる結果が得られるのでしょうか?