namespace caffe
{
+namespace util
+{
+
+template <typename OptsType>
+static inline bool has2DStride(const OptsType& opts)
+{
+ if (opts.has_stride_h() != opts.has_stride_w())
+ {
+ throw PluginException("Conv or Pool layer has only 1 out of 2 2D strides, investigate");
+ }
+ // We already checked that both 2D strides are both present or both are not
+ return opts.has_stride_h();
+}
+
+static inline Shape getStrideFromOneValue(bool hasStride, unsigned int stride)
+{
+ if (hasStride)
+ {
+ return Shape{stride, stride, 1};
+ }
+ else
+ {
+ return Shape{1, 1, 1};
+ }
+}
+
+static inline Shape getStrideFromTwoValues(unsigned int stride1, unsigned int stride2)
+{
+ return Shape{stride1, stride2, 1};
+}
+
+template <typename OptsType>
+static inline Shape getStride(const OptsType& opts)
+{
+ // stride might have size 0, then it defaults to 1
+ if (opts.stride_size() == 0)
+ {
+ return Shape{1, 1, 1};
+ }
+ else if (opts.stride_size() == 1)
+ {
+ return getStrideFromOneValue(true, opts.stride(0));
+ }
+ else
+ {
+ return getStrideFromTwoValues(opts.stride(0), opts.stride(1));
+ }
+}
+
+template <typename OptsType>
+static inline bool has2DPad(const OptsType& opts)
+{
+ return opts.has_pad_h() || opts.has_pad_w();
+}
+
+static ops::PaddingType getPadTypeFromOneValue(bool hasPad, unsigned int pad)
+{
+ if (hasPad)
+ {
+ if (pad == 0)
+ {
+ return ops::PaddingType::Valid;
+ }
+ else
+ {
+ return ops::PaddingType::Same;
+ }
+ }
+ else
+ {
+ return ops::PaddingType::Valid;
+ }
+}
+
+static ops::PaddingType getPadTypeFromTwoValues(unsigned int pad1, unsigned int pad2)
+{
+ bool areBothPadsZero = pad1 == 0 && pad2 == 0;
+ bool doPadsDiffer = pad1 != pad2;
+ if (areBothPadsZero)
+ {
+ return ops::PaddingType::Valid;
+ }
+ else if (doPadsDiffer)
+ {
+ std::string pads = std::to_string(pad1) + ", " + std::to_string(pad2);
+ throw PluginException(std::string("Unsupported pads: ") + pads);
+ }
+ else
+ {
+ return ops::PaddingType::Same;
+ }
+}
+
+template <typename OptsType>
+static inline ops::PaddingType getPadTypeFromTwoValues(const OptsType &opts)
+{
+ unsigned int pad1, pad2;
+ pad1 = opts.has_pad_h() ? opts.pad_h() : 0;
+ pad2 = opts.has_pad_w() ? opts.pad_w() : 0;
+
+ return getPadTypeFromTwoValues(pad1, pad2);
+}
+
+template <typename OptsType>
+static ops::PaddingType getPadType(const OptsType &opts)
+{
+ if (opts.pad_size() == 0)
+ {
+ return ops::PaddingType::Valid;
+ }
+ else if (opts.pad_size() == 1)
+ {
+ return getPadTypeFromOneValue(true, opts.pad(0));
+ }
+ else
+ {
+ return getPadTypeFromTwoValues(opts.pad(0), opts.pad(1));
+ }
+}
+
+/**
+ * @brief Tries to determine whether padding is SAME or VALID given
+ * numeric pad values from Caffe layer options. Only 2D convolutions/pools
+ * are supported currently.
+ * @todo Currently, pad_h and pad_w options take precedence if they are present,
+ * but maybe it is not correct logic. Check how it really is done.
+ * @todo Specific calculations are required to check that padding is indeed SAME,
+ * currently we just return SAME if padding is non-zero.
+ */
+__attribute__ ((unused)) static ops::PaddingType getConvPadType(const ConvolutionParameter& opts)
+{
+ if (has2DPad(opts))
+ return getPadTypeFromTwoValues(opts);
+ else
+ return getPadType(opts);
+}
+
+__attribute__ ((unused)) static ops::PaddingType getPoolPadType(const PoolingParameter& opts)
+{
+ if (has2DPad(opts))
+ return getPadTypeFromTwoValues(opts);
+ else
+ return getPadTypeFromOneValue(opts.has_pad(), opts.pad());
+}
+
+/**
+ * @brief Determines stride values from Caffe layer options.
+ * Only 2D convolutions/pools are supported currently.
+ * @todo Currently, stride_h and stride_w options take precedence if they are present,
+ * but maybe it is not correct logic. Check how it really is done.
+ */
+__attribute__ ((unused)) static Shape getConvStride(const ConvolutionParameter& opts)
+{
+ if (has2DStride(opts))
+ return getStrideFromTwoValues(opts.stride_h(), opts.stride_w());
+ else
+ return getStride(opts);
+}
+
+__attribute__ ((unused)) static Shape getPoolStride(const PoolingParameter& opts)
+{
+ if (has2DStride(opts))
+ return getStrideFromTwoValues(opts.stride_h(), opts.stride_w());
+ else
+ return getStrideFromOneValue(opts.has_stride(), opts.stride());
+}
+
+__attribute__ ((unused)) static Shape getPoolWindowShape(const PoolingParameter &opts)
+{
+ if (opts.has_kernel_h() != opts.has_kernel_w())
+ {
+ throw PluginException("Pool layer has only 1 out of 2 kernel dimensions, investigate");
+ }
+
+ if (opts.has_kernel_h())
+ {
+ return Shape{opts.kernel_h(), opts.kernel_w(), 1};
+ }
+ else if (opts.has_kernel_size())
+ {
+ return Shape{opts.kernel_size(), opts.kernel_size(), 1};
+ }
+ else
+ {
+ throw PluginException("Pooling layer doesn't have kernel size data, investigate");
+ }
+}
+
+__attribute__ ((unused)) static ops::PoolOp::PoolingType getPoolingType(const PoolingParameter& opts)
+{
+ using PoolingType = ops::PoolOp::PoolingType;
+
+ if (opts.pool() == PoolingParameter::MAX)
+ return PoolingType::MAX;
+ else if (opts.pool() == PoolingParameter::AVE)
+ return PoolingType::AVG;
+ else
+ throw PluginException("Unsupported pooling type: " +
+ PoolingParameter::PoolMethod_Name(opts.pool()));
+}
+
+/**
+ * @brief Determines correct value for Caffe Softmax/Concat axis parameter.
+ * @todo Change cout to a log library call.
+ * @todo Decide how to process negative axis values.
+ * @todo Decide how to process axis in general.
+ */
+template <typename OptsType>
+__attribute__ ((unused)) static int getAxisValue(const OptsType& opts)
+{
+ int axis = 2;
+ if (opts.has_axis())
+ {
+ axis = opts.axis();
+ if (axis == 0)
+ {
+ std::cout << "WARNING: axis parameter equals 0. It is normal,"
+ "but implies that the model might not have a batch dimension,"
+ "so make sure import works correctly." << std::endl;
+ }
+ else if (axis < 0)
+ {
+ throw PluginException("Softmax/Concat layer negative axis values are not supported yet.");
+ }
+ else if (axis != 1)
+ {
+ throw PluginException("Softmax/Concat layer axis param is not 1, which implies"
+ "unsupported NN architecture.");
+ }
+ }
+
+ return axis;
+}
+
+} // namespace util
+
std::vector<INode::Ref> OpCreator::createConv2D(InputOps inputs, InputParams params,
const caffe::ConvolutionParameter& opts)
{