Skip to main content

Graph

Struct Graph 

Source
pub struct Graph { /* private fields */ }
Expand description

The static topology of a flow-based network.

A Graph defines the structure of a network: which nodes exist, how they’re connected, and what conditions govern packet flow. The graph is immutable after creation.

§Example

use netrun_sim::graph::{Graph, Node, Edge, PortRef, PortType, Port, PortSlotSpec};
use indexmap::IndexMap;
use std::collections::HashMap;

// Create a simple A -> B graph
let node_a = Node {
    name: "A".to_string(),
    in_ports: HashMap::new(),
    out_ports: [("out".to_string(), Port { slots_spec: PortSlotSpec::Infinite })].into(),
    in_salvo_conditions: IndexMap::new(),
    out_salvo_conditions: IndexMap::new(),
    dependency_request_config: None,
};
let node_b = Node {
    name: "B".to_string(),
    in_ports: [("in".to_string(), Port { slots_spec: PortSlotSpec::Infinite })].into(),
    out_ports: HashMap::new(),
    in_salvo_conditions: IndexMap::new(),
    out_salvo_conditions: IndexMap::new(),
    dependency_request_config: None,
};

let edge = Edge {
    source: PortRef { node_name: "A".to_string(), port_type: PortType::Output, port_name: "out".to_string() },
    target: PortRef { node_name: "B".to_string(), port_type: PortType::Input, port_name: "in".to_string() },
};

let graph = Graph::new(vec![node_a, node_b], vec![edge]);
assert!(graph.validate().is_empty());

Implementations§

Source§

impl Graph

Source

pub fn new(nodes: Vec<Node>, edges: Vec<Edge>) -> Self

Creates a new Graph from a list of nodes and edges.

Builds internal indexes for efficient edge lookups by source (tail) and target (head) ports.

Examples found in repository?
examples/linear_flow.rs (line 134)
122fn create_linear_graph() -> Graph {
123    let nodes = vec![
124        create_node("A", vec![], vec!["out"]),
125        create_node("B", vec!["in"], vec!["out"]),
126        create_node("C", vec!["in"], vec![]),
127    ];
128
129    let edges = vec![
130        create_edge("A", "out", "B", "in"),
131        create_edge("B", "out", "C", "in"),
132    ];
133
134    let graph = Graph::new(nodes, edges);
135    assert!(graph.validate().is_empty(), "Graph validation failed");
136    graph
137}
More examples
Hide additional examples
examples/diamond_flow.rs (line 206)
122fn create_diamond_graph() -> Graph {
123    // Node A: source with two outputs
124    let node_a = Node {
125        name: "A".to_string(),
126        in_ports: HashMap::new(),
127        out_ports: [
128            (
129                "out1".to_string(),
130                Port {
131                    slots_spec: PortSlotSpec::Infinite,
132                },
133            ),
134            (
135                "out2".to_string(),
136                Port {
137                    slots_spec: PortSlotSpec::Infinite,
138                },
139            ),
140        ]
141        .into(),
142        in_salvo_conditions: IndexMap::new(),
143        out_salvo_conditions: IndexMap::new(),
144        dependency_request_config: None,
145    };
146
147    // Node B: one input, one output
148    let node_b = create_simple_node("B");
149
150    // Node C: one input, one output
151    let node_c = create_simple_node("C");
152
153    // Node D: TWO inputs (requires both), no outputs
154    let node_d = Node {
155        name: "D".to_string(),
156        in_ports: [
157            (
158                "in1".to_string(),
159                Port {
160                    slots_spec: PortSlotSpec::Infinite,
161                },
162            ),
163            (
164                "in2".to_string(),
165                Port {
166                    slots_spec: PortSlotSpec::Infinite,
167                },
168            ),
169        ]
170        .into(),
171        out_ports: HashMap::new(),
172        in_salvo_conditions: IndexMap::from([(
173            "default".to_string(),
174            SalvoCondition {
175                max_salvos: MaxSalvos::Finite(1),
176                ports: [
177                    ("in1".to_string(), PacketCount::All),
178                    ("in2".to_string(), PacketCount::All),
179                ]
180                .into_iter()
181                .collect(),
182                // Require BOTH inputs to be non-empty
183                term: SalvoConditionTerm::And(vec![
184                    SalvoConditionTerm::Port {
185                        port_name: "in1".to_string(),
186                        state: PortState::NonEmpty,
187                    },
188                    SalvoConditionTerm::Port {
189                        port_name: "in2".to_string(),
190                        state: PortState::NonEmpty,
191                    },
192                ]),
193            },
194        )]),
195        out_salvo_conditions: IndexMap::new(),
196        dependency_request_config: None,
197    };
198
199    let edges = vec![
200        create_edge("A", "out1", "B", "in"),
201        create_edge("A", "out2", "C", "in"),
202        create_edge("B", "out", "D", "in1"),
203        create_edge("C", "out", "D", "in2"),
204    ];
205
206    let graph = Graph::new(vec![node_a, node_b, node_c, node_d], edges);
207    assert!(graph.validate().is_empty(), "Graph validation failed");
208    graph
209}
Source

