public void lbfgsTest()
        {
            Func<double[], double> f = rosenbrockFunction;
            Func<double[], double[]> g = rosenbrockGradient;

            Assert.AreEqual(104, f(new[] { -1.0, 2.0 }));


            int n = 2; // number of variables
            double[] initial = { -1.2, 1 };

            BroydenFletcherGoldfarbShanno lbfgs = new BroydenFletcherGoldfarbShanno(n, f, g);

            double actual = lbfgs.Minimize(initial);
            double expected = 0;
            Assert.AreEqual(expected, actual, 1e-10);

            double[] result = lbfgs.Solution;

            Assert.AreEqual(49, lbfgs.Evaluations);
            Assert.AreEqual(40, lbfgs.Iterations);
            Assert.AreEqual(0.99999999999963229, result[0]);
            Assert.AreEqual(0.99999999999924027, result[1]);

            double y = f(result);
            double[] d = g(result);

            Assert.AreEqual(1.9432410039142452E-25, y);
            Assert.AreEqual(0.0000000000089901419642010907, d[0]);
            Assert.AreEqual(-0.0000000000048627768478581856, d[1]);
        }
        public void ConstructorTest2()
        {
            Function function = // min f(x) = 10 * (x+1)^2 + y^2
                x => 10.0 * Math.Pow(x[0] + 1.0, 2.0) + Math.Pow(x[1], 2.0);

            Gradient gradient = x => new[] { 20 * (x[0] + 1), 2 * x[1] };


            double[] start = new double[2];

            BroydenFletcherGoldfarbShanno target = new BroydenFletcherGoldfarbShanno(2,
                function.Invoke, gradient.Invoke);

            Assert.IsTrue(target.Minimize());
            double minimum = target.Value;

            double[] solution = target.Solution;

            Assert.AreEqual(0, minimum, 1e-10);
            Assert.AreEqual(-1, solution[0], 1e-5);
            Assert.AreEqual(0, solution[1], 1e-5);

            double expectedMinimum = function(target.Solution);
            Assert.AreEqual(expectedMinimum, minimum);
        }
        public void lbfgsTest()
        {
            Func<double[], double> f = rosenbrockFunction;
            Func<double[], double[]> g = rosenbrockGradient;

            Assert.AreEqual(104, f(new[] { -1.0, 2.0 }));


            int n = 2; // number of variables
            double[] initial = { -1.2, 1 };

            var lbfgs = new BroydenFletcherGoldfarbShanno(n, f, g);

            double expected = 0;
            Assert.IsTrue(lbfgs.Minimize(initial));

            bool success = lbfgs.Minimize();
            double actual = lbfgs.Value;
            Assert.IsTrue(success);

            Assert.AreEqual(expected, actual, 1e-10);

            double[] result = lbfgs.Solution;

            Assert.AreEqual(1.0, result[0], 1e-5);
            Assert.AreEqual(1.0, result[1], 1e-5);

            double y = f(result);
            double[] d = g(result);

            Assert.AreEqual(0, y, 1e-10);
            Assert.AreEqual(0, d[0], 1e-6);
            Assert.AreEqual(0, d[1], 1e-6);
        }
        /// <summary>
        ///   Creates a new instance of the Augmented Lagrangian algorithm.
        /// </summary>
        ///
        /// <param name="numberOfVariables">The number of free parameters in the optimization problem.</param>
        /// <param name="constraints">The <see cref="NonlinearConstraint"/>s to which the solution must be subjected.</param>
        ///
        public AugmentedLagrangianSolver(int numberOfVariables,
                                         IEnumerable <NonlinearConstraint> constraints)
        {
            var innerSolver = new BroydenFletcherGoldfarbShanno(numberOfVariables);

            init(numberOfVariables, constraints, innerSolver);
        }
Example #5
0
        private void init(NonlinearObjectiveFunction function,
                          IEnumerable <NonlinearConstraint> constraints, IGradientOptimizationMethod innerSolver)
        {
            if (function != null)
            {
                if (function.NumberOfVariables != NumberOfVariables)
                {
                    throw new ArgumentOutOfRangeException("function",
                                                          "Incorrect number of variables in the objective function. " +
                                                          "The number of variables must match the number of variables set in the solver.");
                }

                this.Function = function.Function;
                this.Gradient = function.Gradient;
            }

            if (innerSolver == null)
            {
                innerSolver = new BroydenFletcherGoldfarbShanno(NumberOfVariables)
                {
                    LineSearch  = Optimization.LineSearch.BacktrackingArmijo,
                    Corrections = 10
                };
            }

            List <NonlinearConstraint> equality    = new List <NonlinearConstraint>();
            List <NonlinearConstraint> lesserThan  = new List <NonlinearConstraint>();
            List <NonlinearConstraint> greaterThan = new List <NonlinearConstraint>();

            foreach (var c in constraints)
            {
                switch (c.ShouldBe)
                {
                case ConstraintType.EqualTo:
                    equality.Add(c); break;

                case ConstraintType.GreaterThanOrEqualTo:
                    greaterThan.Add(c); break;

                case ConstraintType.LesserThanOrEqualTo:
                    lesserThan.Add(c); break;

                default:
                    throw new ArgumentException("Unknown constraint type.", "constraints");
                }
            }

            this.lesserThanConstraints  = lesserThan.ToArray();
            this.greaterThanConstraints = greaterThan.ToArray();
            this.equalityConstraints    = equality.ToArray();

            this.lambda = new double[equalityConstraints.Length];
            this.mu     = new double[lesserThanConstraints.Length];
            this.nu     = new double[greaterThanConstraints.Length];

            this.dualSolver     = innerSolver;
            dualSolver.Function = objectiveFunction;
            dualSolver.Gradient = objectiveGradient;
        }
Example #6
0
File: Layer.cs Project: Diover/nets
        public static void Test()
        {
            var m = Matrix.Create(1, 10, 1.0);
            var v = Matrix.Create(10, 1, 1.0);

            var p = m.Multiply(v);

            Func<double[], double> f = (x) =>
                                       -Math.Exp(-Math.Pow(x[0] - 1, 2)) - Math.Exp(-0.5*Math.Pow(x[1] - 2, 2));
            Func<double[], double[]> g = (x) => new[]
                {
                    // df/dx = {-2 e^(-    (x-1)^2) (x-1)}
                    2*Math.Exp(-Math.Pow(x[0] - 1, 2))*(x[0] - 1),

                    // df/dy = {-  e^(-1/2 (y-2)^2) (y-2)}
                    Math.Exp(-0.5*Math.Pow(x[1] - 2, 2))*(x[1] - 2)
                };

            var lbfgs = new BroydenFletcherGoldfarbShanno(2, f, g);
            lbfgs.Progress += (sender, args) =>
                {
                    //var grad = args.Gradient;
                    //var sol = args.Solution;
                    //var step = args.Step;
                    Console.WriteLine("Iteration: {0}, solution: {1}, {2}", args.Iteration, args.Solution[0], args.Solution[1]);
                };
            bool runAgain;
            do
            {
                try
                {
                    lbfgs.Minimize();
                    runAgain = false;
                }
                catch (LineSearchFailedException)
                {
                    Console.WriteLine("Failed");
                    runAgain = true;
                }
            } while (runAgain);

            double minValue = lbfgs.Value;
            double[] solution = lbfgs.Solution;
        }
Example #7
0
        /// <summary>
        ///   Creates a new instance of the Augmented Lagrangian algorithm.
        /// </summary>
        ///
        /// <param name="numberOfVariables">The number of free parameters in the optimization problem.</param>
        /// <param name="constraints">The <see cref="NonlinearConstraint"/>s to which the solution must be subjected.</param>
        ///
        public AugmentedLagrangianSolver(int numberOfVariables, IEnumerable <NonlinearConstraint> constraints)
        {
            this.numberOfVariables = numberOfVariables;

            List <NonlinearConstraint> equality    = new List <NonlinearConstraint>();
            List <NonlinearConstraint> lesserThan  = new List <NonlinearConstraint>();
            List <NonlinearConstraint> greaterThan = new List <NonlinearConstraint>();

            foreach (var c in constraints)
            {
                switch (c.ShouldBe)
                {
                case ConstraintType.EqualTo:
                    equality.Add(c); break;

                case ConstraintType.GreaterThanOrEqualTo:
                    greaterThan.Add(c); break;

                case ConstraintType.LesserThanOrEqualTo:
                    lesserThan.Add(c); break;

                default:
                    throw new ArgumentException("Unknown constraint type.", "constraints");
                }
            }

            this.lesserThanConstraints  = lesserThan.ToArray();
            this.greaterThanConstraints = greaterThan.ToArray();
            this.equalityConstraints    = equality.ToArray();

            this.Solution = new double[numberOfVariables];

            dualSolver          = new BroydenFletcherGoldfarbShanno(numberOfVariables);
            dualSolver.Function = objectiveFunction;
            dualSolver.Gradient = objectiveGradient;

            //for (int i = 0; i < Solution.Length; i++)
            //    Solution[i] = Accord.Math.Tools.Random.NextDouble() * 2 - 1;
        }
Example #8
0
        public virtual void ProcessCTF(MapHeader originalHeader, Image originalStack, bool doastigmatism, decimal scaleFactor)
        {
            if (!Directory.Exists(PowerSpectrumDir))
                Directory.CreateDirectory(PowerSpectrumDir);

            //CTF = new CTF();
            PS1D = null;
            _SimulatedBackground = null;
            _SimulatedScale = new Cubic1D(new[] { new float2(0, 1), new float2(1, 1) });

            #region Dimensions and grids

            int NFrames = originalHeader.Dimensions.Z;
            int2 DimsImage = new int2(originalHeader.Dimensions);
            int2 DimsRegion = new int2(MainWindow.Options.CTFWindow, MainWindow.Options.CTFWindow);

            float OverlapFraction = 0.5f;
            int2 DimsPositionGrid;
            int3[] PositionGrid = Helper.GetEqualGridSpacing(DimsImage, new int2(DimsRegion.X / 1, DimsRegion.Y / 1), OverlapFraction, out DimsPositionGrid);
            int NPositions = (int)DimsPositionGrid.Elements();

            int CTFGridX = Math.Min(DimsPositionGrid.X, MainWindow.Options.GridCTFX);
            int CTFGridY = Math.Min(DimsPositionGrid.Y, MainWindow.Options.GridCTFY);
            int CTFGridZ = Math.Min(NFrames, MainWindow.Options.GridCTFZ);
            GridCTF = new CubicGrid(new int3(CTFGridX, CTFGridY, CTFGridZ));
            GridCTFPhase = new CubicGrid(new int3(1, 1, CTFGridZ));

            bool CTFSpace = CTFGridX * CTFGridY > 1;
            bool CTFTime = CTFGridZ > 1;
            int3 CTFSpectraGrid = new int3(CTFSpace ? DimsPositionGrid.X : 1,
                                           CTFSpace ? DimsPositionGrid.Y : 1,
                                           CTFTime ? CTFGridZ : 1);

            int MinFreqInclusive = (int)(MainWindow.Options.CTFRangeMin * DimsRegion.X / 2);
            int MaxFreqExclusive = (int)(MainWindow.Options.CTFRangeMax * DimsRegion.X / 2);
            int NFreq = MaxFreqExclusive - MinFreqInclusive;

            float PixelSize = (float)(MainWindow.Options.CTFPixelMin + MainWindow.Options.CTFPixelMax) * 0.5f;
            float PixelDelta = (float)(MainWindow.Options.CTFPixelMax - MainWindow.Options.CTFPixelMin) * 0.5f;
            float PixelAngle = (float)MainWindow.Options.CTFPixelAngle / 180f * (float)Math.PI;

            #endregion

            #region Allocate GPU memory

            Image CTFSpectra = new Image(IntPtr.Zero, new int3(DimsRegion.X, DimsRegion.X, (int)CTFSpectraGrid.Elements()), true);
            Image CTFMean = new Image(IntPtr.Zero, new int3(DimsRegion), true);
            Image CTFCoordsCart = new Image(new int3(DimsRegion), true, true);
            Image CTFCoordsPolarTrimmed = new Image(new int3(NFreq, DimsRegion.X, 1), false, true);

            #endregion

            // Extract movie regions, create individual spectra in Cartesian coordinates and their mean.

            #region Create spectra

            GPU.CreateSpectra(originalStack.GetDevice(Intent.Read),
                              DimsImage,
                              NFrames,
                              PositionGrid,
                              NPositions,
                              DimsRegion,
                              CTFSpectraGrid,
                              CTFSpectra.GetDevice(Intent.Write),
                              CTFMean.GetDevice(Intent.Write));
            originalStack.FreeDevice(); // Won't need it in this method anymore.

            #endregion

            // Populate address arrays for later.

            #region Init addresses

            {
                float2[] CoordsData = new float2[CTFCoordsCart.ElementsSliceComplex];

                Helper.ForEachElementFT(DimsRegion, (x, y, xx, yy, r, a) => CoordsData[y * (DimsRegion.X / 2 + 1) + x] = new float2(r, a));
                CTFCoordsCart.UpdateHostWithComplex(new[] { CoordsData });

                CoordsData = new float2[NFreq * DimsRegion.X];
                Helper.ForEachElement(CTFCoordsPolarTrimmed.DimsSlice, (x, y) =>
                {
                    float Angle = ((float)y / DimsRegion.X + 0.5f) * (float)Math.PI;
                    float Ny = 1f / DimsRegion.X;
                    CoordsData[y * NFreq + x] = new float2((x + MinFreqInclusive) * Ny, Angle);
                });
                CTFCoordsPolarTrimmed.UpdateHostWithComplex(new[] { CoordsData });
            }

            #endregion

            // Retrieve average 1D spectrum from CTFMean (not corrected for astigmatism yet).

            #region Initial 1D spectrum

            {
                Image CTFAverage1D = new Image(IntPtr.Zero, new int3(DimsRegion.X / 2, 1, 1));

                GPU.CTFMakeAverage(CTFMean.GetDevice(Intent.Read),
                                   CTFCoordsCart.GetDevice(Intent.Read),
                                   (uint)CTFMean.ElementsSliceReal,
                                   (uint)DimsRegion.X,
                                   new[] { new CTF().ToStruct() },
                                   new CTF().ToStruct(),
                                   0,
                                   (uint)DimsRegion.X / 2,
                                   null,
                                   1,
                                   CTFAverage1D.GetDevice(Intent.Write));

                //CTFAverage1D.WriteMRC("CTFAverage1D.mrc");

                float[] CTFAverage1DData = CTFAverage1D.GetHost(Intent.Read)[0];
                float2[] ForPS1D = new float2[DimsRegion.X / 2];
                for (int i = 0; i < ForPS1D.Length; i++)
                    ForPS1D[i] = new float2((float)i / DimsRegion.X, (float)Math.Round(CTFAverage1DData[i], 4));
                _PS1D = ForPS1D;

                CTFAverage1D.Dispose();
            }

            #endregion

            #region Background fitting methods

            Action UpdateBackgroundFit = () =>
            {
                float2[] ForPS1D = PS1D.Skip(Math.Max(5, MinFreqInclusive / 2)).ToArray();
                Cubic1D.FitCTF(ForPS1D,
                               v => v.Select(x => CTF.Get1D(x / (float)CTF.PixelSize, true)).ToArray(),
                               CTF.GetZeros(),
                               CTF.GetPeaks(),
                               out _SimulatedBackground,
                               out _SimulatedScale);
            };

            Action<bool> UpdateRotationalAverage = keepbackground =>
            {
                float[] MeanData = CTFMean.GetHost(Intent.Read)[0];

                Image CTFMeanCorrected = new Image(new int3(DimsRegion), true);
                float[] MeanCorrectedData = CTFMeanCorrected.GetHost(Intent.Write)[0];

                // Subtract current background estimate from spectra, populate coords.
                Helper.ForEachElementFT(DimsRegion,
                                        (x, y, xx, yy, r, a) =>
                                        {
                                            int i = y * (DimsRegion.X / 2 + 1) + x;
                                            MeanCorrectedData[i] = MeanData[i] - _SimulatedBackground.Interp(r / DimsRegion.X);
                                        });

                Image CTFAverage1D = new Image(IntPtr.Zero, new int3(DimsRegion.X / 2, 1, 1));

                GPU.CTFMakeAverage(CTFMeanCorrected.GetDevice(Intent.Read),
                                   CTFCoordsCart.GetDevice(Intent.Read),
                                   (uint)CTFMeanCorrected.DimsEffective.ElementsSlice(),
                                   (uint)DimsRegion.X,
                                   new[] { CTF.ToStruct() },
                                   CTF.ToStruct(),
                                   0,
                                   (uint)DimsRegion.X / 2,
                                   null,
                                   1,
                                   CTFAverage1D.GetDevice(Intent.Write));

                //CTFAverage1D.WriteMRC("CTFAverage1D.mrc");

                float[] RotationalAverageData = CTFAverage1D.GetHost(Intent.Read)[0];
                float2[] ForPS1D = new float2[PS1D.Length];
                if (keepbackground)
                    for (int i = 0; i < ForPS1D.Length; i++)
                        ForPS1D[i] = new float2((float)i / DimsRegion.X, RotationalAverageData[i] + _SimulatedBackground.Interp((float)i / DimsRegion.X));
                else
                    for (int i = 0; i < ForPS1D.Length; i++)
                        ForPS1D[i] = new float2((float)i / DimsRegion.X, RotationalAverageData[i]);
                MathHelper.UnNaN(ForPS1D);

                _PS1D = ForPS1D;

                CTFMeanCorrected.Dispose();
                CTFAverage1D.Dispose();
            };

            #endregion

            // Fit background to currently best average (not corrected for astigmatism yet).
            {
                float2[] ForPS1D = PS1D.Skip(MinFreqInclusive).Take(Math.Max(2, NFreq / 2)).ToArray();

                int NumNodes = Math.Max(3, (int)((MainWindow.Options.CTFRangeMax - MainWindow.Options.CTFRangeMin) * 5M));
                _SimulatedBackground = Cubic1D.Fit(ForPS1D, NumNodes); // This won't fit falloff and scale, because approx function is 0

                float[] CurrentBackground = _SimulatedBackground.Interp(PS1D.Select(p => p.X).ToArray()).Skip(MinFreqInclusive).Take(NFreq / 2).ToArray();
                float[] Subtracted1D = new float[ForPS1D.Length];
                for (int i = 0; i < ForPS1D.Length; i++)
                    Subtracted1D[i] = ForPS1D[i].Y - CurrentBackground[i];
                MathHelper.NormalizeInPlace(Subtracted1D);

                float ZMin = (float)MainWindow.Options.CTFZMin;
                float ZMax = (float)MainWindow.Options.CTFZMax;
                float ZStep = (ZMax - ZMin) / 100f;

                float BestZ = 0, BestPhase = 0, BestScore = -999;
                for (float z = ZMin; z <= ZMax + 1e-5f; z += ZStep)
                {
                    for (float p = 0; p <= (MainWindow.Options.CTFDoPhase ? 1f : 0f); p += 0.01f)
                    {
                        CTF CurrentParams = new CTF
                        {
                            PixelSize = (MainWindow.Options.CTFPixelMin + MainWindow.Options.CTFPixelMax) * 0.5M,

                            Defocus = (decimal)z,
                            PhaseShift = (decimal)p,

                            Cs = MainWindow.Options.CTFCs,
                            Voltage = MainWindow.Options.CTFVoltage,
                            Amplitude = MainWindow.Options.CTFAmplitude
                        };
                        float[] SimulatedCTF = CurrentParams.Get1D(PS1D.Length, true).Skip(MinFreqInclusive).Take(Math.Max(2, NFreq / 2)).ToArray();
                        MathHelper.NormalizeInPlace(SimulatedCTF);
                        float Score = MathHelper.CrossCorrelate(Subtracted1D, SimulatedCTF);
                        if (Score > BestScore)
                        {
                            BestScore = Score;
                            BestZ = z;
                            BestPhase = p;
                        }
                    }
                }

                CTF = new CTF
                {
                    PixelSize = (MainWindow.Options.CTFPixelMin + MainWindow.Options.CTFPixelMax) * 0.5M,

                    Defocus = (decimal)BestZ,
                    PhaseShift = (decimal)BestPhase,

                    Cs = MainWindow.Options.CTFCs,
                    Voltage = MainWindow.Options.CTFVoltage,
                    Amplitude = MainWindow.Options.CTFAmplitude
                };

                UpdateRotationalAverage(true); // This doesn't have a nice background yet.
                UpdateBackgroundFit(); // Now get a reasonably nice background.
            }

            

            // Fit defocus, (phase shift), (astigmatism) to average background-subtracted spectrum, 
            // which is in polar coords at this point (for equal weighting of all frequencies).

            #region Grid search

            {
                Image CTFMeanPolarTrimmed = CTFMean.AsPolar((uint)MinFreqInclusive, (uint)(MinFreqInclusive + NFreq / 1));

                // Subtract current background.
                Image CurrentBackground = new Image(_SimulatedBackground.Interp(PS1D.Select(p => p.X).ToArray()).Skip(MinFreqInclusive).Take(NFreq / 1).ToArray());
                CTFMeanPolarTrimmed.SubtractFromLines(CurrentBackground);
                CurrentBackground.Dispose();

                /*Image WaterMask = new Image(new int3(NFreq, 1, 1));
                float[] WaterData = WaterMask.GetHost(Intent.Write)[0];
                for (int i = 0; i < NFreq; i++)
                {
                    float f = (i + MinFreqInclusive) / (float)DimsRegion.X * 2f;
                    WaterData[i] = f > 0.2f && f < 0.6f ? 0f : 1f;
                }
                //CTFMeanPolarTrimmed.MultiplyLines(WaterMask);
                WaterMask.Dispose();*/

                // Normalize for CC (not strictly needed, but it's converted for fp16 later, so let's be on the safe side of the fp16 range.
                GPU.Normalize(CTFMeanPolarTrimmed.GetDevice(Intent.Read), CTFMeanPolarTrimmed.GetDevice(Intent.Write), (uint)CTFMeanPolarTrimmed.ElementsReal, 1);
                //CTFMeanPolarTrimmed.WriteMRC("ctfmeanpolartrimmed.mrc");

                CTF StartParams = new CTF
                {
                    PixelSize = (MainWindow.Options.CTFPixelMin + MainWindow.Options.CTFPixelMax) * 0.5M,
                    PixelSizeDelta = Math.Abs(MainWindow.Options.CTFPixelMax - MainWindow.Options.CTFPixelMin),
                    PixelSizeAngle = MainWindow.Options.CTFPixelAngle,

                    Defocus = CTF.Defocus,// (MainWindow.Options.CTFZMin + MainWindow.Options.CTFZMax) * 0.5M,
                    DefocusDelta = doastigmatism ? 0 : MainWindow.Options.CTFAstigmatism,
                    DefocusAngle = doastigmatism ? 0 : MainWindow.Options.CTFAstigmatismAngle,

                    Cs = MainWindow.Options.CTFCs,
                    Voltage = MainWindow.Options.CTFVoltage,
                    Amplitude = MainWindow.Options.CTFAmplitude
                };

                CTFFitStruct FitParams = new CTFFitStruct
                {
                    //Pixelsize = new float3(-0.02e-10f, 0.02e-10f, 0.01e-10f),
                    //Pixeldelta = new float3(0.0f, 0.02e-10f, 0.01e-10f),
                    //Pixelangle = new float3(0, 2 * (float)Math.PI, 1 * (float)Math.PI / 18),

                    //Defocus = new float3((float)(MainWindow.Options.CTFZMin - StartParams.Defocus) * 1e-6f,
                    //                     (float)(MainWindow.Options.CTFZMax - StartParams.Defocus) * 1e-6f,
                    //                     0.025e-6f),
                    Defocus = new float3(-0.4e-6f,
                                         0.4e-6f,
                                         0.025e-6f),

                    Defocusdelta = doastigmatism ? new float3(0, 0.8e-6f, 0.02e-6f) : new float3(0, 0, 0),
                    Astigmatismangle = doastigmatism ? new float3(0, 2 * (float)Math.PI, 1 * (float)Math.PI / 18) : new float3(0, 0, 0),
                    Phaseshift = MainWindow.Options.CTFDoPhase ? new float3(0, (float)Math.PI, 0.025f * (float)Math.PI) : new float3(0, 0, 0)
                };

                CTFStruct ResultStruct = GPU.CTFFitMean(CTFMeanPolarTrimmed.GetDevice(Intent.Read),
                                                        CTFCoordsPolarTrimmed.GetDevice(Intent.Read),
                                                        CTFMeanPolarTrimmed.DimsSlice,
                                                        StartParams.ToStruct(),
                                                        FitParams,
                                                        doastigmatism);
                CTF.FromStruct(ResultStruct);
                CTF.Defocus = Math.Max(CTF.Defocus, MainWindow.Options.CTFZMin);

                CTFMeanPolarTrimmed.Dispose();

                UpdateRotationalAverage(true); // This doesn't have a nice background yet.
                UpdateBackgroundFit(); // Now get a reasonably nice background.

                UpdateRotationalAverage(true); // This time, with the nice background.
                UpdateBackgroundFit(); // Make the background even nicer!
            }

            #endregion

            /*for (int i = 0; i < PS1D.Length; i++)
                PS1D[i].Y -= SimulatedBackground.Interp(PS1D[i].X);
            SimulatedBackground = new Cubic1D(SimulatedBackground.Data.Select(v => new float2(v.X, 0f)).ToArray());
            OnPropertyChanged("PS1D");

            CTFSpectra.Dispose();
            CTFMean.Dispose();
            CTFCoordsCart.Dispose();
            CTFCoordsPolarTrimmed.Dispose();

            Simulated1D = GetSimulated1D();
            CTFQuality = GetCTFQuality();

            return;*/

            // Do BFGS optimization of defocus, astigmatism and phase shift,
            // using 2D simulation for comparison

            #region BFGS

            bool[] CTFSpectraConsider = new bool[CTFSpectraGrid.Elements()];
            for (int i = 0; i < CTFSpectraConsider.Length; i++)
                CTFSpectraConsider[i] = true;
            int NCTFSpectraConsider = CTFSpectraConsider.Length;

            GridCTF = new CubicGrid(GridCTF.Dimensions, (float)CTF.Defocus, (float)CTF.Defocus, Dimension.X);
            GridCTFPhase = new CubicGrid(GridCTFPhase.Dimensions, (float)CTF.PhaseShift, (float)CTF.PhaseShift, Dimension.X);

            for (int preciseFit = 2; preciseFit < 3; preciseFit++)
            {
                NFreq = (MaxFreqExclusive - MinFreqInclusive) * (preciseFit + 1) / 3;
                //if (preciseFit >= 2)
                //    NFreq = MaxFreqExclusive - MinFreqInclusive;

                Image CTFSpectraPolarTrimmed = CTFSpectra.AsPolar((uint)MinFreqInclusive, (uint)(MinFreqInclusive + NFreq));
                CTFSpectra.FreeDevice(); // This will only be needed again for the final PS1D.

                #region Create background and scale

                float[] CurrentScale = _SimulatedScale.Interp(PS1D.Select(p => p.X).ToArray());

                Image CTFSpectraScale = new Image(new int3(NFreq, DimsRegion.X, 1));
                float[] CTFSpectraScaleData = CTFSpectraScale.GetHost(Intent.Write)[0];

                // Trim polar to relevant frequencies, and populate coordinates.
                Parallel.For(0, DimsRegion.X, y =>
                {
                    float Angle = ((float)y / DimsRegion.X + 0.5f) * (float)Math.PI;
                    for (int x = 0; x < NFreq; x++)
                        CTFSpectraScaleData[y * NFreq + x] = CurrentScale[x + MinFreqInclusive];
                });
                //CTFSpectraScale.WriteMRC("ctfspectrascale.mrc");

                // Background is just 1 line since we're in polar.
                Image CurrentBackground = new Image(_SimulatedBackground.Interp(PS1D.Select(p => p.X).ToArray()).Skip(MinFreqInclusive).Take(NFreq).ToArray());

                #endregion

                CTFSpectraPolarTrimmed.SubtractFromLines(CurrentBackground);
                CurrentBackground.Dispose();

                // Normalize background-subtracted spectra.
                GPU.Normalize(CTFSpectraPolarTrimmed.GetDevice(Intent.Read),
                              CTFSpectraPolarTrimmed.GetDevice(Intent.Write),
                              (uint)CTFSpectraPolarTrimmed.ElementsSliceReal,
                              (uint)CTFSpectraGrid.Elements());
                //CTFSpectraPolarTrimmed.WriteMRC("ctfspectrapolartrimmed.mrc");

                #region Convert to fp16

                Image CTFSpectraPolarTrimmedHalf = CTFSpectraPolarTrimmed.AsHalf();
                CTFSpectraPolarTrimmed.Dispose();

                Image CTFSpectraScaleHalf = CTFSpectraScale.AsHalf();
                CTFSpectraScale.Dispose();
                Image CTFCoordsPolarTrimmedHalf = CTFCoordsPolarTrimmed.AsHalf();

                #endregion

                // Wiggle weights show how the defocus on the spectra grid is altered 
                // by changes in individual anchor points of the spline grid.
                // They are used later to compute the dScore/dDefocus values for each spectrum 
                // only once, and derive the values for each anchor point from them.
                float[][] WiggleWeights = GridCTF.GetWiggleWeights(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, 1f / (CTFGridZ + 1)));
                float[][] WiggleWeightsPhase = GridCTFPhase.GetWiggleWeights(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, 1f / (CTFGridZ + 1)));

                // Helper method for getting CTFStructs for the entire spectra grid.
                Func<double[], CTF, float[], float[], CTFStruct[]> EvalGetCTF = (input, ctf, defocusValues, phaseValues) =>
                {
                    decimal AlteredDelta = (decimal)input[input.Length - 2];
                    decimal AlteredAngle = (decimal)(input[input.Length - 1] * 20 / (Math.PI / 180));

                    CTF Local = ctf.GetCopy();
                    Local.DefocusDelta = AlteredDelta;
                    Local.DefocusAngle = AlteredAngle;

                    CTFStruct LocalStruct = Local.ToStruct();
                    CTFStruct[] LocalParams = new CTFStruct[defocusValues.Length];
                    for (int i = 0; i < LocalParams.Length; i++)
                    {
                        LocalParams[i] = LocalStruct;
                        LocalParams[i].Defocus = defocusValues[i] * -1e-6f;
                        LocalParams[i].PhaseShift = phaseValues[i] * (float)Math.PI;
                    }

                    return LocalParams;
                };

                // Simulate with adjusted CTF, compare to originals

                #region Eval and Gradient methods

                float BorderZ = 0.5f / CTFGridZ;

                Func<double[], double> Eval = input =>
                {
                    CubicGrid Altered = new CubicGrid(GridCTF.Dimensions, input.Take((int)GridCTF.Dimensions.Elements()).Select(v => (float)v).ToArray());
                    float[] DefocusValues = Altered.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, BorderZ));
                    CubicGrid AlteredPhase = new CubicGrid(GridCTFPhase.Dimensions, input.Skip((int)GridCTF.Dimensions.Elements()).Take((int)GridCTFPhase.Dimensions.Elements()).Select(v => (float)v).ToArray());
                    float[] PhaseValues = AlteredPhase.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, BorderZ));

                    CTFStruct[] LocalParams = EvalGetCTF(input, CTF, DefocusValues, PhaseValues);

                    float[] Result = new float[LocalParams.Length];

                    GPU.CTFCompareToSim(CTFSpectraPolarTrimmedHalf.GetDevice(Intent.Read),
                                        CTFCoordsPolarTrimmedHalf.GetDevice(Intent.Read),
                                        CTFSpectraScaleHalf.GetDevice(Intent.Read),
                                        (uint)CTFSpectraPolarTrimmedHalf.ElementsSliceReal,
                                        LocalParams,
                                        Result,
                                        (uint)LocalParams.Length);

                    float Score = 0;
                    for (int i = 0; i < Result.Length; i++)
                        if (CTFSpectraConsider[i])
                            Score += Result[i];

                    Score /= NCTFSpectraConsider;

                    if (float.IsNaN(Score) || float.IsInfinity(Score))
                        throw new Exception("Bad score.");

                    return (1.0 - Score) * 1000.0;
                };

                Func<double[], double[]> Gradient = input =>
                {
                    const float Step = 0.005f;
                    double[] Result = new double[input.Length];

                    // In 0D grid case, just get gradient for all 4 parameters.
                    // In 1+D grid case, do simple gradient for astigmatism and phase...
                    int StartComponent = input.Length - 2;
                    //int StartComponent = 0;
                    for (int i = StartComponent; i < input.Length; i++)
                    {
                        double[] UpperInput = new double[input.Length];
                        input.CopyTo(UpperInput, 0);
                        UpperInput[i] += Step;
                        double UpperValue = Eval(UpperInput);

                        double[] LowerInput = new double[input.Length];
                        input.CopyTo(LowerInput, 0);
                        LowerInput[i] -= Step;
                        double LowerValue = Eval(LowerInput);

                        Result[i] = (UpperValue - LowerValue) / (2f * Step);
                    }

                    float[] ResultPlus = new float[CTFSpectraGrid.Elements()];
                    float[] ResultMinus = new float[CTFSpectraGrid.Elements()];

                    // ..., take shortcut for defoci...
                    {
                        CubicGrid AlteredPhase = new CubicGrid(GridCTFPhase.Dimensions, input.Skip((int)GridCTF.Dimensions.Elements()).Take((int)GridCTFPhase.Dimensions.Elements()).Select(v => (float)v).ToArray());
                        float[] PhaseValues = AlteredPhase.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, BorderZ));

                        {
                            CubicGrid AlteredPlus = new CubicGrid(GridCTF.Dimensions, input.Take((int)GridCTF.Dimensions.Elements()).Select(v => (float)v + Step).ToArray());
                            float[] DefocusValues = AlteredPlus.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, BorderZ));

                            CTFStruct[] LocalParams = EvalGetCTF(input, CTF, DefocusValues, PhaseValues);

                            GPU.CTFCompareToSim(CTFSpectraPolarTrimmedHalf.GetDevice(Intent.Read),
                                                CTFCoordsPolarTrimmedHalf.GetDevice(Intent.Read),
                                                CTFSpectraScaleHalf.GetDevice(Intent.Read),
                                                (uint)CTFSpectraPolarTrimmedHalf.ElementsSliceReal,
                                                LocalParams,
                                                ResultPlus,
                                                (uint)LocalParams.Length);
                        }
                        {
                            CubicGrid AlteredMinus = new CubicGrid(GridCTF.Dimensions, input.Take((int)GridCTF.Dimensions.Elements()).Select(v => (float)v - Step).ToArray());
                            float[] DefocusValues = AlteredMinus.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, BorderZ));

                            CTFStruct[] LocalParams = EvalGetCTF(input, CTF, DefocusValues, PhaseValues);

                            GPU.CTFCompareToSim(CTFSpectraPolarTrimmedHalf.GetDevice(Intent.Read),
                                                CTFCoordsPolarTrimmedHalf.GetDevice(Intent.Read),
                                                CTFSpectraScaleHalf.GetDevice(Intent.Read),
                                                (uint)CTFSpectraPolarTrimmedHalf.ElementsSliceReal,
                                                LocalParams,
                                                ResultMinus,
                                                (uint)LocalParams.Length);
                        }
                        float[] LocalGradients = new float[ResultPlus.Length];
                        for (int i = 0; i < LocalGradients.Length; i++)
                            LocalGradients[i] = ResultMinus[i] - ResultPlus[i];

                        // Now compute gradients per grid anchor point using the precomputed individual gradients and wiggle factors.
                        Parallel.For(0, GridCTF.Dimensions.Elements(), i => Result[i] = MathHelper.ReduceWeighted(LocalGradients, WiggleWeights[i]) / LocalGradients.Length / (2f * Step) * 1000f);
                    }

                    // ..., and take shortcut for phases.
                    if (MainWindow.Options.CTFDoPhase)
                    {
                        CubicGrid AlteredPlus = new CubicGrid(GridCTF.Dimensions, input.Take((int)GridCTF.Dimensions.Elements()).Select(v => (float)v).ToArray());
                        float[] DefocusValues = AlteredPlus.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, BorderZ));

                        {
                            CubicGrid AlteredPhasePlus = new CubicGrid(GridCTFPhase.Dimensions, input.Skip((int)GridCTF.Dimensions.Elements()).Take((int)GridCTFPhase.Dimensions.Elements()).Select(v => (float)v + Step).ToArray());
                            float[] PhaseValues = AlteredPhasePlus.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, BorderZ));
                            CTFStruct[] LocalParams = EvalGetCTF(input, CTF, DefocusValues, PhaseValues);

                            GPU.CTFCompareToSim(CTFSpectraPolarTrimmedHalf.GetDevice(Intent.Read),
                                                CTFCoordsPolarTrimmedHalf.GetDevice(Intent.Read),
                                                CTFSpectraScaleHalf.GetDevice(Intent.Read),
                                                (uint)CTFSpectraPolarTrimmedHalf.ElementsSliceReal,
                                                LocalParams,
                                                ResultPlus,
                                                (uint)LocalParams.Length);
                        }
                        {
                            CubicGrid AlteredPhaseMinus = new CubicGrid(GridCTFPhase.Dimensions, input.Skip((int)GridCTF.Dimensions.Elements()).Take((int)GridCTFPhase.Dimensions.Elements()).Select(v => (float)v - Step).ToArray());
                            float[] PhaseValues = AlteredPhaseMinus.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, BorderZ));
                            CTFStruct[] LocalParams = EvalGetCTF(input, CTF, DefocusValues, PhaseValues);

                            GPU.CTFCompareToSim(CTFSpectraPolarTrimmedHalf.GetDevice(Intent.Read),
                                                CTFCoordsPolarTrimmedHalf.GetDevice(Intent.Read),
                                                CTFSpectraScaleHalf.GetDevice(Intent.Read),
                                                (uint)CTFSpectraPolarTrimmedHalf.ElementsSliceReal,
                                                LocalParams,
                                                ResultMinus,
                                                (uint)LocalParams.Length);
                        }
                        float[] LocalGradients = new float[ResultPlus.Length];
                        for (int i = 0; i < LocalGradients.Length; i++)
                            LocalGradients[i] = ResultMinus[i] - ResultPlus[i];

                        // Now compute gradients per grid anchor point using the precomputed individual gradients and wiggle factors.
                        Parallel.For(0, GridCTFPhase.Dimensions.Elements(), i => Result[i + GridCTF.Dimensions.Elements()] = MathHelper.ReduceWeighted(LocalGradients, WiggleWeightsPhase[i]) / LocalGradients.Length / (2f * Step) * 1000f);
                    }

                    foreach (var i in Result)
                        if (double.IsNaN(i) || double.IsInfinity(i))
                            throw new Exception("Bad score.");

                    return Result;
                };

                #endregion

                #region Minimize first time with potential outpiers

                double[] StartParams = new double[GridCTF.Dimensions.Elements() + GridCTFPhase.Dimensions.Elements() + 2];
                for (int i = 0; i < GridCTF.Dimensions.Elements(); i++)
                    StartParams[i] = GridCTF.FlatValues[i];
                for (int i = 0; i < GridCTFPhase.Dimensions.Elements(); i++)
                    StartParams[i + GridCTF.Dimensions.Elements()] = GridCTFPhase.FlatValues[i];
                StartParams[StartParams.Length - 2] = (double)CTF.DefocusDelta;
                StartParams[StartParams.Length - 1] = (double)CTF.DefocusAngle / 20 * (Math.PI / 180);

                // Compute correlation for individual spectra, and throw away those that are >.75 sigma worse than mean.

                #region Discard outliers

                if (CTFSpace || CTFTime)
                {
                    CubicGrid Altered = new CubicGrid(GridCTF.Dimensions, StartParams.Take((int)GridCTF.Dimensions.Elements()).Select(v => (float)v).ToArray());
                    float[] DefocusValues = Altered.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, BorderZ));
                    CubicGrid AlteredPhase = new CubicGrid(GridCTFPhase.Dimensions, StartParams.Skip((int)GridCTF.Dimensions.Elements()).Take((int)GridCTFPhase.Dimensions.Elements()).Select(v => (float)v).ToArray());
                    float[] PhaseValues = AlteredPhase.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, BorderZ));

                    CTFStruct[] LocalParams = EvalGetCTF(StartParams, CTF, DefocusValues, PhaseValues);

                    float[] Result = new float[LocalParams.Length];

                    GPU.CTFCompareToSim(CTFSpectraPolarTrimmedHalf.GetDevice(Intent.Read),
                                        CTFCoordsPolarTrimmedHalf.GetDevice(Intent.Read),
                                        CTFSpectraScaleHalf.GetDevice(Intent.Read),
                                        (uint)CTFSpectraPolarTrimmedHalf.ElementsSliceReal,
                                        LocalParams,
                                        Result,
                                        (uint)LocalParams.Length);

                    float MeanResult = MathHelper.Mean(Result);
                    float StdResult = MathHelper.StdDev(Result);
                    CTFSpectraConsider = new bool[CTFSpectraGrid.Elements()];
                    Parallel.For(0, CTFSpectraConsider.Length, i =>
                    {
                        //if (Result[i] > MeanResult - StdResult * 1.5f)
                        CTFSpectraConsider[i] = true;
                        /*else
                        {
                            CTFSpectraConsider[i] = false;
                            for (int j = 0; j < WiggleWeights.Length; j++)
                                // Make sure the spectrum's gradient doesn't affect the overall gradient.
                                WiggleWeights[j][i] = 0;
                        }*/
                    });
                    NCTFSpectraConsider = CTFSpectraConsider.Where(v => v).Count();
                }

                #endregion

                BroydenFletcherGoldfarbShanno Optimizer = new BroydenFletcherGoldfarbShanno(StartParams.Length, Eval, Gradient)
                {
                    Past = 1,
                    Delta = 1e-6,
                    MaxLineSearch = 15,
                    Corrections = 20
                };
                Optimizer.Minimize(StartParams);

                #endregion

                #region Retrieve parameters

                CTF.Defocus = (decimal)MathHelper.Mean(Optimizer.Solution.Take((int)GridCTF.Dimensions.Elements()).Select(v => (float)v));
                CTF.DefocusDelta = (decimal)Optimizer.Solution[StartParams.Length - 2];
                CTF.DefocusAngle = (decimal)(Optimizer.Solution[StartParams.Length - 1] * 20 / (Math.PI / 180));
                CTF.PhaseShift = (decimal)MathHelper.Mean(Optimizer.Solution.Skip((int)GridCTF.Dimensions.Elements()).Take((int)GridCTFPhase.Dimensions.Elements()).Select(v => (float)v));

                if (CTF.DefocusDelta < 0)
                {
                    CTF.DefocusAngle += 90;
                    CTF.DefocusDelta *= -1;
                }
                CTF.DefocusAngle = ((int)CTF.DefocusAngle + 180 * 99) % 180;

                GridCTF = new CubicGrid(GridCTF.Dimensions, Optimizer.Solution.Take((int)GridCTF.Dimensions.Elements()).Select(v => (float)v).ToArray());
                GridCTFPhase = new CubicGrid(GridCTFPhase.Dimensions, Optimizer.Solution.Skip((int)GridCTF.Dimensions.Elements()).Take((int)GridCTFPhase.Dimensions.Elements()).Select(v => (float)v).ToArray());

                #endregion

                // Dispose GPU resources manually because GC can't be bothered to do it in time.
                CTFSpectraPolarTrimmedHalf.Dispose();
                CTFCoordsPolarTrimmedHalf.Dispose();
                CTFSpectraScaleHalf.Dispose();

                #region Get nicer envelope fit

                if (preciseFit >= 2)
                {
                    if (!CTFSpace && !CTFTime)
                    {
                        UpdateRotationalAverage(true);
                    }
                    else
                    {
                        Image CTFSpectraBackground = new Image(new int3(DimsRegion), true);
                        float[] CTFSpectraBackgroundData = CTFSpectraBackground.GetHost(Intent.Write)[0];

                        // Construct background in Cartesian coordinates.
                        Helper.ForEachElementFT(DimsRegion, (x, y, xx, yy, r, a) =>
                        {
                            CTFSpectraBackgroundData[y * CTFSpectraBackground.DimsEffective.X + x] = _SimulatedBackground.Interp(r / DimsRegion.X);
                        });

                        CTFSpectra.SubtractFromSlices(CTFSpectraBackground);

                        float[] DefocusValues = GridCTF.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, BorderZ));
                        CTFStruct[] LocalParams = DefocusValues.Select(v =>
                        {
                            CTF Local = CTF.GetCopy();
                            Local.Defocus = (decimal)v + 0.0M;

                            return Local.ToStruct();
                        }).ToArray();

                        Image CTFAverage1D = new Image(IntPtr.Zero, new int3(DimsRegion.X / 2, 1, 1));

                        CTF CTFAug = CTF.GetCopy();
                        CTFAug.Defocus += 0.0M;
                        GPU.CTFMakeAverage(CTFSpectra.GetDevice(Intent.Read),
                                           CTFCoordsCart.GetDevice(Intent.Read),
                                           (uint)CTFSpectra.ElementsSliceReal,
                                           (uint)DimsRegion.X,
                                           LocalParams,
                                           CTFAug.ToStruct(),
                                           0,
                                           (uint)DimsRegion.X / 2,
                                           CTFSpectraConsider.Select(v => v ? 1 : 0).ToArray(),
                                           (uint)CTFSpectraGrid.Elements(),
                                           CTFAverage1D.GetDevice(Intent.Write));

                        CTFSpectra.AddToSlices(CTFSpectraBackground);

                        float[] RotationalAverageData = CTFAverage1D.GetHost(Intent.Read)[0];
                        float2[] ForPS1D = new float2[PS1D.Length];
                        for (int i = 0; i < ForPS1D.Length; i++)
                            ForPS1D[i] = new float2((float)i / DimsRegion.X, (float)Math.Round(RotationalAverageData[i], 4) + _SimulatedBackground.Interp((float)i / DimsRegion.X));
                        MathHelper.UnNaN(ForPS1D);
                        _PS1D = ForPS1D;

                        CTFSpectraBackground.Dispose();
                        CTFAverage1D.Dispose();
                        CTFSpectra.FreeDevice();
                    }

                    CTF.Defocus = Math.Max(CTF.Defocus, MainWindow.Options.CTFZMin);
                    UpdateBackgroundFit();
                }

                #endregion
            }

            #endregion

            // Subtract background from 2D average and write it to disk. 
            // This image is used for quick visualization purposes only.

            #region PS2D update

            {
                int3 DimsAverage = new int3(DimsRegion.X, DimsRegion.X / 2, 1);
                float[] Average2DData = new float[DimsAverage.Elements()];
                float[] OriginalAverageData = CTFMean.GetHost(Intent.Read)[0];

                for (int y = 0; y < DimsAverage.Y; y++)
                {
                    int yy = y * y;
                    for (int x = 0; x < DimsAverage.Y; x++)
                    {
                        int xx = DimsRegion.X / 2 - x - 1;
                        xx *= xx;
                        float r = (float)Math.Sqrt(xx + yy) / DimsRegion.X;
                        Average2DData[y * DimsAverage.X + x] = OriginalAverageData[(y + DimsRegion.X / 2) * (DimsRegion.X / 2 + 1) + x] - SimulatedBackground.Interp(r);
                    }

                    for (int x = 0; x < DimsRegion.X / 2; x++)
                    {
                        int xx = x * x;
                        float r = (float)Math.Sqrt(xx + yy) / DimsRegion.X;
                        Average2DData[y * DimsAverage.X + x + DimsRegion.X / 2] = OriginalAverageData[(DimsRegion.X / 2 - y) * (DimsRegion.X / 2 + 1) + (DimsRegion.X / 2 - 1 - x)] - SimulatedBackground.Interp(r);
                    }
                }

                IOHelper.WriteMapFloat(PowerSpectrumPath,
                                       new HeaderMRC
                                       {
                                           Dimensions = DimsAverage,
                                           MinValue = MathHelper.Min(Average2DData),
                                           MaxValue = MathHelper.Max(Average2DData)
                                       },
                                       Average2DData);

                PS2DTemp = null;
                OnPropertyChanged("PS2D");
            }

            #endregion

            for (int i = 0; i < PS1D.Length; i++)
                PS1D[i].Y -= SimulatedBackground.Interp(PS1D[i].X);
            SimulatedBackground = new Cubic1D(SimulatedBackground.Data.Select(v => new float2(v.X, 0f)).ToArray());
            OnPropertyChanged("PS1D");

            CTFSpectra.Dispose();
            CTFMean.Dispose();
            CTFCoordsCart.Dispose();
            CTFCoordsPolarTrimmed.Dispose();

            Simulated1D = GetSimulated1D();
            CTFQuality = GetCTFQuality();

            SaveMeta();
        }
        public void lbfgsTest3()
        {
            Accord.Math.Tools.SetupGenerator(0);

            Func<double[], double> f;
            Func<double[], double[]> g;
            createExpDiff(out f, out g);

            int errors = 0;

            for (int i = 0; i < 10000; i++)
            {
                double[] start = Accord.Math.Matrix.Random(2, -1.0, 1.0);

                var lbfgs = new BroydenFletcherGoldfarbShanno(numberOfVariables: 2, function: f, gradient: g);

                lbfgs.Minimize(start);
                double minValue = lbfgs.Value;
                double[] solution = lbfgs.Solution;

                double expected = -2;

                if (Math.Abs(expected - minValue) > 1e-3)
                    errors++;
            }

            Assert.IsTrue(errors < 800);
        }
        public void MutableGradientSizeTest()
        {
            BroydenFletcherGoldfarbShanno target = new BroydenFletcherGoldfarbShanno(2)
            {
                Function = (x) => 0.0,
                Gradient = (x) => x
            };

            target.Minimize();
        }
        public void WrongGradientSizeTest()
        {
            BroydenFletcherGoldfarbShanno target = new BroydenFletcherGoldfarbShanno(2)
            {
                Function = (x) => 0.0,
                Gradient = (x) => new double[1]
            };

            target.Minimize();
        }
        public void NoGradientTest()
        {
            BroydenFletcherGoldfarbShanno target = new BroydenFletcherGoldfarbShanno(2)
            {
                Function = (x) => 0.0
            };

            target.Minimize();
        }
