// <summary>ソースコード：ニューラルネットワークコーディングサンプル</summary>
// <author>CommonMP</author>
using System;
using System.Collections.Generic;
using System.Text;

namespace CommonMP.HYMCO.OptionImpl.NeuralNetworkSample
{
    /// <summary><para>class outline:</para>
    /// <para>ニューロン層間の結合ネットワーク</para>
    /// </summary>
    /// <remarks><para>history:</para>
    /// <para>[CommonMP][ver 1.0.0][2010/02/01][新規作成]</para>
    /// </remarks>
    public class NN_Network
    {
        /// <summary>乱数発生用 </summary>
        protected static Random csRnd = new Random(DateTime.Now.GetHashCode());
        //====================================
        // 常数
        //====================================
        /// <summary>学習係数 </summary>
        public static double m_dAlpha = 0.1;
        /// <summary>学習慣性係数 </summary>
        public static double m_dInertia = 0.75;
        //====================================
        // メンバー変数
        //====================================
        /// <summary>上位ネットワーク層 </summary>
        protected NN_Layer m_csUpperLayer = null;
        /// <summary>上位ネットワーク層内ニューロン配列 </summary>
        protected NN_Neuron[] m_csUpperNeuron = null;
        /// <summary>下位ネットワーク層 </summary>
        protected NN_Layer m_csLowerLayer = null;
        /// <summary>下位ネットワーク層内ニューロン配列 </summary>
        protected NN_Neuron[] m_csLowerNeuron = null;

        /// <summary>上位ネットワーク層内ニューロン数 </summary>
        protected int m_iUpperNeuronNum = 0;
        /// <summary>下位ネットワーク層内ニューロン数 </summary>
        protected int m_iLowerNeuronNum = 0;

        /// <summary>ネットワークの重み係数 </summary>
        public  double[,] m_dW = null;
        /// <summary>前回の重み補正値 </summary>
        protected double[,] m_dPrevDltW = null;

        /// <summary><para>method outline:</para>
        /// <para>ニューロン層間の結合ネットワーク生成</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>CreateNetwork(ref csUpperLayer,ref csLowerLayer)</para>
        /// </example>
        /// <param name="csUpperLayer">接続上位ニューロン層</param>
        /// <param name="csLowerLayer">接続下位ニューロン層</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public void CreateNetwork( ref NN_Layer csUpperLayer, ref NN_Layer csLowerLayer )
        {
            m_iUpperNeuronNum = csUpperLayer.GetNeuronNum();
            m_iLowerNeuronNum = csLowerLayer.GetNeuronNum() + 1;
            m_dW = new double[m_iUpperNeuronNum, m_iLowerNeuronNum];
            m_dPrevDltW = new double[m_iUpperNeuronNum, m_iLowerNeuronNum];

            m_csUpperLayer = csUpperLayer;
            m_csUpperLayer.SetLowerNetwork(this);
            m_csUpperNeuron = csUpperLayer.GetNeuron();

            m_csLowerLayer = csLowerLayer;
            m_csLowerLayer.SetUpperNetwork(this);
            m_csLowerNeuron = csLowerLayer.GetNeuron();

        }

        /// <summary><para>method outline:</para>
        /// <para>想起処理</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>Recognize()</para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public virtual void Recognize()
        {
            double dU = 0.0;
            for (int iUp = 0; iUp < m_iUpperNeuronNum; iUp++)
            {
                dU = 0.0;
                for (int iLw = 0; iLw < m_iLowerNeuronNum; iLw++)
                {
                    dU+= m_dW[iUp,iLw] * m_csLowerNeuron[iLw].GetOut();
                }
                m_csUpperNeuron[iUp].CalOut(dU);
            }
            // 上位層連想
            m_csUpperLayer.Recognize();
        }

        /// <summary><para>method outline:</para>
        /// <para>学習処理</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>Learn()</para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public virtual void Learn()
        {
            // Dlt 計算
            double dEps = 0.0;
            for (int iLw = 0; iLw < m_iLowerNeuronNum; iLw++)
            {
                dEps = 0.0;
                for (int iUp = 0; iUp < m_iUpperNeuronNum; iUp++)
                {
                    dEps += m_dW[iUp, iLw] * m_csUpperNeuron[iUp].GetDlt();
                }
                // 下位ニューロンの誤差計算
                m_csLowerNeuron[iLw].CalDlt(dEps);  
            }
            // 重み付け変更
            double dDlt =0.0;
            double dDltWChange = 0.0;
            for (int iUp = 0; iUp < m_iUpperNeuronNum; iUp++)
            {
                dDlt = m_csUpperNeuron[iUp].GetDlt(); // 上位層誤差値 
                for (int iLw = 0; iLw < m_iLowerNeuronNum; iLw++)
                {
                    dDltWChange = m_dAlpha * dDlt * m_csLowerNeuron[iLw].GetOut() + m_dInertia * m_dPrevDltW[iUp, iLw];
                    m_dW[iUp, iLw] += dDltWChange;
                    m_dPrevDltW[iUp, iLw] = dDltWChange; 
                    //m_dW[iUp, iLw] += m_dAlpha * dDlt * m_csLowerNeuron[iLw].GetOut();
                }
            }

            // 下位層の学習
            m_csLowerLayer.Learn();
        }

        /// <summary><para>method outline:</para>
        /// <para>結合の重みの初期化</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>WeightInitialize( dMax, dMin )</para>
        /// </example>
        /// <param name="dMax">最大値</param>
        /// <param name="dMin">最小値（負値もOK)</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public void WeightInitialize(double dMax, double dMin)
        {
            //int iSeed = DateTime.Now.GetHashCode();
            //Random csRnd = new Random(iSeed);   // <-- static 化
            for (int iUp = 0; iUp < m_iUpperNeuronNum; iUp++)
            {
                for (int iLw = 0; iLw < m_iLowerNeuronNum; iLw++)
                {
                    m_dW[iUp, iLw] = (double)csRnd.Next((int)(dMin*1000.0), (int)(dMax*1000.0)) / 1000.0;
                    m_dPrevDltW[iUp, iLw] = 0.0;
                }
            }
        }
    }
}
