Skip to content
Snippets Groups Projects
GraphView.cpp 54.9 KiB
Newer Older
            if ((oldOIn.size() == 1) && (inputParents[0].first)) {
                for (std::size_t i = 0; i < newOIn.size(); ++i) {
                    inputParents[0].first -> addChild(newOIn[i].first, inputParents[0].second, newOIn[i].second);
                for (std::size_t i = 0; i < oldOIn.size(); ++i) {
                        inputParents[i].first -> addChild(newOIn[0].first, inputParents[i].second, newOIn[0].second);
            for (std::size_t o = 0; o < oldOOut.size(); ++o) {
Olivier BICHLER's avatar
Olivier BICHLER committed
                for (const auto& child : outputChildren[o]) {
                    newOOut[o].first -> addChild(child.first, newOOut[o].second, child.second);
        else {
            for (const auto& nodePtr : oldNodes) {
                nodePtr->removeView(oldGraph);
            }
            for (const auto& nodePtr : newNodes) {
                nodePtr->removeView(newGraph);
    auto oldGOutputs = oldGraph->outputNodes();
    for (const auto& nodePtr : oldNodes) {
        bool removeFromGraphs = true;
        if (std::find(oldGOutputs.cbegin(), oldGOutputs.cend(), nodePtr) == oldGOutputs.cend()) {
            for (const auto& chPtr : nodePtr->getChildren()) {
                if (oldNodes.find(chPtr) == oldNodes.cend()) {
                    removeFromGraphs = false;
                }
            }
        }
        if (removeFromGraphs) {
            for (const auto& g : commonGraphViews) {
                g -> remove(nodePtr, false);
                g -> updateInputsOutputsDelete(nodePtr);
            }
            nodePtr -> resetConnections(true);
        }

    }

    for (const auto& nodePtr : newNodes) {
        for (const auto& g : commonGraphViews) {
            g -> add(nodePtr);
    for (const auto& nodePtr : oldNodes) {
        nodePtr -> removeView(oldGraph);
    for (const auto& nodePtr : newNodes) {
        nodePtr -> removeView(newGraph);
void Aidge::GraphView::updateInputsOutputs() {
  for (auto node : mNodes) {
    updateInputsOutputsNew(node);
  }
}

Olivier BICHLER's avatar
Olivier BICHLER committed
void Aidge::GraphView::updateInputsOutputsNew(std::shared_ptr<Node> newNode) {
  // Can be called several times with the same node, e.g. when addChild() is
  // called on a node already part of the GraphView. In this case, inputs/outputs
  // need to be updated!
  std::vector<std::pair<NodePtr, IOIndex_t>>::const_iterator newInputsInsertionPoint = mInputNodes.cend();
Olivier BICHLER's avatar
Olivier BICHLER committed

  // Remove inputs that are not input anymore because connected to newNode
  for (auto orderedChilds : newNode->getOrderedChildren()) {
    for (auto ch_ptr : orderedChilds) {
      // Check that newNode child is in current GraphView
      if (mNodes.find(ch_ptr) != mNodes.cend()) {
        IOIndex_t inputIdx = 0;
Olivier BICHLER's avatar
Olivier BICHLER committed
        for (const std::shared_ptr<Node>& pa_ptr : ch_ptr->getParents()) {
          // If newNode is connected to it
          if (pa_ptr == newNode) {
            const auto val = std::make_pair(ch_ptr, inputIdx);
            const auto iter = std::find(mInputNodes.cbegin(), mInputNodes.cend(), val);
Olivier BICHLER's avatar
Olivier BICHLER committed

            // Check that it was not already the case (if node UPDATE)
            if (iter != mInputNodes.cend()) { // newNode is linked to an actual inputNode to an input connection
              // The first old (removed) input becomes the insertion point for newNode GraphView inputs
              if (std::distance(newInputsInsertionPoint, iter) <= 0) {
                newInputsInsertionPoint = mInputNodes.erase(iter);
              }
              else {
                mInputNodes.erase(iter);
              }
    // Manage newNode parents
    // Check if any input connection is an input for the GraphView
    IOIndex_t inputIdx = 0U;
    for (const std::shared_ptr<Node>& pa_ptr : newNode->getParents()) {
        const auto val = std::make_pair(newNode, inputIdx);
        const auto it = std::find(mInputNodes.cbegin(), mInputNodes.cend(), val);
        if ((pa_ptr == nullptr) ||
            (mNodes.find(pa_ptr) == mNodes.cend())) {
            // Parent doesn't exist || Parent not in the graph
            if (it == mInputNodes.cend()) {
                // If node's inputs are inputs for the GraphView: add them to the input list
                // Addition rule:
                // - Inputs addition order follows node inputs order
                // - Inputs are inserted at the position of the first input removed
                newInputsInsertionPoint = mInputNodes.insert(newInputsInsertionPoint, val);
                newInputsInsertionPoint = std::next(newInputsInsertionPoint);
            }
        } else if (it != mInputNodes.cend()) {
            // Parent already in the graph SO edge is not an input anymore for the graph
            mInputNodes.erase(it);
        }
        ++inputIdx;
  std::vector<std::pair<NodePtr, IOIndex_t>>::const_iterator newOutputsInsertionPoint = mOutputNodes.cend();
Olivier BICHLER's avatar
Olivier BICHLER committed

  // Remove outputs that are not output anymore because connected to newNode
  for (const std::shared_ptr<Node>& parent : newNode->getParents()) {
    // Check that newNode parent is in current GraphView
    if (mNodes.find(parent) != mNodes.cend()) {
Olivier BICHLER's avatar
Olivier BICHLER committed
      IOIndex_t outputIdx = 0;
Olivier BICHLER's avatar
Olivier BICHLER committed
      for (auto orderedChilds : parent->getOrderedChildren()) {
        for (auto ch_ptr : orderedChilds) {
          // If newNode is connected to it
          if (ch_ptr == newNode) {
            const auto val = std::make_pair(parent, outputIdx);
            const auto iter = std::find(mOutputNodes.cbegin(), mOutputNodes.cend(), val);
Olivier BICHLER's avatar
Olivier BICHLER committed

            if (iter != mOutputNodes.cend()) {
              // The first old (removed) output becomes the insertion point for newNode GraphView outputs
              if (std::distance(newOutputsInsertionPoint, iter) <= 0) {
                newOutputsInsertionPoint = mOutputNodes.erase(iter);
              }
              else {
                mOutputNodes.erase(iter);
              }
Olivier BICHLER's avatar
Olivier BICHLER committed
      }
    }
  }

  // Check if node outputs are outputs for the GraphView and add them to the output list if so
  IOIndex_t outputIdx = 0;
  for (const auto& orderedChilds : newNode->getOrderedChildren()) {
Olivier BICHLER's avatar
Olivier BICHLER committed
    bool noInsideConnection = true;
    for (const auto& ch_ptr : orderedChilds) {
      if (mNodes.find(ch_ptr) != mNodes.cend()) {
Olivier BICHLER's avatar
Olivier BICHLER committed
        noInsideConnection = false;
Cyril Moineau's avatar
Cyril Moineau committed
        break;
      }
    }
Olivier BICHLER's avatar
Olivier BICHLER committed

    if (noInsideConnection) {
      const auto val = std::make_pair(newNode, outputIdx);
      // Output may be already be present (see addChild() with a node already in GraphView)
      if (std::find(mOutputNodes.cbegin(), mOutputNodes.cend(), val) == mOutputNodes.cend()) {
Olivier BICHLER's avatar
Olivier BICHLER committed
        newOutputsInsertionPoint = mOutputNodes.insert(newOutputsInsertionPoint, val);
        newOutputsInsertionPoint = std::next(newOutputsInsertionPoint);
Olivier BICHLER's avatar
Olivier BICHLER committed
      }
    }
    ++outputIdx;
  }
}

void Aidge::GraphView::updateInputsOutputsDelete(std::shared_ptr<Node> deletedNode) {
  std::vector<std::pair<NodePtr, IOIndex_t>>::const_iterator newInputsInsertionPoint = mInputNodes.cend();
Olivier BICHLER's avatar
Olivier BICHLER committed

  // Check if node inputs were inputs for the GraphView and remove them from the list if so
  for (IOIndex_t inputIdx = 0; inputIdx < deletedNode->getParents().size(); ++inputIdx) {
Olivier BICHLER's avatar
Olivier BICHLER committed
    const auto val = std::make_pair(deletedNode, inputIdx);
    const auto iter = std::find(mInputNodes.cbegin(), mInputNodes.cend(), val);
Olivier BICHLER's avatar
Olivier BICHLER committed

    if (iter != mInputNodes.cend()) {
      // The first old (removed) input becomes the insertion point for new GraphView inputs
      if (std::distance(newInputsInsertionPoint, iter) <= 0) {
        newInputsInsertionPoint = mInputNodes.erase(iter);
      }
      else {
        mInputNodes.erase(iter);
      }
Olivier BICHLER's avatar
Olivier BICHLER committed
    }
  }

  // Add child node inputs that become GraphView input following the removal of the node
  // Inputs addition order follows deletedNode outputs order
  for (auto orderedChilds : deletedNode->getOrderedChildren()) {
    for (auto ch_ptr : orderedChilds) {
      // Check that deletedNode child is in current GraphView
      if (mNodes.find(ch_ptr) != mNodes.cend()) {
        IOIndex_t inputIdx = 0;
Olivier BICHLER's avatar
Olivier BICHLER committed
        for (const std::shared_ptr<Node>& pa_ptr : ch_ptr->getParents()) {
          // If newNode was connected to it
          if (pa_ptr == deletedNode) {
            const auto val = std::make_pair(ch_ptr, inputIdx);
            if (std::find(mInputNodes.cbegin(), mInputNodes.cend(), val) == mInputNodes.cend()) {
              newInputsInsertionPoint = mInputNodes.insert(newInputsInsertionPoint, val);
              newInputsInsertionPoint = std::next(newInputsInsertionPoint);
            }

  std::vector<std::pair<NodePtr, IOIndex_t>>::const_iterator newOutputsInsertionPoint = mOutputNodes.cend();
Olivier BICHLER's avatar
Olivier BICHLER committed

  // Check if node outputs were outputs for the GraphView and remove them from the list if so
  for (IOIndex_t outputIdx = 0; outputIdx < deletedNode->getOrderedChildren().size(); ++outputIdx) {
Olivier BICHLER's avatar
Olivier BICHLER committed
    const auto val = std::make_pair(deletedNode, outputIdx);
    const auto iter = std::find(mOutputNodes.cbegin(), mOutputNodes.cend(), val);
Olivier BICHLER's avatar
Olivier BICHLER committed

    if (iter != mOutputNodes.cend()) {
      // The first old (removed) output becomes the insertion point for newNode GraphView outputs
      if (std::distance(newOutputsInsertionPoint, iter) <= 0) {
        newOutputsInsertionPoint = mOutputNodes.erase(iter);
      }
      else {
        mOutputNodes.erase(iter);
      }
Olivier BICHLER's avatar
Olivier BICHLER committed
    }
  }

  // Add parent node outputs that become GraphView output following the removal of the node
  // Outputs addition order follows deletedNode inputs order
  for (const std::shared_ptr<Node>& parent : deletedNode->getParents()) {
    if (mNodes.find(parent) != mNodes.end()) {
      IOIndex_t outputIdx = 0;
      for (auto orderedChilds : parent->getOrderedChildren()) {
        bool noInsideConnection = true;
        for (auto ch_ptr : orderedChilds) {
          if (mNodes.find(ch_ptr) != mNodes.end()) {
            noInsideConnection = false;
            break;
          }
        if (noInsideConnection) {
          const auto val = std::make_pair(parent, outputIdx);
          if (std::find(mOutputNodes.cbegin(), mOutputNodes.cend(), val) == mOutputNodes.cend()) {
            newOutputsInsertionPoint = mOutputNodes.insert(newOutputsInsertionPoint, val);
            newOutputsInsertionPoint = std::next(newOutputsInsertionPoint);
          }
        }
        ++outputIdx;

  if (deletedNode == mRootNode) {
    const std::pair<std::vector<NodePtr>, size_t> ranked_nodes = getRankedNodes();
Olivier BICHLER's avatar
Olivier BICHLER committed
    if(ranked_nodes.second== 0 || ranked_nodes.first.size() <= 1)
      mRootNode = nullptr;
    } else {
      // The new root node will be the second node in the order of ranked nodes
      setRootNode(*std::next(ranked_nodes.first.cbegin(),1));
    }
Cyril Moineau's avatar
Cyril Moineau committed

Olivier BICHLER's avatar
Olivier BICHLER committed
std::shared_ptr<Aidge::GraphView> Aidge::GraphView::cloneCallback(NodePtr(*cloneNode)(NodePtr)) const {
  std::shared_ptr<GraphView> newGraph = std::make_shared<GraphView>(mName);

  // Map for old node -> new node correspondance
  std::map<NodePtr, NodePtr> oldToNewNodes;

  for (const std::shared_ptr<Node> &node_ptr : mNodes) {
    auto clonedNode = cloneNode(node_ptr);
    if (clonedNode == nullptr) {
      AIDGE_ASSERT(node_ptr->getChildren().size() <= 1, "deleted nodes in GraphView::clone() cannot have multiple children");
Olivier BICHLER's avatar
Olivier BICHLER committed
      AIDGE_ASSERT(node_ptr->nbData() <= 1, "deleted nodes in GraphView::clone() cannot have multiple data input parents");
    }
    oldToNewNodes[node_ptr] = clonedNode;
  // For each node, convert old node -> new node connections
  for (auto &oldToNewNode : oldToNewNodes) {
    if (oldToNewNode.second == nullptr) {
      continue;  // deleted node

    // Connect parent nodes. Nodes that were removed with cloneNode() are set to nullptr
    size_t parentId = 0;
    for (auto parent : oldToNewNode.first->inputs()) {
      if (parent.first != nullptr) {
        while (oldToNewNodes[parent.first] == nullptr) {
          // Find next valid parent in line, going backward in the graph
          AIDGE_INTERNAL_ASSERT(parent.first->getChildren().size() == 1);
Olivier BICHLER's avatar
Olivier BICHLER committed
          AIDGE_INTERNAL_ASSERT(parent.first->nbData() <= 1);
          const auto& parents = parent.first->dataInputs();

          if (!parents.empty() && parents[0].first != nullptr // a valid parent exists
            && oldToNewNodes.find(parents[0].first) != oldToNewNodes.end()) // parent is in the GraphView
          {
            parent = parents[0];
          }
          else {
            break;
          }
        if (oldToNewNodes[parent.first]) {
          AIDGE_INTERNAL_ASSERT(oldToNewNodes[parent.first]->nbOutputs() == parent.first->nbOutputs());
          oldToNewNodes[parent.first]->addChild(oldToNewNode.second, parent.second, parentId);
        }
      ++parentId;
Olivier BICHLER's avatar
Olivier BICHLER committed
  // Once connected, add each new nodes to new GraphView
  // This has to be done in a second step to ensure that new GraphView inputs/outputs
  // are properly set (otherwise, some node's inputs/outputs may be wrongly registered as
  // GraphView inputs/outputs because not yet connected to other nodes)
  if (oldToNewNodes[mRootNode] != nullptr) {
    // Add root node first if is still exists!
    newGraph->add(oldToNewNodes[mRootNode], false);
  }

Olivier BICHLER's avatar
Olivier BICHLER committed
  for (auto &oldToNewNode : oldToNewNodes) {
    if (oldToNewNode.second == nullptr)
      continue;  // deleted node

    newGraph->add(oldToNewNode.second, false);
  }

  // Update cloned graph inputs/outputs order to match initial graph order
  auto newInputNodes = mInputNodes;
Olivier BICHLER's avatar
Olivier BICHLER committed
  for (auto it = newInputNodes.begin(); it != newInputNodes.end(); ) {
Olivier BICHLER's avatar
Olivier BICHLER committed
    // If input node was removed, find next valid input
    while (oldToNewNodes[it->first] == nullptr) {
      // Removed node should have only one connected output, otherwise cloning is invalid
      AIDGE_INTERNAL_ASSERT(it->first->getChildren().size() <= 1);
Olivier BICHLER's avatar
Olivier BICHLER committed
      bool found = false;

      if (it->first->getChildren().size() == 1) {
        auto child = *it->first->getChildren().begin();

        std::size_t inputIdx = 0;
        for (auto parent : child->getParents()) {
          if (parent == it->first) {
            it->first = child;
            it->second = inputIdx;
            found = true;
            break;
          }
          ++inputIdx;
Olivier BICHLER's avatar
Olivier BICHLER committed

    if (oldToNewNodes[it->first] == nullptr) {
      it = newInputNodes.erase(it);
    }
    else {
      it->first = oldToNewNodes[it->first];
Olivier BICHLER's avatar
Olivier BICHLER committed
      ++it;
Olivier BICHLER's avatar
Olivier BICHLER committed
    }
Olivier BICHLER's avatar
Olivier BICHLER committed
  }
  newGraph->setOrderedInputs(newInputNodes);

  auto newOutputNodes = mOutputNodes;
Olivier BICHLER's avatar
Olivier BICHLER committed
  for (auto it = newOutputNodes.begin(); it != newOutputNodes.end(); ) {
Olivier BICHLER's avatar
Olivier BICHLER committed
    // If output node was removed, find previous valid output
    while (oldToNewNodes[it->first] == nullptr) {
      // Removed node should have only one connected data input, otherwise cloning is invalid
Olivier BICHLER's avatar
Olivier BICHLER committed
      AIDGE_INTERNAL_ASSERT(it->first->nbData() <= 1);
Olivier BICHLER's avatar
Olivier BICHLER committed
      auto parents = it->first->dataInputs();

      if (!parents.empty() && parents[0].first != nullptr // a valid parent exists
        && oldToNewNodes.find(parents[0].first) != oldToNewNodes.end()) // parent is in the GraphView
      {
Olivier BICHLER's avatar
Olivier BICHLER committed
        *it = parents[0];
      }
      else {
        break;
      }
    }
Olivier BICHLER's avatar
Olivier BICHLER committed

    if (oldToNewNodes[it->first] == nullptr) {
      it = newOutputNodes.erase(it);
    }
    else {
      it->first = oldToNewNodes[it->first];
Olivier BICHLER's avatar
Olivier BICHLER committed
      ++it;
Olivier BICHLER's avatar
Olivier BICHLER committed
    }
Olivier BICHLER's avatar
Olivier BICHLER committed
  }
  newGraph->setOrderedOutputs(newOutputNodes);

std::shared_ptr<Aidge::GraphView> Aidge::getConnectedGraphView(std::shared_ptr<Node> node) {
Maxence Naud's avatar
Maxence Naud committed
  std::vector<NodePtr> foundNodes{node};
Maxence Naud's avatar
Maxence Naud committed
  for (std::size_t curNodeIdx = 0; curNodeIdx < foundNodes.size(); ++curNodeIdx) {
    NodePtr curNode = foundNodes[curNodeIdx];

    for (auto childs : curNode->getOrderedChildren()) {
      for (auto child : childs) {
        if (child != nullptr && std::find(foundNodes.begin(), foundNodes.end(), child) == foundNodes.end()) {
          foundNodes.push_back(child);
        }
      }
    }

    for (auto parent : curNode->getParents()) {
      if (parent != nullptr && std::find(foundNodes.begin(), foundNodes.end(), parent) == foundNodes.end()) {
        foundNodes.push_back(parent);
      }
    }
  }

  auto graph = std::make_shared<GraphView>();
  graph->add(node);
  graph->add({foundNodes.cbegin(), foundNodes.cend()});
  return graph;
}