Example #13
0
        public void ProcessShift(MapHeader originalHeader, Image originalStack, decimal scaleFactor)
        {
            // Deal with dimensions and grids.

            int NFrames = originalHeader.Dimensions.Z;
            int2 DimsImage = new int2(originalHeader.Dimensions);
            int2 DimsRegion = new int2(768, 768);

            float OverlapFraction = 0.0f;
            int2 DimsPositionGrid;
            int3[] PositionGrid = Helper.GetEqualGridSpacing(DimsImage, DimsRegion, OverlapFraction, out DimsPositionGrid);
            //PositionGrid = new[] { new int3(0, 0, 0) };
            //DimsPositionGrid = new int2(1, 1);
            int NPositions = PositionGrid.Length;

            int ShiftGridX = 1;
            int ShiftGridY = 1;
            int ShiftGridZ = Math.Min(NFrames, MainWindow.Options.GridMoveZ);
            GridMovementX = new CubicGrid(new int3(ShiftGridX, ShiftGridY, ShiftGridZ));
            GridMovementY = new CubicGrid(new int3(ShiftGridX, ShiftGridY, ShiftGridZ));

            int LocalGridX = Math.Min(DimsPositionGrid.X, MainWindow.Options.GridMoveX);
            int LocalGridY = Math.Min(DimsPositionGrid.Y, MainWindow.Options.GridMoveY);
            int LocalGridZ = Math.Min(2, NFrames);
            GridLocalX = new CubicGrid(new int3(LocalGridX, LocalGridY, LocalGridZ));
            GridLocalY = new CubicGrid(new int3(LocalGridX, LocalGridY, LocalGridZ));

            int3 ShiftGrid = new int3(DimsPositionGrid.X, DimsPositionGrid.Y, NFrames);

            int MinFreqInclusive = (int)(MainWindow.Options.MovementRangeMin * DimsRegion.X / 2);
            int MaxFreqExclusive = (int)(MainWindow.Options.MovementRangeMax * DimsRegion.X / 2);
            int NFreq = MaxFreqExclusive - MinFreqInclusive;

            int CentralFrame = NFrames / 2;

            int MaskExpansions = Math.Max(1, ShiftGridZ / 3);
            int[] MaskSizes = new int[MaskExpansions];

            // Allocate memory and create all prerequisites:
            int MaskLength;
            Image ShiftFactors;
            Image Phases;
            Image PhasesAverage;
            Image Shifts;
            {
                List<long> Positions = new List<long>();
                List<float2> Factors = new List<float2>();
                List<float2> Freq = new List<float2>();
                int Min2 = MinFreqInclusive * MinFreqInclusive;
                int Max2 = MaxFreqExclusive * MaxFreqExclusive;
                float PixelSize = (float)(MainWindow.Options.CTFPixelMin + MainWindow.Options.CTFPixelMax) * 0.5f;
                float PixelDelta = (float)(MainWindow.Options.CTFPixelMax - MainWindow.Options.CTFPixelMin) * 0.5f;
                float PixelAngle = (float)MainWindow.Options.CTFPixelAngle;

                for (int y = 0; y < DimsRegion.Y; y++)
                {
                    int yy = y - DimsRegion.X / 2;
                    for (int x = 0; x < DimsRegion.X / 2 + 1; x++)
                    {
                        int xx = x - DimsRegion.X / 2;
                        int r2 = xx * xx + yy * yy;
                        if (r2 >= Min2 && r2 < Max2)
                        {
                            Positions.Add(y * (DimsRegion.X / 2 + 1) + x);
                            Factors.Add(new float2((float)xx / DimsRegion.X * 2f * (float)Math.PI,
                                                   (float)yy / DimsRegion.X * 2f * (float)Math.PI));

                            float Angle = (float)Math.Atan2(yy, xx);
                            float r = (float)Math.Sqrt(r2);
                            Freq.Add(new float2(r, Angle));
                        }
                    }
                }

                // Sort everyone with ascending distance from center.
                List<KeyValuePair<float, int>> FreqIndices = Freq.Select((v, i) => new KeyValuePair<float, int>(v.X, i)).ToList();
                FreqIndices.Sort((a, b) => a.Key.CompareTo(b.Key));
                int[] SortedIndices = FreqIndices.Select(v => v.Value).ToArray();

                Helper.Reorder(Positions, SortedIndices);
                Helper.Reorder(Factors, SortedIndices);
                Helper.Reorder(Freq, SortedIndices);

                float Bfac = (float)MainWindow.Options.MovementBfactor * 0.25f / PixelSize / DimsRegion.X;
                float2[] BfacWeightsData = Freq.Select(v => (float)Math.Exp(v.X * Bfac)).Select(v => new float2(v, v)).ToArray();
                Image BfacWeights = new Image(Helper.ToInterleaved(BfacWeightsData), false, false, false);

                long[] RelevantMask = Positions.ToArray();
                ShiftFactors = new Image(Helper.ToInterleaved(Factors.ToArray()));
                MaskLength = RelevantMask.Length;

                // Get mask sizes for different expansion steps.
                for (int i = 0; i < MaskExpansions; i++)
                {
                    float CurrentMaxFreq = MinFreqInclusive + (MaxFreqExclusive - MinFreqInclusive) / (float)MaskExpansions * (i + 1);
                    MaskSizes[i] = Freq.Count(v => v.X * v.X < CurrentMaxFreq * CurrentMaxFreq);
                }

                Phases = new Image(IntPtr.Zero, new int3(MaskLength * 2, DimsPositionGrid.X * DimsPositionGrid.Y, NFrames), false, false, false);

                GPU.CreateShift(originalStack.GetDevice(Intent.Read),
                                new int2(originalHeader.Dimensions),
                                originalHeader.Dimensions.Z,
                                PositionGrid,
                                PositionGrid.Length,
                                DimsRegion,
                                RelevantMask,
                                (uint)MaskLength,
                                Phases.GetDevice(Intent.Write));

                Phases.MultiplyLines(BfacWeights);
                BfacWeights.Dispose();

                originalStack.FreeDevice();
                PhasesAverage = new Image(IntPtr.Zero, new int3(MaskLength, NPositions, 1), false, true, false);
                Shifts = new Image(new float[NPositions * NFrames * 2]);
            }

            #region Fit global movement

            {
                int MinXSteps = 1, MinYSteps = 1;
                int MinZSteps = Math.Min(NFrames, 3);
                int3 ExpansionGridSize = new int3(MinXSteps, MinYSteps, MinZSteps);
                float[][] WiggleWeights = new CubicGrid(ExpansionGridSize).GetWiggleWeights(ShiftGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, 0f));
                double[] StartParams = new double[ExpansionGridSize.Elements() * 2];

                for (int m = 0; m < MaskExpansions; m++)
                {
                    double[] LastAverage = null;

                    Action<double[]> SetPositions = input =>
                    {
                        // Construct CubicGrids and get interpolated shift values.
                        CubicGrid AlteredGridX = new CubicGrid(ExpansionGridSize, input.Where((v, i) => i % 2 == 0).Select(v => (float)v).ToArray());
                        float[] AlteredX = AlteredGridX.GetInterpolatedNative(new int3(DimsPositionGrid.X, DimsPositionGrid.Y, NFrames),
                                                                              new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, 0f));
                        CubicGrid AlteredGridY = new CubicGrid(ExpansionGridSize, input.Where((v, i) => i % 2 == 1).Select(v => (float)v).ToArray());
                        float[] AlteredY = AlteredGridY.GetInterpolatedNative(new int3(DimsPositionGrid.X, DimsPositionGrid.Y, NFrames),
                                                                              new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, 0f));

                        // Let movement start at 0 in the central frame.
                        /*float2[] CenterFrameOffsets = new float2[NPositions];
                        for (int i = 0; i < NPositions; i++)
                            CenterFrameOffsets[i] = new float2(AlteredX[CentralFrame * NPositions + i], AlteredY[CentralFrame * NPositions + i]);*/

                        // Finally, set the shift values in the device array.
                        float[] ShiftData = Shifts.GetHost(Intent.Write)[0];
                        Parallel.For(0, AlteredX.Length, i =>
                        {
                            ShiftData[i * 2] = AlteredX[i];// - CenterFrameOffsets[i % NPositions].X;
                            ShiftData[i * 2 + 1] = AlteredY[i];// - CenterFrameOffsets[i % NPositions].Y;
                        });
                    };

                    Action<double[]> DoAverage = input =>
                    {
                        if (LastAverage == null || input.Where((t, i) => t != LastAverage[i]).Any())
                        {
                            SetPositions(input);
                            GPU.ShiftGetAverage(Phases.GetDevice(Intent.Read),
                                                PhasesAverage.GetDevice(Intent.Write),
                                                ShiftFactors.GetDevice(Intent.Read),
                                                (uint)MaskLength,
                                                (uint)MaskSizes[m],
                                                Shifts.GetDevice(Intent.Read),
                                                (uint)NPositions,
                                                (uint)NFrames);

                            if (LastAverage == null)
                                LastAverage = new double[input.Length];
                            Array.Copy(input, LastAverage, input.Length);
                        }
                    };

                    Func<double[], double> Eval = input =>
                    {
                        DoAverage(input);

                        float[] Diff = new float[NPositions * NFrames];
                        GPU.ShiftGetDiff(Phases.GetDevice(Intent.Read),
                                         PhasesAverage.GetDevice(Intent.Read),
                                         ShiftFactors.GetDevice(Intent.Read),
                                         (uint)MaskLength,
                                         (uint)MaskSizes[m],
                                         Shifts.GetDevice(Intent.Read),
                                         Diff,
                                         (uint)NPositions,
                                         (uint)NFrames);

                        for (int i = 0; i < Diff.Length; i++)
                            Diff[i] = Diff[i];// * 100f;

                        return Diff.Sum();
                    };

                    Func<double[], double[]> Grad = input =>
                    {
                        DoAverage(input);

                        float[] GradX = new float[NPositions * NFrames], GradY = new float[NPositions * NFrames];

                        float[] Diff = new float[NPositions * NFrames * 2];
                        GPU.ShiftGetGrad(Phases.GetDevice(Intent.Read),
                                         PhasesAverage.GetDevice(Intent.Read),
                                         ShiftFactors.GetDevice(Intent.Read),
                                         (uint)MaskLength,
                                         (uint)MaskSizes[m],
                                         Shifts.GetDevice(Intent.Read),
                                         Diff,
                                         (uint)NPositions,
                                         (uint)NFrames);

                        //for (int i = 0; i < Diff.Length; i++)
                            //Diff[i] = Diff[i] * 100f;

                        for (int i = 0; i < GradX.Length; i++)
                        {
                            GradX[i] = Diff[i * 2];
                            GradY[i] = Diff[i * 2 + 1];
                        }

                        double[] Result = new double[input.Length];
                        Parallel.For(0, input.Length / 2, i =>
                        {
                            Result[i * 2] = MathHelper.ReduceWeighted(GradX, WiggleWeights[i]);
                            Result[i * 2 + 1] = MathHelper.ReduceWeighted(GradY, WiggleWeights[i]);
                        });
                        return Result;
                    };

                    /*Func<double[], double[]> Grad = input =>
                    {
                        DoAverage(input);

                        float[] GradX = new float[NPositions * NFrames], GradY = new float[NPositions * NFrames];
                        float Step = 0.002f;

                        {
                            double[] InputXP = new double[input.Length];
                            for (int i = 0; i < input.Length; i++)
                                if (i % 2 == 0)
                                    InputXP[i] = input[i] + Step;
                                else
                                    InputXP[i] = input[i];
                            SetPositions(InputXP);

                            float[] DiffXP = new float[NPositions * NFrames];
                            GPU.ShiftGetDiff(Phases.GetDevice(Intent.Read),
                                             PhasesAverage.GetDevice(Intent.Read),
                                             ShiftFactors.GetDevice(Intent.Read),
                                             (uint)MaskLength,
                                             (uint)MaskSizes[m],
                                             Shifts.GetDevice(Intent.Read),
                                             DiffXP,
                                             (uint)NPositions,
                                             (uint)NFrames);


                            double[] InputXM = new double[input.Length];
                            for (int i = 0; i < input.Length; i++)
                                if (i % 2 == 0)
                                    InputXM[i] = input[i] - Step;
                                else
                                    InputXM[i] = input[i];
                            SetPositions(InputXM);

                            float[] DiffXM = new float[NPositions * NFrames];
                            GPU.ShiftGetDiff(Phases.GetDevice(Intent.Read),
                                             PhasesAverage.GetDevice(Intent.Read),
                                             ShiftFactors.GetDevice(Intent.Read),
                                             (uint)MaskLength,
                                             (uint)MaskSizes[m],
                                             Shifts.GetDevice(Intent.Read),
                                             DiffXM,
                                             (uint)NPositions,
                                             (uint)NFrames);

                            for (int i = 0; i < GradX.Length; i++)
                                GradX[i] = (DiffXP[i] - DiffXM[i]) / (Step * 2);
                        }

                        {
                            double[] InputYP = new double[input.Length];
                            for (int i = 0; i < input.Length; i++)
                                if (i % 2 == 1)
                                    InputYP[i] = input[i] + Step;
                                else
                                    InputYP[i] = input[i];
                            SetPositions(InputYP);

                            float[] DiffYP = new float[NPositions * NFrames];
                            GPU.ShiftGetDiff(Phases.GetDevice(Intent.Read),
                                             PhasesAverage.GetDevice(Intent.Read),
                                             ShiftFactors.GetDevice(Intent.Read),
                                             (uint)MaskLength,
                                             (uint)MaskSizes[m],
                                             Shifts.GetDevice(Intent.Read),
                                             DiffYP,
                                             (uint)NPositions,
                                             (uint)NFrames);


                            double[] InputYM = new double[input.Length];
                            for (int i = 0; i < input.Length; i++)
                                if (i % 2 == 1)
                                    InputYM[i] = input[i] - Step;
                                else
                                    InputYM[i] = input[i];
                            SetPositions(InputYM);

                            float[] DiffYM = new float[NPositions * NFrames];
                            GPU.ShiftGetDiff(Phases.GetDevice(Intent.Read),
                                             PhasesAverage.GetDevice(Intent.Read),
                                             ShiftFactors.GetDevice(Intent.Read),
                                             (uint)MaskLength,
                                             (uint)MaskSizes[m],
                                             Shifts.GetDevice(Intent.Read),
                                             DiffYM,
                                             (uint)NPositions,
                                             (uint)NFrames);

                            for (int i = 0; i < GradY.Length; i++)
                                GradY[i] = (DiffYP[i] - DiffYM[i]) / (Step * 2);
                        }

                        double[] Result = new double[input.Length];
                        Parallel.For(0, input.Length / 2, i =>
                        {
                            Result[i * 2] = MathHelper.ReduceWeighted(GradX, WiggleWeights[i]);
                            Result[i * 2 + 1] = MathHelper.ReduceWeighted(GradY, WiggleWeights[i]);
                        });
                        return Result;
                    };*/

                    BroydenFletcherGoldfarbShanno Optimizer = new BroydenFletcherGoldfarbShanno(StartParams.Length, Eval, Grad);
                    Optimizer.Corrections = 20;
                    Optimizer.Minimize(StartParams);

                    float MeanX = MathHelper.Mean(Optimizer.Solution.Where((v, i) => i % 2 == 0).Select(v => (float)v));
                    float MeanY = MathHelper.Mean(Optimizer.Solution.Where((v, i) => i % 2 == 1).Select(v => (float)v));
                    for (int i = 0; i < ExpansionGridSize.Elements(); i++)
                    {
                        Optimizer.Solution[i * 2] -= MeanX;
                        Optimizer.Solution[i * 2 + 1] -= MeanY;
                    }

                    // Store coarse values in grids.
                    GridMovementX = new CubicGrid(ExpansionGridSize, Optimizer.Solution.Where((v, i) => i % 2 == 0).Select(v => (float)v).ToArray());
                    GridMovementY = new CubicGrid(ExpansionGridSize, Optimizer.Solution.Where((v, i) => i % 2 == 1).Select(v => (float)v).ToArray());

                    if (m < MaskExpansions - 1)
                    {
                        // Refine sampling.
                        ExpansionGridSize = new int3((int)Math.Round((float)(ShiftGridX - MinXSteps) / (MaskExpansions - 1) * (m + 1) + MinXSteps),
                                                     (int)Math.Round((float)(ShiftGridY - MinYSteps) / (MaskExpansions - 1) * (m + 1) + MinYSteps),
                                                     (int)Math.Round((float)(ShiftGridZ - MinZSteps) / (MaskExpansions - 1) * (m + 1) + MinZSteps));
                        WiggleWeights = new CubicGrid(ExpansionGridSize).GetWiggleWeights(ShiftGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, 0f));

                        // Resize the grids to account for finer sampling.
                        GridMovementX = GridMovementX.Resize(ExpansionGridSize);
                        GridMovementY = GridMovementY.Resize(ExpansionGridSize);

                        // Construct start parameters for next optimization iteration.
                        StartParams = new double[ExpansionGridSize.Elements() * 2];
                        for (int i = 0; i < ExpansionGridSize.Elements(); i++)
                        {
                            StartParams[i * 2] = GridMovementX.FlatValues[i];
                            StartParams[i * 2 + 1] = GridMovementY.FlatValues[i];
                        }
                    }
                }
            }

            #endregion

            // Center the global shifts
            /*{
                float2[] AverageShifts = new float2[ShiftGridZ];
                for (int i = 0; i < AverageShifts.Length; i++)
                    AverageShifts[i] = new float2(MathHelper.Mean(GridMovementX.GetSliceXY(i)),
                                                  MathHelper.Mean(GridMovementY.GetSliceXY(i)));
                float2 CenterShift = MathHelper.Mean(AverageShifts);

                GridMovementX = new CubicGrid(GridMovementX.Dimensions, GridMovementX.FlatValues.Select(v => v - CenterShift.X).ToArray());
                GridMovementY = new CubicGrid(GridMovementY.Dimensions, GridMovementY.FlatValues.Select(v => v - CenterShift.Y).ToArray());
            }*/

            #region Fit local movement

            /*{
                int MinXSteps = LocalGridX, MinYSteps = LocalGridY;
                int MinZSteps = LocalGridZ;
                int3 ExpansionGridSize = new int3(MinXSteps, MinYSteps, MinZSteps);
                float[][] WiggleWeights = new CubicGrid(ExpansionGridSize).GetWiggleWeights(ShiftGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, 0f));
                double[] StartParams = new double[ExpansionGridSize.Elements() * 2];

                for (int m = MaskExpansions - 1; m < MaskExpansions; m++)
                {
                    double[] LastAverage = null;

                    Action<double[]> SetPositions = input =>
                    {
                        // Construct CubicGrids and get interpolated shift values.
                        float[] GlobalX = GridMovementX.GetInterpolatedNative(new int3(DimsPositionGrid.X, DimsPositionGrid.Y, NFrames),
                                                                              new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, 0f));
                        CubicGrid AlteredGridX = new CubicGrid(ExpansionGridSize, input.Where((v, i) => i % 2 == 0).Select(v => (float)v).ToArray());
                        float[] AlteredX = AlteredGridX.GetInterpolatedNative(new int3(DimsPositionGrid.X, DimsPositionGrid.Y, NFrames),
                                                                              new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, 0f));
                        AlteredX = MathHelper.Plus(GlobalX, AlteredX);

                        float[] GlobalY = GridMovementY.GetInterpolatedNative(new int3(DimsPositionGrid.X, DimsPositionGrid.Y, NFrames),
                                                                              new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, 0f));
                        CubicGrid AlteredGridY = new CubicGrid(ExpansionGridSize, input.Where((v, i) => i % 2 == 1).Select(v => (float)v).ToArray());
                        float[] AlteredY = AlteredGridY.GetInterpolatedNative(new int3(DimsPositionGrid.X, DimsPositionGrid.Y, NFrames),
                                                                              new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, 0f));
                        AlteredY = MathHelper.Plus(GlobalY, AlteredY);

                        // Let movement start at 0 in the central frame.
                        float2[] CenterFrameOffsets = new float2[NPositions];
                        for (int i = 0; i < NPositions; i++)
                            CenterFrameOffsets[i] = new float2(AlteredX[CentralFrame * NPositions + i], AlteredY[CentralFrame * NPositions + i]);

                        // Finally, set the shift values in the device array.
                        float[] ShiftData = Shifts.GetHost(Intent.Write)[0];
                        Parallel.For(0, AlteredX.Length, i =>
                        {
                            ShiftData[i * 2] = AlteredX[i] - CenterFrameOffsets[i % NPositions].X;
                            ShiftData[i * 2 + 1] = AlteredY[i] - CenterFrameOffsets[i % NPositions].Y;
                        });
                    };

                    Action<double[]> DoAverage = input =>
                    {
                        if (LastAverage == null || input.Where((t, i) => t != LastAverage[i]).Any())
                        {
                            SetPositions(input);
                            GPU.ShiftGetAverage(Phases.GetDevice(Intent.Read),
                                                PhasesAverage.GetDevice(Intent.Write),
                                                ShiftFactors.GetDevice(Intent.Read),
                                                (uint)MaskLength,
                                                (uint)MaskSizes[m],
                                                Shifts.GetDevice(Intent.Read),
                                                (uint)NPositions,
                                                (uint)NFrames);

                            if (LastAverage == null)
                                LastAverage = new double[input.Length];
                            Array.Copy(input, LastAverage, input.Length);
                        }
                    };

                    Func<double[], double> Eval = input =>
                    {
                        DoAverage(input);

                        float[] Diff = new float[NPositions * NFrames];
                        GPU.ShiftGetDiff(Phases.GetDevice(Intent.Read),
                                         PhasesAverage.GetDevice(Intent.Read),
                                         ShiftFactors.GetDevice(Intent.Read),
                                         (uint)MaskLength,
                                         (uint)MaskSizes[m],
                                         Shifts.GetDevice(Intent.Read),
                                         Diff,
                                         (uint)NPositions,
                                         (uint)NFrames);

                        for (int i = 0; i < Diff.Length; i++)
                            Diff[i] = Diff[i] * 100f;

                        return MathHelper.Mean(Diff);
                    };

                    Func<double[], double[]> Grad = input =>
                    {
                        DoAverage(input);

                        float[] Diff = new float[NPositions * NFrames * 2];
                        GPU.ShiftGetGrad(Phases.GetDevice(Intent.Read),
                                         PhasesAverage.GetDevice(Intent.Read),
                                         ShiftFactors.GetDevice(Intent.Read),
                                         (uint)MaskLength,
                                         (uint)MaskSizes[m],
                                         Shifts.GetDevice(Intent.Read),
                                         Diff,
                                         (uint)NPositions,
                                         (uint)NFrames);

                        for (int i = 0; i < Diff.Length; i++)
                            Diff[i] = Diff[i] * 100f;

                        float[] DiffX = new float[NPositions * NFrames], DiffY = new float[NPositions * NFrames];
                        for (int i = 0; i < DiffX.Length; i++)
                        {
                            DiffX[i] = Diff[i * 2];
                            DiffY[i] = Diff[i * 2 + 1];
                        }

                        double[] Result = new double[input.Length];
                        Parallel.For(0, input.Length / 2, i =>
                        {
                            Result[i * 2] = MathHelper.ReduceWeighted(DiffX, WiggleWeights[i]);
                            Result[i * 2 + 1] = MathHelper.ReduceWeighted(DiffY, WiggleWeights[i]);
                        });
                        return Result;
                    };

                    BroydenFletcherGoldfarbShanno Optimizer = new BroydenFletcherGoldfarbShanno(StartParams.Length, Eval, Grad);
                    Optimizer.Corrections = 20;
                    Optimizer.Minimize(StartParams);

                    float MeanX = MathHelper.Mean(Optimizer.Solution.Where((v, i) => i % 2 == 0).Select(v => (float)v));
                    float MeanY = MathHelper.Mean(Optimizer.Solution.Where((v, i) => i % 2 == 1).Select(v => (float)v));
                    for (int i = 0; i < ExpansionGridSize.Elements(); i++)
                    {
                        Optimizer.Solution[i * 2] -= MeanX;
                        Optimizer.Solution[i * 2 + 1] -= MeanY;
                    }

                    // Store coarse values in grids.
                    GridLocalX = new CubicGrid(ExpansionGridSize, Optimizer.Solution.Where((v, i) => i % 2 == 0).Select(v => (float)v).ToArray());
                    GridLocalY = new CubicGrid(ExpansionGridSize, Optimizer.Solution.Where((v, i) => i % 2 == 1).Select(v => (float)v).ToArray());

                    if (m < MaskExpansions - 1)
                    {
                        // Refine sampling.
                        ExpansionGridSize = new int3((int)Math.Round((float)(LocalGridX - MinXSteps) / (MaskExpansions - 1) * (m + 1) + MinXSteps),
                                                     (int)Math.Round((float)(LocalGridY - MinYSteps) / (MaskExpansions - 1) * (m + 1) + MinYSteps),
                                                     (int)Math.Round((float)(LocalGridZ - MinZSteps) / (MaskExpansions - 1) * (m + 1) + MinZSteps));
                        WiggleWeights = new CubicGrid(ExpansionGridSize).GetWiggleWeights(ShiftGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, 0f));

                        // Resize the grids to account for finer sampling.
                        GridLocalX = GridLocalX.Resize(ExpansionGridSize);
                        GridLocalY = GridLocalY.Resize(ExpansionGridSize);

                        // Construct start parameters for next optimization iteration.
                        StartParams = new double[ExpansionGridSize.Elements() * 2];
                        for (int i = 0; i < ExpansionGridSize.Elements(); i++)
                        {
                            StartParams[i * 2] = GridLocalX.FlatValues[i];
                            StartParams[i * 2 + 1] = GridLocalY.FlatValues[i];
                        }
                    }
                }
            }*/

            #endregion

            ShiftFactors.Dispose();
            Phases.Dispose();
            PhasesAverage.Dispose();
            Shifts.Dispose();

            // Center the local shifts
            /*{
                float2[] AverageShifts = new float2[LocalGridZ];
                for (int i = 0; i < AverageShifts.Length; i++)
                    AverageShifts[i] = new float2(MathHelper.Mean(GridLocalX.GetSliceXY(i)),
                                                  MathHelper.Mean(GridLocalY.GetSliceXY(i)));
                float2 CenterShift = MathHelper.Mean(AverageShifts);

                GridLocalX = new CubicGrid(GridLocalX.Dimensions, GridLocalX.FlatValues.Select(v => v - CenterShift.X).ToArray());
                GridLocalY = new CubicGrid(GridLocalY.Dimensions, GridLocalY.FlatValues.Select(v => v - CenterShift.Y).ToArray());
            }*/

            SaveMeta();
        }
        public void InvalidLineSearchTest2()
        {
            int n = 10;

            Func<double[], double> function = (parameters) =>
            {
                return -(n * Math.Log(0) - n * Math.Log(Math.PI));
            };

            Func<double[], double[]> gradient = (parameters) =>
            {
                return new[] { 2.0, Double.NegativeInfinity };
            };

            double[] start = { 0, 0 };

            var lbfgs = new BroydenFletcherGoldfarbShanno(2, function, gradient);

            bool success = lbfgs.Minimize(start); 

            Assert.IsFalse(success);
        }
