Пример #1
0
        private static int OrderIntensityDesc(RankedMI mi1, RankedMI mi2)
        {
            float i1 = mi1.Intensity, i2 = mi2.Intensity;

            if (i1 > i2)
            {
                return(-1);
            }
            if (i1 < i2)
            {
                return(1);
            }
            return(-OrderMz(mi1, mi2));
        }
Пример #2
0
        private bool ApplyRanking(RankingState rankingState, double ionMz, MatchedFragmentIon match, bool filter, int start, int end, double startMz, ref RankedMI rankedMI)
        {
            // Avoid ranking precursor ions without losses, if the precursor isotopes will
            // not be taken from product ions
            if (!ExcludePrecursorIsotopes || match.IonType != IonType.precursor || match.Losses != null)
            {
                int offset = OrdinalToOffset(match.IonType, match.Ordinal);
                var type   = match.IonType;
                if (filter)
                {
                    if (TargetInfoObj.LookupMods == null || !TargetInfoObj.LookupMods.HasCrosslinks)
                    {
                        if (!TransitionSettings.Accept(Sequence, MoleculeMassesObj.precursorMz, type, offset, ionMz, start,
                                                       end, startMz))
                        {
                            return(false);
                        }
                    }
                }
                if (rankingState.matchAll)
                {
                    if (MinMz > ionMz || ionMz > MaxMz)
                    {
                        return(false);
                    }

                    if (!RankTypes.Contains(type))
                    {
                        return(false);
                    }

                    if (RankLimit.HasValue && rankingState.Ranked >= RankLimit)
                    {
                        return(false);
                    }

                    if (type != IonType.precursor)
                    {
                        // CONSIDER(bspratt) we may eventually want adduct-level control for small molecules, not just abs charge
                        if (!RankCharges.Contains(Math.Abs(match.Charge.AdductCharge)))
                        {
                            return(false);
                        }
                    }
                }

                rankedMI = rankedMI.ChangeRank(rankingState.RankNext());
                return(true);
            }

            return(false);
        }
Пример #3
0
        private bool MatchNext(RankingState rankingState, double ionMz, MatchedFragmentIon match, bool filter, int end, int start, double startMz, ref RankedMI rankedMI)
        {
            // Unless trying to match everything, stop looking outside the instrument range
            if (!rankingState.matchAll && !HasLosses && ionMz > MaxMz)
            {
                return(false);
            }
            // Check filter properties, if appropriate
            if ((rankingState.matchAll || ionMz >= MinMz) && Math.Abs(ionMz - rankedMI.ObservedMz) < Libraries.IonMatchTolerance)
            {
                // Make sure each m/z value is only used for the most intense peak
                // that is within the tolerance range.
                if (rankingState.IsSeen(ionMz))
                {
                    return(true); // Keep looking
                }
                rankingState.Seen(ionMz);

                // If this m/z already matched a different ion, just remember the second ion.
                if (rankedMI.MatchedIons != null)
                {
                    // If first type was excluded from causing a ranking, but second does, then make it the first
                    // Otherwise, this can cause very mysterious failures to rank transitions that appear in the
                    // document.
                    if (rankedMI.Rank == 0 && ApplyRanking(rankingState, ionMz, match, filter, start, end, startMz, ref rankedMI))
                    {
                        rankedMI = rankedMI.ChangeMatchedIons(rankedMI.MatchedIons.Prepend(match));
                    }
                    else
                    {
                        rankedMI = rankedMI.ChangeMatchedIons(rankedMI.MatchedIons.Append(match));
                    }
                    if (rankedMI.MatchedIons.Count < MAX_MATCH)
                    {
                        return(true);
                    }

                    rankingState.matched = true;
                    return(false);
                }

                double predictedMz = match.PredictedMz;
                // Avoid using the same predicted m/z on two different peaks
                if (predictedMz == ionMz || !rankingState.IsSeen(predictedMz))
                {
                    rankingState.Seen(predictedMz);

                    ApplyRanking(rankingState, ionMz, match, filter, start, end, startMz, ref rankedMI);
                    rankedMI             = rankedMI.ChangeMatchedIons(ImmutableList.Singleton(match));
                    rankingState.matched = !rankingState.matchAll;
                    return(rankingState.matchAll);
                }
            }
            // Stop looking once the mass has been passed, unless there are losses to consider
            if (HasLosses)
            {
                return(true);
            }
            return(ionMz <= rankedMI.ObservedMz);
        }
