1pub extern crate num_traits;
2extern crate rayon;
3
4#[cfg(feature = "json")]
5extern crate serde;
6#[cfg(feature = "json")]
7extern crate serde_json;
8#[cfg(feature = "json")]
9#[macro_use]
10extern crate serde_derive;
11
12pub mod algorithm;
13pub mod concurrency;
14pub mod indexers;
15pub mod config;
16#[cfg(feature = "json")]
17pub mod io;
18pub mod output_stream;
19pub mod pstreams;
20pub mod stream;
21pub mod striding;
22#[cfg(test)]
23mod test_utils;
24pub mod updaters;
25pub mod ui;
26pub mod utils;
27mod validation;
28
29pub use crate::config::{Config, AlgorithmConfig, NegativeHandling, OutputStyle};
30pub use crate::indexers::{Indexer, IndexFactory, SequentialIndexer, InterleavedIndexer, SeparateIndexer};
31pub use crate::updaters::{SeparateUpdater, AbsUpdater, Updater};
32
33use crate::concurrency::{CancellationToken};
34use crate::validation::{ValidationError};
35use crate::ui::{ProgressUpdater, ThreadSafePU, ModProgressUpdater};
36
37use rayon::prelude::*;
38
39use num_traits::identities::{Zero};
40use num_traits::sign::{Signed};
41use std::ops::{Sub, AddAssign};
43use std::sync::{Mutex, PoisonError};
44
45
46#[derive(Debug, PartialEq)]
47pub enum Error
48{
49 UnknownMemoryOrder,
50 Cancellation,
51 PoisonedMutex(String)
52}
53
54impl Error
55{
56 pub fn unknown_memory_order() -> Self
57 {
58 Self::UnknownMemoryOrder
59 }
60
61 pub fn code(&self) -> i32
62 {
63 match self
64 {
65 Self::UnknownMemoryOrder => 1,
66 Self::Cancellation => 2,
67 Self::PoisonedMutex(_) => 3
68 }
69 }
70
71 pub fn to_string(&self) -> String
72 {
73 match self
74 {
75 Self::UnknownMemoryOrder => String::from("Unknown Memory Order"),
76 Self::Cancellation => String::from("Cancelled"),
77 Self::PoisonedMutex(e) => format!("Poisoned Mutex {}", e.to_string())
78 }
79 }
80}
81
82impl<T> From<PoisonError<T>> for Error
83{
84 fn from(error: PoisonError<T>) -> Self
85 {
86 Self::PoisonedMutex(error.to_string())
87 }
88}
89
90impl TryFrom<i32> for Error
91{
92 type Error = ();
93 fn try_from(error_code: i32) -> Result<Self, Self::Error>
94 {
95 match error_code
96 {
97 1 => Ok(Self::UnknownMemoryOrder),
98 2 => Ok(Self::Cancellation),
99 3 => Ok(Self::PoisonedMutex(String::new())),
100 _ => Err(())
101 }
102 }
103}
104
105pub const fn get_version() -> &'static str
106{
107 env!("CARGO_PKG_VERSION")
108}
109
110#[cfg(feature = "json")]
111pub fn get_metadata(config: &Config) -> String
112{
113 format!("hawk_core:\nVersion: {}\nconfig: {}", get_version(), config.to_io())
114}
115
116pub fn validate(n_data: u64, config: &Config) -> Result<(), ValidationError>
117{
118 validation::validate(n_data, config)
119}
120
121fn hawk_iterator_inner<'a, T, I: Iterator<Item=&'a [T]>, U: Updater, X: Indexer>(input_image: I, config: &AlgorithmConfig, output_image: &mut Vec<Vec<T>>, output_data: &mut Vec<T>, indexer: X) -> ()
122where T: Sub<Output=T> + Zero + AddAssign + Signed + Copy + PartialOrd + std::fmt::Display + std::fmt::Debug + 'a,
123 <T as Sub>::Output : AddAssign
124{
125 for pixel_timeseries in input_image
126 {
127 for x in output_data.iter_mut()
128 {
129 *x = T::zero();
130 }
131 algorithm::custom_transform_hawk_timeseries::<T, U, &X>(pixel_timeseries, config, output_data, &indexer);
133 output_image.push(output_data.clone());
134 }
135}
136
137fn hawk_iterator_with<'a, T, I: Iterator<Item=&'a [T]>, U: Updater, X: Indexer + IndexFactory>(mut input_image: I, config: &AlgorithmConfig) -> Vec<Vec<T>>
138where T: Sub<Output=T> + Zero + AddAssign + Signed + Copy + PartialOrd + std::fmt::Display + std::fmt::Debug + 'a,
139 <T as Sub>::Output : AddAssign
140{
141 let mut output_image = Vec::new();
142 let output_size : u64;
143 let mut output_data : Vec<T>;
144 let indexer : X;
145
146 match input_image.next()
147 {
148 None => return output_image,
149 Some(pixel_timeseries) =>
150 {
151 output_size = utils::output_size(pixel_timeseries.len() as u64, config);
152 indexer = X::create(pixel_timeseries.len(), config);
153 output_data = vec![T::zero(); output_size as usize];
154 algorithm::custom_transform_hawk_timeseries::<T, U, &X>(pixel_timeseries, config, &mut output_data, &indexer);
156 output_image.push(output_data.clone());
157 }
158 }
159
160 hawk_iterator_inner::<T, I, U, X>(input_image, config, &mut output_image, &mut output_data, indexer);
161 output_image
162}
163
164fn hawk_iterator_with_indexer<'a, T, I: Iterator<Item=&'a [T]>, X: Indexer + IndexFactory>(input_image: I, config: &AlgorithmConfig) -> Vec<Vec<T>>
165where T: Sub<Output=T> + Zero + AddAssign + Signed + Copy + PartialOrd + std::fmt::Display + std::fmt::Debug + 'a,
166 <T as Sub>::Output : AddAssign
167{
168 match config.negative_handling()
169 {
170 NegativeHandling::Absolute => hawk_iterator_with::<T, I, AbsUpdater, X>(input_image, config),
171 NegativeHandling::Separate => hawk_iterator_with::<T, I, SeparateUpdater, SeparateIndexer<X>>(input_image, config)
172 }
173}
174
175pub fn hawk_iterator<'a, T, I: Iterator<Item=&'a [T]>>(input_image: I, config: &AlgorithmConfig) -> Vec<Vec<T>>
176where T: Sub<Output=T> + Zero + AddAssign + Signed + Copy + PartialOrd + std::fmt::Display + std::fmt::Debug + 'a,
177 <T as Sub>::Output : AddAssign
178{
179 match config.output_style()
180 {
181 OutputStyle::Sequential => hawk_iterator_with_indexer::<T, I, SequentialIndexer>(input_image, config),
182 OutputStyle::Interleaved => hawk_iterator_with_indexer::<T, I, InterleavedIndexer>(input_image, config)
183 }
184}
185
186
187fn contiguous_shortcut<T, U: Updater, I: Indexer + IndexFactory + Send + Sync, PU: ProgressUpdater + Send + Sync>(image_array: &[T], output_array: &mut [T], config: &AlgorithmConfig, cancellation_token: CancellationToken, n_pixels: usize, n_timepoints: usize, pu: PU) -> Result<(), Error>
188where T: Sub<Output=T> + Zero + AddAssign + Signed + Copy + PartialOrd + Send + Sync + std::fmt::Display + std::fmt::Debug,
189{
190 let indexer = I::create(n_timepoints, config);
191 let output_size = utils::output_size(n_timepoints as u64, config) as usize;
192 let output_image_mutex = Mutex::new(output_array);
195 let pu_mutex = Mutex::new(pu);
196 let _ = (0..n_pixels).into_par_iter().try_for_each(| pixel |
197 {
198 if cancellation_token.is_cancelled()
199 {
200 match pu_mutex.lock()
201 {
202 Ok(mut pu) => pu.finish(),
203 Err(_) => {}
204 }
205 return Err(Error::Cancellation)
206 }
207 let start = pixel * n_timepoints;
208 let end = start + n_timepoints;
209 let mut output_data = vec![T::zero(); output_size];
210 algorithm::custom_transform_hawk_timeseries::<T, U, &I>(&image_array[start..end], config, &mut output_data, &indexer);
217 match output_image_mutex.lock()
220 {
221 Ok(mut image) =>
222 {
223 let output_start = pixel * output_size;
224 let output_end = output_start + output_size;
225 image[output_start..output_end].clone_from_slice(&output_data);
227 }
228 Err(e) =>
229 {
230 return Err(Error::PoisonedMutex(e.to_string()));
231 }
232 }
233
234 match pu_mutex.lock()
235 {
236 Ok(mut pu) =>
237 {
238 pu.update_progress(pixel as u64, n_pixels as u64);
239 Ok(())
240 },
241 Err(e) =>
242 {
243 Err(Error::from(e))
244 }
245 }
246 });
247 pu_mutex.into_inner().map(|mut pu| pu.finish()).map_err(Error::from)
248}
249
250
251fn hawk_contiguous_with<T, I: Indexer + IndexFactory + Send + Sync, PU: ProgressUpdater + Send + Sync>(image_array: &[T], output_array: &mut [T], config: &AlgorithmConfig, cancellation_token: CancellationToken, n_pixels: usize, n_timepoints: usize, pu: PU) -> Result<(), Error>
252where T: Sub<Output=T> + Zero + AddAssign + Signed + Copy + PartialOrd + Send + Sync + std::fmt::Display + std::fmt::Debug,
253{
254 match config.negative_handling()
255 {
256 NegativeHandling::Absolute => contiguous_shortcut::<T, AbsUpdater, I, PU>(image_array, output_array, config, cancellation_token, n_pixels, n_timepoints, pu),
257 NegativeHandling::Separate => contiguous_shortcut::<T, SeparateUpdater, SeparateIndexer<I>, PU>(image_array, output_array, config, cancellation_token, n_pixels, n_timepoints, pu)
258 }
259}
260
261pub fn hawk_contiguous<T, PU: ProgressUpdater + Send + Sync>(image_array: &[T], output_array: &mut [T], config: &AlgorithmConfig, cancellation_token: CancellationToken, n_pixels: usize, n_timepoints: usize, pu: PU) -> Result<(), Error>
262where T: Sub<Output=T> + Zero + AddAssign + Signed + Copy + PartialOrd + Send + Sync + std::fmt::Display + std::fmt::Debug,
263{
264 match config.output_style()
265 {
266 OutputStyle::Sequential => hawk_contiguous_with::<T, SequentialIndexer, PU>(image_array, output_array, config, cancellation_token, n_pixels, n_timepoints, pu),
267 OutputStyle::Interleaved => hawk_contiguous_with::<T, InterleavedIndexer, PU>(image_array, output_array, config, cancellation_token, n_pixels, n_timepoints, pu)
268 }
269}
270
271fn perform_with<T, U: Updater, X: Indexer + IndexFactory + Sync, PU: ProgressUpdater + Send + Sync>(input_image: &Vec<Vec<T>>, config: &AlgorithmConfig, pu: PU) -> Result<Vec<Vec<T>>, String>
272where T: Sub<Output=T> + Zero + AddAssign + Signed + Copy + PartialOrd + std::fmt::Display + std::fmt::Debug + Send + Sync,
273 <T as Sub>::Output : AddAssign
274{
275 let output_size = utils::output_size(input_image[0].len() as u64, config);
276 let indexer = X::create(input_image[0].len(), config);
277 let output_image = vec![Vec::new(); input_image.len()];
278 let output_image_mutex = Mutex::new(output_image);
279 let mut ts_pu = ThreadSafePU::new(ModProgressUpdater::new(pu, 100));
280
281 let n_pixels = input_image.len();
282 let _ = input_image.par_iter().enumerate().try_for_each(|(pixel, t_series)|
283 {
284 let _ = ts_pu.thread_safe_update_progress(pixel as u64, n_pixels as u64).map_err(|e| e.to_string())?; let mut output_data = vec![T::zero(); output_size as usize];
287 algorithm::custom_transform_hawk_timeseries::<T, U, &X>(t_series, config, &mut output_data, &indexer);
289 match output_image_mutex.lock()
290 {
291 Ok(mut image) =>
292 {
293 image[pixel] = output_data;
294 Ok(())
295 }
296 Err(e) =>
297 {
298 Err(e.to_string())
299 }
300 }
301 })?;
302 ts_pu.finish();
303 match output_image_mutex.into_inner()
304 {
305 Ok(image) => Ok(image),
306 Err(poisoned) => Ok(poisoned.into_inner())
307 }
308}
309
310fn perform_with_indexer<T, I: Indexer + IndexFactory + Sync, PU: ProgressUpdater + Send + Sync>(input_image: &Vec<Vec<T>>, config: &AlgorithmConfig, pu: PU) -> Result<Vec<Vec<T>>, String>
311where T: Sub<Output=T> + Zero + AddAssign + Signed + Copy + PartialOrd + std::fmt::Display + std::fmt::Debug + Send + Sync,
312 <T as Sub>::Output : AddAssign
313{
314 match config.negative_handling()
315 {
316 NegativeHandling::Absolute => perform_with::<T, AbsUpdater, I, PU>(input_image, config, pu),
317 NegativeHandling::Separate => perform_with::<T, SeparateUpdater, SeparateIndexer<I>, PU>(input_image, config, pu)
318 }
319}
320
321pub fn hawk_image_vectors_parallel<T, PU: ProgressUpdater + Send + Sync>(input_image: &Vec<Vec<T>>, config: &AlgorithmConfig, pu: PU) -> Result<Vec<Vec<T>>, String>
322where T: Sub<Output=T> + Zero + AddAssign + Signed + Copy + PartialOrd + std::fmt::Display + std::fmt::Debug + Send + Sync,
323 <T as Sub>::Output : AddAssign
324{
325 match config.output_style()
326 {
327 OutputStyle::Sequential => perform_with_indexer::<T, SequentialIndexer, PU>(input_image, config, pu),
328 OutputStyle::Interleaved => perform_with_indexer::<T, InterleavedIndexer, PU>(input_image, config, pu)
329 }
330}
331
332pub fn hawk_timeseries<T>(input: &[T], output: &mut [T], config: &AlgorithmConfig) -> ()
333where T: Sub<Output=T> + Zero + AddAssign + Signed + Copy + PartialOrd + std::fmt::Display + std::fmt::Debug,
334{
335 let images = hawk_iterator(std::iter::once(input), config);
338 if images.is_empty()
339 {
340 return
341 }
342 output.clone_from_slice(&images[0])
343}
344
345
346#[cfg(test)]
347mod tests
348{
349 use super::*;
350
351 #[test]
352 fn hawk_timeseries_test()
353 {
354 let input = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
355 let mut output_data = [0; 19];
356 hawk_timeseries(&input, &mut output_data, &AlgorithmConfig::default());
357 let expected = [1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 4, 4, 4, 4, 4, 16, 16, 16];
358 assert_eq!(output_data, expected);
359 }
360
361 #[cfg(feature = "json")]
362 #[test]
363 fn metadata_test()
364 {
365 let config = Config::default();
366 let expected = r#"hawk_core:
367Version: 0.1.0
368config: {"Version1":{"threading":"Parallel","memory":"Contiguous","run_style":"InMemory","algorithm":{"n_levels":3,"output_style":"Sequential","negative_handling":"Absolute"},"validation":null}}"#;
369 assert_eq!(get_metadata(&config).as_str(), expected)
370 }
371}