util.hpp
1 //*****************************************************************************
2 // Copyright 2017-2021 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //*****************************************************************************
16 
17 #pragma once
18 
19 #include <algorithm>
20 #include <chrono>
21 #include <cmath>
22 #include <cstdlib> // llvm 8.1 gets confused about `malloc` otherwise
23 #include <functional>
24 #include <iostream>
25 #include <map>
26 #include <memory>
27 #include <sstream>
28 #include <string>
29 #include <typeindex>
30 #include <typeinfo>
31 #include <unordered_map>
32 #include <vector>
33 
34 #include "ngraph/axis_vector.hpp"
35 #include "ngraph/graph_util.hpp"
36 #include "ngraph/node.hpp"
37 #include "ngraph/runtime/host_tensor.hpp"
38 #include "ngraph/runtime/tensor.hpp"
39 #include "ngraph/shape.hpp"
40 
41 namespace ngraph
42 {
43  class Node;
44  class Function;
45  class stopwatch;
46 
47  namespace runtime
48  {
49  class Backend;
50  class Value;
51  class Tensor;
52  } // namespace runtime
53 
54  template <typename T>
55  std::string join(const T& v, const std::string& sep = ", ")
56  {
57  std::ostringstream ss;
58  size_t count = 0;
59  for (const auto& x : v)
60  {
61  if (count++ > 0)
62  {
63  ss << sep;
64  }
65  ss << x;
66  }
67  return ss.str();
68  }
69 
70  template <typename T>
71  std::string vector_to_string(const T& v)
72  {
73  std::ostringstream os;
74  os << "[ " << ngraph::join(v) << " ]";
75  return os.str();
76  }
77 
78  NGRAPH_API
79  size_t hash_combine(const std::vector<size_t>& list);
80  NGRAPH_API
81  void dump(std::ostream& out, const void*, size_t);
82  NGRAPH_API
83  std::string to_lower(const std::string& s);
84  NGRAPH_API
85  std::string to_upper(const std::string& s);
86  NGRAPH_API
87  std::string trim(const std::string& s);
88  NGRAPH_API
89  std::vector<std::string> split(const std::string& s, char delimiter, bool trim = false);
90 
91  template <typename T>
92  std::string locale_string(T x)
93  {
94  std::stringstream ss;
95  ss.imbue(std::locale(""));
96  ss << x;
97  return ss.str();
98  }
99 
100  class NGRAPH_API stopwatch
101  {
102  public:
103  void start()
104  {
105  if (m_active == false)
106  {
107  m_total_count++;
108  m_active = true;
109  m_start_time = m_clock.now();
110  }
111  }
112 
113  void stop()
114  {
115  if (m_active == true)
116  {
117  auto end_time = m_clock.now();
118  m_last_time = end_time - m_start_time;
119  m_total_time += m_last_time;
120  m_active = false;
121  }
122  }
123 
124  size_t get_call_count() const;
125  size_t get_seconds() const;
126  size_t get_milliseconds() const;
127  size_t get_microseconds() const;
128  std::chrono::nanoseconds get_timer_value() const;
129  size_t get_nanoseconds() const;
130 
131  size_t get_total_seconds() const;
132  size_t get_total_milliseconds() const;
133  size_t get_total_microseconds() const;
134  size_t get_total_nanoseconds() const;
135 
136  private:
137  std::chrono::high_resolution_clock m_clock;
138  std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time;
139  bool m_active = false;
140  std::chrono::nanoseconds m_total_time =
141  std::chrono::high_resolution_clock::duration::zero();
142  std::chrono::nanoseconds m_last_time = std::chrono::high_resolution_clock::duration::zero();
143  size_t m_total_count = 0;
144  };
145 
146  /// Parses a string containing a literal of the underlying type.
147  template <typename T>
148  T parse_string(const std::string& s)
149  {
150  T result;
151  std::stringstream ss;
152 
153  ss << s;
154  ss >> result;
155 
156  // Check that (1) parsing succeeded and (2) the entire string was used.
157  if (ss.fail() || ss.rdbuf()->in_avail() != 0)
158  {
159  throw std::runtime_error("Could not parse literal '" + s + "'");
160  }
161 
162  return result;
163  }
164 
165  /// template specializations for float and double to handle INFINITY, -INFINITY
166  /// and NaN values.
167  template <>
168  NGRAPH_API float parse_string<float>(const std::string& s);
169  template <>
170  NGRAPH_API double parse_string<double>(const std::string& s);
171 
172  /// template specializations for int8_t and uint8_t to handle the fact that default
173  /// implementation ends up treating values as characters so that the number "0" turns into
174  /// the parsed value 48, which is it's ASCII value
175  template <>
176  NGRAPH_API int8_t parse_string<int8_t>(const std::string& s);
177  template <>
178  NGRAPH_API uint8_t parse_string<uint8_t>(const std::string& s);
179 
180  /// Parses a list of strings containing literals of the underlying type.
181  template <typename T>
182  std::vector<T> parse_string(const std::vector<std::string>& ss)
183  {
184  std::vector<T> result(ss.size());
185  std::transform(ss.begin(), ss.end(), result.begin(), [](const std::string& s) {
186  return parse_string<T>(s);
187  });
188  return result;
189  }
190 
191  template <typename T>
192  T ceil_div(const T& x, const T& y)
193  {
194  return (x == 0 ? 0 : (1 + (x - 1) / y));
195  }
196 
197  template <typename T>
198  T subtract_or_zero(T x, T y)
199  {
200  return y > x ? 0 : x - y;
201  }
202 
203  NGRAPH_API
204  void* ngraph_malloc(size_t size);
205  NGRAPH_API
206  void ngraph_free(void*);
207 
208  NGRAPH_API
209  size_t round_up(size_t size, size_t alignment);
210  bool is_valid_permutation(ngraph::AxisVector permutation, ngraph::Rank rank = Rank::dynamic());
211  template <typename T>
212  T apply_permutation(T input, ngraph::AxisVector order);
213 
214  extern template NGRAPH_API AxisVector apply_permutation<AxisVector>(AxisVector input,
215  AxisVector order);
216 
217  extern template NGRAPH_API Coordinate apply_permutation<Coordinate>(Coordinate input,
218  AxisVector order);
219 
220  extern template NGRAPH_API Strides apply_permutation<Strides>(Strides input, AxisVector order);
221 
222  extern template NGRAPH_API Shape apply_permutation<Shape>(Shape input, AxisVector order);
223 
224  template <>
225  NGRAPH_API PartialShape apply_permutation(PartialShape input, AxisVector order);
226 
227  NGRAPH_API
228  AxisVector get_default_order(size_t rank);
229 
230  NGRAPH_API
231  AxisVector get_default_order(const Shape& shape);
232 
233  //
234  // EnumMask is intended to work with a scoped enum type. It's used to store
235  // a combination of enum values and provides easy access and manipulation
236  // of these enum values as a mask.
237  //
238  // EnumMask does not provide a set_all() or invert() operator because they
239  // could do things unexpected by the user, i.e. for enum with 4 bit values,
240  // invert(001000...) != 110100..., due to the extra bits.
241  //
242  template <typename T>
243  class EnumMask
244  {
245  public:
246  /// Make sure the template type is an enum.
247  static_assert(std::is_enum<T>::value, "EnumMask template type must be an enum");
248  /// Extract the underlying type of the enum.
249  typedef typename std::underlying_type<T>::type value_type;
250  /// Some bit operations are not safe for signed values, we require enum
251  /// type to use unsigned underlying type.
252  static_assert(std::is_unsigned<value_type>::value, "EnumMask enum must use unsigned type.");
253 
254  constexpr EnumMask()
255  : m_value{0}
256  {
257  }
258  constexpr EnumMask(const T& enum_value)
259  : m_value{static_cast<value_type>(enum_value)}
260  {
261  }
262  EnumMask(const EnumMask& other)
263  : m_value{other.m_value}
264  {
265  }
266  EnumMask(std::initializer_list<T> enum_values)
267  : m_value{0}
268  {
269  for (auto& v : enum_values)
270  {
271  m_value |= static_cast<value_type>(v);
272  }
273  }
274  value_type value() const { return m_value; }
275  /// Check if any of the input parameter enum bit mask match
276  bool is_any_set(const EnumMask& p) const { return m_value & p.m_value; }
277  /// Check if all of the input parameter enum bit mask match
278  bool is_set(const EnumMask& p) const { return (m_value & p.m_value) == p.m_value; }
279  /// Check if any of the input parameter enum bit mask does not match
280  bool is_any_clear(const EnumMask& p) const { return !is_set(p); }
281  /// Check if all of the input parameter enum bit mask do not match
282  bool is_clear(const EnumMask& p) const { return !is_any_set(p); }
283  void set(const EnumMask& p) { m_value |= p.m_value; }
284  void clear(const EnumMask& p) { m_value &= ~p.m_value; }
285  void clear_all() { m_value = 0; }
286  bool operator[](const EnumMask& p) const { return is_set(p); }
287  bool operator==(const EnumMask& other) const { return m_value == other.m_value; }
288  bool operator!=(const EnumMask& other) const { return m_value != other.m_value; }
289  EnumMask& operator=(const EnumMask& other)
290  {
291  m_value = other.m_value;
292  return *this;
293  }
294  EnumMask& operator&=(const EnumMask& other)
295  {
296  m_value &= other.m_value;
297  return *this;
298  }
299 
300  EnumMask& operator|=(const EnumMask& other)
301  {
302  m_value |= other.m_value;
303  return *this;
304  }
305 
306  EnumMask operator&(const EnumMask& other) const
307  {
308  return EnumMask(m_value & other.m_value);
309  }
310 
311  EnumMask operator|(const EnumMask& other) const
312  {
313  return EnumMask(m_value | other.m_value);
314  }
315 
316  friend std::ostream& operator<<(std::ostream& os, const EnumMask& m)
317  {
318  os << m.m_value;
319  return os;
320  }
321 
322  private:
323  /// Only used internally
324  explicit EnumMask(const value_type& value)
325  : m_value{value}
326  {
327  }
328 
329  value_type m_value;
330  };
331 
332  /// \brief Function to query parsed version information of the version of ngraph which
333  /// contains this function. Version information strictly follows Semantic Versioning
334  /// http://semver.org
335  /// \param version The major part of the version
336  /// \param major Returns the major part of the version
337  /// \param minor Returns the minor part of the version
338  /// \param patch Returns the patch part of the version
339  /// \param extra Returns the extra part of the version. This includes everything following
340  /// the patch version number.
341  ///
342  /// \note Throws a runtime_error if there is an error during parsing
343  NGRAPH_API
345  std::string version, size_t& major, size_t& minor, size_t& patch, std::string& extra);
346 
347  template <typename T>
348  T double_to_int(double x, double float_to_int_converter(double))
349  {
350  if (!std::is_integral<T>())
351  {
352  throw std::runtime_error(
353  "Function double_to_int template parameter must be an integral type.");
354  }
355 
356  x = float_to_int_converter(x);
357 
358  double min_t = static_cast<double>(std::numeric_limits<T>::min());
359  if (x < min_t)
360  {
361  return std::numeric_limits<T>::min();
362  }
363 
364  double max_t = static_cast<double>(std::numeric_limits<T>::max());
365  if (x > max_t)
366  {
367  return std::numeric_limits<T>::max();
368  }
369 
370  return static_cast<T>(x);
371  }
372 } // end namespace ngraph
373 
374 template <typename T>
375 std::vector<T> read_vector(std::shared_ptr<ngraph::runtime::Tensor> tv)
376 {
377  if (ngraph::element::from<T>() != tv->get_element_type())
378  {
379  throw std::invalid_argument("read_vector type must match Tensor type");
380  }
381  size_t element_count = ngraph::shape_size(tv->get_shape());
382  size_t size = element_count * sizeof(T);
383  std::vector<T> rc(element_count);
384  tv->read(rc.data(), size);
385  return rc;
386 }
387 
388 template <typename T>
389 std::vector<T> host_tensor_2_vector(ngraph::HostTensorPtr tensor)
390 {
391  NGRAPH_CHECK(tensor != nullptr,
392  "Invalid Tensor received, can't read the data from a null pointer.");
393 
394  switch (tensor->get_element_type())
395  {
396  case ngraph::element::Type_t::boolean:
397  {
398  auto p = tensor->get_data_ptr<ngraph::element::Type_t::boolean>();
399  return std::vector<T>(p, p + tensor->get_element_count());
400  }
401  case ngraph::element::Type_t::bf16:
402  {
403  auto p = tensor->get_data_ptr<ngraph::element::Type_t::bf16>();
404  return std::vector<T>(p, p + tensor->get_element_count());
405  }
406  case ngraph::element::Type_t::f16:
407  {
408  auto p = tensor->get_data_ptr<ngraph::element::Type_t::f16>();
409  return std::vector<T>(p, p + tensor->get_element_count());
410  }
411  case ngraph::element::Type_t::f32:
412  {
413  auto p = tensor->get_data_ptr<ngraph::element::Type_t::f32>();
414  return std::vector<T>(p, p + tensor->get_element_count());
415  }
416  case ngraph::element::Type_t::f64:
417  {
418  auto p = tensor->get_data_ptr<ngraph::element::Type_t::f64>();
419  return std::vector<T>(p, p + tensor->get_element_count());
420  }
421  case ngraph::element::Type_t::i8:
422  {
423  auto p = tensor->get_data_ptr<ngraph::element::Type_t::i8>();
424  return std::vector<T>(p, p + tensor->get_element_count());
425  }
426  case ngraph::element::Type_t::i16:
427  {
428  auto p = tensor->get_data_ptr<ngraph::element::Type_t::i16>();
429  return std::vector<T>(p, p + tensor->get_element_count());
430  }
431  case ngraph::element::Type_t::i32:
432  {
433  auto p = tensor->get_data_ptr<ngraph::element::Type_t::i32>();
434  return std::vector<T>(p, p + tensor->get_element_count());
435  }
436  case ngraph::element::Type_t::i64:
437  {
438  auto p = tensor->get_data_ptr<ngraph::element::Type_t::i64>();
439  return std::vector<T>(p, p + tensor->get_element_count());
440  }
441  case ngraph::element::Type_t::u1: NGRAPH_CHECK(false, "u1 element type is unsupported"); break;
442  case ngraph::element::Type_t::u8:
443  {
444  auto p = tensor->get_data_ptr<ngraph::element::Type_t::u8>();
445  return std::vector<T>(p, p + tensor->get_element_count());
446  }
447  case ngraph::element::Type_t::u16:
448  {
449  auto p = tensor->get_data_ptr<ngraph::element::Type_t::u16>();
450  return std::vector<T>(p, p + tensor->get_element_count());
451  }
452  case ngraph::element::Type_t::u32:
453  {
454  auto p = tensor->get_data_ptr<ngraph::element::Type_t::u32>();
455  return std::vector<T>(p, p + tensor->get_element_count());
456  }
457  case ngraph::element::Type_t::u64:
458  {
459  auto p = tensor->get_data_ptr<ngraph::element::Type_t::u64>();
460  return std::vector<T>(p, p + tensor->get_element_count());
461  }
462  default: NGRAPH_UNREACHABLE("unsupported element type");
463  }
464 }
465 
466 std::vector<float> NGRAPH_API read_float_vector(std::shared_ptr<ngraph::runtime::Tensor> tv);
467 
468 std::vector<int64_t> NGRAPH_API read_index_vector(std::shared_ptr<ngraph::runtime::Tensor> tv);
469 
470 NGRAPH_API
471 std::ostream& operator<<(std::ostream& os, const ngraph::NodeVector& nv);
A vector of axes.
Definition: axis_vector.hpp:30
Class representing a dimension, which may be dynamic (undetermined until runtime),...
Definition: dimension.hpp:35
static Dimension dynamic()
Create a dynamic dimension.
Definition: dimension.hpp:130
Definition: util.hpp:244
bool is_clear(const EnumMask &p) const
Check if all of the input parameter enum bit mask do not match.
Definition: util.hpp:282
bool is_set(const EnumMask &p) const
Check if all of the input parameter enum bit mask match.
Definition: util.hpp:278
constexpr EnumMask()
Definition: util.hpp:254
bool is_any_clear(const EnumMask &p) const
Check if any of the input parameter enum bit mask does not match.
Definition: util.hpp:280
std::underlying_type< T >::type value_type
Make sure the template type is an enum.
Definition: util.hpp:247
bool is_any_set(const EnumMask &p) const
Check if any of the input parameter enum bit mask match.
Definition: util.hpp:276
Definition: util.hpp:101
The Intel nGraph C++ API.
Definition: attribute_adapter.hpp:28
NGRAPH_API void parse_version_string(std::string version, size_t &major, size_t &minor, size_t &patch, std::string &extra)
Function to query parsed version information of the version of ngraph which contains this function....
size_t shape_size(const SHAPE_TYPE &shape)
Number of elements in spanned by a shape.
Definition: shape.hpp:71
T parse_string(const std::string &s)
Parses a string containing a literal of the underlying type.
Definition: util.hpp:148
NGRAPH_API float parse_string< float >(const std::string &s)
NGRAPH_API int8_t parse_string< int8_t >(const std::string &s)