Skip to content

Commit

Permalink
Improved topological sort.
Browse files Browse the repository at this point in the history
  • Loading branch information
SamiPerttu committed Sep 12, 2024
1 parent e3a7d5a commit 2804400
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 51 deletions.
92 changes: 41 additions & 51 deletions src/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ use thingbuf::mpsc::{channel, Receiver, Sender};
extern crate alloc;
use super::sequencer::Fade;
use alloc::boxed::Box;
use alloc::vec;
use alloc::vec::Vec;

pub type NodeIndex = usize;
Expand Down Expand Up @@ -640,7 +639,10 @@ impl Net {
for j in 0..self.vertex.len() {
self.vertex[j].update_source_vertex();
}
let mut order = Vec::new();
let mut order = match self.order.take() {
Some(v) => v,
None => Vec::with_capacity(self.vertex.len()),
};
if !self.determine_order_in(&mut order) {
panic!("Cycle detected");
}
Expand All @@ -649,65 +651,53 @@ impl Net {

/// Determine node order in the supplied vector. Returns true if successful, false
/// if a cycle was detected.
fn determine_order_in(&self, order: &mut Vec<NodeIndex>) -> bool {
let mut vertices_left = self.vertex.len();
let mut vertex_left = vec![true; self.vertex.len()];
// Note about contents of the edge vector.
// Each node input appears there exactly once.
// Sources, however, are not unique or guaranteed to appear.
let mut all_edges: Vec<Edge> = Vec::new();
for vertex in self.vertex.iter() {
for edge in &vertex.source {
all_edges.push(*edge);
}
}

let mut inputs_left = vec![0; self.vertex.len()];
for i in 0..inputs_left.len() {
inputs_left[i] = self.vertex[i].unit.inputs();
if inputs_left[i] == 0 {
vertex_left[i] = false;
order.push(i);
vertices_left -= 1;
fn determine_order_in(&mut self, order: &mut Vec<NodeIndex>) -> bool {
// We calculate an inverse order here and then reverse it,
// as that is efficient with the data we have at hand.
// A downside of this algorithm is that vertices that are not
// connected to outputs at all still get included in the ordering.
order.clear();

for i in 0..self.vertex.len() {
self.vertex[i].unplugged = 0;
self.vertex[i].ordered = false;
}

for i in 0..self.vertex.len() {
for channel in 0..self.vertex[i].inputs() {
if let Port::Local(j, _) = self.vertex[i].source[channel].source {
self.vertex[j].unplugged += 1;
}
}
}

// Start from network inputs.
for edge in all_edges.iter() {
if let (Port::Global(_) | Port::Zero, Port::Local(vertex, _)) =
(edge.source, edge.target)
{
if vertex_left[vertex] {
inputs_left[vertex] -= 1;
if inputs_left[vertex] == 0 {
vertex_left[vertex] = false;
order.push(vertex);
vertices_left -= 1;
fn propagate(net: &mut Net, i: usize, order: &mut Vec<NodeIndex>) {
for channel in 0..net.vertex[i].inputs() {
if let Port::Local(j, _) = net.vertex[i].source[channel].source {
net.vertex[j].unplugged -= 1;
if net.vertex[j].unplugged == 0 {
net.vertex[j].ordered = true;
order.push(j);
propagate(net, j, order);
}
}
}
}
while vertices_left > 0 {
let mut progress = false;
for edge in all_edges.iter() {
if let (Port::Local(source, _), Port::Local(target, _)) = (edge.source, edge.target)
{
if !vertex_left[source] && vertex_left[target] {
progress = true;
inputs_left[target] -= 1;
if inputs_left[target] == 0 {
vertex_left[target] = false;
order.push(target);
vertices_left -= 1;
}
}
}

for i in 0..self.vertex.len() {
if self.vertex[i].ordered {
continue;
}
if !progress {
return false;
if self.vertex[i].unplugged == 0 {
self.vertex[i].ordered = true;
order.push(i);
propagate(self, i, order);
}
}
true

order.reverse();

order.len() == self.vertex.len()
}

/// Wrap arbitrary unit in a network.
Expand Down
6 changes: 6 additions & 0 deletions src/vertex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ pub(crate) struct Vertex {
pub source_vertex: Option<(NodeIndex, usize)>,
/// Network revision in which this vertex was changed last.
pub changed: u64,
/// Used during order determination: number of unaccounted for outputs.
pub unplugged: usize,
/// Used during order determination: has this vertex been ordered yet.
pub ordered: bool,
}

impl Vertex {
Expand All @@ -65,6 +69,8 @@ impl Vertex {
latest: NodeEdit::default(),
source_vertex: None,
changed: 0,
unplugged: 0,
ordered: false,
};
for i in 0..vertex.inputs() {
vertex.source.push(edge(Port::Zero, Port::Local(index, i)));
Expand Down

0 comments on commit 2804400

Please sign in to comment.