holoscan::Subgraph

Beta
View as Markdown

A reusable subgraph that directly populates a Fragment’s operator graph.

Subgraph receives Fragment* during construction and directly adds operators and flows to the Fragment’s main graph during compose().

Usage example:

#include <holoscan/subgraph.hpp>

Example

class CameraSubgraph : public Subgraph {
public:
CameraSubgraph(Fragment* fragment, const std::string& name)
: Subgraph(fragment, name) {}
void compose() override {
auto source = make_operator<V4L2VideoOp>("source", from_config("v4l2"));
auto converter = make_operator<FormatConverterOp>("converter",
from_config("format_converter"));
add_flow(source, converter); // Directly added to Fragment's main graph
// Expose interface ports for external connections
// The "tensor" output port of converter will be exposed as "video_out"
add_output_interface_port("video_out", converter, "tensor");
}
};
// In Fragment::compose():
// Note that for subgraph name "camera1" and "camera2", the operator names will become
// "camera1_source", "camera2_source", "camera1_converter", "camera2_converter".
auto camera1 = make_subgraph<CameraSubgraph>("camera1");
auto camera2 = make_subgraph<CameraSubgraph>("camera2");
auto visualizer = make_operator<HolovizOp>("visualizer", from_config("holoviz"));
// Direct connection to other operators (or subgraphs) via interface ports
add_flow(camera1, visualizer, {{"video_out", "receivers"}});
add_flow(camera2, visualizer, {{"video_out", "receivers"}});

Constructors

Subgraph

holoscan::Subgraph::Subgraph(
Fragment *fragment,
const std::string &name,
const std::string &config_file = ""
)

Construct Subgraph with target Fragment.

Parameters

fragment
Fragment *

Target Fragment to populate with operators

name
const std::string &

Unique instance name for operator qualification

config_file
const std::string &Defaults to ""

Optional path to a YAML configuration file for this subgraph. If provided, the configuration is loaded before compose() is called, making from_config() available during composition.

Destructor

~Subgraph

virtual holoscan::Subgraph::~Subgraph() = default

Methods

compose

virtual void holoscan::Subgraph::compose()

Define the internal structure of the subgraph.

This method should create operators and flows, which will be directly added to the Fragment’s main graph with qualified names.

name

const std::string & holoscan::Subgraph::name() const

Get the name for this subgraph.

instance_name

const std::string & holoscan::Subgraph::instance_name() const

Get the instance name for this subgraph.

Deprecated

Use name() instead. This method will be removed in a future release.

fragment

Fragment * holoscan::Subgraph::fragment()

Get the Fragment that this subgraph belongs to.

Returns: Pointer to the fragment this subgraph belongs to

get_qualified_name

std::string holoscan::Subgraph::get_qualified_name(
const std::string &object_name,
const std::string &type_name = "operator"
) const

Create qualified operator name: subgraph_name + ”_” + operator_name.

make_operator

template <typename OperatorT,
typename StringT,
typename... ArgsT,
typename = std::enable_if_t<std::is_constructible_v<std::string, StringT>>>
std::shared_ptr<OperatorT> holoscan::Subgraph::make_operator(
StringT name,
ArgsT &&... args
)

make_condition

template <typename ConditionT,
typename StringT,
typename... ArgsT,
typename = std::enable_if_t<std::is_constructible_v<std::string, StringT>>>
std::shared_ptr<ConditionT> holoscan::Subgraph::make_condition(
StringT name,
ArgsT &&... args
)

make_resource

template <typename ResourceT,
typename StringT,
typename... ArgsT,
typename = std::enable_if_t<std::is_constructible_v<std::string, StringT>>>
std::shared_ptr<ResourceT> holoscan::Subgraph::make_resource(
StringT name,
ArgsT &&... args
)

register_service

template <typename ServiceT>
bool holoscan::Subgraph::register_service(
const std::shared_ptr<ServiceT> &svc,
std::string_view id = ""
)

