1use std::fs::read_dir;
4use std::fs::{create_dir, remove_dir_all, File};
5use std::io::{BufReader, BufWriter, Write};
6use std::path::{Path, PathBuf};
7use std::result::Result as StdResult;
8use std::sync::atomic::{AtomicBool, Ordering};
9use std::sync::Arc;
10
11use bincode;
12use ctrlc::set_handler;
13use log::LevelFilter;
14use ron::{self, ser::PrettyConfig};
15use serde::{de::DeserializeOwned, Deserialize, Serialize};
16use simple_logging::log_to_file;
17use tar::{Archive as TarArchive, Builder as TarBuilder};
18use time::{Duration, Error as TimeError, OffsetDateTime};
19
20use crate::error::{IndexOutOfBounds, Result, StatesNotRecorded};
21use crate::params::FromParams;
22
23pub trait Runnable: FromParams + Sized {
25 type State: Clone;
27
28 fn step(&mut self) -> Option<&Self::State>;
32}
33
34pub const DATA_DIR_NAME: &'static str = "data";
35pub const SETTINGS_FILE_NAME: &'static str = "settings.ron";
36pub const STATS_FILE_NAME: &'static str = "stats.ron";
37pub const PARAMS_FILE_NAME: &'static str = "params.ron";
38pub const LOG_FILE_NAME: &'static str = "run.log";
39
40pub fn run_and_save<R: Runnable>(mut settings: Settings) -> Result<Run<R>>
42where
43 R::Params: Serialize + DeserializeOwned,
44 R::State: Serialize,
45{
46 let mut stats = Stats::new_paused()?; let is_running = Arc::new(AtomicBool::new(true));
50 let r = is_running.clone();
51
52 set_handler(move || {
53 r.store(false, Ordering::SeqCst);
54 })?;
55
56 if settings.verbose {
57 match settings.name {
58 Some(ref name) => println!("Starting run for {}", name),
59 None => println!("Starting run"),
60 }
61 println!("\tmax_iterations: {:?}", settings.max_iterations);
62 println!("\tkeep_in_memory: {}", settings.keep_in_memory);
63 println!("\toutput_dir: {:?}", settings.output_dir);
64 println!("\tfilename_prefix: {:?}", settings.filename_prefix);
65 println!("\tfilename_suffix: {:?}", settings.filename_suffix);
66 println!();
67 }
68
69 let params: R::Params = load_ron(&settings.parameter_file)?;
71 if settings.verbose {
72 println!("Parameters loaded");
73 }
74
75 let (run_dir, data_dir) = if let Some(output_dir) = settings.output_dir {
77 let run_dir = output_dir;
79 create_dir(&run_dir)?;
80
81 log_to_file(run_dir.join(LOG_FILE_NAME), LevelFilter::Info)?;
83
84 let data_dir = run_dir.join(DATA_DIR_NAME);
86 create_dir(&data_dir)?;
87
88 (Some(run_dir), Some(data_dir))
89 } else {
90 (None, None)
91 };
92
93 let mut runnable = R::from_params(params.clone())?;
95
96 settings.output_dir = run_dir;
97
98 let mut states = if settings.keep_in_memory {
99 Some(StateStorage::new_memory())
100 } else {
101 match data_dir {
102 Some(dir) => Some(StateStorage::new_disk(
103 dir,
104 settings.filename_prefix.clone(),
105 settings.filename_suffix.clone(),
106 )),
107 None => None,
108 }
109 };
110
111 while is_running.load(Ordering::SeqCst) {
113 if let Some(max_iterations) = settings.max_iterations {
114 if settings.verbose {
115 print!(
116 "\rRunning iteration: ({}/{})",
117 stats.iterations + 1,
118 max_iterations
119 );
120 std::io::stdout().flush()?;
121 }
122 log::info!(
123 "Running iteration: ({}/{})",
124 stats.iterations + 1,
125 max_iterations
126 );
127 } else {
128 if settings.verbose {
129 print!("\rRunning iteration {}", stats.iterations + 1);
130 std::io::stdout().flush()?;
131 }
132 log::info!("Running iteration {}", stats.iterations + 1);
133 }
134
135 stats.start()?; let state_opt = runnable.step();
137 stats.pause()?; match state_opt {
140 Some(state) => {
141 match states.as_mut() {
143 Some(StateStorage::InMemory(states)) => {
144 states.push(stats.timestamp(state.clone())?);
145
146 if settings.verbose {
147 print!(", Saved to memory");
148 }
149 }
150 Some(StateStorage::OnDisk {
151 dir,
152 prefix,
153 suffix,
154 count,
155 ..
156 }) => {
157 let path =
159 get_indexed_path(dir, prefix.as_ref(), suffix.as_ref(), *count);
160 save_bincode(&path, &stats.timestamp(state)?)?;
161 *count += 1;
162
163 if settings.verbose {
164 print!(", Saved to disk");
165 }
166 }
167 None => {
168 if settings.verbose {
169 print!(", State information not saved");
170 }
171 }
172 }
173
174 stats.next_iteration();
175 if let Some(max_iterations) = settings.max_iterations {
177 if stats.iterations >= max_iterations {
178 break;
179 }
180 }
181 }
182 None => break,
183 }
184 }
185 println!();
186
187 stats.pause()?;
189
190 let mut run = Run {
191 settings,
192 stats,
193 params,
194 states,
195 };
196
197 run.save()?;
199
200 if run.settings.verbose {
201 println!(
202 "Algorithm running time: {:.4} seconds",
203 run.stats.get_running_time().as_seconds_f64()
204 );
205
206 if let Some(total_time) = run.stats.get_total_time() {
207 println!("Total time: {:.4} seconds", total_time.as_seconds_f64());
208 }
209 }
210
211 return Ok(run);
212}
213
214#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
215pub struct Settings {
216 pub verbose: bool,
217 pub algorithm: String,
218 pub name: Option<String>,
219 pub parameter_file: PathBuf,
220 pub max_iterations: Option<usize>,
221 pub keep_in_memory: bool,
222 pub double: bool,
223 pub output_dir: Option<PathBuf>,
224 pub filename_prefix: Option<String>,
225 pub filename_suffix: Option<String>,
226}
227
228impl Settings {
229 pub fn load<P: AsRef<Path>>(dir: P) -> Result<Self> {
230 let path = dir.as_ref().join(SETTINGS_FILE_NAME);
232 let mut settings: Self = load_ron(&path)?;
233 if settings.verbose {
234 println!("Settings loaded from \"{}\"", path.display());
235 }
236
237 settings.output_dir = Some(dir.as_ref().to_path_buf());
238
239 Ok(settings)
240 }
241}
242
243#[derive(Debug, Clone, Copy)]
244enum TimerState {
245 Running { last_start_time: OffsetDateTime },
246 Paused,
247 Finished,
248}
249
250impl Default for TimerState {
251 fn default() -> Self {
252 TimerState::Finished
253 }
254}
255
256#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
257pub struct Stats {
258 start_time: OffsetDateTime,
259 #[serde(skip)]
260 state: TimerState,
261 total_time: Option<Duration>,
262 running_time: Duration,
263 iterations: usize,
264}
265
266impl Stats {
267 fn new_paused() -> Result<Self> {
269 Ok(Self {
270 start_time: Self::time_now()?,
271 state: TimerState::Paused,
272 total_time: None,
273 running_time: Duration::ZERO,
274 iterations: 0,
275 })
276 }
277
278 fn timestamp<T>(&self, obj: T) -> Result<TimeStamped<T>> {
280 Ok(TimeStamped {
281 time_since_start: self.total_time_since_start()?,
282 time_running_since_start: self.running_time_since_start()?,
283 obj,
284 })
285 }
286
287 fn time_now() -> StdResult<OffsetDateTime, TimeError> {
288 Ok(OffsetDateTime::now_utc())
289 }
290
291 fn running_time_since_start(&self) -> Result<Duration> {
292 match self.state {
293 TimerState::Running { last_start_time } => {
294 let since_last_start = Self::time_now()? - last_start_time;
295 Ok(since_last_start + self.get_running_time())
296 }
297 TimerState::Paused | TimerState::Finished => Ok(self.get_running_time()),
298 }
299 }
300
301 fn total_time_since_start(&self) -> Result<Duration> {
302 Ok(Self::time_now()? - self.start_time)
303 }
304
305 fn start(&mut self) -> Result<()> {
307 match self.state {
308 TimerState::Running { .. } => {
309 Ok(())
311 }
312 TimerState::Paused => {
313 self.state = TimerState::Running {
314 last_start_time: Self::time_now()?,
315 };
316 Ok(())
317 }
318 TimerState::Finished => {
319 Ok(())
321 }
322 }
323 }
324
325 fn pause(&mut self) -> Result<()> {
327 match self.state {
328 TimerState::Running { .. } => {
329 self.running_time = self.running_time_since_start()?;
330 self.state = TimerState::Paused;
331 Ok(())
332 }
333 TimerState::Paused => {
334 Ok(())
336 }
337 TimerState::Finished => {
338 Ok(())
340 }
341 }
342 }
343
344 fn finish(&mut self) -> Result<()> {
345 self.pause()?;
346 self.state = TimerState::Finished;
347 self.total_time = Some(self.total_time_since_start()?);
348 Ok(())
349 }
350
351 pub fn get_total_time(&self) -> Option<&Duration> {
355 self.total_time.as_ref()
356 }
357
358 pub fn get_running_time(&self) -> Duration {
360 self.running_time
361 }
362
363 fn next_iteration(&mut self) {
364 self.iterations += 1;
365 }
366
367 pub fn get_iterations(&self) -> usize {
368 self.iterations
369 }
370}
371
372#[derive(Debug, Clone, Serialize, Deserialize)]
373#[serde(bound(
374 serialize = "T: Serialize",
375 deserialize = "T: DeserializeOwned"
376))]
377enum StateStorage<T> {
378 InMemory(Vec<T>),
380 OnDisk {
382 dir: PathBuf,
384 prefix: Option<String>,
386 suffix: Option<String>,
388 count: usize,
390 #[serde(skip)]
392 last: Option<T>,
393 },
394}
395
396impl<T> StateStorage<T> {
397 fn new_memory() -> Self {
398 Self::InMemory(Vec::new())
399 }
400
401 fn load_memory(
402 dir: &PathBuf,
403 prefix: Option<String>,
404 suffix: Option<String>,
405 count: usize,
406 verbose: bool,
407 ) -> Result<Self>
408 where
409 T: DeserializeOwned,
410 {
411 let mut vec = Vec::new();
412 for i in 0..count {
413 let path = get_indexed_path(dir, prefix.as_ref(), suffix.as_ref(), i);
414 let new = load_bincode(path)?;
415 vec.push(new);
416
417 if verbose {
418 print!("\r> Loading states from file: ({}/{})", i + 1, count);
419 }
420 }
421 if verbose {
422 println!(" Done");
423 }
424
425 Ok(Self::InMemory(vec))
426 }
427
428 fn new_disk(
429 dir: PathBuf,
430 prefix: Option<String>,
431 suffix: Option<String>,
432 ) -> Self {
433 Self::OnDisk {
434 dir,
435 prefix,
436 suffix,
437 count: 0,
438 last: None,
439 }
440 }
441
442 fn load_disk(
443 dir: &PathBuf,
444 prefix: Option<String>,
445 suffix: Option<String>,
446 count: usize,
447 ) -> Result<Self> {
448 Ok(Self::OnDisk {
450 dir: dir.clone(),
451 prefix,
452 suffix,
453 count,
454 last: None,
455 })
456 }
457
458 fn get(&mut self, index: usize) -> Result<&T>
459 where
460 T: DeserializeOwned,
461 {
462 match self {
463 StateStorage::InMemory(states) => match states.get(index) {
464 Some(obj) => Ok(obj),
465 None => Err(IndexOutOfBounds)?,
466 },
467 StateStorage::OnDisk {
468 dir,
469 prefix,
470 suffix,
471 count,
472 last,
473 } => {
474 if index >= *count {
475 Err(IndexOutOfBounds)?;
476 }
477
478 let path =
480 get_indexed_path(dir, prefix.as_ref(), suffix.as_ref(), index);
481 let ref_ = last.insert(load_bincode(&path)?);
482 Ok(ref_)
483 }
484 }
485 }
486
487 fn try_get_all(&self) -> Option<&Vec<T>> {
488 match self {
489 StateStorage::InMemory(states) => Some(states),
490 StateStorage::OnDisk {
491 dir: _,
492 prefix: _,
493 suffix: _,
494 count: _,
495 last: _,
496 } => None,
497 }
498 }
499
500 fn count(&self) -> usize {
501 match self {
502 StateStorage::InMemory(states) => states.len(),
503 StateStorage::OnDisk {
504 dir: _,
505 prefix: _,
506 suffix: _,
507 count,
508 last: _,
509 } => *count,
510 }
511 }
512}
513
514impl<T> Drop for StateStorage<T> {
515 fn drop(&mut self) {
516 if let StateStorage::OnDisk {
517 dir,
518 prefix: _,
519 suffix: _,
520 count: _,
521 last: _,
522 } = self
523 {
524 remove_dir_all(&dir).unwrap();
526 println!("> Removed \"{}\"", dir.display());
527 }
528 }
529}
530
531#[derive(Debug, Clone, Serialize, Deserialize)]
532#[serde(bound(
533 serialize = "T: Serialize",
534 deserialize = "T: DeserializeOwned"
535))]
536pub struct TimeStamped<T> {
537 time_since_start: Duration,
538 time_running_since_start: Duration,
539 obj: T,
540}
541
542impl<T> TimeStamped<T> {
543 pub fn get_timestamp(&self) -> Duration {
544 self.time_since_start
545 }
546
547 pub fn get_running_timestamp(&self) -> Duration {
548 self.time_running_since_start
549 }
550
551 pub fn get_value(&self) -> &T {
552 &self.obj
553 }
554}
555
556impl<T> From<TimeStamped<&T>> for TimeStamped<T>
557where
558 T: Clone,
559{
560 fn from(timestamped: TimeStamped<&T>) -> Self {
561 Self {
562 time_since_start: timestamped.time_since_start,
563 time_running_since_start: timestamped.time_running_since_start,
564 obj: timestamped.obj.clone(),
565 }
566 }
567}
568
569#[derive(Debug)]
570pub struct Run<R: Runnable> {
571 settings: Settings,
572 stats: Stats,
573 params: R::Params,
574 states: Option<StateStorage<TimeStamped<R::State>>>,
575}
576
577impl<R: Runnable> Clone for Run<R> {
578 fn clone(&self) -> Self {
579 Self {
580 settings: self.settings.clone(),
581 stats: self.stats.clone(),
582 params: self.params.clone(),
583 states: self.states.clone(),
584 }
585 }
586}
587
588impl<R: Runnable> Run<R> {
589 pub fn name(&self) -> Option<&String> {
590 self.settings.name.as_ref()
591 }
592
593 pub fn settings(&self) -> &Settings {
594 &self.settings
595 }
596
597 pub fn params(&self) -> &R::Params {
598 &self.params
599 }
600
601 pub fn max_iterations(&self) -> Option<usize> {
602 self.settings.max_iterations
603 }
604
605 pub fn count(&self) -> usize {
606 match self.states {
607 Some(ref states) => states.count(),
608 None => 0,
609 }
610 }
611
612 pub fn get(&mut self, index: usize) -> Result<&TimeStamped<R::State>>
613 where
614 R::State: DeserializeOwned,
615 {
616 match self.states {
617 Some(ref mut states) => states.get(index),
618 None => Err(StatesNotRecorded)?,
619 }
620 }
621
622 pub fn try_get_all(&self) -> Option<&Vec<TimeStamped<R::State>>> {
623 match self.states {
624 Some(ref states) => states.try_get_all(),
625 None => None,
626 }
627 }
628
629 pub fn save(&mut self) -> Result<()>
630 where
631 R::Params: Serialize,
632 R::State: Serialize,
633 {
634 match &self.states {
636 Some(StateStorage::InMemory(states)) => {
637 if let Some(ref output_dir) = self.settings.output_dir {
638 let dir = output_dir.join(DATA_DIR_NAME);
640 for (i, state) in states.iter().enumerate() {
641 let path = get_indexed_path(
642 &dir,
643 self.settings.filename_prefix.clone(),
644 self.settings.filename_suffix.clone(),
645 i,
646 );
647 save_bincode(&path, state)?;
648
649 if self.settings.verbose {
650 print!("\r> Saving states to file: ({}/{})", i + 1, states.len());
651 }
652 }
653 if self.settings.verbose {
654 println!(" Done");
655 }
656 }
657 }
658 Some(StateStorage::OnDisk {
659 dir: _,
660 prefix: _,
661 suffix: _,
662 count: _,
663 last: _,
664 }) => {
665 }
668 None => (), }
670
671 if let Some(ref output_dir) = self.settings.output_dir {
672 let data_dir = output_dir.join(DATA_DIR_NAME);
674 let data_file = output_dir.join(format!("{}.tar", DATA_DIR_NAME));
675 let file = File::create(&data_file)?;
676 let mut tar = TarBuilder::new(file);
678 for (i, entry) in read_dir(&data_dir)?.enumerate() {
679 let entry = entry?;
680 if entry.file_type()?.is_file() {
681 let file_path = entry.path();
682 let mut file = File::open(&file_path)?;
683 let archive_path = file_path.strip_prefix(&data_dir)?;
684 if self.settings.verbose {
685 print!(
686 "\r> Compressing states: ({}/{})",
687 i + 1,
688 self.stats.iterations
689 );
690 }
691 tar.append_file(archive_path, &mut file)?;
692 }
693 }
694 let file = tar.into_inner()?;
695 drop(file);
697 if self.settings.verbose {
698 println!(" Done\n> Data archived to \"{}\"", data_file.display());
699 }
700
701 let path = output_dir.join(SETTINGS_FILE_NAME);
709 save_ron(&path, &self.settings)?;
710 if self.settings.verbose {
711 println!("> Settings saved to \"{}\"", path.display());
712 }
713
714 let path = output_dir.join(PARAMS_FILE_NAME);
716 save_ron(&path, &self.params)?;
717 if self.settings.verbose {
718 println!("> Parameters saved to \"{}\"", path.display());
719 }
720
721 self.stats.finish()?;
723 let path = output_dir.join(STATS_FILE_NAME);
724 save_ron(&path, &self.stats)?;
725 if self.settings.verbose {
726 println!("> Statistics saved to \"{}\"", path.display());
727 }
728 }
729
730 Ok(())
731 }
732
733 pub fn load(settings: Settings) -> Result<Self>
736 where
737 R::Params: DeserializeOwned,
738 R::State: Serialize + DeserializeOwned,
739 {
740 let dir = match settings.output_dir {
741 Some(ref output_dir) => output_dir,
742 None => Err(StatesNotRecorded)?,
743 };
744
745 if settings.verbose {
746 match settings.name {
747 Some(ref name) => println!("Visualizing {}", name),
748 None => println!("Visualizing"),
749 }
750 println!("\tmax_iterations: {:?}", settings.max_iterations);
751 println!("\tkeep_in_memory: {}", settings.keep_in_memory);
752 println!("\toutput_dir: {:?}", settings.output_dir);
753 println!("\tfilename_prefix: {:?}", settings.filename_prefix);
754 println!("\tfilename_suffix: {:?}", settings.filename_suffix);
755 println!();
756 }
757
758 let path = dir.join(STATS_FILE_NAME);
760 let stats: Stats = load_ron(&path)?;
761 if settings.verbose {
762 println!("> Statistics loaded from \"{}\"", path.display());
763 }
764
765 let path = dir.join(PARAMS_FILE_NAME);
767 let params: R::Params = load_ron(&path)?;
768 if settings.verbose {
769 println!("> Parameters loaded from \"{}\"", path.display());
770 }
771
772 let data_dir = dir.join(DATA_DIR_NAME);
774 create_dir(&data_dir)?;
775 let data_file = dir.join(format!("{}.tar", DATA_DIR_NAME));
776 let file = File::open(data_file)?;
777 let mut archive = TarArchive::new(file);
779 for (i, entry) in archive.entries()?.enumerate() {
780 let mut file = entry?;
781 print!("\r> Uncompressing states: ({}/{})", i + 1, stats.iterations);
782 file.unpack_in(&data_dir)?;
783 }
784 println!(" Done");
785
786 let states = if settings.keep_in_memory {
788 Some(StateStorage::load_memory(
789 &data_dir,
790 settings.filename_prefix.clone(),
791 settings.filename_suffix.clone(),
792 stats.get_iterations(),
793 settings.verbose,
794 )?)
795 } else {
796 Some(StateStorage::load_disk(
797 &data_dir,
798 settings.filename_prefix.clone(),
799 settings.filename_suffix.clone(),
800 stats.get_iterations(),
801 )?)
802 };
803
804 Ok(Run {
805 settings,
806 stats,
807 params,
808 states,
809 })
810 }
811}
812
813pub fn save_ron<T, P>(path: P, obj: &T) -> Result<()>
814where
815 T: Serialize,
816 P: AsRef<Path>,
817{
818 let file = BufWriter::new(File::create(path)?);
819 Ok(ron::ser::to_writer_pretty(
820 file,
821 obj,
822 PrettyConfig::default(),
823 )?)
824}
825
826pub fn load_ron<T, P>(path: P) -> Result<T>
827where
828 T: DeserializeOwned,
829 P: AsRef<Path>,
830{
831 let file = BufReader::new(File::open(path)?);
832 Ok(ron::de::from_reader(file)?)
833}
834
835pub fn save_bincode<T, P>(path: P, obj: &T) -> Result<()>
836where
837 T: Serialize,
838 P: AsRef<Path>,
839{
840 let file = BufWriter::new(File::create(path)?);
841 Ok(bincode::serialize_into(file, obj)?)
842}
843
844pub fn load_bincode<T, P>(path: P) -> Result<T>
845where
846 T: DeserializeOwned,
847 P: AsRef<Path>,
848{
849 let file = BufReader::new(File::open(path)?);
850 Ok(bincode::deserialize_from(file)?)
851}
852
853fn get_indexed_path<P, S1, S2>(
854 directory: P,
855 prefix: Option<S1>,
856 suffix: Option<S2>,
857 index: usize,
858) -> PathBuf
859where
860 P: AsRef<Path>,
861 S1: AsRef<str>,
862 S2: AsRef<str>,
863{
864 let filename = match (prefix, suffix) {
865 (Some(prefix), Some(suffix)) => {
866 prefix.as_ref().to_owned() + &index.to_string() + suffix.as_ref()
867 }
868 (Some(prefix), None) => prefix.as_ref().to_owned() + &index.to_string(),
869 (None, Some(suffix)) => index.to_string() + suffix.as_ref(),
870 (None, None) => index.to_string(),
871 };
872 directory.as_ref().join(filename)
873}