Imported Upstream version 1.12.0
[platform/core/ml/nnfw.git] / compiler / luci / service / src / Validate.cpp
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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  */
16
17 #include "luci/Service/Validate.h"
18
19 #include <luci/IR/Nodes/CircleOutput.h>
20 #include <luci/Log.h>
21
22 #include <loco/IR/NodeShape.h>
23 #include <loco/Service/ShapeInference.h>
24 #include <loco/Service/TypeInference.h>
25
26 #include <cassert>
27 #include <vector>
28
29 namespace
30 {
31
32 std::ostream &operator<<(std::ostream &os, const loco::TensorShape &tensor_shape)
33 {
34   os << "[";
35   for (uint32_t r = 0; r < tensor_shape.rank(); ++r)
36   {
37     if (r)
38       os << ",";
39     os << tensor_shape.dim(r).value();
40   }
41   os << "]";
42   return os;
43 }
44
45 std::ostream &operator<<(std::ostream &os, const luci::CircleNode *circle_node)
46 {
47   os << "[";
48   for (uint32_t r = 0; r < circle_node->rank(); ++r)
49   {
50     if (r)
51       os << ",";
52     os << circle_node->dim(r).value();
53   }
54   os << "]";
55   return os;
56 }
57
58 /**
59  * @brief  returns a node that is CircleOutput with index is out_index in nodes
60  */
61 luci::CircleOutput *find_node(std::vector<loco::Node *> nodes, loco::GraphOutputIndex out_index)
62 {
63   for (auto node : nodes)
64   {
65     auto circle_output = dynamic_cast<luci::CircleOutput *>(node);
66     if (circle_output != nullptr)
67     {
68       if (circle_output->indexed() && circle_output->index() == out_index)
69         return circle_output;
70     }
71   }
72   return nullptr;
73 }
74
75 bool validate_shape_dtype(loco::Graph *g)
76 {
77   LOGGER(l);
78
79   auto output_nodes = loco::output_nodes(g);
80
81   auto count = g->outputs()->size();
82   for (uint32_t out = 0; out < count; ++out)
83   {
84     auto graph_out = g->outputs()->at(out);
85     auto out_index = graph_out->index();
86
87     auto circle_output = find_node(output_nodes, out_index);
88     assert(circle_output != nullptr);
89     assert(circle_output->from() != nullptr);
90     auto circle_node = loco::must_cast<luci::CircleNode *>(circle_output->from());
91
92     // Shape and dtype validation for CiecleOutputExclude is not needed
93     if (dynamic_cast<luci::CircleOutputExclude *>(circle_node))
94       continue;
95
96     assert(circle_node->shape_status() != luci::ShapeStatus::UNDEFINED);
97
98     // check if output node shape is same as graph output shape
99     auto go_tensor_shape = graph_out->shape();
100     assert(go_tensor_shape);
101
102     bool is_shape_valid = (circle_node->rank() == go_tensor_shape->rank());
103     for (uint32_t i = 0; is_shape_valid && i < circle_node->rank(); ++i)
104       if (circle_node->dim(i).value() != go_tensor_shape->dim(i).value())
105         is_shape_valid = false;
106
107     if (is_shape_valid == false)
108     {
109       INFO(l) << "[luci] Shape for output #" << out_index << " not same " << std::endl;
110       INFO(l) << "[luci]    " << circle_node->name() << " " << circle_node << " vs "
111               << *go_tensor_shape << std::endl;
112       return false;
113     }
114
115     // check if data type match
116     assert(circle_node->dtype() != loco::DataType::Unknown);
117     if (graph_out->dtype() != circle_node->dtype())
118     {
119       INFO(l) << "[luci] Type for output #" << out_index << " not same " << std::endl;
120       return false;
121     }
122   }
123
124   return true;
125 }
126
127 bool validate_shape_signature(loco::Graph *g)
128 {
129   LOGGER(l);
130
131   for (auto node : loco::postorder_traversal(loco::output_nodes(g)))
132   {
133     auto circle_node = loco::must_cast<luci::CircleNode *>(node);
134     const auto shape_signature = circle_node->shape_signature();
135
136     if (shape_signature.rank() == 0)
137       continue;
138
139     // Rank of shape and shape signature should be same
140     if (circle_node->rank() != shape_signature.rank())
141     {
142       INFO(l) << "[luci] Rank of shape signature for " << circle_node->name() << " do not match"
143               << std::endl;
144       return false;
145     }
146
147     bool has_unknown = false;
148
149     // If shape siganture is not -1, dimension value should be same
150     for (uint32_t d = 0; d < shape_signature.rank(); ++d)
151     {
152       if (shape_signature.dim(d) != -1 &&
153           shape_signature.dim(d) != (int32_t)(circle_node->dim(d).value()))
154       {
155         INFO(l) << "[luci] Dimension " << d << "of shape signature for " << circle_node->name()
156                 << " do not match" << std::endl;
157         return false;
158       }
159
160       if (shape_signature.dim(d) == -1)
161         has_unknown = true;
162     }
163
164     // Shape signature should have at least one -1 value.
165     if (!has_unknown)
166     {
167       INFO(l) << "[luci] Shape signature in " << circle_node->name()
168               << " do not have unknown dimension" << std::endl;
169       return false;
170     }
171   }
172
173   return true;
174 }
175
176 } // namespace
177
178 namespace luci
179 {
180
181 bool validate(loco::Graph *g)
182 {
183   if (!loco::valid(g))
184     return false;
185
186   if (!validate_shape_dtype(g))
187     return false;
188
189   if (!validate_shape_signature(g))
190     return false;
191
192   // TODO add more validation
193
194   return true;
195 }
196
197 } // namespace luci