/// <summary>
        /// The find optimum hypothesis.
        /// </summary>
        /// <param name="observations">
        /// The observations.
        /// </param>
        /// <param name="driftTubeLength">
        /// The drift Tube Length.
        /// </param>
        /// <param name="massTarget">
        /// The mass Target.
        /// </param>
        /// <param name="parameters">
        /// The parameters.
        /// </param>
        /// <returns>
        /// The <see cref="AssociationHypothesis"/>.
        /// </returns>
        /// <exception cref="NotImplementedException">
        /// </exception>
        public AssociationHypothesis FindOptimumHypothesis(IEnumerable<ObservedPeak> observations, double driftTubeLength, IImsTarget massTarget, CrossSectionSearchParameters parameters, int numberOfVoltageGroups)
        {
            observations = observations.ToList();

            ObservationTransitionGraph<IonTransition> transitionGraph = new ObservationTransitionGraph<IonTransition>(observations, (a, b) => new IonTransition(a, b));

            // Visualize the graph.
            // transitionGraph.PlotGraph();

            // Find all the possible combinotorial tracks
            // IList<IsomerTrack> candidateTracks = this.FindAllReasonableTracks(transitionGraph, driftTubeLength, massTarget, parameters).ToList();

            // Find the top N tracks using K shorestest path algorithm
            IEnumerable<IEnumerable<IonTransition>> kShorestPaths = transitionGraph.PeakGraph.RankedShortestPathHoffmanPavley(t => 0 - Math.Log(t.TransitionProbability), transitionGraph.SourceVertex, transitionGraph.SinkVertex, this.maxTracks);

            IEnumerable<IsomerTrack> candidateTracks = MinCostFlowIonTracker.ToTracks(kShorestPaths, parameters, numberOfVoltageGroups, parameters.RegressionSelection);

            // filter paths
            TrackFilter filter = new TrackFilter();
            Predicate<IsomerTrack> trackPredicate = track => filter.IsTrackPossible(track, massTarget, parameters);
            List<IsomerTrack> filteredTracks = candidateTracks.ToList().FindAll(trackPredicate);

            // Select the top N tracks to proceed to next step.
            var hypotheses = this.FindAllHypothesis(filteredTracks, observations).ToArray();

            // Find the combination of tracks that produces the highest posterior probablity.
              IOrderedEnumerable<AssociationHypothesis> sortedAssociationHypotheses = hypotheses.OrderByDescending(h => h.ProbabilityOfHypothesisGivenData);

            return sortedAssociationHypotheses.FirstOrDefault();
        }
        /// <summary>
        /// The conclude status.
        /// </summary>
        /// <param name="minFitPoints">
        /// The min fit points.
        /// </param>
        /// <param name="minR2">
        /// The min r 2.
        /// </param>
        /// <returns>
        /// The <see cref="AnalysisStatus"/>.
        /// </returns>
        private AnalysisStatus ConcludeStatus(int minFitPoints, double minR2)
        {
            TrackFilter filter = new TrackFilter();
            bool lowFitPoints = filter.FilterLowFitPointNumber(this.ObservedPeaks.Count(), minFitPoints);
            bool lowR2 = filter.IsLowR2(this.mobilityInfo.RSquared, minR2);

            return lowFitPoints ? AnalysisStatus.NotSufficientPoints :
                lowR2 ? AnalysisStatus.Rejected : AnalysisStatus.Positive;
        }