Example #15
0
        public void ExportParticlesMovie(Star tableIn, Star tableOut, MapHeader originalHeader, Image originalStack, int size, float particleradius, decimal scaleFactor)
        {
            int CurrentDevice = GPU.GetDevice();

            #region Make sure directories exist.
            lock (tableIn)
            {
                if (!Directory.Exists(ParticleMoviesDir))
                    Directory.CreateDirectory(ParticleMoviesDir);
                if (!Directory.Exists(ParticleCTFMoviesDir))
                    Directory.CreateDirectory(ParticleCTFMoviesDir);
            }
            #endregion

            #region Get row indices for all, and individual halves

            List<int> RowIndices = new List<int>();
            string[] ColumnMicrographName = tableIn.GetColumn("rlnMicrographName");
            for (int i = 0; i < ColumnMicrographName.Length; i++)
                if (ColumnMicrographName[i].Contains(RootName))
                    RowIndices.Add(i);

            //RowIndices = RowIndices.Take(13).ToList();

            List<int> RowIndices1 = new List<int>();
            List<int> RowIndices2 = new List<int>();
            for (int i = 0; i < RowIndices.Count; i++)
                if (tableIn.GetRowValue(RowIndices[i], "rlnRandomSubset") == "1")
                    RowIndices1.Add(RowIndices[i]);
                else
                    RowIndices2.Add(RowIndices[i]);

            #endregion

            if (RowIndices.Count == 0)
                return;

            #region Auxiliary variables

            List<int> TableOutIndices = new List<int>();

            int3 Dims = originalHeader.Dimensions;
            Dims.Z = 36;
            int3 DimsRegion = new int3(size, size, 1);
            int3 DimsPadded = new int3(size * 2, size * 2, 1);
            int NParticles = RowIndices.Count;
            int NParticles1 = RowIndices1.Count;
            int NParticles2 = RowIndices2.Count;

            float PixelSize = (float)CTF.PixelSize / 1.00f;
            float PixelDelta = (float)CTF.PixelSizeDelta / 1.00f;
            float PixelAngle = (float)CTF.PixelSizeAngle * Helper.ToRad;

            #endregion

            #region Prepare initial coordinates and shifts

            string[] ColumnPosX = tableIn.GetColumn("rlnCoordinateX");
            string[] ColumnPosY = tableIn.GetColumn("rlnCoordinateY");
            string[] ColumnOriginX = tableIn.GetColumn("rlnOriginX");
            string[] ColumnOriginY = tableIn.GetColumn("rlnOriginY");

            int3[] Origins1 = new int3[NParticles1];
            int3[] Origins2 = new int3[NParticles2];
            float3[] ResidualShifts1 = new float3[NParticles1];
            float3[] ResidualShifts2 = new float3[NParticles2];

            lock (tableIn)  // Writing to the table, better be on the safe side
            {
                // Half1: Add translational shifts to coordinates, sans the fractional part
                for (int i = 0; i < NParticles1; i++)
                {
                    float2 Pos = new float2(float.Parse(ColumnPosX[RowIndices1[i]], CultureInfo.InvariantCulture),
                                            float.Parse(ColumnPosY[RowIndices1[i]], CultureInfo.InvariantCulture)) * 1.00f;
                    float2 Shift = new float2(float.Parse(ColumnOriginX[RowIndices1[i]], CultureInfo.InvariantCulture),
                                              float.Parse(ColumnOriginY[RowIndices1[i]], CultureInfo.InvariantCulture)) * 1.00f;

                    Origins1[i] = new int3((int)(Pos.X - Shift.X),
                                           (int)(Pos.Y - Shift.Y),
                                           0);
                    ResidualShifts1[i] = new float3(-MathHelper.ResidualFraction(Pos.X - Shift.X),
                                                    -MathHelper.ResidualFraction(Pos.Y - Shift.Y),
                                                    0f);

                    tableIn.SetRowValue(RowIndices1[i], "rlnCoordinateX", Origins1[i].X.ToString());
                    tableIn.SetRowValue(RowIndices1[i], "rlnCoordinateY", Origins1[i].Y.ToString());
                    tableIn.SetRowValue(RowIndices1[i], "rlnOriginX", "0.0");
                    tableIn.SetRowValue(RowIndices1[i], "rlnOriginY", "0.0");
                }

                // Half2: Add translational shifts to coordinates, sans the fractional part
                for (int i = 0; i < NParticles2; i++)
                {
                    float2 Pos = new float2(float.Parse(ColumnPosX[RowIndices2[i]], CultureInfo.InvariantCulture),
                                            float.Parse(ColumnPosY[RowIndices2[i]], CultureInfo.InvariantCulture)) * 1.00f;
                    float2 Shift = new float2(float.Parse(ColumnOriginX[RowIndices2[i]], CultureInfo.InvariantCulture),
                                              float.Parse(ColumnOriginY[RowIndices2[i]], CultureInfo.InvariantCulture)) * 1.00f;

                    Origins2[i] = new int3((int)(Pos.X - Shift.X),
                                           (int)(Pos.Y - Shift.Y),
                                           0);
                    ResidualShifts2[i] = new float3(-MathHelper.ResidualFraction(Pos.X - Shift.X),
                                                    -MathHelper.ResidualFraction(Pos.Y - Shift.Y),
                                                    0f);

                    tableIn.SetRowValue(RowIndices2[i], "rlnCoordinateX", Origins2[i].X.ToString());
                    tableIn.SetRowValue(RowIndices2[i], "rlnCoordinateY", Origins2[i].Y.ToString());
                    tableIn.SetRowValue(RowIndices2[i], "rlnOriginX", "0.0");
                    tableIn.SetRowValue(RowIndices2[i], "rlnOriginY", "0.0");
                }
            }

            #endregion

            #region Allocate memory for particle and PS stacks

            Image ParticleStackAll = new Image(new int3(DimsRegion.X, DimsRegion.Y, NParticles * Dims.Z));
            Image ParticleStack1 = new Image(new int3(DimsRegion.X, DimsRegion.Y, NParticles1 * Dims.Z));
            Image ParticleStack2 = new Image(new int3(DimsRegion.X, DimsRegion.Y, NParticles2 * Dims.Z));
            Image PSStackAll = new Image(new int3(DimsRegion.X, DimsRegion.Y, NParticles * Dims.Z), true);
            Image PSStack1 = new Image(new int3(DimsRegion.X, DimsRegion.Y, NParticles1 * Dims.Z), true);
            Image PSStack2 = new Image(new int3(DimsRegion.X, DimsRegion.Y, NParticles2 * Dims.Z), true);

            Image FrameParticles1 = new Image(IntPtr.Zero, new int3(DimsPadded.X, DimsPadded.Y, NParticles1));
            Image FrameParticles2 = new Image(IntPtr.Zero, new int3(DimsPadded.X, DimsPadded.Y, NParticles2));

            float[][] ParticleStackData = ParticleStackAll.GetHost(Intent.Write);
            float[][] ParticleStackData1 = ParticleStack1.GetHost(Intent.Write);
            float[][] ParticleStackData2 = ParticleStack2.GetHost(Intent.Write);
            float[][] PSStackData = PSStackAll.GetHost(Intent.Write);
            float[][] PSStackData1 = PSStack1.GetHost(Intent.Write);
            float[][] PSStackData2 = PSStack2.GetHost(Intent.Write);

            #endregion

            #region Create rows in outTable

            lock (tableOut)  // Creating rows in outTable, this absolutely needs to be staged sequentially
            {
                for (int z = 0; z < Dims.Z; z++)
                {
                    for (int i = 0; i < NParticles; i++)
                    {
                        int Index = i < NParticles1 ? RowIndices1[i] : RowIndices2[i - NParticles1];

                        string OriParticlePath = (i + 1).ToString("D6") + "@particles/" + RootName + "_particles.mrcs";
                        string ParticleName = (z * NParticles + i + 1).ToString("D6") + "@particlemovies/" + RootName + "_particles.mrcs";
                        string ParticleCTFName = (z * NParticles + i + 1).ToString("D6") + "@particlectfmovies/" + RootName + "_particlectf.mrcs";

                        List<string> NewRow = tableIn.GetRow(Index).Select(v => v).ToList(); // Get copy of original row.
                        NewRow[tableOut.GetColumnIndex("rlnOriginalParticleName")] = OriParticlePath;
                        NewRow[tableOut.GetColumnIndex("rlnAngleRotPrior")] = tableIn.GetRowValue(Index, "rlnAngleRot");
                        NewRow[tableOut.GetColumnIndex("rlnAngleTiltPrior")] = tableIn.GetRowValue(Index, "rlnAngleTilt");
                        NewRow[tableOut.GetColumnIndex("rlnAnglePsiPrior")] = tableIn.GetRowValue(Index, "rlnAnglePsi");
                        NewRow[tableOut.GetColumnIndex("rlnOriginXPrior")] = "0.0";
                        NewRow[tableOut.GetColumnIndex("rlnOriginYPrior")] = "0.0";

                        NewRow[tableOut.GetColumnIndex("rlnImageName")] = ParticleName;
                        NewRow[tableOut.GetColumnIndex("rlnCtfImage")] = ParticleCTFName;
                        NewRow[tableOut.GetColumnIndex("rlnMicrographName")] = (z + 1).ToString("D6") + "@stack/" + RootName + "_movie.mrcs";

                        TableOutIndices.Add(tableOut.RowCount);
                        tableOut.AddRow(NewRow);
                    }
                }
            }

            #endregion

            #region For every frame, extract particles from each half; shift, correct, and norm them

            float StepZ = 1f / Math.Max(Dims.Z - 1, 1);
            for (int z = 0; z < Dims.Z; z++)
            {
                float CoordZ = z * StepZ;

                #region Extract, correct, and norm particles

                #region Half 1
                {
                    if (originalStack != null)
                        GPU.Extract(originalStack.GetDeviceSlice(z, Intent.Read),
                                    FrameParticles1.GetDevice(Intent.Write),
                                    Dims.Slice(),
                                    DimsPadded,
                                    Helper.ToInterleaved(Origins1.Select(v => new int3(v.X - DimsPadded.X / 2, v.Y - DimsPadded.Y / 2, 0)).ToArray()),
                                    (uint)NParticles1);

                    // Shift particles
                    {
                        float3[] Shifts = new float3[NParticles1];

                        for (int i = 0; i < NParticles1; i++)
                        {
                            float3 Coords = new float3((float)Origins1[i].X / Dims.X, (float)Origins1[i].Y / Dims.Y, CoordZ);
                            Shifts[i] = ResidualShifts1[i] + new float3(GetShiftFromPyramid(Coords)) * 1.00f;
                        }
                        FrameParticles1.ShiftSlices(Shifts);
                    }

                    Image FrameParticlesCropped = FrameParticles1.AsPadded(new int2(DimsRegion));
                    Image FrameParticlesCorrected = FrameParticlesCropped.AsAnisotropyCorrected(new int2(DimsRegion),
                                                                                                PixelSize + PixelDelta / 2f,
                                                                                                PixelSize - PixelDelta / 2f,
                                                                                                PixelAngle,
                                                                                                6);
                    FrameParticlesCropped.Dispose();

                    GPU.NormParticles(FrameParticlesCorrected.GetDevice(Intent.Read),
                                      FrameParticlesCorrected.GetDevice(Intent.Write),
                                      DimsRegion,
                                      (uint)(particleradius / PixelSize),
                                      true,
                                      (uint)NParticles1);

                    float[][] FrameParticlesCorrectedData = FrameParticlesCorrected.GetHost(Intent.Read);
                    for (int n = 0; n < NParticles1; n++)
                    {
                        ParticleStackData[z * NParticles + n] = FrameParticlesCorrectedData[n];
                        ParticleStackData1[z * NParticles1 + n] = FrameParticlesCorrectedData[n];
                    }

                    //FrameParticlesCorrected.WriteMRC("intermediate_particles1.mrc");

                    FrameParticlesCorrected.Dispose();
                }
                #endregion

                #region Half 2
                {
                    if (originalStack != null)
                        GPU.Extract(originalStack.GetDeviceSlice(z, Intent.Read),
                                    FrameParticles2.GetDevice(Intent.Write),
                                    Dims.Slice(),
                                    DimsPadded,
                                    Helper.ToInterleaved(Origins2.Select(v => new int3(v.X - DimsPadded.X / 2, v.Y - DimsPadded.Y / 2, 0)).ToArray()),
                                    (uint)NParticles2);

                    // Shift particles
                    {
                        float3[] Shifts = new float3[NParticles2];

                        for (int i = 0; i < NParticles2; i++)
                        {
                            float3 Coords = new float3((float)Origins2[i].X / Dims.X, (float)Origins2[i].Y / Dims.Y, CoordZ);
                            Shifts[i] = ResidualShifts2[i] + new float3(GetShiftFromPyramid(Coords)) * 1.00f;
                        }
                        FrameParticles2.ShiftSlices(Shifts);
                    }

                    Image FrameParticlesCropped = FrameParticles2.AsPadded(new int2(DimsRegion));
                    Image FrameParticlesCorrected = FrameParticlesCropped.AsAnisotropyCorrected(new int2(DimsRegion),
                                                                                                PixelSize + PixelDelta / 2f,
                                                                                                PixelSize - PixelDelta / 2f,
                                                                                                PixelAngle,
                                                                                                6);
                    FrameParticlesCropped.Dispose();

                    GPU.NormParticles(FrameParticlesCorrected.GetDevice(Intent.Read),
                                      FrameParticlesCorrected.GetDevice(Intent.Write),
                                      DimsRegion,
                                      (uint)(particleradius / PixelSize),
                                      true,
                                      (uint)NParticles2);

                    float[][] FrameParticlesCorrectedData = FrameParticlesCorrected.GetHost(Intent.Read);
                    for (int n = 0; n < NParticles2; n++)
                    {
                        ParticleStackData[z * NParticles + NParticles1 + n] = FrameParticlesCorrectedData[n];
                        ParticleStackData2[z * NParticles2 + n] = FrameParticlesCorrectedData[n];
                    }

                    //FrameParticlesCorrected.WriteMRC("intermediate_particles2.mrc");

                    FrameParticlesCorrected.Dispose();
                }
                #endregion

                #endregion
                
                #region PS Half 1
                {
                    Image PS = new Image(new int3(DimsRegion.X, DimsRegion.Y, NParticles1), true);
                    PS.Fill(1f);

                    // Apply motion blur filter.

                    #region Motion blur weighting

                    {
                        const int Samples = 11;
                        float StartZ = (z - 0.5f) * StepZ;
                        float StopZ = (z + 0.5f) * StepZ;

                        float2[] Shifts = new float2[Samples * NParticles1];
                        for (int p = 0; p < NParticles1; p++)
                        {
                            float NormX = (float)Origins1[p].X / Dims.X;
                            float NormY = (float)Origins1[p].Y / Dims.Y;

                            for (int zz = 0; zz < Samples; zz++)
                            {
                                float zp = StartZ + (StopZ - StartZ) / (Samples - 1) * zz;
                                float3 Coords = new float3(NormX, NormY, zp);
                                Shifts[p * Samples + zz] = GetShiftFromPyramid(Coords) * 1.00f;
                            }
                        }

                        Image MotionFilter = new Image(IntPtr.Zero, new int3(DimsRegion.X, DimsRegion.Y, NParticles1), true);
                        GPU.CreateMotionBlur(MotionFilter.GetDevice(Intent.Write),
                                             DimsRegion,
                                             Helper.ToInterleaved(Shifts.Select(v => new float3(v.X, v.Y, 0)).ToArray()),
                                             Samples,
                                             (uint)NParticles1);
                        PS.Multiply(MotionFilter);
                        //MotionFilter.WriteMRC("motion.mrc");
                        MotionFilter.Dispose();
                    }

                    #endregion

                    float[][] PSData = PS.GetHost(Intent.Read);
                    for (int n = 0; n < NParticles1; n++)
                        PSStackData[z * NParticles + n] = PSData[n];

                    //PS.WriteMRC("intermediate_ps1.mrc");

                    PS.Dispose();
                }
                #endregion

                #region PS Half 2
                {
                    Image PS = new Image(new int3(DimsRegion.X, DimsRegion.Y, NParticles2), true);
                    PS.Fill(1f);

                    // Apply motion blur filter.

                    #region Motion blur weighting

                    {
                        const int Samples = 11;
                        float StartZ = (z - 0.5f) * StepZ;
                        float StopZ = (z + 0.5f) * StepZ;

                        float2[] Shifts = new float2[Samples * NParticles2];
                        for (int p = 0; p < NParticles2; p++)
                        {
                            float NormX = (float)Origins2[p].X / Dims.X;
                            float NormY = (float)Origins2[p].Y / Dims.Y;

                            for (int zz = 0; zz < Samples; zz++)
                            {
                                float zp = StartZ + (StopZ - StartZ) / (Samples - 1) * zz;
                                float3 Coords = new float3(NormX, NormY, zp);
                                Shifts[p * Samples + zz] = GetShiftFromPyramid(Coords) * 1.00f;
                            }
                        }

                        Image MotionFilter = new Image(IntPtr.Zero, new int3(DimsRegion.X, DimsRegion.Y, NParticles2), true);
                        GPU.CreateMotionBlur(MotionFilter.GetDevice(Intent.Write),
                                             DimsRegion,
                                             Helper.ToInterleaved(Shifts.Select(v => new float3(v.X, v.Y, 0)).ToArray()),
                                             Samples,
                                             (uint)NParticles2);
                        PS.Multiply(MotionFilter);
                        //MotionFilter.WriteMRC("motion.mrc");
                        MotionFilter.Dispose();
                    }

                    #endregion

                    float[][] PSData = PS.GetHost(Intent.Read);
                    for (int n = 0; n < NParticles2; n++)
                        PSStackData[z * NParticles + NParticles1 + n] = PSData[n];

                    //PS.WriteMRC("intermediate_ps2.mrc");

                    PS.Dispose();
                }
                #endregion
            }
            FrameParticles1.Dispose();
            FrameParticles2.Dispose();
            originalStack.FreeDevice();

            #endregion

            HeaderMRC ParticlesHeader = new HeaderMRC
            {
                Pixelsize = new float3(PixelSize, PixelSize, PixelSize)
            };

            // Do translation and rotation BFGS per particle
            {
                float MaxHigh = 2.6f;

                CubicGrid GridX = new CubicGrid(new int3(NParticles1, 1, 2));
                CubicGrid GridY = new CubicGrid(new int3(NParticles1, 1, 2));
                CubicGrid GridRot = new CubicGrid(new int3(NParticles1, 1, 2));
                CubicGrid GridTilt = new CubicGrid(new int3(NParticles1, 1, 2));
                CubicGrid GridPsi = new CubicGrid(new int3(NParticles1, 1, 2));

                int2 DimsCropped = new int2(DimsRegion / (MaxHigh / PixelSize / 2f)) / 2 * 2;

                #region Get coordinates for CTF and Fourier-space shifts
                Image CTFCoords;
                Image ShiftFactors;
                {
                    float2[] CTFCoordsData = new float2[(DimsCropped.X / 2 + 1) * DimsCropped.Y];
                    float2[] ShiftFactorsData = new float2[(DimsCropped.X / 2 + 1) * DimsCropped.Y];
                    for (int y = 0; y < DimsCropped.Y; y++)
                        for (int x = 0; x < DimsCropped.X / 2 + 1; x++)
                        {
                            int xx = x;
                            int yy = y < DimsCropped.Y / 2 + 1 ? y : y - DimsCropped.Y;

                            float xs = xx / (float)DimsRegion.X;
                            float ys = yy / (float)DimsRegion.Y;
                            float r = (float)Math.Sqrt(xs * xs + ys * ys);
                            float angle = (float)(Math.Atan2(yy, xx));

                            CTFCoordsData[y * (DimsCropped.X / 2 + 1) + x] = new float2(r / PixelSize, angle);
                            ShiftFactorsData[y * (DimsCropped.X / 2 + 1) + x] = new float2((float)-xx / DimsRegion.X * 2f * (float)Math.PI,
                                                                                          (float)-yy / DimsRegion.X * 2f * (float)Math.PI);
                        }

                    CTFCoords = new Image(CTFCoordsData, new int3(DimsCropped), true);
                    ShiftFactors = new Image(ShiftFactorsData, new int3(DimsCropped), true);
                }
                #endregion

                #region Get inverse sigma2 spectrum for this micrograph from Relion's model.star
                Image Sigma2Noise = new Image(new int3(DimsCropped), true);
                {
                    int GroupNumber = int.Parse(tableIn.GetRowValue(RowIndices[0], "rlnGroupNumber"));
                    //Star SigmaTable = new Star("D:\\rado27\\Refine3D\\run1_ct5_it009_half1_model.star", "data_model_group_" + GroupNumber);
                    Star SigmaTable = new Star(MainWindow.Options.ModelStarPath, "data_model_group_" + GroupNumber);
                    float[] SigmaValues = SigmaTable.GetColumn("rlnSigma2Noise").Select(v => float.Parse(v)).ToArray();

                    float[] Sigma2NoiseData = Sigma2Noise.GetHost(Intent.Write)[0];
                    Helper.ForEachElementFT(DimsCropped, (x, y, xx, yy, r, angle) =>
                    {
                        int ir = (int)r;
                        float val = 0;
                        if (ir < SigmaValues.Length && ir >= size / (50f / PixelSize) && ir < DimsCropped.X / 2)
                        {
                            if (SigmaValues[ir] != 0f)
                                val = 1f / SigmaValues[ir];
                        }
                        Sigma2NoiseData[y * (DimsCropped.X / 2 + 1) + x] = val;
                    });
                    float MaxSigma = MathHelper.Max(Sigma2NoiseData);
                    for (int i = 0; i < Sigma2NoiseData.Length; i++)
                        Sigma2NoiseData[i] /= MaxSigma;

                    Sigma2Noise.RemapToFT();
                }
                //Sigma2Noise.WriteMRC("d_sigma2noise.mrc");
                #endregion

                #region Initialize particle angles for both halves

                float3[] ParticleAngles1 = new float3[NParticles1];
                float3[] ParticleAngles2 = new float3[NParticles2];
                for (int p = 0; p < NParticles1; p++)
                    ParticleAngles1[p] = new float3(float.Parse(tableIn.GetRowValue(RowIndices1[p], "rlnAngleRot")),
                                                    float.Parse(tableIn.GetRowValue(RowIndices1[p], "rlnAngleTilt")),
                                                    float.Parse(tableIn.GetRowValue(RowIndices1[p], "rlnAnglePsi")));
                for (int p = 0; p < NParticles2; p++)
                    ParticleAngles2[p] = new float3(float.Parse(tableIn.GetRowValue(RowIndices2[p], "rlnAngleRot")),
                                                    float.Parse(tableIn.GetRowValue(RowIndices2[p], "rlnAngleTilt")),
                                                    float.Parse(tableIn.GetRowValue(RowIndices2[p], "rlnAnglePsi")));
                #endregion

                #region Prepare masks
                Image Masks1, Masks2;
                {
                    // Half 1
                    {
                        Image Volume = StageDataLoad.LoadMap(MainWindow.Options.MaskPath, new int2(1, 1), 0, typeof (float));
                        Image VolumePadded = Volume.AsPadded(Volume.Dims * MainWindow.Options.ProjectionOversample);
                        Volume.Dispose();
                        VolumePadded.RemapToFT(true);
                        Image VolMaskFT = VolumePadded.AsFFT(true);
                        VolumePadded.Dispose();

                        Image MasksFT = VolMaskFT.AsProjections(ParticleAngles1.Select(v => new float3(v.X * Helper.ToRad, v.Y * Helper.ToRad, v.Z * Helper.ToRad)).ToArray(),
                                                                new int2(DimsRegion),
                                                                MainWindow.Options.ProjectionOversample);
                        VolMaskFT.Dispose();

                        Masks1 = MasksFT.AsIFFT();
                        MasksFT.Dispose();

                        Masks1.RemapFromFT();

                        Parallel.ForEach(Masks1.GetHost(Intent.ReadWrite), slice =>
                        {
                            for (int i = 0; i < slice.Length; i++)
                                slice[i] = (Math.Max(2f, Math.Min(50f, slice[i])) - 2) / 48f;
                        });
                    }

                    // Half 2
                    {
                        Image Volume = StageDataLoad.LoadMap(MainWindow.Options.MaskPath, new int2(1, 1), 0, typeof(float));
                        Image VolumePadded = Volume.AsPadded(Volume.Dims * MainWindow.Options.ProjectionOversample);
                        Volume.Dispose();
                        VolumePadded.RemapToFT(true);
                        Image VolMaskFT = VolumePadded.AsFFT(true);
                        VolumePadded.Dispose();

                        Image MasksFT = VolMaskFT.AsProjections(ParticleAngles2.Select(v => new float3(v.X * Helper.ToRad, v.Y * Helper.ToRad, v.Z * Helper.ToRad)).ToArray(),
                                                                new int2(DimsRegion),
                                                                MainWindow.Options.ProjectionOversample);
                        VolMaskFT.Dispose();

                        Masks2 = MasksFT.AsIFFT();
                        MasksFT.Dispose();

                        Masks2.RemapFromFT();

                        Parallel.ForEach(Masks2.GetHost(Intent.ReadWrite), slice =>
                        {
                            for (int i = 0; i < slice.Length; i++)
                                slice[i] = (Math.Max(2f, Math.Min(50f, slice[i])) - 2) / 48f;
                        });
                    }
                }
                //Masks1.WriteMRC("d_masks1.mrc");
                //Masks2.WriteMRC("d_masks2.mrc");
                #endregion

                #region Load and prepare references for both halves
                Image VolRefFT1;
                {
                    Image Volume = StageDataLoad.LoadMap(MainWindow.Options.ReferencePath, new int2(1, 1), 0, typeof(float));
                    //GPU.Normalize(Volume.GetDevice(Intent.Read), Volume.GetDevice(Intent.Write), (uint)Volume.ElementsReal, 1);
                    Image VolumePadded = Volume.AsPadded(Volume.Dims * MainWindow.Options.ProjectionOversample);
                    Volume.Dispose();
                    VolumePadded.RemapToFT(true);
                    VolRefFT1 = VolumePadded.AsFFT(true);
                    VolumePadded.Dispose();
                }
                VolRefFT1.FreeDevice();

                Image VolRefFT2;
                {
                    // Can't assume there is a second half, but certainly hope so
                    string Half2Path = MainWindow.Options.ReferencePath;
                    if (Half2Path.Contains("half1"))
                        Half2Path = Half2Path.Replace("half1", "half2");

                    Image Volume = StageDataLoad.LoadMap(Half2Path, new int2(1, 1), 0, typeof(float));
                    //GPU.Normalize(Volume.GetDevice(Intent.Read), Volume.GetDevice(Intent.Write), (uint)Volume.ElementsReal, 1);
                    Image VolumePadded = Volume.AsPadded(Volume.Dims * MainWindow.Options.ProjectionOversample);
                    Volume.Dispose();
                    VolumePadded.RemapToFT(true);
                    VolRefFT2 = VolumePadded.AsFFT(true);
                    VolumePadded.Dispose();
                }
                VolRefFT2.FreeDevice();
                #endregion

                #region Prepare particles: group and resize to DimsCropped

                Image ParticleStackFT1 = new Image(IntPtr.Zero, new int3(DimsCropped.X, DimsCropped.Y, NParticles1 * Dims.Z / 3), true, true);
                {
                    GPU.CreatePolishing(ParticleStack1.GetDevice(Intent.Read),
                                        ParticleStackFT1.GetDevice(Intent.Write),
                                        Masks1.GetDevice(Intent.Read),
                                        new int2(DimsRegion),
                                        DimsCropped,
                                        NParticles1,
                                        Dims.Z);

                    ParticleStack1.FreeDevice();
                    Masks1.Dispose();

                    /*Image Amps = ParticleStackFT1.AsIFFT();
                    Amps.RemapFromFT();
                    Amps.WriteMRC("d_particlestackft1.mrc");
                    Amps.Dispose();*/
                }

                Image ParticleStackFT2 = new Image(IntPtr.Zero, new int3(DimsCropped.X, DimsCropped.Y, NParticles2 * Dims.Z / 3), true, true);
                {
                    GPU.CreatePolishing(ParticleStack2.GetDevice(Intent.Read),
                                        ParticleStackFT2.GetDevice(Intent.Write),
                                        Masks2.GetDevice(Intent.Read),
                                        new int2(DimsRegion),
                                        DimsCropped,
                                        NParticles2,
                                        Dims.Z);

                    ParticleStack1.FreeDevice();
                    Masks2.Dispose();

                    /*Image Amps = ParticleStackFT2.AsIFFT();
                    Amps.RemapFromFT();
                    Amps.WriteMRC("d_particlestackft2.mrc");
                    Amps.Dispose();*/
                }
                #endregion

                Image Projections1 = new Image(IntPtr.Zero, new int3(DimsCropped.X, DimsCropped.Y, NParticles1 * Dims.Z / 3), true, true);
                Image Projections2 = new Image(IntPtr.Zero, new int3(DimsCropped.X, DimsCropped.Y, NParticles2 * Dims.Z / 3), true, true);

                Image Shifts1 = new Image(new int3(NParticles1, Dims.Z / 3, 1), false, true);
                float3[] Angles1 = new float3[NParticles1 * Dims.Z / 3];
                CTFStruct[] CTFParams1 = new CTFStruct[NParticles1 * Dims.Z / 3];

                Image Shifts2 = new Image(new int3(NParticles2, Dims.Z / 3, 1), false, true);
                float3[] Angles2 = new float3[NParticles2 * Dims.Z / 3];
                CTFStruct[] CTFParams2 = new CTFStruct[NParticles2 * Dims.Z / 3];

                float[] BFacs =
                {
                    -3.86f,
                    0.00f,
                    -17.60f,
                    -35.24f,
                    -57.48f,
                    -93.51f,
                    -139.57f,
                    -139.16f
                };

                #region Initialize defocus and phase shift values
                float[] InitialDefoci1 = new float[NParticles1 * (Dims.Z / 3)];
                float[] InitialPhaseShifts1 = new float[NParticles1 * (Dims.Z / 3)];
                float[] InitialDefoci2 = new float[NParticles2 * (Dims.Z / 3)];
                float[] InitialPhaseShifts2 = new float[NParticles2 * (Dims.Z / 3)];
                for (int z = 0, i = 0; z < Dims.Z / 3; z++)
                {
                    for (int p = 0; p < NParticles1; p++, i++)
                    {
                        InitialDefoci1[i] = GridCTF.GetInterpolated(new float3((float)Origins1[p].X / Dims.X,
                                                                               (float)Origins1[p].Y / Dims.Y,
                                                                               (float)(z * 3 + 1) / (Dims.Z - 1)));
                        InitialPhaseShifts1[i] = GridCTFPhase.GetInterpolated(new float3((float)Origins1[p].X / Dims.X,
                                                                                         (float)Origins1[p].Y / Dims.Y,
                                                                                         (float)(z * 3 + 1) / (Dims.Z - 1)));

                        CTF Alt = CTF.GetCopy();
                        Alt.PixelSize = (decimal)PixelSize;
                        Alt.PixelSizeDelta = 0;
                        Alt.Defocus = (decimal)InitialDefoci1[i];
                        Alt.PhaseShift = (decimal)InitialPhaseShifts1[i];
                        //Alt.Bfactor = (decimal)BFacs[z];

                        CTFParams1[i] = Alt.ToStruct();
                    }
                }
                for (int z = 0, i = 0; z < Dims.Z / 3; z++)
                {
                    for (int p = 0; p < NParticles2; p++, i++)
                    {
                        InitialDefoci2[i] = GridCTF.GetInterpolated(new float3((float)Origins2[p].X / Dims.X,
                                                                               (float)Origins2[p].Y / Dims.Y,
                                                                               (float)(z * 3 + 1) / (Dims.Z - 1)));
                        InitialPhaseShifts2[i] = GridCTFPhase.GetInterpolated(new float3((float)Origins2[p].X / Dims.X,
                                                                                         (float)Origins2[p].Y / Dims.Y,
                                                                                         (float)(z * 3 + 1) / (Dims.Z - 1)));

                        CTF Alt = CTF.GetCopy();
                        Alt.PixelSize = (decimal)PixelSize;
                        Alt.PixelSizeDelta = 0;
                        Alt.Defocus = (decimal)InitialDefoci2[i];
                        Alt.PhaseShift = (decimal)InitialPhaseShifts2[i];
                        //Alt.Bfactor = (decimal)BFacs[z];

                        CTFParams2[i] = Alt.ToStruct();
                    }
                }
                #endregion

                #region SetPositions lambda
                Action<double[]> SetPositions = input =>
                {
                    float BorderZ = 0.5f / (Dims.Z / 3);

                    GridX = new CubicGrid(new int3(NParticles, 1, 2), input.Take(NParticles * 2).Select(v => (float)v).ToArray());
                    GridY = new CubicGrid(new int3(NParticles, 1, 2), input.Skip(NParticles * 2 * 1).Take(NParticles * 2).Select(v => (float)v).ToArray());

                    float[] AlteredX = GridX.GetInterpolatedNative(new int3(NParticles, 1, Dims.Z / 3), new float3(0, 0, BorderZ));
                    float[] AlteredY = GridY.GetInterpolatedNative(new int3(NParticles, 1, Dims.Z / 3), new float3(0, 0, BorderZ));

                    GridRot = new CubicGrid(new int3(NParticles, 1, 2), input.Skip(NParticles * 2 * 2).Take(NParticles * 2).Select(v => (float)v).ToArray());
                    GridTilt = new CubicGrid(new int3(NParticles, 1, 2), input.Skip(NParticles * 2 * 3).Take(NParticles * 2).Select(v => (float)v).ToArray());
                    GridPsi = new CubicGrid(new int3(NParticles, 1, 2), input.Skip(NParticles * 2 * 4).Take(NParticles * 2).Select(v => (float)v).ToArray());

                    float[] AlteredRot = GridRot.GetInterpolatedNative(new int3(NParticles, 1, Dims.Z / 3), new float3(0, 0, BorderZ));
                    float[] AlteredTilt = GridTilt.GetInterpolatedNative(new int3(NParticles, 1, Dims.Z / 3), new float3(0, 0, BorderZ));
                    float[] AlteredPsi = GridPsi.GetInterpolatedNative(new int3(NParticles, 1, Dims.Z / 3), new float3(0, 0, BorderZ));

                    float[] ShiftData1 = Shifts1.GetHost(Intent.Write)[0];
                    float[] ShiftData2 = Shifts2.GetHost(Intent.Write)[0];

                    for (int z = 0; z < Dims.Z / 3; z++)
                    {
                        // Half 1
                        for (int p = 0; p < NParticles1; p++)
                        {
                            int i1 = z * NParticles1 + p;
                            int i = z * NParticles + p;
                            ShiftData1[i1 * 2] = AlteredX[i];
                            ShiftData1[i1 * 2 + 1] = AlteredY[i];

                            Angles1[i1] = new float3(AlteredRot[i] * 1f * Helper.ToRad, AlteredTilt[i] * 1f * Helper.ToRad, AlteredPsi[i] * 1f * Helper.ToRad);
                        }

                        // Half 2
                        for (int p = 0; p < NParticles2; p++)
                        {
                            int i2 = z * NParticles2 + p;
                            int i = z * NParticles + NParticles1 + p;
                            ShiftData2[i2 * 2] = AlteredX[i];
                            ShiftData2[i2 * 2 + 1] = AlteredY[i];

                            Angles2[i2] = new float3(AlteredRot[i] * 1f * Helper.ToRad, AlteredTilt[i] * 1f * Helper.ToRad, AlteredPsi[i] * 1f * Helper.ToRad);
                        }
                    }
                };
                #endregion

                #region EvalIndividuals lambda
                Func<double[], bool, double[]> EvalIndividuals = (input, redoProj) =>
                {
                    SetPositions(input);

                    if (redoProj)
                    {
                        GPU.ProjectForward(VolRefFT1.GetDevice(Intent.Read),
                                           Projections1.GetDevice(Intent.Write),
                                           VolRefFT1.Dims,
                                           DimsCropped,
                                           Helper.ToInterleaved(Angles1),
                                           MainWindow.Options.ProjectionOversample,
                                           (uint)(NParticles1 * Dims.Z / 3));

                        GPU.ProjectForward(VolRefFT2.GetDevice(Intent.Read),
                                           Projections2.GetDevice(Intent.Write),
                                           VolRefFT2.Dims,
                                           DimsCropped,
                                           Helper.ToInterleaved(Angles2),
                                           MainWindow.Options.ProjectionOversample,
                                           (uint)(NParticles2 * Dims.Z / 3));
                    }

                    /*{
                        Image ProjectionsAmps = Projections1.AsIFFT();
                        ProjectionsAmps.RemapFromFT();
                        ProjectionsAmps.WriteMRC("d_projectionsamps1.mrc");
                        ProjectionsAmps.Dispose();
                    }
                    {
                        Image ProjectionsAmps = Projections2.AsIFFT();
                        ProjectionsAmps.RemapFromFT();
                        ProjectionsAmps.WriteMRC("d_projectionsamps2.mrc");
                        ProjectionsAmps.Dispose();
                    }*/

                    float[] Diff1 = new float[NParticles1];
                    float[] DiffAll1 = new float[NParticles1 * (Dims.Z / 3)];
                    GPU.PolishingGetDiff(ParticleStackFT1.GetDevice(Intent.Read),
                                         Projections1.GetDevice(Intent.Read),
                                         ShiftFactors.GetDevice(Intent.Read),
                                         CTFCoords.GetDevice(Intent.Read),
                                         CTFParams1,
                                         Sigma2Noise.GetDevice(Intent.Read),
                                         DimsCropped,
                                         Shifts1.GetDevice(Intent.Read),
                                         Diff1,
                                         DiffAll1,
                                         (uint)NParticles1,
                                         (uint)Dims.Z / 3);

                    float[] Diff2 = new float[NParticles2];
                    float[] DiffAll2 = new float[NParticles2 * (Dims.Z / 3)];
                    GPU.PolishingGetDiff(ParticleStackFT2.GetDevice(Intent.Read),
                                         Projections2.GetDevice(Intent.Read),
                                         ShiftFactors.GetDevice(Intent.Read),
                                         CTFCoords.GetDevice(Intent.Read),
                                         CTFParams2,
                                         Sigma2Noise.GetDevice(Intent.Read),
                                         DimsCropped,
                                         Shifts2.GetDevice(Intent.Read),
                                         Diff2,
                                         DiffAll2,
                                         (uint)NParticles2,
                                         (uint)Dims.Z / 3);

                    double[] DiffBoth = new double[NParticles];
                    for (int p = 0; p < NParticles1; p++)
                        DiffBoth[p] = Diff1[p];
                    for (int p = 0; p < NParticles2; p++)
                        DiffBoth[NParticles1 + p] = Diff2[p];

                    return DiffBoth;
                };
                #endregion

                Func<double[], double> Eval = input =>
                {
                    float Result = MathHelper.Mean(EvalIndividuals(input, true).Select(v => (float)v)) * NParticles;
                    Debug.WriteLine(Result);
                    return Result;
                };

                Func<double[], double[]> Grad = input =>
                {
                    SetPositions(input);

                    GPU.ProjectForward(VolRefFT1.GetDevice(Intent.Read),
                                       Projections1.GetDevice(Intent.Write),
                                       VolRefFT1.Dims,
                                       DimsCropped,
                                       Helper.ToInterleaved(Angles1),
                                       MainWindow.Options.ProjectionOversample,
                                       (uint)(NParticles1 * Dims.Z / 3));

                    GPU.ProjectForward(VolRefFT2.GetDevice(Intent.Read),
                                       Projections2.GetDevice(Intent.Write),
                                       VolRefFT2.Dims,
                                       DimsCropped,
                                       Helper.ToInterleaved(Angles2),
                                       MainWindow.Options.ProjectionOversample,
                                       (uint)(NParticles2 * Dims.Z / 3));

                    double[] Result = new double[input.Length];

                    double Step = 0.1;
                    int NVariables = 10;    // (Shift + Euler) * 2
                    for (int v = 0; v < NVariables; v++)
                    {
                        double[] InputPlus = new double[input.Length];
                        for (int i = 0; i < input.Length; i++)
                        {
                            int iv = i / NParticles;

                            if (iv == v)
                                InputPlus[i] = input[i] + Step;
                            else
                                InputPlus[i] = input[i];
                        }
                        double[] ScorePlus = EvalIndividuals(InputPlus, v >= 4);

                        double[] InputMinus = new double[input.Length];
                        for (int i = 0; i < input.Length; i++)
                        {
                            int iv = i / NParticles;

                            if (iv == v)
                                InputMinus[i] = input[i] - Step;
                            else
                                InputMinus[i] = input[i];
                        }
                        double[] ScoreMinus = EvalIndividuals(InputMinus, v >= 4);

                        for (int i = 0; i < NParticles; i++)
                            Result[v * NParticles + i] = (ScorePlus[i] - ScoreMinus[i]) / (Step * 2.0);
                    }

                    return Result;
                };

                double[] StartParams = new double[NParticles * 2 * 5];
                
                for (int i = 0; i < NParticles * 2; i++)
                {
                    int p = i % NParticles;
                    StartParams[NParticles * 2 * 0 + i] = 0;
                    StartParams[NParticles * 2 * 1 + i] = 0;

                    if (p < NParticles1)
                    {
                        StartParams[NParticles * 2 * 2 + i] = ParticleAngles1[p].X / 1.0;
                        StartParams[NParticles * 2 * 3 + i] = ParticleAngles1[p].Y / 1.0;
                        StartParams[NParticles * 2 * 4 + i] = ParticleAngles1[p].Z / 1.0;
                    }
                    else
                    {
                        p -= NParticles1;
                        StartParams[NParticles * 2 * 2 + i] = ParticleAngles2[p].X / 1.0;
                        StartParams[NParticles * 2 * 3 + i] = ParticleAngles2[p].Y / 1.0;
                        StartParams[NParticles * 2 * 4 + i] = ParticleAngles2[p].Z / 1.0;
                    }
                }

                BroydenFletcherGoldfarbShanno Optimizer = new BroydenFletcherGoldfarbShanno(StartParams.Length, Eval, Grad);
                Optimizer.Epsilon = 3e-7;
                
                Optimizer.Maximize(StartParams);

                #region Calculate particle quality for high frequencies
                float[] ParticleQuality = new float[NParticles * (Dims.Z / 3)];
                {
                    Sigma2Noise.Dispose();
                    Sigma2Noise = new Image(new int3(DimsCropped), true);
                    {
                        int GroupNumber = int.Parse(tableIn.GetRowValue(RowIndices[0], "rlnGroupNumber"));
                        //Star SigmaTable = new Star("D:\\rado27\\Refine3D\\run1_ct5_it009_half1_model.star", "data_model_group_" + GroupNumber);
                        Star SigmaTable = new Star(MainWindow.Options.ModelStarPath, "data_model_group_" + GroupNumber);
                        float[] SigmaValues = SigmaTable.GetColumn("rlnSigma2Noise").Select(v => float.Parse(v)).ToArray();

                        float[] Sigma2NoiseData = Sigma2Noise.GetHost(Intent.Write)[0];
                        Helper.ForEachElementFT(DimsCropped, (x, y, xx, yy, r, angle) =>
                        {
                            int ir = (int)r;
                            float val = 0;
                            if (ir < SigmaValues.Length && ir >= size / (4.0f / PixelSize) && ir < DimsCropped.X / 2)
                            {
                                if (SigmaValues[ir] != 0f)
                                    val = 1f / SigmaValues[ir] / (ir * 3.14f);
                            }
                            Sigma2NoiseData[y * (DimsCropped.X / 2 + 1) + x] = val;
                        });
                        float MaxSigma = MathHelper.Max(Sigma2NoiseData);
                        for (int i = 0; i < Sigma2NoiseData.Length; i++)
                            Sigma2NoiseData[i] /= MaxSigma;

                        Sigma2Noise.RemapToFT();
                    }
                    //Sigma2Noise.WriteMRC("d_sigma2noiseScore.mrc");

                    SetPositions(StartParams);

                    GPU.ProjectForward(VolRefFT1.GetDevice(Intent.Read),
                                       Projections1.GetDevice(Intent.Write),
                                       VolRefFT1.Dims,
                                       DimsCropped,
                                       Helper.ToInterleaved(Angles1),
                                       MainWindow.Options.ProjectionOversample,
                                       (uint)(NParticles1 * Dims.Z / 3));

                    GPU.ProjectForward(VolRefFT2.GetDevice(Intent.Read),
                                       Projections2.GetDevice(Intent.Write),
                                       VolRefFT2.Dims,
                                       DimsCropped,
                                       Helper.ToInterleaved(Angles2),
                                       MainWindow.Options.ProjectionOversample,
                                       (uint)(NParticles2 * Dims.Z / 3));

                    float[] Diff1 = new float[NParticles1];
                    float[] ParticleQuality1 = new float[NParticles1 * (Dims.Z / 3)];
                    GPU.PolishingGetDiff(ParticleStackFT1.GetDevice(Intent.Read),
                                         Projections1.GetDevice(Intent.Read),
                                         ShiftFactors.GetDevice(Intent.Read),
                                         CTFCoords.GetDevice(Intent.Read),
                                         CTFParams1,
                                         Sigma2Noise.GetDevice(Intent.Read),
                                         DimsCropped,
                                         Shifts1.GetDevice(Intent.Read),
                                         Diff1,
                                         ParticleQuality1,
                                         (uint)NParticles1,
                                         (uint)Dims.Z / 3);

                    float[] Diff2 = new float[NParticles2];
                    float[] ParticleQuality2 = new float[NParticles2 * (Dims.Z / 3)];
                    GPU.PolishingGetDiff(ParticleStackFT2.GetDevice(Intent.Read),
                                         Projections2.GetDevice(Intent.Read),
                                         ShiftFactors.GetDevice(Intent.Read),
                                         CTFCoords.GetDevice(Intent.Read),
                                         CTFParams2,
                                         Sigma2Noise.GetDevice(Intent.Read),
                                         DimsCropped,
                                         Shifts2.GetDevice(Intent.Read),
                                         Diff2,
                                         ParticleQuality2,
                                         (uint)NParticles2,
                                         (uint)Dims.Z / 3);

                    for (int z = 0; z < Dims.Z / 3; z++)
                    {
                        for (int p = 0; p < NParticles1; p++)
                            ParticleQuality[z * NParticles + p] = ParticleQuality1[z * NParticles1 + p];

                        for (int p = 0; p < NParticles2; p++)
                            ParticleQuality[z * NParticles + NParticles1 + p] = ParticleQuality2[z * NParticles2 + p];
                    }
                }
                #endregion

                lock (tableOut)     // Only changing cell values, but better be safe in case table implementation changes later
                {
                    GridX = new CubicGrid(new int3(NParticles, 1, 2), Optimizer.Solution.Take(NParticles * 2).Select(v => (float)v).ToArray());
                    GridY = new CubicGrid(new int3(NParticles, 1, 2), Optimizer.Solution.Skip(NParticles * 2 * 1).Take(NParticles * 2).Select(v => (float)v).ToArray());
                    float[] AlteredX = GridX.GetInterpolated(new int3(NParticles, 1, Dims.Z), new float3(0, 0, 0));
                    float[] AlteredY = GridY.GetInterpolated(new int3(NParticles, 1, Dims.Z), new float3(0, 0, 0));

                    GridRot = new CubicGrid(new int3(NParticles, 1, 2), Optimizer.Solution.Skip(NParticles * 2 * 2).Take(NParticles * 2).Select(v => (float)v).ToArray());
                    GridTilt = new CubicGrid(new int3(NParticles, 1, 2), Optimizer.Solution.Skip(NParticles * 2 * 3).Take(NParticles * 2).Select(v => (float)v).ToArray());
                    GridPsi = new CubicGrid(new int3(NParticles, 1, 2), Optimizer.Solution.Skip(NParticles * 2 * 4).Take(NParticles * 2).Select(v => (float)v).ToArray());
                    float[] AlteredRot = GridRot.GetInterpolated(new int3(NParticles, 1, Dims.Z), new float3(0, 0, 0));
                    float[] AlteredTilt = GridTilt.GetInterpolated(new int3(NParticles, 1, Dims.Z), new float3(0, 0, 0));
                    float[] AlteredPsi = GridPsi.GetInterpolated(new int3(NParticles, 1, Dims.Z), new float3(0, 0, 0));
                    
                    for (int i = 0; i < TableOutIndices.Count; i++)
                    {
                        int p = i % NParticles;
                        int z = i / NParticles;
                        float Defocus = 0, PhaseShift = 0;

                        if (p < NParticles1)
                        {
                            Defocus = GridCTF.GetInterpolated(new float3((float)Origins1[p].X / Dims.X,
                                                                         (float)Origins1[p].Y / Dims.Y,
                                                                         (float)z / (Dims.Z - 1)));
                            PhaseShift = GridCTFPhase.GetInterpolated(new float3((float)Origins1[p].X / Dims.X,
                                                                                 (float)Origins1[p].Y / Dims.Y,
                                                                                 (float)z / (Dims.Z - 1)));
                        }
                        else
                        {
                            p -= NParticles1;
                            Defocus = GridCTF.GetInterpolated(new float3((float)Origins2[p].X / Dims.X,
                                                                         (float)Origins2[p].Y / Dims.Y,
                                                                         (float)z / (Dims.Z - 1)));
                            PhaseShift = GridCTFPhase.GetInterpolated(new float3((float)Origins2[p].X / Dims.X,
                                                                                 (float)Origins2[p].Y / Dims.Y,
                                                                                 (float)z / (Dims.Z - 1)));
                        }

                        tableOut.SetRowValue(TableOutIndices[i], "rlnOriginX", AlteredX[i].ToString(CultureInfo.InvariantCulture));
                        tableOut.SetRowValue(TableOutIndices[i], "rlnOriginY", AlteredY[i].ToString(CultureInfo.InvariantCulture));
                        tableOut.SetRowValue(TableOutIndices[i], "rlnAngleRot", (-AlteredRot[i]).ToString(CultureInfo.InvariantCulture));
                        tableOut.SetRowValue(TableOutIndices[i], "rlnAngleTilt", (-AlteredTilt[i]).ToString(CultureInfo.InvariantCulture));
                        tableOut.SetRowValue(TableOutIndices[i], "rlnAnglePsi", (-AlteredPsi[i]).ToString(CultureInfo.InvariantCulture));
                        tableOut.SetRowValue(TableOutIndices[i], "rlnDefocusU", ((Defocus + (float)CTF.DefocusDelta / 2f) * 1e4f).ToString(CultureInfo.InvariantCulture));
                        tableOut.SetRowValue(TableOutIndices[i], "rlnDefocusV", ((Defocus - (float)CTF.DefocusDelta / 2f) * 1e4f).ToString(CultureInfo.InvariantCulture));
                        tableOut.SetRowValue(TableOutIndices[i], "rlnPhaseShift", (PhaseShift * 180f).ToString(CultureInfo.InvariantCulture));
                        tableOut.SetRowValue(TableOutIndices[i], "rlnCtfFigureOfMerit", (ParticleQuality[(z / 3) * NParticles + (i % NParticles)]).ToString(CultureInfo.InvariantCulture));

                        tableOut.SetRowValue(TableOutIndices[i], "rlnMagnification", ((float)MainWindow.Options.CTFDetectorPixel * 10000f / PixelSize).ToString());
                    }
                }

                VolRefFT1.Dispose();
                VolRefFT2.Dispose();
                Projections1.Dispose();
                Projections2.Dispose();
                Sigma2Noise.Dispose();
                ParticleStackFT1.Dispose();
                ParticleStackFT2.Dispose();
                Shifts1.Dispose();
                Shifts2.Dispose();
                CTFCoords.Dispose();
                ShiftFactors.Dispose();

                ParticleStack1.Dispose();
                ParticleStack2.Dispose();
                PSStack1.Dispose();
                PSStack2.Dispose();
            }
            
            // Write movies to disk asynchronously, so the next micrograph can load.
            Thread SaveThread = new Thread(() =>
            {
                GPU.SetDevice(CurrentDevice);   // It's a separate thread, make sure it's using the same device

                ParticleStackAll.WriteMRC(ParticleMoviesPath, ParticlesHeader);
                //ParticleStackAll.WriteMRC("D:\\gala\\particlemovies\\" + RootName + "_particles.mrcs", ParticlesHeader);
                ParticleStackAll.Dispose();

                PSStackAll.WriteMRC(ParticleCTFMoviesPath);
                //PSStackAll.WriteMRC("D:\\rado27\\particlectfmovies\\" + RootName + "_particlectf.mrcs");
                PSStackAll.Dispose();
            });
            SaveThread.Start();
        }
