1 //*****************************************************************************
2 // Copyright 2017-2020 Intel Corporation
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
8 // http://www.apache.org/licenses/LICENSE-2.0
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //*****************************************************************************
22 #include "ngraph/except.hpp"
23 #include "ngraph/node.hpp"
24 #include "ngraph/op/broadcast.hpp"
25 #include "ngraph/op/constant.hpp"
31 class numpy_autobroadcast_incompatible_shapes : public ngraph::ngraph_error
34 numpy_autobroadcast_incompatible_shapes(const ngraph::Shape& shape1,
35 const ngraph::Shape& shape2);
38 const ngraph::Shape m_shape1;
39 const ngraph::Shape m_shape2;
41 static std::string error_str(const ngraph::Shape& shape1, const ngraph::Shape& shape2);
45 /// \brief Broadcast all values, if necessary, to obtain equal shapes according
46 /// to NumPy's auto-broadcasting scheme.
48 /// \note There are some shape combinations which the autobroadcast algoritm cannot
49 /// handle. An exception is thrown when such combinations are provided to this
52 /// \param values Vector of output values.
54 /// \exception ngraph::builder::numpy_autobroadcast_incompatible_shapes
56 /// \return Vector of broadcasted values.
58 OutputVector numpy_broadcast_outputs(const OutputVector& values);
61 /// \brief Broadcast input value to provided shape using NumPy's auto-broadcasting
64 /// \param value Input value
65 /// \param shape Requested output shape
67 /// \return Node producing values with requested shape.
69 std::shared_ptr<Node> numpy_broadcast(const Output<Node>& value, const Shape& shape);
71 /// \brief Wrap two graph values, if necessary, to obtain values with identical shapes,
72 /// using NumPy's auto-broadcast rules.
74 /// The elements in the std::pair returned by this function correspond to those supplied
75 /// in the std::pair provided via \p args.
77 /// If \p args.first and \p args.second produce identical shapes, then the returned
78 /// std::pair will have the same value as \p args.
80 /// If \p args.first and \p args.second produce different shapes, then this function creates
81 /// new ngraph::op::Reshape and/or ngraph::op::Broadcast nodes, as needed, to wrap
82 /// \p args.first and/or \p args.second in a manner that yields values with the same shape.
84 /// There are some shape combinations which the autobroadcast algoritm cannot handle.
85 /// An exception is thrown when such combinations are provided to this function.
88 /// - \p args.first is not null
89 /// - \p args.second is not null
92 /// - The ngraph::Node objects pointed to by \p args.first and \p args.second have not been
93 /// altered by this function, except by possibly having added consumers of their values.
95 /// - If an exception was not thrown, then the return value's \p first and \p second
96 /// elements point to ngraph::Node objects whose output values have the same shape.
98 /// \exception ngraph::builder::numpy_autobroadcast_incompatible_shapes
99 std::pair<std::shared_ptr<Node>, std::shared_ptr<Node>>
100 numpy_broadcast(const std::pair<Output<Node>, Output<Node>>& args);
102 /// \brief Broadcast shape of two nodes to make them compatible for a matrix
105 /// \note This function is reflecting broadcasting behaviour of NumPy's `matmul`
107 /// (https://docs.scipy.org/doc/numpy/reference/generated/numpy.matmul.html)
108 /// This mean that only \"stack of matrices\" axes are bidirectionally
109 /// broadcasted. The last two dimension are left untouched.
111 /// \param[in] left The Node providing data for the left-hand side of matrix
113 /// \param[in] right The Node providing data for the right-hand side of matrix
116 /// \return The vector containing both outputs broadcasted.
118 OutputVector numpy_broadcast_for_matmul_operation(const Output<Node>& left,
119 const Output<Node>& right);
121 /// \brief Cast shape of all input nodes for an element-wise operation that requires
122 /// shape-compatibility
124 /// \param inputs Original list of inputs
125 /// \param axis Index starting to align
127 /// \return pdpd-style broadcasted list of nodes.
128 OutputVector pdpd_broadcast(const OutputVector& inputs, int64_t axis);
130 /// \brief Generate a list of broadcast axes.
132 /// \details Informally, a broadcast "adds" axes to the input tensor, replicating
133 /// elements from the input tensor as needed to fill the new dimensions.
134 /// Function calculate which of the output axes are added in this way.
136 /// \param output_shape The new shape for the output tensor.
137 /// \param input_shape The shape of input tensor.
138 /// \param start_match_axis The axis along which we want to replicate elements.
139 /// The starting axis position (0-based) int the output
140 /// shape from which the current shape of the tensor
141 /// matches the desired new shape.
143 /// \return The indices of added axes.
144 std::shared_ptr<Node> calculate_broadcast_axes(const Shape& output_shape,
145 const Shape& input_shape,
146 std::size_t start_match_axis);
149 /// \brief Calculate the output shape of numpy-style broadcast operation for all input
152 /// This function finds the maximum tensor shape that will be the result of
153 /// element-wise operation that will be applied to the input shapes vector.
154 /// The function also prepares the shape of each input for the element-wise
155 /// operation by left-padding those shapes so that their rank is equal to the
156 /// left_shape's rank.
158 /// \param input_shapes A vector of input shapes for which a common shape should be
161 /// \return A pair that contains the target shape as its first object and a vector of
162 /// padded input shapes ready to be broadcasted as the second object
164 std::pair<Shape, std::vector<Shape>>
165 get_numpy_broadcast_shapes(const std::vector<Shape>& input_shapes);
167 inline std::shared_ptr<Node> make_broadcast_node(const Output<Node>& value,
168 const Shape& new_shape,
169 std::size_t start_match_axis)
172 op::Constant::create(element::u64, Shape{new_shape.size()}, new_shape);
173 return std::make_shared<op::v1::Broadcast>(
176 calculate_broadcast_axes(new_shape, value.get_shape(), start_match_axis));
182 /// \brief Broadcast right node to left node's shape using legacy scheme.
184 /// \param[in] left The left hand side node of binary operation.
185 /// \param[in] right The right hand side node of binary operation. The one
186 /// to be broadcasted.
187 /// \param[in] start_match_axis The axis index starting mutually equal shapes
190 /// \return The Output object connected to node producing broadcasted right node.
192 Output<Node> legacy_broadcast_for_binary_operation(const Output<Node>& left,
193 const Output<Node>& right,
194 size_t start_match_axis);
197 /// \brief Reconstructs axes mapping vector for Broadcast:v1 operation.
199 /// \param[in] output_shape The output shape of Broadcast operation.
200 /// \param[in] broadcast_axes The broadcast axes used for Broadcast:v0 operator.
202 /// \return The vector with axes indexes mapping .
204 std::vector<std::size_t> get_axes_mapping(const Shape& output_shape,
205 const AxisSet& broadcast_axes);
208 /// \brief Creates Node returning the axes mapping for Broadcast:v1 operation.
210 /// \param[in] output_shape The output shape of Broadcast operation.
211 /// \param[in] input_shape The input shape.
212 /// \param[in] start_match_axis The axis index at which input shape starts to be
213 /// identical as the output shape.
215 /// \return Returns the Output object pointing to node with the axes mapping.
217 Output<Node> get_axes_mapping_output(const Shape& output_shape,
218 const Shape& input_shape,
219 std::size_t start_match_axis);
222 /// \brief Creates Node returning the axes mapping for Broadcast:v1 operation.
224 /// \param[in] output_shape The output shape of Broadcast operation.
225 /// \param[in] broadcast_axes The broadcast axes used for Broadcast:v0 operator.
227 /// \return The Output object with Node returning axes mapping.
229 Output<Node> get_axes_mapping_output(const Shape& output_shape,
230 const AxisSet& broadcast_axes);
232 Output<Node> make_broadcast(const Output<Node>& node,
233 const Shape& target_shape,
234 const AxisSet& broadcast_axes);
236 Output<Node> make_broadcast(const Output<Node>& node,
237 const Shape& target_shape,
238 std::size_t start_match_axis);
240 } // namespace opset1
241 } // namespace builder
242 } // namespace ngraph