Register an existing service instance with the fragment.

Registers an already created service instance with the fragment. This allows the service to be retrieved later using Fragment::service().

Returns: true if the service was successfully registered, false otherwise.

Template parameters

ServiceT
typename

The type of the service (must inherit from Resource or FragmentService).

Parameters

svc
const std::shared_ptr<ServiceT> &

The shared pointer to the service instance to register.

id
std::string_viewDefaults to ""

The identifier for the service registration. If empty, uses the service type or resource name as identifier.

make_subgraph

template <typename SubgraphT, typename... ArgsT>
std::shared_ptr<SubgraphT> holoscan::Subgraph::make_subgraph(
const std::string &child_name,
ArgsT &&... args
)

Create a nested subgraph within this subgraph.

This enables hierarchical Subgraph composition. The nested Subgraph will use the same Fragment* and will have its operators added directly to the Fragment’s main graph with hierarchical qualified names (parent_name_child_name_operator).

Returns: Shared pointer to the nested subgraph

Template parameters

SubgraphT
typename

The nested subgraph class type

Parameters

child_name
const std::string &

Name for the nested subgraph

args
ArgsT &&...

Additional arguments for the nested subgraph constructor

add_operator

void holoscan::Subgraph::add_operator(
const std::shared_ptr<Operator> &op
)

Add an operator to the Fragment’s main graph with qualified name.

This directly calls fragment_->add_operator() with a qualified name, eliminating the need for intermediate graph storage.

add_subgraph

void holoscan::Subgraph::add_subgraph(
const std::shared_ptr<Subgraph> &subgraph
)

Add a pre-constructed subgraph as a nested subgraph, taking ownership.

This method stores the subgraph in nested_subgraphs_ for lifetime management and interface port resolution.

Two paths are supported:

  • Not yet composed (C++ factory pattern): The subgraph’s name is qualified with this parent’s name prefix, then compose() is called. The subgraph should be constructed with an unqualified name.
  • Already composed (Python path): The name must already be qualified with this parent’s prefix (e.g. constructed with this subgraph as the parent). The subgraph is stored as-is.

Example (C++ factory pattern):

Throws: std::runtime_error if a nested subgraph with the same name already exists.

Throws: std::runtime_error if already composed but the name is not properly qualified.

Parameters

subgraph
const std::shared_ptr<Subgraph> &

The subgraph to be added.

Example

void compose() override {
auto camera = camera_factory(fragment(), "camera1", config);
add_subgraph(camera); // qualifies name to "parent_camera1", composes, takes ownership
add_output_interface_port("video_out", camera, "video_out");
}

add_flow

void holoscan::Subgraph::add_flow(
const std::shared_ptr<Operator> &upstream,
const std::shared_ptr<Operator> &downstream,
std::set<std::pair<std::string, std::string>> port_pairs = {}
)

Add a flow between two operators directly in the Fragment’s main graph.

This directly calls fragment_->add_flow(), eliminating the need for intermediate flow storage.

bind_input_topic

void holoscan::Subgraph::bind_input_topic(
const std::string &interface_port,
const std::string &topic,
const std::optional<nvidia::gxf::QoSProfile> &qos = std::nullopt,
bool replace_connector = false
)

Bind an input interface port to a Pub/Sub topic.

Applies the topic binding to every internal operator port mapped from this interface port.

Parameters

interface_port
const std::string &

Interface port name on this subgraph.

topic
const std::string &

Topic name to subscribe to.

qos
const std::optional<nvidia::gxf::QoSProfile> &Defaults to std::nullopt

Optional QoS profile override. When nullopt, any QoS already configured on the mapped internal ports is preserved.

replace_connector
boolDefaults to false

If true, replace explicitly non-PubSub connectors on mapped internal ports with Pub/Sub connectors.

bind_output_topic

