1use std::fmt;
2use std::collections::BTreeMap;
3use std::sync::Arc;
4use nalgebra::DVector;
5use std::fmt::{Display, Formatter};
6use bincode::{Decode, Encode};
7use serde::{Serialize, Deserialize};
8
9extern crate rand;
10
11use rand::distributions::{Uniform, Distribution};
12use rand::Rng;
13use statrs::distribution::Normal;
14
15pub trait ToResolution {
17 fn to_resolution(&self, resolution: i32) -> Self;
18}
19
20pub trait Vectorized<T> {
22 fn vectorized(&self, resolution: i32) -> T;
23}
24
25#[derive(Clone, PartialEq, Debug, Serialize, Deserialize, Encode, Decode)]
32pub enum MsType {
33 Precursor,
34 FragmentDda,
35 FragmentDia,
36 Unknown,
37}
38
39impl MsType {
40 pub fn new(ms_type: i32) -> MsType {
47 match ms_type {
48 0 => MsType::Precursor,
49 8 => MsType::FragmentDda,
50 9 => MsType::FragmentDia,
51 _ => MsType::Unknown,
52 }
53 }
54
55 pub fn ms_type_numeric(&self) -> i32 {
57 match self {
58 MsType::Precursor => 0,
59 MsType::FragmentDda => 8,
60 MsType::FragmentDia => 9,
61 MsType::Unknown => -1,
62 }
63 }
64}
65
66impl Default for MsType {
67 fn default() -> Self {
68 MsType::Unknown
69 }
70}
71
72impl Display for MsType {
73 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
74 match self {
75 MsType::Precursor => write!(f, "Precursor"),
76 MsType::FragmentDda => write!(f, "FragmentDda"),
77 MsType::FragmentDia => write!(f, "FragmentDia"),
78 MsType::Unknown => write!(f, "Unknown"),
79 }
80 }
81}
82
83#[derive(Clone, Debug, Serialize, Deserialize)]
87pub struct MzSpectrum {
88 pub mz: Arc<Vec<f64>>,
89 pub intensity: Arc<Vec<f64>>,
90}
91
92impl Encode for MzSpectrum {
94 fn encode<E: bincode::enc::Encoder>(&self, encoder: &mut E) -> Result<(), bincode::error::EncodeError> {
95 bincode::Encode::encode(&*self.mz, encoder)?;
97 bincode::Encode::encode(&*self.intensity, encoder)?;
98 Ok(())
99 }
100}
101
102impl<Context> Decode<Context> for MzSpectrum {
103 fn decode<D: bincode::de::Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, bincode::error::DecodeError> {
104 let mz: Vec<f64> = bincode::Decode::decode(decoder)?;
105 let intensity: Vec<f64> = bincode::Decode::decode(decoder)?;
106 Ok(MzSpectrum::new(mz, intensity))
107 }
108}
109
110impl<'de, Context> bincode::BorrowDecode<'de, Context> for MzSpectrum {
111 fn borrow_decode<D: bincode::de::BorrowDecoder<'de, Context = Context>>(decoder: &mut D) -> Result<Self, bincode::error::DecodeError> {
112 let mz: Vec<f64> = bincode::BorrowDecode::borrow_decode(decoder)?;
113 let intensity: Vec<f64> = bincode::BorrowDecode::borrow_decode(decoder)?;
114 Ok(MzSpectrum::new(mz, intensity))
115 }
116}
117
118impl MzSpectrum {
119 pub fn new(mz: Vec<f64>, intensity: Vec<f64>) -> Self {
139 MzSpectrum {
140 mz: Arc::new(mz),
141 intensity: Arc::new(intensity),
142 }
143 }
144
145 pub fn filter_ranged(&self, mz_min: f64, mz_max: f64, intensity_min:f64, intensity_max: f64) -> Self {
146 let mut mz_vec: Vec<f64> = Vec::new();
147 let mut intensity_vec: Vec<f64> = Vec::new();
148
149 for (mz, intensity) in self.mz.iter().zip(self.intensity.iter()) {
150 if mz_min <= *mz && *mz <= mz_max && *intensity >= intensity_min && *intensity <= intensity_max {
151 mz_vec.push(*mz);
152 intensity_vec.push(*intensity);
153 }
154 }
155 MzSpectrum::new(mz_vec, intensity_vec)
156 }
157
158 pub fn to_windows(&self, window_length: f64, overlapping: bool, min_peaks: usize, min_intensity: f64) -> BTreeMap<i32, MzSpectrum> {
190 let mut splits: BTreeMap<i32, (Vec<f64>, Vec<f64>)> = BTreeMap::new();
192
193 for (i, &mz) in self.mz.iter().enumerate() {
194 let intensity = self.intensity[i];
195 let tmp_key = (mz / window_length).floor() as i32;
196
197 let entry = splits.entry(tmp_key).or_insert_with(|| (Vec::new(), Vec::new()));
198 entry.0.push(mz);
199 entry.1.push(intensity);
200 }
201
202 if overlapping {
203 let mut splits_offset: BTreeMap<i32, (Vec<f64>, Vec<f64>)> = BTreeMap::new();
204
205 for (i, &mmz) in self.mz.iter().enumerate() {
206 let intensity = self.intensity[i];
207 let tmp_key = -((mmz + window_length / 2.0) / window_length).floor() as i32;
208
209 let entry = splits_offset.entry(tmp_key).or_insert_with(|| (Vec::new(), Vec::new()));
210 entry.0.push(mmz);
211 entry.1.push(intensity);
212 }
213
214 for (key, (mz_vec, int_vec)) in splits_offset {
215 let entry = splits.entry(key).or_insert_with(|| (Vec::new(), Vec::new()));
216 entry.0.extend(mz_vec);
217 entry.1.extend(int_vec);
218 }
219 }
220
221 splits.into_iter()
223 .filter(|(_, (mz_vec, int_vec))| {
224 mz_vec.len() >= min_peaks && int_vec.iter().cloned().max_by(
225 |a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)).unwrap_or(0.0) >= min_intensity
226 })
227 .map(|(key, (mz_vec, int_vec))| (key, MzSpectrum::new(mz_vec, int_vec)))
228 .collect()
229 }
230
231 pub fn to_centroid(&self, baseline_noise_level: i32, sigma: f64, normalize: bool) -> MzSpectrum {
232
233 let filtered = self.filter_ranged(0.0, 1e9, baseline_noise_level as f64, 1e9);
234
235 let mut cent_mz = Vec::new();
236 let mut cent_i: Vec<f64> = Vec::new();
237
238 let mut last_mz = 0.0;
239 let mut mean_mz = 0.0;
240 let mut sum_i = 0.0;
241
242 for (i, ¤t_mz) in filtered.mz.iter().enumerate() {
243 let current_intensity = filtered.intensity[i];
244
245 if current_mz - last_mz > sigma && mean_mz > 0.0 {
247 mean_mz /= sum_i;
248 cent_mz.push(mean_mz);
249 cent_i.push(sum_i);
250
251 sum_i = 0.0;
253 mean_mz = 0.0;
254 }
255
256 mean_mz += current_mz * current_intensity as f64;
257 sum_i += current_intensity;
258 last_mz = current_mz;
259 }
260
261 if mean_mz > 0.0 {
263 mean_mz /= sum_i;
264 cent_mz.push(mean_mz);
265 cent_i.push(sum_i);
266 }
267
268 if normalize {
269 let sum_i: f64 = cent_i.iter().sum();
270 cent_i = cent_i.iter().map(|&i| i / sum_i).collect();
271 }
272 MzSpectrum::new(cent_mz, cent_i)
273 }
274
275 pub fn from_collection(collection: Vec<MzSpectrum>) -> MzSpectrum {
276
277 let quantize = |mz: f64| -> i64 {
278 (mz * 1_000_000.0).round() as i64
279 };
280
281 let mut combined_map: BTreeMap<i64, f64> = BTreeMap::new();
282
283 for spectrum in collection {
284 for (mz, intensity) in spectrum.mz.iter().zip(spectrum.intensity.iter()) {
285 let key = quantize(*mz);
286 let entry = combined_map.entry(key).or_insert(0.0);
287 *entry += *intensity;
288 }
289 }
290
291 let mz_combined: Vec<f64> = combined_map.keys().map(|&key| key as f64 / 1_000_000.0).collect();
292 let intensity_combined: Vec<f64> = combined_map.values().cloned().collect();
293
294 MzSpectrum::new(mz_combined, intensity_combined)
295 }
296
297 pub fn add_mz_noise_uniform(&self, ppm: f64, right_drag: bool) -> Self {
298 let mut rng = rand::thread_rng();
299 self.add_mz_noise_uniform_with_rng(ppm, right_drag, &mut rng)
300 }
301
302 pub fn add_mz_noise_uniform_with_rng<R: Rng>(&self, ppm: f64, right_drag: bool, rng: &mut R) -> Self {
306 self.add_mz_noise(ppm, rng, |rng, mz, ppm| {
307
308 let ppm_mz = match right_drag {
309 true => mz * ppm / 1e6 / 2.0,
310 false => mz * ppm / 1e6,
311 };
312
313 let dist = match right_drag {
314 true => Uniform::from(mz - (ppm_mz / 3.0)..=mz + ppm_mz),
315 false => Uniform::from(mz - ppm_mz..=mz + ppm_mz),
316 };
317
318 dist.sample(rng)
319 })
320 }
321
322 pub fn add_mz_noise_normal(&self, ppm: f64) -> Self {
323 let mut rng = rand::thread_rng();
324 self.add_mz_noise_normal_with_rng(ppm, &mut rng)
325 }
326
327 pub fn add_mz_noise_normal_with_rng<R: Rng>(&self, ppm: f64, rng: &mut R) -> Self {
329 self.add_mz_noise(ppm, rng, |rng, mz, ppm| {
330 let ppm_mz = mz * ppm / 1e6;
331 let dist = Normal::new(mz, ppm_mz / 3.0).unwrap();
332 dist.sample(rng)
333 })
334 }
335
336 fn add_mz_noise<R: Rng, F>(&self, ppm: f64, rng: &mut R, noise_fn: F) -> Self
337 where
338 F: Fn(&mut R, f64, f64) -> f64,
339 {
340 let mz: Vec<f64> = self.mz.iter().map(|&mz_value| noise_fn(rng, mz_value, ppm)).collect();
341 let spectrum = MzSpectrum { mz: Arc::new(mz), intensity: self.intensity.clone() };
343 spectrum.to_resolution(6)
345 }
346}
347
348impl ToResolution for MzSpectrum {
349 fn to_resolution(&self, resolution: i32) -> Self {
374 let mut binned: BTreeMap<i64, f64> = BTreeMap::new();
375 let factor = 10f64.powi(resolution);
376
377 for (mz, inten) in self.mz.iter().zip(self.intensity.iter()) {
378
379 let key = (mz * factor).round() as i64;
380 let entry = binned.entry(key).or_insert(0.0);
381 *entry += *inten;
382 }
383
384 let mz: Vec<f64> = binned.keys().map(|&key| key as f64 / 10f64.powi(resolution)).collect();
385 let intensity: Vec<f64> = binned.values().cloned().collect();
386
387 MzSpectrum::new(mz, intensity)
388 }
389}
390
391impl Vectorized<MzSpectrumVectorized> for MzSpectrum {
392 fn vectorized(&self, resolution: i32) -> MzSpectrumVectorized {
396
397 let binned_spectrum = self.to_resolution(resolution);
398
399 let indices: Vec<i32> = binned_spectrum.mz.iter().map(|&mz| (mz * 10f64.powi(resolution)).round() as i32).collect();
401
402 MzSpectrumVectorized {
403 resolution,
404 indices,
405 values: (*binned_spectrum.intensity).clone(),
406 }
407 }
408}
409
410impl Display for MzSpectrum {
412 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
413
414 let (mz, i) = self.mz.iter()
415 .zip(self.intensity.iter())
416 .max_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal))
417 .unwrap();
418
419 write!(f, "MzSpectrum(data points: {}, max by intensity:({}, {}))", self.mz.len(), format!("{:.3}", mz), i)
420 }
421}
422
423impl std::ops::Add for MzSpectrum {
424 type Output = Self;
425 fn add(self, other: Self) -> MzSpectrum {
443 let mut combined_map: BTreeMap<i64, f64> = BTreeMap::new();
444
445 let quantize = |mz: f64| -> i64 {
447 (mz * 1_000_000.0).round() as i64
448 };
449
450 for (mz, intensity) in self.mz.iter().zip(self.intensity.iter()) {
452 let key = quantize(*mz);
453 combined_map.insert(key, *intensity);
454 }
455
456 for (mz, intensity) in other.mz.iter().zip(other.intensity.iter()) {
458 let key = quantize(*mz);
459 let entry = combined_map.entry(key).or_insert(0.0);
460 *entry += *intensity;
461 }
462
463 let mz_combined: Vec<f64> = combined_map.keys().map(|&key| key as f64 / 1_000_000.0).collect();
465 let intensity_combined: Vec<f64> = combined_map.values().cloned().collect();
466
467 MzSpectrum::new(mz_combined, intensity_combined)
468 }
469}
470
471impl std::ops::Mul<f64> for MzSpectrum {
472 type Output = Self;
473 fn mul(self, scale: f64) -> Self::Output {
474 let scaled_intensities: Vec<f64> = self.intensity.iter().map(|i| scale * i).collect();
475 Self { mz: self.mz.clone(), intensity: Arc::new(scaled_intensities) }
477 }
478}
479
480impl std::ops::Sub for MzSpectrum {
481 type Output = Self;
482 fn sub(self, other: Self) -> Self::Output {
483 let mut combined_map: BTreeMap<i64, f64> = BTreeMap::new();
484
485 let quantize = |mz: f64| -> i64 {
487 (mz * 1_000_000.0).round() as i64
488 };
489
490 for (mz, intensity) in self.mz.iter().zip(self.intensity.iter()) {
492 let key = quantize(*mz);
493 combined_map.insert(key, *intensity);
494 }
495
496 for (mz, intensity) in other.mz.iter().zip(other.intensity.iter()) {
498 let key = quantize(*mz);
499 let entry = combined_map.entry(key).or_insert(0.0);
500 *entry -= *intensity;
501 }
502
503 let mz_combined: Vec<f64> = combined_map.keys().map(|&key| key as f64 / 1_000_000.0).collect();
505 let intensity_combined: Vec<f64> = combined_map.values().cloned().collect();
506
507 MzSpectrum::new(mz_combined, intensity_combined)
508 }
509}
510
511#[derive(Clone, Debug)]
513pub struct IndexedMzSpectrum {
514 pub index: Vec<i32>,
515 pub mz_spectrum: MzSpectrum,
516}
517
518impl Default for IndexedMzSpectrum {
520 fn default() -> Self {
521 IndexedMzSpectrum { index: Vec::new(), mz_spectrum: MzSpectrum::new(Vec::new(), Vec::new()) }
522 }
523}
524
525impl IndexedMzSpectrum {
526 pub fn new(index: Vec<i32>, mz: Vec<f64>, intensity: Vec<f64>) -> Self {
543 IndexedMzSpectrum { index, mz_spectrum: MzSpectrum::new(mz, intensity) }
544 }
545
546 pub fn from_mz_spectrum(index: Vec<i32>, mz_spectrum: MzSpectrum) -> Self {
556 IndexedMzSpectrum { index, mz_spectrum }
557 }
558
559 pub fn to_resolution(&self, resolution: i32) -> IndexedMzSpectrum {
579
580 let mut mz_bins: BTreeMap<i64, (f64, Vec<i64>)> = BTreeMap::new();
581 let factor = 10f64.powi(resolution);
582
583 for ((mz, intensity), tof_val) in self.mz_spectrum.mz.iter().zip(self.mz_spectrum.intensity.iter()).zip(&self.index) {
584 let key = (mz * factor).round() as i64;
585 let entry = mz_bins.entry(key).or_insert((0.0, Vec::new()));
586 entry.0 += *intensity;
587 entry.1.push(*tof_val as i64);
588 }
589
590 let mz: Vec<f64> = mz_bins.keys().map(|&key| key as f64 / factor).collect();
591 let intensity: Vec<f64> = mz_bins.values().map(|(intensity, _)| *intensity).collect();
592 let tof: Vec<i32> = mz_bins.values().map(|(_, tof_vals)| {
593 let sum: i64 = tof_vals.iter().sum();
594 let count: i32 = tof_vals.len() as i32;
595 (sum as f64 / count as f64).round() as i32
596 }).collect();
597
598 IndexedMzSpectrum {index: tof, mz_spectrum: MzSpectrum::new(mz, intensity) }
599 }
600
601 pub fn vectorized(&self, resolution: i32) -> IndexedMzSpectrumVectorized {
622
623 let binned_spectrum = self.to_resolution(resolution);
624
625 let indices: Vec<i32> = binned_spectrum.mz_spectrum.mz.iter()
627 .map(|&mz| (mz * 10f64.powi(resolution)).round() as i32).collect();
628
629 IndexedMzSpectrumVectorized {
630 index: binned_spectrum.index,
631 mz_vector: MzSpectrumVectorized {
632 resolution,
633 indices,
634 values: (*binned_spectrum.mz_spectrum.intensity).clone(),
635 }
636 }
637 }
638
639 pub fn filter_ranged(&self, mz_min: f64, mz_max: f64, intensity_min:f64, intensity_max: f64) -> Self {
640 let mut mz_vec: Vec<f64> = Vec::new();
641 let mut intensity_vec: Vec<f64> = Vec::new();
642 let mut index_vec: Vec<i32> = Vec::new();
643
644 for ((&mz, &intensity), &index) in self.mz_spectrum.mz.iter().zip(self.mz_spectrum.intensity.iter()).zip(self.index.iter()) {
645 if mz_min <= mz && mz <= mz_max && intensity >= intensity_min && intensity <= intensity_max {
646 mz_vec.push(mz);
647 intensity_vec.push(intensity);
648 index_vec.push(index);
649 }
650 }
651 IndexedMzSpectrum { index: index_vec, mz_spectrum: MzSpectrum::new(mz_vec, intensity_vec) }
652 }
653}
654
655impl Display for IndexedMzSpectrum {
656 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
657 let (mz, i) = self.mz_spectrum.mz.iter()
658 .zip(self.mz_spectrum.intensity.iter())
659 .max_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal))
660 .unwrap();
661
662 write!(f, "IndexedMzSpectrum(data points: {}, max by intensity:({}, {}))", self.mz_spectrum.mz.len(), format!("{:.3}", mz), i)
663 }
664}
665
666#[derive(Clone)]
667pub struct MzSpectrumVectorized {
668 pub resolution: i32,
669 pub indices: Vec<i32>,
670 pub values: Vec<f64>,
671}
672
673impl MzSpectrumVectorized {
674 fn get_max_index(&self) -> usize {
684 let base: i32 = 10;
685 let max_mz: i32 = 2000;
686 let max_index: usize = (max_mz*base.pow(self.resolution as u32)) as usize;
687 max_index
688 }
689
690 pub fn to_dense(&self, max_index: Option<usize>) -> DVector<f64> {
691 let max_index = match max_index {
692 Some(max_index) => max_index,
693 None => self.get_max_index(),
694 };
695 let mut dense_intensities: DVector<f64> = DVector::<f64>::zeros(max_index + 1);
696 for (&index, &intensity) in self.indices.iter().zip(self.values.iter()) {
697 if (index as usize) <= max_index {
698 dense_intensities[index as usize] = intensity;
699 }
700 }
701 dense_intensities
702 }
703 pub fn to_dense_spectrum(&self, max_index: Option<usize>) -> MzSpectrumVectorized{
704 let max_index = match max_index {
705 Some(max_index) => max_index,
706 None => self.get_max_index(),
707 };
708 let dense_intensities: Vec<f64> = self.to_dense(Some(max_index)).data.into();
709 let dense_indices: Vec<i32> = (0..=max_index).map(|i| i as i32).collect();
710 let dense_spectrum: MzSpectrumVectorized = MzSpectrumVectorized { resolution: (self.resolution), indices: (dense_indices), values: (dense_intensities) };
711 dense_spectrum
712 }
713}
714
715#[derive(Clone)]
716pub struct IndexedMzSpectrumVectorized {
717 pub index: Vec<i32>,
718 pub mz_vector: MzSpectrumVectorized,
719}
720#[cfg(test)]
721mod mz_noise_seeded_tests {
722 use super::*;
723 use crate::simulation::noise_rng::noise_rng;
724
725 fn spectrum() -> MzSpectrum {
726 MzSpectrum::new(
727 vec![300.1234, 450.5678, 700.9012, 1100.3456],
728 vec![1000.0, 2500.0, 700.0, 150.0],
729 )
730 }
731
732 #[test]
733 fn seeded_uniform_jitter_is_reproducible() {
734 let s = spectrum();
735 let a = s.add_mz_noise_uniform_with_rng(6.5, false, &mut noise_rng(41, &[7]));
736 let b = s.add_mz_noise_uniform_with_rng(6.5, false, &mut noise_rng(41, &[7]));
737 assert_eq!(a.mz, b.mz);
738 assert_eq!(a.intensity, b.intensity);
739 }
740
741 #[test]
742 fn seeded_normal_jitter_is_reproducible() {
743 let s = spectrum();
744 let a = s.add_mz_noise_normal_with_rng(6.5, &mut noise_rng(41, &[7]));
745 let b = s.add_mz_noise_normal_with_rng(6.5, &mut noise_rng(41, &[7]));
746 assert_eq!(a.mz, b.mz);
747 }
748
749 #[test]
750 fn a_different_frame_key_gives_different_jitter() {
751 let s = spectrum();
752 let a = s.add_mz_noise_normal_with_rng(6.5, &mut noise_rng(41, &[7]));
753 let b = s.add_mz_noise_normal_with_rng(6.5, &mut noise_rng(41, &[8]));
754 assert_ne!(a.mz, b.mz, "frames must not share a noise stream");
755 }
756
757 #[test]
758 fn the_jitter_stays_within_the_requested_ppm() {
759 let s = spectrum();
760 let ppm = 6.5;
761 let out = s.add_mz_noise_uniform_with_rng(ppm, false, &mut noise_rng(41, &[1]));
762 assert_eq!(out.mz.len(), s.mz.len());
763 for (before, after) in s.mz.iter().zip(out.mz.iter()) {
764 let window = before * ppm / 1e6 + 1e-6;
766 assert!((after - before).abs() <= window, "{before} -> {after} exceeds {ppm} ppm");
767 }
768 }
769}