Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix various bugs in matrix graph #427

Merged
merged 5 commits into from May 16, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
152 changes: 117 additions & 35 deletions src/matrix_graph.rs
Expand Up @@ -230,13 +230,23 @@ impl<N, E, Ty: EdgeType, Null: Nullable<Wrapped = E>, Ix: IndexType>
};

debug_assert!(node_capacity <= <Ix as IndexType>::max().index());
m.extend_capacity_for_node(NodeIndex::new(node_capacity));
if node_capacity > 0 {
m.extend_capacity_for_node(NodeIndex::new(node_capacity - 1), true);
}

m
}

#[inline]
fn to_edge_position(&self, a: NodeIndex<Ix>, b: NodeIndex<Ix>) -> usize {
fn to_edge_position(&self, a: NodeIndex<Ix>, b: NodeIndex<Ix>) -> Option<usize> {
if cmp::max(a.index(), b.index()) >= self.node_capacity {
return None;
}
Some(self.to_edge_position_unchecked(a, b))
}

#[inline]
fn to_edge_position_unchecked(&self, a: NodeIndex<Ix>, b: NodeIndex<Ix>) -> usize {
to_linearized_matrix_position::<Ty>(a.index(), b.index(), self.node_capacity)
}

Expand Down Expand Up @@ -290,31 +300,36 @@ impl<N, E, Ty: EdgeType, Null: Nullable<Wrapped = E>, Ix: IndexType>
pub fn remove_node(&mut self, a: NodeIndex<Ix>) -> N {
for id in self.nodes.iter_ids() {
let position = self.to_edge_position(a, NodeIndex::new(id));
self.node_adjacencies[position] = Default::default();
if let Some(pos) = position {
self.node_adjacencies[pos] = Default::default();
}

if Ty::is_directed() {
let position = self.to_edge_position(NodeIndex::new(id), a);
self.node_adjacencies[position] = Default::default();
if let Some(pos) = position {
self.node_adjacencies[pos] = Default::default();
}
}
}

self.nodes.remove(a.index())
}

#[inline]
fn extend_capacity_for_node(&mut self, min_node: NodeIndex<Ix>) {
fn extend_capacity_for_node(&mut self, min_node: NodeIndex<Ix>, exact: bool) {
self.node_capacity = extend_linearized_matrix::<Ty, _>(
&mut self.node_adjacencies,
self.node_capacity,
min_node.index(),
min_node.index() + 1,
exact,
);
}

#[inline]
fn extend_capacity_for_edge(&mut self, a: NodeIndex<Ix>, b: NodeIndex<Ix>) {
let min_node = cmp::max(a, b);
if min_node.index() >= self.node_capacity {
self.extend_capacity_for_node(min_node);
self.extend_capacity_for_node(min_node, false);
}
}

Expand All @@ -328,8 +343,7 @@ impl<N, E, Ty: EdgeType, Null: Nullable<Wrapped = E>, Ix: IndexType>
/// **Panics** if any of the nodes don't exist.
pub fn update_edge(&mut self, a: NodeIndex<Ix>, b: NodeIndex<Ix>, weight: E) -> Option<E> {
self.extend_capacity_for_edge(a, b);

let p = self.to_edge_position(a, b);
let p = self.to_edge_position_unchecked(a, b);
let old_weight = mem::replace(&mut self.node_adjacencies[p], Null::new(weight));
if old_weight.is_null() {
self.nb_edges += 1;
Expand Down Expand Up @@ -360,7 +374,9 @@ impl<N, E, Ty: EdgeType, Null: Nullable<Wrapped = E>, Ix: IndexType>
/// **Panics** if any of the nodes don't exist.
/// **Panics** if no edge exists between `a` and `b`.
pub fn remove_edge(&mut self, a: NodeIndex<Ix>, b: NodeIndex<Ix>) -> E {
let p = self.to_edge_position(a, b);
let p = self
.to_edge_position(a, b)
.expect("No edge found between the nodes.");
let old_weight = mem::replace(&mut self.node_adjacencies[p], Default::default())
.into()
.unwrap();
Expand All @@ -373,8 +389,10 @@ impl<N, E, Ty: EdgeType, Null: Nullable<Wrapped = E>, Ix: IndexType>
///
/// **Panics** if any of the nodes don't exist.
pub fn has_edge(&self, a: NodeIndex<Ix>, b: NodeIndex<Ix>) -> bool {
let p = self.to_edge_position(a, b);
!self.node_adjacencies[p].is_null()
if let Some(p) = self.to_edge_position(a, b) {
return !self.node_adjacencies[p].is_null();
}
false
}

/// Access the weight for node `a`.
Expand All @@ -401,8 +419,12 @@ impl<N, E, Ty: EdgeType, Null: Nullable<Wrapped = E>, Ix: IndexType>
///
/// **Panics** if no edge exists between `a` and `b`.
pub fn edge_weight(&self, a: NodeIndex<Ix>, b: NodeIndex<Ix>) -> &E {
let p = self.to_edge_position(a, b);
self.node_adjacencies[p].as_ref().unwrap()
let p = self
.to_edge_position(a, b)
.expect("No edge found between the nodes.");
self.node_adjacencies[p]
.as_ref()
.expect("No edge found between the nodes.")
}

/// Access the weight for edge `e`, mutably.
Expand All @@ -411,8 +433,12 @@ impl<N, E, Ty: EdgeType, Null: Nullable<Wrapped = E>, Ix: IndexType>
///
/// **Panics** if no edge exists between `a` and `b`.
pub fn edge_weight_mut(&mut self, a: NodeIndex<Ix>, b: NodeIndex<Ix>) -> &mut E {
let p = self.to_edge_position(a, b);
self.node_adjacencies[p].as_mut().unwrap()
let p = self
.to_edge_position(a, b)
.expect("No edge found between the nodes.");
self.node_adjacencies[p]
.as_mut()
.expect("No edge found between the nodes.")
}

/// Return an iterator of all nodes with an edge starting from `a`.
Expand Down Expand Up @@ -784,12 +810,16 @@ fn to_linearized_matrix_position<Ty: EdgeType>(row: usize, column: usize, width:
fn extend_linearized_matrix<Ty: EdgeType, T: Default>(
node_adjacencies: &mut Vec<T>,
old_node_capacity: usize,
min_node_capacity: usize,
new_capacity: usize,
exact: bool,
) -> usize {
if old_node_capacity >= new_capacity {
return old_node_capacity;
}
if Ty::is_directed() {
extend_flat_square_matrix(node_adjacencies, old_node_capacity, min_node_capacity)
extend_flat_square_matrix(node_adjacencies, old_node_capacity, new_capacity, exact)
} else {
extend_lower_triangular_matrix(node_adjacencies, min_node_capacity)
extend_lower_triangular_matrix(node_adjacencies, new_capacity)
}
}

Expand All @@ -802,30 +832,42 @@ fn to_flat_square_matrix_position(row: usize, column: usize, width: usize) -> us
fn extend_flat_square_matrix<T: Default>(
node_adjacencies: &mut Vec<T>,
old_node_capacity: usize,
min_node_capacity: usize,
new_node_capacity: usize,
exact: bool,
) -> usize {
let min_node_capacity = (min_node_capacity + 1).next_power_of_two();
// Grow the capacity by exponential steps to avoid repeated allocations.
// Disabled for the with_capacity constructor.
let new_node_capacity = if exact {
new_node_capacity
} else {
const MIN_CAPACITY: usize = 4;
cmp::max(new_node_capacity.next_power_of_two(), MIN_CAPACITY)
};

// Optimization: when resizing the matrix this way we skip the first few grows to make
// small matrices a bit faster to work with.
const MIN_CAPACITY: usize = 4;
let new_node_capacity = cmp::max(min_node_capacity, MIN_CAPACITY);

let mut new_node_adjacencies = vec![];
ensure_len(&mut new_node_adjacencies, new_node_capacity.pow(2));

for c in 0..old_node_capacity {
ensure_len(node_adjacencies, new_node_capacity.pow(2));
for c in (1..old_node_capacity).rev() {
let pos = c * old_node_capacity;
let new_pos = c * new_node_capacity;

let mut old = &mut node_adjacencies[pos..pos + old_node_capacity];
let mut new = &mut new_node_adjacencies[new_pos..new_pos + old_node_capacity];
// Move the slices directly if they do not overlap with their new position
if pos + old_node_capacity <= new_pos {
let old = node_adjacencies[pos..pos + old_node_capacity].as_mut_ptr();
let new = node_adjacencies[new_pos..new_pos + old_node_capacity].as_mut_ptr();

mem::swap(&mut old, &mut new);
// SAFE: new starts at least `old_node_capacity` positions after old.
unsafe {
std::ptr::swap_nonoverlapping(old, new, old_node_capacity);
}
} else {
for i in (0..old_node_capacity).rev() {
node_adjacencies.as_mut_slice().swap(pos + i, new_pos + i);
}
}
}

mem::swap(node_adjacencies, &mut new_node_adjacencies);

new_node_capacity
}

Expand All @@ -842,11 +884,12 @@ fn to_lower_triangular_matrix_position(row: usize, column: usize) -> usize {
#[inline]
fn extend_lower_triangular_matrix<T: Default>(
node_adjacencies: &mut Vec<T>,
new_node_capacity: usize,
new_capacity: usize,
) -> usize {
let max_pos = to_lower_triangular_matrix_position(new_node_capacity, new_node_capacity);
let max_node = new_capacity - 1;
let max_pos = to_lower_triangular_matrix_position(max_node, max_node);
ensure_len(node_adjacencies, max_pos + 1);
new_node_capacity + 1
new_capacity
}

/// Grow a Vec by appending the type's default value until the `size` is reached.
Expand Down Expand Up @@ -1279,6 +1322,45 @@ mod tests {
assert_eq!(g.edge_count(), 2);
}

#[test]
/// Adds an edge that triggers a second extension of the matrix.
/// From #425
fn test_add_edge_with_extension() {
let mut g = DiMatrix::<u8, ()>::new();
let _n0 = g.add_node(0);
let n1 = g.add_node(1);
let n2 = g.add_node(2);
let n3 = g.add_node(3);
let n4 = g.add_node(4);
let _n5 = g.add_node(5);
g.add_edge(n2, n1, ());
g.add_edge(n2, n3, ());
g.add_edge(n2, n4, ());
assert_eq!(g.node_count(), 6);
assert_eq!(g.edge_count(), 3);
assert!(g.has_edge(n2, n1));
assert!(g.has_edge(n2, n3));
assert!(g.has_edge(n2, n4));
}

#[test]
fn test_matrix_resize() {
let mut g = DiMatrix::<u8, ()>::with_capacity(3);
let n0 = g.add_node(0);
let n1 = g.add_node(1);
let n2 = g.add_node(2);
let n3 = g.add_node(3);
g.add_edge(n1, n0, ());
g.add_edge(n1, n1, ());
// Triggers a resize from capacity 3 to 4
g.add_edge(n2, n3, ());
assert_eq!(g.node_count(), 4);
assert_eq!(g.edge_count(), 3);
assert!(g.has_edge(n1, n0));
assert!(g.has_edge(n1, n1));
assert!(g.has_edge(n2, n3));
}

#[test]
fn test_add_edge_with_weights() {
let mut g = MatrixGraph::new();
Expand Down