public static void Initialize(int numThreads)
 {
     lock (_lockObject)
     {
         if (NumThreads == 0)
         {
             Contracts.Assert(numThreads > 0);
             Contracts.Assert(NumThreads == 0);
             NumThreads = numThreads;
             BlockingThreadPool.Initialize(numThreads);
         }
     }
 }
        public double ComputeDominationLoss(double[] scores)
        {
            int    chunkSize   = 1 + Dataset.NumQueries / BlockingThreadPool.NumThreads; // Minimizes the number of repeat computations in sparse array to have each thread take as big a chunk as possible
            double totalOutput = 0.0;
            var    _lock       = new Object();

            for (int queryBegin = 0; queryBegin < Dataset.NumQueries; queryBegin += chunkSize)
            {
                BlockingThreadPool.RunOrBlock(delegate(int startQuery, int endQuery)
                {
                    double output = 0.0;
                    for (int query = startQuery; query <= endQuery; query++)
                    {
                        int begin = Dataset.Boundaries[query];
                        int end   = Dataset.Boundaries[query + 1];

                        if (end - begin <= 1)
                        {
                            continue;
                        }

                        int bestDoc               = _bestDocsPerQuery.BestDocs[query];
                        int secondBestDoc         = _bestDocsPerQuery.SecondBestDocs[query];
                        double bestDocScore       = scores[bestDoc];
                        double secondBestDocScore = scores[secondBestDoc];

                        // find max score
                        double max        = double.NegativeInfinity;
                        double maxNotBest = double.NegativeInfinity;

                        for (int d = begin; d < end; ++d)
                        {
                            if (max < scores[d])
                            {
                                max = scores[d];
                            }
                            if (d != bestDoc && maxNotBest < scores[d])
                            {
                                maxNotBest = scores[d];
                            }
                        }

                        // sum of exponents and sum of all but best
                        double sum           = 0.0;
                        double sumAllButBest = 0.0;
                        for (int d = begin; d < end; ++d)
                        {
                            sum += Math.Exp(scores[d] - max);
                            if (d != bestDoc)
                            {
                                sumAllButBest += Math.Exp(scores[d] - maxNotBest);
                            }
                        }

                        output += max - bestDocScore + Math.Log(sum) + 0.5 * (maxNotBest - secondBestDocScore + Math.Log(sumAllButBest));
                    }
                    lock (_lock)
                    {
                        totalOutput += output;
                    }
                }, queryBegin, Math.Min(queryBegin + chunkSize - 1, Dataset.NumQueries - 1));
            }
            BlockingThreadPool.BlockUntilAllWorkItemsFinish();
            return(totalOutput);
        }