Skip to main content

Build a Custom Data Graph

FieldValue
DifficultyIntermediate
Estimated Read Time15-20 minutes
Labelsgraph, traversal, metadata

Concept

Build the smallest useful public Neat Graph — one named Input wired to one named Output — then push a sample through and verify metadata survives traversal. This is the baseline before hybrid or multistream graph tutorials.

A public Graph is the application composition surface. You add Nodes with graph.add(...), wire named public endpoints with graph.connect("input_name", "output_name"), build once with graph.build(), then use named runtime calls:

run.push("image", [sample])
sample = run.pull("out")

The old low-level pyneat.graph module has been removed. Application code should use pyneat.Graph and reusable public Graph fragments.

APIs introduced

  • pyneat.Graph() — the public graph container.
  • pyneat.nodes.input("image") — a named push endpoint.
  • pyneat.nodes.output("out") — a named pull endpoint.
  • graph.add(node) — add a Node or reusable Graph fragment.
  • graph.connect("image", "out") — wire public endpoint names.
  • graph.build() — materialize the Graph into a Run.
  • run.push("image", [sample]) / run.pull("out") — named runtime I/O.

When to use this

  • Custom orchestration where linear model calls are not enough (fan-out, fan-in, per-stream routing).
  • Multistream scheduling (see chapter 014).
  • Embedding model execution as one stage of a larger flow (see chapter 013).

Prerequisites Chapter 003 (Graph basics).

References

Learning Process

  1. Build a minimal public Graph and push one deterministic tensor sample.
  2. Use named endpoints rather than node IDs.
  3. Pull graph output and validate stream/frame/timestamp metadata preservation.

Run

Python:

python3 share/sima-neat/tutorials/012_build_a_custom_data_graph/build_a_custom_data_graph.py

C++ (prebuilt):

./lib/sima-neat/tutorials/tutorial_012_build_a_custom_data_graph

C++ (build from source):

./build.sh --target tutorial_012_build_a_custom_data_graph
./build/tutorials-standalone/tutorial_012_build_a_custom_data_graph

To integrate this chapter's C++ source into your own project with a custom CMakeLists.txt (no extras folder required), see How to Run Tutorials on the landing page.

Code

tutorials/012_build_a_custom_data_graph/build_a_custom_data_graph.cpp
// Compose a minimal public Neat Graph: named Input -> named Output.
//
// Usage:
// tutorial_012_build_a_custom_data_graph

#include "neat.h"

#include <cstdint>
#include <iostream>
#include <stdexcept>
#include <utility>
#include <vector>

namespace {

std::vector<int64_t> contiguous_strides_bytes(const std::vector<int64_t>& shape,
int64_t elem_bytes) {
std::vector<int64_t> strides(shape.size(), 0);
int64_t stride = elem_bytes;
for (int i = static_cast<int>(shape.size()) - 1; i >= 0; --i) {
strides[static_cast<size_t>(i)] = stride;
stride *= shape[static_cast<size_t>(i)];
}
return strides;
}

simaai::neat::Sample make_sample() {
const int w = 8;
const int h = 8;
const int c = 3;
const std::size_t bytes = static_cast<std::size_t>(w) * h * c;

simaai::neat::Tensor t;
t.device = {simaai::neat::DeviceType::CPU, 0};
t.dtype = simaai::neat::TensorDType::UInt8;
t.layout = simaai::neat::TensorLayout::HWC;
t.shape = {h, w, c};
t.semantic.image = simaai::neat::ImageSpec{simaai::neat::ImageSpec::PixelFormat::RGB, ""};
t.storage = simaai::neat::make_cpu_owned_storage(bytes);
t.strides_bytes = contiguous_strides_bytes(t.shape, 1);
t.read_only = false;
{
auto map = t.map(simaai::neat::MapMode::Write);
auto* p = static_cast<std::uint8_t*>(map.data);
for (std::size_t i = 0; i < bytes; ++i)
p[i] = static_cast<std::uint8_t>(i % 255);
}
t.read_only = true;

simaai::neat::Sample s;
s.kind = simaai::neat::SampleKind::Tensor;
s.tensor = std::move(t);
s.stream_id = "graph";
s.frame_id = 42;
s.pts_ns = 123456789;
return s;
}

} // namespace

int main() {
try {
// CORE LOGIC
// `Graph` is the public composition type. Input("image") declares the name
// used by Run::push("image", ...). Output("out") declares the name used by
// Run::pull("out", ...).
simaai::neat::Graph graph;
graph.add(simaai::neat::nodes::Input("image"));
graph.add(simaai::neat::nodes::Output("out"));
graph.connect("image", "out");

std::cout << graph.describe() << "\n";

simaai::neat::Run run = graph.build();
if (!run.push("image", make_sample())) {
throw std::runtime_error("push failed: " + run.last_error());
}
auto out = run.pull("out", /*timeout_ms=*/2000);
run.close();

if (!out.has_value())
throw std::runtime_error("graph produced no output");
std::cout << "stream=" << out->stream_id << " frame=" << out->frame_id
<< " pts_ns=" << out->pts_ns << "\n";
std::cout << "[OK] 012_build_a_custom_data_graph\n";
return 0;
} catch (const std::exception& e) {
std::cerr << "[FAIL] " << e.what() << "\n";
return 1;
}
}

Source