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 Tuple <double, double> EstimateObjectives(float[,] xImage, float[,] residuals, float[,] psf, float[,] xExplore, float[,] xAccelerated, float lambda, float alpha, float[,] psfCut, float[,] bMap)
        {
            Tuple <double, double> output = null;

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

            using (var residualsCalculator = new PaddedConvolver(CONVKernel, new Rectangle(0, 0, psf.GetLength(0), psf.GetLength(1))))
            {
                var residualsExplore     = new float[xImage.GetLength(0), xImage.GetLength(1)];
                var residualsAccelerated = new float[xImage.GetLength(0), xImage.GetLength(1)];
                for (int i = 0; i < xImage.GetLength(0); i++)
                {
                    for (int j = 0; j < xImage.GetLength(1); j++)
                    {
                        residualsExplore[i, j]     = xExplore[i, j] - xImage[i, j];
                        residualsAccelerated[i, j] = xAccelerated[i, j] - xImage[i, j];
                    }
                }
                residualsCalculator.ConvolveInPlace(residualsExplore);
                residualsCalculator.ConvolveInPlace(residualsAccelerated);

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

                var objectiveExplore     = Residuals.CalcPenalty(residualsExplore) + ElasticNet.CalcPenalty(xExplore, lambda, alpha);
                var objectiveAccelerated = Residuals.CalcPenalty(residualsAccelerated) + ElasticNet.CalcPenalty(xAccelerated, lambda, alpha);

                /*
                 * var CORRKernel = PSF.CalcPaddedFourierCorrelation(psfCut, new Rectangle(0, 0, residuals.GetLength(0), residuals.GetLength(1)));
                 * var bMapFull = Residuals.CalcBMap(residualsExplore, CORRKernel, new Rectangle(0, 0, psfCut.GetLength(0), psfCut.GetLength(1)));
                 * FitsIO.Write(bMap, "bMapOriginal.fits");
                 * FitsIO.Write(bMapFull, "bMapSanity.fits");
                 * var diff = new float[bMap.GetLength(0), bMap.GetLength(1)];
                 * for (int i = 0; i < xImage.GetLength(0); i++)
                 *  for (int j = 0; j < xImage.GetLength(1); j++)
                 *      diff[i, j] = bMap[i, j] - bMapFull[i, j];
                 * FitsIO.Write(diff, "bmapSanityDiff.fits");*/

                output = new Tuple <double, double>(objectiveExplore, objectiveAccelerated);
            }

            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);
        }
        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 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 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);
        }
        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 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);
        }
            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);
            }
        private static Tuple <double, double> CalcObjectives(float[,] xImage, float[,] residuals, float[,] psf, float[,] xExplore, float[,] xAccelerated, float lambda, float alpha)
        {
            Tuple <double, double> output = null;

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

            using (var residualsCalculator = new PaddedConvolver(CONVKernel, new Rectangle(0, 0, psf.GetLength(0), psf.GetLength(1))))
            {
                var residualsExplore     = new float[xImage.GetLength(0), xImage.GetLength(1)];
                var residualsAccelerated = new float[xImage.GetLength(0), xImage.GetLength(1)];
                for (int i = 0; i < xImage.GetLength(0); i++)
                {
                    for (int j = 0; j < xImage.GetLength(1); j++)
                    {
                        residualsExplore[i, j]     = xExplore[i, j] - xImage[i, j];
                        residualsAccelerated[i, j] = xAccelerated[i, j] - xImage[i, j];
                    }
                }

                residualsCalculator.ConvolveInPlace(residualsExplore);
                residualsCalculator.ConvolveInPlace(residualsAccelerated);
                for (int i = 0; i < xImage.GetLength(0); i++)
                {
                    for (int j = 0; j < xImage.GetLength(1); j++)
                    {
                        residualsExplore[i, j]     -= residuals[i, j];
                        residualsAccelerated[i, j] -= residuals[i, j];
                    }
                }

                var objectiveExplore     = Residuals.CalcPenalty(residualsExplore) + ElasticNet.CalcPenalty(xExplore, lambda, alpha);
                var objectiveAccelerated = Residuals.CalcPenalty(residualsAccelerated) + ElasticNet.CalcPenalty(xAccelerated, lambda, alpha);

                output = new Tuple <double, double>(objectiveExplore, objectiveAccelerated);
            }

            return(output);
        }
            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);
            }
        private static void ReconstructSerial(MeasurementData input, GriddingConstants c, float[,] fullPsf, string folder, string file, int processorCount)
        {
            var totalWatch   = new Stopwatch();
            var currentWatch = new Stopwatch();

            var totalSize   = new Rectangle(0, 0, c.GridSize, c.GridSize);
            var psfCut      = PSF.Cut(fullPsf, CUT_FACTOR_SERIAL);
            var maxSidelobe = PSF.CalcMaxSidelobe(fullPsf, CUT_FACTOR_SERIAL);
            var fastCD      = new FastSerialCD(totalSize, psfCut, processorCount);
            var metadata    = Partitioner.CreatePartition(c, input.UVW, input.Frequencies);

            var writer  = new StreamWriter(folder + "/" + file + ".txt");
            var psfBMap = psfCut;

            using (var bMapCalculator = new PaddedConvolver(PSF.CalcPaddedFourierCorrelation(psfBMap, totalSize), new Rectangle(0, 0, psfBMap.GetLength(0), psfBMap.GetLength(1))))
                using (var bMapCalculator2 = new PaddedConvolver(PSF.CalcPaddedFourierCorrelation(fullPsf, totalSize), new Rectangle(0, 0, fullPsf.GetLength(0), fullPsf.GetLength(1))))
                {
                    var currentBMapCalculator = bMapCalculator;

                    var maxLipschitz = PSF.CalcMaxLipschitz(psfCut);
                    var lambda       = (float)(LAMBDA * maxLipschitz);
                    var lambdaTrue   = (float)(LAMBDA * PSF.CalcMaxLipschitz(fullPsf));
                    var alpha        = ALPHA;

                    var switchedToOtherPsf         = false;
                    var xImage                     = new float[c.GridSize, c.GridSize];
                    var residualVis                = input.Visibilities;
                    DeconvolutionResult lastResult = null;
                    for (int cycle = 0; cycle < 6; cycle++)
                    {
                        Console.WriteLine("cycle " + cycle);
                        var dirtyGrid  = IDG.GridW(c, metadata, residualVis, input.UVW, input.Frequencies);
                        var dirtyImage = FFT.WStackIFFTFloat(dirtyGrid, c.VisibilitiesCount);
                        FFT.Shift(dirtyImage);
                        FitsIO.Write(dirtyImage, folder + "/dirty" + cycle + ".fits");

                        currentWatch.Restart();
                        totalWatch.Start();
                        var maxDirty         = Residuals.GetMax(dirtyImage);
                        var bMap             = bMapCalculator.Convolve(dirtyImage);
                        var maxB             = Residuals.GetMax(bMap);
                        var correctionFactor = Math.Max(maxB / (maxDirty * fastCD.MaxLipschitz), 1.0f);
                        var currentSideLobe  = maxB * maxSidelobe * correctionFactor;
                        var currentLambda    = Math.Max(currentSideLobe / alpha, lambda);


                        var objective = Residuals.CalcPenalty(dirtyImage) + ElasticNet.CalcPenalty(xImage, lambdaTrue, alpha);

                        var absMax = fastCD.GetAbsMaxDiff(xImage, bMap, lambdaTrue, alpha);

                        if (absMax >= MAJOR_STOP)
                        {
                            lastResult = fastCD.Deconvolve(xImage, bMap, currentLambda, alpha, 30000, 1e-5f);
                        }

                        if (lambda == currentLambda & !switchedToOtherPsf)
                        {
                            currentBMapCalculator = bMapCalculator2;
                            lambda             = lambdaTrue;
                            switchedToOtherPsf = true;
                        }

                        currentWatch.Stop();
                        totalWatch.Stop();
                        writer.WriteLine(cycle + ";" + currentLambda + ";" + objective + ";" + absMax + ";" + lastResult.IterationCount + ";" + totalWatch.Elapsed.TotalSeconds + ";" + currentWatch.Elapsed.TotalSeconds);
                        writer.Flush();

                        if (absMax < MAJOR_STOP)
                        {
                            break;
                        }

                        FFT.Shift(xImage);
                        var xGrid = FFT.Forward(xImage);
                        FFT.Shift(xImage);
                        var modelVis = IDG.DeGridW(c, metadata, xGrid, input.UVW, input.Frequencies);
                        residualVis = Visibilities.Substract(input.Visibilities, modelVis, input.Flags);
                    }
                }
        }
            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);
            }
