Caffe2 - C++ API
A deep learning, cross platform ML framework
tile_op.h
1 
17 #ifndef CAFFE2_OPERATORS_TILE_OP_H_
18 #define CAFFE2_OPERATORS_TILE_OP_H_
19 
20 #include "caffe2/core/common_omp.h"
21 #include "caffe2/core/context.h"
22 #include "caffe2/core/logging.h"
23 #include "caffe2/core/operator.h"
24 #include "caffe2/utils/math.h"
25 
26 namespace caffe2 {
27 
28 // Copy a Blob n times along a specified axis.
29 template <class Context>
30 class TileOp : public Operator<Context> {
31  public:
32  USE_OPERATOR_CONTEXT_FUNCTIONS;
33  TileOp(const OperatorDef& operator_def, Workspace* ws)
34  : Operator<Context>(operator_def, ws),
35  tiles_(OperatorBase::GetSingleArgument<int32_t>("tiles", 1)),
36  axis_(OperatorBase::GetSingleArgument<int32_t>("axis", 0)) {}
37  ~TileOp() {}
38 
39  bool RunOnDevice() override {
40  const auto& input = Input(0);
41  std::array<int32_t, 2> temp_params = {{tiles_, axis_}};
42  if (InputSize() > 1) {
43  // We potentially have tiles and/or axis specified as inputs
44  // as well. We will check for them in that order. In other words:
45  // InputSize() == 2: tiles is specified
46  // InputSize() == 3: tiles is specified and axis.
47  // Anything specified as input will override the arguments
48  CAFFE_ENFORCE(
49  Input(1).ndim() == 1 && Input(1).size() == 1,
50  "Input `tiles` should be a vector of size 1.");
51 
52  const auto& input1 = Input(1);
53  context_.template CopyItems<Context, CPUContext>(
54  input1.meta(),
55  1,
56  static_cast<const char*>(input1.raw_data()),
57  &(temp_params[0]));
58 
59  if (InputSize() > 2) {
60  CAFFE_ENFORCE(
61  Input(2).ndim() == 1 && Input(2).size() == 1,
62  "Input `axis` should be a vector of size 1.");
63 
64  const auto& input2 = Input(2);
65  context_.template CopyItems<Context, CPUContext>(
66  input2.meta(),
67  1,
68  static_cast<const char*>(input2.raw_data()),
69  &(temp_params[1]));
70  } else {
71  CAFFE_ENFORCE(
73  "Argument `axis` is missing and was not specified as input.");
74  }
75  } else {
76  CAFFE_ENFORCE(
78  "Argument `tiles` is missing and was not specified as input.");
79  CAFFE_ENFORCE(
81  "Argument `axis` is missing and was not specified as input.");
82  }
83 
84  tiles_ = temp_params[0];
85  axis_ = temp_params[1];
86 
87  auto* output = Output(0);
88  const auto axis = input.canonical_axis_index(axis_);
89 
90  // reshape output to be input tiled along the axis
91  vector<TIndex> output_dims(input.dims());
92  output_dims[axis_] = output_dims[axis_] * tiles_;
93  output->Resize(output_dims);
94 
95  // size up to (and not including) axis
96  const auto outer_dim = input.size_to_dim(axis);
97  // size from axis up
98  const auto inner_dim = input.size_from_dim(axis);
99 
108  const char* input_data = static_cast<const char*>(input.raw_data());
109  char* output_data =
110  static_cast<char*>(output->raw_mutable_data(input.meta()));
111 
112  DoTile(
113  input.meta(),
114  input.itemsize(),
115  outer_dim,
116  inner_dim,
117  input_data,
118  output_data);
119 
120  return true;
121  }
122 
123  private:
124  void DoTile(
125  const TypeMeta& meta,
126  int item_size,
127  int outer_dim,
128  int inner_dim,
129  const char* input_data,
130  char* output_data) {
131  for (auto i = 0; i < outer_dim; ++i) {
132  for (auto t = 0; t < tiles_; ++t) {
133  context_.template CopyItems<Context, Context>(
134  meta, inner_dim, input_data, output_data);
135  output_data += inner_dim * item_size;
136  }
137  input_data += inner_dim * item_size;
138  }
139  }
140 
141  int32_t tiles_;
142  int32_t axis_;
143 };
144 
145 template <typename T, class Context>
146 class TileGradientOp : public Operator<Context> {
147  public:
148  USE_OPERATOR_CONTEXT_FUNCTIONS;
149  TileGradientOp(const OperatorDef& operator_def, Workspace* ws)
150  : Operator<Context>(operator_def, ws),
151  tiles_(OperatorBase::GetSingleArgument<int32_t>("tiles", 1)),
152  axis_(OperatorBase::GetSingleArgument<int32_t>("axis", 0)) {}
153  ~TileGradientOp() {}
154 
155  bool RunOnDevice() override {
156  std::array<int32_t, 2> temp_params = {{tiles_, axis_}};
157  if (InputSize() > 1) {
158  // We potentially have tiles and/or axis specified as inputs
159  // as well. We will check for them in that order. In other words:
160  // InputSize() == 2: tiles is specified
161  // InputSize() == 3: tiles is specified and axis.
162  // Anything specified as input will override the arguments
163  CAFFE_ENFORCE(
164  Input(1).ndim() == 1 && Input(1).size() == 1,
165  "Input `tiles` should be a vector of size 1.");
166 
167  const auto& input1 = Input(1);
168  context_.template CopyItems<Context, CPUContext>(
169  input1.meta(),
170  1,
171  static_cast<const char*>(input1.raw_data()),
172  &(temp_params[0]));
173 
174  if (InputSize() > 2) {
175  CAFFE_ENFORCE(
176  Input(2).ndim() == 1 && Input(2).size() == 1,
177  "Input `axis` should be a vector of size 1.");
178 
179  const auto& input2 = Input(2);
180  context_.template CopyItems<Context, CPUContext>(
181  input2.meta(),
182  1,
183  static_cast<const char*>(input2.raw_data()),
184  &(temp_params[1]));
185  } else {
186  CAFFE_ENFORCE(
188  "Argument `axis` is missing and was not specified as input.");
189  }
190  } else {
191  CAFFE_ENFORCE(
192  OperatorBase::HasArgument("tiles"),
193  "Argument `tiles` is missing and was not specified as input.");
194  CAFFE_ENFORCE(
196  "Argument `axis` is missing and was not specified as input.");
197  }
198 
199  tiles_ = temp_params[0];
200  axis_ = temp_params[1];
201 
202  const auto& input = Input(0);
203  auto* output = Output(0);
204  const auto axis = input.canonical_axis_index(axis_);
205 
206  // reshape output to be input "untiled" along the axis
207  vector<TIndex> output_dims(input.dims());
208  output_dims[axis_] = output_dims[axis_] / tiles_;
209  output->Resize(output_dims);
210 
211  // size up to (and not including) axis
212  const auto outer_dim = output->size_to_dim(axis);
213  // size from axis up
214  const auto inner_dim = output->size_from_dim(axis);
215 
226  const char* input_data = static_cast<const char*>(input.raw_data());
227  char* output_data =
228  static_cast<char*>(output->raw_mutable_data(input.meta()));
229 
230  DoTileGradient(
231  input.meta(),
232  input.itemsize(),
233  outer_dim,
234  inner_dim,
235  input_data,
236  output_data);
237 
238  return true;
239  }
240 
241  private:
242  void DoTileGradient(
243  const TypeMeta& meta,
244  int item_size,
245  int outer_dim,
246  int inner_dim,
247  const char* input_data,
248  char* output_data) {
249  for (auto i = 0; i < outer_dim; ++i) {
250  context_.template CopyItems<Context, Context>(
251  meta, inner_dim, input_data, output_data);
252  input_data += inner_dim * item_size;
253  for (auto t = 1; t < tiles_; ++t) {
254  math::Axpy<T, Context>(
255  inner_dim,
256  T(1),
257  reinterpret_cast<const T*>(input_data),
258  reinterpret_cast<T*>(output_data),
259  &context_);
260  input_data += inner_dim * item_size;
261  }
262  output_data += inner_dim * item_size;
263  }
264  }
265 
266  int32_t tiles_;
267  int32_t axis_;
268 };
269 
270 } // namespace caffe2
271 
272 #endif // CAFFE2_OPERATORS_TILE_OP_H_
Workspace is a class that holds all the related objects created during runtime: (1) all blobs...
Definition: workspace.h:63
bool RunOnDevice() override
Definition: tile_op.h:39
Copyright (c) 2016-present, Facebook, Inc.
TypeMeta is a thin class that allows us to store the type of a container such as a blob...
Definition: typeid.h:104
bool HasArgument(const string &name) const
Checks if the operator has an argument of the given name.
Definition: operator.h:52
bool RunOnDevice() override
Definition: tile_op.h:155