Unverified Commit d4153c91 authored by Matthew Brookhart's avatar Matthew Brookhart Committed by GitHub

Adding Numpy Style Transpose to the Builder (#271)

* Add numpy_transpose to the builder for numpy-stype transpose operations

* fix docstring

* make sure to throw the error
parent e6cc7d8b
......@@ -15,6 +15,7 @@ set (SRC
autodiff/adjoints.cpp
builder/autobroadcast.cpp
builder/reduce_ops.cpp
builder/numpy_transpose.cpp
descriptor/input.cpp
descriptor/layout/dense_tensor_view_layout.cpp
descriptor/layout/tensor_view_layout.cpp
......
/*
Copyright 2017 Nervana Systems Inc.
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 "ngraph/builder/numpy_transpose.hpp"
#include <sstream>
#include "ngraph/except.hpp"
#include "ngraph/ops/reshape.hpp"
namespace ngraph
{
void numpy_transpose_error(const AxisVector& order, const Shape& in_shape)
{
std::ostringstream os;
os << "The axes order ";
os << "[ " << ngraph::join(order) << " ]";
os << " is incompatible with the input shape ";
os << "[ " << ngraph::join(in_shape) << " ]";
os << " during numpy_transpose.";
throw ngraph_error(os.str());
}
namespace builder
{
std::shared_ptr<Node> numpy_transpose(const std::shared_ptr<Node>& node, AxisVector order)
{
auto in_shape = node->get_shape();
// default, reverse the order of the axes
if (order.size() == 0)
{
auto n = in_shape.size();
order = AxisVector(n);
std::generate(order.begin(), order.end(), [&n]() { return --n; });
}
else if (order.size() == in_shape.size())
{
// validate that the axes order is valid, i.e., unique and the right size
std::unordered_set<ngraph::AxisVector::value_type> axes;
for (auto o : order)
{
if (o >= 0 && o < in_shape.size() && !axes.count(o))
{
axes.insert(o);
}
else
{
numpy_transpose_error(order, in_shape);
}
}
}
else
{
numpy_transpose_error(order, in_shape);
}
// create output shape
Shape out_shape;
for (size_t i = 0; i < in_shape.size(); ++i)
out_shape.push_back(in_shape[order[i]]);
// do the reshaping with the order
return std::make_shared<ngraph::op::Reshape>(node, order, out_shape);
}
} // namespace builder
} // namespace ngraph
/*
Copyright 2017 Nervana Systems Inc.
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.
*/
#pragma once
#include "ngraph/common.hpp"
#include "ngraph/function.hpp"
#include "ngraph/node.hpp"
#include "ngraph/ops/constant.hpp"
#include "ngraph/ops/parameter.hpp"
#include "ngraph/ops/reduce.hpp"
#include "ngraph/types/type.hpp"
namespace ngraph
{
namespace builder
{
/// \brief Implement's Numpy's multidimensional transpose op. Doubles as DimShuffle.
///
/// If `order` is empty, the vector is transposed by reversing it's axes, i.e.
///
/// shape [1,2,4] becomes shape [4,2,1]
///
/// If `order` is provided, it should be a vector of unique axis positions ranging
/// from 0 to N-1, when N is the length of the input shape. In this case, numpy_transpose acts
/// like dimshuffle, so
///
/// shape [1,2,4] with order [1,2,0] becomes shape [2,4,1]
///
/// | | Type | Description |
/// | ---------------- | ------------------------------------- | ------------------------------------------------------- |
/// | `node` | \f$E[d_0,\dots,d_{n-1}]~(n \geq 0)\f$ | An input tensor of any shape |
/// | `order` | AxisVector (empty default) | The axes to eliminate through reduction (0 indexed). |
///
/// ## Output
///
/// | Type | Description |
/// | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
/// | \f$E[d_{n-1},\dots,d_0)]\textit{ or }E[d_{order[0]},\dots,d_{order[n-1]}]\f$ | The tensor \f$T\f$, where \f$T\f$ is the input tensor with the axes reordered via Numpy Transpose rules |
std::shared_ptr<Node> numpy_transpose(const std::shared_ptr<Node>& node,
AxisVector order = {});
} // namespace builder
} // namespace ngraph
......@@ -42,6 +42,7 @@
/// recipes, for example auto-broadcast.
#include "ngraph/builder/autobroadcast.hpp"
#include "ngraph/builder/numpy_transpose.hpp"
#include "ngraph/builder/reduce_ops.hpp"
#include "ngraph/common.hpp"
#include "ngraph/descriptor/buffer.hpp"
......
......@@ -22,10 +22,10 @@ include_directories(
)
set (SRC
builder_autobroadcast.cpp
builder_reduce_ops.cpp
autodiff.cpp
build_graph.cpp
builder.cpp
builder_autobroadcast.cpp
copy.cpp
eigen.cpp
element_type.cpp
......
......@@ -90,20 +90,20 @@ std::shared_ptr<ngraph::runtime::TensorView> make_reduce_result_false(
return result;
}
TEST(builder_reduce_ops, l2_norm)
TEST(builder, l2_norm)
{
auto result = make_reduce_result(builder::l2_norm);
ASSERT_TRUE(
all_close((vector<float>{5.9160797831f, 7.48331477355f}), result->get_vector<float>()));
}
TEST(builder_reduce_ops, mean)
TEST(builder, mean)
{
auto result = make_reduce_result(builder::mean);
ASSERT_TRUE(all_close((vector<float>{3, 4}), result->get_vector<float>()));
}
TEST(builder_reduce_ops, std_dev)
TEST(builder, std_dev)
{
auto result = make_reduce_result_false(builder::std_dev);
ASSERT_TRUE(
......@@ -112,7 +112,7 @@ TEST(builder_reduce_ops, std_dev)
ASSERT_TRUE(all_close((vector<float>{2, 2}), result->get_vector<float>()));
}
TEST(builder_reduce_ops, variance)
TEST(builder, variance)
{
auto result = make_reduce_result_false(builder::variance);
ASSERT_TRUE(
......@@ -120,3 +120,31 @@ TEST(builder_reduce_ops, variance)
result = make_reduce_result_true(builder::variance);
ASSERT_TRUE(all_close((vector<float>{4, 4}), result->get_vector<float>()));
}
TEST(builder, numpy_transpose)
{
// 2D Transpose
Shape shape{2, 4};
auto param = std::make_shared<op::Parameter>(ngraph::element::Float32::element_type(), shape);
auto transposed = std::dynamic_pointer_cast<op::Reshape>(builder::numpy_transpose(param));
EXPECT_EQ(Shape({4, 2}), transposed->get_output_shape());
// Multidimensional Transpose
shape = Shape{2, 4, 8};
param = std::make_shared<op::Parameter>(ngraph::element::Float32::element_type(), shape);
transposed = std::dynamic_pointer_cast<op::Reshape>(builder::numpy_transpose(param));
EXPECT_EQ(Shape({8, 4, 2}), transposed->get_output_shape());
// Dimshuffle
shape = Shape{2, 4, 8};
param = std::make_shared<op::Parameter>(ngraph::element::Float32::element_type(), shape);
transposed = std::dynamic_pointer_cast<op::Reshape>(
builder::numpy_transpose(param, AxisVector{2, 0, 1}));
EXPECT_EQ(Shape({8, 2, 4}), transposed->get_output_shape());
// Bad Orders
EXPECT_ANY_THROW(
std::dynamic_pointer_cast<op::Reshape>(builder::numpy_transpose(param, AxisVector{2})));
EXPECT_ANY_THROW(std::dynamic_pointer_cast<op::Reshape>(
builder::numpy_transpose(param, AxisVector{2, 2, 1})));
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment