From 16a1f88565329acd2e9a240b434594f4388bac17 Mon Sep 17 00:00:00 2001 From: Cameron Yick Date: Tue, 3 Sep 2019 19:03:53 -0400 Subject: [PATCH 1/3] [feature] Port algorithms.dag.ancestors from Python --- src/algorithms/dag.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/algorithms/dag.js b/src/algorithms/dag.js index 8de408d..01a9cd0 100644 --- a/src/algorithms/dag.js +++ b/src/algorithms/dag.js @@ -4,6 +4,8 @@ import JSNetworkXError from '../exceptions/JSNetworkXError'; import JSNetworkXUnfeasible from '../exceptions/JSNetworkXUnfeasible'; +import { shortestPathLength } from './shortestPaths'; + import { /*jshint ignore:start*/ Map, @@ -14,7 +16,24 @@ import { } from '../_internals'; // TODO: descendants -// TODO: ancestors + +/** + * Returns all nodes having a path to `source` in `G` + * + * @param {Graph} G A NetworkX directed acyclic graph + * @param {Node} Source: node in `G` + * @return {Set} Set of all nodes that source is descended from + */ +export async function ancestors(G, source) { + if (!G.hasNode(source)) { + throw new JSNetworkXError( + `The node ${source} is not in the graph` + ) + } + const ancestorNodes = new Set(shortestPathLength(G, { source }).keys()); + ancestorNodes.delete(source); // cannot be own parent + return ancestorNodes; +} /** * Return `true` if the graph G is a directed acyclic graph (DAG) or From 4b39f2cfd23ade74474984a74e1099cbaa82f38e Mon Sep 17 00:00:00 2001 From: Cameron Yick Date: Tue, 3 Sep 2019 19:07:54 -0400 Subject: [PATCH 2/3] [feature] Port algorithms.dag.descendants from Python --- src/algorithms/dag.js | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/algorithms/dag.js b/src/algorithms/dag.js index 01a9cd0..74e5bfb 100644 --- a/src/algorithms/dag.js +++ b/src/algorithms/dag.js @@ -15,7 +15,23 @@ import { gcd } from '../_internals'; -// TODO: descendants +/** + * Returns all nodes reachable from `source` in `G` + * + * @param {Graph} G A NetworkX directed acyclic graph + * @param {Node} Source: node in `G` + * @return {Set} The descendants of `source` in `G` + */ +export async function descendants(G, source) { + if (!G.hasNode(source)) { + throw new JSNetworkXError( + `The node ${source} is not in the graph` + ) + } + const descendantNodes = new Set(shortestPathLength(G, { source }).keys()); + descendantNodes.delete(source); // cannot be own parent + return descendantNodes; +} /** * Returns all nodes having a path to `source` in `G` @@ -30,7 +46,7 @@ export async function ancestors(G, source) { `The node ${source} is not in the graph` ) } - const ancestorNodes = new Set(shortestPathLength(G, { source }).keys()); + const ancestorNodes = new Set(shortestPathLength(G, { target: source }).keys()); ancestorNodes.delete(source); // cannot be own parent return ancestorNodes; } From fc141240e83cfcd7223e64dc1a5a6931655b29eb Mon Sep 17 00:00:00 2001 From: Cameron Yick Date: Wed, 4 Sep 2019 01:29:44 -0400 Subject: [PATCH 3/3] [dag.algorithms] Add unit tests for descendants/ancestors --- src/algorithms/__tests__/dag-test.js | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/algorithms/__tests__/dag-test.js b/src/algorithms/__tests__/dag-test.js index 3834d0f..d184ee7 100644 --- a/src/algorithms/__tests__/dag-test.js +++ b/src/algorithms/__tests__/dag-test.js @@ -1,6 +1,7 @@ /*globals assert, utils*/ 'use strict'; +import { Set } from '../../_internals/'; import {Graph, DiGraph} from '../../classes'; import JSNetworkXError from '../../exceptions/JSNetworkXError'; import JSNetworkXUnfeasible from '../../exceptions/JSNetworkXUnfeasible'; @@ -162,5 +163,34 @@ export var TestDAG = { G.addCycle([0,1,2]); G.addEdge(3,3); assert(!dag.isAperiodic(G)); + }, + + // dag.ancestors + // https://github.com/networkx/networkx/blob/master/networkx/algorithms/tests/test_dag.py#L281 + testAncestors: function () { + var DG = new DiGraph(); + DG.addEdgesFrom([[1, 2], [1, 3], [4, 2], [4, 3], [4, 5], [2, 6], [5, 6]]); + assert.deepEqual(dag.ancestors(DG, 6), new Set([1, 2, 4, 5])); + assert.deepEqual(dag.ancestors(DG, 3), new Set([1, 4])); + assert.deepEqual(dag.ancestors(DG, 1), new Set()); + }, + testAncestorsInvalidSource: function () { + var DG = new DiGraph(); + DG.addEdgesFrom([[1, 2], [1, 3]]); // tested node is not in graph + assert.throws(() => dag.ancestors(DG, 0), JSNetworkXError); + }, + + // dag.descendants + testDescendants: function () { + var DG = new DiGraph(); + DG.addEdgesFrom([[1, 2], [1, 3], [4, 2], [4, 3], [4, 5], [2, 6], [5, 6]]); + assert.deepEqual(dag.descendants(DG, 1), new Set([2, 3, 6])); + assert.deepEqual(dag.descendants(DG, 4), new Set([2, 3, 5, 6])); + assert.deepEqual(dag.descendants(DG, 3), new Set([])); + }, + testDescendantsInvalidSource: function () { + var DG = new DiGraph(); + DG.addEdgesFrom([[1, 2], [1, 3]]); + assert.throws(() => dag.descendants(DG, 0), JSNetworkXError); } };