private Pixel GetAbsMax(float[,] xImage, float[,] bMap, float lambda, float alpha)
        {
            var currentMax = new Pixel(0, -1, -1);

            for (int y = 0; y < imageSection.YExtent(); y++)
            {
                for (int x = 0; x < imageSection.XExtent(); x++)
                {
                    var currentA = aMap[y, x];
                    var old      = xImage[y, x];
                    var xTmp     = ElasticNet.ProximalOperator(old * currentA + bMap[y, x], currentA, lambda, alpha);
                    var xDiff    = xTmp - old;

                    if (currentMax.MaxDiff < Math.Abs(xDiff))
                    {
                        var yGlobal = y + imageSection.Y;
                        var xGlobal = x + imageSection.X;
                        currentMax = new Pixel(xDiff, yGlobal, xGlobal);
                    }
                }
            }

            var maxPixelGlobal = comm.Allreduce(currentMax, (aC, bC) => aC.MaxDiff > bC.MaxDiff ? aC : bC);

            return(maxPixelGlobal);
        }
        public static Tuple <int, int, double>[] GetMaxBlocks(float[,] gExplore, float[,] gCorrection, float[,] xExplore, float stepSize, float theta, float lambda, float alpha, int yBlockSize, int xBlockSize, int tau)
        {
            var yBlocks = gExplore.GetLength(0) / yBlockSize;
            var xBlocks = gExplore.GetLength(1) / xBlockSize;

            var tmp = new List <Tuple <int, int, double> >(yBlocks * xBlocks);

            for (int i = 0; i < yBlocks; i++)
            {
                for (int j = 0; j < xBlocks; j++)
                {
                    int yIdx = i * yBlockSize;
                    int xIdx = j * xBlockSize;
                    var sum  = 0.0;
                    for (int y = yIdx; y < yIdx + yBlockSize; y++)
                    {
                        for (int x = xIdx; x < xIdx + xBlockSize; x++)
                        {
                            var update = theta * gCorrection[y, x] + gExplore[y, x] + xExplore[y, x] * stepSize;
                            var shrink = ElasticNet.ProximalOperator(update, stepSize, lambda, alpha) - xExplore[y, x];
                            sum += Math.Abs(shrink);
                        }
                    }
                    tmp.Add(new Tuple <int, int, double>(i, j, sum));
                }
            }
            tmp.Sort((x, y) => x.Item3.CompareTo(y.Item3));
            var output = new Tuple <int, int, double> [tau];

            for (int i = 0; i < tau; i++)
            {
                output[i] = tmp[tmp.Count - i - 1];
            }
            return(output);
        }
            public void Deconvolve()
            {
                var   update     = 0.0f;
                var   blockCount = shared.XExpl.Length;
                float eta        = 1.0f / blockCount;

                DiffMax = 0.0f;

                var beta          = CalcESO(shared.ProcessorCount, shared.DegreeOfSeperability, blockCount);
                var continueAsync = Thread.VolatileRead(ref shared.AsyncFinished) == 0;

                for (int inner = 0; inner < shared.MaxConcurrentIterations & continueAsync; inner++)
                {
                    continueAsync = Thread.VolatileRead(ref shared.AsyncFinished) == 0;
                    var stepFactor  = (float)beta * Theta / shared.Theta0;
                    var theta2      = Theta * Theta;
                    var blockIdx    = GetPseudoRandomBlock(stepFactor, theta2);
                    var blockSample = shared.ActiveSet[blockIdx];
                    var yPixel      = blockSample.Item1;
                    var xPixel      = blockSample.Item2;
                    var step        = shared.AMap[yPixel, xPixel] * stepFactor;

                    var correctionFactor = -(1.0f - Theta / shared.Theta0) / theta2;

                    var xExpl = Thread.VolatileRead(ref shared.XExpl[yPixel, xPixel]);
                    update  = theta2 * Thread.VolatileRead(ref shared.GCorr[yPixel, xPixel]) + Thread.VolatileRead(ref shared.GExpl[yPixel, xPixel]) + xExpl * step;
                    update  = ElasticNet.ProximalOperator(update, step, shared.Lambda, shared.Alpha) - xExpl;
                    DiffMax = Math.Max(DiffMax, Math.Abs(update));

                    //update gradients
                    if (0.0f != Math.Abs(update))
                    {
                        UpdateGradientsApprox(shared.GExpl, shared.GCorr, shared.Psf2, updateCache, yPixel, xPixel, correctionFactor, update);
                        var oldExplore  = shared.XExpl[yPixel, xPixel];   //does not need to be volatile, this index is blocked until this process is finished, and we already made sure with a volatile read that the latest value is in the cache
                        var oldXCorr    = Thread.VolatileRead(ref shared.XCorr[yPixel, xPixel]);
                        var newXExplore = oldExplore + update;

                        Thread.VolatileWrite(ref shared.XExpl[yPixel, xPixel], shared.XExpl[yPixel, xPixel] + update);
                        Thread.VolatileWrite(ref shared.XCorr[yPixel, xPixel], shared.XCorr[yPixel, xPixel] + update * correctionFactor);

                        //not 100% sure this is the correct generalization from single pixel thread rule to block rule
                        var testRestartUpdate = (update) * (newXExplore - (theta2 * oldXCorr + oldExplore));
                        ConcurrentUpdateTestRestart(ref shared.TestRestart, eta, testRestartUpdate);
                    }

                    AsyncIterations++;
                    //unlockBlock
                    Thread.VolatileWrite(ref shared.BlockLock[blockIdx], 0);
                    Theta = (float)(Math.Sqrt((theta2 * theta2) + 4 * (theta2)) - theta2) / 2.0f;
                }

                Thread.VolatileWrite(ref shared.AsyncFinished, 1);
            }
            private static float GetMaxAbsPixelValue(SharedData shared, Tuple <int, int> block, float stepFactor)
            {
                var yPixel = block.Item1;
                var xPixel = block.Item2;

                var step   = shared.AMap[yPixel, xPixel] * stepFactor;
                var xExpl  = Thread.VolatileRead(ref shared.XExpl[yPixel, xPixel]);
                var update = Thread.VolatileRead(ref shared.GExpl[yPixel, xPixel]) + xExpl * step;

                update = ElasticNet.ProximalOperator(update, step, shared.Lambda, shared.Alpha) - xExpl;

                return(Math.Abs(update));
            }
        public static List <Tuple <int, int> > GetActiveSet(float[,] xExplore, float[,] gExplore, int yBlockSize, int xBlockSize, float lambda, float alpha, float[,] lipschitzMap)
        {
            var debug  = new float[xExplore.GetLength(0), xExplore.GetLength(1)];
            var output = new List <Tuple <int, int> >();

            for (int i = 0; i < xExplore.GetLength(0) / yBlockSize; i++)
            {
                for (int j = 0; j < xExplore.GetLength(1) / xBlockSize; j++)
                {
                    var yPixel  = i * yBlockSize;
                    var xPixel  = j * xBlockSize;
                    var nonZero = false;

                    for (int y = yPixel; y < yPixel + yBlockSize; y++)
                    {
                        for (int x = xPixel; x < xPixel + xBlockSize; x++)
                        {
                            var lipschitz = lipschitzMap[y, x];
                            var tmp       = gExplore[y, x] + xExplore[y, x] * lipschitz;
                            tmp = ElasticNet.ProximalOperator(tmp, lipschitz, lambda, alpha);
                            if (0.0f < Math.Abs(tmp - xExplore[y, x]))
                            {
                                nonZero = true;
                            }
                        }
                    }

                    if (nonZero)
                    {
                        output.Add(new Tuple <int, int>(i, j));
                        for (int y = yPixel; y < yPixel + yBlockSize; y++)
                        {
                            for (int x = xPixel; x < xPixel + xBlockSize; x++)
                            {
                                debug[y, x] = 1.0f;
                            }
                        }
                    }
                }
            }
            FitsIO.Write(debug, "activeSet.fits");
            return(output);
        }
        private static List <Tuple <int, int> > GetActiveSet(float[,] xExplore, float[,] gExplore, float lambda, float alpha, float[,] lipschitzMap)
        {
            var output = new List <Tuple <int, int> >();

            for (int y = 0; y < xExplore.GetLength(0); y++)
            {
                for (int x = 0; x < xExplore.GetLength(1); x++)
                {
                    var lipschitz = lipschitzMap[y, x];
                    var tmp       = gExplore[y, x] + xExplore[y, x] * lipschitz;
                    tmp = ElasticNet.ProximalOperator(tmp, lipschitz, lambda, alpha);
                    if (0.0f < Math.Abs(tmp - xExplore[y, x]))
                    {
                        output.Add(new Tuple <int, int>(y, x));
                    }
                }
            }
            return(output);
        }
        private List <Tuple <int, int> > GetActiveSet(float[,] xExplore, float[,] gExplore, float lambda, float alpha, float lipschitz)
        {
            var debug  = new float[xExplore.GetLength(0), xExplore.GetLength(1)];
            var output = new List <Tuple <int, int> >();

            for (int i = 0; i < xExplore.GetLength(0) / yBlockSize; i++)
            {
                for (int j = 0; j < xExplore.GetLength(1) / xBlockSize; j++)
                {
                    var yPixel  = i * yBlockSize;
                    var xPixel  = j * xBlockSize;
                    var nonZero = false;
                    for (int y = yPixel; y < yPixel + yBlockSize; y++)
                    {
                        for (int x = xPixel; x < xPixel + xBlockSize; x++)
                        {
                            var tmp = gExplore[y, x] + xExplore[y, x] * lipschitz;
                            tmp = ElasticNet.ProximalOperator(tmp, lipschitz, lambda, alpha);
                            if (ACTIVE_SET_CUTOFF < Math.Abs(tmp - xExplore[y, x]))
                            {
                                nonZero = true;
                            }
                        }
                    }

                    if (nonZero)
                    {
                        output.Add(new Tuple <int, int>(i, j));
                        for (int y = yPixel; y < yPixel + yBlockSize; y++)
                        {
                            for (int x = xPixel; x < xPixel + xBlockSize; x++)
                            {
                                debug[y, x] = 1.0f;
                            }
                        }
                    }
                }
            }
            //FitsIO.Write(debug, "activeSet.fits");
            //can write max change for convergence purposes
            return(output);
        }
        private Pixel GetAbsMaxSingle(Rectangle subpatch, float[,] xImage, float[,] gradients, float lambda, float alpha)
        {
            var maxPixels = new Pixel[subpatch.YExtent()];

            for (int y = subpatch.Y; y < subpatch.YEnd; y++)
            {
                var yLocal = y;

                var currentMax = new Pixel(-1, -1, 0, 0);
                for (int x = subpatch.X; x < subpatch.XEnd; x++)
                {
                    var xLocal   = x;
                    var currentA = aMap[yLocal, xLocal];
                    var old      = xImage[yLocal, xLocal];
                    //var xTmp = old + bMap[y, x] / currentA;
                    //xTmp = ShrinkElasticNet(xTmp, lambda, alpha);
                    var xTmp  = ElasticNet.ProximalOperator(old * currentA + gradients[y, x], currentA, lambda, alpha);
                    var xDiff = old - xTmp;

                    if (currentMax.PixelMaxDiff < Math.Abs(xDiff))
                    {
                        currentMax.Y            = y;
                        currentMax.X            = x;
                        currentMax.PixelMaxDiff = Math.Abs(xDiff);
                        currentMax.PixelNew     = xTmp;
                    }
                }
                maxPixels[yLocal] = currentMax;
            }

            var maxPixel = new Pixel(-1, -1, 0, 0);

            for (int i = 0; i < maxPixels.Length; i++)
            {
                if (maxPixel.PixelMaxDiff < maxPixels[i].PixelMaxDiff)
                {
                    maxPixel = maxPixels[i];
                }
            }

            return(maxPixel);
        }
            private static float GetMaxAbsBlockValue(SharedData shared, Tuple <int, int> block, float stepFactor, float theta2)
            {
                var yOffset = block.Item1 * shared.YBlockSize;
                var xOffset = block.Item2 * shared.XBlockSize;

                var blockLipschitz = GetBlockLipschitz(shared.AMap, yOffset, xOffset, shared.YBlockSize, shared.XBlockSize);
                var step           = blockLipschitz * stepFactor;
                var updateAbsSum   = 0.0f;

                for (int y = yOffset; y < (yOffset + shared.YBlockSize); y++)
                {
                    for (int x = xOffset; x < (xOffset + shared.XBlockSize); x++)
                    {
                        var xExpl  = Thread.VolatileRead(ref shared.XExpl[y, x]);
                        var update = theta2 * Thread.VolatileRead(ref shared.GCorr[y, x]) + Thread.VolatileRead(ref shared.GExpl[y, x]) + xExpl * step;
                        update        = ElasticNet.ProximalOperator(update, step, shared.Lambda, shared.Alpha) - xExpl;
                        updateAbsSum += Math.Abs(update);
                    }
                }

                return(updateAbsSum);
            }
        public static float GetAbsMax(float[,] xImage, float[,] bMap, float[,] aMap, float lambda, float alpha)
        {
            var maxPixels = new float[xImage.GetLength(0)];

            Parallel.For(0, xImage.GetLength(0), (y) =>
            {
                var yLocal = y;

                var currentMax = 0.0f;
                for (int x = 0; x < xImage.GetLength(1); x++)
                {
                    var xLocal   = x;
                    var currentA = aMap[yLocal, xLocal];
                    var old      = xImage[yLocal, xLocal];
                    //var xTmp = old + bMap[y, x] / currentA;
                    //xTmp = ShrinkElasticNet(xTmp, lambda, alpha);
                    var xTmp  = ElasticNet.ProximalOperator(old * currentA + bMap[y, x], currentA, lambda, alpha);
                    var xDiff = old - xTmp;

                    if (currentMax < Math.Abs(xDiff))
                    {
                        currentMax = Math.Abs(xDiff);
                    }
                }
                maxPixels[yLocal] = currentMax;
            });

            var maxPixel = 0.0f;

            for (int i = 0; i < maxPixels.Length; i++)
            {
                if (maxPixel < maxPixels[i])
                {
                    maxPixel = maxPixels[i];
                }
            }

            return(maxPixel);
        }
        public void ISTAStep(float[,] xImage, float[,] residuals, float[,] psf, float lambda, float alpha)
        {
            var xOld       = Copy(xImage);
            var corrKernel = PSF.CalcPaddedFourierCorrelation(psf, new Rectangle(0, 0, residuals.GetLength(0), residuals.GetLength(1)));
            var gradients  = Residuals.CalcGradientMap(residuals, corrKernel, new Rectangle(0, 0, psf.GetLength(0), psf.GetLength(1)));

            var lipschitz = (float)PSF.CalcMaxLipschitz(psf) * xImage.Length;

            for (int i = 0; i < xImage.GetLength(0); i++)
            {
                for (int j = 0; j < xImage.GetLength(1); j++)
                {
                    var tmp = gradients[i, j] + xImage[i, j] * lipschitz;
                    tmp          = ElasticNet.ProximalOperator(tmp, lipschitz, lambda, alpha);
                    xImage[i, j] = tmp;
                }
            }

            //update residuals
            for (int i = 0; i < xImage.GetLength(0); i++)
            {
                for (int j = 0; j < xImage.GetLength(1); j++)
                {
                    xOld[i, j] = xImage[i, j] - xOld[i, j];
                }
            }

            var convKernel          = PSF.CalcPaddedFourierConvolution(psf, new Rectangle(0, 0, residuals.GetLength(0), residuals.GetLength(1)));
            var residualsCalculator = new PaddedConvolver(convKernel, new Rectangle(0, 0, psf.GetLength(0), psf.GetLength(1)));

            residualsCalculator.ConvolveInPlace(xOld);
            for (int i = 0; i < xImage.GetLength(0); i++)
            {
                for (int j = 0; j < xImage.GetLength(1); j++)
                {
                    residuals[i, j] -= xOld[i, j];
                }
            }
        }
            public void Deconvolve()
            {
                var update     = 0.0f;
                var blockCount = shared.XExpl.Length;

                DiffMax = 0.0f;

                var beta          = CalcESO(shared.ProcessorCount, shared.DegreeOfSeperability, blockCount);
                var continueAsync = Thread.VolatileRead(ref shared.AsyncFinished) == 0;

                for (int inner = 0; inner < shared.MaxConcurrentIterations & continueAsync; inner++)
                {
                    continueAsync = Thread.VolatileRead(ref shared.AsyncFinished) == 0;
                    var blockIdx    = GetPseudoRandomBlock((float)beta);
                    var blockSample = shared.ActiveSet[blockIdx];
                    var yPixel      = blockSample.Item1;
                    var xPixel      = blockSample.Item2;
                    var step        = shared.AMap[yPixel, xPixel] * (float)beta;

                    var xExpl = Thread.VolatileRead(ref shared.XExpl[yPixel, xPixel]);
                    update  = Thread.VolatileRead(ref shared.GExpl[yPixel, xPixel]) + xExpl * step;
                    update  = ElasticNet.ProximalOperator(update, step, shared.Lambda, shared.Alpha) - xExpl;
                    DiffMax = Math.Max(DiffMax, Math.Abs(update));

                    //update gradients
                    if (0.0f != Math.Abs(update))
                    {
                        UpdateGradients(shared.GExpl, shared.Psf2, yPixel, xPixel, update);
                        Thread.VolatileWrite(ref shared.XExpl[yPixel, xPixel], shared.XExpl[yPixel, xPixel] + update);
                    }

                    AsyncIterations++;
                    //unlockBlock
                    Thread.VolatileWrite(ref shared.BlockLock[blockIdx], 0);
                }

                Thread.VolatileWrite(ref shared.AsyncFinished, 1);
            }
            public void Deconvolve()
            {
                var blockCount = shared.XExpl.Length / (shared.YBlockSize * shared.XBlockSize);
                //var blockCount = shared.ActiveSet.Count;
                float eta = 1.0f / blockCount;

                var beta = CalcESO(shared.ProcessorCount, shared.DegreeOfSeperability, blockCount);

                xDiffMax = 0.0f;
                var continueAsync = Thread.VolatileRead(ref shared.asyncFinished) == 0;

                for (int inner = 0; inner < shared.MaxConcurrentIterations & continueAsync; inner++)
                {
                    continueAsync = Thread.VolatileRead(ref shared.asyncFinished) == 0;
                    var blockIdx    = GetRandomBlockIdx(random, id, shared.BlockLock);
                    var blockSample = shared.ActiveSet[blockIdx];
                    var yOffset     = blockSample.Item1 * shared.YBlockSize;
                    var xOffset     = blockSample.Item2 * shared.XBlockSize;

                    var blockLipschitz   = GetBlockLipschitz(shared.AMap, yOffset, xOffset, shared.YBlockSize, shared.XBlockSize);
                    var step             = blockLipschitz * (float)beta * Theta / shared.theta0;
                    var theta2           = Theta * Theta;
                    var correctionFactor = -(1.0f - Theta / shared.theta0) / theta2;

                    var updateSum    = 0.0f;
                    var updateAbsSum = 0.0f;
                    for (int y = yOffset; y < (yOffset + shared.YBlockSize); y++)
                    {
                        for (int x = xOffset; x < (xOffset + shared.XBlockSize); x++)
                        {
                            var xExpl  = Thread.VolatileRead(ref shared.XExpl[y, x]);
                            var update = theta2 * Thread.VolatileRead(ref shared.GCorr[y, x]) + Thread.VolatileRead(ref shared.GExpl[y, x]) + xExpl * step;
                            update = ElasticNet.ProximalOperator(update, step, shared.Lambda, shared.Alpha) - xExpl;
                            blockUpdate[y - yOffset, x - xOffset] = update;
                            updateSum     = update;
                            updateAbsSum += Math.Abs(update);
                        }
                    }

                    //update gradients
                    if (0.0f != updateAbsSum)
                    {
                        xDiffMax = Math.Max(xDiffMax, updateAbsSum);
                        UpdateBMaps(blockUpdate, blockSample.Item1, blockSample.Item2, shared.Psf2, shared.GExpl, shared.GCorr, correctionFactor);
                        var newXExplore = 0.0f;
                        var oldXExplore = 0.0f;
                        var oldXCorr    = 0.0f;
                        for (int y = yOffset; y < (yOffset + shared.YBlockSize); y++)
                        {
                            for (int x = xOffset; x < (xOffset + shared.XBlockSize); x++)
                            {
                                var update     = blockUpdate[y - yOffset, x - xOffset];
                                var oldExplore = shared.XExpl[y, x];    //does not need to be volatile, this index is blocked until this process is finished, and we already made sure with a volatile read that the latest value is in the cache

                                oldXExplore += shared.XExpl[y, x];
                                oldXCorr    += Thread.VolatileRead(ref shared.XCorr[y, x]);
                                newXExplore += oldExplore + update;

                                Thread.VolatileWrite(ref shared.XExpl[y, x], shared.XExpl[y, x] + update);
                                Thread.VolatileWrite(ref shared.XCorr[y, x], shared.XCorr[y, x] + update * correctionFactor);
                            }
                        }

                        //not 100% sure this is the correct generalization from single pixel thread rule to block rule
                        var testRestartUpdate = (updateSum) * (newXExplore - (theta2 * oldXCorr + oldXExplore));
                        ConcurrentUpdateTestRestart(ref shared.testRestart, eta, testRestartUpdate);
                    }

                    //unlockBlock
                    Thread.VolatileWrite(ref shared.BlockLock[blockIdx], 0);

                    if (useAcceleration)
                    {
                        Theta = (float)(Math.Sqrt((theta2 * theta2) + 4 * (theta2)) - theta2) / 2.0f;
                    }
                }

                Thread.VolatileWrite(ref shared.asyncFinished, 1);
            }
        public float DeconvolveAccelerated(float[,] xExplore, float[,] xCorrection, float[,] gExplore, float[,] gCorrection, float[,] psf2, ref List <Tuple <int, int> > activeSet, float maxLipschitz, float lambda, float alpha, Random random, int maxIteration, float epsilon)
        {
            var blockCount = activeSet.Count;
            var beta       = CalcESO(tau, degreeOfSeperability, blockCount);
            var lipschitz  = maxLipschitz * yBlockSize * xBlockSize;

            lipschitz *= (float)beta;

            var   theta  = tau / (float)blockCount;
            var   theta0 = theta;
            float eta    = 1.0f / blockCount;

            var testRestart = 0.0f;
            var iter        = 0;

            var converged = false;

            Console.WriteLine("Starting Active Set iterations with " + activeSet.Count + " blocks");
            while (iter < maxIteration & !converged)
            {
                var xDiffMax       = new float[tau];
                var innerIterCount = Math.Min(activeSet.Count / tau, MAX_ACTIVESET_ITER / tau);
                for (int inner = 0; inner < innerIterCount; inner++)
                {
                    var stepSize         = lipschitz * theta / theta0;
                    var theta2           = theta * theta;
                    var correctionFactor = -(1.0f - theta / theta0) / theta2;
                    var samples          = activeSet.Shuffle(random).Take(tau).ToList();

                    Parallel.For(0, tau, (i) =>
                    {
                        var blockSample = samples[i];
                        var yOffset     = blockSample.Item1 * yBlockSize;
                        var xOffset     = blockSample.Item2 * xBlockSize;

                        var blockUpdate  = new float[yBlockSize, xBlockSize];
                        var updateSum    = 0.0f;
                        var updateAbsSum = 0.0f;
                        for (int y = yOffset; y < (yOffset + yBlockSize); y++)
                        {
                            for (int x = xOffset; x < (xOffset + xBlockSize); x++)
                            {
                                var update = theta2 * gCorrection[y, x] + gExplore[y, x] + xExplore[y, x] * stepSize;
                                update     = ElasticNet.ProximalOperator(update, stepSize, lambda, alpha) - xExplore[y, x];
                                blockUpdate[y - yOffset, x - xOffset] = update;
                                updateSum     = update;
                                updateAbsSum += Math.Abs(update);
                            }
                        }

                        //update gradients
                        if (0.0f != updateAbsSum)
                        {
                            xDiffMax[i] = Math.Max(xDiffMax[i], updateAbsSum);
                            UpdateBMaps(blockUpdate, blockSample.Item1, blockSample.Item2, psf2, gExplore, gCorrection, correctionFactor);
                            var newXExplore = 0.0f;
                            var oldXExplore = 0.0f;
                            var oldXCorr    = 0.0f;
                            for (int y = yOffset; y < (yOffset + yBlockSize); y++)
                            {
                                for (int x = xOffset; x < (xOffset + xBlockSize); x++)
                                {
                                    var update        = blockUpdate[y - yOffset, x - xOffset];
                                    var oldExplore    = xExplore[y, x];
                                    var oldCorrection = xCorrection[y, x];

                                    oldXExplore += xExplore[y, x];
                                    oldXCorr    += xCorrection[y, x];
                                    newXExplore += oldExplore + update;

                                    xExplore[y, x]    += update;
                                    xCorrection[y, x] += update * correctionFactor;
                                }
                            }

                            //not 100% sure this is the correct generalization from single pixel/single thread rule to block/parallel rule
                            var testRestartUpdate = (updateSum) * (newXExplore - (theta2 * oldXCorr + oldXExplore));
                            ConcurrentUpdateTestRestart(ref testRestart, eta, testRestartUpdate);
                        }
                    });

                    theta = (float)(Math.Sqrt((theta2 * theta2) + 4 * (theta2)) - theta2) / 2.0f;
                }

                if (testRestart > 0)
                {
                    //restart acceleration
                    var tmpTheta = theta < 1.0f ? ((theta * theta) / (1.0f - theta)) : theta0;
                    for (int y = 0; y < xExplore.GetLength(0); y++)
                    {
                        for (int x = 0; x < xExplore.GetLength(1); x++)
                        {
                            xExplore[y, x]   += tmpTheta * xCorrection[y, x];
                            xCorrection[y, x] = 0;
                            gExplore[y, x]   += tmpTheta * gCorrection[y, x];
                            gCorrection[y, x] = 0;
                        }
                    }

                    Console.WriteLine("restarting");
                    //new active set
                    activeSet  = GetActiveSet(xExplore, gExplore, lambda, alpha, maxLipschitz);
                    blockCount = activeSet.Count;
                    theta      = tau / (float)blockCount;
                    theta0     = theta;
                    beta       = CalcESO(tau, degreeOfSeperability, blockCount);
                    lipschitz  = maxLipschitz * yBlockSize * xBlockSize;
                    lipschitz *= (float)beta;
                }

                if (xDiffMax.Sum() < epsilon)
                {
                    converged = true;
                }

                Console.WriteLine("Done Active Set iteration " + iter);
                iter++;
            }

            return(theta);
        }
        public float DeconvolveRandomActiveSet(float[,] xExplore, float[,] xCorrection, float[,] gExplore, float[,] gCorrection, float[,] psf2, ref List <Tuple <int, int> > activeSet, float maxLipschitz, float lambda, float alpha, Random random, int maxIteration, float epsilon)
        {
            var blockCount = activeSet.Count;
            var beta       = CalcESO(tau, degreeOfSeperability, blockCount);
            var lipschitz  = maxLipschitz * yBlockSize * xBlockSize;

            lipschitz *= (float)beta;

            var   theta  = tau / (float)blockCount;
            var   theta0 = theta;
            float eta    = 1.0f / blockCount;

            var testRestart     = 0.0;
            var iter            = 0;
            var blocks          = new float[tau, yBlockSize, xBlockSize];
            var containsNonZero = new bool[tau];
            var converged       = false;

            Console.WriteLine("Starting Active Set iterations with " + activeSet.Count + " blocks");
            while (iter < maxIteration & !converged)
            {
                var xDiffMax = 0.0f;
                for (int inner = 0; inner < Math.Min(activeSet.Count / tau, 10000 / tau); inner++)
                {
                    var stepSize = lipschitz * theta / theta0;
                    var theta2   = theta * theta;
                    var samples  = CreateSamples(blockCount, tau, random);

                    //minimize blocks
                    for (int i = 0; i < samples.Length; i++)
                    {
                        var blockSample = activeSet[samples[i]];
                        var yOffset     = blockSample.Item1 * yBlockSize;
                        var xOffset     = blockSample.Item2 * xBlockSize;
                        containsNonZero[i] = false;
                        for (int y = yOffset; y < (yOffset + yBlockSize); y++)
                        {
                            for (int x = xOffset; x < (xOffset + xBlockSize); x++)
                            {
                                var update = theta2 * gCorrection[y, x] + gExplore[y, x] + xExplore[y, x] * stepSize;
                                update = ElasticNet.ProximalOperator(update, stepSize, lambda, alpha) - xExplore[y, x];
                                blocks[i, y - yOffset, x - xOffset] = update;
                                if (update != 0.0)
                                {
                                    containsNonZero[i] = true;
                                }
                            }
                        }
                    }

                    //update bMaps
                    var correctionFactor = -(1.0f - theta / theta0) / (theta * theta);
                    for (int i = 0; i < samples.Length; i++)
                    {
                        if (containsNonZero[i])
                        {
                            var blockSample = activeSet[samples[i]];
                            var yBlock      = blockSample.Item1;
                            var xBlock      = blockSample.Item2;
                            UpdateBMaps(i, blocks, yBlock, xBlock, psf2, gExplore, gCorrection, correctionFactor);
                            //FitsIO.Write(gExplore, "gExplore2.fits");
                            //FitsIO.Write(gCorrection, "gCorr2.fits");
                            var currentDiff = 0.0f;
                            //update reconstructed image
                            var yOffset = yBlock * yBlockSize;
                            var xOffset = xBlock * xBlockSize;
                            for (int y = 0; y < blocks.GetLength(1); y++)
                            {
                                for (int x = 0; x < blocks.GetLength(2); x++)
                                {
                                    var update        = blocks[i, y, x];
                                    var oldExplore    = xExplore[yOffset + y, xOffset + x];
                                    var oldCorrection = xCorrection[yOffset + y, xOffset + x];
                                    var newValue      = oldExplore + update;
                                    testRestart = (1.0 - eta) * testRestart - eta * (update) * (newValue - (theta * theta * oldCorrection + oldExplore));

                                    currentDiff += Math.Abs(update);

                                    xExplore[yOffset + y, xOffset + x]    += update;
                                    xCorrection[yOffset + y, xOffset + x] += update * correctionFactor;
                                }
                            }
                            xDiffMax = Math.Max(xDiffMax, currentDiff);
                        }
                    }

                    theta = (float)(Math.Sqrt((theta * theta * theta * theta) + 4 * (theta * theta)) - theta * theta) / 2.0f;
                }

                if (testRestart > 0)
                {
                    //restart acceleration
                    var tmpTheta = theta < 1.0f ? ((theta * theta) / (1.0f - theta)) : theta0;
                    for (int y = 0; y < xExplore.GetLength(0); y++)
                    {
                        for (int x = 0; x < xExplore.GetLength(1); x++)
                        {
                            xExplore[y, x]   += tmpTheta * xCorrection[y, x];
                            xCorrection[y, x] = 0;
                            gExplore[y, x]   += tmpTheta * gCorrection[y, x];
                            gCorrection[y, x] = 0;
                        }
                    }

                    Console.WriteLine("restarting");
                    //new active set
                    activeSet  = GetActiveSet(xExplore, gExplore, lambda, alpha, maxLipschitz);
                    blockCount = activeSet.Count;
                    theta      = tau / (float)blockCount;
                    theta0     = theta;
                    beta       = CalcESO(tau, degreeOfSeperability, blockCount);
                    lipschitz  = maxLipschitz * yBlockSize * xBlockSize;
                    lipschitz *= (float)beta;
                }

                if (xDiffMax < epsilon)
                {
                    //converged = true;
                }
                Console.WriteLine("Done Active Set iteration " + iter);
                iter++;
            }

            return(theta);
        }