Rust图数据结构库portgraph的使用,portgraph提供高效灵活的节点连接和拓扑操作功能

Rust图数据结构库portgraph的使用

portgraph是一个用于带一级端口的有向图的数据结构库,提供高效的节点连接和拓扑操作功能。该库还包括用于节点和端口权重以及节点层次结构的二级数据结构。

主要特性

  • pyo3: 通过pyo3启用Python绑定
  • serde: 通过serde启用序列化和反序列化
  • petgraph: 通过为PortGraphMultiPortGraph实现petgraph::visit特性,实现与petgraph的互操作性

安装使用

在项目中运行以下Cargo命令:

cargo add portgraph

或者在Cargo.toml中添加:

portgraph = "0.15.2"

示例代码

use portgraph::PortGraph;

fn main() {
    // 创建一个新的PortGraph实例
    let mut graph = PortGraph::new();
    
    // 添加节点,每个节点可以指定输入端口和输出端口数量
    let node1 = graph.add_node(2, 1); // 2个输入端口,1个输出端口
    let node2 = graph.add_node(1, 2); // 1个输入端口,2个输出端口
    
    // 连接节点
    graph.link_nodes(node1, 0, node2, 0).unwrap(); // 将node1的输出端口0连接到node2的输入端口0
    
    // 检查连接
    assert!(graph.connected(node1, node2));
    
    // 获取节点的邻居
    let neighbors: Vec<_> = graph.neighbors(node1).collect();
    assert_eq!(neighbors, vec![node2]);
    
    // 移除连接
    graph.unlink_nodes(node1, node2).unwrap();
    
    // 移除节点
    graph.remove_node(node1);
    
    // 检查图是否为空
    assert_eq!(graph.node_count(), 1);
}

更复杂的示例

use portgraph::{PortGraph, PortView};

fn main() {
    let mut graph = PortGraph::new();
    
    // 创建多个节点
    let nodes: Vec<_> = (0..5).map(|i| {
        graph.add_node(i, 5 - i)  // 每个节点的输入端口数量递增,输出端口数量递减
    }).collect();
    
    // 创建复杂的连接模式
    for i in 0..nodes.len() {
        for j in 0..nodes.len() {
            if i != j {
                let input_ports = graph.input_count(nodes[j]);
                let output_ports = graph.output_count(nodes[i]);
                
                if input_ports > 0 && output_ports > 0 {
                    let port_in = j % input_ports;
                    let port_out = i % output_ports;
                    graph.link_nodes(nodes[i], port_out, nodes[j], port_in).unwrap();
                }
            }
        }
    }
    
    // 使用PortView遍历图结构
    let port_view = PortView::new(&graph);
    for node in port_view.nodes() {
        println!("Node {} has {} inputs and {} outputs",
                 node.index(),
                 port_view.input_count(node),
                 port_view.output_count(node));
    }
    
    // 拓扑排序
    if let Ok(order) = portgraph::algorithms::toposort(&port_view) {
        println!("Topological order: {:?}", order);
    }
}

完整示例demo

以下是一个完整的示例,展示如何使用portgraph创建一个简单的数据处理流程图:

use portgraph::{PortGraph, PortView, algorithms};
use std::collections::HashMap;

fn main() {
    // 创建一个新的PortGraph实例
    let mut graph = PortGraph::new();
    
    // 定义节点类型和端口数量
    let node_types = vec![
        ("DataSource", 0, 2),   // 数据源节点,0输入2输出
        ("Processor", 2, 2),   // 处理器节点,2输入2输出
        ("Sink", 2, 0),        // 数据接收节点,2输入0输出
    ];
    
    // 添加节点到图中
    let mut nodes = HashMap::new();
    for (i, (name, inputs, outputs)) in node_types.iter().enumerate() {
        let node = graph.add_node(*inputs, *outputs);
        nodes.insert(i, (name.to_string(), node));
        println!("Added node {}: {}", i, name);
    }
    
    // 连接节点创建数据流
    graph.link_nodes(nodes[&0].1, 0, nodes[&1].1, 0).unwrap(); // 数据源输出0 -> 处理器输入0
    graph.link_nodes(nodes[&0].1, 1, nodes[&1].1, 1).unwrap(); // 数据源输出1 -> 处理器输入1
    graph.link_nodes(nodes[&1].1, 0, nodes[&2].1, 0).unwrap(); // 处理器输出0 -> 接收器输入0
    graph.link_nodes(nodes[&1].1, 1, nodes[&2].1, 1).unwrap(); // 处理器输出1 -> 接收器输入1
    
    // 使用PortView分析图结构
    let port_view = PortView::new(&graph);
    
    // 打印图中所有节点信息
    println!("\nGraph structure:");
    for node in port_view.nodes() {
        println!("Node {} ({}): {} inputs, {} outputs",
                 node.index(),
                 nodes[&node.index()].0,
                 port_view.input_count(node),
                 port_view.output_count(node));
    }
    
    // 检查图的连通性
    println!("\nGraph connectivity:");
    for (i, (name, node)) in &nodes {
        let neighbors: Vec<_> = graph.neighbors(*node).collect();
        println!("Node {} ({}) is connected to: {:?}", i, name, neighbors);
    }
    
    // 执行拓扑排序
    if let Ok(order) = algorithms::toposort(&port_view) {
        println!("\nTopological order:");
        for node in order {
            println!("- Node {} ({})", node.index(), nodes[&node.index()].0);
        }
    }
    
    // 验证图的完整性
    assert_eq!(graph.node_count(), 3);
    assert_eq!(graph.edge_count(), 4);
    println!("\nGraph validation passed!");
}