void holoscan::Subgraph::bind_output_topic(
const std::string &interface_port,
const std::string &topic,
const std::optional<nvidia::gxf::QoSProfile> &qos = std::nullopt,
bool replace_connector = false
)

Bind an output interface port to a Pub/Sub topic.

Applies the topic binding to every internal operator port mapped from this interface port.

Parameters

interface_port
const std::string &

Interface port name on this subgraph.

topic
const std::string &

Topic name to publish to.

qos
const std::optional<nvidia::gxf::QoSProfile> &Defaults to std::nullopt

Optional QoS profile override. When nullopt, any QoS already configured on the mapped internal ports is preserved.

replace_connector
boolDefaults to false

If true, replace explicitly non-PubSub connectors on mapped internal ports with Pub/Sub connectors.

set_dynamic_flows

virtual void holoscan::Subgraph::set_dynamic_flows(
const std::shared_ptr<Operator> &op,
const std::function<void(const std::shared_ptr<Operator> &)> &dynamic_flow_func
)

Set a callback function to define dynamic flows for an operator at runtime.

This method allows operators to modify their connections with other operators during execution. The callback function is called after the operator executes and can add dynamic flows using the operator’s add_dynamic_flow() methods.

Parameters

op
const std::shared_ptr<Operator> &

The operator to set dynamic flows for

dynamic_flow_func
const std::function<void(const std::shared_ptr<Operator> &)> &

The callback function that defines the dynamic flows. Takes a shared pointer to the operator as input and returns void.

add_data_logger

void holoscan::Subgraph::add_data_logger(
const std::shared_ptr<DataLogger> &logger
)

Add a data logger to the fragment.

This method dispatches to the fragment’s add_data_logger method.

Parameters

logger
const std::shared_ptr<DataLogger> &

The data logger to add.

add_interface_port

void holoscan::Subgraph::add_interface_port(
const std::string &external_name,
const std::shared_ptr<Operator> &internal_op,
std::optional<std::string> internal_port = std::nullopt,
std::optional<bool> is_input = std::nullopt
)

Add an interface port that can be connected from external Subgraphs/Operators.

Validates that the internal operator has the specified port. If is_input is not specified, the method automatically detects whether the port is an input or output. If the port name exists in both inputs and outputs, a runtime_error is thrown requiring explicit specification.

Parameters

external_name
const std::string &

The name of the interface port (used in add_flow calls)

internal_op
const std::shared_ptr<Operator> &

The internal operator that owns the actual port

internal_port
std::optional<std::string>Defaults to std::nullopt

The port name on the internal operator (defaults to external_name if not specified)

is_input
std::optional<bool>Defaults to std::nullopt

Whether this is an input port (true) or output port (false). If not specified, the port direction is auto-detected from the operator’s port definitions.

add_input_interface_port

void holoscan::Subgraph::add_input_interface_port(
const std::string &external_name,
const std::shared_ptr<Operator> &internal_op,
std::optional<std::string> internal_port = std::nullopt
)

Add an input interface port (convenience method).

Parameters

external_name
const std::string &

The name of the interface port (used in add_flow calls)

internal_op
const std::shared_ptr<Operator> &

The internal operator that owns the actual port

internal_port
std::optional<std::string>Defaults to std::nullopt

The port name on the internal operator (defaults to external_name if not specified)

add_output_interface_port

void holoscan::Subgraph::add_output_interface_port(
const std::string &external_name,
const std::shared_ptr<Operator> &internal_op,
std::optional<std::string> internal_port = std::nullopt
)

Add an output interface port (convenience method).

Parameters

external_name
const std::string &

The name of the interface port (used in add_flow calls)

internal_op
const std::shared_ptr<Operator> &

The internal operator that owns the actual port

internal_port
std::optional<std::string>Defaults to std::nullopt

The port name on the internal operator (defaults to external_name if not specified)

add_input_exec_interface_port

void holoscan::Subgraph::add_input_exec_interface_port(
const std::string &external_name,
const std::shared_ptr<Operator> &internal_op
)

