--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LOCO_IR_FEATURE_AXIS_H__
+#define __LOCO_IR_FEATURE_AXIS_H__
+
+namespace loco
+{
+
+enum class FeatureAxis
+{
+ Count,
+ Depth,
+ Height,
+ Width
+};
+
+} // namespace loco
+
+#endif // __LOCO_IR_FEATURE_AXIS_H__
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LOCO_IR_PERMUTING_CODEC_H__
+#define __LOCO_IR_PERMUTING_CODEC_H__
+
+#include "loco/IR/Domain.h"
+
+#include "loco/IR/FeatureAxis.h"
+#include "loco/IR/FeatureCodec.h"
+
+#include <map>
+
+namespace loco
+{
+
+using TensorAxis = uint32_t;
+
+template <Domain D> class Permutation;
+template <Domain D> class PermutingEncoder;
+template <Domain D> class PermutingDecoder;
+
+/**
+ * @brief Mapping between Feature/Tensor Axis
+ */
+template <> class Permutation<Domain::Feature>
+{
+public:
+ Permutation() = default;
+
+public:
+ /**
+ * @brief Return whether a tensor axis is specified for a given feature axis
+ *
+ * This method does not validate the corresponding value.
+ */
+ bool mapped(const FeatureAxis &axis_f) const;
+
+ /**
+ * @brief Get the tensor axis corresponding to a given feature axis
+ *
+ * This method works correclty only when feature axis is mapped before.
+ */
+ TensorAxis axis(const FeatureAxis &axis_f) const;
+
+ /**
+ * @brief Set the tensor axis corresponding to a given feature axis
+ */
+ TensorAxis &axis(const FeatureAxis &axis_f);
+
+ TensorAxis operator[](const FeatureAxis &axis_f) const { return axis(axis_f); }
+ TensorAxis &operator[](const FeatureAxis &axis_f) { return axis(axis_f); }
+
+private:
+ std::map<FeatureAxis, TensorAxis> _map;
+};
+
+template <> class PermutingEncoder<Domain::Feature> final : public FeatureEncoder
+{
+public:
+ PermutingEncoder() = default;
+
+public:
+ bool valid(void) const;
+
+public:
+ FeatureShape shape(const TensorShape &tensor_shape) const override;
+ TensorIndex value(const FeatureIndex &index) const override;
+
+public:
+ const Permutation<Domain::Feature> *perm(void) const { return &_perm; }
+ Permutation<Domain::Feature> *perm(void) { return &_perm; }
+
+private:
+ Permutation<Domain::Feature> _perm;
+};
+
+template <> class PermutingDecoder<Domain::Feature> final : public FeatureDecoder
+{
+public:
+ PermutingDecoder() = default;
+
+public:
+ bool valid(void) const;
+
+public:
+ TensorShape shape(const FeatureShape &tensor_shape) const override;
+ FeatureIndex value(const TensorIndex &index) const override;
+
+public:
+ const Permutation<Domain::Feature> *perm(void) const { return &_perm; }
+ Permutation<Domain::Feature> *perm(void) { return &_perm; }
+
+private:
+ Permutation<Domain::Feature> _perm;
+};
+
+} // namespace loco
+
+#endif // __LOCO_IR_PERMUTING_CODEC_H__
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "loco/IR/FeatureAxis.h"
+
+// NOTE This file validates "FeatureAxis.h". Please DO NOT remove this file.
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "loco/IR/PermutingCodec.h"
+
+#include <cassert>
+#include <set>
+#include <stdexcept>
+
+/**
+ * Feature Domain
+ */
+namespace
+{
+
+using loco::FeatureAxis;
+
+inline bool valid(const FeatureAxis &axis)
+{
+ switch (axis)
+ {
+ case FeatureAxis::Count:
+ return true;
+ case FeatureAxis::Depth:
+ return true;
+ case FeatureAxis::Height:
+ return true;
+ case FeatureAxis::Width:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+inline bool valid(const loco::Permutation<loco::Domain::Feature> &perm)
+{
+ auto check = [&perm](FeatureAxis axis_f) {
+ if (!perm.mapped(axis_f))
+ return false;
+ return perm.axis(axis_f) < 4;
+ };
+
+ if (!check(FeatureAxis::Count))
+ return false;
+ if (!check(FeatureAxis::Depth))
+ return false;
+ if (!check(FeatureAxis::Height))
+ return false;
+ if (!check(FeatureAxis::Width))
+ return false;
+
+ // Check whether tensor axes are all distinct
+ std::set<loco::TensorAxis> values;
+
+ values.insert(perm[FeatureAxis::Count]);
+ values.insert(perm[FeatureAxis::Depth]);
+ values.insert(perm[FeatureAxis::Height]);
+ values.insert(perm[FeatureAxis::Width]);
+
+ return values.size() == 4;
+}
+
+} // namespace
+
+namespace loco
+{
+
+//
+// Permutation
+//
+bool Permutation<Domain::Feature>::mapped(const FeatureAxis &axis_f) const
+{
+ assert(valid(axis_f) && "invalid feature axis");
+ return _map.find(axis_f) != _map.end();
+}
+
+uint32_t Permutation<Domain::Feature>::axis(const FeatureAxis &axis_f) const
+{
+ assert(valid(axis_f) && "invalid feature axis");
+ assert(mapped(axis_f) && "unmapped feature axis");
+ return _map.at(axis_f);
+}
+
+uint32_t &Permutation<Domain::Feature>::axis(const FeatureAxis &axis_f)
+{
+ assert(valid(axis_f) && "invalid feature axis");
+ return _map[axis_f];
+}
+
+//
+// Permuting Encoder
+//
+FeatureShape PermutingEncoder<Domain::Feature>::shape(const TensorShape &in) const
+{
+ assert(valid() && "invalid permutation");
+
+ FeatureShape out;
+
+ out.count() = in.dim(_perm[FeatureAxis::Count]);
+ out.depth() = in.dim(_perm[FeatureAxis::Depth]);
+ out.height() = in.dim(_perm[FeatureAxis::Height]);
+ out.width() = in.dim(_perm[FeatureAxis::Width]);
+
+ return out;
+}
+
+TensorIndex PermutingEncoder<Domain::Feature>::value(const FeatureIndex &in) const
+{
+ assert(valid() && "invalid permutation");
+
+ TensorIndex out;
+
+ out.resize(4);
+
+ out.at(_perm[FeatureAxis::Count]) = in.batch();
+ out.at(_perm[FeatureAxis::Depth]) = in.channel();
+ out.at(_perm[FeatureAxis::Height]) = in.row();
+ out.at(_perm[FeatureAxis::Width]) = in.column();
+
+ return out;
+}
+
+bool PermutingEncoder<Domain::Feature>::valid(void) const { return ::valid(_perm); }
+
+//
+// Permuting Decoder
+//
+TensorShape PermutingDecoder<Domain::Feature>::shape(const FeatureShape &in) const
+{
+ assert(valid() && "invalid permuation");
+
+ TensorShape out;
+
+ out.rank(4);
+
+ out.dim(_perm[FeatureAxis::Count]) = in.count();
+ out.dim(_perm[FeatureAxis::Depth]) = in.depth();
+ out.dim(_perm[FeatureAxis::Height]) = in.height();
+ out.dim(_perm[FeatureAxis::Width]) = in.width();
+
+ return out;
+}
+
+FeatureIndex PermutingDecoder<Domain::Feature>::value(const TensorIndex &in) const
+{
+ assert(valid() && "invalid permutation");
+
+ FeatureIndex out;
+
+ out.batch() = in.at(_perm[FeatureAxis::Count]);
+ out.channel() = in.at(_perm[FeatureAxis::Depth]);
+ out.row() = in.at(_perm[FeatureAxis::Height]);
+ out.column() = in.at(_perm[FeatureAxis::Width]);
+
+ return out;
+}
+
+bool PermutingDecoder<Domain::Feature>::valid(void) const { return ::valid(_perm); }
+
+} // namespace loco
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "loco/IR/PermutingCodec.h"
+
+#include <gtest/gtest.h>
+
+using namespace loco;
+
+TEST(PemutationTest, feature)
+{
+ Permutation<Domain::Feature> perm;
+
+ // All values are invalid at the beginning
+ ASSERT_FALSE(perm.mapped(FeatureAxis::Count));
+ ASSERT_FALSE(perm.mapped(FeatureAxis::Depth));
+ ASSERT_FALSE(perm.mapped(FeatureAxis::Height));
+ ASSERT_FALSE(perm.mapped(FeatureAxis::Width));
+
+ // Update mapping
+ perm[FeatureAxis::Count] = 5;
+ perm[FeatureAxis::Depth] = 6;
+ perm[FeatureAxis::Height] = 7;
+ perm[FeatureAxis::Width] = 8;
+
+ // Now perm has a mapping for all the axes
+ ASSERT_TRUE(perm.mapped(FeatureAxis::Count));
+ ASSERT_TRUE(perm.mapped(FeatureAxis::Depth));
+ ASSERT_TRUE(perm.mapped(FeatureAxis::Height));
+ ASSERT_TRUE(perm.mapped(FeatureAxis::Width));
+
+ // Check the value
+ ASSERT_EQ(perm[FeatureAxis::Count], 5);
+ ASSERT_EQ(perm[FeatureAxis::Depth], 6);
+ ASSERT_EQ(perm[FeatureAxis::Height], 7);
+ ASSERT_EQ(perm[FeatureAxis::Width], 8);
+}
+
+TEST(PermutingEncoderTest, feature)
+{
+ PermutingEncoder<Domain::Feature> enc;
+
+ // Encoder is invalid at the beginning
+ ASSERT_FALSE(enc.valid());
+
+ // Set "invalid" mapping
+ enc.perm()->axis(FeatureAxis::Count) = 0;
+ enc.perm()->axis(FeatureAxis::Depth) = 6;
+ enc.perm()->axis(FeatureAxis::Height) = 1;
+ enc.perm()->axis(FeatureAxis::Width) = 2;
+
+ // Encoder is still invalid
+ ASSERT_FALSE(enc.valid());
+
+ // Set another "invalid" mapping
+ enc.perm()->axis(FeatureAxis::Depth) = 1;
+
+ // Encoder is still invalid
+ ASSERT_FALSE(enc.valid());
+
+ // Set "valid" mapping
+ enc.perm()->axis(FeatureAxis::Depth) = 3;
+
+ // Encoder is now valid
+ ASSERT_TRUE(enc.valid());
+
+ // Let's test with a HD (1280x720) RGB image
+ TensorShape tensor_shape;
+
+ tensor_shape.rank(4);
+ tensor_shape.dim(0) = 1; // COUNT
+ tensor_shape.dim(1) = 720; // HEIGHT
+ tensor_shape.dim(2) = 1280; // WIDTH
+ tensor_shape.dim(3) = 3; // DEPTH
+
+ // Get the feature shape corresponding to a given image
+ auto feature_shape = enc.shape(tensor_shape);
+
+ ASSERT_EQ(feature_shape.count().value(), 1);
+ ASSERT_EQ(feature_shape.depth().value(), 3);
+ ASSERT_EQ(feature_shape.height().value(), 720);
+ ASSERT_EQ(feature_shape.width().value(), 1280);
+
+ // Let's find a source tensor index!
+ FeatureIndex feature_index;
+
+ feature_index.batch() = 0;
+ feature_index.channel() = 1;
+ feature_index.row() = 2;
+ feature_index.column() = 3;
+
+ auto tensor_index = enc.value(feature_index);
+
+ ASSERT_EQ(tensor_index.at(0), 0); // BATCH(COUNT)
+ ASSERT_EQ(tensor_index.at(1), 2); // ROW(HEIGHT)
+ ASSERT_EQ(tensor_index.at(2), 3); // COLUMN(WIDTH)
+ ASSERT_EQ(tensor_index.at(3), 1); // CHANNEL(DEPTH)
+}
+
+TEST(PermutingDecoderTest, feature)
+{
+ PermutingDecoder<Domain::Feature> dec;
+
+ // Decoder is invalid at the beginning
+ ASSERT_FALSE(dec.valid());
+
+ // Set "invalid" mapping
+ dec.perm()->axis(FeatureAxis::Count) = 0;
+ dec.perm()->axis(FeatureAxis::Depth) = 6;
+ dec.perm()->axis(FeatureAxis::Height) = 1;
+ dec.perm()->axis(FeatureAxis::Width) = 2;
+
+ // Decoder is still invalid
+ ASSERT_FALSE(dec.valid());
+
+ // Set another "invalid" mapping
+ dec.perm()->axis(FeatureAxis::Depth) = 1;
+
+ // Decoder is still invalid
+ ASSERT_FALSE(dec.valid());
+
+ // Set "valid" mapping
+ dec.perm()->axis(FeatureAxis::Depth) = 3;
+
+ // Decoder is now valid
+ ASSERT_TRUE(dec.valid());
+
+ // Let's test with a HD (1280x720) RGB image
+ FeatureShape feature_shape;
+
+ feature_shape.count() = 1;
+ feature_shape.depth() = 3;
+ feature_shape.height() = 720;
+ feature_shape.width() = 1280;
+
+ // Get the tensor shape corresponding to a given image
+ auto tensor_shape = dec.shape(feature_shape);
+
+ ASSERT_EQ(tensor_shape.rank(), 4);
+ ASSERT_EQ(tensor_shape.dim(0).value(), 1); // COUNT
+ ASSERT_EQ(tensor_shape.dim(1).value(), 720); // HEIGHT
+ ASSERT_EQ(tensor_shape.dim(2).value(), 1280); // WIDTH
+ ASSERT_EQ(tensor_shape.dim(3).value(), 3); // DEPTH
+
+ // Let's find a source feature index!
+ TensorIndex tensor_index;
+
+ tensor_index.resize(4);
+
+ tensor_index.at(0) = 0; // BATCH(COUNT)
+ tensor_index.at(3) = 1; // CHANNEL(DEPTH)
+ tensor_index.at(1) = 2; // ROW(HEIGHT)
+ tensor_index.at(2) = 3; // COLUMN(WIDTH)
+
+ auto feature_index = dec.value(tensor_index);
+
+ ASSERT_EQ(feature_index.batch(), 0);
+ ASSERT_EQ(feature_index.channel(), 1);
+ ASSERT_EQ(feature_index.row(), 2);
+ ASSERT_EQ(feature_index.column(), 3);
+}