#ifndef GRAPH_INTERFACE_LOGICAL_TENSOR_HPP
#define GRAPH_INTERFACE_LOGICAL_TENSOR_HPP
#include <algorithm>
#include <assert.h>
#include <functional>
#include <limits>
#include <string>
#include <utility>
#include <vector>
#include "common/type_helpers.hpp"
#include "graph/interface/c_types_map.hpp"
#include "graph/utils/utils.hpp"
namespace dnnl {
namespace impl {
namespace graph {
inline logical_tensor_t zero_logical_tensor() {
auto zero = logical_tensor_t();
return zero;
}
inline logical_tensor_t empty_logical_tensor_with_default_id() {
auto empty = logical_tensor_t();
empty.id = std::numeric_limits<size_t>::max();
empty.ndims = -1;
empty.layout_type = layout_type::any;
return empty;
}
struct logical_tensor_wrapper_t {
const logical_tensor_t *lt;
logical_tensor_wrapper_t(const logical_tensor_t *other) : lt(other) {}
logical_tensor_wrapper_t(const logical_tensor_t &other)
: logical_tensor_wrapper_t(&other) {}
size_t id() const { return lt->id; }
int32_t ndims() const { return lt->ndims; }
data_type_t data_type() const { return lt->data_type; }
layout_type_t layout_type() const { return lt->layout_type; }
size_t layout_id() const { return lt->layout.layout_id; }
property_type_t property_type() const { return lt->property; }
const dims_t &dims() const { return lt->dims; }
const dims_t &strides() const { return lt->layout.strides; }
std::vector<dim_t> vdims() const {
return {lt->dims, lt->dims + lt->ndims};
}
std::vector<dim_t> vstrides() const {
return {lt->layout.strides, lt->layout.strides + lt->ndims};
}
bool is_any() const { return lt->layout_type == layout_type::any; }
bool is_strided() const { return lt->layout_type == layout_type::strided; }
bool is_opaque() const { return lt->layout_type == layout_type::opaque; }
bool is_constant() const { return lt->property == property_type::constant; }
bool is_host_scalar() const {
return lt->property == property_type::host_scalar;
}
bool is_layout_type_undef() const {
return lt->layout_type == layout_type::undef;
}
bool is_data_type_undef() const {
return lt->data_type == data_type::undef;
}
bool is_empty() const { return ndims() < 0; }
bool is_scalar() const { return ndims() == 0; }
bool has_zero_dim() const {
for (int d = 0; d < ndims(); ++d) {
if (dims()[d] == 0) return true;
}
return false;
}
bool is_shape_unknown() const {
if (ndims() < 0) return true;
for (int d = 0; d < ndims(); ++d) {
if (dims()[d] < 0) { return true; }
}
return false;
}
bool is_stride_unknown() const {
if (ndims() < 0) return true;
for (int d = 0; d < ndims(); ++d) {
if (strides()[d] == DNNL_GRAPH_UNKNOWN_DIM) return true;
}
return false;
}
bool is_identical(const logical_tensor_wrapper_t &rhs) const {
return is_identical(*(this->lt), *(rhs.lt));
}
bool operator==(const logical_tensor_wrapper_t &rhs) const {
return is_similar(*(this->lt), *(rhs.lt), true,
true);
}
bool operator!=(const logical_tensor_wrapper_t &rhs) const {
return !operator==(rhs);
}
bool operator==(const logical_tensor_t &rhs) const {
return operator==(logical_tensor_wrapper_t(rhs));
}
bool operator!=(const logical_tensor_t &rhs) const {
return !operator==(rhs);
}
bool is_similar(const logical_tensor_wrapper_t &rhs) const {
return is_similar(*(this->lt), *(rhs.lt), false,
true);
}
size_t sub_byte_data_type_multiplier() const {
if (utils::one_of(data_type(), data_type::s4, data_type::u4,
dnnl::impl::data_type::f4_e2m1,
dnnl::impl::data_type::f4_e3m0))
return 2;
return 1;
}
size_t data_type_size() const { return types::data_type_size(data_type()); }
size_t size() const;
dim_t nelems() const {
if (is_empty()) return 0;
if (is_scalar()) return 1;
if (is_shape_unknown()) return -1;
return utils::array_product(dims(), static_cast<size_t>(ndims()));
}
std::vector<dim_t> get_weight_spatial_dims(
const std::string &format) const {
std::vector<dim_t> spatial_dims = vdims();
if (format == "OIX" || format == "IOX") {
spatial_dims.erase(spatial_dims.begin(), spatial_dims.begin() + 2);
} else if (format == "XIO" || format == "XOI") {
spatial_dims.erase(spatial_dims.end() - 2, spatial_dims.end());
} else {
spatial_dims.clear();
}
return spatial_dims;
}
std::vector<dim_t> get_src_spatial_dims(const std::string &format) const {
std::vector<dim_t> spatial_dims = vdims();
if (format == "NCX") {
spatial_dims.erase(spatial_dims.begin(), spatial_dims.begin() + 2);
} else if (format == "NXC") {
spatial_dims.erase(spatial_dims.begin(), spatial_dims.begin() + 1);
spatial_dims.erase(spatial_dims.end() - 1, spatial_dims.end());
} else {
spatial_dims.clear();
}
return spatial_dims;
}
dim_t get_weight_i(const std::string &format) const {
if (format == "OIX") {
return dims()[1];
} else if (format == "XIO") {
return dims()[ndims() - 2];
} else if (format == "IOX") {
return dims()[0];
} else if (format == "XOI") {
return dims()[ndims() - 1];
} else {
return DNNL_GRAPH_UNKNOWN_DIM;
}
}
dim_t get_weight_o(const std::string &format) const {
if (format == "OIX") {
return dims()[0];
} else if (format == "XIO") {
return dims()[ndims() - 1];
} else if (format == "IOX") {
return dims()[1];
} else if (format == "XOI") {
return dims()[ndims() - 2];
} else {
return DNNL_GRAPH_UNKNOWN_DIM;
}
}
dim_t get_src_n() const {
return dims()[0];
}
dim_t get_src_c(const std::string &format) const {
if (format == "NCX") {
return dims()[1];
} else if (format == "NXC") {
return dims()[ndims() - 1];
} else {
return DNNL_GRAPH_UNKNOWN_DIM;
}
}
logical_tensor_t reorder_data_dims_strides() const {
assert(lt->ndims != -1 && "data dims haven't be uninitialized.");
logical_tensor_t cdata = *lt;
int32_t i = 1, j = cdata.ndims - 1;
while (i < j) {
std::swap(cdata.dims[i], cdata.dims[j]);
if (cdata.layout_type == layout_type::strided) {
std::swap(cdata.layout.strides[i], cdata.layout.strides[j]);
}
++i;
}
return cdata;
}
logical_tensor_t reorder_weight_dims_strides() const { assert(lt->ndims != -1 && "data dims haven't be uninitialized.");
logical_tensor_t cweight = *lt;
int32_t i = 0, j = cweight.ndims - 1;
while (i < j) {
std::swap(cweight.dims[i], cweight.dims[j]);
if (cweight.layout_type == layout_type::strided) {
std::swap(cweight.layout.strides[i], cweight.layout.strides[j]);
}
++i;
--j;
}
i = 2, j = cweight.ndims - 1;
while (i < j) {
std::swap(cweight.dims[i], cweight.dims[j]);
if (cweight.layout_type == layout_type::strided) {
std::swap(cweight.layout.strides[i], cweight.layout.strides[j]);
}
++i;
--j;
}
return cweight;
}
bool has_same_shape_as(const logical_tensor_wrapper_t &rhs) const {
if (ndims() != rhs.ndims()) return false;
return std::equal(dims(), dims() + ndims(), rhs.dims());
}
bool has_same_layout_as(const logical_tensor_wrapper_t &rhs) const {
return is_similar(*(this->lt), *(rhs.lt), true, true);
}
size_t hash() const noexcept;
std::string str() const;
private:
bool is_identical(
const logical_tensor_t &lhs, const logical_tensor_t &rhs) const;
bool is_similar(const logical_tensor_t &lhs, const logical_tensor_t &rhs,
bool check_id = true, bool check_dtype = true) const;
};
} } }
namespace std {
template <>
struct hash<dnnl::impl::graph::logical_tensor_t> {
using argument_type = dnnl::impl::graph::logical_tensor_t;
using result_type = std::size_t;
result_type operator()(const argument_type <) const {
using namespace dnnl::impl::graph;
return logical_tensor_wrapper_t(lt).hash();
}
};
template <>
struct equal_to<dnnl::impl::graph::logical_tensor_t> {
using result_type = bool;
using first_argument_type = dnnl::impl::graph::logical_tensor_t;
using second_argument_type = dnnl::impl::graph::logical_tensor_t;
result_type operator()(const first_argument_type &lhs,
const second_argument_type &rhs) const {
using namespace dnnl::impl::graph;
const logical_tensor_wrapper_t lhs_wrapper {lhs};
const logical_tensor_wrapper_t rhs_wrapper {rhs};
return lhs_wrapper == rhs_wrapper;
}
};
}
#endif