Example #16
0
        public Image AlignOneTiltMovie(Image tiltMovie, Image template, float initialAngle, float2 initialShift, float resolution)
        {
            float DownscaleFactor = (float)CTF.PixelSize * 2 / resolution;

            //template.Bandpass(0.02f, DownscaleFactor, false);
            //tiltMovie.Bandpass(0.02f, DownscaleFactor, false);

            //template = template.AsPadded(new int2(template.Dims) - 512);

            int2 DimsTemplate = new int2(template.Dims);
            int2 DimsTemplateCoarse = new int2(DimsTemplate) * DownscaleFactor / 2 * 2;
            int2 DimsFrame = new int2(tiltMovie.Dims);
            int NFrames = tiltMovie.Dims.Z;

            //GPU.Normalize(tiltMovie.GetDevice(Intent.Read),
            //              tiltMovie.GetDevice(Intent.Write),
            //              (uint)tiltMovie.ElementsSliceReal,
            //              (uint)tiltMovie.Dims.Z);

            Image TemplateCoarse = template.AsScaled(DimsTemplateCoarse);

            float GlobalAngle = initialAngle;
            float ConditioningAngle = 180f / DimsFrame.X;
            CubicGrid GridFrameX = new CubicGrid(new int3(1, 1, 1), initialShift.X, initialShift.X, Dimension.X);
            CubicGrid GridFrameY = new CubicGrid(new int3(1, 1, 1), initialShift.Y, initialShift.Y, Dimension.X);

            Action<double[]> SetFromVector = input =>
            {
                GlobalAngle = (float)input[0] * ConditioningAngle;
                GridFrameX = new CubicGrid(GridFrameX.Dimensions, input.Skip(1).Take((int)GridFrameX.Dimensions.Elements()).Select(v => (float)v).ToArray());
                GridFrameY = new CubicGrid(GridFrameY.Dimensions, input.Skip(1 + (int)GridFrameX.Dimensions.Elements()).Take((int)GridFrameY.Dimensions.Elements()).Select(v => (float)v).ToArray());
            };

            Func<double[], double[]> EvalIndividual = input =>
            {
                SetFromVector(input);

                Image Transformed;

                float GridStep = 1f / Math.Max(NFrames - 1, 1);
                float2[] FrameShifts = new float2[NFrames];
                for (int i = 0; i < NFrames; i++)
                    FrameShifts[i] = new float2(GridFrameX.GetInterpolated(new float3(0.5f, 0.5f, i * GridStep)),
                                                GridFrameY.GetInterpolated(new float3(0.5f, 0.5f, i * GridStep)));

                float[] FrameAngles = new float[NFrames].Select(v => -GlobalAngle * Helper.ToRad).ToArray();

                Image MovieCopy = new Image(IntPtr.Zero, tiltMovie.Dims);
                //MovieCopy.ShiftSlicesMassive(FrameShifts);

                GPU.ShiftAndRotate2D(tiltMovie.GetDevice(Intent.Read),
                                     MovieCopy.GetDevice(Intent.Write),
                                     DimsFrame,
                                     Helper.ToInterleaved(FrameShifts),
                                     FrameAngles,
                                     (uint)NFrames);

                Transformed = MovieCopy.AsPadded(DimsTemplate);
                MovieCopy.Dispose();

                Transformed.MultiplySlices(template);

                Image Sums = new Image(IntPtr.Zero, new int3(NFrames, 1, 1));
                GPU.Sum(Transformed.GetDevice(Intent.Read),
                        Sums.GetDevice(Intent.Write),
                        (uint)Transformed.ElementsSliceReal,
                        (uint)NFrames);

                Transformed.Dispose();

                double[] Result = new double[NFrames];
                for (int i = 0; i < NFrames; i++)
                    Result[i] = Sums.GetHost(Intent.Read)[0][i] / Transformed.ElementsSliceReal * 100;

                return Result;
            };

            Func<double[], double> Eval = input =>
            {
                double[] Scores = EvalIndividual(input);
                double Score = Scores.Sum();
                Debug.WriteLine(Score);

                return Score;
            };

            Func<double[], double[]> Grad = input =>
            {
                double Delta = 0.1 / DownscaleFactor;
                double[] Result = new double[input.Length];

                for (int i = 0; i < input.Length; i++)
                {
                    double[] InputPlus = input.ToArray();
                    InputPlus[i] += Delta;
                    double ScorePlus = EvalIndividual(InputPlus).Sum();

                    double[] InputMinus = input.ToArray();
                    InputMinus[i] -= Delta;
                    double ScoreMinus = EvalIndividual(InputMinus).Sum();

                    Result[i] = (ScorePlus - ScoreMinus) / (Delta * 2);
                }

                return Result;
            };

            List<double> StartList = new List<double>();
            StartList.Add(GlobalAngle / ConditioningAngle);
            StartList.AddRange(GridFrameX.FlatValues.Select(v => (double)v));
            StartList.AddRange(GridFrameY.FlatValues.Select(v => (double)v));
            double[] StartVector = StartList.ToArray();

            BroydenFletcherGoldfarbShanno Optimizer = new BroydenFletcherGoldfarbShanno(StartVector.Length, Eval, Grad);
            Optimizer.Maximize(StartVector);

            TemplateCoarse.Dispose();

            return null;
        }
Example #17
0
        public void ProcessCTFOneAngle(Image angleImage,
                                       float angle,
                                       bool fromScratch,
                                       bool fixAstigmatism,
                                       float2 astigmatism,
                                       CTF previousCTF,
                                       CubicGrid previousGrid,
                                       Cubic1D previousBackground,
                                       Cubic1D previousScale,
                                       out CTF thisCTF,
                                       out CubicGrid thisGrid,
                                       out float2[] thisPS1D,
                                       out Cubic1D thisBackground,
                                       out Cubic1D thisScale,
                                       out Image thisPS2D)
        {
            CTF TempCTF = previousCTF != null ? previousCTF.GetCopy() : new CTF();
            float2[] TempPS1D = null;
            Cubic1D TempBackground = null, TempScale = null;
            CubicGrid TempGrid = null;

            #region Dimensions and grids

            int NFrames = angleImage.Dims.Z;
            int2 DimsImage = angleImage.DimsSlice;
            int2 DimsRegion = new int2(MainWindow.Options.CTFWindow, MainWindow.Options.CTFWindow);

            float OverlapFraction = 0.5f;
            int2 DimsPositionGrid;
            int3[] PositionGrid = Helper.GetEqualGridSpacing(DimsImage, new int2(DimsRegion.X, DimsRegion.Y), OverlapFraction, out DimsPositionGrid);
            int NPositions = (int)DimsPositionGrid.Elements();

            if (previousGrid == null)
                TempGrid = new CubicGrid(new int3(2, 2, 1));
            else
                TempGrid = new CubicGrid(new int3(2, 2, 1), previousGrid.FlatValues);

            bool CTFSpace = true;
            bool CTFTime = false;
            int3 CTFSpectraGrid = new int3(DimsPositionGrid.X, DimsPositionGrid.Y, 1);

            int MinFreqInclusive = (int)(MainWindow.Options.CTFRangeMin * DimsRegion.X / 2);
            int MaxFreqExclusive = (int)(MainWindow.Options.CTFRangeMax * DimsRegion.X / 2);
            int NFreq = MaxFreqExclusive - MinFreqInclusive;

            #endregion

            #region Allocate GPU memory

            Image CTFSpectra = new Image(IntPtr.Zero, new int3(DimsRegion.X, DimsRegion.X, (int)CTFSpectraGrid.Elements()), true);
            Image CTFMean = new Image(IntPtr.Zero, new int3(DimsRegion), true);
            Image CTFCoordsCart = new Image(new int3(DimsRegion), true, true);
            Image CTFCoordsPolarTrimmed = new Image(new int3(NFreq, DimsRegion.X, 1), false, true);

            #endregion

            // Extract movie regions, create individual spectra in Cartesian coordinates and their mean.

            #region Create spectra

            GPU.CreateSpectra(angleImage.GetDevice(Intent.Read),
                              DimsImage,
                              NFrames,
                              PositionGrid,
                              NPositions,
                              DimsRegion,
                              CTFSpectraGrid,
                              CTFSpectra.GetDevice(Intent.Write),
                              CTFMean.GetDevice(Intent.Write));
            angleImage.FreeDevice(); // Won't need it in this method anymore.

            #endregion

            // Populate address arrays for later.

            #region Init addresses

            {
                float2[] CoordsData = new float2[CTFCoordsCart.ElementsSliceComplex];

                Helper.ForEachElementFT(DimsRegion, (x, y, xx, yy, r, a) => CoordsData[y * (DimsRegion.X / 2 + 1) + x] = new float2(r, a));
                CTFCoordsCart.UpdateHostWithComplex(new[] { CoordsData });

                CoordsData = new float2[NFreq * DimsRegion.X];
                Helper.ForEachElement(CTFCoordsPolarTrimmed.DimsSlice, (x, y) =>
                {
                    float Angle = ((float)y / DimsRegion.X + 0.5f) * (float)Math.PI;
                    float Ny = 1f / DimsRegion.X;
                    CoordsData[y * NFreq + x] = new float2((x + MinFreqInclusive) * Ny, Angle);
                });
                CTFCoordsPolarTrimmed.UpdateHostWithComplex(new[] { CoordsData });
            }

            #endregion

            // Retrieve average 1D spectrum from CTFMean (not corrected for astigmatism yet).

            #region Initial 1D spectrum

            {
                Image CTFAverage1D = new Image(IntPtr.Zero, new int3(DimsRegion.X / 2, 1, 1));

                GPU.CTFMakeAverage(CTFMean.GetDevice(Intent.Read),
                                   CTFCoordsCart.GetDevice(Intent.Read),
                                   (uint)CTFMean.ElementsSliceReal,
                                   (uint)DimsRegion.X,
                                   new[] { new CTF().ToStruct() },
                                   new CTF().ToStruct(),
                                   0,
                                   (uint)DimsRegion.X / 2,
                                   null,
                                   1,
                                   CTFAverage1D.GetDevice(Intent.Write));

                //CTFAverage1D.WriteMRC("CTFAverage1D.mrc");

                float[] CTFAverage1DData = CTFAverage1D.GetHost(Intent.Read)[0];
                float2[] ForPS1D = new float2[DimsRegion.X / 2];
                for (int i = 0; i < ForPS1D.Length; i++)
                    ForPS1D[i] = new float2((float)i / DimsRegion.X, (float)Math.Round(CTFAverage1DData[i], 4));
                TempPS1D = ForPS1D;

                CTFAverage1D.Dispose();
            }

            #endregion

            #region Background fitting methods

            Action UpdateBackgroundFit = () =>
            {
                float2[] ForPS1D = TempPS1D.Skip(Math.Max(5, MinFreqInclusive / 2)).ToArray();
                Cubic1D.FitCTF(ForPS1D,
                               v => v.Select(x => TempCTF.Get1D(x / (float)TempCTF.PixelSize, true)).ToArray(),
                               TempCTF.GetZeros(),
                               TempCTF.GetPeaks(),
                               out TempBackground,
                               out TempScale);
            };

            Action<bool> UpdateRotationalAverage = keepbackground =>
            {
                float[] MeanData = CTFMean.GetHost(Intent.Read)[0];

                Image CTFMeanCorrected = new Image(new int3(DimsRegion), true);
                float[] MeanCorrectedData = CTFMeanCorrected.GetHost(Intent.Write)[0];

                // Subtract current background estimate from spectra, populate coords.
                Helper.ForEachElementFT(DimsRegion,
                                        (x, y, xx, yy, r, a) =>
                                        {
                                            int i = y * (DimsRegion.X / 2 + 1) + x;
                                            MeanCorrectedData[i] = MeanData[i] - TempBackground.Interp(r / DimsRegion.X);
                                        });

                Image CTFAverage1D = new Image(IntPtr.Zero, new int3(DimsRegion.X / 2, 1, 1));

                GPU.CTFMakeAverage(CTFMeanCorrected.GetDevice(Intent.Read),
                                   CTFCoordsCart.GetDevice(Intent.Read),
                                   (uint)CTFMeanCorrected.DimsEffective.ElementsSlice(),
                                   (uint)DimsRegion.X,
                                   new[] { TempCTF.ToStruct() },
                                   TempCTF.ToStruct(),
                                   0,
                                   (uint)DimsRegion.X / 2,
                                   null,
                                   1,
                                   CTFAverage1D.GetDevice(Intent.Write));

                //CTFAverage1D.WriteMRC("CTFAverage1D.mrc");

                float[] RotationalAverageData = CTFAverage1D.GetHost(Intent.Read)[0];
                float2[] ForPS1D = new float2[TempPS1D.Length];
                if (keepbackground)
                    for (int i = 0; i < ForPS1D.Length; i++)
                        ForPS1D[i] = new float2((float)i / DimsRegion.X, RotationalAverageData[i] + TempBackground.Interp((float)i / DimsRegion.X));
                else
                    for (int i = 0; i < ForPS1D.Length; i++)
                        ForPS1D[i] = new float2((float)i / DimsRegion.X, RotationalAverageData[i]);
                MathHelper.UnNaN(ForPS1D);

                TempPS1D = ForPS1D;

                CTFMeanCorrected.Dispose();
                CTFAverage1D.Dispose();
            };

            #endregion

            // Fit background to currently best average (not corrected for astigmatism yet).
            {
                float2[] ForPS1D = TempPS1D.Skip(MinFreqInclusive).Take(Math.Max(2, NFreq / 2)).ToArray();

                float[] CurrentBackground;
                //if (previousBackground == null)
                {
                    int NumNodes = Math.Max(3, (int)((MainWindow.Options.CTFRangeMax - MainWindow.Options.CTFRangeMin) * 5M));
                    TempBackground = Cubic1D.Fit(ForPS1D, NumNodes); // This won't fit falloff and scale, because approx function is 0

                    CurrentBackground = TempBackground.Interp(TempPS1D.Select(p => p.X).ToArray()).Skip(MinFreqInclusive).Take(NFreq / 2).ToArray();
                }
                /*else
                {
                    CurrentBackground = previousBackground.Interp(TempPS1D.Select(p => p.X).ToArray()).Skip(MinFreqInclusive).Take(NFreq / 2).ToArray();
                    TempBackground = new Cubic1D(previousBackground.Data);
                }*/

                float[] Subtracted1D = new float[ForPS1D.Length];
                for (int i = 0; i < ForPS1D.Length; i++)
                    Subtracted1D[i] = ForPS1D[i].Y - CurrentBackground[i];
                MathHelper.NormalizeInPlace(Subtracted1D);

                float ZMin = (float)MainWindow.Options.CTFZMin;
                float ZMax = (float)MainWindow.Options.CTFZMax;
                float PhaseMin = 0f;
                float PhaseMax = MainWindow.Options.CTFDoPhase ? 1f : 0f;

                if (previousCTF != null)
                {
                    ZMin = (float)previousCTF.Defocus - 0.5f;
                    ZMax = (float)previousCTF.Defocus + 0.5f;
                    if (PhaseMax > 0)
                    {
                        PhaseMin = (float)previousCTF.PhaseShift - 0.3f;
                        PhaseMax = (float)previousCTF.PhaseShift + 0.3f;
                    }
                }

                float ZStep = (ZMax - ZMin) / 100f;

                float BestZ = 0, BestPhase = 0, BestScore = -999;
                for (float z = ZMin; z <= ZMax + 1e-5f; z += ZStep)
                {
                    for (float p = PhaseMin; p <= PhaseMax; p += 0.01f)
                    {
                        CTF CurrentParams = new CTF
                        {
                            PixelSize = (MainWindow.Options.CTFPixelMin + MainWindow.Options.CTFPixelMax) * 0.5M,

                            Defocus = (decimal)z,
                            PhaseShift = (decimal)p,

                            Cs = MainWindow.Options.CTFCs,
                            Voltage = MainWindow.Options.CTFVoltage,
                            Amplitude = MainWindow.Options.CTFAmplitude
                        };
                        float[] SimulatedCTF = CurrentParams.Get1D(TempPS1D.Length, true).Skip(MinFreqInclusive).Take(Math.Max(2, NFreq / 2)).ToArray();
                        MathHelper.NormalizeInPlace(SimulatedCTF);
                        float Score = MathHelper.CrossCorrelate(Subtracted1D, SimulatedCTF);
                        if (Score > BestScore)
                        {
                            BestScore = Score;
                            BestZ = z;
                            BestPhase = p;
                        }
                    }
                }

                TempCTF = new CTF
                {
                    PixelSize = (MainWindow.Options.CTFPixelMin + MainWindow.Options.CTFPixelMax) * 0.5M,

                    Defocus = (decimal)BestZ,
                    PhaseShift = (decimal)BestPhase,

                    Cs = MainWindow.Options.CTFCs,
                    Voltage = MainWindow.Options.CTFVoltage,
                    Amplitude = MainWindow.Options.CTFAmplitude
                };

                UpdateRotationalAverage(true); // This doesn't have a nice background yet.
                UpdateBackgroundFit(); // Now get a reasonably nice background.
            }

            // Fit defocus, (phase shift), (astigmatism) to average background-subtracted spectrum,
            // which is in polar coords at this point (for equal weighting of all frequencies).

            #region Grid search

            if (fromScratch)
            {
                Image CTFMeanPolarTrimmed = CTFMean.AsPolar((uint)MinFreqInclusive, (uint)(MinFreqInclusive + NFreq / 1));

                // Subtract current background.
                Image CurrentBackground = new Image(TempBackground.Interp(TempPS1D.Select(p => p.X).ToArray()).Skip(MinFreqInclusive).Take(NFreq / 1).ToArray());
                CTFMeanPolarTrimmed.SubtractFromLines(CurrentBackground);
                CurrentBackground.Dispose();

                // Normalize for CC (not strictly needed, but it's converted for fp16 later, so let's be on the safe side of the fp16 range.
                GPU.Normalize(CTFMeanPolarTrimmed.GetDevice(Intent.Read), CTFMeanPolarTrimmed.GetDevice(Intent.Write), (uint)CTFMeanPolarTrimmed.ElementsReal, 1);
                //CTFMeanPolarTrimmed.WriteMRC("ctfmeanpolartrimmed.mrc");

                CTF StartParams = new CTF
                {
                    PixelSize = (MainWindow.Options.CTFPixelMin + MainWindow.Options.CTFPixelMax) * 0.5M,
                    PixelSizeDelta = Math.Abs(MainWindow.Options.CTFPixelMax - MainWindow.Options.CTFPixelMin),
                    PixelSizeAngle = MainWindow.Options.CTFPixelAngle,

                    Defocus = TempCTF.Defocus, // (MainWindow.Options.CTFZMin + MainWindow.Options.CTFZMax) * 0.5M,
                    DefocusDelta = 0,
                    DefocusAngle = 0,

                    PhaseShift = TempCTF.PhaseShift,

                    Cs = MainWindow.Options.CTFCs,
                    Voltage = MainWindow.Options.CTFVoltage,
                    Amplitude = MainWindow.Options.CTFAmplitude
                };

                CTFFitStruct FitParams = new CTFFitStruct
                {
                    Defocus = new float3(-0.4e-6f,
                                         0.4e-6f,
                                         0.025e-6f),

                    Defocusdelta = new float3(0, 0.8e-6f, 0.02e-6f),
                    Astigmatismangle = new float3(0, 2 * (float)Math.PI, 1 * (float)Math.PI / 18),
                    Phaseshift = MainWindow.Options.CTFDoPhase ? new float3(-0.2f * (float)Math.PI, 0.2f * (float)Math.PI, 0.025f * (float)Math.PI) : new float3(0, 0, 0)
                };

                CTFStruct ResultStruct = GPU.CTFFitMean(CTFMeanPolarTrimmed.GetDevice(Intent.Read),
                                                        CTFCoordsPolarTrimmed.GetDevice(Intent.Read),
                                                        CTFMeanPolarTrimmed.DimsSlice,
                                                        StartParams.ToStruct(),
                                                        FitParams,
                                                        true);
                TempCTF.FromStruct(ResultStruct);
                TempCTF.Defocus = Math.Max(TempCTF.Defocus, MainWindow.Options.CTFZMin);

                CTFMeanPolarTrimmed.Dispose();

                UpdateRotationalAverage(true); // This doesn't have a nice background yet.
                UpdateBackgroundFit(); // Now get a reasonably nice background.

                UpdateRotationalAverage(true); // This time, with the nice background.
                UpdateBackgroundFit(); // Make the background even nicer!
            }
            else if (previousCTF != null)
            {
                TempCTF.DefocusDelta = previousCTF.DefocusDelta;
                TempCTF.DefocusAngle = previousCTF.DefocusAngle;
            }

            if (fixAstigmatism)
            {
                TempCTF.DefocusDelta = (decimal)astigmatism.X;
                TempCTF.DefocusAngle = (decimal)astigmatism.Y;
            }

            #endregion

            if (previousGrid == null)
                TempGrid = new CubicGrid(TempGrid.Dimensions, (float)TempCTF.Defocus, (float)TempCTF.Defocus, Dimension.X);

            // Do BFGS optimization of defocus, astigmatism and phase shift,
            // using 2D simulation for comparison

            #region BFGS

            bool[] CTFSpectraConsider = new bool[CTFSpectraGrid.Elements()];
            for (int i = 0; i < CTFSpectraConsider.Length; i++)
                CTFSpectraConsider[i] = true;
            int NCTFSpectraConsider = CTFSpectraConsider.Length;

            {
                Image CTFSpectraPolarTrimmed = CTFSpectra.AsPolar((uint)MinFreqInclusive, (uint)(MinFreqInclusive + NFreq));
                CTFSpectra.FreeDevice(); // This will only be needed again for the final PS1D.

                #region Create background and scale

                float[] CurrentScale = TempScale.Interp(TempPS1D.Select(p => p.X).ToArray());

                Image CTFSpectraScale = new Image(new int3(NFreq, DimsRegion.X, 1));
                float[] CTFSpectraScaleData = CTFSpectraScale.GetHost(Intent.Write)[0];

                // Trim polar to relevant frequencies, and populate coordinates.
                Parallel.For(0, DimsRegion.X, y =>
                {
                    for (int x = 0; x < NFreq; x++)
                        CTFSpectraScaleData[y * NFreq + x] = CurrentScale[x + MinFreqInclusive];
                });
                //CTFSpectraScale.WriteMRC("ctfspectrascale.mrc");

                // Background is just 1 line since we're in polar.
                Image CurrentBackground = new Image(TempBackground.Interp(TempPS1D.Select(p => p.X).ToArray()).Skip(MinFreqInclusive).Take(NFreq).ToArray());

                #endregion

                CTFSpectraPolarTrimmed.SubtractFromLines(CurrentBackground);
                CurrentBackground.Dispose();

                // Normalize background-subtracted spectra.
                GPU.Normalize(CTFSpectraPolarTrimmed.GetDevice(Intent.Read),
                              CTFSpectraPolarTrimmed.GetDevice(Intent.Write),
                              (uint)CTFSpectraPolarTrimmed.ElementsSliceReal,
                              (uint)CTFSpectraGrid.Elements());
                //CTFSpectraPolarTrimmed.WriteMRC("ctfspectrapolartrimmed.mrc");

                #region Convert to fp16

                Image CTFSpectraPolarTrimmedHalf = CTFSpectraPolarTrimmed.AsHalf();
                CTFSpectraPolarTrimmed.Dispose();

                Image CTFSpectraScaleHalf = CTFSpectraScale.AsHalf();
                CTFSpectraScale.Dispose();
                Image CTFCoordsPolarTrimmedHalf = CTFCoordsPolarTrimmed.AsHalf();

                #endregion

                // Wiggle weights show how the defocus on the spectra grid is altered
                // by changes in individual anchor points of the spline grid.
                // They are used later to compute the dScore/dDefocus values for each spectrum
                // only once, and derive the values for each anchor point from them.
                float[][] WiggleWeights = TempGrid.GetWiggleWeights(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, 0));

                // Helper method for getting CTFStructs for the entire spectra grid.
                Func<double[], CTF, float[], CTFStruct[]> EvalGetCTF = (input, ctf, defocusValues) =>
                {
                    decimal AlteredPhase = MainWindow.Options.CTFDoPhase ? (decimal)input[input.Length - 3] : 0;
                    decimal AlteredDelta = (decimal)input[input.Length - 2];
                    decimal AlteredAngle = (decimal)(input[input.Length - 1] * 20 / (Math.PI / 180));

                    CTF Local = ctf.GetCopy();
                    Local.PhaseShift = AlteredPhase;
                    Local.DefocusDelta = AlteredDelta;
                    Local.DefocusAngle = AlteredAngle;

                    CTFStruct LocalStruct = Local.ToStruct();
                    CTFStruct[] LocalParams = new CTFStruct[defocusValues.Length];
                    for (int i = 0; i < LocalParams.Length; i++)
                    {
                        LocalParams[i] = LocalStruct;
                        LocalParams[i].Defocus = defocusValues[i] * -1e-6f;
                    }

                    return LocalParams;
                };

                // Simulate with adjusted CTF, compare to originals

                #region Eval and Gradient methods

                Func<double[], double> Eval = input =>
                {
                    CubicGrid Altered = new CubicGrid(TempGrid.Dimensions, input.Take((int)TempGrid.Dimensions.Elements()).Select(v => (float)v).ToArray());
                    float[] DefocusValues = Altered.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, 0));

                    CTFStruct[] LocalParams = EvalGetCTF(input, TempCTF, DefocusValues);

                    float[] Result = new float[LocalParams.Length];

                    GPU.CTFCompareToSim(CTFSpectraPolarTrimmedHalf.GetDevice(Intent.Read),
                                        CTFCoordsPolarTrimmedHalf.GetDevice(Intent.Read),
                                        CTFSpectraScaleHalf.GetDevice(Intent.Read),
                                        (uint)CTFSpectraPolarTrimmedHalf.ElementsSliceReal,
                                        LocalParams,
                                        Result,
                                        (uint)LocalParams.Length);

                    float Score = 0;
                    for (int i = 0; i < Result.Length; i++)
                        if (CTFSpectraConsider[i])
                            Score += Result[i];

                    Score /= NCTFSpectraConsider;

                    if (float.IsNaN(Score) || float.IsInfinity(Score))
                        throw new Exception("Bad score.");

                    return (1.0 - Score) * 1000.0;
                };

                Func<double[], double[]> Gradient = input =>
                {
                    const float Step = 0.005f;
                    double[] Result = new double[input.Length];

                    // In 0D grid case, just get gradient for all 4 parameters.
                    // In 1+D grid case, do simple gradient for astigmatism and phase...
                    int StartComponent = input.Length - 3;
                    //int StartComponent = 0;
                    for (int i = StartComponent; i < input.Length; i++)
                    {
                        if (fixAstigmatism && i > StartComponent)
                            continue;

                        double[] UpperInput = new double[input.Length];
                        input.CopyTo(UpperInput, 0);
                        UpperInput[i] += Step;
                        double UpperValue = Eval(UpperInput);

                        double[] LowerInput = new double[input.Length];
                        input.CopyTo(LowerInput, 0);
                        LowerInput[i] -= Step;
                        double LowerValue = Eval(LowerInput);

                        Result[i] = (UpperValue - LowerValue) / (2f * Step);
                    }

                    float[] ResultPlus = new float[CTFSpectraGrid.Elements()];
                    float[] ResultMinus = new float[CTFSpectraGrid.Elements()];

                    // ..., take shortcut for defoci...
                    {
                        {
                            CubicGrid AlteredPlus = new CubicGrid(TempGrid.Dimensions, input.Take((int)TempGrid.Dimensions.Elements()).Select(v => (float)v + Step).ToArray());
                            float[] DefocusValues = AlteredPlus.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, 0));

                            CTFStruct[] LocalParams = EvalGetCTF(input, TempCTF, DefocusValues);

                            GPU.CTFCompareToSim(CTFSpectraPolarTrimmedHalf.GetDevice(Intent.Read),
                                                CTFCoordsPolarTrimmedHalf.GetDevice(Intent.Read),
                                                CTFSpectraScaleHalf.GetDevice(Intent.Read),
                                                (uint)CTFSpectraPolarTrimmedHalf.ElementsSliceReal,
                                                LocalParams,
                                                ResultPlus,
                                                (uint)LocalParams.Length);
                        }
                        {
                            CubicGrid AlteredMinus = new CubicGrid(TempGrid.Dimensions, input.Take((int)TempGrid.Dimensions.Elements()).Select(v => (float)v - Step).ToArray());
                            float[] DefocusValues = AlteredMinus.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, 0));

                            CTFStruct[] LocalParams = EvalGetCTF(input, TempCTF, DefocusValues);

                            GPU.CTFCompareToSim(CTFSpectraPolarTrimmedHalf.GetDevice(Intent.Read),
                                                CTFCoordsPolarTrimmedHalf.GetDevice(Intent.Read),
                                                CTFSpectraScaleHalf.GetDevice(Intent.Read),
                                                (uint)CTFSpectraPolarTrimmedHalf.ElementsSliceReal,
                                                LocalParams,
                                                ResultMinus,
                                                (uint)LocalParams.Length);
                        }
                        float[] LocalGradients = new float[ResultPlus.Length];
                        for (int i = 0; i < LocalGradients.Length; i++)
                            LocalGradients[i] = ResultMinus[i] - ResultPlus[i];

                        // Now compute gradients per grid anchor point using the precomputed individual gradients and wiggle factors.
                        Parallel.For(0, TempGrid.Dimensions.Elements(), i => Result[i] = MathHelper.ReduceWeighted(LocalGradients, WiggleWeights[i]) / LocalGradients.Length / (2f * Step) * 1000f);
                    }

                    foreach (var i in Result)
                        if (double.IsNaN(i) || double.IsInfinity(i))
                            throw new Exception("Bad score.");

                    return Result;
                };

                #endregion

                #region Minimize first time with potential outpiers

                double[] StartParams = new double[TempGrid.Dimensions.Elements() + 3];
                for (int i = 0; i < TempGrid.Dimensions.Elements(); i++)
                    StartParams[i] = TempGrid.FlatValues[i];
                StartParams[StartParams.Length - 3] = (double)TempCTF.PhaseShift;
                StartParams[StartParams.Length - 2] = (double)TempCTF.DefocusDelta;
                StartParams[StartParams.Length - 1] = (double)TempCTF.DefocusAngle / 20 * (Math.PI / 180);

                // Compute correlation for individual spectra, and throw away those that are >.75 sigma worse than mean.

                BroydenFletcherGoldfarbShanno Optimizer = new BroydenFletcherGoldfarbShanno(StartParams.Length, Eval, Gradient)
                {
                    Past = 1,
                    Delta = 1e-6,
                    MaxLineSearch = 15,
                    Corrections = 20
                };
                Optimizer.Minimize(StartParams);

                #endregion

                #region Retrieve parameters

                TempCTF.Defocus = (decimal)MathHelper.Mean(Optimizer.Solution.Take((int)TempGrid.Dimensions.Elements()).Select(v => (float)v));
                TempCTF.PhaseShift = (decimal)Optimizer.Solution[StartParams.Length - 3];
                TempCTF.DefocusDelta = (decimal)Optimizer.Solution[StartParams.Length - 2];
                TempCTF.DefocusAngle = (decimal)(Optimizer.Solution[StartParams.Length - 1] * 20 / (Math.PI / 180));

                if (TempCTF.DefocusDelta < 0)
                {
                    TempCTF.DefocusAngle += 90;
                    TempCTF.DefocusDelta *= -1;
                }
                TempCTF.DefocusAngle = ((int)TempCTF.DefocusAngle + 180 * 99) % 180;

                TempGrid = new CubicGrid(TempGrid.Dimensions, Optimizer.Solution.Take((int)TempGrid.Dimensions.Elements()).Select(v => (float)v).ToArray());

                #endregion

                // Dispose GPU resources manually because GC can't be bothered to do it in time.
                CTFSpectraPolarTrimmedHalf.Dispose();
                CTFCoordsPolarTrimmedHalf.Dispose();
                CTFSpectraScaleHalf.Dispose();

                #region Get nicer envelope fit

                {
                    {
                        Image CTFSpectraBackground = new Image(new int3(DimsRegion), true);
                        float[] CTFSpectraBackgroundData = CTFSpectraBackground.GetHost(Intent.Write)[0];

                        // Construct background in Cartesian coordinates.
                        Helper.ForEachElementFT(DimsRegion, (x, y, xx, yy, r, a) =>
                        {
                            CTFSpectraBackgroundData[y * CTFSpectraBackground.DimsEffective.X + x] = TempBackground.Interp(r / DimsRegion.X);
                        });

                        CTFSpectra.SubtractFromSlices(CTFSpectraBackground);

                        float[] DefocusValues = TempGrid.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, 0));
                        CTFStruct[] LocalParams = DefocusValues.Select(v =>
                        {
                            CTF Local = TempCTF.GetCopy();
                            Local.Defocus = (decimal)v + 0.0M;

                            return Local.ToStruct();
                        }).ToArray();

                        Image CTFAverage1D = new Image(IntPtr.Zero, new int3(DimsRegion.X / 2, 1, 1));

                        CTF CTFAug = TempCTF.GetCopy();
                        CTFAug.Defocus += 0.0M;
                        GPU.CTFMakeAverage(CTFSpectra.GetDevice(Intent.Read),
                                           CTFCoordsCart.GetDevice(Intent.Read),
                                           (uint)CTFSpectra.ElementsSliceReal,
                                           (uint)DimsRegion.X,
                                           LocalParams,
                                           CTFAug.ToStruct(),
                                           0,
                                           (uint)DimsRegion.X / 2,
                                           CTFSpectraConsider.Select(v => v ? 1 : 0).ToArray(),
                                           (uint)CTFSpectraGrid.Elements(),
                                           CTFAverage1D.GetDevice(Intent.Write));

                        CTFSpectra.AddToSlices(CTFSpectraBackground);

                        float[] RotationalAverageData = CTFAverage1D.GetHost(Intent.Read)[0];
                        float2[] ForPS1D = new float2[TempPS1D.Length];
                        for (int i = 0; i < ForPS1D.Length; i++)
                            ForPS1D[i] = new float2((float)i / DimsRegion.X, (float)Math.Round(RotationalAverageData[i], 4) + TempBackground.Interp((float)i / DimsRegion.X));
                        MathHelper.UnNaN(ForPS1D);
                        TempPS1D = ForPS1D;

                        CTFSpectraBackground.Dispose();
                        CTFAverage1D.Dispose();
                        CTFSpectra.FreeDevice();
                    }

                    TempCTF.Defocus = Math.Max(TempCTF.Defocus, MainWindow.Options.CTFZMin);
                    UpdateBackgroundFit();
                }

                #endregion
            }

            #endregion

            // Subtract background from 2D average and write it to disk.
            // This image is used for quick visualization purposes only.

            #region PS2D update

            {
                int3 DimsAverage = new int3(DimsRegion.X, DimsRegion.X / 2, 1);
                float[] Average2DData = new float[DimsAverage.Elements()];
                float[] OriginalAverageData = CTFMean.GetHost(Intent.Read)[0];

                for (int y = 0; y < DimsAverage.Y; y++)
                {
                    int yy = y * y;
                    for (int x = 0; x < DimsAverage.Y; x++)
                    {
                        int xx = DimsRegion.X / 2 - x - 1;
                        xx *= xx;
                        float r = (float)Math.Sqrt(xx + yy) / DimsRegion.X;
                        Average2DData[y * DimsAverage.X + x] = OriginalAverageData[(y + DimsRegion.X / 2) * (DimsRegion.X / 2 + 1) + x] - TempBackground.Interp(r);
                    }

                    for (int x = 0; x < DimsRegion.X / 2; x++)
                    {
                        int xx = x * x;
                        float r = (float)Math.Sqrt(xx + yy) / DimsRegion.X;
                        Average2DData[y * DimsAverage.X + x + DimsRegion.X / 2] = OriginalAverageData[(DimsRegion.X / 2 - y) * (DimsRegion.X / 2 + 1) + (DimsRegion.X / 2 - 1 - x)] - TempBackground.Interp(r);
                    }
                }

                thisPS2D = new Image(Average2DData, DimsAverage);
            }

            #endregion

            for (int i = 0; i < TempPS1D.Length; i++)
                TempPS1D[i].Y -= TempBackground.Interp(TempPS1D[i].X);

            CTFSpectra.Dispose();
            CTFMean.Dispose();
            CTFCoordsCart.Dispose();
            CTFCoordsPolarTrimmed.Dispose();

            thisPS1D = TempPS1D;
            thisBackground = TempBackground;
            thisScale = TempScale;
            thisCTF = TempCTF;
            thisGrid = TempGrid;
        }
