text: // Copyright (C) 2018 Intel Corporation
  // SPDX-License-Identifier: Apache-2.0
  //
  
  #include "mkldnn_activation_node.h"
  #include "desc_iterator.hpp"
  #include <ie_layers.h>
  #include <algorithm>
  #include <string>
  #include <mkldnn_extension_utils.h>
  #include "details/caseless.hpp"
  
  using namespace mkldnn;
  using namespace MKLDNNPlugin;
  using namespace InferenceEngine;
  using namespace InferenceEngine::details;
  
  // TODO: (ichuraev) I don't fully sure that names of types and parameters are correct for square, abs, sqrt, linear, bounded_relu and soft_relu
  caseless_map<std::string, std::function<void(GenericLayer*, mkldnn::algorithm&, float&, float&)>> MKLDNNActivationNode::initializers = {
          {"relu", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
              alpha = activationLayer->GetParamAsFloat("negative_slope", 0.0f);
              beta = 0.0f;
              algorithm = eltwise_relu;
          }},
          {"elu", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
              alpha = activationLayer->GetParamAsFloat("alpha", 1.0f);
              beta = 0.0f;
              algorithm = eltwise_elu;
          }},
          {"tanh", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
              alpha = 0.0f;
              beta = 0.0f;
              algorithm = eltwise_tanh;
          }},
          {"logistic", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
              alpha = 0.0f;
              beta = 0.0f;
              algorithm = eltwise_logistic;
          }},
          {"square", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
              alpha = 0.0f;
              beta = 0.0f;
              algorithm = eltwise_square;
          }},
          {"abs", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
              alpha = 0.0f;
              beta = 0.0f;
              algorithm = eltwise_abs;
          }},
          {"sqrt", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
              alpha = 0.0f;
              beta = 0.0f;
              algorithm = eltwise_sqrt;
          }},
          {"linear", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
              alpha = activationLayer->GetParamAsFloat("alpha", 1.0f);
              beta = activationLayer->GetParamAsFloat("beta", 0.0f);
              algorithm = eltwise_linear;
          }},
          {"bounded_relu", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
              alpha = activationLayer->GetParamAsFloat("alpha", 0.0f);
              beta = 0.0f;
              algorithm = eltwise_bounded_relu;
          }},
          {"soft_relu", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
              alpha = 0.0f;
              beta = 0.0f;
              algorithm = eltwise_soft_relu;
          }},
          {"relu6", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
              alpha = activationLayer->GetParamAsFloat("n", 6.0f);
              beta = 0.0f;
              algorithm = eltwise_bounded_relu;
          }},
          {"clamp", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
              alpha = activationLayer->GetParamAsFloat("max", 1.0f);
              beta = activationLayer->GetParamAsFloat("min", 0.0f);
              algorithm = eltwise_clamp;
          }}
  };
  
  MKLDNNActivationNode::MKLDNNActivationNode(const InferenceEngine::CNNLayerPtr& layer, const mkldnn::engine& eng) : MKLDNNNode(layer, eng) {}
  
  void MKLDNNActivationNode::getSupportedDescriptors() {
      if (!descs.empty())
          return;
  
      if (getParentEdges().size() != 1)
          THROW_IE_EXCEPTION << "Incorrect number of input edges for layer " << getName();
      if (!getChildEdges().size())
          THROW_IE_EXCEPTION << "Incorrect number of output edges for layer " << getName();
  
      auto parentOutDims = getParentEdgeAt(0)->getDims();
  
      InferenceEngine::Precision precision = getCnnLayer()->insData[0].lock()->getPrecision();
  
      // FIXME: MKLDNN doesn't support not inputs with number of dimensions less than 4 for activation
      while (parentOutDims.ndims() < 4)
          parentOutDims.push_back(1);
      for (auto format : getAvailableFormatsForDims(parentOutDims)) {
          MKLDNNMemoryDesc in_candidate(parentOutDims, MKLDNNExtensionUtils::IEPrecisionToDataType(precision), format);
          createDescriptor({in_candidate}, {});
      }
  }
  
  void MKLDNNActivationNode::createPrimitive() {
      if (prim)
          return;
  
      auto prim_desc = createPrimitiveDescriptor<relu_forward::primitive_desc, relu_forward::desc>();
  
      prim.reset(new relu_forward(prim_desc, getParentEdgeAt(0)->getMemory().GetPrimitive(),
                                  getChildEdgeAt(0)->getMemory().GetPrimitive()));
  }
  
  bool MKLDNNActivationNode::created() const {
      return getType() == Activation;
  }
  
  void MKLDNNActivationNode::initValues() {
      GenericLayer* activationLayer = getCnnLayer().get();
      if (activationLayer == nullptr)
          THROW_IE_EXCEPTION << "Cannot get CNNLayer.";
  
      std::string type = activationLayer->type;
      CaselessEq<std::string> comparator;
      if (comparator(type, "activation"))
          type = activationLayer->GetParamAsString("type");
      if (comparator(type, "sigmoid"))
          type = "logistic";
  
      if (initializers.find(type) == initializers.end())
          THROW_IE_EXCEPTION << "Node " << getName() << " has unsupported activation primitive: "
                             << activationLayer->type << " : " << type;
      initializers[type](activationLayer, algorithm, alpha, beta);
      initialized = true;
  }
  
  void MKLDNNActivationNode::createDescriptor(const std::vector<InferenceEngine::TensorDesc> &inputDesc,
                                              const std::vector<InferenceEngine::TensorDesc> &outputDesc) {
      MKLDNNMemoryDesc inDesc(inputDesc[0]);
      MKLDNNDescriptor desc(std::shared_ptr<eltwise_forward::desc>(
              new eltwise_forward::desc(prop_kind::forward_scoring, getAlgorithm(), inDesc, getAlpha(), getBeta())));
      descs.push_back(desc);
  }
  
  void MKLDNNActivationNode::initOptimalPrimitiveDescriptor() {
      auto config = getSelectedPrimitiveDescriptor()->getConfig();
      if (isInitConfig(config))
          return;
  
      if (config.inConfs.size() != 1 || config.outConfs.size() != 1 ||
              (!isUninitTensorDesc(config.inConfs[0].desc) &&
                      !isUninitTensorDesc(config.outConfs[0].desc) && config.inConfs[0].desc != config.outConfs[0].desc))
          THROW_IE_EXCEPTION << "Layer " << getName() << " has incorrect selected config!";
  
      if (!isUninitTensorDesc(config.inConfs[0].desc)) {
          config.outConfs[0].desc = config.inConfs[0].desc;
      } else if (!isUninitTensorDesc(config.outConfs[0].desc)) {
          config.inConfs[0].desc = config.outConfs[0].desc;
      } else {
          config.outConfs[0].desc = config.inConfs[0].desc = getConfiguredInputDesc(config, 0);
      }
  
      initDescriptor(config);
  }
  
  MKLDNNMemoryDesc MKLDNNActivationNode::getSrcMemDesc(mkldnn::primitive_desc_iterator &primitive_desc_it, size_t idx) {
      InferenceEngine::TensorDesc desc = MKLDNNMemoryDesc(primitive_desc_it.src_primitive_desc(idx).desc());
  
      auto parentOutDims = getParentEdgeAt(idx)->getDims().ToSizeVector();
  
      SizeVector blocked_dims, order, dimOffsets, strides;
      size_t offset = desc.getBlockingDesc().getOffsetPadding();
  
      for (size_t i = 0; i < desc.getBlockingDesc().getStrides().size(); i++) {
          if (desc.getBlockingDesc().getOrder()[i] >= parentOutDims.size())
              continue;
  
          blocked_dims.push_back(desc.getBlockingDesc().getBlockDims()[i]);
          order.push_back(desc.getBlockingDesc().getOrder()[i]);
          dimOffsets.push_back(desc.getBlockingDesc().getOffsetPaddingToData()[i]);
          strides.push_back(desc.getBlockingDesc().getStrides()[i]);
      }
      if (desc.getLayout() == InferenceEngine::Layout::ANY)
          return MKLDNNMemoryDesc(InferenceEngine::TensorDesc(desc.getPrecision(),
                                                              parentOutDims,
                                                              desc.getLayout()));
      else
          return MKLDNNMemoryDesc(InferenceEngine::TensorDesc(desc.getPrecision(),
                                                              parentOutDims,
                                                              {blocked_dims, order, offset, dimOffsets, strides}));
  }
  
  MKLDNNMemoryDesc MKLDNNActivationNode::getDstMemDesc(mkldnn::primitive_desc_iterator &primitive_desc_it, size_t idx) {
      InferenceEngine::TensorDesc desc = MKLDNNMemoryDesc(primitive_desc_it.dst_primitive_desc(idx).desc());
  
      auto childInDims = getChildEdgeAt(idx)->getDims().ToSizeVector();
  
      SizeVector blocked_dims, order, dimOffsets, strides;
      size_t offset = desc.getBlockingDesc().getOffsetPadding();
  
      for (size_t i = 0; i < desc.getBlockingDesc().getStrides().size(); i++) {
          if (desc.getBlockingDesc().getOrder()[i] >= childInDims.size())
              continue;
  
          blocked_dims.push_back(desc.getBlockingDesc().getBlockDims()[i]);
          order.push_back(desc.getBlockingDesc().getOrder()[i]);
          dimOffsets.push_back(desc.getBlockingDesc().getOffsetPaddingToData()[i]);
          strides.push_back(desc.getBlockingDesc().getStrides()[i]);
      }
      if (desc.getLayout() == InferenceEngine::Layout::ANY)
          return MKLDNNMemoryDesc(InferenceEngine::TensorDesc(desc.getPrecision(),
                                                              childInDims,
                                                              desc.getLayout()));
      else
          return MKLDNNMemoryDesc(InferenceEngine::TensorDesc(desc.getPrecision(),
                                                              childInDims,
                                                              {blocked_dims, order, offset, dimOffsets, strides}));
  }
  