许可证

该项目使用Apache License, Version 2.0许可证。


1 回复

Rust图数据结构库portgraph使用指南

portgraph是一个Rust实现的图数据结构库,专注于提供高效的节点连接和拓扑操作功能。它特别适合需要处理复杂图结构的应用场景。

主要特性

  • 高效的节点和边操作
  • 灵活的拓扑查询功能
  • 支持多图结构
  • 内存高效的设计

基本使用方法

添加依赖

首先在Cargo.toml中添加依赖:

[dependencies]
portgraph = "0.3"

创建图结构

use portgraph::Graph;

let mut graph = Graph::new();

// 添加节点
let node1 = graph.add_node();
let node2 = graph.add_node();
let node3 = graph.add_node();

// 添加边
graph.add_edge(node1, node2);
graph.add_edge(node2, node3);
graph.add_edge(node1, node3);

基本查询操作

// 检查节点是否存在
assert!(graph.contains_node(node1));

// 获取节点的邻居
let neighbors: Vec<_> = graph.neighbors(node1).collect();
assert_eq!(neighbors, vec![node2, node3]);

// 获取图的节点数量
assert_eq!(graph.node_count(), 3);

// 获取图的边数量
assert_eq!(graph.edge_count(), 3);

拓扑排序示例

use portgraph::algo::toposort;

let sorted_nodes = toposort(&graph).unwrap();
println!("拓扑排序结果: {:?}", sorted_nodes);

复杂图操作

// 创建带权重的图
use portgraph::weights::WeightGraph;

let mut weighted_graph = WeightGraph::<i32>::new();
let wnode1 = weighted_graph.add_node(10); // 节点权重为10
let wnode2 = weighted_graph.add_node(20);
weighted_graph.add_edge(wnode1, wnode2, 5); // 边权重为5

// 获取权重
assert_eq!(weighted_graph.node_weight(wnode1), Some(&10));
assert_eq!(weighted_graph.edge_weight(wnode1, wnode2), Some(&5));

高级功能

子图操作

use portgraph::algo::subgraph;

let subgraph_nodes = [node1, node2];
let subgraph = subgraph(&graph, subgraph_nodes.iter().copied());
assert_eq!(subgraph.node_count(), 2);
assert_eq!(subgraph.edge_count(), 1);

图遍历

use portgraph::algo::Dfs;

let mut dfs = Dfs::new(&graph, node1);
while let Some(node) = dfs.next(&graph) {
    println!("访问节点: {:?}", node);
}

性能提示

  1. 对于大规模图,考虑使用Graph::with_capacity预分配空间
  2. 频繁的删除操作可能导致内存碎片,必要时可以重建图
  3. 使用WeightGraph时,选择合适大小的权重类型以节省内存

portgraph提供了丰富的图操作API,可以满足大多数图算法实现的需求。其设计注重性能和灵活性,是Rust生态中处理图结构的一个优秀选择。

完整示例demo

下面是一个完整的portgraph使用示例,展示了从创建图到执行各种操作的完整流程:

use portgraph::{Graph, algo::{toposort, Dfs}, weights::WeightGraph};

fn main() {
    // 1. 创建基本图结构
    let mut graph = Graph::new();
    
    // 添加节点
    let node1 = graph.add_node();
    let node2 = graph.add_node();
    let node3 = graph.add_node();
    let node4 = graph.add_node();
    
    // 添加边
    graph.add_edge(node1, node2);
    graph.add_edge(node2, node3);
    graph.add_edge(node1, node3);
    graph.add_edge(node3, node4);
    
    // 2. 基本查询操作
    println!("图中有 {} 个节点", graph.node_count());
    println!("图中有 {} 条边", graph.edge_count());
    
    // 3. 拓扑排序
    match toposort(&graph) {
        Ok(sorted) => println!("拓扑排序结果: {:?}", sorted),
        Err(_) => println!("图中存在环,无法进行拓扑排序"),
    }
    
    // 4. 图遍历
    println!("从节点1开始的DFS遍历:");
    let mut dfs = Dfs::new(&graph, node1);
    while let Some(node) = dfs.next(&graph) {
        println!("访问节点: {:?}", node);
    }
    
    // 5. 子图操作
    let subgraph_nodes = [node1, node2, node3];
    let subgraph = portgraph::algo::subgraph(&graph, subgraph_nodes.iter().copied());
    println!("子图节点数: {}", subgraph.node_count());
    println!("子图边数: {}", subgraph.edge_count());
    
    // 6. 带权图操作
    let mut weighted_graph = WeightGraph::<i32>::new();
    let wnode1 = weighted_graph.add_node(10); // 节点权重10
    let wnode2 = weighted_graph.add_node(20); // 节点权重20
    weighted_graph.add_edge(wnode1, wnode2, 5); // 边权重5
    
    println!("节点1权重: {:?}", weighted_graph.node_weight(wnode1));
    println!("节点1到节点2的边权重: {:?}", 
        weighted_graph.edge_weight(wnode1, wnode2));
}

这个完整示例演示了:

  1. 创建基本图结构并添加节点和边
  2. 执行基本查询操作
  3. 进行拓扑排序
  4. 实现图的深度优先搜索遍历
  5. 创建和操作子图
  6. 使用带权重的图结构

您可以根据实际需求修改或扩展这个示例代码。

回到顶部