/// <summary>
        /// Construct the LMA object.
        /// </summary>
        /// <param name="network">The network to train. Must have a single output neuron.</param>
        /// <param name="training">The training data to use. Must be indexable.</param>
        /// <param name="h">The Hessian calculator to use.</param>
        public LevenbergMarquardtTraining(BasicNetwork network,
                                          IMLDataSet training, IComputeHessian h)
            : base(TrainingImplementationType.Iterative)
        {
            ValidateNetwork.ValidateMethodToData(network, training);

            Training           = training;
            _indexableTraining = Training;
            this._network      = network;
            _trainingLength    = (int)_indexableTraining.Count;
            _weightCount       = this._network.Structure.CalculateSize();
            _lambda            = 0.1;
            _deltas            = new double[_weightCount];
            _diagonal          = new double[_weightCount];

            var input = new BasicMLData(
                _indexableTraining.InputSize);
            var ideal = new BasicMLData(
                _indexableTraining.IdealSize);

            _pair = new BasicMLDataPair(input, ideal);

            _hessian = h;
            _hessian.Init(network, training);
        }
        /// <inheritdoc />
        public override void Iteration()
        {
            if (!_initComplete)
            {
                _hessian.Init(_network, Training);
                _initComplete = true;
            }

            PreIteration();

            _hessian.Clear();
            _weights = NetworkCODEC.NetworkToArray(_network);

            _hessian.Compute();
            double currentError = _hessian.SSE;

            SaveDiagonal();

            double startingError = currentError;
            bool   done          = false;
            bool   singular;

            while (!done)
            {
                ApplyLambda();
                var decomposition = new LUDecomposition(_hessian.HessianMatrix);

                singular = decomposition.IsNonsingular;

                if (singular)
                {
                    _deltas = decomposition.Solve(_hessian.Gradients);
                    UpdateWeights();
                    currentError = CalculateError();
                }

                if (!singular || currentError >= startingError)
                {
                    _lambda *= ScaleLambda;
                    if (_lambda > LambdaMax)
                    {
                        _lambda = LambdaMax;
                        done    = true;
                    }
                }
                else
                {
                    _lambda /= ScaleLambda;
                    done     = true;
                }
            }

            Error = currentError;

            PostIteration();
        }
        /// <summary>
        /// Construct the LMA object. 
        /// </summary>
        /// <param name="network">The network to train. Must have a single output neuron.</param>
        /// <param name="training">The training data to use. Must be indexable.</param>
        /// <param name="h">The Hessian calculator to use.</param>
        public LevenbergMarquardtTraining(BasicNetwork network,
            IMLDataSet training, IComputeHessian h)
            : base(TrainingImplementationType.Iterative)
        {
            ValidateNetwork.ValidateMethodToData(network, training);

            Training = training;
            _indexableTraining = Training;
            this._network = network;
            _trainingLength = (int) _indexableTraining.Count;
            _weightCount = this._network.Structure.CalculateSize();
            _lambda = 0.1;
            _deltas = new double[_weightCount];
            _diagonal = new double[_weightCount];

            var input = new BasicMLData(
                _indexableTraining.InputSize);
            var ideal = new BasicMLData(
                _indexableTraining.IdealSize);

            _hessian = h;
            _hessian.Init(network, training);
        }