public WeightVector RunIterations(WeightVector weightVector, int iterationCount, int threadCount = 1)
        {
            _weightVector = weightVector;

            for (var iter = 0; iter < iterationCount; iter++)
            {
                Console.WriteLine(DateTime.Now + " running iteration " + iter);

                var newWeightVector = _weightVector.DeepCopy();
                SetForwardBackwordAlgo(newWeightVector);
                if (threadCount > 1)
                {
                    var doneEvents = new ManualResetEvent[threadCount];
                    var partition = newWeightVector.FeatureCount / threadCount;

                    for (int threadIndex = 0; threadIndex < threadCount; threadIndex++)
                    {
                        var start = threadIndex*partition;
                        var end = start + partition;
                        end = end > newWeightVector.FeatureCount ? newWeightVector.FeatureCount : end;
                        doneEvents[threadIndex] = new ManualResetEvent(false);

                        var info = new ThreadInfoObject(this, start, end, newWeightVector,
                            doneEvents[threadIndex], null);
                        ThreadPool.QueueUserWorkItem(info.StartGradientComputing, threadIndex);
                    }

                    WaitHandle.WaitAll(doneEvents);
                }
                else
                {
                    ComputeRange(0, _weightVector.FeatureCount, newWeightVector);
                }
                _weightVector = newWeightVector;
                if (iter + 1 < iterationCount)
                {
                    _weightVector.AvgNormalize();
                }
            }
            _weightVector.AvgNormalize();
            return _weightVector;
        }
        public void ComputeGradientMultiThread(WeightVector weightVector, double[] gradient,
            int threadCount)
        {
            if (threadCount > 1)
            {
                //var newWeightVector = weightVector.DeepCopy();

                var doneEvents = new ManualResetEvent[threadCount];
                var partition = weightVector.FeatureCount / threadCount;

                for (int threadIndex = 0; threadIndex < threadCount; threadIndex++)
                {
                    var start = threadIndex*partition;
                    var end = start + partition;
                    end = end > weightVector.FeatureCount ? weightVector.FeatureCount : end;
                    doneEvents[threadIndex] = new ManualResetEvent(false);

                    var info = new ThreadInfoObject(this, start, end, weightVector,
                        doneEvents[threadIndex], gradient);
                    ThreadPool.QueueUserWorkItem(info.StartLBFGGradientComputing, threadIndex);
                }

                WaitHandle.WaitAll(doneEvents);
            }
            ComputeGradientValues(weightVector, gradient, 0, weightVector.FeatureCount);
        }