| 206 | } |
| 207 | |
| 208 | void ExecGraph::Validate() { |
| 209 | // The checks here are extremely defensive, but they're only run once. |
| 210 | auto err = [](auto &&... msg) { |
| 211 | throw std::logic_error(make_string("Internal error: ", msg...)); |
| 212 | }; |
| 213 | |
| 214 | if (validated_) |
| 215 | return; |
| 216 | if (nodes_.empty()) { |
| 217 | if (!edges_.empty()) |
| 218 | err("a graph without any node has edges."); |
| 219 | return; |
| 220 | } |
| 221 | std::unordered_set<const ExecNode *> known_nodes(nodes_.size()); |
| 222 | std::unordered_set<const ExecEdge *> known_edges(edges_.size()); |
| 223 | |
| 224 | for (auto &n : nodes_) |
| 225 | known_nodes.insert(&n); |
| 226 | for (auto &e : edges_) { |
| 227 | known_edges.insert(&e); |
| 228 | } |
| 229 | |
| 230 | for (auto &e : edges_) { |
| 231 | if (!known_nodes.count(e.producer)) |
| 232 | err("an edge's producer is not a known node pointer."); |
| 233 | if (!known_nodes.count(e.consumer)) |
| 234 | err("an edge's consumer is not a known node pointer."); |
| 235 | |
| 236 | if (e.producer_output_idx >= static_cast<int>(e.producer->outputs.size())) |
| 237 | err("producer output index is out of range."); |
| 238 | auto &consumer_edges = e.producer->outputs[e.producer_output_idx].consumers; |
| 239 | if (std::count(consumer_edges.begin(), consumer_edges.end(), &e) != 1) |
| 240 | err("the relevant producer's output doesn't have this edge as one of the consumers."); |
| 241 | |
| 242 | if (e.consumer->inputs[e.consumer_input_idx] != &e) |
| 243 | err("inconsistent edge consumer vs consumer node's input."); |
| 244 | } |
| 245 | |
| 246 | for (auto &n : nodes_) { |
| 247 | if (n.op) { |
| 248 | auto &spec = n.op->GetSpec(); |
| 249 | if (n.inputs.size() != static_cast<size_t>(spec.NumInput())) |
| 250 | err("a node has a different number of inputs than used in the OpSpec"); |
| 251 | if (n.outputs.size() != static_cast<size_t>(spec.NumOutput())) |
| 252 | err("a node has a different number of outputs than used in the OpSpec"); |
| 253 | } |
| 254 | |
| 255 | for (int o = 0, nout = n.outputs.size(); o < nout; o++) { |
| 256 | auto &consumers = n.outputs[o].consumers; |
| 257 | for (auto &e : consumers) { |
| 258 | if (!known_edges.count(e)) |
| 259 | err("a node's output is not a known edge pointer."); |
| 260 | if (e->producer != &n) |
| 261 | err("a node's output's producer should always point to self."); |
| 262 | if (e->producer_output_idx != o) |
| 263 | err("a node's output's index must match its position in the output array."); |
| 264 | } |
| 265 | } |