Add an input execution interface port for control flow.

Exposes an execution port from an internal operator for control flow connections. The internal operator must be a Native operator with an input execution spec.

Parameters

external_name
const std::string &

The name of the interface port (used in add_flow calls)

internal_op
const std::shared_ptr<Operator> &

The internal operator that has the execution port

add_output_exec_interface_port

void holoscan::Subgraph::add_output_exec_interface_port(
const std::string &external_name,
const std::shared_ptr<Operator> &internal_op
)

Add an output execution interface port for control flow.

Exposes an execution port from an internal operator for control flow connections. The internal operator must be a Native operator with an output execution spec.

Parameters

external_name
const std::string &

The name of the interface port (used in add_flow calls)

internal_op
const std::shared_ptr<Operator> &

The internal operator that has the execution port

interface_ports

const std::unordered_map<std::string, InterfacePort> & holoscan::Subgraph::interface_ports() const

Get data interface ports.

Returns a map of interface port names to InterfacePort objects. Each InterfacePort can contain multiple mappings for broadcast input ports.

exec_interface_ports

const std::unordered_map<std::string, InterfacePort> & holoscan::Subgraph::exec_interface_ports() const

Get execution interface ports.

get_interface_operator_port

std::pair<std::shared_ptr<Operator>, std::string> holoscan::Subgraph::get_interface_operator_port(
const std::string &port_name
) const

Get the first operator/port for a data interface port name.

This method first checks local interface ports, then recursively checks nested subgraphs for hierarchical port resolution.

For broadcast input ports that have multiple mappings, this returns only the first mapping. Access the InterfacePort directly via interface_ports() to get all mappings.

Returns: Pair of (operator, actual_port_name) or (nullptr, "") if not found

Parameters

port_name
const std::string &

The interface port name

get_exec_interface_operator_port

std::pair<std::shared_ptr<Operator>, std::string> holoscan::Subgraph::get_exec_interface_operator_port(
const std::string &port_name
) const

Get the operator/port for an execution interface port name.

This method first checks local exec interface ports, then recursively checks nested subgraphs for hierarchical port resolution.

Returns: Pair of (operator, actual_port_name) or (nullptr, "") if not found

Parameters

port_name
const std::string &

The exec interface port name

is_composed

bool holoscan::Subgraph::is_composed() const

Check if the subgraph has been composed.

set_composed

void holoscan::Subgraph::set_composed(
bool composed
)

Set the composed state of the subgraph.

operators

std::vector<std::shared_ptr<Operator>> holoscan::Subgraph::operators() const

Get all operators belonging to this subgraph and its nested subgraphs.

This method returns all operators whose names are prefixed with this subgraph’s name followed by an underscore. This includes operators from nested subgraphs since their names are also prefixed with the parent subgraph’s name.

Returns: Vector of shared pointers to the operators.

nested_subgraphs

const std::vector<std::shared_ptr<Subgraph>> & holoscan::Subgraph::nested_subgraphs() const

Get the nested subgraphs directly owned by this subgraph.

Returns subgraphs added via make_subgraph() or add_subgraph() within this subgraph. Does not recursively include subgraphs nested further down the hierarchy.

Returns: A const reference to the vector of nested subgraphs.

config

Config & holoscan::Subgraph::config()

Get the configuration of the subgraph.

Returns: The reference to the configuration of the subgraph (Config object.)

config_shared

std::shared_ptr<Config> holoscan::Subgraph::config_shared()

Get the shared pointer to the configuration of the subgraph.

Returns: The shared pointer to the configuration of the subgraph.

from_config

ArgList holoscan::Subgraph::from_config(
const std::string &key
)

Get the value of a configuration key as an ArgList.

This method retrieves the value from the subgraph’s configuration for the given key. You can use ’.’ (dot) to access nested fields. The returned ArgList can be passed directly to make_operator() or other methods that accept configuration arguments.

