Caffe2 - C++ API
A deep learning, cross platform ML framework
operator_schema.h
1 #ifndef CAFFE2_CORE_OPERATOR_SCHEMA_H_
2 #define CAFFE2_CORE_OPERATOR_SCHEMA_H_
3 
4 #include <climits>
5 #include <functional>
6 #include <initializer_list>
7 #include <ostream>
8 #include <set>
9 #include <vector>
10 
11 #include "caffe2/core/common.h"
12 #include "caffe2/core/registry.h"
13 #include "caffe2/proto/caffe2.pb.h"
14 
15 namespace caffe2 {
16 
17 // A const value returned by OpSchema::CalculateOutput() if the number of
18 // output cannot be determined.
19 constexpr int kCannotComputeNumOutputs = -1;
20 
36 class OpSchema {
37  public:
38  OpSchema() : file_("unknown"), line_(0) {}
39  OpSchema(const string& file, const int line)
40  : file_(file), line_(line) {}
41 
45  inline const string& file() const { return file_; }
46 
50  inline int line() const { return line_; }
51 
55  inline const char* doc() const {
56  return doc_.empty() ? nullptr : doc_.c_str();
57  }
58 
63  bool Verify(const OperatorDef& def) const;
64 
65  // Functions to set the property of the operator schemas.
66  // Sets the number of inputs, either a fixed number or a min and a max.
67 
71  OpSchema& NumInputs(int n);
75  OpSchema& NumInputs(int min, int max);
79  OpSchema& NumInputs(set<int> allowed_input_nums);
83  OpSchema& NumInputs(std::function<bool(int)> func);
84 
85  // Sets the number of outputs, either a fixed number, a min and a max,
86  // or a function that takes in the input number and produces an output
87  // number. Use only one function in the set below.
91  OpSchema& NumOutputs(int n);
95  OpSchema& NumOutputs(int min, int max);
99  OpSchema& NumOutputs(set<int> allowed_output_nums);
103  OpSchema& NumOutputs(std::function<bool(int)> func);
104 
109  OpSchema& NumInputsOutputs(std::function<bool(int, int)> func);
110 
111  // Set the function that can calculate the number of output based on the
112  // number of input. Use only one function in the set below.
116  OpSchema& OutputCalculator(std::function<int(int)> calc);
121 
122  // Sets the rule to allow optional in-place operation.
123  OpSchema& AllowInplace(std::function<bool(int, int)> inplace);
124  OpSchema& AllowInplace(set<std::pair<int, int>> inplace);
125  OpSchema& AllowOneToOneInplace();
126  // Sets the rule to enforce in-place opeartion.
127  OpSchema& EnforceInplace(std::function<bool(int, int)> inplace);
128  OpSchema& EnforceInplace(set<std::pair<int, int>> inplace);
129  OpSchema& EnforceOneToOneInplace();
130 
131  // Functions to deal with type and shape inference. Basically, this registers
132  // a function that takes in an OperatorDef and a series of input type and
133  // shape specified by TensorProto objects (whose data fields are empty), and
134  // produces a series of output type and shape.
135  typedef std::function<
136  vector<TensorShape>(const OperatorDef&, const vector<TensorShape>&)>
137  TensorInferenceFunctionType;
142  OpSchema& TensorInferenceFunction(TensorInferenceFunctionType function);
148  OpSchema& IdenticalTypeAndShapeOfInput(int idx);
149  OpSchema& IdenticalTypeAndShapeOfInputDim(int idx, int dim);
150  OpSchema& ScalarType(::caffe2::TensorProto_DataType dt);
151 
156  inline vector<TensorShape> InferTensor(
157  const OperatorDef& def,
158  const vector<TensorShape> input_type_shape) const {
159  return tensor_inference_function_(def, input_type_shape);
160  }
161 
162  // Functions to do documentation for the operator schema.
163  OpSchema& SetDoc(const string& doc);
164  OpSchema& Arg(const char* name, const char* description);
165  OpSchema& Input(const int n, const char* name, const char* description);
166  OpSchema& Output(const int n, const char* name, const char* description);
167  // Calls the passed function with `this` as an argument. Useful for
168  // adding docs for temlated/macro ops.
169  OpSchema& FillUsing(std::function<void(OpSchema&)> populator);
170 
175  int CalculateOutput(int num_input) const;
176 
177  friend std::ostream& operator<<(std::ostream& out, const OpSchema& schema);
178 
179  const std::vector<std::pair<const char*, const char*>>& arg_desc() {
180  return arg_desc_;
181  }
182  const std::vector<std::pair<const char*, const char*>>& input_desc() {
183  return input_desc_;
184  }
185  const std::vector<std::pair<const char*, const char*>>& output_desc() {
186  return output_desc_;
187  }
188 
189  private:
190  string file_;
191  string doc_;
192  std::vector<std::pair<const char*, const char*>> arg_desc_{};
193  std::vector<std::pair<const char*, const char*>> input_desc_{};
194  std::vector<std::pair<const char*, const char*>> output_desc_{};
195  int line_ = 0;
196  int min_input_ = 0;
197  int max_input_ = std::numeric_limits<int>::max();
198  int min_output_ = 0;
199  int max_output_ = std::numeric_limits<int>::max();
200  std::function<bool(int)> num_inputs_allowed_
201  = [](int) { return true; };
202  std::function<bool(int)> num_outputs_allowed_
203  = [](int) { return true; };
204  std::function<bool(int, int)> num_inputs_outputs_allowed_
205  = [](int, int) { return true; };
206  std::function<int(int)> calculate_output_;
207  // In default, any in-place operation is neither allowed nor enforced.
208  std::function<bool(int, int)> inplace_allowed_
209  = [](int, int) { return false; };
210  std::function<bool(int, int)> inplace_enforced_
211  = [](int, int) { return false; };
212  TensorInferenceFunctionType tensor_inference_function_ =
213  [](const OperatorDef& def, const vector<TensorShape>&) {
214  vector<TensorShape> out;
215  for(int i=0; i<def.output_size(); i++) {
216  TensorShape ts;
217  ts.set_unknown_shape(true);
218  out.push_back(ts);
219  }
220  return out;
221  };
222 };
223 
228  public:
229  static OpSchema& NewSchema(
230  const string& key, const string& file, const int line) {
231  auto& m = map();
232  if (m.count(key)) {
233  const auto& schema = m[key];
234  std::cerr << "Trying to register schema with name "
235  << key << " from file " << file << " line " << line
236  << ", but it is already registered from file "
237  << schema.file() << " line " << schema.line();
238  abort();
239  }
240  m.emplace(std::make_pair(key, OpSchema(file, line)));
241  return m[key];
242  }
243 
244  static const OpSchema* Schema(const string& key) {
245  auto& m = map();
246  if (m.count(key)) {
247  return &m[key];
248  } else {
249  return nullptr;
250  }
251  }
252 
253  private:
254  // OpSchemaRegistry should not need to be instantiated.
255  OpSchemaRegistry() = delete;
256 
267  static CaffeMap<string, OpSchema>& map();
268 };
269 
270 // Helper function for creating simple tensorproto with dimension and type
271 inline TensorShape CreateTensorShape(
272  vector<int> dims,
273  ::caffe2::TensorProto_DataType dt) {
274  TensorShape ts;
275  for (int d : dims) {
276  ts.add_dims(d);
277  }
278  ts.set_data_type(dt);
279  return ts;
280 }
281 
282 // Helper function
283 inline vector<TIndex> GetDimsVector(const TensorShape& shape) {
284  vector<TIndex> dims;
285  for (auto d : shape.dims()) {
286  dims.push_back(d);
287  }
288  return dims;
289 }
290 
291 } // namespace caffe2
292 
293 #define OPERATOR_SCHEMA(name) \
294  static OpSchema& CAFFE_ANONYMOUS_VARIABLE(name) = \
295  OpSchemaRegistry::NewSchema(#name, __FILE__, __LINE__)
296 #define OPERATOR_SCHEMA_STR(name) \
297  static OpSchema& CAFFE_ANONYMOUS_VARIABLE(schema_registration) = \
298  OpSchemaRegistry::NewSchema(name, __FILE__, __LINE__)
299 
300 #endif // CAFFE2_CORE_OPERATOR_SCHEMA_H_
OpSchema & NumInputs(int n)
A single input.
A registry to hold all the operator schemas.
vector< TensorShape > InferTensor(const OperatorDef &def, const vector< TensorShape > input_type_shape) const
A function to allow one to infer the type and shape from the op schema.
OpSchema & NumInputsOutputs(std::function< bool(int, int)> func)
Relationship between inputs and outputs is checked with a specified function.
OpSchema & OutputCalculator(std::function< int(int)> calc)
Set the output calculator to a user-defined function.
bool Verify(const OperatorDef &def) const
Verifies if an operator definition protobuf matches the pattern specified in the schema.
Simple registry implementation in Caffe2 that uses static variables to register object creators durin...
int CalculateOutput(int num_input) const
A function to allow one to get the number of outputs based on the number of inputs, if this schema supports it.
OpSchema & SameNumberOfOutput()
Set the number of outputs to be the same as the number of inputs.
OpSchema & TensorInferenceFunction(TensorInferenceFunctionType function)
Sets the tensor inference function, which is a std::function object defined in operator_schema.h.
OpSchema & NumOutputs(int n)
A single output.
const char * doc() const
Returns the docstring of the op schema.
int line() const
Returns the line in file that the op schema is registered from.
A class to record the schema of an op.
OpSchema & IdenticalTypeAndShape()
Seets the tensor inference function to produce the same output as the input.
const string & file() const
Returns the file that the op schema is registered from.