Example #18
0
        public void PerformOptimizationStep(Star tableIn, Image tiltStack, int size, int3 volumeDimensions, Dictionary<int, Projector> references, float resolution, Dictionary<int, Projector> outReconstructions, Dictionary<int, Projector> outCTFReconstructions)
        {
            VolumeDimensions = volumeDimensions;

            #region Get rows from table

            List<int> RowIndices = new List<int>();
            string[] ColumnMicrographName = tableIn.GetColumn("rlnMicrographName");
            for (int i = 0; i < ColumnMicrographName.Length; i++)
                if (ColumnMicrographName[i].Contains(RootName + "."))
                    RowIndices.Add(i);

            if (RowIndices.Count == 0)
                return;

            int NParticles = RowIndices.Count;

            #endregion

            #region Make sure all columns and directories are there

            if (!tableIn.HasColumn("rlnImageName"))
                tableIn.AddColumn("rlnImageName");
            if (!tableIn.HasColumn("rlnCtfImage"))
                tableIn.AddColumn("rlnCtfImage");
            if (!tableIn.HasColumn("rlnParticleSelectZScore"))
                tableIn.AddColumn("rlnParticleSelectZScore");

            if (!Directory.Exists(ParticlesDir))
                Directory.CreateDirectory(ParticlesDir);
            if (!Directory.Exists(ParticleCTFDir))
                Directory.CreateDirectory(ParticleCTFDir);

            #endregion

            #region Get subtomo positions from table

            float3[] ParticleOrigins = new float3[NParticles];
            float3[] ParticleOrigins2 = new float3[NParticles];
            float3[] ParticleAngles = new float3[NParticles];
            float3[] ParticleAngles2 = new float3[NParticles];
            int[] ParticleSubset = new int[NParticles];
            {
                string[] ColumnPosX = tableIn.GetColumn("rlnCoordinateX");
                string[] ColumnPosY = tableIn.GetColumn("rlnCoordinateY");
                string[] ColumnPosZ = tableIn.GetColumn("rlnCoordinateZ");
                string[] ColumnOriginX = tableIn.GetColumn("rlnOriginX");
                string[] ColumnOriginY = tableIn.GetColumn("rlnOriginY");
                string[] ColumnOriginZ = tableIn.GetColumn("rlnOriginZ");
                string[] ColumnAngleRot = tableIn.GetColumn("rlnAngleRot");
                string[] ColumnAngleTilt = tableIn.GetColumn("rlnAngleTilt");
                string[] ColumnAnglePsi = tableIn.GetColumn("rlnAnglePsi");
                string[] ColumnSubset = tableIn.GetColumn("rlnRandomSubset");

                string[] ColumnPosX2 = tableIn.GetColumn("rlnOriginXPrior");
                string[] ColumnPosY2 = tableIn.GetColumn("rlnOriginYPrior");
                string[] ColumnPosZ2 = tableIn.GetColumn("rlnOriginZPrior");
                string[] ColumnAngleRot2 = tableIn.GetColumn("rlnAngleRotPrior");
                string[] ColumnAngleTilt2 = tableIn.GetColumn("rlnAngleTiltPrior");
                string[] ColumnAnglePsi2 = tableIn.GetColumn("rlnAnglePsiPrior");

                for (int i = 0; i < NParticles; i++)
                {
                    float3 Pos = new float3(float.Parse(ColumnPosX[RowIndices[i]], CultureInfo.InvariantCulture),
                                            float.Parse(ColumnPosY[RowIndices[i]], CultureInfo.InvariantCulture),
                                            float.Parse(ColumnPosZ[RowIndices[i]], CultureInfo.InvariantCulture));
                    float3 Pos2 = Pos;
                    //if (ColumnPosX2 != null && ColumnPosY2 != null && ColumnPosZ2 != null)
                    //    Pos2 = new float3(float.Parse(ColumnPosX2[RowIndices[i]], CultureInfo.InvariantCulture),
                    //                      float.Parse(ColumnPosY2[RowIndices[i]], CultureInfo.InvariantCulture),
                    //                      float.Parse(ColumnPosZ2[RowIndices[i]], CultureInfo.InvariantCulture));

                    float3 Shift = new float3(float.Parse(ColumnOriginX[RowIndices[i]], CultureInfo.InvariantCulture),
                                              float.Parse(ColumnOriginY[RowIndices[i]], CultureInfo.InvariantCulture),
                                              float.Parse(ColumnOriginZ[RowIndices[i]], CultureInfo.InvariantCulture));

                    ParticleOrigins[i] = Pos - Shift;
                    ParticleOrigins2[i] = Pos2 - Shift;

                    float3 Angle = new float3(float.Parse(ColumnAngleRot[RowIndices[i]], CultureInfo.InvariantCulture),
                                              float.Parse(ColumnAngleTilt[RowIndices[i]], CultureInfo.InvariantCulture),
                                              float.Parse(ColumnAnglePsi[RowIndices[i]], CultureInfo.InvariantCulture));
                    float3 Angle2 = Angle;
                    //if (ColumnAngleRot2 != null && ColumnAngleTilt2 != null && ColumnAnglePsi2 != null)
                    //    Angle2 = new float3(float.Parse(ColumnAngleRot2[RowIndices[i]], CultureInfo.InvariantCulture),
                    //                        float.Parse(ColumnAngleTilt2[RowIndices[i]], CultureInfo.InvariantCulture),
                    //                        float.Parse(ColumnAnglePsi2[RowIndices[i]], CultureInfo.InvariantCulture));

                    ParticleAngles[i] = Angle;
                    ParticleAngles2[i] = Angle2;

                    ParticleSubset[i] = int.Parse(ColumnSubset[RowIndices[i]]);

                    tableIn.SetRowValue(RowIndices[i], "rlnCoordinateX", ParticleOrigins[i].X.ToString(CultureInfo.InvariantCulture));
                    tableIn.SetRowValue(RowIndices[i], "rlnCoordinateY", ParticleOrigins[i].Y.ToString(CultureInfo.InvariantCulture));
                    tableIn.SetRowValue(RowIndices[i], "rlnCoordinateZ", ParticleOrigins[i].Z.ToString(CultureInfo.InvariantCulture));
                    tableIn.SetRowValue(RowIndices[i], "rlnOriginX", "0.0");
                    tableIn.SetRowValue(RowIndices[i], "rlnOriginY", "0.0");
                    tableIn.SetRowValue(RowIndices[i], "rlnOriginZ", "0.0");
                }
            }

            #endregion

            #region Deal with subsets

            List<int> SubsetIDs = new List<int>();
            foreach (var i in ParticleSubset)
                if (!SubsetIDs.Contains(i))
                    SubsetIDs.Add(i);
            SubsetIDs.Sort();

            // For each subset, create a list of its particle IDs
            Dictionary<int, List<int>> SubsetParticleIDs = SubsetIDs.ToDictionary(subsetID => subsetID, subsetID => new List<int>());
            for (int i = 0; i < ParticleSubset.Length; i++)
                SubsetParticleIDs[ParticleSubset[i]].Add(i);
            foreach (var list in SubsetParticleIDs.Values)
                list.Sort();

            // Note where each subset starts and ends in a unified, sorted (by subset) particle ID list
            Dictionary<int, Tuple<int, int>> SubsetRanges = new Dictionary<int, Tuple<int, int>>();
            {
                int Start = 0;
                foreach (var pair in SubsetParticleIDs)
                {
                    SubsetRanges.Add(pair.Key, new Tuple<int, int>(Start, Start + pair.Value.Count));
                    Start += pair.Value.Count;
                }
            }

            List<int> SubsetContinuousIDs = new List<int>();
            foreach (var pair in SubsetParticleIDs)
                SubsetContinuousIDs.AddRange(pair.Value);

            // Reorder particle information to match the order of SubsetContinuousIDs
            ParticleOrigins = SubsetContinuousIDs.Select(i => ParticleOrigins[i]).ToArray();
            ParticleOrigins2 = SubsetContinuousIDs.Select(i => ParticleOrigins2[i]).ToArray();
            ParticleAngles = SubsetContinuousIDs.Select(i => ParticleAngles[i]).ToArray();
            ParticleAngles2 = SubsetContinuousIDs.Select(i => ParticleAngles2[i]).ToArray();
            ParticleSubset = SubsetContinuousIDs.Select(i => ParticleSubset[i]).ToArray();

            #endregion

            if (GridMovementX.Dimensions.Elements() == 1)
            {
                int MaxSlice = SubsetRanges.Last().Value.Item2 > 100 ? 1 : 1;

                GridMovementX = new CubicGrid(new int3(MaxSlice, MaxSlice, NTilts));
                GridMovementY = new CubicGrid(new int3(MaxSlice, MaxSlice, NTilts));

                //GridLocalX = new CubicGrid(new int3(4, 4, 4));
                //GridLocalY = new CubicGrid(new int3(4, 4, 4));
                //GridLocalZ = new CubicGrid(new int3(4, 4, 4));

                GridAngleX = new CubicGrid(new int3(1, 1, NTilts));
                GridAngleY = new CubicGrid(new int3(1, 1, NTilts));
                GridAngleZ = new CubicGrid(new int3(1, 1, NTilts));
            }
            if (GridLocalX.Dimensions.Elements() == 1)
            {
                GridLocalX = new CubicGrid(new int3(4, 4, 4));
                GridLocalY = new CubicGrid(new int3(4, 4, 4));
                GridLocalZ = new CubicGrid(new int3(4, 4, 4));
            }
            //else
            //{
            //    GridMovementX = GridMovementX.Resize(new int3(4, 4, NTilts));
            //    GridMovementY = GridMovementY.Resize(new int3(4, 4, NTilts));
            //}

            int CoarseSize = (int)Math.Round(size * ((float)CTF.PixelSize * 2 / resolution)) / 2 * 2;
            int3 CoarseDims = new int3(CoarseSize, CoarseSize, 1);

            // Positions the particles were extracted at/shifted to, to calculate effectively needed shifts later
            float2[] ExtractedAt = new float2[NParticles * NTilts];

            // Extract images, mask and resize them, create CTFs
            Image ParticleImages = new Image(new int3(CoarseSize, CoarseSize, NParticles * NTilts), true, true);
            Image ParticleCTFs = new Image(new int3(CoarseSize, CoarseSize, NParticles * NTilts), true);
            Image ParticleWeights = null;
            Image ShiftFactors = null;

            #region Preflight
            float KeepBFac = GlobalBfactor;
            GlobalBfactor = 0;
            {
                Image CTFCoords = GetCTFCoords(CoarseSize, size);

                #region Precalculate vectors for shifts in Fourier space
                {
                    float2[] ShiftFactorsData = new float2[(CoarseSize / 2 + 1) * CoarseSize];
                    for (int y = 0; y < CoarseSize; y++)
                        for (int x = 0; x < CoarseSize / 2 + 1; x++)
                        {
                            int xx = x;
                            int yy = y < CoarseSize / 2 + 1 ? y : y - CoarseSize;

                            ShiftFactorsData[y * (CoarseSize / 2 + 1) + x] = new float2((float)-xx / size * 2f * (float)Math.PI,
                                                                                          (float)-yy / size * 2f * (float)Math.PI);
                        }

                    ShiftFactors = new Image(ShiftFactorsData, new int3(CoarseSize, CoarseSize, 1), true);
                }
                #endregion

                #region Create mask with soft edge
                Image Mask;
                Image MaskSubt;
                {
                    Image MaskBig = new Image(new int3(size, size, 1));
                    float MaskRadius = MainWindow.Options.ExportParticleRadius / (float)CTF.PixelSize;
                    float SoftEdge = 16f;

                    float[] MaskBigData = MaskBig.GetHost(Intent.Write)[0];
                    for (int y = 0; y < size; y++)
                    {
                        int yy = y - size / 2;
                        yy *= yy;
                        for (int x = 0; x < size; x++)
                        {
                            int xx = x - size / 2;
                            xx *= xx;
                            float R = (float)Math.Sqrt(xx + yy);

                            if (R <= MaskRadius)
                                MaskBigData[y * size + x] = 1;
                            else
                                MaskBigData[y * size + x] = (float)(Math.Cos(Math.Min(1, (R - MaskRadius) / SoftEdge) * Math.PI) * 0.5 + 0.5);
                        }
                    }
                    //MaskBig.WriteMRC("d_maskbig.mrc");

                    Mask = MaskBig.AsScaled(new int2(CoarseSize, CoarseSize));
                    Mask.RemapToFT();

                    MaskBigData = MaskBig.GetHost(Intent.Write)[0];
                    for (int y = 0; y < size; y++)
                    {
                        int yy = y - size / 2;
                        yy *= yy;
                        for (int x = 0; x < size; x++)
                        {
                            int xx = x - size / 2;
                            xx *= xx;
                            float R = (float)Math.Sqrt(xx + yy);

                            if (R <= 30)
                                MaskBigData[y * size + x] = 1;
                            else
                                MaskBigData[y * size + x] = 0;
                        }
                    }

                    MaskSubt = MaskBig.AsScaled(new int2(CoarseSize, CoarseSize));
                    MaskSubt.RemapToFT();

                    MaskBig.Dispose();
                }
                //Mask.WriteMRC("d_masksmall.mrc");
                #endregion

                #region Create Fourier space mask
                Image FourierMask = new Image(CoarseDims, true);
                {
                    float[] FourierMaskData = FourierMask.GetHost(Intent.Write)[0];
                    int MaxR2 = CoarseSize * CoarseSize / 4;
                    for (int y = 0; y < CoarseSize; y++)
                    {
                        int yy = y < CoarseSize / 2 + 1 ? y : y - CoarseSize;
                        yy *= yy;

                        for (int x = 0; x < CoarseSize / 2 + 1; x++)
                        {
                            int xx = x * x;
                            int R2 = yy + xx;

                            FourierMaskData[y * (CoarseSize / 2 + 1) + x] = R2 < MaxR2 ? 1 : 0;
                        }
                    }
                }
                #endregion

                #region For each particle, create CTFs and extract & preprocess images for entire tilt series
                for (int p = 0; p < NParticles; p++)
                {
                    float3 ParticleCoords = ParticleOrigins[p];
                    float3[] Positions = GetPositionInImages(ParticleCoords);
                    float3[] ProjAngles = GetParticleAngleInImages(ParticleCoords, ParticleAngles[p]);

                    Image Extracted = new Image(new int3(size, size, NTilts));
                    float[][] ExtractedData = Extracted.GetHost(Intent.Write);
                    float3[] Residuals = new float3[NTilts];

                    Image SubtrahendsCTF = new Image(new int3(CoarseSize, CoarseSize, NTilts), true);

                    // Create CTFs
                    {
                        CTFStruct[] CTFParams = new CTFStruct[NTilts];

                        float GridStep = 1f / (NTilts - 1);
                        CTFStruct[] Params = new CTFStruct[NTilts];
                        for (int t = 0; t < NTilts; t++)
                        {
                            decimal Defocus = (decimal)Positions[t].Z;
                            decimal DefocusDelta = (decimal)GridCTFDefocusDelta.GetInterpolated(new float3(0.5f, 0.5f, t * GridStep));
                            decimal DefocusAngle = (decimal)GridCTFDefocusAngle.GetInterpolated(new float3(0.5f, 0.5f, t * GridStep));

                            CTF CurrCTF = CTF.GetCopy();
                            CurrCTF.Defocus = Defocus;
                            CurrCTF.DefocusDelta = DefocusDelta;
                            CurrCTF.DefocusAngle = DefocusAngle;
                            CurrCTF.Scale = (decimal)Math.Cos(Angles[t] * Helper.ToRad);
                            CurrCTF.Bfactor = (decimal)-Dose[t] * 8;

                            Params[t] = CurrCTF.ToStruct();
                        }

                        GPU.CreateCTF(ParticleCTFs.GetDeviceSlice(NTilts * p, Intent.Write),
                                      CTFCoords.GetDevice(Intent.Read),
                                      (uint)CoarseDims.ElementsFFT(),
                                      Params,
                                      false,
                                      (uint)NTilts);
                    }
                    //{
                    //    CTFStruct[] CTFParams = new CTFStruct[NTilts];

                    //    float GridStep = 1f / (NTilts - 1);
                    //    CTFStruct[] Params = new CTFStruct[NTilts];
                    //    for (int t = 0; t < NTilts; t++)
                    //    {
                    //        decimal Defocus = (decimal)Positions[t].Z;
                    //        decimal DefocusDelta = (decimal)GridCTFDefocusDelta.GetInterpolated(new float3(0.5f, 0.5f, t * GridStep));
                    //        decimal DefocusAngle = (decimal)GridCTFDefocusAngle.GetInterpolated(new float3(0.5f, 0.5f, t * GridStep));

                    //        CTF CurrCTF = CTF.GetCopy();
                    //        CurrCTF.Defocus = Defocus;
                    //        CurrCTF.DefocusDelta = DefocusDelta;
                    //        CurrCTF.DefocusAngle = DefocusAngle;
                    //        CurrCTF.Scale = 1;
                    //        CurrCTF.Bfactor = 0;

                    //        Params[t] = CurrCTF.ToStruct();
                    //    }

                    //    GPU.CreateCTF(SubtrahendsCTF.GetDevice(Intent.Write),
                    //                  CTFCoords.GetDevice(Intent.Read),
                    //                  (uint)CoarseDims.ElementsFFT(),
                    //                  Params,
                    //                  false,
                    //                  (uint)NTilts);
                    //}

                    // Extract images
                    {
                        for (int t = 0; t < NTilts; t++)
                        {
                            ExtractedAt[p * NTilts + t] = new float2(Positions[t].X, Positions[t].Y);

                            Positions[t] -= size / 2;
                            int2 IntPosition = new int2((int)Positions[t].X, (int)Positions[t].Y);
                            float2 Residual = new float2(-(Positions[t].X - IntPosition.X), -(Positions[t].Y - IntPosition.Y));
                            Residuals[t] = new float3(Residual / size * CoarseSize);

                            float[] OriginalData;
                            lock (tiltStack)
                                OriginalData = tiltStack.GetHost(Intent.Read)[t];

                            float[] ImageData = ExtractedData[t];
                            for (int y = 0; y < size; y++)
                            {
                                int PosY = (y + IntPosition.Y + tiltStack.Dims.Y) % tiltStack.Dims.Y;
                                for (int x = 0; x < size; x++)
                                {
                                    int PosX = (x + IntPosition.X + tiltStack.Dims.X) % tiltStack.Dims.X;
                                    ImageData[y * size + x] = OriginalData[PosY * tiltStack.Dims.X + PosX];
                                }
                            }
                        }

                        GPU.NormParticles(Extracted.GetDevice(Intent.Read),
                                          Extracted.GetDevice(Intent.Write),
                                          new int3(size, size, 1),
                                          (uint)(MainWindow.Options.ExportParticleRadius / CTF.PixelSize),
                                          true,
                                          (uint)NTilts);

                        Image Scaled = Extracted.AsScaled(new int2(CoarseSize, CoarseSize));
                        //Scaled.WriteMRC("d_scaled.mrc");
                        Extracted.Dispose();

                        Scaled.ShiftSlices(Residuals);
                        Scaled.RemapToFT();

                        //GPU.NormalizeMasked(Scaled.GetDevice(Intent.Read),
                        //              Scaled.GetDevice(Intent.Write),
                        //              MaskSubt.GetDevice(Intent.Read),
                        //              (uint)Scaled.ElementsSliceReal,
                        //              (uint)NTilts);

                        //{
                        //    //Image SubtrahendsFT = subtrahendReference.Project(new int2(CoarseSize, CoarseSize), ProjAngles, CoarseSize / 2);
                        //    //SubtrahendsFT.Multiply(SubtrahendsCTF);

                        //    //Image Subtrahends = SubtrahendsFT.AsIFFT();
                        //    //SubtrahendsFT.Dispose();

                        //    ////GPU.NormalizeMasked(Subtrahends.GetDevice(Intent.Read),
                        //    ////                    Subtrahends.GetDevice(Intent.Write),
                        //    ////                    MaskSubt.GetDevice(Intent.Read),
                        //    ////                    (uint)Subtrahends.ElementsSliceReal,
                        //    ////                    (uint)NTilts);

                        //    //Scaled.Subtract(Subtrahends);
                        //    //Subtrahends.Dispose();

                        //    Image FocusMaskFT = maskReference.Project(new int2(CoarseSize, CoarseSize), ProjAngles, CoarseSize / 2);
                        //    Image FocusMask = FocusMaskFT.AsIFFT();
                        //    FocusMaskFT.Dispose();

                        //    Scaled.Multiply(FocusMask);
                        //    FocusMask.Dispose();
                        //}

                        Scaled.MultiplySlices(Mask);

                        GPU.FFT(Scaled.GetDevice(Intent.Read),
                                ParticleImages.GetDeviceSlice(p * NTilts, Intent.Write),
                                CoarseDims,
                                (uint)NTilts);

                        Scaled.Dispose();
                        SubtrahendsCTF.Dispose();
                    }
                }
                #endregion

                ParticleCTFs.MultiplySlices(FourierMask);

                Mask.Dispose();
                FourierMask.Dispose();
                MaskSubt.Dispose();

                Image ParticleCTFsAbs = new Image(ParticleCTFs.GetDevice(Intent.Read), ParticleCTFs.Dims, true);
                ParticleCTFsAbs.Abs();
                ParticleWeights = ParticleCTFsAbs.AsSum2D();
                ParticleCTFsAbs.Dispose();
                {
                    float[] ParticleWeightsData = ParticleWeights.GetHost(Intent.ReadWrite)[0];
                    float Max = MathHelper.Max(ParticleWeightsData);
                    for (int i = 0; i < ParticleWeightsData.Length; i++)
                        ParticleWeightsData[i] /= Max;
                }

                CTFCoords.Dispose();

                //Image CheckImages = ParticleImages.AsIFFT();
                //CheckImages.WriteMRC("d_particleimages.mrc");
                //CheckImages.Dispose();

                //ParticleCTFs.WriteMRC("d_particlectfs.mrc");
            }
            GlobalBfactor = KeepBFac;
            #endregion

            bool DoPerParticleMotion = true;
            bool DoImageAlignment = true;

            #region BFGS evaluation and gradient

            double[] StartParams;

            Func<double[], Tuple<float2[], float3[]>> GetImageShiftsAndAngles;
            Func<double[], float2[]> GetImageShifts;
            Func<float3[], Image> GetProjections;
            Func<double[], double[]> EvalIndividual;
            Func<double[], double> Eval;
            Func<double[], double[]> Gradient;
            {
                List<double> StartParamsList = new List<double>();
                StartParamsList.AddRange(CreateVectorFromGrids(Dimensions.X));
                StartParamsList.AddRange(CreateVectorFromParameters(ParticleOrigins, ParticleOrigins2, ParticleAngles, ParticleAngles2, size));
                StartParams = StartParamsList.ToArray();

                // Remember where the values for each grid are stored in the optimized vector
                List<Tuple<int, int>> VectorGridRanges = new List<Tuple<int, int>>();
                List<int> GridElements = new List<int>();
                List<int> GridSliceElements = new List<int>();
                {
                    int Start = 0;
                    VectorGridRanges.Add(new Tuple<int, int>(Start, Start + (int)GridMovementX.Dimensions.Elements()));
                    Start += (int)GridMovementX.Dimensions.Elements();
                    VectorGridRanges.Add(new Tuple<int, int>(Start, Start + (int)GridMovementY.Dimensions.Elements()));
                    Start += (int)GridMovementY.Dimensions.Elements();

                    VectorGridRanges.Add(new Tuple<int, int>(Start, Start + (int)GridAngleX.Dimensions.Elements()));
                    Start += (int)GridAngleX.Dimensions.Elements();
                    VectorGridRanges.Add(new Tuple<int, int>(Start, Start + (int)GridAngleY.Dimensions.Elements()));
                    Start += (int)GridAngleY.Dimensions.Elements();
                    VectorGridRanges.Add(new Tuple<int, int>(Start, Start + (int)GridAngleZ.Dimensions.Elements()));
                    Start += (int)GridAngleZ.Dimensions.Elements();

                    VectorGridRanges.Add(new Tuple<int, int>(Start, Start + (int)GridLocalX.Dimensions.Elements()));
                    Start += (int)GridLocalX.Dimensions.Elements();
                    VectorGridRanges.Add(new Tuple<int, int>(Start, Start + (int)GridLocalY.Dimensions.Elements()));
                    Start += (int)GridLocalY.Dimensions.Elements();
                    VectorGridRanges.Add(new Tuple<int, int>(Start, Start + (int)GridLocalZ.Dimensions.Elements()));

                    GridElements.Add((int)GridMovementX.Dimensions.Elements());
                    GridElements.Add((int)GridMovementY.Dimensions.Elements());

                    GridElements.Add((int)GridAngleX.Dimensions.Elements());
                    GridElements.Add((int)GridAngleY.Dimensions.Elements());
                    GridElements.Add((int)GridAngleZ.Dimensions.Elements());

                    GridElements.Add((int)GridLocalX.Dimensions.Elements());
                    GridElements.Add((int)GridLocalY.Dimensions.Elements());
                    GridElements.Add((int)GridLocalZ.Dimensions.Elements());

                    GridSliceElements.Add((int)GridMovementX.Dimensions.ElementsSlice());
                    GridSliceElements.Add((int)GridMovementY.Dimensions.ElementsSlice());

                    GridSliceElements.Add((int)GridAngleX.Dimensions.ElementsSlice());
                    GridSliceElements.Add((int)GridAngleY.Dimensions.ElementsSlice());
                    GridSliceElements.Add((int)GridAngleZ.Dimensions.ElementsSlice());

                    GridSliceElements.Add((int)GridLocalX.Dimensions.ElementsSlice());
                    GridSliceElements.Add((int)GridLocalY.Dimensions.ElementsSlice());
                    GridSliceElements.Add((int)GridLocalZ.Dimensions.ElementsSlice());
                }
                int NVectorGridParams = VectorGridRanges.Last().Item2;
                int NVectorParticleParams = NParticles * 12;

                GetImageShiftsAndAngles = input =>
                {
                    // Retrieve particle positions & angles, and grids from input vector
                    float3[] NewPositions, NewPositions2, NewAngles, NewAngles2;
                    GetParametersFromVector(input, NParticles, size, out NewPositions, out NewPositions2, out NewAngles, out NewAngles2);
                    SetGridsFromVector(input, Dimensions.X);

                    // Using current positions, angles and grids, get parameters for image shifts and reference projection angles
                    float2[] ImageShifts = new float2[NParticles * NTilts];
                    float3[] ImageAngles = new float3[NParticles * NTilts];
                    float3[] PerTiltPositions = new float3[NParticles * NTilts];
                    float3[] PerTiltAngles = new float3[NParticles * NTilts];
                    int[] SortedDosePrecalc = IndicesSortedDose;
                    for (int p = 0; p < NParticles; p++)
                    {
                        if (DoPerParticleMotion)
                        {
                            float3 CoordsDiff = NewPositions2[p] - NewPositions[p];
                            float3 AnglesDiff = NewAngles2[p] - NewAngles[p];
                            for (int t = 0; t < NTilts; t++)
                            {
                                float DoseID = SortedDosePrecalc[t] / (float)(NTilts - 1);
                                PerTiltPositions[p * NTilts + t] = NewPositions[p] + CoordsDiff * DoseID;
                                PerTiltAngles[p * NTilts + t] = NewAngles[p] + AnglesDiff * DoseID;
                            }
                        }
                        else
                        {
                            for (int t = 0; t < NTilts; t++)
                            {
                                PerTiltPositions[p * NTilts + t] = NewPositions[p];
                                PerTiltAngles[p * NTilts + t] = NewAngles[p];
                            }
                        }
                    }

                    float3[] CurrPositions = GetPositionInImages(PerTiltPositions);
                    float3[] CurrAngles = GetParticleAngleInImages(PerTiltPositions, PerTiltAngles);
                    for (int i = 0; i < ImageShifts.Length; i++)
                    {
                        ImageShifts[i] = new float2(ExtractedAt[i].X - CurrPositions[i].X,
                                                    ExtractedAt[i].Y - CurrPositions[i].Y); // -diff because those are extraction positions, i. e. opposite direction of shifts
                        ImageAngles[i] = CurrAngles[i];
                    }

                    return new Tuple<float2[], float3[]>(ImageShifts, ImageAngles);
                };

                GetImageShifts = input =>
                {
                    // Retrieve particle positions & angles, and grids from input vector
                    float3[] NewPositions, NewPositions2, NewAngles, NewAngles2;
                    GetParametersFromVector(input, NParticles, size, out NewPositions, out NewPositions2, out NewAngles, out NewAngles2);
                    SetGridsFromVector(input, Dimensions.X);

                    // Using current positions, angles and grids, get parameters for image shifts and reference projection angles
                    float2[] ImageShifts = new float2[NParticles * NTilts];
                    float3[] PerTiltPositions = new float3[NParticles * NTilts];
                    int[] SortedDosePrecalc = IndicesSortedDose;
                    for (int p = 0; p < NParticles; p++)
                    {
                        if (DoPerParticleMotion)
                        {
                            float3 CoordsDiff = NewPositions2[p] - NewPositions[p];
                            float3 AnglesDiff = NewAngles2[p] - NewAngles[p];
                            for (int t = 0; t < NTilts; t++)
                            {
                                float DoseID = SortedDosePrecalc[t] / (float)(NTilts - 1);
                                PerTiltPositions[p * NTilts + t] = NewPositions[p] + CoordsDiff * DoseID;
                            }
                        }
                        else
                        {
                            for (int t = 0; t < NTilts; t++)
                                PerTiltPositions[p * NTilts + t] = NewPositions[p];
                        }
                    }

                    float3[] CurrPositions = GetPositionInImages(PerTiltPositions);
                    for (int i = 0; i < ImageShifts.Length; i++)
                        ImageShifts[i] = new float2(ExtractedAt[i].X - CurrPositions[i].X,
                                                    ExtractedAt[i].Y - CurrPositions[i].Y); // -diff because those are extraction positions, i. e. opposite direction of shifts

                    return ImageShifts;
                };

                GetProjections = imageAngles =>
                {
                    Image Projections = new Image(IntPtr.Zero, new int3(CoarseSize, CoarseSize, NParticles * NTilts), true, true);
                    foreach (var subset in SubsetRanges)
                    {
                        Projector Reference = references[subset.Key];
                        int SubsetStart = subset.Value.Item1 * NTilts;
                        int SubsetEnd = subset.Value.Item2 * NTilts;
                        float3[] SubsetAngles = imageAngles.Skip(SubsetStart).Take(SubsetEnd - SubsetStart).ToArray();

                        GPU.ProjectForward(Reference.Data.GetDevice(Intent.Read),
                                           Projections.GetDeviceSlice(SubsetStart, Intent.Write),
                                           Reference.Data.Dims,
                                           new int2(CoarseSize, CoarseSize),
                                           Helper.ToInterleaved(SubsetAngles),
                                           Reference.Oversampling,
                                           (uint)(SubsetEnd - SubsetStart));
                    }

                    /*Image CheckProjections = Projections.AsIFFT();
                    //CheckProjections.RemapFromFT();
                    CheckProjections.WriteMRC("d_projections.mrc");
                    CheckProjections.Dispose();*/

                    return Projections;
                };

                EvalIndividual = input =>
                {
                    Tuple<float2[], float3[]> ShiftsAndAngles = GetImageShiftsAndAngles(input);

                    Image Projections = GetProjections(ShiftsAndAngles.Item2);

                    float[] Results = new float[NParticles * NTilts];

                    GPU.TomoRefineGetDiff(ParticleImages.GetDevice(Intent.Read),
                                          Projections.GetDevice(Intent.Read),
                                          ShiftFactors.GetDevice(Intent.Read),
                                          ParticleCTFs.GetDevice(Intent.Read),
                                          ParticleWeights.GetDevice(Intent.Read),
                                          new int2(CoarseSize, CoarseSize),
                                          Helper.ToInterleaved(ShiftsAndAngles.Item1),
                                          Results,
                                          (uint)(NParticles * NTilts));

                    Projections.Dispose();

                    return Results.Select(i => (double)i).ToArray();
                };

                int OptimizationIterations = 0;
                bool GetOut = false;

                double Delta = 0.1;
                float Delta2 = 2 * (float)Delta;

                int[] WarpGridIDs = { 5, 6, 7 };
                Dictionary<int, float2[][]> WiggleWeightsWarp = new Dictionary<int, float2[][]>();
                foreach (var gridID in WarpGridIDs)
                {
                    int NElements = GridElements[gridID];
                    WiggleWeightsWarp.Add(gridID, new float2[NElements][]);

                    for (int ge = 0; ge < NElements; ge++)
                    {
                        double[] InputMinus = new double[StartParams.Length], InputPlus = new double[StartParams.Length];
                        for (int i = 0; i < StartParams.Length; i++)
                        {
                            InputMinus[i] = StartParams[i];
                            InputPlus[i] = StartParams[i];
                        }

                        InputMinus[VectorGridRanges[gridID].Item1 + ge] -= Delta;
                        InputPlus[VectorGridRanges[gridID].Item1 + ge] += Delta;

                        float2[] ImageShiftsPlus = GetImageShifts(InputPlus);
                        float2[] ImageShiftsMinus = GetImageShifts(InputMinus);

                        float2[] Weights = new float2[ImageShiftsPlus.Length];

                        for (int i = 0; i < ImageShiftsPlus.Length; i++)
                            Weights[i] = (ImageShiftsPlus[i] - ImageShiftsMinus[i]) / Delta2;

                        WiggleWeightsWarp[gridID][ge] = Weights;
                    }
                }

                Eval = input =>
                {
                    double Result = EvalIndividual(input).Sum();
                    lock (tableIn)
                        Debug.WriteLine(GPU.GetDevice() + ", " + RootName + ": " + Result);
                    OptimizationIterations++;

                    return Result;
                };

                Func<double[], double[], double, double[]> GradientParticles = (inputMinus, inputPlus, delta) =>
                {
                    double[] EvalMinus = EvalIndividual(inputMinus);
                    double[] EvalPlus = EvalIndividual(inputPlus);

                    double[] Diff = new double[EvalMinus.Length];
                    for (int i = 0; i < Diff.Length; i++)
                        Diff[i] = (EvalPlus[i] - EvalMinus[i]) / (2 * delta);

                    return Diff;
                };

                Gradient = input =>
                {
                    double[] Result = new double[input.Length];

                    if (OptimizationIterations > 60)
                        return Result;

                    float2[] ImageShiftGradients = new float2[NParticles * NTilts];
                    #region Compute gradient for individual image shifts
                    {
                        Tuple<float2[], float3[]> ShiftsAndAngles = GetImageShiftsAndAngles(input);
                        Image Projections = GetProjections(ShiftsAndAngles.Item2);

                        float2[] ShiftsXPlus = new float2[NParticles * NTilts];
                        float2[] ShiftsXMinus = new float2[NParticles * NTilts];
                        float2[] ShiftsYPlus = new float2[NParticles * NTilts];
                        float2[] ShiftsYMinus = new float2[NParticles * NTilts];

                        float2 DeltaX = new float2((float)Delta, 0);
                        float2 DeltaY = new float2(0, (float)Delta);

                        for (int i = 0; i < ShiftsXPlus.Length; i++)
                        {
                            ShiftsXPlus[i] = ShiftsAndAngles.Item1[i] + DeltaX;
                            ShiftsXMinus[i] = ShiftsAndAngles.Item1[i] - DeltaX;

                            ShiftsYPlus[i] = ShiftsAndAngles.Item1[i] + DeltaY;
                            ShiftsYMinus[i] = ShiftsAndAngles.Item1[i] - DeltaY;
                        }

                        float[] ScoresXPlus = new float[NParticles * NTilts];
                        float[] ScoresXMinus = new float[NParticles * NTilts];
                        float[] ScoresYPlus = new float[NParticles * NTilts];
                        float[] ScoresYMinus = new float[NParticles * NTilts];

                        GPU.TomoRefineGetDiff(ParticleImages.GetDevice(Intent.Read),
                                              Projections.GetDevice(Intent.Read),
                                              ShiftFactors.GetDevice(Intent.Read),
                                              ParticleCTFs.GetDevice(Intent.Read),
                                              ParticleWeights.GetDevice(Intent.Read),
                                              new int2(CoarseSize, CoarseSize),
                                              Helper.ToInterleaved(ShiftsXPlus),
                                              ScoresXPlus,
                                              (uint)(NParticles * NTilts));
                        GPU.TomoRefineGetDiff(ParticleImages.GetDevice(Intent.Read),
                                              Projections.GetDevice(Intent.Read),
                                              ShiftFactors.GetDevice(Intent.Read),
                                              ParticleCTFs.GetDevice(Intent.Read),
                                              ParticleWeights.GetDevice(Intent.Read),
                                              new int2(CoarseSize, CoarseSize),
                                              Helper.ToInterleaved(ShiftsXMinus),
                                              ScoresXMinus,
                                              (uint)(NParticles * NTilts));
                        GPU.TomoRefineGetDiff(ParticleImages.GetDevice(Intent.Read),
                                              Projections.GetDevice(Intent.Read),
                                              ShiftFactors.GetDevice(Intent.Read),
                                              ParticleCTFs.GetDevice(Intent.Read),
                                              ParticleWeights.GetDevice(Intent.Read),
                                              new int2(CoarseSize, CoarseSize),
                                              Helper.ToInterleaved(ShiftsYPlus),
                                              ScoresYPlus,
                                              (uint)(NParticles * NTilts));
                        GPU.TomoRefineGetDiff(ParticleImages.GetDevice(Intent.Read),
                                              Projections.GetDevice(Intent.Read),
                                              ShiftFactors.GetDevice(Intent.Read),
                                              ParticleCTFs.GetDevice(Intent.Read),
                                              ParticleWeights.GetDevice(Intent.Read),
                                              new int2(CoarseSize, CoarseSize),
                                              Helper.ToInterleaved(ShiftsYMinus),
                                              ScoresYMinus,
                                              (uint)(NParticles * NTilts));

                        Projections.Dispose();

                        for (int i = 0; i < ImageShiftGradients.Length; i++)
                        {
                            ImageShiftGradients[i] = new float2((ScoresXPlus[i] - ScoresXMinus[i]) / Delta2,
                                                           (ScoresYPlus[i] - ScoresYMinus[i]) / Delta2);
                        }
                    }
                    #endregion

                    // First, do particle parameters, i. e. 3D position within tomogram, rotation, across 2 points in time
                    // Altering each particle's parameters results in a change in its NTilts images, but nothing else
                    {
                        int[] TranslationIDs = DoPerParticleMotion ? new[] { 0, 1, 2, 3, 4, 5 } : new[] { 0, 1, 2 };
                        int[] RotationIDs = DoPerParticleMotion ? new [] {6, 7, 8, 9, 10, 11} : new [] { 6, 7, 8};
                        foreach (var paramID in RotationIDs)
                        {
                            double[] InputMinus = new double[input.Length], InputPlus = new double[input.Length];
                            for (int i = 0; i < input.Length; i++)
                            {
                                InputMinus[i] = input[i];
                                InputPlus[i] = input[i];
                            }
                            for (int p = 0; p < NParticles; p++)
                            {
                                InputMinus[NVectorGridParams + p * 12 + paramID] -= Delta;
                                InputPlus[NVectorGridParams + p * 12 + paramID] += Delta;
                            }

                            double[] ResultParticles = GradientParticles(InputMinus, InputPlus, Delta);
                            for (int p = 0; p < NParticles; p++)
                            {
                                double ParticleSum = 0;
                                for (int t = 0; t < NTilts; t++)
                                    ParticleSum += ResultParticles[p * NTilts + t];

                                Result[NVectorGridParams + p * 12 + paramID] = ParticleSum;
                            }
                        }

                        // Translation-related gradients can all be computed efficiently from previously retrieved per-image gradients
                        foreach (var paramID in TranslationIDs)
                        {
                            double[] InputMinus = new double[input.Length], InputPlus = new double[input.Length];
                            for (int i = 0; i < input.Length; i++)
                            {
                                InputMinus[i] = input[i];
                                InputPlus[i] = input[i];
                            }
                            for (int p = 0; p < NParticles; p++)
                            {
                                InputMinus[NVectorGridParams + p * 12 + paramID] -= Delta;
                                InputPlus[NVectorGridParams + p * 12 + paramID] += Delta;
                            }

                            float2[] ImageShiftsPlus = GetImageShifts(InputPlus);
                            float2[] ImageShiftsMinus = GetImageShifts(InputMinus);

                            for (int p = 0; p < NParticles; p++)
                            {
                                double ParticleSum = 0;
                                for (int t = 0; t < NTilts; t++)
                                {
                                    int i = p * NTilts + t;
                                    float2 ShiftDelta = (ImageShiftsPlus[i] - ImageShiftsMinus[i]) / Delta2;
                                    float ShiftGradient = ShiftDelta.X * ImageShiftGradients[i].X + ShiftDelta.Y * ImageShiftGradients[i].Y;

                                    ParticleSum += ShiftGradient;
                                }

                                Result[NVectorGridParams + p * 12 + paramID] = ParticleSum;
                            }
                        }

                        // If there is no per-particle motion, just copy the gradients for these parameters from parameterIDs 0-5
                        if (!DoPerParticleMotion)
                        {
                            int[] RedundantIDs = { 3, 4, 5, 9, 10, 11 };
                            foreach (var paramID in RedundantIDs)
                                for (int p = 0; p < NParticles; p++)
                                    Result[NVectorGridParams + p * 12 + paramID] = Result[NVectorGridParams + p * 12 + paramID - 3];
                        }
                    }

                    // Now deal with grids. Each grid slice (i. e. temporal point) will correspond to one tilt only, thus the gradient
                    // for each slice is the (weighted, in case of spatial resolution) sum of NParticles images in the corresponding tilt.
                    if (DoImageAlignment)
                    {
                        int[] RotationGridIDs = { 2, 3, 4 };
                        foreach (var gridID in RotationGridIDs)
                        {
                            int SliceElements = GridSliceElements[gridID];

                            for (int se = 0; se < SliceElements; se++)
                            {
                                double[] InputMinus = new double[input.Length], InputPlus = new double[input.Length];
                                for (int i = 0; i < input.Length; i++)
                                {
                                    InputMinus[i] = input[i];
                                    InputPlus[i] = input[i];
                                }
                                for (int gp = VectorGridRanges[gridID].Item1 + se; gp < VectorGridRanges[gridID].Item2; gp += SliceElements)
                                {
                                    InputMinus[gp] -= Delta;
                                    InputPlus[gp] += Delta;
                                }

                                double[] ResultParticles = GradientParticles(InputMinus, InputPlus, Delta);
                                for (int i = 0; i < ResultParticles.Length; i++)
                                {
                                    int GridTime = i % NTilts;
                                    Result[VectorGridRanges[gridID].Item1 + GridTime * SliceElements + se] += ResultParticles[i];
                                }
                            }
                        }

                        // Translation-related gradients can all be computed efficiently from previously retrieved per-image gradients
                        int[] TranslationGridIDs = { 0, 1 };
                        foreach (var gridID in TranslationGridIDs)
                        {
                            int SliceElements = GridSliceElements[gridID];

                            for (int se = 0; se < SliceElements; se++)
                            {
                                double[] InputMinus = new double[input.Length], InputPlus = new double[input.Length];
                                for (int i = 0; i < input.Length; i++)
                                {
                                    InputMinus[i] = input[i];
                                    InputPlus[i] = input[i];
                                }
                                for (int gp = VectorGridRanges[gridID].Item1 + se; gp < VectorGridRanges[gridID].Item2; gp += SliceElements)
                                {
                                    InputMinus[gp] -= Delta;
                                    InputPlus[gp] += Delta;
                                }

                                float2[] ImageShiftsPlus = GetImageShifts(InputPlus);
                                float2[] ImageShiftsMinus = GetImageShifts(InputMinus);

                                for (int i = 0; i < ImageShiftsPlus.Length; i++)
                                {
                                    float2 ShiftDelta = (ImageShiftsPlus[i] - ImageShiftsMinus[i]) / Delta2;
                                    float ShiftGradient = ShiftDelta.X * ImageShiftGradients[i].X + ShiftDelta.Y * ImageShiftGradients[i].Y;

                                    int GridSlice = i % NTilts;
                                    Result[VectorGridRanges[gridID].Item1 + GridSlice * SliceElements + se] += ShiftGradient;
                                }
                            }
                        }
                        // Warp grids don't have any shortcuts for getting multiple gradients at once, so they use pre-calculated wiggle weights
                        foreach (var gridID in WarpGridIDs)
                        {
                            int NElements = GridElements[gridID];

                            for (int ge = 0; ge < NElements; ge++)
                            {
                                float2[] Weights = WiggleWeightsWarp[gridID][ge];

                                for (int i = 0; i < Weights.Length; i++)
                                {
                                    float2 ShiftDelta = Weights[i];
                                    float ShiftGradient = ShiftDelta.X * ImageShiftGradients[i].X + ShiftDelta.Y * ImageShiftGradients[i].Y;

                                    Result[VectorGridRanges[gridID].Item1 + ge] += ShiftGradient;
                                }
                            }
                        }
                    }

                    return Result;
                };
            }

            #endregion

            BroydenFletcherGoldfarbShanno Optimizer = new BroydenFletcherGoldfarbShanno(StartParams.Length, Eval, Gradient);
            Optimizer.Epsilon = 3e-7;

            Optimizer.Maximize(StartParams);

            float3[] OptimizedOrigins, OptimizedOrigins2, OptimizedAngles, OptimizedAngles2;
            GetParametersFromVector(StartParams, NParticles, size, out OptimizedOrigins, out OptimizedOrigins2, out OptimizedAngles, out OptimizedAngles2);
            SetGridsFromVector(StartParams, Dimensions.X);

            #region Calculate correlation scores, update table with new positions and angles
            {
                double[] ImageScores = EvalIndividual(StartParams);
                float[] ParticleScores = new float[NParticles];
                for (int i = 0; i < ImageScores.Length; i++)
                    ParticleScores[i / NTilts] += (float)ImageScores[i];

                //if (!tableIn.HasColumn("rlnOriginXPrior"))
                //    tableIn.AddColumn("rlnOriginXPrior");
                //if (!tableIn.HasColumn("rlnOriginYPrior"))
                //    tableIn.AddColumn("rlnOriginYPrior");
                //if (!tableIn.HasColumn("rlnOriginZPrior"))
                //    tableIn.AddColumn("rlnOriginZPrior");

                //if (!tableIn.HasColumn("rlnAngleRotPrior"))
                //    tableIn.AddColumn("rlnAngleRotPrior");
                //if (!tableIn.HasColumn("rlnAngleTiltPrior"))
                //    tableIn.AddColumn("rlnAngleTiltPrior");
                //if (!tableIn.HasColumn("rlnAnglePsiPrior"))
                //    tableIn.AddColumn("rlnAnglePsiPrior");

                lock (tableIn)
                    for (int p = 0; p < NParticles; p++)
                    {
                        int Row = RowIndices[SubsetContinuousIDs[p]];

                        tableIn.SetRowValue(Row, "rlnCoordinateX", OptimizedOrigins[p].X.ToString(CultureInfo.InvariantCulture));
                        tableIn.SetRowValue(Row, "rlnCoordinateY", OptimizedOrigins[p].Y.ToString(CultureInfo.InvariantCulture));
                        tableIn.SetRowValue(Row, "rlnCoordinateZ", OptimizedOrigins[p].Z.ToString(CultureInfo.InvariantCulture));

                        //tableIn.SetRowValue(Row, "rlnOriginXPrior", OptimizedOrigins2[p].X.ToString(CultureInfo.InvariantCulture));
                        //tableIn.SetRowValue(Row, "rlnOriginYPrior", OptimizedOrigins2[p].Y.ToString(CultureInfo.InvariantCulture));
                        //tableIn.SetRowValue(Row, "rlnOriginZPrior", OptimizedOrigins2[p].Z.ToString(CultureInfo.InvariantCulture));

                        tableIn.SetRowValue(Row, "rlnAngleRot", OptimizedAngles[p].X.ToString(CultureInfo.InvariantCulture));
                        tableIn.SetRowValue(Row, "rlnAngleTilt", OptimizedAngles[p].Y.ToString(CultureInfo.InvariantCulture));
                        tableIn.SetRowValue(Row, "rlnAnglePsi", OptimizedAngles[p].Z.ToString(CultureInfo.InvariantCulture));

                        //tableIn.SetRowValue(Row, "rlnAngleRotPrior", OptimizedAngles2[p].X.ToString(CultureInfo.InvariantCulture));
                        //tableIn.SetRowValue(Row, "rlnAngleTiltPrior", OptimizedAngles2[p].Y.ToString(CultureInfo.InvariantCulture));
                        //tableIn.SetRowValue(Row, "rlnAnglePsiPrior", OptimizedAngles2[p].Z.ToString(CultureInfo.InvariantCulture));

                        tableIn.SetRowValue(Row, "rlnParticleSelectZScore", ParticleScores[p].ToString(CultureInfo.InvariantCulture));
                    }
            }
            #endregion

            ParticleImages?.Dispose();
            ParticleCTFs?.Dispose();
            ParticleWeights?.Dispose();
            ShiftFactors?.Dispose();

            #region Extract particles at full resolution and back-project them into the reconstruction volumes
            {
                GPU.SetDevice(0);

                Image CTFCoords = GetCTFCoords(size, size);
                int[] SortedDosePrecalc = IndicesSortedDose;

                foreach (var subsetRange in SubsetRanges)
                {
                    lock (outReconstructions[subsetRange.Key])
                    {
                        for (int p = subsetRange.Value.Item1; p < subsetRange.Value.Item2; p++)
                        {
                            float3[] PerTiltPositions = new float3[NTilts];
                            float3[] PerTiltAngles = new float3[NTilts];
                            float3 CoordsDiff = OptimizedOrigins2[p] - OptimizedOrigins[p];
                            float3 AnglesDiff = OptimizedAngles2[p] - OptimizedAngles[p];
                            for (int t = 0; t < NTilts; t++)
                            {
                                float DoseID = SortedDosePrecalc[t] / (float)(NTilts - 1);
                                PerTiltPositions[t] = OptimizedOrigins[p] + CoordsDiff * DoseID;
                                PerTiltAngles[t] = OptimizedAngles[p] + AnglesDiff * DoseID;
                            }

                            Image FullParticleImages = GetSubtomoImages(tiltStack, size, PerTiltPositions, true);
                            Image FullParticleCTFs = GetSubtomoCTFs(PerTiltPositions, CTFCoords);

                            FullParticleImages.Multiply(FullParticleCTFs);
                            FullParticleCTFs.Abs();

                            float3[] FullParticleAngles = GetParticleAngleInImages(PerTiltPositions, PerTiltAngles);

                            outReconstructions[subsetRange.Key].BackProject(FullParticleImages, FullParticleCTFs, FullParticleAngles);

                            FullParticleImages.Dispose();
                            FullParticleCTFs.Dispose();
                        }

                        for (int p = subsetRange.Value.Item1; p < subsetRange.Value.Item2; p++)
                        {
                            float3[] PerTiltPositions = new float3[NTilts];
                            float3[] PerTiltAngles = new float3[NTilts];
                            float3 CoordsDiff = OptimizedOrigins2[p] - OptimizedOrigins[p];
                            float3 AnglesDiff = OptimizedAngles2[p] - OptimizedAngles[p];
                            for (int t = 0; t < NTilts; t++)
                            {
                                float DoseID = SortedDosePrecalc[t] / (float)(NTilts - 1);
                                PerTiltPositions[t] = OptimizedOrigins[p] + CoordsDiff * DoseID;
                                PerTiltAngles[t] = OptimizedAngles[p] + AnglesDiff * DoseID;
                            }

                            float3[] FullParticleAngles = GetParticleAngleInImages(PerTiltPositions, PerTiltAngles);

                            Image FullParticleCTFs = GetSubtomoCTFs(PerTiltPositions, CTFCoords, false);
                            Image FullParticleCTFWeights = GetSubtomoCTFs(PerTiltPositions, CTFCoords, true);

                            // CTF has to be converted to complex numbers with imag = 0
                            float2[] CTFsComplexData = new float2[FullParticleCTFs.ElementsComplex];
                            float[] CTFWeightsData = new float[FullParticleCTFs.ElementsComplex];
                            float[] CTFsContinuousData = FullParticleCTFs.GetHostContinuousCopy();
                            float[] CTFWeightsContinuousData = FullParticleCTFWeights.GetHostContinuousCopy();
                            for (int i = 0; i < CTFsComplexData.Length; i++)
                            {
                                CTFsComplexData[i] = new float2(Math.Abs(CTFsContinuousData[i] * CTFWeightsContinuousData[i]), 0);
                                CTFWeightsData[i] = Math.Abs(CTFWeightsContinuousData[i]);
                            }

                            Image CTFsComplex = new Image(CTFsComplexData, FullParticleCTFs.Dims, true);
                            Image CTFWeights = new Image(CTFWeightsData, FullParticleCTFs.Dims, true);

                            outCTFReconstructions[subsetRange.Key].BackProject(CTFsComplex, CTFWeights, FullParticleAngles);

                            FullParticleCTFs.Dispose();
                            FullParticleCTFWeights.Dispose();
                            CTFsComplex.Dispose();
                            CTFWeights.Dispose();
                        }

                        outReconstructions[subsetRange.Key].FreeDevice();
                        outCTFReconstructions[subsetRange.Key].FreeDevice();
                    }
                }

                CTFCoords.Dispose();
            }
            #endregion

            SaveMeta();
        }
