Turi Create  4.0
float_array.hpp
1 /* Copyright © 2018 Apple Inc. All rights reserved.
2  *
3  * Use of this source code is governed by a BSD-3-clause license that can
4  * be found in the LICENSE.txt file or at https://opensource.org/licenses/BSD-3-Clause
5  */
6 
7 #pragma once
8 
9 #include <cstddef>
10 #include <future>
11 #include <map>
12 #include <memory>
13 #include <ostream>
14 #include <string>
15 #include <vector>
16 
17 namespace turi {
18 namespace neural_net {
19 
20 // Pure virtual (but low-level) interface for an n-dimensional array. The inputs
21 // and outputs of the TCMPS library are largely expressed with this type.
22 class float_array {
23 public:
24  virtual ~float_array() = default;
25 
26  // Returns a pointer to the first float value in the data. This pointer is
27  // guaranteed to remain valid for the lifetime of this float_array instance.
28  // Note that for some implementations, calling this function may trigger
29  // synchronization with (waiting for) a thread writing the data.
30  virtual const float* data() const = 0;
31 
32  // Returns the total number of float values present, beginning at the pointer
33  // returned by data(). This number must equal the product of all the sizes in
34  // the shape array.
35  virtual size_t size() const = 0;
36 
37  // Returns a pointer to the first element of the shape array. This pointer is
38  // guaranteed to remain valid for the lifetime of this float_array instance.
39  virtual const size_t* shape() const = 0;
40 
41  // Returns the total number of elements in the array returned by shape().
42  virtual size_t dim() const = 0;
43 };
44 
45 // Wrapper around raw C pointers into an external n-dimensional array. Users
46 // must manually ensure that the external array outlives instances of this
47 // wrapper.
48 class external_float_array: public float_array {
49 public:
50  external_float_array(const float* data, size_t size, const size_t* shape,
51  size_t dim);
52 
53  explicit external_float_array(const float_array& array)
54  : external_float_array(array.data(), array.size(), array.shape(),
55  array.dim())
56  {}
57 
58  const float* data() const override { return data_; }
59  size_t size() const override { return size_; }
60 
61  const size_t* shape() const override { return shape_; }
62  size_t dim() const override { return dim_; }
63 
64 private:
65  const float* data_ = nullptr;
66  size_t size_ = 0;
67 
68  const size_t* shape_ = nullptr;
69  size_t dim_ = 0;
70 };
71 
72 // A float_array implementation that directly owns the memory containing the
73 // float data.
74 class float_buffer: public float_array {
75 public:
76  // Copies enough float values from `data` to fill the given `shape`.
77  float_buffer(const float* data, std::vector<size_t> shape);
78 
79  // Adopts an existing float vector `data`, which must have size consistent
80  // with the provided `shape`.
81  float_buffer(std::vector<float> data, std::vector<size_t> shape);
82 
83  // Copies another float_array.
84  float_buffer(const float_array& other)
85  : float_buffer(other.data(),
86  std::vector<size_t>(other.shape(),
87  other.shape() + other.dim()))
88  {}
89 
90  const float* data() const override { return data_.data(); }
91  size_t size() const override { return size_; }
92 
93  const size_t* shape() const override { return shape_.data(); }
94  size_t dim() const override { return shape_.size(); }
95 
96 private:
97  std::vector<size_t> shape_;
98  size_t size_ = 0;
99  std::vector<float> data_;
100 };
101 
102 // A float_array implementation that just wraps a single scalar value.
103 class float_scalar: public float_array {
104 public:
105  float_scalar() = default;
106 
107  float_scalar(float value): value_(value) {}
108 
109  const float* data() const override { return &value_; }
110  size_t size() const override { return 1; }
111 
112  const size_t* shape() const override { return nullptr; }
113  size_t dim() const override { return 0; }
114 
115 private:
116  float value_ = 0.f;
117 };
118 
119 // A float_array implementation that maintains a view into another float_array
120 // (that is possibly shared with others shared_float_array instances). Instances
121 // of this class can be efficiently copied (in constant time and incurring no
122 // additional allocations).
123 class shared_float_array: public float_array {
124 public:
125  // Convenience functions for creating a shared float_buffer.
126  static shared_float_array copy(const float* data, std::vector<size_t> shape) {
127  return shared_float_array(
128  std::make_shared<float_buffer>(data, std::move(shape)));
129  }
130  static shared_float_array copy(const float_array& other) {
131  return shared_float_array(std::make_shared<float_buffer>(other));
132  }
133  static shared_float_array wrap(std::vector<float> data,
134  std::vector<size_t> shape) {
135  return shared_float_array(
136  std::make_shared<float_buffer>(std::move(data), std::move(shape)));
137  }
138  static shared_float_array wrap(float value) {
139  return shared_float_array(std::make_shared<float_scalar>(value));
140  }
141 
142  // Simply wraps an existing float_array shared_ptr.
143  explicit shared_float_array(std::shared_ptr<float_array> impl)
144  : shared_float_array(impl, /* offset */ 0, impl->shape(), impl->dim())
145  {}
146 
147  // Creates an array containing the scalar 0.f.
148  shared_float_array(): shared_float_array(default_value()) {}
149 
150  const float* data() const override { return impl_->data() + offset_; }
151  size_t size() const override { return size_; }
152 
153  const size_t* shape() const override { return shape_; }
154  size_t dim() const override { return dim_; }
155 
156  // Returns the sub-array at the specified index in the first dimension.
157  shared_float_array operator[](size_t idx) const;
158 
159  // TODO: Operations such as reshape, slice, etc.?
160 
161 protected:
162  shared_float_array(std::shared_ptr<float_array> impl, size_t offset,
163  const size_t* shape, size_t dim);
164 
165 private:
166  static std::shared_ptr<float_array> default_value();
167 
168  std::shared_ptr<float_array> impl_;
169 
170  size_t offset_ = 0;
171  const size_t* shape_ = nullptr;
172  size_t dim_ = 0;
173  size_t size_ = 0;
174 };
175 
176 // A float_array implementation that wraps a future shared_float_array.
177 class deferred_float_array: public float_array {
178 public:
179  // Wraps `data_future`, which must have a (future) shape matching the provided
180  // (known upfront) `shape`.
181  deferred_float_array(std::shared_future<shared_float_array> data_future,
182  std::vector<size_t> shape);
183 
184  // Wraps an existing (not deferred) float_array
185  deferred_float_array(shared_float_array params);
186 
187  // Waits for the data future if necessary.
188  const float* data() const override;
189 
190  // The size and shape of the array are known at construction time.
191  size_t size() const override { return size_; }
192  const size_t* shape() const override { return shape_.data(); }
193  size_t dim() const override { return shape_.size(); }
194 
195 private:
196  std::shared_future<shared_float_array> data_future_;
197  std::vector<size_t> shape_;
198  size_t size_ = 0;
199 };
200 
201 // Convenient typedef for data structure used to pass configuration and weights.
202 using float_array_map = std::map<std::string, shared_float_array>;
203 
204 std::ostream &operator<<(std::ostream &out, const float_array &arr);
205 
206 } // namespace neural_net
207 } // namespace turi
STL namespace.
void copy(Iterator begin, Iterator end, SWriter &&writer)
Definition: algorithm.hpp:416