Skip to content

Commit f1cf6fb

Browse files
committed
Added WeightedDirectedGraph methods: updateEdgeWeight and weightedEdges.
1 parent 524eaad commit f1cf6fb

6 files changed

Lines changed: 96 additions & 21 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11

2+
## 0.5.3
3+
- Added `WeightedDirectedGraph` methods: `weightedEdges` and `updateEdgeWeight`.
4+
- Updated docs and examples.
5+
26
## 0.5.2
37
- Fixed grammar in CHANGELOG entry below.
48
- Moved [`GraphCrawler`][GraphCrawler] to a separate folder.
@@ -30,7 +34,7 @@
3034
`DirectedGraphBase` to
3135
[`GraphCrawler`][GraphCrawler]. It is now using a more efficient recursive
3236
algorithm.
33-
- Extended the definition of a quasi-topological ordering in section `Usage`.
37+
- Extended the definition of a quasi-topological ordering in section `Usage`.
3438
- Lowered the required SDK version to ^3.5.0.
3539

3640
## 0.5.0

README.md

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -348,14 +348,10 @@ a [comparator] function is provided
348348
or if `T` implements [`Comparator`][Comparator].
349349

350350
```Dart
351-
352351
import 'package:directed_graph/directed_graph.dart';
353352
354353
void main(List<String> args) {
355-
int comparator(
356-
String s1,
357-
String s2,
358-
) {
354+
int comparator(String s1, String s2) {
359355
return s1.compareTo(s2);
360356
}
361357
@@ -375,15 +371,15 @@ void main(List<String> args) {
375371
376372
var graph = WeightedDirectedGraph<String, int>(
377373
{
378-
a: {b: 1, h: 7, c: 2, e: 40, g:7},
374+
a: {b: 1, h: 7, c: 2, e: 40, g: 7},
379375
b: {h: 6},
380376
c: {h: 5, g: 4},
381377
d: {e: 1, f: 2},
382378
e: {g: 2},
383379
f: {i: 3},
384380
i: {l: 3, k: 2},
385381
k: {g: 4, f: 5},
386-
l: {l: 0}
382+
l: {l: 0},
387383
},
388384
summation: sum,
389385
zero: 0,
@@ -407,6 +403,19 @@ void main(List<String> args) {
407403
final shortestPath = graph.shortestPath(a, g);
408404
print('\nShortest path a -> g');
409405
print('$shortestPath weight: ${graph.weightAlong(shortestPath)}');
406+
407+
print('\nTransitive Closure');
408+
print(WeightedDirectedGraph.transitiveClosure(graph));
409+
410+
print('\nTransitive Weighted Edges:');
411+
print(graph.transitiveWeightedEdges);
412+
413+
print('\nVertices reachable from d:');
414+
print(graph.reachableVertices(d));
415+
416+
print('\nUpdate weight of edge (a,b) with value 101:');
417+
graph.updateEdgeWeight(vertex: a, connectedVertex: b, weight: 101);
418+
print('graph.weightedEdges(a): ${graph.weightedEdges(a)}');
410419
}
411420
```
412421

@@ -429,7 +438,7 @@ Weighted Graph:
429438
'l': {'l': 0},
430439
}
431440