Example #19
0
        public static void FitCTF(float2[] data, Func<float[], float[]> approximation, float[] zeros, float[] peaks, out Cubic1D background, out Cubic1D scale)
        {
            float MinX = MathHelper.Min(data.Select(p => p.X)), MaxX = MathHelper.Max(data.Select(p => p.X)), ScaleX = 1f / (MaxX - MinX);
            float MinY = MathHelper.Min(data.Select(p => p.Y)), MaxY = MathHelper.Max(data.Select(p => p.Y)), ScaleY = 1f / (MaxY - MinY);
            if (float.IsNaN(ScaleY))
                ScaleY = 1f;

            peaks = peaks.Where(v => v >= MinX && v <= MaxX).Where((v, i) => i % 1 == 0).ToArray();
            zeros = zeros.Where(v => v >= MinX && v <= MaxX).Where((v, i) => i % 1 == 0).ToArray();

            float2[] ScaledData = data.Select(p => new float2((p.X - MinX) * ScaleX, (p.Y - MinY) * ScaleY)).ToArray();
            float StdY = MathHelper.StdDev(data.Select(p => p.Y).ToArray());

            double[] Start = new double[zeros.Length + peaks.Length];
            double[] NodeX = new double[zeros.Length + peaks.Length];
            Cubic1D DataSpline = new Cubic1D(data);
            for (int i = 0; i < zeros.Length; i++)
            {
                NodeX[i] = (zeros[i] - MinX) * ScaleX;
                Start[i] = DataSpline.Interp(zeros[i]) - MinY;
            }
            {
                Cubic1D PreliminaryBackground = new Cubic1D(Helper.Zip(NodeX.Take(zeros.Length).Select(v => (float)v).ToArray(),
                                                                       Start.Take(zeros.Length).Select(v => (float)v).ToArray()));
                float[] PreliminaryInterpolated = PreliminaryBackground.Interp(data.Select(v => (v.X - MinX) * ScaleX).ToArray());
                float2[] BackgroundSubtracted = data.Select((v, i) => new float2(v.X, v.Y - MinY - PreliminaryInterpolated[i])).ToArray();
                Cubic1D BackgroundSpline = new Cubic1D(BackgroundSubtracted);

                for (int i = 0; i < peaks.Length; i++)
                {
                    NodeX[i + zeros.Length] = (peaks[i] - MinX) * ScaleX;
                    float PeakValue = BackgroundSpline.Interp(peaks[i]);
                    Start[i + zeros.Length] = Math.Max(0.0001f, PeakValue);
                }
            }

            float[] DataX = ScaledData.Select(p => p.X).ToArray();
            float[] OriginalDataX = data.Select(p => p.X).ToArray();
            float[] SimulatedCTF = approximation(OriginalDataX);

            float2[] NodesBackground = new float2[zeros.Length];
            for (int i = 0; i < NodesBackground.Length; i++)
                NodesBackground[i] = new float2((float)NodeX[i], 0f);
            float2[] NodesScale = new float2[peaks.Length];
            for (int i = 0; i < NodesScale.Length; i++)
                NodesScale[i] = new float2((float)NodeX[i + zeros.Length], 0f);

            Func<double[], double> Eval = input =>
            {
                float2[] NodesBackgroundCopy = new float2[NodesBackground.Length];
                for (int i = 0; i < zeros.Length; i++)
                    NodesBackgroundCopy[i] = new float2(NodesBackground[i].X, (float)input[i]);

                float2[] NodesScaleCopy = new float2[NodesScale.Length];
                for (int i = 0; i < peaks.Length; i++)
                    NodesScaleCopy[i] = new float2(NodesScale[i].X, (float)input[i + zeros.Length]);

                float[] InterpolatedBackground = new Cubic1D(NodesBackgroundCopy).Interp(DataX);
                float[] InterpolatedScale = new Cubic1D(NodesScaleCopy).Interp(DataX);

                double Sum = 0f;
                for (int i = 0; i < ScaledData.Length; i++)
                {
                    double Diff = ScaledData[i].Y - (InterpolatedBackground[i] + SimulatedCTF[i] * (double)InterpolatedScale[i]) * ScaleY;
                    Sum += Diff * Diff;
                    if (InterpolatedScale[i] < 0.0005f)
                        Sum += (0.0005 - InterpolatedScale[i]) * 10;
                }

                //return Math.Sqrt(Sum / data.Length) * 10;
                return 0;
            };

            Func<double[], double[]> Gradient = input =>
            {
                double[] Result = new double[input.Length];

                //Parallel.For(0, input.Length, i =>
                for (int i = 0; i < input.Length; i++)
                {
                    double[] UpperInput = new double[input.Length];
                    input.CopyTo(UpperInput, 0);
                    UpperInput[i] += 0.0001;
                    double UpperValue = Eval(UpperInput);

                    double[] LowerInput = new double[input.Length];
                    input.CopyTo(LowerInput, 0);
                    LowerInput[i] -= 0.0001;
                    double LowerValue = Eval(LowerInput);

                    Result[i] = (UpperValue - LowerValue) / 0.0002;
                }//);

                return Result;
            };

            BroydenFletcherGoldfarbShanno Optimizer = new BroydenFletcherGoldfarbShanno(Start.Length, Eval, Gradient);
            Optimizer.Minimize(Start);

            {
                for (int i = 0; i < zeros.Length; i++)
                    NodesBackground[i] = new float2((float) NodeX[i] / ScaleX + MinX, (float) Optimizer.Solution[i] + MinY);
                for (int i = 0; i < peaks.Length; i++)
                    NodesScale[i] = new float2((float)NodeX[i + zeros.Length] / ScaleX + MinX, Math.Max(0.001f, (float)Optimizer.Solution[i + zeros.Length]));

                background = new Cubic1D(NodesBackground);
                scale = new Cubic1D(NodesScale);
            }
        }
Example #20
0
        private unsafe int cvsmod(double[] s, ref double stp, ref int info,
                                  ref int nfev, double[] wa, ref double dginit, ref double dgout)
        {
            int n = numberOfVariables;

            if (info == 1)
            {
                goto L321;
            }

            infoc = 1;


            if (stp <= 0) // Check the input parameters for errors
            {
                throw new LineSearchFailedException(1, "Invalid step size.");
            }

            // Compute the initial gradient in the search direction
            // and check that S is a descent direction.

            if (dginit >= 0)
            {
                throw new LineSearchFailedException(0, "The search direction is not a descent direction.");
            }

            // Initialize local variables
            brackt = false;
            stage1 = true;
            nfev   = 0;
            finit  = f;
            dgtest = ftol * dginit;
            width  = stpmax - stpmin;
            width1 = width / 0.5;

            for (int j = 0; j < x.Length; ++j)
            {
                wa[j] = x[j];
            }

            // The variables STX, FX, DGX contain the values of the step,
            //   function, and directional derivative at the best step.
            // The variables STY, FY, DGY contain the value of the step,
            //   function, and derivative at the other endpoint of the interval
            //   of uncertainty.
            // The variables STP, F, DG contain the values of the step,
            //   function, and derivative at the current step.

            stx = 0;
            fx  = finit;
            dgx = dginit;
            sty = 0;
            fy  = finit;
            dgy = dginit;


L30:        // Start of iteration.

            // Set the minimum and maximum steps to correspond
            // to the present interval of uncertainty.

            if (brackt)
            {
                stmin = Math.Min(stx, sty);
                stmax = Math.Max(stx, sty);
            }
            else
            {
                stmin = stx;
                stmax = stp + 4 * (stp - stx);
            }

            // Force the step to be within
            // the bounds STPMAX and STPMIN.

            stp = Math.Max(stp, stpmin);
            stp = Math.Min(stp, stpmax);

            // If an unusual termination is to occur then
            // let STP be the lowest point obtained so far.

            if (brackt && (stp <= stmin || stp >= stmax) || nfev >= maxfev - 1 ||
                infoc == 0 || brackt && stmax - stmin <= xtol * stmax)
            {
                stp = stx;
            }

            // Evaluate the function and gradient at STP
            // and compute the directional derivative.

            for (int j = 0; j < s.Length; ++j)
            {
                x[j] = wa[j] + stp * s[j];
            }

            // Fetch function and gradient
            f = Function(x);
            g = Gradient(x);


            info = 0;
            nfev++;
            dg2 = 0;

            for (int j = 0; j < g.Length; ++j)
            {
                dg2 += g[j] * s[j];
            }

            ftest1 = finit + stp * dgtest;

            if ((brackt && (stp <= stmin || stp >= stmax)) || infoc == 0)
            {
                throw new LineSearchFailedException(6, "Rounding errors prevent further progress." +
                                                    "There may not be a step which satisfies the sufficient decrease and curvature conditions. Tolerances may be too small.");
            }

            if (stp == stpmax && f <= ftest1 && dg2 <= dgtest)
            {
                throw new LineSearchFailedException(5, "The step size has reached the upper bound.");
            }

            if (stp == stpmin && (f > ftest1 || dg2 >= dgtest))
            {
                throw new LineSearchFailedException(4, "The step size has reached the lower bound.");
            }

            if (nfev >= maxfev)
            {
                throw new LineSearchFailedException(3, "Maximum number of function evaluations has been reached.");
            }

            if (brackt && stmax - stmin <= xtol * stmax)
            {
                throw new LineSearchFailedException(2, "Relative width of the interval of uncertainty is at machine precision.");
            }


            // More's code has been modified so that at least one new
            //  function value is computed during the line search (enforcing
            //  at least one interpolation is not easy, since the code may
            //  override an interpolation)

            if (f <= ftest1 && Math.Abs(dg2) <= gtol * (-dginit) && nfev > 1)
            {
                info  = 1;
                dgout = dg2;
                return(0);
            }


L321:

            // In the first stage we seek a step for which the modified
            // function has a nonpositive value and nonnegative derivative.
            if (stage1 && f <= ftest1 && dg2 >= Math.Min(ftol, gtol) * dginit)
            {
                stage1 = false;
            }

            // A modified function is used to predict the step only if
            // we have not obtained a step for which the modified function
            // has a nonpositive function value and nonnegative derivative,
            // and if a lower function value has been obtained but the
            // decrease is not sufficient.

            if (stage1 && f <= fx && f > ftest1)
            {
                // Define the modified function and derivative values
                double fm   = f - stp * dgtest;
                double fxm  = fx - stx * dgtest;
                double fym  = fy - sty * dgtest;
                double dgm  = dg2 - dgtest;
                double dgxm = dgx - dgtest;
                double dgym = dgy - dgtest;

                // Call CSTEPM to update the interval of
                // uncertainty and to compute the new step.

                BroydenFletcherGoldfarbShanno.SearchStep(ref stx, ref fxm, ref dgxm,
                                                         ref sty, ref fym, ref dgym, ref stp, fm, dgm, ref brackt, out infoc);

                // Reset the function and gradient values for f.
                fx  = fxm + stx * dgtest;
                fy  = fym + sty * dgtest;
                dgx = dgxm + dgtest;
                dgy = dgym + dgtest;
            }
            else
            {
                // Call CSTEPM to update the interval of
                // uncertainty and to compute the new step.
                BroydenFletcherGoldfarbShanno.SearchStep(ref stx, ref fx, ref dgx,
                                                         ref sty, ref fy, ref dgy, ref stp, f, dg2, ref brackt, out infoc);
            }

            // Force a sufficient decrease in the
            // size of the interval of uncertainty.

            if (brackt)
            {
                if ((Math.Abs(sty - stx)) >= 0.66 * width1)
                {
                    stp = stx + 0.5 * (sty - stx);
                }

                width1 = width;
                width  = Math.Abs(sty - stx);
            }

            goto L30;
        }
