* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* This program is free software, you can redistribute it and/or modify it under the terms and conditions of
* CANN Open Software License Agreement Version 2.0 (the "License").
* Please refer to the License for details. You may not use this file except in compliance with the License.
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
* See LICENSE in the root of the software repository for the full text of the License.
*/
* \file subgraph_to_function_check.cpp
* \brief
*/
#include "passes/pass_check/subgraph_to_function_checker.h"
#include "passes/pass_log/pass_log.h"
#include "passes/pass_utils/subgraph_utils.h"
#include "tilefwk/error_code.h"
#include "passes/pass_utils/pass_utils.h"
#define MODULE_NAME "SubgraphToFunction"
namespace npu {
namespace tile_fwk {
Status SubGraphToFuncChecker::NOPCheck(const Operation& op) const
{
if (!op.IsNOP()) {
APASS_LOG_ERROR_C(
OperationErr::OP_SPECIAL_CONSTRAINT, Elements::Operation, "op[%d] is not an NOP. %s", op.GetOpMagic(),
GetFormatBacktrace(op).c_str());
return FAILED;
}
if (op.GetIOperands().size() > 0) {
APASS_LOG_ERROR_C(
OperationErr::OP_INVALID_OPERAND_COUNT, Elements::Operation, "NOP[%d] has IOperands size %zu. %s",
op.GetOpMagic(), op.GetIOperands().size(), GetFormatBacktrace(op).c_str());
return FAILED;
}
if (op.GetInCtrlOperations().size() > 0) {
APASS_LOG_ERROR_C(
OperationErr::OP_INVALID_OPERAND_COUNT, Elements::Operation, "NOP[%d] has InCtrlOperations size %zu. %s",
op.GetOpMagic(), op.GetInCtrlOperations().size(), GetFormatBacktrace(op).c_str());
return FAILED;
}
if (op.GetOOperands().size() > 0) {
APASS_LOG_ERROR_C(
OperationErr::OP_INVALID_OPERAND_COUNT, Elements::Operation, "NOP[%d] has OOperands size %zu. %s",
op.GetOpMagic(), op.GetOOperands().size(), GetFormatBacktrace(op).c_str());
return FAILED;
}
if (op.GetOutCtrlOperations().size() > 0) {
APASS_LOG_ERROR_C(
OperationErr::OP_SPECIAL_CONSTRAINT, Elements::Operation, "NOP[%d] has OutCtrlOperations size %zu. %s",
op.GetOpMagic(), op.GetOutCtrlOperations().size(), GetFormatBacktrace(op).c_str());
return FAILED;
}
return SUCCESS;
}
Status SubGraphToFuncChecker::CheckSubGraphTopo(Function& function) const
{
auto operations = function.Operations();
int totalSubGraphNum = function.GetTotalSubGraphCount();
if (operations.size() > 0 && totalSubGraphNum <= 0) {
APASS_LOG_ERROR_C(
FunctionErr::FUNCTION_GRAPH_STRUCTURE, Elements::Function, "input totalSubGraphNum %d is invalid",
totalSubGraphNum);
return FAILED;
}
std::vector<bool> hitSubgraph = std::vector<bool>(totalSubGraphNum, false);
for (size_t i = 0; i < operations.size(); i++) {
auto& op = operations[i];
int subGraphId = op.GetSubgraphID();
if (subGraphId < 0 && NOPCheck(op) != SUCCESS) {
APASS_LOG_ERROR_C(
GraphErr::GRAPH_SUBGRAPH_ID_INVALID, Elements::Graph,
"operation %zu has negative subGraphID %d and failed NOP check. %s", i, subGraphId,
GetFormatBacktrace(op).c_str());
return FAILED;
}
if (subGraphId >= totalSubGraphNum) {
APASS_LOG_ERROR_C(
GraphErr::GRAPH_SUBGRAPH_ID_INVALID, Elements::Graph,
"operation %zu has subGraphID %d that exceeds totalSubGraphNum %d. %s", i, subGraphId, totalSubGraphNum,
GetFormatBacktrace(op).c_str());
return FAILED;
}
hitSubgraph[subGraphId] = true;
for (auto inOperand : op.GetIOperands()) {
for (auto parentOp : inOperand->GetProducers()) {
int parentSubGraphId = parentOp->GetSubgraphID();
if (parentSubGraphId > subGraphId) {
APASS_LOG_ERROR_C(
GraphErr::GRAPH_TOPOLOGY_STRUCTURE, Elements::Graph,
"operation %zu has subGraphId %d and parent subGraphId %d, parent subGraphId should be less "
"than or equal to subGraphId. %s",
i, subGraphId, parentSubGraphId, GetFormatBacktrace(op).c_str());
return FAILED;
}
}
}
}
for (int i = 0; i < totalSubGraphNum; i++) {
if (hitSubgraph[i] == false) {
APASS_LOG_ERROR_C(GraphErr::GRAPH_SUBGRAPH_EMPTY, Elements::Graph, "Subgraph %d is empty", i);
return FAILED;
}
}
return SUCCESS;
}
Status SubGraphToFuncChecker::EdgeIndexCheck(const bool found, const int newIndex, const size_t graphSize) const
{
if (!found) {
APASS_LOG_ERROR_C(FunctionErr::FUNCTION_GRAPH_CONNECTION, Elements::Function, "op magic not found");
return FAILED;
}
if (static_cast<size_t>(newIndex) >= graphSize) {
APASS_LOG_ERROR_C(
GraphErr::GRAPH_TOPOLOGY_STRUCTURE,
Elements::Graph, "parent index %d is larger than operations_ size %zu", newIndex, graphSize);
return FAILED;
}
return SUCCESS;
}
Status SubGraphToFuncChecker::BuildInGraph(Function& function)
{
auto operationViewer = function.Operations();
for (size_t i = 0; i < operationViewer.size(); i++) {
inGraph_[i].clear();
for (auto& inOperand : operationViewer[i].GetIOperands()) {
for (auto& parentOp : inOperand->GetProducers()) {
auto [parentSeqNo, found] = operationViewer.FindOpPosition(*parentOp);
if (EdgeIndexCheck(found, parentSeqNo, inGraph_.size()) != SUCCESS) {
APASS_LOG_ERROR_C(
FunctionErr::FUNCTION_GRAPH_CONNECTION, Elements::Function,
"error inserting op magic %d in function %d %s to inGraph. %s", parentOp->GetOpMagic(),
function.GetFuncMagic(), function.GetRawName().c_str(), GetFormatBacktrace(parentOp).c_str());
return FAILED;
}
auto it = std::find(inGraph_[i].begin(), inGraph_[i].end(), parentSeqNo);
if (it == inGraph_[i].end()) {
inGraph_[i].push_back(parentSeqNo);
}
}
}
for (const auto& inControlOp : operationViewer[i].GetInCtrlOperations()) {
auto [parentSeqNo, found] = operationViewer.FindOpPosition(*inControlOp);
if (EdgeIndexCheck(found, parentSeqNo, inGraph_.size()) != SUCCESS) {
APASS_LOG_ERROR_C(
FunctionErr::FUNCTION_GRAPH_CONNECTION, Elements::Function,
"Error inserting op magic %d in function %d %s to inGraph. %s", inControlOp->GetOpMagic(),
function.GetFuncMagic(), function.GetRawName().c_str(), GetFormatBacktrace(inControlOp).c_str());
return FAILED;
}
auto it = std::find(inGraph_[i].begin(), inGraph_[i].end(), parentSeqNo);
if (it == inGraph_[i].end()) {
inGraph_[i].push_back(parentSeqNo);
}
}
}
return SUCCESS;
}
Status SubGraphToFuncChecker::BuildOutGraph(Function& function)
{
auto operationViewer = function.Operations();
for (size_t i = 0; i < operationViewer.size(); i++) {
outGraph_[i].clear();
for (auto& outOperand : operationViewer[i].GetOOperands()) {
for (auto& childOp : outOperand->GetConsumers()) {
auto [childSeqNo, found] = operationViewer.FindOpPosition(*childOp);
if (EdgeIndexCheck(found, childSeqNo, inGraph_.size()) != SUCCESS) {
APASS_LOG_ERROR_C(
FunctionErr::FUNCTION_GRAPH_CONNECTION, Elements::Function,
"Error inserting op magic %d in function %d %s to outGraph_. %s", childOp->GetOpMagic(),
function.GetFuncMagic(), function.GetRawName().c_str(), GetFormatBacktrace(childOp).c_str());
return FAILED;
}
auto it = std::find(outGraph_[i].begin(), outGraph_[i].end(), childSeqNo);
if (it == outGraph_[i].end()) {
outGraph_[i].push_back(childSeqNo);
}
}
}
for (const auto& outControlOp : operationViewer[i].GetOutCtrlOperations()) {
auto [childSeqNo, found] = operationViewer.FindOpPosition(*outControlOp);
if (EdgeIndexCheck(found, childSeqNo, inGraph_.size()) != SUCCESS) {
APASS_LOG_ERROR_C(
FunctionErr::FUNCTION_GRAPH_CONNECTION, Elements::Function,
"Error inserting op magic %d in function %d %s to outGraph_. %s", outControlOp->GetOpMagic(),
function.GetFuncMagic(), function.GetRawName().c_str(), GetFormatBacktrace(outControlOp).c_str());
return FAILED;
}
auto it = std::find(outGraph_[i].begin(), outGraph_[i].end(), childSeqNo);
if (it == outGraph_[i].end()) {
outGraph_[i].push_back(childSeqNo);
}
}
std::sort(outGraph_[i].begin(), outGraph_[i].end());
}
return SUCCESS;
}
template <typename eType>
Status SubGraphToFuncChecker::InAndOutGraphConsistencyCheck(
const std::vector<std::vector<eType>>& inEdgeGraph, const std::vector<std::vector<eType>>& outEdgeGraph)
{
if (inEdgeGraph.size() != outEdgeGraph.size()) {
APASS_LOG_ERROR_F(
Elements::Graph, "inEdgeGraph size %zu, outEdgeGraph size %zu", inEdgeGraph.size(), outEdgeGraph.size());
return FAILED;
}
std::vector<size_t> nodeColIdx = std::vector<size_t>(inEdgeGraph.size(), 0);
for (size_t i = 0; i < inEdgeGraph.size(); i++) {
for (size_t j = 0; j < inEdgeGraph[i].size(); j++) {
size_t parentSeqNo = static_cast<size_t>(inEdgeGraph[i][j]);
if (nodeColIdx[parentSeqNo] >= outEdgeGraph[parentSeqNo].size()) {
APASS_LOG_ERROR_C(
GraphErr::GRAPH_EDGE_CONSISTENCY, Elements::Graph,
"node %zu, %zu th parentSeqNo %zu exceeds outgraph[%zu] size %zu", i, j, parentSeqNo, parentSeqNo,
outEdgeGraph[parentSeqNo].size());
return FAILED;
}
if (static_cast<size_t>(outEdgeGraph[parentSeqNo][nodeColIdx[parentSeqNo]++]) != i) {
APASS_LOG_ERROR_C(
GraphErr::GRAPH_EDGE_CONSISTENCY, Elements::Graph,
"node %zu, %zu th parentSeqNo %zu is not found in outgraph[%zu]", i, j, parentSeqNo, parentSeqNo);
return FAILED;
}
}
}
for (size_t i = 0; i < outEdgeGraph.size(); i++) {
if (outEdgeGraph[i].size() != nodeColIdx[i]) {
APASS_LOG_ERROR_C(
GraphErr::GRAPH_EDGE_CONSISTENCY, Elements::Graph,
"outEdgeGraph[%zu] has size %zu, but only %zu of them have been traversed", i, outEdgeGraph[i].size(),
nodeColIdx[i]);
return FAILED;
}
}
return SUCCESS;
}
Status SubGraphToFuncChecker::CheckInAndOutGraphMatch(Function& function)
{
auto operationViewer = function.Operations();
inGraph_.resize(operationViewer.size());
outGraph_.resize(operationViewer.size());
for (size_t i = 0; i < operationViewer.size(); i++) {
std::sort(inGraph_[i].begin(), inGraph_[i].end());
}
if (BuildInGraph(function) != SUCCESS) {
APASS_LOG_ERROR_F(Elements::Function, "Build inGraph failed");
return FAILED;
}
if (BuildOutGraph(function) != SUCCESS) {
APASS_LOG_ERROR_F(Elements::Function, "Build outGraph failed");
return FAILED;
}
if (InAndOutGraphConsistencyCheck(inGraph_, outGraph_) != SUCCESS) {
APASS_LOG_ERROR_F(Elements::Function, "Consistency check for input inGraph_ and outGraph_ failed");
return FAILED;
}
return SUCCESS;
}
bool SubGraphToFuncChecker::HasOnlyViewProducers(const std::set<Operation*, LogicalTensor::CompareOp>& producers)
{
if (producers.empty()) {
return false;
}
for (auto& producer : producers) {
if (producer->GetOpcode() != Opcode::OP_VIEW) {
return false;
}
}
return true;
}
Status SubGraphToFuncChecker::CheckSubGraphBoundary(Function& function)
{
auto operations = function.Operations();
for (size_t i = 0; i < operations.size(); i++) {
auto& op = operations[i];
int subGraphId = op.GetSubgraphID();
for (size_t k = 0; k < op.iOperand.size(); k++) {
auto iOperand = op.GetInputOperand(k);
auto producers = iOperand->GetProducers();
bool hasOnlyViewProducers = HasOnlyViewProducers(producers);
if (iOperand->GetMemoryTypeOriginal() == MemoryType::MEM_DEVICE_DDR && !SubgraphUtils::IsBoundary(iOperand) &&
!hasOnlyViewProducers) {
APASS_LOG_ERROR_C(
TensorErr::TENSOR_SUBGRAPH_BOUNDARY, Elements::Tensor,
"Input operand %zu of operation %zu (opdump: %s) is from DDR but not marked as subgraph boundary! "
"%s",
k, i, op.Dump().c_str(), GetFormatBacktrace(op).c_str());
return FAILED;
}
if (subGraphId != CommonUtils::GetTensorSubgraphID(iOperand) && !SubgraphUtils::IsBoundary(iOperand)) {
APASS_LOG_ERROR_C(
TensorErr::TENSOR_SUBGRAPH_BOUNDARY, Elements::Tensor,
"Input operand %zu of operation %zu (opdump: %s) has a consumer in a different subgraph but not "
"marked as subgraph boundary! %s",
k, i, op.Dump().c_str(), GetFormatBacktrace(op).c_str());
return FAILED;
}
if (IsCopyIn(op.GetOpcode()) && !SubgraphUtils::IsBoundary(iOperand)) {
APASS_LOG_ERROR_C(
TensorErr::TENSOR_SUBGRAPH_BOUNDARY, Elements::Tensor,
"Input operand %zu of IsCopyIn operation %zu (opdump: %s) is not marked as subgraph boundary! %s",
k, i, op.Dump().c_str(), GetFormatBacktrace(op).c_str());
return FAILED;
}
}
for (size_t k = 0; k < op.oOperand.size(); k++) {
auto oOperand = op.GetOutputOperand(k);
auto producers = oOperand->GetProducers();
bool hasOnlyViewProducers = HasOnlyViewProducers(producers);
if (oOperand->GetMemoryTypeOriginal() == MemoryType::MEM_DEVICE_DDR && !SubgraphUtils::IsBoundary(oOperand) &&
!hasOnlyViewProducers) {
APASS_LOG_ERROR_C(
TensorErr::TENSOR_SUBGRAPH_BOUNDARY, Elements::Tensor,
"Output operand %zu of operation %zu (opdump: %s) is from DDR but not marked as subgraph boundary! "
"%s",
k, i, op.Dump().c_str(), GetFormatBacktrace(op).c_str());
return FAILED;
}
if (subGraphId != CommonUtils::GetTensorSubgraphID(oOperand) && !SubgraphUtils::IsBoundary(oOperand)) {
APASS_LOG_ERROR_C(
TensorErr::TENSOR_SUBGRAPH_BOUNDARY, Elements::Tensor,
"Output operand %zu of operation %zu (opdump: %s) has a producer in a different subgraph but not "
"marked as subgraph boundary! %s",
k, i, op.Dump().c_str(), GetFormatBacktrace(op).c_str());
return FAILED;
}
if (IsCopyOut(op.GetOpcode()) && !SubgraphUtils::IsBoundary(oOperand)) {
APASS_LOG_ERROR_C(
TensorErr::TENSOR_SUBGRAPH_BOUNDARY, Elements::Tensor,
"Output operand %zu of IsCopyOut operation %zu (opdump: %s) is not marked as subgraph boundary! %s",
k, i, op.Dump().c_str(), GetFormatBacktrace(op).c_str());
return FAILED;
}
}
}
return SUCCESS;
}
Status SubGraphToFuncChecker::DoPreCheck(Function& function)
{
APASS_LOG_INFO_F(Elements::Operation, "Start PreCheck for SubgraphToFunction!");
if (CheckSubGraphTopo(function) != SUCCESS) {
APASS_LOG_ERROR_F(Elements::Function, "CheckSubGraphTopo failed");
return FAILED;
}
if (CheckSubGraphBoundary(function) != SUCCESS) {
APASS_LOG_ERROR_F(Elements::Function, "CheckSubGraphBoundary failed");
return FAILED;
}
if (CheckInAndOutGraphMatch(function) != SUCCESS) {
APASS_LOG_ERROR_F(Elements::Function, "check input ioperands and ooperands relation failed");
return FAILED;
}
return SUCCESS;
}
bool SubGraphToFuncChecker::foundNodeInNeighbor(const int dstNode, const std::vector<int>& searchGraph) const
{
auto it = std::find(searchGraph.begin(), searchGraph.end(), dstNode);
if (it != searchGraph.end()) {
return true;
}
return false;
}
Status SubGraphToFuncChecker::VerifyRedundantEdge(const int srcNode, const int dstNode) const
{
if (foundNodeInNeighbor(dstNode, colorOutGraph_[srcNode])) {
return SUCCESS;
}
for (int firstNbr : colorOutGraph_[srcNode]) {
if (foundNodeInNeighbor(dstNode, colorOutGraph_[firstNbr])) {
return SUCCESS;
}
for (int secondNbr : colorOutGraph_[firstNbr]) {
if (foundNodeInNeighbor(dstNode, colorOutGraph_[secondNbr])) {
return SUCCESS;
}
}
}
APASS_LOG_ERROR_F(
Elements::Graph, "source node %d and destination node %d are not related in colorOutGraph_ within three jumps",
srcNode, dstNode);
return FAILED;
}
Status SubGraphToFuncChecker::ColorOutGraphCheck(Function& function) const
{
std::vector<std::vector<bool>> hitEdgeMark = std::vector<std::vector<bool>>(colorOutGraph_.size());
for (size_t i = 0; i < colorOutGraph_.size(); i++) {
hitEdgeMark[i] = std::vector<bool>(colorOutGraph_[i].size(), false);
}
auto list = function.Operations();
for (size_t i = 0; i < list.size(); i++) {
int iSubGraphId = list[i].GetSubgraphID();
if (iSubGraphId < 0) {
continue;
}
for (int j : outGraph_[i]) {
int jSubGraphId = list[j].GetSubgraphID();
if (iSubGraphId == jSubGraphId || jSubGraphId < 0) {
continue;
}
auto it = std::find(colorOutGraph_[iSubGraphId].begin(), colorOutGraph_[iSubGraphId].end(), jSubGraphId);
if (it != colorOutGraph_[iSubGraphId].end()) {
int index = std::distance(colorOutGraph_[iSubGraphId].begin(), it);
hitEdgeMark[iSubGraphId][index] = true;
continue;
}
if (VerifyRedundantEdge(iSubGraphId, jSubGraphId) != SUCCESS) {
APASS_LOG_ERROR_F(
Elements::Graph,
"edge between original operator %zu with subgraph ID %d and operator %d with subgraph ID %d is "
"missed in colorOutGraph_",
i, iSubGraphId, static_cast<int>(j), jSubGraphId);
return FAILED;
}
}
}
for (size_t i = 0; i < hitEdgeMark.size(); i++) {
for (size_t j = 0; j < hitEdgeMark[i].size(); j++) {
if (hitEdgeMark[i][j] == false) {
APASS_LOG_ERROR_F(
Elements::Graph, "edge between %zu and %d on colorOutGraph_ has no correspondent edge in outGraph_",
i, colorOutGraph_[i][j]);
return FAILED;
}
}
}
return SUCCESS;
}
Status SubGraphToFuncChecker::DoPostCheck(Function& function)
{
APASS_LOG_INFO_F(Elements::Operation, "Start PostCheck for SubgraphToFunction!");
if (function.GetFunctionType() == FunctionType::STATIC) {
if (InAndOutGraphConsistencyCheck(colorInGraph_, colorOutGraph_) != SUCCESS) {
APASS_LOG_ERROR_F(Elements::Graph, "Consistency check for input colorInGraph_ and colorOutGraph_ failed");
return FAILED;
}
if (ColorOutGraphCheck(function) != SUCCESS) {
APASS_LOG_ERROR_F(Elements::Graph, "Consistency check for colorOutGraph_ and input failed");
return FAILED;
}
for (size_t i = 0; i < function.rootFunc_->topoInfo_.topology_.size(); i++) {
if (CheckReadyStateConsistency(function, i) != SUCCESS) {
APASS_LOG_ERROR_F(Elements::Graph, "Ready state inconsistency found for topology entry %zu", i);
return FAILED;
}
}
for (size_t i = 0; i < function.rootFunc_->Operations().size(); ++i) {
if (VerifySingleOpTopology(function, i) != SUCCESS) {
APASS_LOG_ERROR_F(Elements::Graph, "Failed to verify topology for operation %zu", i);
return FAILED;
}
}
}
return SUCCESS;
}
Status SubGraphToFuncChecker::VerifySingleOpTopology(Function& function, size_t opIdx)
{
const auto& callOps = function.rootFunc_->Operations();
Operation* currentOp = nullptr;
for (const auto& op : callOps) {
if (static_cast<size_t>(op.GetSubgraphID()) == opIdx) {
currentOp = const_cast<Operation*>(&op);
break;
}
}
if (currentOp == nullptr) {
APASS_LOG_ERROR_F(Elements::Operation, "Cannot find operation with subgraphId %zu", opIdx);
return FAILED;
}
auto consumers = currentOp->ConsumerOps();
auto producers = currentOp->ProducerOps();
APASS_LOG_DEBUG_F(Elements::Operation, "=================Call ===============%zu", opIdx);
for (auto& prod : producers) {
APASS_LOG_DEBUG_F(Elements::Operation, "Producer %s %d", prod->GetOpcodeStr().c_str(), prod->opmagic);
}
auto& topoInfo = function.rootFunc_->topoInfo_.topology_[opIdx];
std::unordered_set<Operation*> consumersNoSelf;
for (auto* cons : consumers) {
if (cons->opmagic != currentOp->opmagic) {
consumersNoSelf.insert(cons);
}
}
if (consumersNoSelf.size() < topoInfo.outGraph.size()) {
APASS_LOG_ERROR_F(
Elements::Operation, "Call %zu %d consumers size are %zu and %zu. %s", opIdx, currentOp->opmagic,
consumersNoSelf.size(), topoInfo.outGraph.size(), GetFormatBacktrace(currentOp).c_str());
return FAILED;
}
for (auto succ : topoInfo.outGraph) {
const int consumerSubgraphId = static_cast<int>(succ);
Operation* expectedConsumer = nullptr;
for (const auto& op : callOps) {
if (op.GetSubgraphID() == consumerSubgraphId) {
expectedConsumer = const_cast<Operation*>(&op);
break;
}
}
if (expectedConsumer == nullptr) {
APASS_LOG_ERROR_F(Elements::Graph, "Cannot find expected consumer with subgraphId %d", consumerSubgraphId);
return FAILED;
}
if (consumers.count(expectedConsumer) == 0) {
APASS_LOG_ERROR_F(
Elements::Operation, "Cannot find consumer %d for call %zu. %s", succ, opIdx,
GetFormatBacktrace(currentOp).c_str());
return FAILED;
}
}
return SUCCESS;
}
Status SubGraphToFuncChecker::CheckReadyStateConsistency(Function& function, size_t opIdx)
{
auto& topology = function.rootFunc_->topoInfo_.topology_;
int actualPredCount = 0;
for (size_t j = 0; j < topology.size(); j++) {
if (opIdx == j) {
continue;
}
auto& otherEntry = topology[j];
if (std::find(otherEntry.outGraph.begin(), otherEntry.outGraph.end(), opIdx) != otherEntry.outGraph.end()) {
actualPredCount++;
}
}
if (topology[opIdx].readyState != -actualPredCount) {
APASS_LOG_ERROR_F(
Elements::Graph, "Subgraph %zu has inconsistent readyState: actual=%d, expected=%zu", opIdx,
topology[opIdx].readyState, static_cast<size_t>(-actualPredCount));
return FAILED;
}
return SUCCESS;
}
void SubGraphToFuncChecker::SetInOutGraph(
const std::vector<std::vector<size_t>>& inGraph, const std::vector<std::vector<size_t>>& outGraph)
{
inGraph_ = inGraph;
outGraph_ = outGraph;
}
void SubGraphToFuncChecker::SetColorGraph(
const std::vector<std::vector<int>>& colorInGraph, const std::vector<std::vector<int>>& colorOutGraph)
{
colorInGraph_ = colorInGraph;
colorOutGraph_ = colorOutGraph;
}
template Status SubGraphToFuncChecker::InAndOutGraphConsistencyCheck<size_t>(
const std::vector<std::vector<size_t>>&, const std::vector<std::vector<size_t>>&);
}
}