432-
Neighbouring vertices sorted by weight
441+
Neighbouring vertices sorted by weight:
433442
{
434443
'a': {'b': 1, 'c': 2, 'h': 7, 'g': 7, 'e': 40},
435444
'b': {'h': 6},
@@ -452,6 +461,30 @@ Heaviest path a -> g
452461

453462
Shortest path a -> g
454463
[a, g] weight: 7
464+
465+
Transitive Closure
466+
{
467+
'a': {'b': 1, 'c': 2, 'h': 7, 'g': 6, 'e': 40},
468+
'b': {'h': 6},
469+
'c': {'g': 4, 'h': 5},
470+
'd': {'e': 1, 'f': 2, 'g': 3, 'i': 5, 'k': 7, 'l': 8},
471+
'e': {'g': 2},
472+
'f': {'i': 3, 'k': 5, 'l': 6, 'g': 9, 'f': 10},
473+
'g': {},
474+
'h': {},
475+
'i': {'k': 2, 'l': 3, 'g': 6, 'f': 7, 'i': 10},
476+
'k': {'g': 4, 'f': 5, 'i': 8, 'k': 10, 'l': 11},
477+
'l': {'l': 0},
478+
}
479+
480+
Transitive Weighted Edges:
481+
{a: {b: 1, c: 2, h: 7, g: 6, e: 40}, b: {h: 6}, c: {g: 4, h: 5}, d: {e: 1, f: 2, g: 3, i: 5, k: 7, l: 8}, e: {g: 2}, f: {i: 3, k: 5, l: 6, g: 9, f: 10}, g: {}, h: {}, i: {k: 2, l: 3, g: 6, f: 7, i: 10}, k: {g: 4, f: 5, i: 8, k: 10, l: 11}, l: {l: 0}}
482+
483+
Vertices reachable from d:
484+
{e, g, f, i, k, l}
485+
486+
Update weight of edge (a,b) with value 101:
487+
graph.weightedEdges(a): {b: 101, c: 2, h: 7, g: 7, e: 40}
455488
```
456489
</details>
457490

example/bin/weighted_graph_example.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,8 @@ void main(List<String> args) {
6262

6363
print('\nVertices reachable from d:');
6464
print(graph.reachableVertices(d));
65+
66+
print('\nUpdate weight of edge (a,b) with value 101:');
67+
graph.updateEdgeWeight(vertex: a, connectedVertex: b, weight: 101);
68+
print('graph.weightedEdges(a): ${graph.weightedEdges(a)}');
6569
}

lib/src/graph/weighted_directed_graph.dart

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,15 @@ class WeightedDirectedGraph<T extends Object, W extends Comparable>
8484
/// Note: Mathematically, an edge is an ordered pair
8585
/// (vertex, connected-vertex).
8686
@override
87-
Set<T> edges(T vertex) =>
88-
_edges[vertex] == null ? <T>{} : _edges[vertex]!.keys.toSet();
87+
Set<T> edges(T vertex) => _edges[vertex]?.keys.toSet() ?? <T>{};
88+
89+
90+
/// Returns a map containing the vertices connected to [vertex] as keys
91+
/// and the weight associated with each edge as values.
92+
/// ---
93+
/// Returns and empty map if [vertex] is not connected to any other vertices
94+
/// or if [vertex] is not a graph vertex.
95+
Map<T, W> weightedEdges(T vertex) => Map.of(_edges[vertex] ?? <T, W>{});
8996

9097
/// Lazy variable representing the graph weight.
9198
late final _weight = Lazy<W>(() {
@@ -206,12 +213,12 @@ class WeightedDirectedGraph<T extends Object, W extends Comparable>
206213
updateCache();
207214
}
208215

209-
/// Returns the weight obtained by traversing `walk` and
216+
/// Returns the weight obtained by traversing the iterable [walk] and
210217
/// summing all edge weights.
211218
/// * The vertices must be traversable in the specified order.
212219
/// * Vertices and edges may be repeated.
213-
/// * Throws an error if the `walk` cannot be traversed.
214-
/// * Returns zero if `walk` is empty.
220+
/// * Throws an error if the [walk] cannot be traversed.
221+
/// * Returns zero if the iterable [walk] is empty.
215222
W weightAlong(Iterable<T> walk) {
216223
final edge = walk.take(2);
217224
if (edge.length < 2) {
@@ -221,20 +228,41 @@ class WeightedDirectedGraph<T extends Object, W extends Comparable>
221228
final connectedVertex = edge.last;
222229

223230
if (!_edges.containsKey(vertex)) {
224-
throw ErrorOfType<UnkownVertex>();
231+
throw ErrorOfType<UnkownVertex>(
232+
message: 'Could not calculate weight of walk: $walk',
233+
invalidState: '$vertex is not a graph vertex.');
225234
}
226235
if (!_edges[vertex]!.containsKey(connectedVertex)) {
227236
throw ErrorOfType<NotAnEdge>(
228-
message: 'Could not calculate weight of walk: $walk.',
229-
invalidState: 'Vertex $vertex not connected to $connectedVertex.}',
230-
);
237+
message: 'Could not calculate the weight of walk: $walk.',
238+
invalidState: 'Vertex $vertex is not connected to $connectedVertex.}',
239+
expectedState:
240+
'$walk must be traversable using existing graph edges.');
231241
}
232242
return summation(
233243
_edges[vertex]![connectedVertex]!,
234244
weightAlong(walk.skip(1)),
235245
);
236246
}
237247

248+
/// Assigns [weight] to the `existing` edge connecting [vertex]
249+
/// to [connectedVertex].
250+
/// * Returns `true` on success.
251+
/// * Return `false` if there is no graph edge connecting [vertex] to
252+
/// [connectedVertex].
253+
/// * Note: To create an new graph edge connecting [vertex] to
254+
/// [connectedVertex] use the method [addEdge].
255+
bool updateEdgeWeight(
256+
{required T vertex, required T connectedVertex, required W weight}) {
257+
if (!edgeExists(vertex, connectedVertex)) {
258+
return false;
259+
} else {
260+
_edges[vertex]![connectedVertex] = weight;
261+
_weight.updateCache();
262+
return true;
263+
}
264+
}
265+
238266
/// Sorts the neighbouring vertices of each vertex using [comparator].
239267
/// * By default the neighbouring vertices of a vertex are listed in
240268
/// insertion order.

pubspec.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name: directed_graph
33
description: Generic directed graph and weighted directed graph with algorithms
44
enabling sorting and topological ordering of vertices.
55

6-
version: 0.5.2
6+
version: 0.5.3
77

88
homepage: https://github.com/simphotonics/directed_graph
99

@@ -25,6 +25,6 @@ dependencies:
2525
quote_buffer: ^0.2.7
2626

2727
dev_dependencies:
28-
benchmark_runner: ^2.0.1
29-
lints: ^6.0.0
28+
benchmark_runner: ^2.0.2
29+
lints: ^6.1.0
3030
test: ^1.29.0

test/weighted_directed_graph_test.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,12 @@ void main() {
278278
final graph = WeightedDirectedGraph.of(graph0);
279279
expect(graph.weightAlong([a, c, g]), 6);
280280
});
281+
test('updateEdgeWeight', () {
282+
final graph = WeightedDirectedGraph.of(graph0);
283+
graph.updateEdgeWeight(vertex: a, connectedVertex: c, weight: 1002);
284+
expect(graph.weightAlong([a, c]), 1002);
285+
expect(graph.weight, graph0.weight + 1000);
286+
});
281287
});
282288

283289
group('TransitiveClosure', () {

0 commit comments

Comments
 (0)