Example #21
0
        private void OptimizePerTiltWeights()
        {
            if (!Options.Movies.Any(m => m.GetType() == typeof(TiltSeries)))
                return;

            Image Mask = StageDataLoad.LoadMap("F:\\stefanribo\\vlion\\mask_warped2_OST_post.mrc", new int2(1, 1), 0, typeof(float));
            List<Image> SubsetMasks = new List<Image> { Mask, Mask };

            int3 Dims = Mask.Dims;
            float AngleMin = float.MaxValue, AngleMax = float.MinValue;
            float DoseMax = float.MinValue;
            List<WeightOptContainer> Reconstructions = new List<WeightOptContainer>();
            Dictionary<TiltSeries, int> SeriesIndices = new Dictionary<TiltSeries, int>();

            int NTilts = 0;

            {
                TiltSeries Series = (TiltSeries)Options.Movies[0];
                string[] FileNames = Directory.EnumerateFiles(Series.WeightOptimizationDir, "subset*.mrc").Where(p => p.Contains("subset3") || p.Contains("subset4")).Select(v => new FileInfo(v).Name).ToArray();

                string[] MapNames = FileNames.Where(v => !v.Contains(".weight.mrc")).ToArray();
                string[] WeightNames = FileNames.Where(v => v.Contains(".weight.mrc")).ToArray();
                if (MapNames.Length != WeightNames.Length)
                    throw new Exception("Number of reconstructions and weights does not match!");

                string[] MapSuffixes = MapNames;
                int[] MapSubsets = MapSuffixes.Select(v =>
                {
                    string S = v.Substring(v.IndexOf("subset") + "subset".Length);
                    return int.Parse(S.Substring(0, S.IndexOf("_"))) - 3;
                }).ToArray();
                int[] MapTilts = MapSuffixes.Select(v =>
                {
                    string S = v.Substring(v.IndexOf("tilt") + "tilt".Length);
                    return int.Parse(S.Substring(0, S.IndexOf(".mrc")));
                }).ToArray();

                SeriesIndices.Add(Series, SeriesIndices.Count);

                float[] MapAngles = MapTilts.Select(t => Series.AnglesCorrect[t]).ToArray();
                float[] MapDoses = MapTilts.Select(t => Series.Dose[t]).ToArray();

                for (int i = 0; i < MapNames.Length; i++)
                {
                    Image Map = StageDataLoad.LoadMap(Series.WeightOptimizationDir + MapNames[i], new int2(1, 1), 0, typeof(float));
                    Image MapFT = Map.AsFFT(true);
                    float[] MapData = MapFT.GetHostContinuousCopy();
                    Map.Dispose();
                    MapFT.Dispose();

                    Image Weights = StageDataLoad.LoadMap(Series.WeightOptimizationDir + WeightNames[i], new int2(1, 1), 0, typeof(float));
                    float[] WeightsData = Weights.GetHostContinuousCopy();
                    Weights.Dispose();

                    Reconstructions.Add(new WeightOptContainer(SeriesIndices[Series], MapSubsets[i], MapData, WeightsData, MapAngles[i], MapDoses[i]));
                }

                AngleMin = Math.Min(MathHelper.Min(MapAngles), AngleMin);
                AngleMax = Math.Max(MathHelper.Max(MapAngles), AngleMax);
                DoseMax = Math.Max(MathHelper.Max(MapDoses), DoseMax);

                NTilts = Series.NTilts;

                //break;
            }

            float[][] PackedRecFT = new float[SeriesIndices.Count][];
            float[][] PackedRecWeights = new float[SeriesIndices.Count][];
            foreach (var s in SeriesIndices)
            {
                WeightOptContainer[] SeriesRecs = Reconstructions.Where(r => r.SeriesID == s.Value).ToArray();
                PackedRecFT[s.Value] = new float[SeriesRecs.Length * SeriesRecs[0].DataFT.Length];
                PackedRecWeights[s.Value] = new float[SeriesRecs.Length * SeriesRecs[0].DataWeights.Length];

                for (int n = 0; n < SeriesRecs.Length; n++)
                {
                    Array.Copy(SeriesRecs[n].DataFT, 0, PackedRecFT[s.Value], n * SeriesRecs[0].DataFT.Length, SeriesRecs[0].DataFT.Length);
                    Array.Copy(SeriesRecs[n].DataWeights, 0, PackedRecWeights[s.Value], n * SeriesRecs[0].DataWeights.Length, SeriesRecs[0].DataWeights.Length);
                }
            }

            float PixelSize = (float)Options.Movies[0].CTF.PixelSize;
            float FreqMin = 1f / (10f / PixelSize), FreqMin2 = FreqMin * FreqMin;
            float FreqMax = 1f / (8.5f / PixelSize), FreqMax2 = FreqMax * FreqMax;

            int ShellMin = (int)(Dims.X * FreqMin);
            int ShellMax = (int)(Dims.X * FreqMax);
            int NShells = ShellMax - ShellMin;

            float[] R2 = new float[(Dims.X / 2 + 1) * Dims.Y * Dims.Z];
            int[] ShellIndices = new int[R2.Length];

            for (int z = 0; z < Dims.Z; z++)
            {
                int zz = z < Dims.Z / 2 + 1 ? z : z - Dims.Z;
                zz *= zz;
                for (int y = 0; y < Dims.Y; y++)
                {
                    int yy = y < Dims.Y / 2 + 1 ? y : y - Dims.Y;
                    yy *= yy;
                    for (int x = 0; x < Dims.X / 2 + 1; x++)
                    {
                        int xx = x;
                        xx *= x;

                        float r = (float)Math.Sqrt(zz + yy + xx) / Dims.X / PixelSize;
                        R2[(z * Dims.Y + y) * (Dims.X / 2 + 1) + x] = r * r;
                        int ir = (int)Math.Round(Math.Sqrt(zz + yy + xx));
                        ShellIndices[(z * Dims.Y + y) * (Dims.X / 2 + 1) + x] = ir < Dims.X / 2 ? ir : -1;
                    }
                }
            }

            float[] SeriesWeights = new float[SeriesIndices.Count];
            float[] SeriesBfacs = new float[SeriesIndices.Count];
            float[] InitGridAngle = new float[NTilts], InitGridDose = new float[NTilts];
            for (int i = 0; i < InitGridAngle.Length; i++)
            {
                InitGridAngle[i] = (float)Math.Cos((i / (float)(InitGridAngle.Length - 1) * (AngleMax - AngleMin) + AngleMin) * Helper.ToRad) * 100f;
                InitGridDose[i] = -8 * i / (float)(InitGridAngle.Length - 1) * DoseMax / 10f;
            }
            CubicGrid GridAngle = new CubicGrid(new int3(NTilts, 1, 1), InitGridAngle);
            CubicGrid GridDose = new CubicGrid(new int3(NTilts, 1, 1), InitGridDose);

            Func<double[], float[]> WeightedFSC = input =>
            {
                // Set parameters from input vector
                {
                    int Skip = 0;
                    GridAngle = new CubicGrid(GridAngle.Dimensions, input.Skip(Skip).Take((int)GridAngle.Dimensions.Elements()).Select(v => (float)v / 100f).ToArray());
                    Skip += (int)GridAngle.Dimensions.Elements();
                    GridDose = new CubicGrid(GridDose.Dimensions, input.Skip(Skip).Take((int)GridDose.Dimensions.Elements()).Select(v => (float)v * 10f).ToArray());
                }

                // Initialize sum vectors
                float[] FSC = new float[Dims.X / 2];

                float[] MapSum1 = new float[Dims.ElementsFFT() * 2], MapSum2 = new float[Dims.ElementsFFT() * 2];
                float[] WeightSum1 = new float[Dims.ElementsFFT()], WeightSum2 = new float[Dims.ElementsFFT()];

                int ElementsFT = (int)Dims.ElementsFFT();

                foreach (var s in SeriesIndices)
                {
                    WeightOptContainer[] SeriesRecs = Reconstructions.Where(r => r.SeriesID == s.Value).ToArray();

                    float[] PrecalcWeights = new float[SeriesRecs.Length];
                    float[] PrecalcBfacs = new float[SeriesRecs.Length];
                    int[] PrecalcSubsets = new int[SeriesRecs.Length];

                    for (int n = 0; n < SeriesRecs.Length; n++)
                    {
                        WeightOptContainer reconstruction = SeriesRecs[n];
                        // Weight is Weight(Series) * Weight(Angle) * exp((Bfac(Series) + Bfac(Dose)) / 4 * r^2)

                        float AngleWeight = GridAngle.GetInterpolated(new float3((reconstruction.Angle - AngleMin) / (AngleMax - AngleMin), 0.5f, 0.5f));
                        float DoseBfac = GridDose.GetInterpolated(new float3(reconstruction.Dose / DoseMax, 0.5f, 0.5f));

                        PrecalcWeights[n] = AngleWeight;
                        PrecalcBfacs[n] = DoseBfac * 0.25f;
                        PrecalcSubsets[n] = reconstruction.Subset;
                    }

                    CPU.OptimizeWeights(SeriesRecs.Length,
                                        PackedRecFT[s.Value],
                                        PackedRecWeights[s.Value],
                                        R2,
                                        ElementsFT,
                                        PrecalcSubsets,
                                        PrecalcBfacs,
                                        PrecalcWeights,
                                        MapSum1,
                                        MapSum2,
                                        WeightSum1,
                                        WeightSum2);
                }

                for (int i = 0; i < ElementsFT; i++)
                {
                    float Weight = Math.Max(1e-3f, WeightSum1[i]);
                    MapSum1[i * 2] /= Weight;
                    MapSum1[i * 2 + 1] /= Weight;

                    Weight = Math.Max(1e-3f, WeightSum2[i]);
                    MapSum2[i * 2] /= Weight;
                    MapSum2[i * 2 + 1] /= Weight;
                }

                lock (GridAngle)
                {
                    Image Map1FT = new Image(MapSum1, Dims, true, true);
                    Image Map1 = Map1FT.AsIFFT(true);
                    Map1.Multiply(SubsetMasks[0]);
                    Image MaskedFT1 = Map1.AsFFT(true);
                    float[] MaskedFT1Data = MaskedFT1.GetHostContinuousCopy();

                    Map1FT.Dispose();
                    Map1.Dispose();
                    MaskedFT1.Dispose();

                    Image Map2FT = new Image(MapSum2, Dims, true, true);
                    Image Map2 = Map2FT.AsIFFT(true);
                    Map2.Multiply(SubsetMasks[1]);
                    Image MaskedFT2 = Map2.AsFFT(true);
                    float[] MaskedFT2Data = MaskedFT2.GetHostContinuousCopy();

                    Map2FT.Dispose();
                    Map2.Dispose();
                    MaskedFT2.Dispose();

                    float[] Nums = new float[Dims.X / 2];
                    float[] Denoms1 = new float[Dims.X / 2];
                    float[] Denoms2 = new float[Dims.X / 2];
                    for (int i = 0; i < ElementsFT; i++)
                    {
                        int Shell = ShellIndices[i];
                        if (Shell < 0)
                            continue;

                        Nums[Shell] += MaskedFT1Data[i * 2] * MaskedFT2Data[i * 2] + MaskedFT1Data[i * 2 + 1] * MaskedFT2Data[i * 2 + 1];
                        Denoms1[Shell] += MaskedFT1Data[i * 2] * MaskedFT1Data[i * 2] + MaskedFT1Data[i * 2 + 1] * MaskedFT1Data[i * 2 + 1];
                        Denoms2[Shell] += MaskedFT2Data[i * 2] * MaskedFT2Data[i * 2] + MaskedFT2Data[i * 2 + 1] * MaskedFT2Data[i * 2 + 1];
                    }

                    for (int i = 0; i < Dims.X / 2; i++)
                        FSC[i] = Nums[i] / (float)Math.Sqrt(Denoms1[i] * Denoms2[i]);
                }

                return FSC;
            };

            Func<double[], double> EvalForGrad = input =>
            {
                return WeightedFSC(input).Skip(ShellMin).Take(NShells).Sum() * Reconstructions.Count;
            };

            Func<double[], double> Eval = input =>
            {
                double Score = EvalForGrad(input);
                Debug.WriteLine(Score);

                return Score;
            };

            int Iterations = 0;

            Func<double[], double[]> Grad = input =>
            {
                double[] Result = new double[input.Length];
                double Step = 1;

                if (Iterations++ > 15)
                    return Result;

                //Parallel.For(0, input.Length, new ParallelOptions { MaxDegreeOfParallelism = 4 }, i =>
                for (int i = 0; i < input.Length; i++)
                {
                    double[] InputCopy = input.ToList().ToArray();
                    double Original = InputCopy[i];
                    InputCopy[i] = Original + Step;
                    double ResultPlus = EvalForGrad(InputCopy);
                    InputCopy[i] = Original - Step;
                    double ResultMinus = EvalForGrad(InputCopy);
                    InputCopy[i] = Original;

                    Result[i] = (ResultPlus - ResultMinus) / (Step * 2);
                }//);

                return Result;
            };

            List<double> StartParamsList = new List<double>();
            StartParamsList.AddRange(GridAngle.FlatValues.Select(v => (double)v));
            StartParamsList.AddRange(GridDose.FlatValues.Select(v => (double)v));

            double[] StartParams = StartParamsList.ToArray();

            BroydenFletcherGoldfarbShanno Optimizer = new BroydenFletcherGoldfarbShanno(StartParams.Length, Eval, Grad);
            Optimizer.Epsilon = 3e-7;
            Optimizer.Maximize(StartParams);

            EvalForGrad(StartParams);

            float MaxAngleWeight = MathHelper.Max(GridAngle.FlatValues);
            GridAngle = new CubicGrid(GridAngle.Dimensions, GridAngle.FlatValues.Select(v => v / MaxAngleWeight).ToArray());

            float MaxDoseBfac = MathHelper.Max(GridDose.FlatValues);
            GridDose = new CubicGrid(GridDose.Dimensions, GridDose.FlatValues.Select(v => v - MaxDoseBfac).ToArray());

            foreach (var s in Options.Movies)
            {
                TiltSeries Series = (TiltSeries)s;

                List<float> AngleWeights = new List<float>();
                List<float> DoseBfacs = new List<float>();
                for (int i = 0; i < Series.Angles.Length; i++)
                {
                    float AngleWeight = GridAngle.GetInterpolated(new float3(Math.Min(1, (Series.AnglesCorrect[i] - AngleMin) / (AngleMax - AngleMin)), 0.5f, 0.5f));
                    float DoseBfac = GridDose.GetInterpolated(new float3(Math.Min(1, Series.Dose[i] / DoseMax), 0.5f, 0.5f));

                    AngleWeights.Add(AngleWeight);
                    DoseBfacs.Add(DoseBfac);
                }

                Series.GridAngleWeights = new CubicGrid(new int3(1, 1, AngleWeights.Count), AngleWeights.ToArray());
                Series.GridDoseBfacs = new CubicGrid(new int3(1, 1, DoseBfacs.Count), DoseBfacs.ToArray());

                Series.SaveMeta();
            }
        }
 /// <summary>
 ///   Creates a new instance of the Augmented Lagrangian algorithm.
 /// </summary>
 /// 
 /// <param name="numberOfVariables">The number of free parameters in the optimization problem.</param>
 /// <param name="constraints">The <see cref="NonlinearConstraint"/>s to which the solution must be subjected.</param>
 /// 
 public AugmentedLagrangianSolver(int numberOfVariables,
     IEnumerable<NonlinearConstraint> constraints)
 {
     var innerSolver = new BroydenFletcherGoldfarbShanno(numberOfVariables);
     init(numberOfVariables, constraints, innerSolver);
 }
        /// <summary>
        ///   Creates a new instance of the Augmented Lagrangian algorithm.
        /// </summary>
        /// 
        /// <param name="numberOfVariables">The number of free parameters in the optimization problem.</param>
        /// <param name="constraints">The <see cref="NonlinearConstraint"/>s to which the solution must be subjected.</param>
        /// 
        public AugmentedLagrangianSolver(int numberOfVariables, IEnumerable<NonlinearConstraint> constraints)
        {
            this.numberOfVariables = numberOfVariables;

            List<NonlinearConstraint> equality = new List<NonlinearConstraint>();
            List<NonlinearConstraint> lesserThan = new List<NonlinearConstraint>();
            List<NonlinearConstraint> greaterThan = new List<NonlinearConstraint>();

            foreach (var c in constraints)
            {
                switch (c.ShouldBe)
                {
                    case ConstraintType.EqualTo:
                        equality.Add(c); break;

                    case ConstraintType.GreaterThanOrEqualTo:
                        greaterThan.Add(c); break;

                    case ConstraintType.LesserThanOrEqualTo:
                        lesserThan.Add(c); break;

                    default:
                        throw new ArgumentException("Unknown constraint type.", "constraints");
                }
            }

            this.lesserThanConstraints = lesserThan.ToArray();
            this.greaterThanConstraints = greaterThan.ToArray();
            this.equalityConstraints = equality.ToArray();

            this.Solution = new double[numberOfVariables];

            dualSolver = new BroydenFletcherGoldfarbShanno(numberOfVariables);
            dualSolver.Function = objectiveFunction;
            dualSolver.Gradient = objectiveGradient;

            //for (int i = 0; i < Solution.Length; i++)
            //    Solution[i] = Accord.Math.Tools.Random.NextDouble() * 2 - 1;
        }
        private void init(NonlinearObjectiveFunction function,
            IEnumerable<NonlinearConstraint> constraints, IGradientOptimizationMethod innerSolver)
        {
            if (function != null)
            {
                if (function.NumberOfVariables != NumberOfVariables)
                {
                    throw new ArgumentOutOfRangeException("function",
                        "Incorrect number of variables in the objective function. " +
                        "The number of variables must match the number of variables set in the solver.");
                }

                this.Function = function.Function;
                this.Gradient = function.Gradient;
            }

            if (innerSolver == null)
            {
                innerSolver = new BroydenFletcherGoldfarbShanno(NumberOfVariables)
                {
                    LineSearch = Optimization.LineSearch.BacktrackingArmijo,
                    Corrections = 10
                };
            }

            List<NonlinearConstraint> equality = new List<NonlinearConstraint>();
            List<NonlinearConstraint> lesserThan = new List<NonlinearConstraint>();
            List<NonlinearConstraint> greaterThan = new List<NonlinearConstraint>();

            foreach (var c in constraints)
            {
                switch (c.ShouldBe)
                {
                    case ConstraintType.EqualTo:
                        equality.Add(c); break;

                    case ConstraintType.GreaterThanOrEqualTo:
                        greaterThan.Add(c); break;

                    case ConstraintType.LesserThanOrEqualTo:
                        lesserThan.Add(c); break;

                    default:
                        throw new ArgumentException("Unknown constraint type.", "constraints");
                }
            }

            this.lesserThanConstraints = lesserThan.ToArray();
            this.greaterThanConstraints = greaterThan.ToArray();
            this.equalityConstraints = equality.ToArray();

            this.lambda = new double[equalityConstraints.Length];
            this.mu = new double[lesserThanConstraints.Length];
            this.nu = new double[greaterThanConstraints.Length];

            this.dualSolver = innerSolver;
            dualSolver.Function = objectiveFunction;
            dualSolver.Gradient = objectiveGradient;
        }
Example #25
0
        public void ProcessParticleShift(MapHeader originalHeader, Image originalStack, Star stardata, Image refft, Image maskft, int dimbox, decimal scaleFactor)
        {
            // Deal with dimensions and grids.

            int NFrames = originalHeader.Dimensions.Z;
            int2 DimsImage = new int2(originalHeader.Dimensions);
            int2 DimsRegion = new int2(dimbox, dimbox);

            decimal SubdivisionRatio = 4M;
            List<int3> PyramidSizes = new List<int3>();
            PyramidSizes.Add(new int3(MainWindow.Options.GridMoveX, MainWindow.Options.GridMoveX, Math.Min(NFrames, MainWindow.Options.GridMoveZ)));
            while (true)
            {
                int3 Previous = PyramidSizes.Last();
                int NewZ = Math.Min((int)Math.Round(Previous.Z / SubdivisionRatio), Previous.Z - 1);
                if (NewZ < 2)
                    break;

                PyramidSizes.Add(new int3(Previous.X * 2, Previous.Y * 2, NewZ));
            }

            PyramidShiftX.Clear();
            PyramidShiftY.Clear();

            float3[] PositionsGrid, PositionsGridPerFrame;
            float2[] PositionsExtraction, PositionsShift;
            float3[] ParticleAngles;
            List<int> RowIndices = new List<int>();
            {
                string[] ColumnNames = stardata.GetColumn("rlnMicrographName");
                for (int i = 0; i < ColumnNames.Length; i++)
                    if (ColumnNames[i].Contains(RootName))
                        RowIndices.Add(i);

                string[] ColumnOriginX = stardata.GetColumn("rlnCoordinateX");
                string[] ColumnOriginY = stardata.GetColumn("rlnCoordinateY");
                string[] ColumnShiftX = stardata.GetColumn("rlnOriginX");
                string[] ColumnShiftY = stardata.GetColumn("rlnOriginY");
                string[] ColumnAngleRot = stardata.GetColumn("rlnAngleRot");
                string[] ColumnAngleTilt = stardata.GetColumn("rlnAngleTilt");
                string[] ColumnAnglePsi = stardata.GetColumn("rlnAnglePsi");

                PositionsGrid = new float3[RowIndices.Count];
                PositionsGridPerFrame = new float3[RowIndices.Count * NFrames];
                PositionsExtraction = new float2[RowIndices.Count];
                PositionsShift = new float2[RowIndices.Count * NFrames];
                ParticleAngles = new float3[RowIndices.Count];

                {
                    int i = 0;
                    foreach (var nameIndex in RowIndices)
                    {
                        float OriginX = float.Parse(ColumnOriginX[nameIndex]);
                        float OriginY = float.Parse(ColumnOriginY[nameIndex]);
                        float ShiftX = float.Parse(ColumnShiftX[nameIndex]);
                        float ShiftY = float.Parse(ColumnShiftY[nameIndex]);

                        PositionsExtraction[i] = new float2(OriginX - ShiftX, OriginY - ShiftY);
                        PositionsGrid[i] = new float3((OriginX - ShiftX) / DimsImage.X, (OriginY - ShiftY) / DimsImage.Y, 0.5f);
                        for (int z = 0; z < NFrames; z++)
                        {
                            PositionsGridPerFrame[z * RowIndices.Count + i] = new float3(PositionsGrid[i].X,
                                                                                          PositionsGrid[i].Y,
                                                                                          (float)z / (NFrames - 1));

                            PositionsShift[z * RowIndices.Count + i] = GetShiftFromPyramid(PositionsGridPerFrame[z * RowIndices.Count + i]);
                        }
                        ParticleAngles[i] = new float3(float.Parse(ColumnAngleRot[nameIndex]) * Helper.ToRad,
                                                       float.Parse(ColumnAngleTilt[nameIndex]) * Helper.ToRad,
                                                       float.Parse(ColumnAnglePsi[nameIndex]) * Helper.ToRad);

                        i++;
                    }
                }
            }
            int NPositions = PositionsGrid.Length;
            if (NPositions == 0)
                return;

            int MinFreqInclusive = (int)(MainWindow.Options.MovementRangeMin * DimsRegion.X / 2);
            int MaxFreqExclusive = (int)(MainWindow.Options.MovementRangeMax * DimsRegion.X / 2);
            int NFreq = MaxFreqExclusive - MinFreqInclusive;

            int CentralFrame = NFrames / 2;

            int MaskExpansions = 4; // Math.Max(1, PyramidSizes[0].Z / 3);
            int[] MaskSizes = new int[MaskExpansions];

            // Allocate memory and create all prerequisites:
            int MaskLength;
            Image ShiftFactors;
            Image Phases;
            Image Projections;
            Image Shifts;
            Image InvSigma;
            {
                List<long> Positions = new List<long>();
                List<float2> Factors = new List<float2>();
                List<float2> Freq = new List<float2>();
                int Min2 = MinFreqInclusive * MinFreqInclusive;
                int Max2 = MaxFreqExclusive * MaxFreqExclusive;

                for (int y = 0; y < DimsRegion.Y; y++)
                {
                    int yy = y - DimsRegion.X / 2;
                    for (int x = 0; x < DimsRegion.X / 2 + 1; x++)
                    {
                        int xx = x - DimsRegion.X / 2;
                        int r2 = xx * xx + yy * yy;
                        if (r2 >= Min2 && r2 < Max2)
                        {
                            Positions.Add(y * (DimsRegion.X / 2 + 1) + x);
                            Factors.Add(new float2((float)xx / DimsRegion.X * 2f * (float)Math.PI,
                                                   (float)yy / DimsRegion.X * 2f * (float)Math.PI));

                            float Angle = (float)Math.Atan2(yy, xx);
                            float r = (float)Math.Sqrt(r2);
                            Freq.Add(new float2(r, Angle));
                        }
                    }
                }

                // Addresses for CTF simulation
                Image CTFCoordsCart = new Image(new int3(DimsRegion), true, true);
                {
                    float2[] CoordsData = new float2[CTFCoordsCart.ElementsSliceComplex];

                    Helper.ForEachElementFT(DimsRegion, (x, y, xx, yy, r, a) => CoordsData[y * (DimsRegion.X / 2 + 1) + x] = new float2(r / DimsRegion.X, a));
                    CTFCoordsCart.UpdateHostWithComplex(new[] { CoordsData });
                    CTFCoordsCart.RemapToFT();
                }
                float[] ValuesDefocus = GridCTF.GetInterpolatedNative(PositionsGrid);
                CTFStruct[] PositionsCTF = ValuesDefocus.Select(v =>
                {
                    CTF Altered = CTF.GetCopy();
                    Altered.PixelSizeDelta = 0;
                    Altered.Defocus = (decimal)v;
                    //Altered.Bfactor = -MainWindow.Options.MovementBfactor;
                    return Altered.ToStruct();
                }).ToArray();

                // Sort everyone with ascending distance from center.
                List<KeyValuePair<float, int>> FreqIndices = Freq.Select((v, i) => new KeyValuePair<float, int>(v.X, i)).ToList();
                FreqIndices.Sort((a, b) => a.Key.CompareTo(b.Key));
                int[] SortedIndices = FreqIndices.Select(v => v.Value).ToArray();

                Helper.Reorder(Positions, SortedIndices);
                Helper.Reorder(Factors, SortedIndices);
                Helper.Reorder(Freq, SortedIndices);

                long[] RelevantMask = Positions.ToArray();
                ShiftFactors = new Image(Helper.ToInterleaved(Factors.ToArray()));
                MaskLength = RelevantMask.Length;

                // Get mask sizes for different expansion steps.
                for (int i = 0; i < MaskExpansions; i++)
                {
                    float CurrentMaxFreq = MinFreqInclusive + (MaxFreqExclusive - MinFreqInclusive) / (float)MaskExpansions * (i + 1);
                    MaskSizes[i] = Freq.Count(v => v.X * v.X < CurrentMaxFreq * CurrentMaxFreq);
                }

                Phases = new Image(IntPtr.Zero, new int3(MaskLength, NPositions, NFrames), false, true, false);
                Projections = new Image(IntPtr.Zero, new int3(MaskLength, NPositions, NFrames), false, true, false);
                InvSigma = new Image(IntPtr.Zero, new int3(MaskLength, 1, 1));

                Image ParticleMasksFT = maskft.AsProjections(ParticleAngles, DimsRegion, MainWindow.Options.ProjectionOversample);
                Image ParticleMasks = ParticleMasksFT.AsIFFT();
                ParticleMasksFT.Dispose();
                ParticleMasks.RemapFromFT();

                Parallel.ForEach(ParticleMasks.GetHost(Intent.ReadWrite), slice =>
                {
                    for (int i = 0; i < slice.Length; i++)
                        slice[i] = (Math.Max(2f, Math.Min(25f, slice[i])) - 2) / 23f;
                });

                Image ProjectionsSparse = refft.AsProjections(ParticleAngles, DimsRegion, MainWindow.Options.ProjectionOversample);

                Image InvSigmaSparse = new Image(new int3(DimsRegion), true);
                {
                    int GroupNumber = int.Parse(stardata.GetRowValue(RowIndices[0], "rlnGroupNumber"));
                    //Star SigmaTable = new Star("D:\\rado27\\RefineWarppolish\\run1_model.star", "data_model_group_" + GroupNumber);
                    Star SigmaTable = new Star(MainWindow.Options.ModelStarPath, "data_model_group_" + GroupNumber);
                    float[] SigmaValues = SigmaTable.GetColumn("rlnSigma2Noise").Select(v => float.Parse(v)).ToArray();

                    float[] Sigma2NoiseData = InvSigmaSparse.GetHost(Intent.Write)[0];
                    Helper.ForEachElementFT(new int2(DimsRegion.X, DimsRegion.Y), (x, y, xx, yy, r, angle) =>
                    {
                        int ir = (int)r;
                        float val = 0;
                        if (ir < SigmaValues.Length)
                        {
                            if (SigmaValues[ir] != 0f)
                                val = 1f / SigmaValues[ir];
                        }
                        Sigma2NoiseData[y * (DimsRegion.X / 2 + 1) + x] = val;
                    });
                    float MaxSigma = MathHelper.Max(Sigma2NoiseData);
                    for (int i = 0; i < Sigma2NoiseData.Length; i++)
                        Sigma2NoiseData[i] /= MaxSigma;

                    InvSigmaSparse.RemapToFT();
                }
                //InvSigmaSparse.WriteMRC("d_sigma2noise.mrc");

                float PixelSize = (float)CTF.PixelSize;
                float PixelDelta = (float)CTF.PixelSizeDelta;
                float PixelAngle = (float)CTF.PixelSizeAngle * Helper.ToRad;

                GPU.CreateParticleShift(originalStack.GetDevice(Intent.Read),
                                        DimsImage,
                                        NFrames,
                                        Helper.ToInterleaved(PositionsExtraction),
                                        Helper.ToInterleaved(PositionsShift),
                                        NPositions,
                                        DimsRegion,
                                        RelevantMask,
                                        (uint)RelevantMask.Length,
                                        ParticleMasks.GetDevice(Intent.Read),
                                        ProjectionsSparse.GetDevice(Intent.Read),
                                        PositionsCTF,
                                        CTFCoordsCart.GetDevice(Intent.Read),
                                        InvSigmaSparse.GetDevice(Intent.Read),
                                        PixelSize + PixelDelta / 2,
                                        PixelSize - PixelDelta / 2,
                                        PixelAngle,
                                        Phases.GetDevice(Intent.Write),
                                        Projections.GetDevice(Intent.Write),
                                        InvSigma.GetDevice(Intent.Write));

                InvSigmaSparse.Dispose();
                ParticleMasks.Dispose();
                ProjectionsSparse.Dispose();
                CTFCoordsCart.Dispose();
                originalStack.FreeDevice();
                Shifts = new Image(new float[NPositions * NFrames * 2]);
            }

            #region Fit movement

            {

                int NPyramidPoints = 0;
                float[][][] WiggleWeights = new float[PyramidSizes.Count][][];
                for (int p = 0; p < PyramidSizes.Count; p++)
                {
                    CubicGrid WiggleGrid = new CubicGrid(PyramidSizes[p]);
                    NPyramidPoints += (int)PyramidSizes[p].Elements();

                    WiggleWeights[p] = WiggleGrid.GetWiggleWeights(PositionsGridPerFrame);
                }

                double[] StartParams = new double[NPyramidPoints * 2];

                for (int m = 3; m < MaskExpansions; m++)
                {
                    for (int currentGrid = 0; currentGrid < PyramidSizes.Count; currentGrid++)
                    {
                        Action<double[]> SetPositions = input =>
                        {
                            // Construct CubicGrids and get interpolated shift values.
                            float[] AlteredX = new float[PositionsGridPerFrame.Length];
                            float[] AlteredY = new float[PositionsGridPerFrame.Length];

                            int Offset = 0;
                            foreach (var size in PyramidSizes)
                            {
                                int Elements = (int)size.Elements();
                                CubicGrid GridX = new CubicGrid(size, input.Skip(Offset).Take(Elements).Select(v => (float)v).ToArray());
                                AlteredX = MathHelper.Plus(AlteredX, GridX.GetInterpolatedNative(PositionsGridPerFrame));

                                CubicGrid GridY = new CubicGrid(size, input.Skip(NPyramidPoints + Offset).Take(Elements).Select(v => (float)v).ToArray());
                                AlteredY = MathHelper.Plus(AlteredY, GridY.GetInterpolatedNative(PositionsGridPerFrame));

                                Offset += Elements;
                            }

                            // Finally, set the shift values in the device array.
                            float[] ShiftData = Shifts.GetHost(Intent.Write)[0];
                            for (int i = 0; i < PositionsGridPerFrame.Length; i++)
                            {
                                ShiftData[i * 2] = AlteredX[i];
                                ShiftData[i * 2 + 1] = AlteredY[i];
                            }
                        };

                        Func<double[], double> Eval = input =>
                        {
                            SetPositions(input);

                            float[] Diff = new float[NPositions * NFrames];
                            GPU.ParticleShiftGetDiff(Phases.GetDevice(Intent.Read),
                                                     Projections.GetDevice(Intent.Read),
                                                     ShiftFactors.GetDevice(Intent.Read),
                                                     InvSigma.GetDevice(Intent.Read),
                                                     (uint)MaskLength,
                                                     (uint)MaskSizes[m],
                                                     Shifts.GetDevice(Intent.Read),
                                                     Diff,
                                                     (uint)NPositions,
                                                     (uint)NFrames);

                            //for (int i = 0; i < Diff.Length; i++)
                            //Diff[i] = Diff[i] * 100f;

                            double Score = Diff.Sum();
                            //Debug.WriteLine(Score);
                            return Score;
                        };

                        Func<double[], double[]> Grad = input =>
                        {
                            SetPositions(input);

                            float[] Diff = new float[NPositions * NFrames * 2];
                            GPU.ParticleShiftGetGrad(Phases.GetDevice(Intent.Read),
                                                     Projections.GetDevice(Intent.Read),
                                                     ShiftFactors.GetDevice(Intent.Read),
                                                     InvSigma.GetDevice(Intent.Read),
                                                     (uint)MaskLength,
                                                     (uint)MaskSizes[m],
                                                     Shifts.GetDevice(Intent.Read),
                                                     Diff,
                                                     (uint)NPositions,
                                                     (uint)NFrames);

                            //for (int i = 0; i < Diff.Length; i++)
                                //Diff[i] = Diff[i] * 100f;

                            float[] DiffX = new float[NPositions * NFrames], DiffY = new float[NPositions * NFrames];
                            for (int i = 0; i < DiffX.Length; i++)
                            {
                                DiffX[i] = Diff[i * 2];
                                DiffY[i] = Diff[i * 2 + 1];
                            }

                            double[] Result = new double[input.Length];
                            int Offset = 0;
                            for (int p = 0; p < PyramidSizes.Count; p++)
                            {
                                //if (p == currentGrid)
                                    Parallel.For(0, (int)PyramidSizes[p].Elements(), i =>
                                    {
                                        Result[Offset + i] = MathHelper.ReduceWeighted(DiffX, WiggleWeights[p][i]);
                                        Result[NPyramidPoints + Offset + i] = MathHelper.ReduceWeighted(DiffY, WiggleWeights[p][i]);
                                    });

                                Offset += (int)PyramidSizes[p].Elements();
                            }
                            return Result;
                        };

                        BroydenFletcherGoldfarbShanno Optimizer = new BroydenFletcherGoldfarbShanno(StartParams.Length, Eval, Grad);
                        //Optimizer.Corrections = 20;
                        Optimizer.Minimize(StartParams);
                    }

                    {
                        PyramidShiftX.Clear();
                        PyramidShiftY.Clear();
                        int Offset = 0;
                        foreach (var size in PyramidSizes)
                        {
                            int Elements = (int)size.Elements();
                            CubicGrid GridX = new CubicGrid(size, StartParams.Skip(Offset).Take(Elements).Select(v => (float)v).ToArray());
                            PyramidShiftX.Add(GridX);

                            CubicGrid GridY = new CubicGrid(size, StartParams.Skip(NPyramidPoints + Offset).Take(Elements).Select(v => (float)v).ToArray());
                            PyramidShiftY.Add(GridY);

                            Offset += Elements;
                        }
                    }
                }
            }

            #endregion

            ShiftFactors.Dispose();
            Phases.Dispose();
            Projections.Dispose();
            Shifts.Dispose();
            InvSigma.Dispose();

            SaveMeta();
        }
        public void lbfgsTest2()
        {
            // Suppose we would like to find the minimum of the function
            // 
            //   f(x,y)  =  -exp{-(x-1)²} - exp{-(y-2)²/2}
            //

            // First we need write down the function either as a named
            // method, an anonymous method or as a lambda function:

            Func<double[], double> f = (x) =>
                -Math.Exp(-Math.Pow(x[0] - 1, 2)) - Math.Exp(-0.5 * Math.Pow(x[1] - 2, 2));

            // Now, we need to write its gradient, which is just the
            // vector of first partial derivatives del_f / del_x, as:
            //
            //   g(x,y)  =  { del f / del x, del f / del y }
            // 

            Func<double[], double[]> g = (x) => new double[] 
            {
                // df/dx = {-2 e^(-    (x-1)^2) (x-1)}
                2 * Math.Exp(-Math.Pow(x[0] - 1, 2)) * (x[0] - 1),

                // df/dy = {-  e^(-1/2 (y-2)^2) (y-2)}
                Math.Exp(-0.5 * Math.Pow(x[1] - 2, 2)) * (x[1] - 2)
            };

            // Finally, we can create the L-BFGS solver, passing the functions as arguments
            var lbfgs = new BroydenFletcherGoldfarbShanno(numberOfVariables: 2, function: f, gradient: g);

            // And then minimize the function:
            double minValue = lbfgs.Minimize();
            double[] solution = lbfgs.Solution;

            // The resultant minimum value should be -2, and the solution
            // vector should be { 1.0, 2.0 }. The answer can be checked on
            // Wolfram Alpha by clicking the following the link:

            // http://www.wolframalpha.com/input/?i=maximize+%28exp%28-%28x-1%29%C2%B2%29+%2B+exp%28-%28y-2%29%C2%B2%2F2%29%29

            double expected = -2;
            Assert.AreEqual(expected, minValue, 1e-10);

            Assert.AreEqual(1, solution[0], 1e-6);
            Assert.AreEqual(2, solution[1], 1e-6);

        }
        public void ConstructorTest3()
        {
            // minimize f(x) = x*y*z, 
            // s.t. 
            //   
            //    1 - x² - 2y² - 3z² > 0
            //    x > 0,
            //    y > 0
            //

            // Easy three dimensional minimization in ellipsoid.
            var function = new NonlinearObjectiveFunction(3,
                function: x => x[0] * x[1] * x[2], 
                gradient: x => new[] { x[1] * x[2], x[0] * x[2], x[0] * x[1] });

            NonlinearConstraint[] constraints = 
            {
                new NonlinearConstraint(3,
                    function: x =>  1.0 - x[0] * x[0] - 2.0 * x[1] * x[1] - 3.0 * x[2] * x[2],
                    gradient: x =>  new[] { -2.0 * x[0],  -4.0 * x[1], -6.0 * x[2] }),
                new NonlinearConstraint(3,
                    function: x =>  x[0],
                    gradient: x =>  new[] { 1.0, 0, 0 }),
                new NonlinearConstraint(3,
                    function: x =>  x[1],
                    gradient: x =>  new[] { 0, 1.0, 0 }),
                new NonlinearConstraint(3,
                    function: x =>  -x[2],
                    gradient: x =>  new[] { 0, 0, -1.0 }),
            };

            for (int i = 0; i < constraints.Length; i++)
			{
                Assert.AreEqual(ConstraintType.GreaterThanOrEqualTo, constraints[i].ShouldBe);
                Assert.AreEqual(0, constraints[i].Value);
			}

            var inner = new BroydenFletcherGoldfarbShanno(3);
            inner.LineSearch = LineSearch.BacktrackingArmijo;
            inner.Corrections = 10;

            var solver = new AugmentedLagrangian(inner, function, constraints);

            Assert.AreEqual(inner, solver.Optimizer);

            Assert.IsTrue(solver.Minimize());
            double minimum = solver.Value;
            double[] solution = solver.Solution;

            double[] expected = 
            {
                1.0 / Math.Sqrt(3.0), 1.0 / Math.Sqrt(6.0), -1.0 / 3.0
            };


            for (int i = 0; i < expected.Length; i++)
                Assert.AreEqual(expected[i], solver.Solution[i], 1e-3);
            Assert.AreEqual(-0.078567420132031968, minimum, 1e-4);

            double expectedMinimum = function.Function(solver.Solution);
            Assert.AreEqual(expectedMinimum, minimum);
        }
