SHOGUN  6.1.3
progress.h
Go to the documentation of this file.
1 /*
2 * BSD 3-Clause License
3 *
4 * Copyright (c) 2017, Shogun-Toolbox e.V. <shogun-team@shogun-toolbox.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * * Redistributions of source code must retain the above copyright notice, this
11 * list of conditions and the following disclaimer.
12 *
13 * * Redistributions in binary form must reproduce the above copyright notice,
14 * this list of conditions and the following disclaimer in the documentation
15 * and/or other materials provided with the distribution.
16 *
17 * * Neither the name of the copyright holder nor the names of its
18 * contributors may be used to endorse or promote products derived from
19 * this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 * Written (W) 2017 Giovanni De Toni
33 *
34 */
35 
36 #ifndef __SG_PROGRESS_H__
37 #define __SG_PROGRESS_H__
38 
39 #include <functional>
40 #include <iterator>
41 #include <memory>
42 #include <string>
43 
44 #include <shogun/base/init.h>
45 #include <shogun/base/range.h>
46 #include <shogun/io/SGIO.h>
47 #include <shogun/lib/Lock.h>
48 #include <shogun/lib/Time.h>
50 
51 #ifdef WIN32
52 #include <windows.h>
53 #else
54 #include <sys/ioctl.h>
55 #include <unistd.h>
56 
57 #endif
58 
59 namespace shogun
60 {
61 
64  {
67  };
68 
73  {
74  public:
84  const SGIO& io, float64_t max_value, float64_t min_value,
85  const std::string& prefix, const SG_PRG_MODE mode)
86  : m_io(io), m_max_value(max_value), m_min_value(min_value),
87  m_prefix(prefix), m_mode(mode), m_last_progress(0),
88  m_last_progress_time(0),
89  m_progress_start_time(CTime::get_curtime()),
90  m_current_value(min_value)
91  {
92  }
94  {
95  }
96 
103  void print_progress() const
104  {
105  lock.lock();
106  if (m_current_value.load() - m_min_value >
107  m_max_value - m_min_value)
108  {
109  increment();
110  lock.unlock();
111  return;
112  }
113  print_progress_impl();
114  if (m_current_value.load() - m_min_value ==
115  m_max_value - m_min_value)
116  {
117  print_end();
118  increment();
119  lock.unlock();
120  return;
121  }
122  increment();
123  lock.unlock();
124  }
125 
127  float64_t current_val, float64_t val, float64_t min_val,
128  float64_t max_val)
129  {
130  lock.lock();
131  if (val - m_min_value > m_max_value - m_min_value)
132  {
133  lock.unlock();
134  return;
135  }
136  print_progress_absolute_impl(current_val, val, min_val, max_val);
137  if (val - m_min_value == m_max_value - m_min_value)
138  {
139  print_end();
140  lock.unlock();
141  return;
142  }
143  lock.unlock();
144  }
145 
151  {
152  if (m_current_value.load() < m_max_value - 1)
153  m_current_value.store(m_max_value);
154  }
155 
158  {
159  return m_current_value.load();
160  }
161 
162  private:
166  void print_progress_impl() const
167  {
168 
169  // Check if the progress was enabled
170  if (!m_io.get_show_progress())
171  return;
172 
173  if (m_max_value <= m_min_value)
174  return;
175 
176  // Check for terminal dimension. This is for provide
177  // a minimal resize functionality.
178  set_screen_size();
179 
180  float64_t difference = m_max_value - m_min_value, v = -1,
181  estimate = 0, total_estimate = 0;
182  float64_t size_chunk = -1;
183 
184  // Check if we have enough space to show the progress bar
185  // Use only a fraction of it to account for the size of the
186  // time displayed (decimals and integer).
187  int32_t progress_bar_space =
188  (m_columns_num - 50 - m_prefix.length()) * 0.9;
189 
190  // TODO: this guy here brokes testing
191  // REQUIRE(
192  // progress_bar_space > 0,
193  // "Not enough terminal space to show the progress bar!\n")
194 
195  char str[1000];
196  float64_t runtime = CTime::get_curtime();
197 
198  if (difference > 0.0)
199  v = 100 * (m_current_value.load() - m_min_value) /
200  (m_max_value - m_min_value);
201 
202  // Set up chunk size
203  size_chunk = difference / (float64_t)progress_bar_space;
204 
205  if (m_last_progress == 0)
206  {
207  m_last_progress_time = runtime;
208  m_last_progress = v;
209  }
210  else
211  {
212  m_last_progress = v - 1e-6;
213 
214  if ((v != 100.0) && (runtime - m_last_progress_time < 0.5))
215  return;
216 
217  m_last_progress_time = runtime;
218  estimate = (1 - v / 100) *
219  (m_last_progress_time - m_progress_start_time) /
220  (v / 100);
221  total_estimate =
222  (m_last_progress_time - m_progress_start_time) / (v / 100);
223  }
224 
226  m_io.message(MSG_MESSAGEONLY, "", "", -1, "%s |", m_prefix.c_str());
227  for (index_t i = 1; i < progress_bar_space; i++)
228  {
229  if (m_current_value.load() - m_min_value > i * size_chunk)
230  {
231  m_io.message(
232  MSG_MESSAGEONLY, "", "", -1, "%s",
233  get_pb_char().c_str());
234  }
235  else
236  {
237  m_io.message(MSG_MESSAGEONLY, "", "", -1, " ");
238  }
239  }
240  m_io.message(MSG_MESSAGEONLY, "", "", -1, "| %.2f\%", v);
241 
242  if (estimate > 120)
243  {
244  snprintf(
245  str, sizeof(str),
246  " %%1.1f minutes remaining %%1.1f minutes total\r");
247  m_io.message(
248  MSG_MESSAGEONLY, "", "", -1, str, estimate / 60,
249  total_estimate / 60);
250  }
251  else
252  {
253  snprintf(
254  str, sizeof(str),
255  " %%1.1f seconds remaining %%1.1f seconds total\r");
256  m_io.message(
257  MSG_MESSAGEONLY, "", "", -1, str, estimate, total_estimate);
258  }
259  }
260 
264  void print_progress_absolute_impl(
265  float64_t current_val, float64_t val, float64_t min_value,
266  float64_t max_value) const
267  {
268  // Check if the progress was enabled
269  if (!m_io.get_show_progress())
270  return;
271 
272  m_current_value.store(current_val);
273 
274  if (max_value <= min_value)
275  return;
276 
277  // Check for terminal dimension. This is for provide
278  // a minimal resize functionality.
279  set_screen_size();
280 
281  float64_t difference = max_value - min_value, v = -1, estimate = 0,
282  total_estimate = 0;
283  float64_t size_chunk = -1;
284 
285  // Check if we have enough space to show the progress bar
286  // Use only a fraction of it to account for the size of the
287  // time displayed (decimals and integer).
288  int32_t progress_bar_space =
289  (m_columns_num - 50 - m_prefix.length()) * 0.9;
290 
291  // TODO: this guy here brokes testing
292  // REQUIRE(
293  // progress_bar_space > 0,
294  // "Not enough terminal space to show the progress bar!\n")
295 
296  char str[1000];
297  float64_t runtime = CTime::get_curtime();
298 
299  if (difference > 0.0)
300  v = 100 * (val - min_value) / (max_value - min_value);
301 
302  // Set up chunk size
303  size_chunk = difference / (float64_t)progress_bar_space;
304 
305  if (m_last_progress == 0)
306  {
307  m_last_progress_time = runtime;
308  m_last_progress = v;
309  }
310  else
311  {
312  m_last_progress = v - 1e-6;
313 
314  if ((v != 100.0) && (runtime - m_last_progress_time < 0.5))
315  return;
316 
317  m_last_progress_time = runtime;
318  estimate = (1 - v / 100) *
319  (m_last_progress_time - m_progress_start_time) /
320  (v / 100);
321  total_estimate =
322  (m_last_progress_time - m_progress_start_time) / (v / 100);
323  }
324 
326  m_io.message(MSG_MESSAGEONLY, "", "", -1, "%s |", m_prefix.c_str());
327  for (index_t i = 1; i < progress_bar_space; i++)
328  {
329  if (m_current_value.load() - min_value > i * size_chunk)
330  {
331  m_io.message(
332  MSG_MESSAGEONLY, "", "", -1, "%s",
333  get_pb_char().c_str());
334  }
335  else
336  {
337  m_io.message(MSG_MESSAGEONLY, "", "", -1, " ");
338  }
339  }
340  m_io.message(MSG_MESSAGEONLY, "", "", -1, "| %.2f\%", current_val);
341 
342  if (estimate > 120)
343  {
344  snprintf(
345  str, sizeof(str),
346  " %%1.1f minutes remaining %%1.1f minutes total\r");
347  m_io.message(
348  MSG_MESSAGEONLY, "", "", -1, str, estimate / 60,
349  total_estimate / 60);
350  }
351  else
352  {
353  snprintf(
354  str, sizeof(str),
355  " %%1.1f seconds remaining %%1.1f seconds total\r");
356  m_io.message(
357  MSG_MESSAGEONLY, "", "", -1, str, estimate, total_estimate);
358  }
359  }
360 
362  void print_end() const
363  {
364  // Check if the progress was enabled
365  if (!m_io.get_show_progress())
366  return;
367 
368  m_io.message(MSG_MESSAGEONLY, "", "", -1, "\n");
369  }
370 
375  std::string get_pb_char() const
376  {
377  switch (m_mode)
378  {
379  case ASCII:
380  return m_ascii_char;
381  case UTF8:
382  return m_utf8_char;
383  default:
384  return m_ascii_char;
385  }
386  }
387 
391  void set_screen_size() const
392  {
393 #ifdef WIN32
394  CONSOLE_SCREEN_BUFFER_INFO csbi;
395  GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
396  m_columns_num = csbi.srWindow.Right - csbi.srWindow.Left + 1;
397  m_rows_num = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
398 #else
399  struct winsize wind;
400  ioctl(STDOUT_FILENO, TIOCGWINSZ, &wind);
401  m_columns_num = wind.ws_col;
402  m_rows_num = wind.ws_row;
403 #endif
404  }
405 
406  /* Increment the current value (atomically) */
407  void increment() const
408  {
409  m_current_value++;
410  }
411 
413  SGIO m_io;
415  float64_t m_max_value;
417  float64_t m_min_value;
419  std::string m_prefix;
421  SG_PRG_MODE m_mode;
423  std::string m_ascii_char = "#";
425  std::string m_utf8_char = "\u2588";
426  /* Screen column number*/
427  mutable int32_t m_columns_num;
428  /* Screen row number*/
429  mutable int32_t m_rows_num;
431  mutable float64_t m_last_progress;
433  mutable float64_t m_last_progress_time;
435  mutable float64_t m_progress_start_time;
437  mutable std::atomic<int64_t> m_current_value;
439  mutable CLock lock;
440  };
441 
448  template <typename T>
449  class PRange
450  {
451  public:
465  Range<T> range, const SGIO& io, const std::string prefix,
466  const SG_PRG_MODE mode, std::function<bool()> condition)
467  : m_range(range), m_condition(condition)
468  {
469  set_up_range();
470  m_printer = std::make_shared<ProgressPrinter>(
471  io, m_end_range, m_begin_range, prefix, mode);
472  }
473 
475  class PIterator : public std::iterator<std::input_iterator_tag, T>
476  {
477  public:
485  typename Range<T>::Iterator value,
486  std::shared_ptr<ProgressPrinter> shrd_ptr,
487  std::function<bool()> condition)
488  : m_value(value), m_printer(shrd_ptr), m_condition(condition)
489  {
490  }
491  PIterator(const PIterator& other)
492  : m_value(other.m_value), m_printer(other.m_printer),
493  m_condition(other.m_condition)
494  {
495  }
497  : m_value(other.m_value), m_printer(other.m_printer),
498  m_condition(other.m_condition)
499  {
500  }
501  PIterator& operator=(const PIterator&) = delete;
503  {
504  // Every time we update the iterator we print
505  // also the updated progress bar
506  m_printer->print_progress();
507  m_value++;
508  return *this;
509  }
511  {
512  PIterator tmp(*this);
513  ++*this;
514  return tmp;
515  }
517  {
518  // Since PIterator is a wrapper we have
519  // to return the actual value of the
520  // wrapped iterator
521  return *m_value;
522  }
523  bool operator!=(const PIterator& other)
524  {
525  if (!(this->m_value != other.m_value))
526  {
527  m_printer->premature_end();
528  m_printer->print_progress();
529  return false;
530  }
531  bool result = evaluate_condition();
532  return (this->m_value != other.m_value) && result;
533  }
534 
535  private:
540  bool evaluate_condition()
541  {
542  if (!m_condition())
543  {
544  m_printer->premature_end();
545  m_printer->print_progress();
546  }
547  return m_condition();
548  }
549 
550  /* The wrapped range */
551  typename Range<T>::Iterator m_value;
552  /* The ProgressPrinter object which will be used to show the
553  * progress bar*/
554  std::shared_ptr<ProgressPrinter> m_printer;
555  /* The function which will contain the custom condition
556  * to premature stop the loop */
557  std::function<bool()> m_condition;
558  };
559 
572  PIterator begin() const
573  {
574  return PIterator(m_range.begin(), m_printer, m_condition);
575  }
576 
589  PIterator end() const
590  {
591  return PIterator(m_range.end(), m_printer, m_condition);
592  }
593 
600  {
601  return m_printer->get_current_progress();
602  }
603 
617  void print_progress() const
618  {
619  m_printer->print_progress();
620  }
621 
632  float64_t current_val, float64_t val, float64_t min_value,
633  float64_t max_value) const
634  {
635  m_printer->print_progress_absolute(
636  current_val, val, min_value, max_value);
637  }
638 
652  void complete() const
653  {
654  m_printer->premature_end();
655  m_printer->print_progress();
656  }
657 
671  void complete_absolute() const
672  {
673  m_printer->print_progress_absolute(100, 100, 0, 100);
674  }
675 
676  private:
680  void set_up_range()
681  {
682  m_begin_range = *(m_range.begin());
683  m_end_range = *(m_range.end());
684  }
685 
687  Range<T> m_range;
689  std::shared_ptr<ProgressPrinter> m_printer;
690  /* Start of the range */
691  float64_t m_begin_range;
692  /* End of the range */
693  float64_t m_end_range;
694  /* Function which store the premature stop condition */
695  std::function<bool()> m_condition = []() { return true; };
696  };
697 
711  template <typename T>
713  Range<T> range, const SGIO& io, std::string prefix = "PROGRESS: ",
714  SG_PRG_MODE mode = UTF8,
715  std::function<bool()> condition = []() { return true; })
716  {
717  return PRange<T>(range, io, prefix, mode, condition);
718  }
719 
732  template <typename T>
734  Range<T> range, std::string prefix = "PROGRESS: ",
735  SG_PRG_MODE mode = UTF8,
736  std::function<bool()> condition = []() { return true; })
737  {
738  return PRange<T>(range, *sg_io, prefix, mode, condition);
739  }
740 };
741 #endif /* __SG_PROGRESS_H__ */
Class Time that implements a stopwatch based on either cpu time or wall clock time.
Definition: Time.h:42
PIterator(typename Range< T >::Iterator value, std::shared_ptr< ProgressPrinter > shrd_ptr, std::function< bool()> condition)
Definition: progress.h:484
float64_t get_current_progress() const
Definition: progress.h:157
int32_t index_t
Definition: common.h:72
PIterator begin() const
Definition: progress.h:572
PRange< T > progress(Range< T > range, const SGIO &io, std::string prefix="PROGRESS: ", SG_PRG_MODE mode=UTF8, std::function< bool()> condition=[](){return true;})
Definition: progress.h:712
PIterator & operator++()
Definition: progress.h:502
SG_PRG_MODE
Definition: progress.h:63
PRange(Range< T > range, const SGIO &io, const std::string prefix, const SG_PRG_MODE mode, std::function< bool()> condition)
Definition: progress.h:464
void print_progress_absolute(float64_t current_val, float64_t val, float64_t min_val, float64_t max_val)
Definition: progress.h:126
Range< T > range(T rend)
Definition: range.h:136
void complete_absolute() const
Definition: progress.h:671
PIterator end() const
Definition: progress.h:589
static float64_t get_curtime()
Definition: Time.h:116
void complete() const
Definition: progress.h:652
PIterator(PIterator &&other)
Definition: progress.h:496
float64_t get_current_progress() const
Definition: progress.h:599
SGIO * sg_io
Definition: init.cpp:44
Class Lock used for synchronization in concurrent programs.
Definition: Lock.h:19
void message(EMessageType prio, const char *function, const char *file, int32_t line, const char *fmt,...) const
Definition: SGIO.cpp:71
double float64_t
Definition: common.h:60
void print_progress() const
Definition: progress.h:617
SG_FORCED_INLINE void lock()
Definition: Lock.h:23
void print_progress() const
Definition: progress.h:103
bool get_show_progress() const
Definition: SGIO.h:255
all of classes and functions are contained in the shogun namespace
Definition: class_list.h:18
PIterator(const PIterator &other)
Definition: progress.h:491
ProgressPrinter(const SGIO &io, float64_t max_value, float64_t min_value, const std::string &prefix, const SG_PRG_MODE mode)
Definition: progress.h:83
SG_FORCED_INLINE void unlock()
Definition: Lock.h:34
void print_absolute(float64_t current_val, float64_t val, float64_t min_value, float64_t max_value) const
Definition: progress.h:631
Class SGIO, used to do input output operations throughout shogun.
Definition: SGIO.h:218
PIterator operator++(int)
Definition: progress.h:510
bool operator!=(const PIterator &other)
Definition: progress.h:523

SHOGUN Machine Learning Toolbox - Documentation