示例#17
0
        private static ReconstructionInfo Reconstruct(Data input, float fullLipschitz, float[,] maskedPsf, string folder, float maskFactor, int maxMajor, string dirtyPrefix, string xImagePrefix, StreamWriter writer, double objectiveCutoff, float epsilon, bool maskPsf2)
        {
            var info      = new ReconstructionInfo();
            var totalSize = new Rectangle(0, 0, input.c.GridSize, input.c.GridSize);

            var bMapCalculator = new PaddedConvolver(PSF.CalcPaddedFourierCorrelation(maskedPsf, totalSize), new Rectangle(0, 0, maskedPsf.GetLength(0), maskedPsf.GetLength(1)));
            var maskedPsf2     = PSF.CalcPSFSquared(maskedPsf);

            if (maskPsf2)
            {
                Mask(maskedPsf2, 1e-5f);
            }
            writer.WriteLine((CountNonZero(maskedPsf2) - maskedPsf2.Length) / (double)maskedPsf2.Length);
            var fastCD = new FastSerialCD(totalSize, totalSize, maskedPsf, maskedPsf2);

            FitsIO.Write(maskedPsf, folder + maskFactor + "psf.fits");



            var lambda     = 0.4f * fastCD.MaxLipschitz;
            var lambdaTrue = 0.4f * fullLipschitz;
            var alpha      = 0.1f;

            var xImage      = new float[input.c.GridSize, input.c.GridSize];
            var residualVis = input.visibilities;
            DeconvolutionResult lastResult = null;

            for (int cycle = 0; cycle < maxMajor; cycle++)
            {
                Console.WriteLine("cycle " + cycle);
                var dirtyGrid  = IDG.GridW(input.c, input.metadata, residualVis, input.uvw, input.frequencies);
                var dirtyImage = FFT.WStackIFFTFloat(dirtyGrid, input.c.VisibilitiesCount);
                FFT.Shift(dirtyImage);
                FitsIO.Write(dirtyImage, folder + dirtyPrefix + cycle + ".fits");

                //calc data and reg penalty
                var dataPenalty       = Residuals.CalcPenalty(dirtyImage);
                var regPenalty        = ElasticNet.CalcPenalty(xImage, lambdaTrue, alpha);
                var regPenaltyCurrent = ElasticNet.CalcPenalty(xImage, lambda, alpha);
                info.lastDataPenalty = dataPenalty;
                info.lastRegPenalty  = regPenalty;

                bMapCalculator.ConvolveInPlace(dirtyImage);
                //FitsIO.Write(dirtyImage, folder + dirtyPrefix + "bmap_" + cycle + ".fits");
                var currentLambda = lambda;

                writer.Write(cycle + ";" + currentLambda + ";" + Residuals.GetMax(dirtyImage) + ";" + dataPenalty + ";" + regPenalty + ";" + regPenaltyCurrent + ";");
                writer.Flush();

                //check wether we can minimize the objective further with the current psf
                var objectiveReached = (dataPenalty + regPenalty) < objectiveCutoff;
                var minimumReached   = (lastResult != null && lastResult.IterationCount < 100 && lastResult.Converged);
                if (!objectiveReached & !minimumReached)
                {
                    info.totalDeconv.Start();
                    lastResult = fastCD.Deconvolve(xImage, dirtyImage, currentLambda, alpha, 50000, epsilon);
                    info.totalDeconv.Stop();

                    FitsIO.Write(xImage, folder + xImagePrefix + cycle + ".fits");
                    writer.Write(lastResult.Converged + ";" + lastResult.IterationCount + ";" + lastResult.ElapsedTime.TotalSeconds + "\n");
                    writer.Flush();

                    FFT.Shift(xImage);
                    var xGrid = FFT.Forward(xImage);
                    FFT.Shift(xImage);
                    var modelVis = IDG.DeGridW(input.c, input.metadata, xGrid, input.uvw, input.frequencies);
                    residualVis = Visibilities.Substract(input.visibilities, modelVis, input.flags);
                }
                else
                {
                    writer.Write(false + ";0;0");
                    writer.Flush();
                    break;
                }
            }

            return(info);
        }
        private static ReconstructionInfo ReconstructGradientApprox(Data input, float[,] fullPsf, string folder, int cutFactor, int maxMajor, string dirtyPrefix, string xImagePrefix, StreamWriter writer, double objectiveCutoff, float epsilon)
        {
            var info            = new ReconstructionInfo();
            var psfCut          = PSF.Cut(fullPsf, cutFactor);
            var maxSidelobe     = PSF.CalcMaxSidelobe(fullPsf, cutFactor);
            var totalSize       = new Rectangle(0, 0, input.c.GridSize, input.c.GridSize);
            var psfBMap         = psfCut;
            var bMapCalculator  = new PaddedConvolver(PSF.CalcPaddedFourierCorrelation(psfBMap, totalSize), new Rectangle(0, 0, psfBMap.GetLength(0), psfBMap.GetLength(1)));
            var bMapCalculator2 = new PaddedConvolver(PSF.CalcPaddedFourierCorrelation(fullPsf, totalSize), new Rectangle(0, 0, fullPsf.GetLength(0), fullPsf.GetLength(1)));
            var fastCD          = new FastSerialCD(totalSize, psfCut);
            var fastCD2         = new FastSerialCD(totalSize, psfCut);

            fastCD2.ResetLipschitzMap(fullPsf);
            FitsIO.Write(psfCut, folder + cutFactor + "psf.fits");

            var lambda     = LAMBDA_GLOBAL * fastCD.MaxLipschitz;
            var lambdaTrue = (float)(LAMBDA_GLOBAL * PSF.CalcMaxLipschitz(fullPsf));

            var xImage      = new float[input.c.GridSize, input.c.GridSize];
            var residualVis = input.visibilities;
            DeconvolutionResult lastResult = null;
            var firstTimeConverged         = false;
            var lastLambda = 0.0f;

            for (int cycle = 0; cycle < maxMajor; cycle++)
            {
                Console.WriteLine("cycle " + cycle);
                var dirtyGrid  = IDG.GridW(input.c, input.metadata, residualVis, input.uvw, input.frequencies);
                var dirtyImage = FFT.WStackIFFTFloat(dirtyGrid, input.c.VisibilitiesCount);
                FFT.Shift(dirtyImage);
                FitsIO.Write(dirtyImage, folder + dirtyPrefix + cycle + ".fits");

                //calc data and reg penalty
                var dataPenalty       = Residuals.CalcPenalty(dirtyImage);
                var regPenalty        = ElasticNet.CalcPenalty(xImage, lambdaTrue, alpha);
                var regPenaltyCurrent = ElasticNet.CalcPenalty(xImage, lambda, alpha);
                info.lastDataPenalty = dataPenalty;
                info.lastRegPenalty  = regPenalty;

                var maxDirty = Residuals.GetMax(dirtyImage);
                var bMap     = bMapCalculator.Convolve(dirtyImage);
                FitsIO.Write(bMap, folder + dirtyPrefix + "bmap_" + cycle + ".fits");
                var maxB             = Residuals.GetMax(bMap);
                var correctionFactor = Math.Max(maxB / (maxDirty * fastCD.MaxLipschitz), 1.0f);
                var currentSideLobe  = maxB * maxSidelobe * correctionFactor;
                var currentLambda    = Math.Max(currentSideLobe / alpha, lambda);

                writer.Write(cycle + ";" + currentLambda + ";" + currentSideLobe + ";" + ";" + fastCD2.GetAbsMaxDiff(xImage, bMap, lambdaTrue, alpha) + ";" + dataPenalty + ";" + regPenalty + ";" + regPenaltyCurrent + ";");;
                writer.Flush();

                //check wether we can minimize the objective further with the current psf
                var objectiveReached = (dataPenalty + regPenalty) < objectiveCutoff;
                var minimumReached   = (lastResult != null && lastResult.Converged && fastCD2.GetAbsMaxDiff(xImage, dirtyImage, lambdaTrue, alpha) < MAJOR_EPSILON && currentLambda == lambda);
                if (lambda == lastLambda & !firstTimeConverged)
                {
                    firstTimeConverged = true;
                    minimumReached     = false;
                }

                if (!objectiveReached & !minimumReached)
                {
                    //writer.Write(firstTimeConverged + ";");
                    //writer.Flush();
                    info.totalDeconv.Start();
                    if (!firstTimeConverged)
                    {
                        lastResult = fastCD.Deconvolve(xImage, bMap, currentLambda, alpha, 30000, epsilon);
                    }
                    else
                    {
                        bMap = bMapCalculator2.Convolve(dirtyImage);
                        //FitsIO.Write(bMap, folder + dirtyPrefix + "bmap_" + cycle + "_full.fits");
                        maxB             = Residuals.GetMax(bMap);
                        correctionFactor = Math.Max(maxB / (maxDirty * fastCD2.MaxLipschitz), 1.0f);
                        currentSideLobe  = maxB * maxSidelobe * correctionFactor;
                        currentLambda    = Math.Max(currentSideLobe / alpha, lambdaTrue);
                        info.totalDeconv.Start();
                        lastResult = fastCD.Deconvolve(xImage, bMap, currentLambda, alpha, 30000, epsilon);
                        info.totalDeconv.Stop();
                    }

                    info.totalDeconv.Stop();

                    FitsIO.Write(xImage, folder + xImagePrefix + cycle + ".fits");
                    writer.Write(lastResult.Converged + ";" + lastResult.IterationCount + ";" + lastResult.ElapsedTime.TotalSeconds + "\n");
                    writer.Flush();

                    FFT.Shift(xImage);
                    var xGrid = FFT.Forward(xImage);
                    FFT.Shift(xImage);
                    var modelVis = IDG.DeGridW(input.c, input.metadata, xGrid, input.uvw, input.frequencies);
                    residualVis = Visibilities.Substract(input.visibilities, modelVis, input.flags);
                }
                else
                {
                    writer.Write(false + ";0;0\n");
                    writer.Flush();

                    break;
                }

                lastLambda = currentLambda;
            }

            bMapCalculator.Dispose();
            bMapCalculator2.Dispose();

            return(info);
        }
        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 bool DeconvolveGreedy(float[,] xImage, float[,] residuals, float[,] psf, float lambda, float alpha, Random random, int blockSize, int threadCount, int maxIteration = 100, float epsilon = 1e-4f)
        {
            var xExplore    = Copy(xImage);
            var xCorrection = new float[xImage.GetLength(0), xImage.GetLength(1)];

            var PSFCorr = PSF.CalcPaddedFourierCorrelation(psf, new Rectangle(0, 0, residuals.GetLength(0), residuals.GetLength(1)));

            //calculate gradients for each pixel
            var gExplore    = Residuals.CalcGradientMap(residuals, PSFCorr, new Rectangle(0, 0, psf.GetLength(0), psf.GetLength(1)));
            var gCorrection = new float[residuals.GetLength(0), residuals.GetLength(1)];
            var psf2        = PSF.CalcPSFSquared(psf);

            FitsIO.Write(gExplore, "gExplore.fits");

            yBlockSize           = blockSize;
            xBlockSize           = blockSize;
            degreeOfSeperability = CountNonZero(psf);
            tau = threadCount; //number of processors
            var maxLipschitz = (float)PSF.CalcMaxLipschitz(psf);

            var theta = Greedy(xExplore, xCorrection, gExplore, gCorrection, psf2, maxLipschitz, lambda, alpha, random, maxIteration, epsilon);

            //decide which version should be taken#
            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)));
            var theta0   = tau / (float)(xExplore.Length / (yBlockSize * xBlockSize));
            var tmpTheta = theta < 1.0f ? ((theta * theta) / (1.0f - theta)) : theta0;

            //calculate residuals
            var residualsExplore     = Copy(xExplore);
            var residualsAccelerated = Copy(xExplore);

            for (int i = 0; i < xImage.GetLength(0); i++)
            {
                for (int j = 0; j < xImage.GetLength(1); j++)
                {
                    residualsExplore[i, j]     -= xImage[i, j];
                    residualsAccelerated[i, j] += tmpTheta * xCorrection[i, j] - xImage[i, j];
                    xCorrection[i, j]           = tmpTheta * xCorrection[i, j] + xExplore[i, j];
                }
            }

            FitsIO.Write(xExplore, "xExplore.fits");
            FitsIO.Write(xCorrection, "xAcc.fits");

            residualsCalculator.ConvolveInPlace(residualsExplore);
            residualsCalculator.ConvolveInPlace(residualsAccelerated);
            for (int i = 0; i < xImage.GetLength(0); i++)
            {
                for (int j = 0; j < xImage.GetLength(1); j++)
                {
                    residualsExplore[i, j]     -= residuals[i, j];
                    residualsAccelerated[i, j] -= residuals[i, j];
                }
            }

            var objectiveExplore = Residuals.CalcPenalty(residualsExplore) + ElasticNet.CalcPenalty(xExplore, lambda, alpha);
            var objectiveAcc     = Residuals.CalcPenalty(residualsAccelerated) + ElasticNet.CalcPenalty(xCorrection, lambda, alpha);

            if (objectiveAcc < objectiveExplore)
            {
                for (int i = 0; i < xImage.GetLength(0); i++)
                {
                    for (int j = 0; j < xImage.GetLength(1); j++)
                    {
                        xImage[i, j] = xCorrection[i, j];
                    }
                }
            }
            else
            {
                for (int i = 0; i < xImage.GetLength(0); i++)
                {
                    for (int j = 0; j < xImage.GetLength(1); j++)
                    {
                        xImage[i, j] = xExplore[i, j];
                    }
                }
            }

            return(objectiveAcc < objectiveExplore);
        }