pub fn with_dependency_edges(self, dependency_edges: Vec<Edge>) -> Self

Set which edges are dependency edges. Each must be in the graph’s edge set. Panics if any dependency edge is not in the graph.

Source

pub fn is_dependency_edge(&self, edge: &Edge) -> bool

Check if an edge is a dependency edge.

Source

pub fn dependency_edges(&self) -> &HashSet<Edge>

Returns a reference to all dependency edges.

Source

pub fn nodes(&self) -> &HashMap<NodeName, Node>

Returns a reference to all nodes in the graph, keyed by name.

Examples found in repository?
examples/linear_flow.rs (line 23)
20fn main() {
21    // Create a linear graph: A -> B -> C
22    let graph = create_linear_graph();
23    println!("Created graph with {} nodes", graph.nodes().len());
24
25    // Create a network from the graph
26    let mut net = NetSim::new(graph);
27
28    // Create a packet outside the network
29    let packet_id = match net.do_action(&NetAction::CreatePacket(None)) {
30        NetActionResponse::Success(NetActionResponseData::Packet(id), _) => {
31            println!("Created packet: {}", id);
32            id
33        }
34        _ => panic!("Failed to create packet"),
35    };
36
37    // Transport packet to the edge A -> B
38    let edge_a_b = PacketLocation::Edge(Edge {
39        source: PortRef {
40            node_name: "A".to_string(),
41            port_type: PortType::Output,
42            port_name: "out".to_string(),
43        },
44        target: PortRef {
45            node_name: "B".to_string(),
46            port_type: PortType::Input,
47            port_name: "in".to_string(),
48        },
49    });
50    net.do_action(&NetAction::TransportPacketToLocation(
51        packet_id.clone(),
52        edge_a_b,
53    ));
54    println!("Placed packet on edge A -> B");
55
56    // Run the network - packet moves to B's input port and triggers an epoch
57    net.run_until_blocked();
58    println!("Ran network until blocked");
59
60    // Check for startable epochs
61    let startable = net.get_startable_epochs();
62    println!("Startable epochs: {}", startable.len());
63
64    if let Some(epoch_id) = startable.first() {
65        // Start the epoch
66        match net.do_action(&NetAction::StartEpoch(epoch_id.clone())) {
67            NetActionResponse::Success(NetActionResponseData::StartedEpoch(epoch), _) => {
68                println!("Started epoch {} on node {}", epoch.id, epoch.node_name);
69
70                // In a real scenario, external code would process the packet here
71                // For this example, we'll just consume it and create an output
72
73                // Consume the input packet
74                net.do_action(&NetAction::ConsumePacket(packet_id));
75                println!("Consumed input packet");
76
77                // Create an output packet
78                let output_packet =
79                    match net.do_action(&NetAction::CreatePacket(Some(epoch.id.clone()))) {
80                        NetActionResponse::Success(NetActionResponseData::Packet(id), _) => id,
81                        _ => panic!("Failed to create output packet"),
82                    };
83                println!("Created output packet: {}", output_packet);
84
85                // Load it into the output port
86                net.do_action(&NetAction::LoadPacketIntoOutputPort(
87                    output_packet.clone(),
88                    "out".to_string(),
89                ));
90                println!("Loaded packet into output port");
91
92                // Send the output salvo
93                net.do_action(&NetAction::SendOutputSalvo(
94                    epoch.id.clone(),
95                    "default".to_string(),
96                ));
97                println!("Sent output salvo - packet is now on edge B -> C");
98
99                // Finish the epoch
100                net.do_action(&NetAction::FinishEpoch(epoch.id));
101                println!("Finished epoch");
102
103                // Run the network again - packet moves to C
104                net.run_until_blocked();
105                println!("Ran network until blocked again");
106
107                // Check for new startable epochs at C
108                let startable_c = net.get_startable_epochs();
109                println!(
110                    "New startable epochs (should be at C): {}",
111                    startable_c.len()
112                );
113            }
114            _ => panic!("Failed to start epoch"),
115        }
116    }
117
118    println!("\nLinear flow example complete!");
119}
Source

