#ifndef GRAPH_INTERFACE_OP_SCHEMA_HPP
#define GRAPH_INTERFACE_OP_SCHEMA_HPP
#include <algorithm>
#include <functional>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include "common/rw_mutex.hpp"
#include "graph/interface/c_types_map.hpp"
#include "graph/interface/op.hpp"
#include "graph/utils/any.hpp"
#include "graph/utils/attribute_value.hpp"
namespace dnnl {
namespace impl {
namespace graph {
using opset_version = size_t;
using shape_infer_fn = std::function<status_t(op_t *,
std::vector<logical_tensor_t *> &, std::vector<logical_tensor_t *> &)>;
using op_def_constraint_fn = std::function<bool(const op_t *)>;
class op_schema_t {
public:
op_schema_t();
op_schema_t(op_kind_t op_name, opset_version version);
class op_parameter_t {
public:
op_parameter_t() = default;
explicit op_parameter_t(std::string &&name, std::string &&dtype_string)
: name_(std::move(name))
, dtype_string_(std::move(dtype_string))
, is_initialized(true) {}
std::string name_;
std::string dtype_string_;
bool is_initialized = false;
};
class attribute_t {
public:
attribute_t() = default;
attribute_t(op_attr_t name, bool required, attribute_kind_t attr_kind,
utils::attribute_value_t value,
std::vector<utils::attribute_value_t> &&candidates = {})
: name_(name)
, required_(false)
, has_default_value_(true)
, attr_kind_(attr_kind)
, attr_(std::move(value))
, candidates_(std::move(candidates)) {
assertm(!required,
"this attribute should be an optional attribute "
"since default value is provided");
UNUSED(required);
}
attribute_t(op_attr_t name, bool required, attribute_kind_t attr_kind,
std::vector<utils::attribute_value_t> &&candidates = {})
: name_(name)
, required_(required)
, has_default_value_(false)
, attr_kind_(attr_kind)
, candidates_(std::move(candidates)) {}
op_attr_t name_;
bool required_;
bool has_default_value_;
attribute_kind_t attr_kind_;
utils::attribute_value_t attr_;
std::vector<utils::attribute_value_t> candidates_;
};
enum class param_num_option { fixed, optional, variadic };
op_kind_t get_op_kind() const;
op_schema_t &set_op_kind(op_kind_t kind);
opset_version get_since_version() const;
op_schema_t &since_version(opset_version n);
op_schema_t &set_num_inputs(size_t input_num);
op_schema_t &set_num_inputs(std::set<size_t> &&input_num);
std::set<size_t> get_num_inputs() const;
op_schema_t &set_num_outputs(size_t output_num);
op_schema_t &set_num_outputs(std::set<size_t> &&output_num);
std::set<size_t> get_num_outputs() const;
op_schema_t &set_input(size_t in_offset, std::string &&in_name,
std::string &&dtype_string = "any");
op_schema_t &set_output(size_t out_offset, std::string &&out_name,
std::string &&dtype_string = "any");
op_schema_t &set_commutative_inputs(const std::set<size_t> &inputs);
bool is_commutative_inputs(const size_t input0, const size_t input1) const;
bool is_commutative_op() const;
op_schema_t &set_type_constraints(
std::string &&dtype_string, std::set<data_type_t> &&dtypes);
op_schema_t &set_attr(op_attr_t name, bool required,
attribute_kind_t attr_kind,
const std::vector<const char *> &candidates = {});
template <typename T>
op_schema_t &set_attr(op_attr_t name, bool required,
attribute_kind_t attr_kind, const T &value,
const std::vector<T> &candidates = {}) {
assertm(attributes_.count(name) == 0,
"provided attribute has already been set");
std::vector<utils::attribute_value_t> candidates_tmp(candidates.size());
std::transform(candidates.begin(), candidates.end(),
candidates_tmp.begin(),
[](const T &c) { return utils::attribute_value_t {c}; });
attributes_[name] = attribute_t(name, required, attr_kind,
utils::attribute_value_t(value), std::move(candidates_tmp));
return *this;
}
op_schema_t &set_attr(op_attr_t name, bool required,
attribute_kind_t attr_kind, const char *value,
const std::vector<const char *> &candidates = {});
op_schema_t &set_shape_inference_function(shape_infer_fn fn);
shape_infer_fn get_shape_inference_function() const;
op_schema_t &set_op_def_constraint_function(op_def_constraint_fn fn);
std::vector<op_def_constraint_fn> get_op_def_constraint_functions() const;
const std::vector<op_parameter_t> &get_inputs() const;
const std::vector<op_parameter_t> &get_outputs() const;
const std::unordered_map<op_attr_t, attribute_t> &get_attrs() const;
bool verify(const op_t *l_op, bool check_undefined_attrs = true) const;
status_t shape_infer(op_t *n, std::vector<logical_tensor_t *> &inputs,
std::vector<logical_tensor_t *> &outputs) const;
op_schema_t &set_inputs_option(param_num_option option);
param_num_option get_inputs_option() const;
op_schema_t &set_outputs_option(param_num_option option);
param_num_option get_outputs_option() const;
void set_default_attribute(op_t *l_op) const;
op_schema_t &set_additional_item(
const std::string &key, const utils::any_t &value);
const utils::any_t &get_additional_item(const std::string &key) const;
template <typename T>
op_schema_t &set_additional_item(const std::string &key, const T &value) {
return set_additional_item(key, utils::any_t {value});
}
template <typename T>
T get_additional_item(const std::string &key) const {
return utils::any_cast<T>(get_additional_item(key));
}
bool has_additional_item(const std::string &key) const;
private:
void verify_input_(size_t in_offset);
void verify_output_(size_t out_offset);
bool verify_param_num(size_t actual_num,
const std::set<size_t> &expected_num,
param_num_option option) const;
bool verify_param_dtype(
const std::vector<std::shared_ptr<value_t>> &actual_values,
const std::vector<op_schema_t::op_parameter_t> &expected_params,
param_num_option option,
std::unordered_map<std::string, std::set<data_type_t>>
&dtype_constraints) const;
bool verify_attributes(
const std::unordered_map<op_attr_t, utils::attribute_value_t>
&actual_attrs,
const std::unordered_map<op_attr_t, attribute_t> &expected_attrs,
bool check_undefined_attrs) const;
size_t get_max_valid_param_num(
const std::set<size_t> ¶m_num, param_num_option option) const;
op_kind_t op_kind_;
opset_version version_;
std::set<size_t> num_inputs_;
std::set<size_t> num_outputs_;
std::set<size_t> inputs_offset;
std::set<size_t> outputs_offset;
std::unordered_map<std::string, std::set<data_type_t>>
op_parameter_dtype_map_;
param_num_option inputs_option = param_num_option::fixed;
param_num_option outputs_option = param_num_option::fixed;
std::vector<op_parameter_t> inputs_;
std::vector<op_parameter_t> outputs_;
std::unordered_map<op_attr_t, attribute_t> attributes_;
shape_infer_fn tensor_inference_function_ = nullptr;
std::vector<op_def_constraint_fn> op_def_constraint_functions_;
std::set<size_t> commutative_inputs_;
std::unordered_map<std::string, utils::any_t> additional_items_map_;
};
using op_kind_version_schema_map
= std::unordered_map<op_kind_t, std::map<opset_version, op_schema_t>>;
class op_schema_registry_t {
public:
class op_schema_registry_once_t {
public:
op_schema_registry_once_t(op_schema_t &&schema);
};
static const op_schema_t *get_op_schema(op_kind_t a_op_kind);
private:
static op_kind_version_schema_map &get_map_without_ensuring_registration();
static op_kind_version_schema_map &get_map();
static impl::utils::rw_mutex_t &get_rw_mutex();
};
#ifdef GNUC
#define DNNL_GRAPH_UNUSED __attribute__((__unused__))
#else
#define DNNL_GRAPH_UNUSED
#endif
void register_schema(op_schema_t &&schema);
template <class T>
void register_opset_schema() {
T::for_each_schema(register_schema);
}
template <typename T>
op_schema_t get_op_schema();
#define DNNL_GRAPH_OP_SCHEMA_CLASS_NAME(opkind, version) \
_dnnl_graph_op_schema_##opkind##_##version##_
#define DNNL_GRAPH_OP_SCHEMA(opkind, version, impl) \
class DNNL_GRAPH_OP_SCHEMA_CLASS_NAME(opkind, version); \
template <> \
inline op_schema_t \
get_op_schema<DNNL_GRAPH_OP_SCHEMA_CLASS_NAME(opkind, version)>() { \
return (impl).set_op_kind(op_kind::opkind).since_version(version); \
}
#define SET_MATMUL_COMMON_ATTRS \
set_attr(op_attr::transpose_a, false, attribute_kind::b, false) \
.set_attr(op_attr::transpose_b, false, attribute_kind::b, false) \
.set_attr(op_attr::accumulation_mode, false, attribute_kind::s, \
"strict", \
{"strict", "relaxed", "any", "f32", "s32", "f16"})
#define SET_CONV_COMMON_ATTRS \
set_attr(op_attr::strides, true, attribute_kind::is) \
.set_attr(op_attr::pads_begin, true, attribute_kind::is) \
.set_attr(op_attr::pads_end, true, attribute_kind::is) \
.set_attr(op_attr::dilations, true, attribute_kind::is) \
.set_attr(op_attr::auto_pad, false, attribute_kind::s, "None", \
{"None", "SAME_UPPER", "SAME_LOWER", "VALID"}) \
.set_attr(op_attr::groups, false, attribute_kind::i, (int64_t)1) \
.set_attr(op_attr::data_format, false, attribute_kind::s, "NXC", \
{"NXC", "NCX"}) \
.set_attr(op_attr::weights_format, false, attribute_kind::s, \
"XIO", {"XIO", "OIX"})
#define SET_CONVTRANSPOSE_COMMON_ATTRS \
set_attr(op_attr::strides, true, attribute_kind::is) \
.set_attr(op_attr::pads_begin, true, attribute_kind::is) \
.set_attr(op_attr::pads_end, true, attribute_kind::is) \
.set_attr(op_attr::dilations, true, attribute_kind::is) \
.set_attr(op_attr::auto_pad, false, attribute_kind::s, "None", \
{"None", "SAME_UPPER", "SAME_LOWER", "VALID"}) \
.set_attr(op_attr::groups, false, attribute_kind::i, (int64_t)1) \
.set_attr(op_attr::data_format, false, attribute_kind::s, "NXC", \
{"NXC", "NCX"}) \
.set_attr(op_attr::weights_format, false, attribute_kind::s, \
"XOI", {"XOI", "IOX"})
#define SET_REDUCE_COMMON_ATTRS \
set_attr(op_attr::axes, false, attribute_kind::is, std::vector<int64_t>()) \
.set_attr(op_attr::keep_dims, false, attribute_kind::b, false)
} } }
#endif