graph-sp
A high-performance DAG (Directed Acyclic Graph) execution engine with true parallel execution, built in Rust with Python bindings.
Features
- ⚡ True Parallel Execution: Automatic parallelization of independent nodes (44% faster for fan-out patterns)
- 🔌 Port-based Architecture: Type-safe data flow between nodes via named ports
- 🌿 Branching & Nested Graphs: Create isolated subgraphs for experiments and variants
- 🔀 Merge Operations: Combine outputs from multiple branches with custom merge functions
- 🔄 Variants & Config Sweeps: Automated parameter variation with cartesian product support
- 🐍 Python & Rust APIs: Full feature parity across both languages
- 🔍 Graph Inspection: Analysis, visualization, and Mermaid diagram generation
- ✅ Cycle Detection: Built-in DAG validation with detailed error reporting
- 📊 Rich Data Types: Primitives, collections, JSON, nested objects, and binary data
- 🎯 Zero-Copy Optimization: Efficient data sharing using Arc
Quick Start
Python
Install from PyPI:
Simple example:
# Create a graph
=
# Add nodes with Python functions using the simplified API
return
return
# Add nodes - function name becomes the node ID by default
# Connect nodes using function names
# Execute with parallel processing
=
=
# [2, 4, 6, 8, 10]
Rust
Add to your Cargo.toml:
[]
= "0.1"
= { = "1", = ["full"] }
Simple example:
use ;
use HashMap;
use Arc;
async
Installation
Python
From PyPI (recommended):
From source:
# Clone the repository
# Install maturin
# Build and install
Rust
From crates.io:
[]
= "0.1"
From source:
Core Concepts
Ports
Nodes communicate through typed ports with separate broadcast and implementation names:
# Python - Simple ports (broadcast_name == impl_name)
# Advanced: Separate broadcast and implementation names
# Useful for connecting nodes with different parameter names
# Most explicit: Use Port objects
=
// Rust - Simple port (both names the same)
let port = simple;
// Separate broadcast and implementation names
let port = new;
Port Name Types:
- broadcast_name: External name used for connecting nodes via edges
- impl_name: Internal parameter name used in function signatures
- display_name: Human-readable name for visualizations (defaults to broadcast_name)
Data Types
Support for multiple data types:
- Primitives: Int, Float, String, Bool, None
- Collections: List, Map (nested HashMap)
- Structured: JSON (arbitrary structures)
- Binary: Bytes (raw binary data)
# Python - nested objects work seamlessly
=
Parallel Execution
The engine automatically parallelizes independent branches:
# This creates a fan-out pattern with 3 parallel branches
# source
# / | \
# slow fast medium <- These run in parallel!
# \ | /
# merger
# Execution time: ~500ms (parallel) vs ~900ms (sequential)
# 44% speedup achieved automatically!
Graph Inspection
# Analyze graph structure
=
# Parallelization potential
# Generate Mermaid diagram
=
# GitHub-compatible markdown
Branching & Variants (New in v0.2.0)
Branches allow you to create isolated subgraphs:
# Create experimental branches
# Check branches
# ["experiment_a", "experiment_b"]
# True
Variants enable config sweeps and hyperparameter tuning:
# Python - Create variants with different learning rates
=
return
=
# Creates: lr_0 (0.001), lr_1 (0.01), lr_2 (0.1)
// Rust - Same functionality
use ;
let variant_fn: VariantFunction = new;
let config = new;
let branches = graph.create_variants?;
Merge combines outputs from multiple branches:
# Python - Merge outputs from variant branches
# Custom merge function (e.g., average)
return
// Rust - Same functionality
use MergeConfig;
let merge_config = new;
graph.merge?;
// Custom merge function
let max_fn = new;
let config = new
.with_merge_fn;
Nested Variants create cartesian products:
// 2 learning rates × 3 batch sizes = 6 total configurations
let lr_config = new;
let lr_branches = graph.create_variants?;
for lr_branch in &lr_branches
Examples
Python Examples
Located in python_examples/:
- simple_pipeline.py: Basic 3-node pipeline with implicit port mapping
- parallel_execution.py: Fan-out/fan-in pattern with 3 parallel branches
- variants_demo.py: Hyperparameter sweeps, custom merge, nested variants
- port_mapping_demo.py: ⭐ Explicit port mapping (broadcast vs implementation names)
- complex_objects.py: Nested objects, JSON, and lists
- branching_example.py: Branch creation and management
- All other examples: Use simple implicit port mapping
Note: Most examples use implicit port mapping where port names and function parameters match.
See port_mapping_demo.py for the one example showing explicit broadcast/implementation name separation.
Run an example:
Rust Examples
Located in examples/:
- simple_pipeline.rs: 4-node data processing pipeline with implicit ports
- parallel_execution.rs: Fan-out/fan-in pattern with performance analysis
- branching_and_variants.rs: Comprehensive demo of branches, merge, and variants
- all_features_demo.rs: ⭐ Shows both implicit and explicit port mapping
- complex_objects.rs: All PortData types with nested structures
Note: Most examples use Port::simple("name") for implicit mapping.
See all_features_demo.rs for examples using Port::new("broadcast", "impl") for explicit mapping.
Run an example:
Performance
Measured with 3-branch parallel execution example:
| Metric | Sequential | Parallel | Improvement |
|---|---|---|---|
| Rust | 900ms | 500ms | 44% faster |
| Python | 900ms | 502ms | 44% faster |
The executor identifies dependency levels and executes all independent nodes concurrently using tokio::task::spawn_blocking.
Architecture
Core Components
-
Data Model (
src/core/data.rs)PortData: Enum for all supported typesGraphData: HashMap for port-to-port storage- Support for nested structures via recursive variants
-
Graph (
src/core/graph.rs)- petgraph-backed DAG representation
- Topological sorting and cycle detection
- Type-safe port connections
-
Executor (
src/executor/mod.rs)- Dependency-level grouping
- Concurrent execution with
tokio::task::spawn_blocking - Automatic concurrency management
-
Inspector (
src/inspector/mod.rs)- Graph statistics (depth, width, sources, sinks)
- Mermaid diagram generation
- Optimization suggestions
-
Python Bindings (
src/python/mod.rs)- PyO3-based wrappers
- GIL-aware parallel execution
- Automatic type conversion
Building & Testing
Rust
# Run tests
# Run with all features
# Build release
# Run examples
Python
# Install development dependencies
# Build Python bindings
# Run Python examples
Build for Multiple Platforms
# Linux (using Docker for manylinux compatibility)
# macOS
# Windows
Documentation
- Python API: See
python_examples/README.mdfor detailed Python usage - Rust API: Run
cargo doc --openfor full API documentation - Port Data Types: See
docs/PORT_DATA_TYPES.mdfor supported types
Publishing
PyPI
Wheels are automatically built and published to PyPI on version tags:
Builds wheels for:
- Linux (manylinux)
- macOS (Intel & ARM)
- Windows
Crates.io
Contributing
Contributions welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass (
cargo test --all-features) - Submit a pull request
License
MIT License - see LICENSE file for details
Roadmap
- True parallel execution
- Python bindings with PyPI distribution
- Mermaid diagram generation
- Comprehensive examples
- Distributed execution support
- Graph serialization/deserialization
- WebAssembly support
- Real-time monitoring dashboard
- Advanced optimization algorithms