mirror of
https://github.com/status-im/sourcecred.git
synced 2025-02-28 12:10:30 +00:00
pagerankGraph: add edge filter (#1105)
Part of ongoing work for #1020. Test plan: Added tests that mirror the edge filtering tests in `graph.test` to check that `graph` and `pagerankGraph` return the same edges with the given `EdgesOptions` parameter. Also added a sanity check that a `weight` prop is returned from the iterator along with the edge. Given the dependence on a helper function to test the edge iterator's equality between graphs, I would suggest reviewers give particular attention to that function: `expectConsistentEdges()`
This commit is contained in:
parent
656a2d1543
commit
b16c374a2b
@ -6,6 +6,7 @@ import {toCompat, fromCompat, type Compatible} from "../util/compat";
|
||||
import {
|
||||
Graph,
|
||||
type Edge,
|
||||
type EdgesOptions,
|
||||
type NodeAddressT,
|
||||
type EdgeAddressT,
|
||||
type GraphJSON,
|
||||
@ -237,15 +238,18 @@ export class PagerankGraph {
|
||||
/**
|
||||
* Provides edge and weight for every edge in the underlying graph.
|
||||
*
|
||||
* TODO(#1020): Allow optional filtering, as in Graph.edges.
|
||||
* Optionally, provide an EdgesOptions parameter to return an
|
||||
* iterator containing edges matching the EdgesOptions prefix
|
||||
* filter parameters. See Graph.edges for details.
|
||||
*/
|
||||
edges(): Iterator<WeightedEdge> {
|
||||
edges(options?: EdgesOptions): Iterator<WeightedEdge> {
|
||||
this._verifyGraphNotModified();
|
||||
return this._edgesIterator();
|
||||
const iterator = this._graph.edges(options);
|
||||
return this._edgesIterator(iterator);
|
||||
}
|
||||
|
||||
*_edgesIterator(): Iterator<WeightedEdge> {
|
||||
for (const edge of this._graph.edges()) {
|
||||
*_edgesIterator(iterator: Iterator<Edge>): Iterator<WeightedEdge> {
|
||||
for (const edge of iterator) {
|
||||
const weight = NullUtil.get(this._edgeWeights.get(edge.address));
|
||||
yield {edge, weight};
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
EdgeAddress,
|
||||
type NodeAddressT,
|
||||
type Edge,
|
||||
type EdgesOptions,
|
||||
} from "./graph";
|
||||
import {PagerankGraph} from "./pagerankGraph";
|
||||
import {advancedGraph} from "./graphTestUtil";
|
||||
@ -230,6 +231,123 @@ describe("core/pagerankGraph", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("edge filtering", () => {
|
||||
const src1 = NodeAddress.fromParts(["src", "1"]);
|
||||
const src2 = NodeAddress.fromParts(["src", "2"]);
|
||||
const dst1 = NodeAddress.fromParts(["dst", "1"]);
|
||||
const dst2 = NodeAddress.fromParts(["dst", "2"]);
|
||||
const e11 = {
|
||||
src: src1,
|
||||
dst: dst1,
|
||||
address: EdgeAddress.fromParts(["e", "1", "1"]),
|
||||
};
|
||||
const e12 = {
|
||||
src: src1,
|
||||
dst: dst2,
|
||||
address: EdgeAddress.fromParts(["e", "1", "2"]),
|
||||
};
|
||||
const e21 = {
|
||||
src: src2,
|
||||
dst: dst1,
|
||||
address: EdgeAddress.fromParts(["e", "2", "1"]),
|
||||
};
|
||||
const e22 = {
|
||||
src: src2,
|
||||
dst: dst2,
|
||||
address: EdgeAddress.fromParts(["e", "2", "2"]),
|
||||
};
|
||||
const graph = () => {
|
||||
const g = new Graph();
|
||||
[src1, src2, dst1, dst2].forEach((n) => g.addNode(n));
|
||||
[e11, e12, e21, e22].forEach((e) => g.addEdge(e));
|
||||
return g;
|
||||
};
|
||||
const pagerankGraph = () => new PagerankGraph(graph(), defaultEvaluator);
|
||||
|
||||
function expectConsistentEdges(options: EdgesOptions | void) {
|
||||
const pagerankGraphEdges = Array.from(pagerankGraph().edges(options));
|
||||
pagerankGraphEdges.forEach((e) => {
|
||||
expect(e.weight.froWeight).toBe(0);
|
||||
expect(e.weight.toWeight).toBe(1);
|
||||
});
|
||||
const graphEdges = Array.from(graph().edges(options));
|
||||
expect(pagerankGraphEdges.map((e) => e.edge)).toEqual(graphEdges);
|
||||
}
|
||||
|
||||
describe("edge filter matches graph edge filter", () => {
|
||||
it("finds all edges when no options are specified", () => {
|
||||
expectConsistentEdges(undefined);
|
||||
});
|
||||
it("finds all edges when all-inclusive filters are specified", () => {
|
||||
expectConsistentEdges({
|
||||
addressPrefix: EdgeAddress.fromParts(["e"]),
|
||||
srcPrefix: NodeAddress.fromParts(["src"]),
|
||||
dstPrefix: NodeAddress.fromParts(["dst"]),
|
||||
});
|
||||
});
|
||||
it("finds edges by address prefix", () => {
|
||||
expectConsistentEdges({
|
||||
addressPrefix: EdgeAddress.fromParts(["e", "1"]),
|
||||
srcPrefix: NodeAddress.empty,
|
||||
dstPrefix: NodeAddress.empty,
|
||||
});
|
||||
});
|
||||
it("finds edges by src prefix", () => {
|
||||
expectConsistentEdges({
|
||||
addressPrefix: EdgeAddress.empty,
|
||||
srcPrefix: NodeAddress.fromParts(["src", "1"]),
|
||||
dstPrefix: NodeAddress.empty,
|
||||
});
|
||||
});
|
||||
it("finds edges by dst prefix", () => {
|
||||
expectConsistentEdges({
|
||||
addressPrefix: EdgeAddress.empty,
|
||||
srcPrefix: NodeAddress.empty,
|
||||
dstPrefix: NodeAddress.fromParts(["dst", "1"]),
|
||||
});
|
||||
});
|
||||
it("yields nothing for disjoint filters", () => {
|
||||
expectConsistentEdges({
|
||||
addressPrefix: EdgeAddress.fromParts(["e", "1"]),
|
||||
srcPrefix: NodeAddress.fromParts(["src", "2"]),
|
||||
dstPrefix: NodeAddress.empty,
|
||||
});
|
||||
});
|
||||
it("yields appropriate filter intersection", () => {
|
||||
expectConsistentEdges({
|
||||
addressPrefix: EdgeAddress.empty,
|
||||
srcPrefix: NodeAddress.fromParts(["src", "1"]),
|
||||
dstPrefix: NodeAddress.fromParts(["dst", "2"]),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("edge filter options", () => {
|
||||
it("requires `addressPrefix` to be present in provided options", () => {
|
||||
expect(() => {
|
||||
pagerankGraph()
|
||||
// $ExpectFlowError
|
||||
.edges({srcPrefix: src1, dstPrefix: dst1});
|
||||
}).toThrow("Invalid address prefix: undefined");
|
||||
});
|
||||
it("requires `srcPrefix` to be present in provided options", () => {
|
||||
expect(() => {
|
||||
pagerankGraph()
|
||||
// $ExpectFlowError
|
||||
.edges({addressPrefix: e11, dstPrefix: dst1});
|
||||
}).toThrow("Invalid src prefix: undefined");
|
||||
});
|
||||
|
||||
it("requires `dstPrefix` to be present in provided options", () => {
|
||||
expect(() => {
|
||||
pagerankGraph()
|
||||
// $ExpectFlowError
|
||||
.edges({addressPrefix: e11, srcPrefix: dst1});
|
||||
}).toThrow("Invalid dst prefix: undefined");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("runPagerank", () => {
|
||||
// The mathematical semantics of PageRank are thoroughly tested
|
||||
// in the markovChain module. The goal for these tests is just
|
||||
|
Loading…
x
Reference in New Issue
Block a user