NVIDIA Holoscan SDK v3.8.0

Program Listing for File subgraph.hpp

Return to documentation for file (include/holoscan/core/subgraph.hpp)

Copy
Copied!
            

/* * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef HOLOSCAN_CORE_SUBGRAPH_HPP #define HOLOSCAN_CORE_SUBGRAPH_HPP #include <fmt/format.h> #include <memory> #include <string> #include <unordered_map> #include <unordered_set> #include <set> #include <utility> #include <type_traits> #include <vector> #include "io_spec.hpp" namespace holoscan { // Forward declarations class Fragment; class Operator; class OperatorSpec; struct InterfacePort { enum class PortType { kData, kExecution }; std::shared_ptr<Operator> internal_operator; std::string internal_port_name; bool is_input; PortType port_type = PortType::kData; }; class Subgraph { public: Subgraph(Fragment* fragment, const std::string& instance_name); virtual ~Subgraph() = default; virtual void compose() = 0; const std::string& instance_name() const { return instance_name_; } Fragment* fragment() { return fragment_; } const Fragment* fragment() const { return fragment_; } std::string get_qualified_name(const std::string& object_name, const std::string& type_name = "operator") const { if (instance_name_.empty()) { return object_name; } if (instance_name_ == object_name) { auto err_msg = fmt::format( "Subgraph: child {} name '{}' is the same as the parent subgraph name '{}'. " "This is not allowed.", type_name, object_name, instance_name_); HOLOSCAN_LOG_ERROR(err_msg); throw std::runtime_error(err_msg); } // Check if the operator name is already qualified with this instance name std::string expected_prefix = instance_name_ + "_"; if (object_name.size() >= expected_prefix.size() && object_name.substr(0, expected_prefix.size()) == expected_prefix) { HOLOSCAN_LOG_TRACE( "Subgraph: {} name '{}' is already qualified with instance name '{}', returning " "as-is", type_name, object_name, instance_name_); return object_name; // Already qualified, return as-is } return fmt::format("{}_{}", instance_name_, object_name); } // Fragment API compatibility - template method implementations in fragment.hpp template <typename OperatorT, typename StringT, typename... ArgsT, typename = std::enable_if_t<std::is_constructible_v<std::string, StringT>>> std::shared_ptr<OperatorT> make_operator(StringT name, ArgsT&&... args); template <typename OperatorT, typename... ArgsT> std::shared_ptr<OperatorT> make_operator(ArgsT&&... args); template <typename ConditionT, typename StringT, typename... ArgsT, typename = std::enable_if_t<std::is_constructible_v<std::string, StringT>>> std::shared_ptr<ConditionT> make_condition(StringT name, ArgsT&&... args); template <typename ConditionT, typename... ArgsT> std::shared_ptr<ConditionT> make_condition(ArgsT&&... args); template <typename ResourceT, typename StringT, typename... ArgsT, typename = std::enable_if_t<std::is_constructible_v<std::string, StringT>>> std::shared_ptr<ResourceT> make_resource(StringT name, ArgsT&&... args); template <typename ResourceT, typename... ArgsT> std::shared_ptr<ResourceT> make_resource(ArgsT&&... args); template <typename SubgraphT, typename... ArgsT> std::shared_ptr<SubgraphT> make_subgraph(const std::string& child_instance_name, ArgsT&&... args) { // Check for duplicate nested subgraph instance names if (nested_subgraph_instance_names_.find(child_instance_name) != nested_subgraph_instance_names_.end()) { throw std::runtime_error(fmt::format( "Subgraph::make_subgraph: Duplicate nested subgraph instance name '{}' in subgraph " "'{}'. Each nested subgraph instance must have a unique name within the same parent " "subgraph.", child_instance_name, instance_name_)); } // Create hierarchical instance name: parent_instance + "_" + child_instance std::string hierarchical_name = get_qualified_name(child_instance_name, "subgraph"); // Create nested subgraph with same Fragment* and hierarchical name auto nested_subgraph = std::make_shared<SubgraphT>(fragment_, hierarchical_name, std::forward<ArgsT>(args)...); // Compose immediately - operators added directly to Fragment's main graph if (!nested_subgraph->is_composed()) { nested_subgraph->compose(); nested_subgraph->set_composed(true); } // Store for interface port resolution nested_subgraphs_.push_back(nested_subgraph); // Register the child instance name nested_subgraph_instance_names_.insert(child_instance_name); return nested_subgraph; } void add_operator(std::shared_ptr<Operator> op); void add_flow(const std::shared_ptr<Operator>& upstream, const std::shared_ptr<Operator>& downstream, std::set<std::pair<std::string, std::string>> port_pairs = {}); void add_flow(const std::shared_ptr<Operator>& upstream_op, const std::shared_ptr<Subgraph>& downstream_subgraph, std::set<std::pair<std::string, std::string>> port_pairs = {}); void add_flow(const std::shared_ptr<Subgraph>& upstream_subgraph, const std::shared_ptr<Operator>& downstream_op, std::set<std::pair<std::string, std::string>> port_pairs = {}); void add_flow(const std::shared_ptr<Subgraph>& upstream_subgraph, const std::shared_ptr<Subgraph>& downstream_subgraph, std::set<std::pair<std::string, std::string>> port_pairs = {}); void add_flow(const std::shared_ptr<Operator>& upstream_op, const std::shared_ptr<Subgraph>& downstream_subgraph, const IOSpec::ConnectorType connector_type); void add_flow(const std::shared_ptr<Operator>& upstream_op, const std::shared_ptr<Subgraph>& downstream_subgraph, std::set<std::pair<std::string, std::string>> port_pairs, const IOSpec::ConnectorType connector_type); void add_flow(const std::shared_ptr<Subgraph>& upstream_subgraph, const std::shared_ptr<Operator>& downstream_op, const IOSpec::ConnectorType connector_type); void add_flow(const std::shared_ptr<Subgraph>& upstream_subgraph, const std::shared_ptr<Operator>& downstream_op, std::set<std::pair<std::string, std::string>> port_pairs, const IOSpec::ConnectorType connector_type); void add_flow(const std::shared_ptr<Subgraph>& upstream_subgraph, const std::shared_ptr<Subgraph>& downstream_subgraph, const IOSpec::ConnectorType connector_type); void add_flow(const std::shared_ptr<Subgraph>& upstream_subgraph, const std::shared_ptr<Subgraph>& downstream_subgraph, std::set<std::pair<std::string, std::string>> port_pairs, const IOSpec::ConnectorType connector_type); void add_interface_port(const std::string& external_name, const std::shared_ptr<Operator>& internal_op, const std::string& internal_port, bool is_input); void add_input_interface_port(const std::string& external_name, const std::shared_ptr<Operator>& internal_op, const std::string& internal_port); void add_output_interface_port(const std::string& external_name, const std::shared_ptr<Operator>& internal_op, const std::string& internal_port); void add_interface_port(const std::string& external_name, const std::shared_ptr<Subgraph>& internal_subgraph, const std::string& internal_interface_port, bool is_input); void add_input_interface_port(const std::string& external_name, const std::shared_ptr<Subgraph>& internal_subgraph, const std::string& internal_interface_port); void add_output_interface_port(const std::string& external_name, const std::shared_ptr<Subgraph>& internal_subgraph, const std::string& internal_interface_port); // ========== Execution Interface Port Methods ========== void add_input_exec_interface_port(const std::string& external_name, const std::shared_ptr<Operator>& internal_op); void add_output_exec_interface_port(const std::string& external_name, const std::shared_ptr<Operator>& internal_op); void add_input_exec_interface_port(const std::string& external_name, const std::shared_ptr<Subgraph>& internal_subgraph, const std::string& internal_interface_port); void add_output_exec_interface_port(const std::string& external_name, const std::shared_ptr<Subgraph>& internal_subgraph, const std::string& internal_interface_port); const std::unordered_map<std::string, InterfacePort>& interface_ports() const { return interface_ports_; } const std::unordered_map<std::string, InterfacePort>& exec_interface_ports() const { return exec_interface_ports_; } std::pair<std::shared_ptr<Operator>, std::string> get_interface_operator_port( const std::string& port_name) const; std::pair<std::shared_ptr<Operator>, std::string> get_exec_interface_operator_port( const std::string& port_name) const; bool is_composed() const { return is_composed_; } void set_composed(bool composed) { is_composed_ = composed; } private: std::unordered_map<std::string, InterfacePort> interface_ports_; std::unordered_map<std::string, InterfacePort> exec_interface_ports_; std::vector<std::shared_ptr<Subgraph>> nested_subgraphs_; std::unordered_set<std::string> nested_subgraph_instance_names_; bool is_composed_ = false; Fragment* fragment_; const std::string instance_name_; std::string format_port_list( const std::unordered_map<std::string, std::shared_ptr<IOSpec>>& ports); bool validate_operator_port(std::shared_ptr<Operator> op, const std::string& port_name, bool expect_input); bool validate_operator_exec_port(std::shared_ptr<Operator> op); bool check_exec_port_name_available(const std::string& external_name); void register_exec_interface_port(const std::string& external_name, const std::shared_ptr<Operator>& internal_op, const std::string& internal_port_name, bool is_input); std::pair<std::shared_ptr<Operator>, std::string> resolve_nested_exec_port( const std::string& external_name, const std::shared_ptr<Subgraph>& internal_subgraph, const std::string& internal_interface_port, bool expect_input); }; // Template method implementations moved to fragment.hpp to resolve circular dependency } // namespace holoscan #endif/* HOLOSCAN_CORE_SUBGRAPH_HPP */

© Copyright 2022-2025, NVIDIA. Last updated on Dec 1, 2025