pub fn edges(&self) -> &HashSet<Edge>

Returns a reference to all edges in the graph.

Source

pub fn get_edge_by_tail(&self, output_port_ref: &PortRef) -> Option<&Edge>

Returns the edge that has the given output port as its source (tail).

Source

pub fn get_edges_by_head(&self, input_port_ref: &PortRef) -> &[Edge]

Returns all edges that have the given input port as their target (head). Fan-in is allowed, so multiple edges can connect to the same input port.

Source

pub fn validate(&self) -> Vec<GraphValidationError>

Validates the graph structure.

Returns a list of all validation errors found. An empty list means the graph is valid.

Examples found in repository?
examples/linear_flow.rs (line 135)
122fn create_linear_graph() -> Graph {
123    let nodes = vec![
124        create_node("A", vec![], vec!["out"]),
125        create_node("B", vec!["in"], vec!["out"]),
126        create_node("C", vec!["in"], vec![]),
127    ];
128
129    let edges = vec![
130        create_edge("A", "out", "B", "in"),
131        create_edge("B", "out", "C", "in"),
132    ];
133
134    let graph = Graph::new(nodes, edges);
135    assert!(graph.validate().is_empty(), "Graph validation failed");
136    graph
137}
More examples
Hide additional examples
examples/diamond_flow.rs (line 207)
122fn create_diamond_graph() -> Graph {
123    // Node A: source with two outputs
124    let node_a = Node {
125        name: "A".to_string(),
126        in_ports: HashMap::new(),
127        out_ports: [
128            (
129                "out1".to_string(),
130                Port {
131                    slots_spec: PortSlotSpec::Infinite,
132                },
133            ),
134            (
135                "out2".to_string(),
136                Port {
137                    slots_spec: PortSlotSpec::Infinite,
138                },
139            ),
140        ]
141        .into(),
142        in_salvo_conditions: IndexMap::new(),
143        out_salvo_conditions: IndexMap::new(),
144        dependency_request_config: None,
145    };
146
147    // Node B: one input, one output
148    let node_b = create_simple_node("B");
149
150    // Node C: one input, one output
151    let node_c = create_simple_node("C");
152
153    // Node D: TWO inputs (requires both), no outputs
154    let node_d = Node {
155        name: "D".to_string(),
156        in_ports: [
157            (
158                "in1".to_string(),
159                Port {
160                    slots_spec: PortSlotSpec::Infinite,
161                },
162            ),
163            (
164                "in2".to_string(),
165                Port {
166                    slots_spec: PortSlotSpec::Infinite,
167                },
168            ),
169        ]
170        .into(),
171        out_ports: HashMap::new(),
172        in_salvo_conditions: IndexMap::from([(
173            "default".to_string(),
174            SalvoCondition {
175                max_salvos: MaxSalvos::Finite(1),
176                ports: [
177                    ("in1".to_string(), PacketCount::All),
178                    ("in2".to_string(), PacketCount::All),
179                ]
180                .into_iter()
181                .collect(),
182                // Require BOTH inputs to be non-empty
183                term: SalvoConditionTerm::And(vec![
184                    SalvoConditionTerm::Port {
185                        port_name: "in1".to_string(),
186                        state: PortState::NonEmpty,
187                    },
188                    SalvoConditionTerm::Port {
189                        port_name: "in2".to_string(),
190                        state: PortState::NonEmpty,
191                    },
192                ]),
193            },
194        )]),
195        out_salvo_conditions: IndexMap::new(),
196        dependency_request_config: None,
197    };
198
199    let edges = vec![
200        create_edge("A", "out1", "B", "in"),
201        create_edge("A", "out2", "C", "in"),
202        create_edge("B", "out", "D", "in1"),
203        create_edge("C", "out", "D", "in2"),
204    ];
205
206    let graph = Graph::new(vec![node_a, node_b, node_c, node_d], edges);
207    assert!(graph.validate().is_empty(), "Graph validation failed");
208    graph
209}
Source§

impl Graph

Source

pub fn cascade_backward( &self, start_ports: &[PortRef], ) -> Result<CascadeResult, CascadeError>

Backward BFS from a set of input ports, following all edges upstream.

Returns source nodes (nodes with no incoming edges on any input port). Errors on cycles or unconnected input ports.

Trait Implementations§

Source§

impl Clone for Graph

Source§

fn clone(&self) -> Graph

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Graph

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Graph

§

impl RefUnwindSafe for Graph

§

impl Send for Graph

§

impl Sync for Graph

§

impl Unpin for Graph

§

impl UnsafeUnpin for Graph

§

impl UnwindSafe for Graph

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V