示例#21
0
        public static void DebugSimulatedApprox()
        {
            var    frequencies  = FitsIO.ReadFrequencies(@"C:\dev\GitHub\p9-data\small\fits\simulation_point\freq.fits");
            var    uvw          = FitsIO.ReadUVW(@"C:\dev\GitHub\p9-data\small\fits\simulation_point\uvw.fits");
            var    flags        = new bool[uvw.GetLength(0), uvw.GetLength(1), frequencies.Length]; //completely unflagged dataset
            double norm         = 2.0;
            var    visibilities = FitsIO.ReadVisibilities(@"C:\dev\GitHub\p9-data\small\fits\simulation_point\vis.fits", uvw.GetLength(0), uvw.GetLength(1), frequencies.Length, norm);

            var    visibilitiesCount = visibilities.Length;
            int    gridSize          = 256;
            int    subgridsize       = 8;
            int    kernelSize        = 4;
            int    max_nr_timesteps  = 1024;
            double cellSize          = 1.0 / 3600.0 * PI / 180.0;
            var    c = new GriddingConstants(visibilitiesCount, gridSize, subgridsize, kernelSize, max_nr_timesteps, (float)cellSize, 1, 0.0f);

            var watchTotal     = new Stopwatch();
            var watchForward   = new Stopwatch();
            var watchBackwards = new Stopwatch();
            var watchDeconv    = new Stopwatch();

            watchTotal.Start();
            var metadata = Partitioner.CreatePartition(c, uvw, frequencies);

            var psfGrid = IDG.GridPSF(c, metadata, uvw, flags, frequencies);
            var psf     = FFT.BackwardFloat(psfGrid, c.VisibilitiesCount);

            FFT.Shift(psf);
            var psfCut = PSF.Cut(psf);

            FitsIO.Write(psfCut, "psfCut.fits");

            var random         = new Random(123);
            var totalSize      = new Rectangle(0, 0, gridSize, gridSize);
            var bMapCalculator = new PaddedConvolver(PSF.CalcPaddedFourierCorrelation(psfCut, totalSize), new Rectangle(0, 0, psfCut.GetLength(0), psfCut.GetLength(1)));
            var fastCD         = new FastSerialCD(totalSize, psfCut);
            //fastCD.ResetAMap(psf);
            var lambda  = 0.5f * fastCD.MaxLipschitz;
            var alpha   = 0.8f;
            var approx  = new ApproxParallel();
            var approx2 = new ApproxFast(totalSize, psfCut, 4, 8, 0f, 0.25f, false, true);

            var xImage      = new float[gridSize, gridSize];
            var residualVis = visibilities;

            /*var truth = new double[gridSize, gridSize];
             * truth[30, 30] = 1.0;
             * truth[35, 36] = 1.5;
             * var truthVis = IDG.ToVisibilities(c, metadata, truth, uvw, frequencies);
             * visibilities = truthVis;
             * var residualVis = truthVis;*/
            var data = new ApproxFast.TestingData(new StreamWriter("approxConvergence.txt"));

            for (int cycle = 0; cycle < 4; cycle++)
            {
                //FORWARD
                watchForward.Start();
                var dirtyGrid  = IDG.Grid(c, metadata, residualVis, uvw, frequencies);
                var dirtyImage = FFT.BackwardFloat(dirtyGrid, c.VisibilitiesCount);
                FFT.Shift(dirtyImage);
                FitsIO.Write(dirtyImage, "dirty_" + cycle + ".fits");
                watchForward.Stop();

                //DECONVOLVE
                watchDeconv.Start();
                //approx.ISTAStep(xImage, dirtyImage, psf, lambda, alpha);
                //FitsIO.Write(xImage, "xIsta.fits");
                //FitsIO.Write(dirtyImage, "dirtyFista.fits");
                //bMapCalculator.ConvolveInPlace(dirtyImage);
                //FitsIO.Write(dirtyImage, "bMap_" + cycle + ".fits");
                //var result = fastCD.Deconvolve(xImage, dirtyImage, 0.5f * fastCD.MaxLipschitz, 0.8f, 1000, 1e-4f);
                //var converged = approx.DeconvolveActiveSet(xImage, dirtyImage, psfCut, lambda, alpha, random, 8, 1, 1);
                //var converged = approx.DeconvolveGreedy(xImage, dirtyImage, psfCut, lambda, alpha, random, 4, 4, 500);
                //var converged = approx.DeconvolveApprox(xImage, dirtyImage, psfCut, lambda, alpha, random, 1, threads, 500, 1e-4f, cycle == 0);

                approx2.DeconvolveTest(data, cycle, 0, xImage, dirtyImage, psfCut, psf, lambda, alpha, random, 10, 1e-4f);


                if (data.converged)
                {
                    Console.WriteLine("-----------------------------CONVERGED!!!!------------------------");
                }
                else
                {
                    Console.WriteLine("-------------------------------not converged----------------------");
                }
                FitsIO.Write(xImage, "xImageApprox_" + cycle + ".fits");
                watchDeconv.Stop();

                //BACKWARDS
                watchBackwards.Start();
                FFT.Shift(xImage);
                var xGrid = FFT.Forward(xImage);
                FFT.Shift(xImage);
                var modelVis = IDG.DeGrid(c, metadata, xGrid, uvw, frequencies);
                residualVis = Visibilities.Substract(visibilities, modelVis, flags);
                watchBackwards.Stop();
            }


            var dirtyGridCheck = IDG.Grid(c, metadata, residualVis, uvw, frequencies);
            var dirtyCheck     = FFT.Backward(dirtyGridCheck, c.VisibilitiesCount);

            FFT.Shift(dirtyCheck);

            var l2Penalty      = Residuals.CalcPenalty(ToFloatImage(dirtyCheck));
            var elasticPenalty = ElasticNet.CalcPenalty(xImage, (float)lambda, (float)alpha);
            var sum            = l2Penalty + elasticPenalty;

            data.writer.Close();
        }
        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);
        }
        private static void ReconstructPCDM(MeasurementData input, GriddingConstants c, float[,] fullPsf, string folder, string file, int minorCycles, float searchPercent, int processorCount)
        {
            var totalWatch   = new Stopwatch();
            var currentWatch = new Stopwatch();

            var totalSize    = new Rectangle(0, 0, c.GridSize, c.GridSize);
            var psfCut       = PSF.Cut(fullPsf, CUT_FACTOR_PCDM);
            var maxSidelobe  = PSF.CalcMaxSidelobe(fullPsf, CUT_FACTOR_PCDM);
            var sidelobeHalf = PSF.CalcMaxSidelobe(fullPsf, 2);
            var random       = new Random(123);
            var pcdm         = new ParallelCoordinateDescent(totalSize, psfCut, 1, 1000, searchPercent);

            var metadata = Partitioner.CreatePartition(c, input.UVW, input.Frequencies);

            using (var bMapCalculator = new PaddedConvolver(PSF.CalcPaddedFourierCorrelation(psfCut, totalSize), new Rectangle(0, 0, psfCut.GetLength(0), psfCut.GetLength(1))))
                using (var bMapCalculator2 = new PaddedConvolver(PSF.CalcPaddedFourierCorrelation(fullPsf, totalSize), new Rectangle(0, 0, fullPsf.GetLength(0), fullPsf.GetLength(1))))
                    using (var residualsConvolver = new PaddedConvolver(totalSize, fullPsf))
                    {
                        var currentBMapCalculator = bMapCalculator;

                        var maxLipschitz = PSF.CalcMaxLipschitz(psfCut);
                        var lambda       = (float)(LAMBDA * maxLipschitz);
                        var lambdaTrue   = (float)(LAMBDA * PSF.CalcMaxLipschitz(fullPsf));
                        var alpha        = ALPHA;

                        var switchedToOtherPsf = false;
                        var writer             = new StreamWriter(folder + "/" + file + ".txt");
                        var xImage             = new float[c.GridSize, c.GridSize];
                        var residualVis        = input.Visibilities;
                        ParallelCoordinateDescent.PCDMStatistics lastResult = null;
                        for (int cycle = 0; cycle < 6; cycle++)
                        {
                            Console.WriteLine("Beginning Major cycle " + cycle);
                            var dirtyGrid  = IDG.GridW(c, metadata, residualVis, input.UVW, input.Frequencies);
                            var dirtyImage = FFT.WStackIFFTFloat(dirtyGrid, c.VisibilitiesCount);
                            FFT.Shift(dirtyImage);
                            FitsIO.Write(dirtyImage, folder + "/dirty" + cycle + ".fits");

                            currentWatch.Restart();
                            totalWatch.Start();

                            var breakMajor       = false;
                            var minLambda        = 0.0f;
                            var dirtyCopy        = Copy(dirtyImage);
                            var xCopy            = Copy(xImage);
                            var currentLambda    = 0f;
                            var currentObjective = 0.0;
                            var absMax           = 0.0f;
                            for (int minorCycle = 0; minorCycle < minorCycles; minorCycle++)
                            {
                                Console.WriteLine("Beginning Minor Cycle " + minorCycle);
                                var maxDirty         = Residuals.GetMax(dirtyImage);
                                var bMap             = currentBMapCalculator.Convolve(dirtyImage);
                                var maxB             = Residuals.GetMax(bMap);
                                var correctionFactor = Math.Max(maxB / (maxDirty * maxLipschitz), 1.0f);
                                var currentSideLobe  = maxB * maxSidelobe * correctionFactor;
                                currentLambda = (float)Math.Max(currentSideLobe / alpha, lambda);

                                if (minorCycle == 0)
                                {
                                    minLambda = (float)(maxB * sidelobeHalf * correctionFactor / alpha);
                                }
                                if (currentLambda < minLambda)
                                {
                                    currentLambda = minLambda;
                                }
                                currentObjective = Residuals.CalcPenalty(dirtyImage) + ElasticNet.CalcPenalty(xImage, lambdaTrue, alpha);
                                absMax           = pcdm.GetAbsMax(xImage, bMap, lambdaTrue, alpha);
                                if (absMax < MAJOR_STOP)
                                {
                                    breakMajor = true;
                                    break;
                                }

                                lastResult = pcdm.Deconvolve(xImage, bMap, currentLambda, alpha, 100, 1e-5f);

                                if (currentLambda == lambda | currentLambda == minLambda)
                                {
                                    break;
                                }

                                var residualsUpdate = new float[xImage.GetLength(0), xImage.GetLength(1)];
                                Parallel.For(0, xCopy.GetLength(0), (i) =>
                                {
                                    for (int j = 0; j < xCopy.GetLength(1); j++)
                                    {
                                        residualsUpdate[i, j] = xImage[i, j] - xCopy[i, j];
                                    }
                                });
                                residualsConvolver.ConvolveInPlace(residualsUpdate);
                                Parallel.For(0, xCopy.GetLength(0), (i) =>
                                {
                                    for (int j = 0; j < xCopy.GetLength(1); j++)
                                    {
                                        dirtyImage[i, j] = dirtyCopy[i, j] - residualsUpdate[i, j];
                                    }
                                });
                            }

                            currentWatch.Stop();
                            totalWatch.Stop();
                            writer.WriteLine(cycle + ";" + currentLambda + ";" + currentObjective + ";" + absMax + ";" + lastResult.IterationCount + ";" + totalWatch.Elapsed.TotalSeconds + ";" + currentWatch.Elapsed.TotalSeconds);
                            writer.Flush();

                            FitsIO.Write(xImage, folder + "/xImage_pcdm_" + cycle + ".fits");

                            if (breakMajor)
                            {
                                break;
                            }
                            if (currentLambda == lambda & !switchedToOtherPsf)
                            {
                                pcdm.ResetAMap(fullPsf);
                                currentBMapCalculator = bMapCalculator2;
                                lambda             = lambdaTrue;
                                switchedToOtherPsf = true;
                                writer.WriteLine("switched");
                                writer.Flush();
                            }

                            FFT.Shift(xImage);
                            var xGrid = FFT.Forward(xImage);
                            FFT.Shift(xImage);
                            var modelVis = IDG.DeGridW(c, metadata, xGrid, input.UVW, input.Frequencies);
                            residualVis = Visibilities.Substract(input.Visibilities, modelVis, input.Flags);
                        }

                        writer.Close();
                    }
        }