Пример #4
0
        private RankedMI CalculateRank(RankingState rankingState, RankedMI rankedMI)
        {
            // Rank based on filtered range, if the settings use it in picking
            bool filter = (Pick == TransitionLibraryPick.filter);

            var knownFragments = MoleculeMassesObj.MatchIonMasses.KnownFragments;

            if (knownFragments != null)
            {
                // Small molecule work - we only know about the fragments we're given, we can't predict others
                foreach (IonType type in Types)
                {
                    if (Transition.IsPrecursor(type))
                    {
                        var matchedFragmentIon = MakeMatchedFragmentIon(type, 0, PrecursorAdduct, null, out double matchMz);

                        if (!MatchNext(rankingState, matchMz, matchedFragmentIon, filter, 0, 0, 0, ref rankedMI))
                        {
                            // If matched return.  Otherwise look for other ion types.
                            if (rankingState.matched)
                            {
                                rankingState.Clean();
                                return(rankedMI);
                            }
                        }
                    }
                    else
                    {
                        for (var i = 0; i < knownFragments.Count; i++)
                        {
                            var    fragment = knownFragments[i];
                            double matchMz  = MoleculeMassesObj.PredictIonMasses.KnownFragments[i].PredictedMz;
                            if (!MatchNext(rankingState, matchMz, fragment, filter, 0, 0, fragment.PredictedMz, ref rankedMI))
                            {
                                // If matched return.  Otherwise look for other ion types.
                                if (rankingState.matched)
                                {
                                    rankingState.Clean();
                                    return(rankedMI);
                                }
                            }
                        }
                    }
                }
                return(rankedMI);
            }

            // Look for a predicted match within the acceptable tolerance
            int len = MoleculeMassesObj.MatchIonMasses.FragmentMasses.GetLength(1);

            foreach (IonType type in Types)
            {
                if (Transition.IsPrecursor(type))
                {
                    foreach (var losses in TransitionGroup.CalcTransitionLosses(type, 0, MassType, PotentialLosses))
                    {
                        var matchedFragmentIon =
                            MakeMatchedFragmentIon(type, 0, PrecursorAdduct, losses, out double matchMz);
                        if (!MatchNext(rankingState, matchMz, matchedFragmentIon, filter, len, len, 0, ref rankedMI))
                        {
                            // If matched return.  Otherwise look for other ion types.
                            if (rankingState.matched)
                            {
                                rankingState.Clean();
                                return(rankedMI);
                            }
                        }
                    }
                    continue;
                }

                foreach (var adduct in Adducts)
                {
                    // Precursor charge can never be lower than product ion charge.
                    if (Math.Abs(PrecursorAdduct.AdductCharge) < Math.Abs(adduct.AdductCharge))
                    {
                        continue;
                    }

                    int    start = 0, end = 0;
                    double startMz = 0;
                    if (filter)
                    {
                        start = TransitionSettings.Filter.FragmentRangeFirst.FindStartFragment(
                            MoleculeMassesObj.MatchIonMasses.FragmentMasses, type, adduct,
                            MoleculeMassesObj.precursorMz, TransitionSettings.Filter.PrecursorMzWindow, out startMz);
                        end = TransitionSettings.Filter.FragmentRangeLast.FindEndFragment(type, start, len);
                        if (Transition.IsCTerminal(type))
                        {
                            Helpers.Swap(ref start, ref end);
                        }
                    }

                    // These inner loops are performance bottlenecks, and the following
                    // code duplication proved the fastest implementation under a
                    // profiler.  Apparently .NET failed to inline an attempt to put
                    // the loop contents in a function.
                    if (Transition.IsCTerminal(type))
                    {
                        for (int i = len - 1; i >= 0; i--)
                        {
                            foreach (var losses in TransitionGroup.CalcTransitionLosses(type, i, MassType, PotentialLosses))
                            {
                                var matchedFragmentIon =
                                    MakeMatchedFragmentIon(type, i, adduct, losses, out double matchMz);
                                if (!MatchNext(rankingState, matchMz, matchedFragmentIon, filter, end, start, startMz, ref rankedMI))
                                {
                                    if (rankingState.matched)
                                    {
                                        rankingState.Clean();
                                        return(rankedMI);
                                    }
                                    i = -1; // Terminate loop on i
                                    break;
                                }
                            }
                        }
                    }
                    else
                    {
                        for (int i = 0; i < len; i++)
                        {
                            foreach (var losses in TransitionGroup.CalcTransitionLosses(type, i, MassType, PotentialLosses))
                            {
                                var matchedFragmentIon =
                                    MakeMatchedFragmentIon(type, i, adduct, losses, out double matchMz);
                                if (!MatchNext(rankingState, matchMz, matchedFragmentIon, filter, end, start, startMz, ref rankedMI))
                                {
                                    if (rankingState.matched)
                                    {
                                        rankingState.Clean();
                                        return(rankedMI);
                                    }
                                    i = len; // Terminate loop on i
                                    break;
                                }
                            }
                        }
                    }
                }
            }

            return(rankedMI);
        }
