/// <summary>
 /// sin()関数の学習の例
 /// <para>NeuralNetworkクラスを直接利用して関数近似を行わせるサンプルです。</para>
 /// <para>
 /// 2011/12/25現在、0~1までの出力しか得られない。
 /// 出力関数と学習則を変更するか、関数近似時には値のマッピングが必要である。
 /// 他の人はどうやってんだろう?</para>
 /// </summary>
 public static void sin_learnig()
 {
     var para = new Parameter(1, 13, 1, 3, 0.01);            // ニューラルネットのパラメータ設定
     var NNforSin = new NeuralNetwork();                     // ニューラルネットクラスのインスタンス生成
     NNforSin.Setup(para);
     for (int i = 0; i < 500; i++)                           // 学習開始
     {
         Console.Write("\nnow i: " + i.ToString() + "@ ");
         Console.Write("ans = ");
         for (int k = 0; k < 15; k++)
         {
             double theta = (double)k / 15.0 * 2.0 * Math.PI;
             double ans = 0.0;
             for (int l = 0; l < 100; l++) ans = NNforSin.Learn(theta, Math.Sin(theta));       // 特徴ベクトルと教師ベクトルを渡す
             Console.Write(", " + ans.ToString("0.000"));
         }
     }
     NNforSin.Save("NN.ini");
     return;
 }
 /// <summary>
 /// XORの学習の例
 /// <para>NeuralNetworkクラスを直接操作して論理演算を実行する例です。</para>
 /// </summary>
 public static void XOR_learning()
 {
     var para = new Parameter(2, 5, 1, 1, 0.1);                          // ニューラルネットのパラメータ設定(中間層が多いと、学習係数を小さくとる必要がある)7層が限界・・
     var NNforXOR = new NeuralNetwork();                                 // ニューラルネットクラスのインスタンス生成
     NNforXOR.Setup(para);
     for (int i = 0; i < 100; i++)                                       // 学習開始
     {
         var ans = new double[4];
         Console.Write("now i: " + i.ToString() + "@ ");
         for (int k = 0; k < 100; k++)
         {
             ans[0] = NNforXOR.Learn(new double[2] { 0.0, 0.0 }, 0.0);   // 特徴ベクトルと教師ベクトルを渡す
             ans[1] = NNforXOR.Learn(new double[2] { 0.0, 1.0 }, 1.0);
             ans[2] = NNforXOR.Learn(new double[2] { 1.0, 0.0 }, 1.0);
             ans[3] = NNforXOR.Learn(new double[2] { 1.0, 1.0 }, 0.0);
         }
         Console.WriteLine("ans = " + ans[0].ToString("0.000") + ", " + ans[1].ToString("0.000") + ", " + ans[2].ToString("0.000") + ", " + ans[3].ToString("0.000") +
             ", 最後の学習結果→Error variation," + NNforXOR.VariationOfError.ToString("0.000") + ", Total Error," + NNforXOR.TotalOutputError.ToString("0.000"));
     }
     NNforXOR.Save("NN.ini");
     return;
 }
 /// <summary>
 /// 最小パラメータ構造体及び入力層と出力層のユニット数で初期化する
 /// </summary>
 /// <param name="minParameter">最小パラメータ</param>
 /// <param name="_numOfUnitInInputLayer">入力層のユニット数<para>特徴ベクトルの次元数と同じ数を指定して下さい。</para></param>
 /// <param name="_numOfUnitInOutputLayer">出力層のユニット数<para>この数は出力状態量と同じ数を指定して下さい。</para></param>
 public void Setup(Object minParameter, int _numOfUnitInInputLayer, int _numOfUnitInOutputLayer)
 {
     if (minParameter is MinParameter)
     {
         MinParameter _minPara = (MinParameter)minParameter;
         if (_minPara.NumOfHiddenLayer == 0) Console.WriteLine("中間層数が0でしたので中間層のユニット数は無視しました。");  // 実質無視されます。
         Parameter para = new Parameter(_minPara, _numOfUnitInInputLayer, _numOfUnitInOutputLayer);
         this.parameter = para;
         if (this.parameter.Error) throw new Exception("NeuralNetworkクラスのSetup()メソッドにおいてエラーがスローされました。設定パラメータの値が不正です。");
         this.Init();
         return;
     }
     else
         throw new Exception("不正なパラメータが渡されました。minParameterの型をチェックして下さい。");
 }
 /// <summary>
 /// パラメータ構造体で初期化する
 /// </summary>
 /// <param name="_parameter">ニューラルネットワーク構造を規定するパラメータ</param>
 public void Setup(Parameter _parameter)
 {
     this.parameter = _parameter;
     if (this.parameter.Error) throw new Exception("NeuralNetworkクラスのSetup()メソッドにおいてエラーがスローされました。設定パラメータの値が不正です。");
     this.Init();
     return;
 }
 /// <summary>
 /// ファイルを指定して結合係数を読み込ませる
 /// <para>ファイル名はデフォルトでは"NN.ini"です。</para>
 /// </summary>
 /// <param name="fname">初期化用のパラメータの書かれたファイルのパス</param>
 public void Setup(string fname = "NN.ini")
 {
     if (System.IO.File.Exists(fname) == false) throw new SystemException("NeuralNetworkクラスのSetup()メソッドにおいてエラーがスローされました。指定されたファイルは存在しません。");
     Parameter para = new Parameter(fname);
     this.parameter = para;
     if (this.parameter.Error) throw new Exception("NeuralNetworkクラスのSetup()メソッドにおいてエラーがスローされました。設定パラメータの値が不正です。");
     this.Init();
     this.layer.Setup(fname);                        // 実際の処理はlayerに丸投げ
     return;
 }
 // 3層以上と限定すれば、numOfPreLayerUnitは要らないんだけど2層も試したかったので導入した
 /// <summary>
 /// レイヤークラスのコンストラクタ
 /// <para>このクラスインスタンスを一つ生成すれば、入力層から出力層までを表現可能です。</para>
 /// <para>
 /// 本クラスを生成する際、numOfPreLayerUnitは0と設定することをお勧めします。
 /// 0でなくともエラーは出ませんが、入力層には結合係数が定義されないためです。
 /// </para>
 /// </summary>
 /// <param name="_parameter">ニューラルネットのパラメータ</param>
 /// <param name="_myLayerNum">層番号</param>
 /// <param name="numOfPreLayerUnit">前の層のユニット数(省略可能です。デフォルトでは0となります。)</param>
 public Layer(Parameter _parameter, int _myLayerNum, int numOfPreLayerUnit = 0)
 {
     this.parameter = _parameter;
     this.grossLayerNum = this.parameter.TotalLayer;
     this.myLayerNum = _myLayerNum;
     // ユニットの準備と、前層のユニット数を記憶する
     switch (this.MyKind)
     {
         case Kind.InputLayer:                                                       // 入力層
             this.units = new Unit[this.parameter.NumOfUnitInInputLayer];            // 層の種類に合わせて、ユニット数を宣言する
             break;
         case Kind.HiddenLayer:                                                      // 中間層
             this.units = new Unit[this.parameter.NumOfUnitInHiddenLayer];
             break;
         case Kind.OutputLayer:                                                      // 出力層
             this.units = new Unit[this.parameter.NumOfUnitInOutputLayer];
             break;
     }
     for (int i = 0; i < this.units.Length; i++) this.units[i] = new Unit(numOfPreLayerUnit);        // 前の層のユニット数だけ結合係数を生成させる
     this.preLayerUnitNum = numOfPreLayerUnit;                                                       // 前の層のユニット数を記憶しておく
     if (this.MyKind != Kind.OutputLayer)
         this.subsidiaryLayer = new Layer(this.parameter, this.myLayerNum - 1, this.units.Length);   // 出力層以外なら子レイヤを生成
 }