示例#24
0
        /// <summary>
        /// Major cycle implemnentation for the parallel coordinate descent algorithm
        /// </summary>
        /// <param name="data"></param>
        /// <param name="c"></param>
        /// <param name="psfCutFactor"></param>
        /// <param name="maxMajorCycle"></param>
        /// <param name="maxMinorCycle"></param>
        /// <param name="lambda"></param>
        /// <param name="alpha"></param>
        /// <param name="deconvIterations"></param>
        /// <param name="deconvEpsilon"></param>
        public static void ReconstructPCDM(string obsName, MeasurementData data, GriddingConstants c, int psfCutFactor, int maxMajorCycle, int maxMinorCycle, float lambda, float alpha, int deconvIterations, float deconvEpsilon)
        {
            var metadata = Partitioner.CreatePartition(c, data.UVW, data.Frequencies);
            var psfVis   = new Complex[data.UVW.GetLength(0), data.UVW.GetLength(1), data.Frequencies.Length];

            for (int i = 0; i < data.Visibilities.GetLength(0); i++)
            {
                for (int j = 0; j < data.Visibilities.GetLength(1); j++)
                {
                    for (int k = 0; k < data.Visibilities.GetLength(2); k++)
                    {
                        if (!data.Flags[i, j, k])
                        {
                            psfVis[i, j, k] = new Complex(1.0, 0);
                        }
                        else
                        {
                            psfVis[i, j, k] = new Complex(0, 0);
                        }
                    }
                }
            }

            Console.WriteLine("gridding psf");
            var psfGrid = IDG.Grid(c, metadata, psfVis, data.UVW, data.Frequencies);
            var psf     = FFT.BackwardFloat(psfGrid, c.VisibilitiesCount);

            FFT.Shift(psf);

            var totalWatch   = new Stopwatch();
            var currentWatch = new Stopwatch();

            var totalSize    = new Rectangle(0, 0, c.GridSize, c.GridSize);
            var psfCut       = PSF.Cut(psf, psfCutFactor);
            var maxSidelobe  = PSF.CalcMaxSidelobe(psf, psfCutFactor);
            var sidelobeHalf = PSF.CalcMaxSidelobe(psf, 2);

            var pcdm = new ParallelCoordinateDescent(totalSize, psfCut, Environment.ProcessorCount, 1000);

            using (var gCalculator = new PaddedConvolver(PSF.CalcPaddedFourierCorrelation(psfCut, totalSize), new Rectangle(0, 0, psfCut.GetLength(0), psfCut.GetLength(1))))
                using (var gCalculator2 = new PaddedConvolver(PSF.CalcPaddedFourierCorrelation(psf, totalSize), new Rectangle(0, 0, psf.GetLength(0), psf.GetLength(1))))
                    using (var residualsConvolver = new PaddedConvolver(totalSize, psf))
                    {
                        var currentGCalculator = gCalculator;

                        var maxLipschitz    = PSF.CalcMaxLipschitz(psfCut);
                        var lambdaLipschitz = (float)(lambda * maxLipschitz);
                        var lambdaTrue      = (float)(lambda * PSF.CalcMaxLipschitz(psf));

                        var switchedToOtherPsf = false;
                        var xImage             = new float[c.GridSize, c.GridSize];
                        var residualVis        = data.Visibilities;
                        ParallelCoordinateDescent.PCDMStatistics lastResult = null;
                        for (int cycle = 0; cycle < maxMajorCycle; cycle++)
                        {
                            Console.WriteLine("Beginning Major cycle " + cycle);
                            var dirtyGrid  = IDG.GridW(c, metadata, residualVis, data.UVW, data.Frequencies);
                            var dirtyImage = FFT.WStackIFFTFloat(dirtyGrid, c.VisibilitiesCount);
                            FFT.Shift(dirtyImage);
                            FitsIO.Write(dirtyImage, obsName + "_dirty_pcdm_majorCycle" + cycle + ".fits");

                            currentWatch.Restart();
                            totalWatch.Start();

                            var breakMajor       = false;
                            var minLambda        = 0.0f;
                            var dirtyCopy        = Copy(dirtyImage);
                            var xCopy            = Copy(xImage);
                            var currentLambda    = 0f;
                            var currentObjective = 0.0;
                            var absMax           = 0.0f;
                            for (int minorCycle = 0; minorCycle < maxMinorCycle; minorCycle++)
                            {
                                Console.WriteLine("Beginning Minor Cycle " + minorCycle);
                                var maxDirty         = Residuals.GetMax(dirtyImage);
                                var bMap             = currentGCalculator.Convolve(dirtyImage);
                                var maxB             = Residuals.GetMax(bMap);
                                var correctionFactor = Math.Max(maxB / (maxDirty * maxLipschitz), 1.0f);
                                var currentSideLobe  = maxB * maxSidelobe * correctionFactor;
                                currentLambda = (float)Math.Max(currentSideLobe / alpha, lambdaLipschitz);

                                if (minorCycle == 0)
                                {
                                    minLambda = (float)(maxB * sidelobeHalf * correctionFactor / alpha);
                                }
                                if (currentLambda < minLambda)
                                {
                                    currentLambda = minLambda;
                                }
                                currentObjective = Residuals.CalcPenalty(dirtyImage) + ElasticNet.CalcPenalty(xImage, lambdaTrue, alpha);
                                absMax           = pcdm.GetAbsMax(xImage, bMap, lambdaTrue, alpha);
                                if (absMax < MAJOR_EPSILON)
                                {
                                    breakMajor = true;
                                    break;
                                }

                                lastResult = pcdm.Deconvolve(xImage, bMap, currentLambda, alpha, 40, deconvEpsilon);

                                if (currentLambda == lambda | currentLambda == minLambda)
                                {
                                    break;
                                }

                                var residualsUpdate = new float[xImage.GetLength(0), xImage.GetLength(1)];
                                Parallel.For(0, xCopy.GetLength(0), (i) =>
                                {
                                    for (int j = 0; j < xCopy.GetLength(1); j++)
                                    {
                                        residualsUpdate[i, j] = xImage[i, j] - xCopy[i, j];
                                    }
                                });
                                residualsConvolver.ConvolveInPlace(residualsUpdate);
                                Parallel.For(0, xCopy.GetLength(0), (i) =>
                                {
                                    for (int j = 0; j < xCopy.GetLength(1); j++)
                                    {
                                        dirtyImage[i, j] = dirtyCopy[i, j] - residualsUpdate[i, j];
                                    }
                                });
                            }

                            currentWatch.Stop();
                            totalWatch.Stop();

                            if (breakMajor)
                            {
                                break;
                            }
                            if (currentLambda == lambda & !switchedToOtherPsf)
                            {
                                pcdm.ResetAMap(psf);
                                currentGCalculator = gCalculator2;
                                lambda             = lambdaTrue;
                                switchedToOtherPsf = true;
                            }

                            FitsIO.Write(xImage, obsName + "_model_pcdm_majorCycle" + cycle + ".fits");

                            FFT.Shift(xImage);
                            var xGrid = FFT.Forward(xImage);
                            FFT.Shift(xImage);
                            var modelVis = IDG.DeGridW(c, metadata, xGrid, data.UVW, data.Frequencies);
                            residualVis = Visibilities.Substract(data.Visibilities, modelVis, data.Flags);
                        }

                        Console.WriteLine("Reconstruction finished in (seconds): " + totalWatch.Elapsed.TotalSeconds);
                    }
        }