Пример #5
0
 private static int OrderMz(RankedMI mi1, RankedMI mi2)
 {
     return(mi1.ObservedMz.CompareTo(mi2.ObservedMz));
 }
Пример #6
0
        public LibraryRankedSpectrumInfo RankSpectrum(SpectrumPeaksInfo info, int minPeaks, double?score)
        {
            var          ionsToReturn = FragmentFilterObj.FragmentMatchCount;
            RankingState rankingState = new RankingState()
            {
                matchAll = MatchAll,
            };
            // Get the library spectrum mass-intensity pairs
            IList <SpectrumPeaksInfo.MI> listMI = info.Peaks;

            // Because sorting and matching observed ions with predicted
            // ions appear as bottlenecks in a profiler, a minimum number
            // of peaks may be supplied to allow the use of a 2-phase linear
            // filter that can significantly reduce the number of peaks
            // needing the O(n*log(n)) sorting and the O(n*m) matching.

            int   len             = listMI.Count;
            float intensityCutoff = 0;

            if (minPeaks != -1)
            {
                // Start searching for good cut-off at mean intensity.
                double totalIntensity = info.Intensities.Sum();

                FindIntensityCutoff(listMI, 0, (float)(totalIntensity / len) * 2, minPeaks, 1, ref intensityCutoff, ref len);
            }
            // Create filtered peak array storing original index for m/z ordering
            // to avoid needing to sort to return to this order.
            RankedMI[] arrayRMI = new RankedMI[len];
            // Detect when m/z values are out of order, and use the expensive sort
            // by m/z to correct this.
            double lastMz = double.MinValue;
            bool   sortMz = false;

            for (int i = 0, j = 0, lenOrig = listMI.Count; i < lenOrig; i++)
            {
                SpectrumPeaksInfo.MI mi = listMI[i];
                if (mi.Intensity >= intensityCutoff || intensityCutoff == 0)
                {
                    arrayRMI[j] = new RankedMI(mi, j);
                    j++;
                }
                if (!ionsToReturn.HasValue)
                {
                    if (mi.Mz < lastMz)
                    {
                        sortMz = true;
                    }
                    lastMz = mi.Mz;
                }
            }

            // The one expensive sort is used to determine rank order
            // by intensity, or m/z in case of a tie.
            Array.Sort(arrayRMI, OrderIntensityDesc);


            RankedMI[] arrayResult = new RankedMI[ionsToReturn.HasValue ? ionsToReturn.Value : arrayRMI.Length];

            foreach (RankedMI rmi in arrayRMI)
            {
                var rankedRmi = CalculateRank(rankingState, rmi);

                // If not filtering for only the highest ionMatchCount ranks
                if (!ionsToReturn.HasValue)
                {
                    // Put the ranked record back where it started in the
                    // m/z ordering to avoid a second sort.
                    arrayResult[rmi.IndexMz] = rankedRmi;
                }
                // Otherwise, if this ion was ranked, add it to the result array
                else if (rankedRmi.Rank > 0)
                {
                    int countRanks = rankedRmi.Rank;
                    arrayResult[countRanks - 1] = rankedRmi;
                    // And stop when the array is full
                    if (countRanks == ionsToReturn.Value)
                    {
                        break;
                    }
                }
            }

            // Is this a theoretical library with no intensity variation? If so it can't be ranked.
            // If it has any interesting peak annotations, pass those through
            if (rankingState.Ranked == 0 && arrayRMI.All(rmi => rmi.Intensity == arrayRMI[0].Intensity))
            {
                // Only do this if we have been asked to limit the ions matched, and there are any annotations
                if (ionsToReturn.HasValue && arrayRMI.Any(rmi => rmi.HasAnnotations))
                {
                    // Pass through anything with an annotation as being of probable interest
                    arrayResult  = arrayRMI.Where(rmi => rmi.HasAnnotations).ToArray();
                    ionsToReturn = null;
                }
            }

            // If not enough ranked ions were found, fill the rest of the results array
            if (ionsToReturn.HasValue)
            {
                for (int i = rankingState.Ranked; i < ionsToReturn.Value; i++)
                {
                    arrayResult[i] = RankedMI.EMPTY;
                }
            }
            // If all ions are to be included, and some were found out of order, then
            // the expensive full sort by m/z is necessary.
            else if (sortMz)
            {
                Array.Sort(arrayResult, OrderMz);
            }

            double?spectrumScore;

            if (score == null && GroupDocNode.HasLibInfo && GroupDocNode.LibInfo is BiblioSpecSpectrumHeaderInfo libInfo)
            {
                spectrumScore = libInfo.Score;
            }
            else
            {
                spectrumScore = score;
            }
            return(new LibraryRankedSpectrumInfo(PredictLabelType, Libraries.IonMatchTolerance, arrayResult, spectrumScore));
        }