Example usage:

Returns: The argument list of the configuration for the key.

Parameters

key
const std::string &

The key of the configuration.

Example

auto op = make_operator<MyOp>("my_op", from_config("my_op"));

config_keys

std::unordered_set<std::string> holoscan::Subgraph::config_keys()

Determine the set of keys present in the subgraph’s config.

Returns: The set of valid keys.

format_port_list

std::string holoscan::Subgraph::format_port_list(
const std::unordered_map<std::string, std::shared_ptr<IOSpec>> &ports
)

Efficiently format a port list for error messages.

Uses fmt::memory_buffer for O(n) string building instead of O(n²) string concatenation. This provides better performance and avoids repeated memory reallocations.

Returns: Formatted string with port names like “‘port1’, ‘port2’, ‘port3’”

Parameters

ports
const std::unordered_map<std::string, std::shared_ptr<IOSpec>> &

The port collection (inputs or outputs from OperatorSpec)

validate_operator_port

bool holoscan::Subgraph::validate_operator_port(
const std::shared_ptr<Operator> &op,
const std::string &port_name,
bool expect_input
)

Validate that an operator has a port with the correct type.

Returns: true if the port exists and has the correct type, false otherwise

Parameters

op
const std::shared_ptr<Operator> &

The operator to validate

port_name
const std::string &

The name of the port to check

expect_input
bool

Whether we expect this to be an input port (true) or output port (false)

validate_operator_exec_port

bool holoscan::Subgraph::validate_operator_exec_port(
const std::shared_ptr<Operator> &op
)

Validate that an operator can be used for execution control flow.

Checks that the operator is a Native operator. Execution specs are created automatically by the GXF executor during initialization.

Returns: true if the operator can be used for execution control flow, false otherwise

Parameters

op
const std::shared_ptr<Operator> &

The operator to validate

check_exec_port_name_available

bool holoscan::Subgraph::check_exec_port_name_available(
const std::string &external_name
)

Check if an exec interface port name is already in use.

Checks both data and exec interface port maps for name conflicts.

Returns: true if the name is available (not already used)

Throws: std::runtime_error if the name is already in use

Parameters

external_name
const std::string &

The name to check

register_exec_interface_port

void holoscan::Subgraph::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
)

Register an exec interface port.

Creates and stores an InterfacePort in exec_interface_ports_ with the given parameters.

Parameters

external_name
const std::string &

The external interface port name

internal_op
const std::shared_ptr<Operator> &

The internal operator

internal_port_name
const std::string &

The internal port name (kInputExecPortName or kOutputExecPortName)

is_input
bool

Whether this is an input port

resolve_nested_exec_port

std::pair<std::shared_ptr<Operator>, std::string> holoscan::Subgraph::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
)

Resolve a nested subgraph’s exec interface port to an operator and port.

Looks up the specified exec interface port in the nested subgraph and validates that it has the expected direction (input vs output).

Returns: Pair of (operator, port_name) or (nullptr, "") if resolution fails

Parameters

external_name
const std::string &

The name for this subgraph’s interface port (for error messages)

internal_subgraph
const std::shared_ptr<Subgraph> &

The nested subgraph to query

internal_interface_port
const std::string &

The exec interface port name to look up

expect_input
bool

Whether we expect an input port (true) or output port (false)


Member variables

NameTypeDescription
interface_ports_std::unordered_map< std::string, InterfacePort >Data interface ports.
exec_interface_ports_std::unordered_map< std::string, InterfacePort >Execution interface ports.
nested_subgraphs_std::vector< std::shared_ptr< Subgraph > >Nested Subgraphs for hierarchical composition.
nested_subgraph_names_std::unordered_set< std::string >Track nested child names to detect duplicates.
is_composed_bool
fragment_Fragment *Target fragment for direct operator/flow addition.
name_std::stringName for this subgraph.
config_std::shared_ptr< Config >Subgraph-specific configuration.