Example #28
0
        public OptimizationProgressEventArgs[] Actual(Specification problem)
        {
            BroydenFletcherGoldfarbShanno target = new BroydenFletcherGoldfarbShanno(problem.Variables)
            {
                Corrections = m,
                Epsilon = epsilon,
                Past = past,
                Delta = delta,
                MaxIterations = max_iterations,
                LineSearch = (LineSearch)linesearch,
                MaxLineSearch = max_linesearch,
                MinStep = min_step,
                MaxStep = max_step,
                ParameterTolerance = ftol,
                Wolfe = wolfe,
                GradientTolerance = gtol,
                FunctionTolerance = xtol,
                OrthantwiseC = orthantwise_c,
                OrthantwiseStart = orthantwise_start,
                OrthantwiseEnd = orthantwise_end
            };

            target.Function = problem.Function;
            target.Gradient = problem.Gradient;

            actual.Clear();
            target.Progress += new EventHandler<OptimizationProgressEventArgs>(target_Progress);

            target.Minimize((double[])problem.Start.Clone());

            ActualMessage = target.Status.GetDescription();


            return actual.ToArray();
        }
        public void NoFunctionTest()
        {
            BroydenFletcherGoldfarbShanno target = new BroydenFletcherGoldfarbShanno(2);

            target.Minimize();
        }
Example #30
0
        private void OptimizePerTomoWeights()
        {
            if (!Options.Movies.Any(m => m.GetType() == typeof (TiltSeries)))
                return;

            Image Mask1 = StageDataLoad.LoadMap("F:\\chloroplastribo\\vlion\\mask_post.mrc", new int2(1, 1), 0, typeof (float));
            //Image Mask2 = StageDataLoad.LoadMap("F:\\badaben\\vlion\\mask_C3_post.mrc", new int2(1, 1), 0, typeof(float));
            //Image Mask3 = StageDataLoad.LoadMap("F:\\badaben\\vlion\\mask_C4_post.mrc", new int2(1, 1), 0, typeof(float));
            List<Image> SubsetMasks = new List<Image> { Mask1, Mask1 };

            int3 Dims = Mask1.Dims;
            List<WeightOptContainer> Reconstructions = new List<WeightOptContainer>();
            Dictionary<TiltSeries, int> SeriesIndices = new Dictionary<TiltSeries, int>();

            foreach (Movie movie in Options.Movies)
            {
                if (!movie.DoProcess)
                    continue;

                TiltSeries Series = (TiltSeries)movie;
                string[] FileNames = Directory.EnumerateFiles(Series.WeightOptimizationDir, Series.RootName + "_subset*.mrc").Where(v => v.Contains("subset1") || v.Contains("subset2")).Select(v => new FileInfo(v).Name).ToArray();
                if (FileNames.Length == 0)
                    continue;

                string[] MapNames = FileNames.Where(v => !v.Contains(".weight.mrc")).ToArray();
                string[] WeightNames = FileNames.Where(v => v.Contains(".weight.mrc")).ToArray();
                if (MapNames.Length != WeightNames.Length)
                    throw new Exception("Number of reconstructions and weights does not match!");

                string[] MapSuffixes = MapNames.Select(v => v.Substring(Series.RootName.Length)).ToArray();
                int[] MapSubsets = MapSuffixes.Select(v =>
                {
                    string S = v.Substring(v.IndexOf("subset") + "subset".Length);
                    return int.Parse(S.Substring(0, S.IndexOf(".mrc"))) - 1;
                }).ToArray();

                SeriesIndices.Add(Series, SeriesIndices.Count);

                for (int i = 0; i < MapNames.Length; i++)
                {
                    Image Map = StageDataLoad.LoadMap(Series.WeightOptimizationDir + MapNames[i], new int2(1, 1), 0, typeof (float));
                    Image MapFT = Map.AsFFT(true);
                    float[] MapData = MapFT.GetHostContinuousCopy();
                    Map.Dispose();
                    MapFT.Dispose();

                    Image Weights = StageDataLoad.LoadMap(Series.WeightOptimizationDir + WeightNames[i], new int2(1, 1), 0, typeof (float));
                    float[] WeightsData = Weights.GetHostContinuousCopy();
                    Weights.Dispose();

                    Reconstructions.Add(new WeightOptContainer(SeriesIndices[Series], MapSubsets[i], MapData, WeightsData, 0, 0));
                }

                //break;
            }

            float[][] PackedRecFT = new float[SeriesIndices.Count][];
            float[][] PackedRecWeights = new float[SeriesIndices.Count][];
            foreach (var s in SeriesIndices)
            {
                WeightOptContainer[] SeriesRecs = Reconstructions.Where(r => r.SeriesID == s.Value).ToArray();
                PackedRecFT[s.Value] = new float[SeriesRecs.Length * SeriesRecs[0].DataFT.Length];
                PackedRecWeights[s.Value] = new float[SeriesRecs.Length * SeriesRecs[0].DataWeights.Length];

                for (int n = 0; n < SeriesRecs.Length; n++)
                {
                    Array.Copy(SeriesRecs[n].DataFT, 0, PackedRecFT[s.Value], n * SeriesRecs[0].DataFT.Length, SeriesRecs[0].DataFT.Length);
                    Array.Copy(SeriesRecs[n].DataWeights, 0, PackedRecWeights[s.Value], n * SeriesRecs[0].DataWeights.Length, SeriesRecs[0].DataWeights.Length);
                }
            }

            float PixelSize = (float)Options.Movies[0].CTF.PixelSize;
            float FreqMin = 1f / (18.0f / PixelSize), FreqMin2 = FreqMin * FreqMin;
            float FreqMax = 1f / (14.5f / PixelSize), FreqMax2 = FreqMax * FreqMax;

            int ShellMin = (int)(Dims.X * FreqMin);
            int ShellMax = (int)(Dims.X * FreqMax);
            int NShells = ShellMax - ShellMin;

            float[] R2 = new float[(Dims.X / 2 + 1) * Dims.Y * Dims.Z];
            int[] ShellIndices = new int[R2.Length];

            for (int z = 0; z < Dims.Z; z++)
            {
                int zz = z < Dims.Z / 2 + 1 ? z : z - Dims.Z;
                zz *= zz;
                for (int y = 0; y < Dims.Y; y++)
                {
                    int yy = y < Dims.Y / 2 + 1 ? y : y - Dims.Y;
                    yy *= yy;
                    for (int x = 0; x < Dims.X / 2 + 1; x++)
                    {
                        int xx = x;
                        xx *= x;

                        float r = (float)Math.Sqrt(zz + yy + xx) / Dims.X / PixelSize;
                        R2[(z * Dims.Y + y) * (Dims.X / 2 + 1) + x] = r * r;
                        int ir = (int)Math.Round(Math.Sqrt(zz + yy + xx));
                        ShellIndices[(z * Dims.Y + y) * (Dims.X / 2 + 1) + x] = ir < Dims.X / 2 ? ir : -1;
                    }
                }
            }

            float[] SeriesWeights = new float[SeriesIndices.Count];
            float[] SeriesBfacs = new float[SeriesIndices.Count];

            Func<double[], float[]> WeightedFSC = input =>
            {
                // Set parameters from input vector
                //{
                    int Skip = 0;
                    SeriesWeights = input.Take(SeriesWeights.Length).Select(v => (float)v / 100f).ToArray();
                    Skip += SeriesWeights.Length;
                    SeriesBfacs = input.Skip(Skip).Take(SeriesBfacs.Length).Select(v => (float)v * 10f).ToArray();
                //}

                // Initialize sum vectors
                float[] FSC = new float[Dims.X / 2];

                float[] MapSum1 = new float[Dims.ElementsFFT() * 2], MapSum2 = new float[Dims.ElementsFFT() * 2];
                float[] WeightSum1 = new float[Dims.ElementsFFT()], WeightSum2 = new float[Dims.ElementsFFT()];

                int ElementsFT = (int)Dims.ElementsFFT();

                foreach (var s in SeriesIndices)
                {
                    WeightOptContainer[] SeriesRecs = Reconstructions.Where(r => r.SeriesID == s.Value).ToArray();

                    float[] PrecalcWeights = new float[SeriesRecs.Length];
                    float[] PrecalcBfacs = new float[SeriesRecs.Length];
                    int[] PrecalcSubsets = new int[SeriesRecs.Length];

                    for (int n = 0; n < SeriesRecs.Length; n++)
                    {
                        WeightOptContainer reconstruction = SeriesRecs[n];
                        // Weight is Weight(Series) * exp(Bfac(Series) / 4 * r^2)

                        float SeriesWeight = (float)Math.Exp(SeriesWeights[reconstruction.SeriesID]);
                        float SeriesBfac = SeriesBfacs[reconstruction.SeriesID];

                        PrecalcWeights[n] = SeriesWeight;
                        PrecalcBfacs[n] = SeriesBfac * 0.25f;
                        PrecalcSubsets[n] = reconstruction.Subset;
                    }

                    CPU.OptimizeWeights(SeriesRecs.Length,
                                        PackedRecFT[s.Value],
                                        PackedRecWeights[s.Value],
                                        R2,
                                        ElementsFT,
                                        PrecalcSubsets,
                                        PrecalcBfacs,
                                        PrecalcWeights,
                                        MapSum1,
                                        MapSum2,
                                        WeightSum1,
                                        WeightSum2);
                }

                for (int i = 0; i < ElementsFT; i++)
                {
                    float Weight = Math.Max(1e-3f, WeightSum1[i]);
                    MapSum1[i * 2] /= Weight;
                    MapSum1[i * 2 + 1] /= Weight;

                    Weight = Math.Max(1e-3f, WeightSum2[i]);
                    MapSum2[i * 2] /= Weight;
                    MapSum2[i * 2 + 1] /= Weight;
                }

                Image Map1FT = new Image(MapSum1, Dims, true, true);
                Image Map1 = Map1FT.AsIFFT(true);
                Map1.Multiply(SubsetMasks[0]);
                Image MaskedFT1 = Map1.AsFFT(true);
                float[] MaskedFT1Data = MaskedFT1.GetHostContinuousCopy();

                Map1FT.Dispose();
                Map1.Dispose();
                MaskedFT1.Dispose();

                Image Map2FT = new Image(MapSum2, Dims, true, true);
                Image Map2 = Map2FT.AsIFFT(true);
                Map2.Multiply(SubsetMasks[1]);
                Image MaskedFT2 = Map2.AsFFT(true);
                float[] MaskedFT2Data = MaskedFT2.GetHostContinuousCopy();

                Map2FT.Dispose();
                Map2.Dispose();
                MaskedFT2.Dispose();

                float[] Nums = new float[Dims.X / 2];
                float[] Denoms1 = new float[Dims.X / 2];
                float[] Denoms2 = new float[Dims.X / 2];
                for (int i = 0; i < ElementsFT; i++)
                {
                    int Shell = ShellIndices[i];
                    if (Shell < 0)
                        continue;

                    Nums[Shell] += MaskedFT1Data[i * 2] * MaskedFT2Data[i * 2] + MaskedFT1Data[i * 2 + 1] * MaskedFT2Data[i * 2 + 1];
                    Denoms1[Shell] += MaskedFT1Data[i * 2] * MaskedFT1Data[i * 2] + MaskedFT1Data[i * 2 + 1] * MaskedFT1Data[i * 2 + 1];
                    Denoms2[Shell] += MaskedFT2Data[i * 2] * MaskedFT2Data[i * 2] + MaskedFT2Data[i * 2 + 1] * MaskedFT2Data[i * 2 + 1];
                }

                for (int i = 0; i < Dims.X / 2; i++)
                    FSC[i] = Nums[i] / (float)Math.Sqrt(Denoms1[i] * Denoms2[i]);

                return FSC;
            };

            Func<double[], double> EvalForGrad = input =>
            {
                return WeightedFSC(input).Skip(ShellMin).Take(NShells).Sum() * Reconstructions.Count;
            };

            Func<double[], double> Eval = input =>
            {
                double Score = EvalForGrad(input);
                Debug.WriteLine(Score);

                return Score;
            };

            int Iterations = 0;

            Func<double[], double[]> Grad = input =>
            {
                double[] Result = new double[input.Length];
                double Step = 4;

                if (Iterations++ > 15)
                    return Result;

                //Parallel.For(0, input.Length, new ParallelOptions { MaxDegreeOfParallelism = 4 }, i =>
                for (int i = 0; i < input.Length; i++)
                {
                    double[] InputCopy = input.ToList().ToArray();
                    double Original = InputCopy[i];
                    InputCopy[i] = Original + Step;
                    double ResultPlus = EvalForGrad(InputCopy);
                    InputCopy[i] = Original - Step;
                    double ResultMinus = EvalForGrad(InputCopy);
                    InputCopy[i] = Original;

                    Result[i] = (ResultPlus - ResultMinus) / (Step * 2);
                }//);

                return Result;
            };

            List<double> StartParamsList = new List<double>();
            StartParamsList.AddRange(SeriesWeights.Select(v => (double)v));
            StartParamsList.AddRange(SeriesBfacs.Select(v => (double)v));

            double[] StartParams = StartParamsList.ToArray();

            BroydenFletcherGoldfarbShanno Optimizer = new BroydenFletcherGoldfarbShanno(StartParams.Length, Eval, Grad);
            Optimizer.Epsilon = 3e-7;
            Optimizer.Maximize(StartParams);

            EvalForGrad(StartParams);

            foreach (var s in SeriesIndices)
            {
                s.Key.GlobalWeight = (float)Math.Exp(SeriesWeights[s.Value] - MathHelper.Max(SeriesWeights));   // Minus, because exponential
                s.Key.GlobalBfactor = SeriesBfacs[s.Value] - MathHelper.Max(SeriesBfacs);

                s.Key.SaveMeta();
            }
        }
Example #31
0
        public void ProcessParticleCTF(MapHeader originalHeader, Image originalStack, Star stardata, Image refft, Image maskft, int dimbox, decimal scaleFactor)
        {
            //CTF.Cs = MainWindow.Options.CTFCs;

            #region Dimensions and grids

            int NFrames = originalHeader.Dimensions.Z;
            int2 DimsImage = new int2(originalHeader.Dimensions);
            int2 DimsRegion = new int2(dimbox, dimbox);

            float3[] PositionsGrid;
            float3[] PositionsExtraction;
            float3[] ParticleAngles;
            List<int> RowIndices = new List<int>();
            {
                string[] ColumnNames = stardata.GetColumn("rlnMicrographName");
                for (int i = 0; i < ColumnNames.Length; i++)
                    if (ColumnNames[i].Contains(RootName))
                        RowIndices.Add(i);

                string[] ColumnOriginX = stardata.GetColumn("rlnCoordinateX");
                string[] ColumnOriginY = stardata.GetColumn("rlnCoordinateY");
                string[] ColumnShiftX = stardata.GetColumn("rlnOriginX");
                string[] ColumnShiftY = stardata.GetColumn("rlnOriginY");
                string[] ColumnAngleRot = stardata.GetColumn("rlnAngleRot");
                string[] ColumnAngleTilt = stardata.GetColumn("rlnAngleTilt");
                string[] ColumnAnglePsi = stardata.GetColumn("rlnAnglePsi");

                PositionsGrid = new float3[RowIndices.Count];
                PositionsExtraction = new float3[RowIndices.Count];
                ParticleAngles = new float3[RowIndices.Count];

                {
                    int i = 0;
                    foreach (var nameIndex in RowIndices)
                    {
                        float OriginX = float.Parse(ColumnOriginX[nameIndex]);
                        float OriginY = float.Parse(ColumnOriginY[nameIndex]);
                        float ShiftX = float.Parse(ColumnShiftX[nameIndex]);
                        float ShiftY = float.Parse(ColumnShiftY[nameIndex]);

                        PositionsExtraction[i] = new float3(OriginX - ShiftX - dimbox / 2, OriginY - ShiftY - dimbox / 2, 0f);
                        PositionsGrid[i] = new float3((OriginX - ShiftX) / DimsImage.X, (OriginY - ShiftY) / DimsImage.Y, 0);
                        ParticleAngles[i] = new float3(float.Parse(ColumnAngleRot[nameIndex]) * Helper.ToRad,
                                                       float.Parse(ColumnAngleTilt[nameIndex]) * Helper.ToRad,
                                                       float.Parse(ColumnAnglePsi[nameIndex]) * Helper.ToRad);
                        i++;
                    }
                }
            }
            int NPositions = PositionsGrid.Length;
            if (NPositions == 0)
                return;

            int CTFGridX = MainWindow.Options.GridCTFX;
            int CTFGridY = MainWindow.Options.GridCTFY;
            int CTFGridZ = Math.Min(NFrames, MainWindow.Options.GridCTFZ);

            int FrameGroupSize = CTFGridZ > 1 ? 12 : 1;
            int NFrameGroups = CTFGridZ > 1 ? NFrames / FrameGroupSize : 1;

            GridCTF = GridCTF.Resize(new int3(CTFGridX, CTFGridY, CTFGridZ));
            GridCTFPhase = GridCTFPhase.Resize(new int3(1, 1, CTFGridZ));

            int NSpectra = NFrameGroups * NPositions;

            int MinFreqInclusive = (int)(MainWindow.Options.CTFRangeMin * DimsRegion.X / 2);
            int MaxFreqExclusive = (int)(MainWindow.Options.CTFRangeMax * DimsRegion.X / 2);
            int NFreq = MaxFreqExclusive - MinFreqInclusive;

            float PixelSize = (float)CTF.PixelSize;
            float PixelDelta = (float)CTF.PixelSizeDelta;
            float PixelAngle = (float)CTF.PixelSizeAngle * Helper.ToRad;

            #endregion

            #region Allocate GPU memory

            Image CTFSpectra = new Image(IntPtr.Zero, new int3(DimsRegion.X, DimsRegion.X, NSpectra), true, true);
            Image CTFCoordsCart = new Image(new int3(DimsRegion), true, true);
            Image ParticleRefs = refft.AsProjections(ParticleAngles, DimsRegion, MainWindow.Options.ProjectionOversample);
            /*Image ParticleRefsIFT = ParticleRefs.AsIFFT();
            ParticleRefsIFT.WriteMRC("d_particlerefs.mrc");
            ParticleRefsIFT.Dispose();*/

            #endregion

            // Extract movie regions, create individual spectra in Cartesian coordinates.

            #region Create spectra

            Image ParticleMasksFT = maskft.AsProjections(ParticleAngles, DimsRegion, MainWindow.Options.ProjectionOversample);
            Image ParticleMasks = ParticleMasksFT.AsIFFT();
            ParticleMasksFT.Dispose();

            Parallel.ForEach(ParticleMasks.GetHost(Intent.ReadWrite), slice =>
            {
                for (int i = 0; i < slice.Length; i++)
                    slice[i] = (Math.Max(2f, Math.Min(25f, slice[i])) - 2) / 23f;
            });

            int3[] PositionsExtractionPerFrame = new int3[PositionsExtraction.Length * NFrames];
            for (int z = 0; z < NFrames; z++)
            {
                for (int p = 0; p < NPositions; p++)
                {
                    float3 Coords = new float3(PositionsGrid[p].X, PositionsGrid[p].Y, z / (float)(NFrames - 1));
                    float2 Offset = GetShiftFromPyramid(Coords);

                    PositionsExtractionPerFrame[z * NPositions + p] = new int3((int)Math.Round(PositionsExtraction[p].X - Offset.X),
                                                                               (int)Math.Round(PositionsExtraction[p].Y - Offset.Y),
                                                                               0);
                }
            }

            float3[] PositionsGridPerFrame = new float3[NSpectra];
            for (int z = 0; z < NFrameGroups; z++)
            {
                for (int p = 0; p < NPositions; p++)
                {
                    float3 Coords = new float3(PositionsGrid[p].X, PositionsGrid[p].Y, (z * FrameGroupSize + FrameGroupSize / 2) / (float)(NFrames - 1));
                    PositionsGridPerFrame[z * NPositions + p] = Coords;
                }
            }

            GPU.CreateParticleSpectra(originalStack.GetDevice(Intent.Read),
                                      DimsImage,
                                      NFrames,
                                      PositionsExtractionPerFrame,
                                      NPositions,
                                      ParticleMasks.GetDevice(Intent.Read),
                                      DimsRegion,
                                      CTFGridZ > 1,
                                      FrameGroupSize,
                                      PixelSize + PixelDelta / 2f,
                                      PixelSize - PixelDelta / 2f,
                                      PixelAngle,
                                      CTFSpectra.GetDevice(Intent.Write));
            originalStack.FreeDevice(); // Won't need it in this method anymore.
            ParticleMasks.Dispose();

            /*Image CTFSpectraIFT = CTFSpectra.AsIFFT();
            CTFSpectraIFT.RemapFromFT();
            CTFSpectraIFT.WriteMRC("d_ctfspectra.mrc");
            CTFSpectraIFT.Dispose();*/

            #endregion

            // Populate address arrays for later.

            #region Init addresses

            {
                float2[] CoordsData = new float2[CTFCoordsCart.ElementsSliceComplex];

                Helper.ForEachElementFT(DimsRegion, (x, y, xx, yy, r, a) => CoordsData[y * (DimsRegion.X / 2 + 1) + x] = new float2(r / DimsRegion.X, a));
                CTFCoordsCart.UpdateHostWithComplex(new[] { CoordsData });
                CTFCoordsCart.RemapToFT();
            }

            #endregion

            // Band-pass filter reference projections
            {
                Image BandMask = new Image(new int3(DimsRegion.X, DimsRegion.Y, 1), true);
                float[] BandMaskData = BandMask.GetHost(Intent.Write)[0];

                float[] CTFCoordsData = CTFCoordsCart.GetHost(Intent.Read)[0];
                for (int i = 0; i < BandMaskData.Length; i++)
                    BandMaskData[i] = (CTFCoordsData[i * 2] >= MinFreqInclusive / (float)DimsRegion.X && CTFCoordsData[i * 2] < MaxFreqExclusive / (float)DimsRegion.X) ? 1 : 0;

                ParticleRefs.MultiplySlices(BandMask);
                BandMask.Dispose();
            }

            Image Sigma2Noise = new Image(new int3(DimsRegion), true);
            {
                int GroupNumber = int.Parse(stardata.GetRowValue(RowIndices[0], "rlnGroupNumber"));
                Star SigmaTable = new Star(MainWindow.Options.ModelStarPath, "data_model_group_" + GroupNumber);
                float[] SigmaValues = SigmaTable.GetColumn("rlnSigma2Noise").Select(v => float.Parse(v)).ToArray();

                float[] Sigma2NoiseData = Sigma2Noise.GetHost(Intent.Write)[0];
                Helper.ForEachElementFT(new int2(DimsRegion.X, DimsRegion.Y), (x, y, xx, yy, r, angle) =>
                {
                    int ir = (int)r;
                    float val = 0;
                    if (ir < SigmaValues.Length)
                    {
                        if (SigmaValues[ir] != 0f)
                            val = 1f / SigmaValues[ir];
                    }
                    Sigma2NoiseData[y * (DimsRegion.X / 2 + 1) + x] = val;
                });
                float MaxSigma = MathHelper.Max(Sigma2NoiseData);
                for (int i = 0; i < Sigma2NoiseData.Length; i++)
                    Sigma2NoiseData[i] /= MaxSigma;

                Sigma2Noise.RemapToFT();
            }
            Sigma2Noise.WriteMRC("d_sigma2noise.mrc");

            // Do BFGS optimization of defocus, astigmatism and phase shift,
            // using 2D simulation for comparison

            #region BFGS
            {
                // Wiggle weights show how the defocus on the spectra grid is altered 
                // by changes in individual anchor points of the spline grid.
                // They are used later to compute the dScore/dDefocus values for each spectrum 
                // only once, and derive the values for each anchor point from them.
                float[][] WiggleWeights = GridCTF.GetWiggleWeights(PositionsGridPerFrame);
                float[][] WiggleWeightsPhase = GridCTFPhase.GetWiggleWeights(PositionsGridPerFrame);

                // Helper method for getting CTFStructs for the entire spectra grid.
                Func<double[], CTF, float[], float[], CTFStruct[]> EvalGetCTF = (input, ctf, defocusValues, phaseValues) =>
                {
                    decimal AlteredDelta = (decimal)input[input.Length - 2];
                    decimal AlteredAngle = (decimal)(input[input.Length - 1] * 20 / (Math.PI / 180));

                    CTF Local = ctf.GetCopy();
                    Local.DefocusDelta = AlteredDelta;
                    Local.DefocusAngle = AlteredAngle;
                    Local.PixelSizeDelta = 0;

                    CTFStruct LocalStruct = Local.ToStruct();
                    CTFStruct[] LocalParams = new CTFStruct[defocusValues.Length];
                    for (int i = 0; i < LocalParams.Length; i++)
                    {
                        LocalParams[i] = LocalStruct;
                        LocalParams[i].Defocus = defocusValues[i] * -1e-6f;
                        LocalParams[i].PhaseShift = phaseValues[i] * (float)Math.PI;
                    }

                    return LocalParams;
                };

                // Simulate with adjusted CTF, compare to originals

                #region Eval and Gradient methods

                Func<double[], double> Eval = input =>
                {
                    CubicGrid Altered = new CubicGrid(GridCTF.Dimensions, input.Take((int)GridCTF.Dimensions.Elements()).Select(v => (float)v).ToArray());
                    float[] DefocusValues = Altered.GetInterpolatedNative(PositionsGridPerFrame);
                    CubicGrid AlteredPhase = new CubicGrid(GridCTFPhase.Dimensions, input.Skip((int)GridCTF.Dimensions.Elements()).Take((int)GridCTFPhase.Dimensions.Elements()).Select(v => (float)v).ToArray());
                    float[] PhaseValues = AlteredPhase.GetInterpolatedNative(PositionsGridPerFrame);

                    CTFStruct[] LocalParams = EvalGetCTF(input, CTF, DefocusValues, PhaseValues);

                    float[] Result = new float[LocalParams.Length];

                    GPU.ParticleCTFCompareToSim(CTFSpectra.GetDevice(Intent.Read),
                                                CTFCoordsCart.GetDevice(Intent.Read),
                                                ParticleRefs.GetDevice(Intent.Read),
                                                Sigma2Noise.GetDevice(Intent.Read),
                                                (uint)CTFSpectra.ElementsSliceComplex,
                                                LocalParams,
                                                Result,
                                                (uint)NFrameGroups,
                                                (uint)NPositions);

                    float Score = 0;
                    for (int i = 0; i < Result.Length; i++)
                        Score += Result[i];

                    Score /= NSpectra;

                    if (float.IsNaN(Score) || float.IsInfinity(Score))
                        throw new Exception("Bad score.");

                    return Score * 1.0;
                };

                Func<double[], double[]> Gradient = input =>
                {
                    const float Step = 0.001f;
                    double[] Result = new double[input.Length];

                    // In 0D grid case, just get gradient for all 4 parameters.
                    // In 1+D grid case, do simple gradient for astigmatism and phase...
                    int StartComponent = input.Length - 2;
                    //int StartComponent = 0;
                    /*for (int i = StartComponent; i < input.Length; i++)
                    {
                        double[] UpperInput = new double[input.Length];
                        input.CopyTo(UpperInput, 0);
                        UpperInput[i] += Step;
                        double UpperValue = Eval(UpperInput);

                        double[] LowerInput = new double[input.Length];
                        input.CopyTo(LowerInput, 0);
                        LowerInput[i] -= Step;
                        double LowerValue = Eval(LowerInput);

                        Result[i] = (UpperValue - LowerValue) / (2f * Step);
                    }*/

                    float[] ResultPlus = new float[NSpectra];
                    float[] ResultMinus = new float[NSpectra];

                    // ..., take shortcut for defoci...
                    {
                        CubicGrid AlteredPhase = new CubicGrid(GridCTFPhase.Dimensions, input.Skip((int)GridCTF.Dimensions.Elements()).Take((int)GridCTFPhase.Dimensions.Elements()).Select(v => (float)v).ToArray());
                        float[] PhaseValues = AlteredPhase.GetInterpolatedNative(PositionsGridPerFrame);

                        {
                            CubicGrid AlteredPlus = new CubicGrid(GridCTF.Dimensions, input.Take((int)GridCTF.Dimensions.Elements()).Select(v => (float)v + Step).ToArray());
                            float[] DefocusValues = AlteredPlus.GetInterpolatedNative(PositionsGridPerFrame);

                            CTFStruct[] LocalParams = EvalGetCTF(input, CTF, DefocusValues, PhaseValues);

                            GPU.ParticleCTFCompareToSim(CTFSpectra.GetDevice(Intent.Read),
                                                        CTFCoordsCart.GetDevice(Intent.Read),
                                                        ParticleRefs.GetDevice(Intent.Read),
                                                        Sigma2Noise.GetDevice(Intent.Read),
                                                        (uint)CTFSpectra.ElementsSliceComplex,
                                                        LocalParams,
                                                        ResultPlus,
                                                        (uint)NFrameGroups,
                                                        (uint)NPositions);
                        }
                        {
                            CubicGrid AlteredMinus = new CubicGrid(GridCTF.Dimensions, input.Take((int)GridCTF.Dimensions.Elements()).Select(v => (float)v - Step).ToArray());
                            float[] DefocusValues = AlteredMinus.GetInterpolatedNative(PositionsGridPerFrame);

                            CTFStruct[] LocalParams = EvalGetCTF(input, CTF, DefocusValues, PhaseValues);

                            GPU.ParticleCTFCompareToSim(CTFSpectra.GetDevice(Intent.Read),
                                                        CTFCoordsCart.GetDevice(Intent.Read),
                                                        ParticleRefs.GetDevice(Intent.Read),
                                                        Sigma2Noise.GetDevice(Intent.Read),
                                                        (uint)CTFSpectra.ElementsSliceComplex,
                                                        LocalParams,
                                                        ResultMinus,
                                                        (uint)NFrameGroups,
                                                        (uint)NPositions);
                        }
                        float[] LocalGradients = new float[ResultPlus.Length];
                        for (int i = 0; i < LocalGradients.Length; i++)
                            LocalGradients[i] = ResultPlus[i] - ResultMinus[i];

                        // Now compute gradients per grid anchor point using the precomputed individual gradients and wiggle factors.
                        Parallel.For(0, GridCTF.Dimensions.Elements(), i => Result[i] = MathHelper.ReduceWeighted(LocalGradients, WiggleWeights[i]) / LocalGradients.Length / (2f * Step) * 1f);
                    }

                    // ..., and take shortcut for phases.
                    if (MainWindow.Options.CTFDoPhase)
                    {
                        CubicGrid AlteredPlus = new CubicGrid(GridCTF.Dimensions, input.Take((int)GridCTF.Dimensions.Elements()).Select(v => (float)v).ToArray());
                        float[] DefocusValues = AlteredPlus.GetInterpolatedNative(PositionsGridPerFrame);

                        {
                            CubicGrid AlteredPhasePlus = new CubicGrid(GridCTFPhase.Dimensions, input.Skip((int)GridCTF.Dimensions.Elements()).Take((int)GridCTFPhase.Dimensions.Elements()).Select(v => (float)v + Step).ToArray());
                            float[] PhaseValues = AlteredPhasePlus.GetInterpolatedNative(PositionsGridPerFrame);
                            CTFStruct[] LocalParams = EvalGetCTF(input, CTF, DefocusValues, PhaseValues);

                            GPU.ParticleCTFCompareToSim(CTFSpectra.GetDevice(Intent.Read),
                                                        CTFCoordsCart.GetDevice(Intent.Read),
                                                        ParticleRefs.GetDevice(Intent.Read),
                                                        Sigma2Noise.GetDevice(Intent.Read),
                                                        (uint)CTFSpectra.ElementsSliceComplex,
                                                        LocalParams,
                                                        ResultPlus,
                                                        (uint)NFrameGroups,
                                                        (uint)NPositions);
                        }
                        {
                            CubicGrid AlteredPhaseMinus = new CubicGrid(GridCTFPhase.Dimensions, input.Skip((int)GridCTF.Dimensions.Elements()).Take((int)GridCTFPhase.Dimensions.Elements()).Select(v => (float)v - Step).ToArray());
                            float[] PhaseValues = AlteredPhaseMinus.GetInterpolatedNative(PositionsGridPerFrame);
                            CTFStruct[] LocalParams = EvalGetCTF(input, CTF, DefocusValues, PhaseValues);

                            GPU.ParticleCTFCompareToSim(CTFSpectra.GetDevice(Intent.Read),
                                                        CTFCoordsCart.GetDevice(Intent.Read),
                                                        ParticleRefs.GetDevice(Intent.Read),
                                                        Sigma2Noise.GetDevice(Intent.Read),
                                                        (uint)CTFSpectra.ElementsSliceComplex,
                                                        LocalParams,
                                                        ResultMinus,
                                                        (uint)NFrameGroups,
                                                        (uint)NPositions);
                        }
                        float[] LocalGradients = new float[ResultPlus.Length];
                        for (int i = 0; i < LocalGradients.Length; i++)
                            LocalGradients[i] = ResultPlus[i] - ResultMinus[i];

                        // Now compute gradients per grid anchor point using the precomputed individual gradients and wiggle factors.
                        Parallel.For(0, GridCTFPhase.Dimensions.Elements(), i => Result[i + GridCTF.Dimensions.Elements()] = MathHelper.ReduceWeighted(LocalGradients, WiggleWeightsPhase[i]) / LocalGradients.Length / (2f * Step) * 1f);
                    }

                    foreach (var i in Result)
                        if (double.IsNaN(i) || double.IsInfinity(i))
                            throw new Exception("Bad score.");

                    return Result;
                };

                #endregion

                #region Maximize normalized cross-correlation

                double[] StartParams = new double[GridCTF.Dimensions.Elements() + GridCTFPhase.Dimensions.Elements() + 2];
                for (int i = 0; i < GridCTF.Dimensions.Elements(); i++)
                    StartParams[i] = GridCTF.FlatValues[i];
                for (int i = 0; i < GridCTFPhase.Dimensions.Elements(); i++)
                    StartParams[i + GridCTF.Dimensions.Elements()] = GridCTFPhase.FlatValues[i];
                StartParams[StartParams.Length - 2] = (double)CTF.DefocusDelta;
                StartParams[StartParams.Length - 1] = (double)CTF.DefocusAngle / 20 * (Math.PI / 180);

                BroydenFletcherGoldfarbShanno Optimizer = new BroydenFletcherGoldfarbShanno(StartParams.Length, Eval, Gradient);
                /*{
                    Past = 1,
                    Delta = 1e-6,
                    MaxLineSearch = 15,
                    Corrections = 20
                };*/

                double[] BestStart = new double[StartParams.Length];
                for (int i = 0; i < BestStart.Length; i++)
                    BestStart[i] = StartParams[i];
                double BestValue = Eval(StartParams);
                for (int o = 0; o < 1; o++)
                {
                    /*for (int step = 0; step < 150; step++)
                    {
                        float Adjustment = (step - 75) / 75f * 0.075f;
                        double[] Adjusted = new double[StartParams.Length];
                        for (int j = 0; j < Adjusted.Length; j++)
                            if (j < GridCTF.Dimensions.Elements())
                                Adjusted[j] = StartParams[j] + Adjustment;
                            else
                                Adjusted[j] = StartParams[j];

                        double NewValue = Eval(Adjusted);
                        if (NewValue > BestValue)
                        {
                            BestValue = NewValue;
                            for (int j = 0; j < GridCTF.Dimensions.Elements(); j++)
                                BestStart[j] = StartParams[j] + Adjustment;
                        }
                    }
                    for (int i = 0; i < GridCTF.Dimensions.Elements(); i++)
                        StartParams[i] = BestStart[i];*/

                    Optimizer.Maximize(StartParams);
                }

                #endregion

                #region Retrieve parameters

                decimal NewDefocus = (decimal)MathHelper.Mean(StartParams.Take((int)GridCTF.Dimensions.Elements()).Select(v => (float)v));
                Debug.WriteLine(CTF.Defocus - NewDefocus);
                CTF.Defocus = (decimal)MathHelper.Mean(StartParams.Take((int)GridCTF.Dimensions.Elements()).Select(v => (float)v));
                CTF.DefocusDelta = (decimal)StartParams[StartParams.Length - 2];
                CTF.DefocusAngle = (decimal)(StartParams[StartParams.Length - 1] * 20 / (Math.PI / 180));
                CTF.PhaseShift = (decimal)MathHelper.Mean(StartParams.Skip((int)GridCTF.Dimensions.Elements()).Take((int)GridCTFPhase.Dimensions.Elements()).Select(v => (float)v));

                GridCTF = new CubicGrid(GridCTF.Dimensions, StartParams.Take((int)GridCTF.Dimensions.Elements()).Select(v => (float)v).ToArray());
                GridCTFPhase = new CubicGrid(GridCTFPhase.Dimensions, StartParams.Skip((int)GridCTF.Dimensions.Elements()).Take((int)GridCTFPhase.Dimensions.Elements()).Select(v => (float)v).ToArray());

                #endregion

                Sigma2Noise.Dispose();
            }

            #endregion

            ParticleRefs.Dispose();
            //ParticleAmps.Dispose();
            CTFSpectra.Dispose();
            CTFCoordsCart.Dispose();

            Simulated1D = GetSimulated1D();
            CTFQuality = GetCTFQuality();

            SaveMeta();
        }