示例#25
0
        /// <summary>
        /// Major cycle implementation for the Serial CD
        /// </summary>
        /// <param name="obsName"></param>
        /// <param name="data"></param>
        /// <param name="c"></param>
        /// <param name="useGPU"></param>
        /// <param name="psfCutFactor"></param>
        /// <param name="maxMajorCycle"></param>
        /// <param name="lambda"></param>
        /// <param name="alpha"></param>
        /// <param name="deconvIterations"></param>
        /// <param name="deconvEpsilon"></param>
        public static void ReconstructSerialCD(string obsName, MeasurementData data, GriddingConstants c, bool useGPU, int psfCutFactor, int maxMajorCycle, float lambda, float alpha, int deconvIterations, float deconvEpsilon)
        {
            var metadata = Partitioner.CreatePartition(c, data.UVW, data.Frequencies);
            var psfVis   = new Complex[data.UVW.GetLength(0), data.UVW.GetLength(1), data.Frequencies.Length];

            for (int i = 0; i < data.Visibilities.GetLength(0); i++)
            {
                for (int j = 0; j < data.Visibilities.GetLength(1); j++)
                {
                    for (int k = 0; k < data.Visibilities.GetLength(2); k++)
                    {
                        if (!data.Flags[i, j, k])
                        {
                            psfVis[i, j, k] = new Complex(1.0, 0);
                        }
                        else
                        {
                            psfVis[i, j, k] = new Complex(0, 0);
                        }
                    }
                }
            }

            Console.WriteLine("gridding psf");
            var psfGrid = IDG.GridW(c, metadata, psfVis, data.UVW, data.Frequencies);
            var psf     = FFT.WStackIFFTFloat(psfGrid, c.VisibilitiesCount);

            FFT.Shift(psf);

            var totalWatch   = new Stopwatch();
            var currentWatch = new Stopwatch();

            var totalSize   = new Rectangle(0, 0, c.GridSize, c.GridSize);
            var psfCut      = PSF.Cut(psf, psfCutFactor);
            var maxSidelobe = PSF.CalcMaxSidelobe(psf, psfCutFactor);

            IDeconvolver deconvolver = null;

            if (useGPU & GPUSerialCD.IsGPUSupported())
            {
                deconvolver = new GPUSerialCD(totalSize, psfCut, 1000);
            }
            else if (useGPU & !GPUSerialCD.IsGPUSupported())
            {
                Console.WriteLine("GPU not supported by library. Switching to CPU implementation");
                deconvolver = new FastSerialCD(totalSize, psfCut);
            }
            else
            {
                deconvolver = new FastSerialCD(totalSize, psfCut);
            }

            var psfBMap = psfCut;

            using (var gCalculator = new PaddedConvolver(PSF.CalcPaddedFourierCorrelation(psfBMap, totalSize), new Rectangle(0, 0, psfBMap.GetLength(0), psfBMap.GetLength(1))))
                using (var gCalculator2 = new PaddedConvolver(PSF.CalcPaddedFourierCorrelation(psf, totalSize), new Rectangle(0, 0, psf.GetLength(0), psf.GetLength(1))))
                {
                    var currentGCalculator = gCalculator;
                    var maxLipschitz       = PSF.CalcMaxLipschitz(psfCut);
                    var lambdaLipschitz    = (float)(lambda * maxLipschitz);
                    var lambdaTrue         = (float)(lambda * PSF.CalcMaxLipschitz(psf));
                    var switchedToOtherPsf = false;

                    var xImage      = new float[c.GridSize, c.GridSize];
                    var residualVis = data.Visibilities;
                    DeconvolutionResult lastResult = null;
                    for (int cycle = 0; cycle < maxMajorCycle; cycle++)
                    {
                        Console.WriteLine("Beginning Major cycle " + cycle);
                        var dirtyGrid  = IDG.GridW(c, metadata, residualVis, data.UVW, data.Frequencies);
                        var dirtyImage = FFT.WStackIFFTFloat(dirtyGrid, c.VisibilitiesCount);
                        FFT.Shift(dirtyImage);
                        FitsIO.Write(dirtyImage, obsName + "_dirty_serial_majorCycle" + cycle + ".fits");

                        currentWatch.Restart();
                        totalWatch.Start();
                        var maxDirty         = Residuals.GetMax(dirtyImage);
                        var gradients        = gCalculator.Convolve(dirtyImage);
                        var maxB             = Residuals.GetMax(gradients);
                        var correctionFactor = Math.Max(maxB / (maxDirty * maxLipschitz), 1.0f);
                        var currentSideLobe  = maxB * maxSidelobe * correctionFactor;
                        var currentLambda    = (float)Math.Max(currentSideLobe / alpha, lambdaLipschitz);

                        var objective = Residuals.CalcPenalty(dirtyImage) + ElasticNet.CalcPenalty(xImage, lambdaTrue, alpha);

                        var absMax = deconvolver.GetAbsMaxDiff(xImage, gradients, lambdaTrue, alpha);

                        if (absMax >= MAJOR_EPSILON)
                        {
                            lastResult = deconvolver.Deconvolve(xImage, gradients, currentLambda, alpha, deconvIterations, deconvEpsilon);
                        }

                        if (lambda == currentLambda & !switchedToOtherPsf)
                        {
                            currentGCalculator = gCalculator2;
                            lambda             = lambdaTrue;
                            maxLipschitz       = PSF.CalcMaxLipschitz(psf);
                            switchedToOtherPsf = true;
                        }

                        FitsIO.Write(xImage, obsName + "_model_serial_majorCycle" + cycle + ".fits");

                        currentWatch.Stop();
                        totalWatch.Stop();

                        if (absMax < MAJOR_EPSILON)
                        {
                            break;
                        }

                        FFT.Shift(xImage);
                        var xGrid = FFT.Forward(xImage);
                        FFT.Shift(xImage);
                        var modelVis = IDG.DeGridW(c, metadata, xGrid, data.UVW, data.Frequencies);
                        residualVis = Visibilities.Substract(data.Visibilities, modelVis, data.Flags);
                    }

                    Console.WriteLine("Reconstruction finished in (seconds): " + totalWatch.Elapsed.TotalSeconds);
                }
        }