Compute Library
 22.11
OperatorGraph.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2022 Arm Limited.
3  *
4  * SPDX-License-Identifier: MIT
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24 #ifdef ENABLE_EXPERIMENTAL_DYNAMIC_FUSION
29 
30 namespace arm_compute
31 {
32 namespace experimental
33 {
34 namespace dynamic_fusion
35 {
36 namespace
37 {
38 void check_dependency_graph_op_success(OperatorGraph &graph, const Status &status)
39 {
40  if(!bool(status))
41  {
42  graph.impl()->status = Status{ status.error_code(), "Cycles or loops are not allowed" };
43  }
44 }
45 
46 // Check if there are more than one roots in the graph
47 void check_multiple_roots(OperatorGraph &graph)
48 {
49  if(graph.impl()->graph.get_root_ops().size() > 1)
50  {
51  graph.impl()->status = Status{ ErrorCode::RUNTIME_ERROR, "Multiple roots are not allowed" };
52  }
53 }
54 
55 void check_execution_shape(OperatorGraph &graph, const ITensorInfo &dst_info)
56 {
57  const auto roots = graph.impl()->graph.get_root_ops();
58  for(auto root : roots)
59  {
60  // We assume exactly 1 dst tensor for all operators
61  const auto root_info = graph.impl()->tensors[graph.impl()->graph.dst_tensors(root)[0]]->get_tensor_info();
62  for(unsigned int dim = 0; dim < root_info->num_dimensions(); ++dim)
63  {
64  if(root_info->dimension(dim) != dst_info.dimension(dim))
65  {
66  graph.impl()->status = Status{ ErrorCode::RUNTIME_ERROR, "Cannot change execution space" };
67  return;
68  }
69  }
70  }
71 }
72 } // namespace
73 
75  : _id{ id }
76 {
77 }
78 
80 {
81  return _id;
82 }
83 
84 bool operator<(const OpTensor &t0, const OpTensor &t1)
85 {
86  return t0.id() < t1.id();
87 }
88 
90  : _id{ id }
91 {
92 }
93 
95 {
96  return _id;
97 }
98 
99 bool operator<(const Operator &op0, const Operator &op1)
100 {
101  return op0.id() < op1.id();
102 }
103 
105  : _impl{ std::make_unique<Implementation>() }
106 {
107 }
108 
110 
112 {
113  return _impl.get();
114 }
115 
117 {
118  return _impl.get();
119 }
120 
122 {
123  return graph.impl()->status;
124 }
125 
127 {
128  auto id = graph.impl()->graph.add_tensor();
129  OpTensor op_tensor(id);
130  graph.impl()->add_tensor(id, &info);
131  return op_tensor;
132 }
133 
135 {
136  // Check if map is empty as a complex operator can only be root
137  if(!graph.impl()->graph.get_root_ops().empty())
138  {
139  graph.impl()->status = Status{ ErrorCode::RUNTIME_ERROR, "Cannot add multiple complex operators" };
140  return Operator{};
141  }
142 
143  std::pair<Status, DependencyGraph::Id> status_id;
144 
145  if(bias.id() == -1)
146  {
147  status_id = graph.impl()->graph.add_operator({ input.id(), weights.id() }, { dst.id() });
148  }
149  else
150  {
151  status_id = graph.impl()->graph.add_operator({ input.id(), weights.id(), bias.id() }, { dst.id() });
152  }
153 
154  check_dependency_graph_op_success(graph, status_id.first);
155 
156  Operator op_node(status_id.second);
157 
158  // Infer TensorInfo
159  OpTensorContent *dst_tensor = graph.impl()->tensors[dst.id()].get();
160  if(dst_tensor->get_tensor_info()->total_size() == 0)
161  {
162  auto src = graph.impl()->tensors[input.id()]->get_tensor_info();
163  auto wts = graph.impl()->tensors[weights.id()]->get_tensor_info();
164  auto shape = misc::shape_calculator::compute_deep_convolution_shape(src->tensor_shape(), src->data_layout(), wts->tensor_shape(), PadStrideInfo(desc.stride.x(), desc.stride.y(), desc.pad.left,
165  desc.pad.right,
166  desc.pad.top, desc.pad.bottom, DimensionRoundingType::FLOOR)); // use the default DimensionRoundingType
167 
168  auto_init_if_empty(*(dst_tensor->get_tensor_info()), src->clone()->set_tensor_shape(shape));
169  }
170 
171  // Check execution space
172  auto dst_info = dst_tensor->get_tensor_info();
173  check_execution_shape(graph, *dst_info);
174 
176  tensors.add_const_tensor(ACL_SRC_0, graph.impl()->tensors[input.id()].get());
177  tensors.add_const_tensor(ACL_SRC_1, graph.impl()->tensors[weights.id()].get());
178  if(bias.id() != -1)
179  {
180  tensors.add_const_tensor(ACL_SRC_2, graph.impl()->tensors[bias.id()].get());
181  }
182  tensors.add_const_tensor(ACL_DST_0, graph.impl()->tensors[dst.id()].get());
183 
184  graph.impl()->add_node<Conv2dContent>(status_id.second, desc, tensors);
185  check_multiple_roots(graph);
186 
187  return op_node;
188 }
189 
191 {
192  return add_op_conv2d(graph, desc, input, weights, OpTensor(-1), dst);
193 }
194 
196 {
197  auto node = utils::cast::polymorphic_downcast<Conv2dContent *>(graph.impl()->operators[conv2d.id()].get());
198  node->set_method(method);
199 }
200 
202 {
203  auto id = graph.impl()->graph.add_operator({ rhs.id(), lhs.id() }, { dst.id() });
204  check_dependency_graph_op_success(graph, id.first);
205 
206  Operator op_node(id.second);
207 
208  // Infer TensorInfo
209  auto node_lhs = graph.impl()->tensors[lhs.id()]->get_tensor_info();
210  auto node_rhs = graph.impl()->tensors[rhs.id()]->get_tensor_info();
211  OpTensorContent *node_dst = graph.impl()->tensors[dst.id()].get();
212 
213  if(node_dst->get_tensor_info()->total_size() == 0)
214  {
215  const std::pair<TensorShape, ValidRegion> broadcast_pair = ITensorInfo::broadcast_shape_and_valid_region(*node_rhs, *node_lhs);
216  auto_init_if_empty(*(node_dst->get_tensor_info()), node_lhs->clone()->set_tensor_shape(broadcast_pair.first));
217  }
218 
219  // Check execution space
220  auto dst_info = node_dst->get_tensor_info();
221  check_execution_shape(graph, *dst_info);
222 
224  tensors.add_const_tensor(ACL_SRC_0, graph.impl()->tensors[lhs.id()].get());
225  tensors.add_const_tensor(ACL_SRC_1, graph.impl()->tensors[rhs.id()].get());
226  tensors.add_const_tensor(ACL_DST_0, graph.impl()->tensors[dst.id()].get());
227  graph.impl()->add_node<ElementwiseContent>(id.second, desc, tensors);
228  check_multiple_roots(graph);
229 
230  return op_node;
231 }
232 
234 {
235  auto id = graph.impl()->graph.add_operator({ src.id() }, { dst.id() });
236  check_dependency_graph_op_success(graph, id.first);
237 
238  Operator op_node(id.second);
239 
240  // Infer TensorInfo
241  auto node_src = graph.impl()->tensors[src.id()]->get_tensor_info();
242  OpTensorContent *node_dst = graph.impl()->tensors[dst.id()].get();
243 
244  if(node_dst->get_tensor_info()->total_size() == 0)
245  {
246  auto_init_if_empty(*(node_dst->get_tensor_info()), *node_src);
247  }
248 
249  // Check execution space
250  auto dst_info = node_dst->get_tensor_info();
251  check_execution_shape(graph, *dst_info);
252 
254  tensors.add_const_tensor(ACL_SRC_0, graph.impl()->tensors[src.id()].get());
255  tensors.add_const_tensor(ACL_DST_0, graph.impl()->tensors[dst.id()].get());
256  graph.impl()->add_node<FloorContent>(id.second, desc, tensors);
257  check_multiple_roots(graph);
258 
259  return op_node;
260 }
261 } // namespace dynamic_fusion
262 } // namespace experimental
263 } // namespace arm_compute
264 #endif /* ENABLE_EXPERIMENTAL_DYNAMIC_FUSION */
Operator Tensor Handle This can be either an argument tensor, or an intermediate tensor linking 2 Ope...
Definition: OperatorGraph.h:68
Status validate(const OperatorGraph &op_graph)
Return the validity of op_graph, usually after performing an operation (e.g.
Id add_tensor(Id merge_tensor=empty_id())
Add a new tensor.
std::vector< Id > get_root_ops() const
Get all root ops.
Operator add_op_elementwise_op(OperatorGraph &graph, const ElementwiseDescriptor &desc, OpTensor lhs, OpTensor rhs, OpTensor dst)
Add op Elementwise to graph, and optionally describes fusion through passing of intermediate OpTensor...
Graph of operators to execute within a Workload.
Definition: OperatorGraph.h:42
size_t bottom
Padding across the height dimension on the bottom, in elements.
Definition: Types.h:796
size_t right
Padding across the width dimension on the right, in elements.
Definition: Types.h:794
Store the tensor&#39;s metadata.
Definition: ITensorInfo.h:40
size_t x() const
Semantic accessor for width as x.
Definition: Size2D.h:75
Status class.
Definition: Error.h:52
ConvolutionMethod
Available ConvolutionMethod.
Definition: Types.h:134
size_t left
Padding across the width dimension on the left, in elements.
Definition: Types.h:793
static std::pair< TensorShape, ValidRegion > broadcast_shape_and_valid_region(const Infos &... infos)
If infos are broadcast compatible tensor info&#39;s, return the broadcasted shape and the intersection of...
Definition: ITensorInfo.h:317
void force_conv2d_method(OperatorGraph &graph, Operator conv2d, ConvolutionMethod method)
(Only for Debuging and Testing) Force a conv2d method
SimpleTensor< float > src
Definition: DFT.cpp:155
Copyright (c) 2017-2022 Arm Limited.
size_t top
Padding across the height dimension on the top, in elements.
Definition: Types.h:795
bool auto_init_if_empty(ITensorInfo &info, const TensorShape &shape, int num_channels, DataType data_type, QuantizationInfo quantization_info=QuantizationInfo())
Auto initialize the tensor info (shape, number of channels and data type) if the current assignment i...
Padding and stride information class.
Definition: Types.h:669
Descriptor for Elementwise binary operation.
size_t y() const
Semantic accessor for height as y.
Definition: Size2D.h:84
Descriptor for Conv2dDescriptor operation.
Operator add_op_floor(OperatorGraph &graph, const FloorDescriptor &desc, OpTensor src, OpTensor dst)
Add op Floor to graph, and optionally describes fusion through passing of intermediate OpTensor s...
void add_const_tensor(int id, const TDesc *tensor)
Add const tensor to the pack.
ScaleKernelInfo info(interpolation_policy, default_border_mode, PixelValue(), sampling_policy, false)
virtual size_t total_size() const =0
Returns the total size of the tensor in bytes.
std::pair< Status, DependencyGraph::Id > add_operator(const std::vector< Id > &inputs, const std::vector< Id > &outputs)
Add a new operator.
OpTensor add_tensor(OperatorGraph &graph, ITensorInfo &info)
Associate a TensorInfo with a newly created OpTensor in the graph.
Operator Handle This can be used to further modify an existing operator.
bool operator<(const OpTensor &t0, const OpTensor &t1)
Provide order of OpTensor by checking if t0 is "lower than" t1.
TensorShape compute_deep_convolution_shape(const TensorShape &input_shape, DataLayout input_data_layout, const TensorShape &weights_shape, const PadStrideInfo &conv_info)
Calculate the deep convolution shape output shape of a tensor.
Operator add_op_conv2d(OperatorGraph &graph, const Conv2dDescriptor &desc, OpTensor input, OpTensor weights, OpTensor bias, OpTensor dst)
Add op Conv2d to graph.
const int32_t * bias