Add the `attribution/pagerank` module (#455)
This module exposes a method, `pagerank`, which is a convenient entry point for taking a `Graph` and returning a `PagerankResult`. This obviates the need for `src/v1/app/credExplorer/basicPagerank.js`. Test plan: Unit tests included.
This commit is contained in:
parent
5c93085430
commit
fe64377194
|
@ -0,0 +1,59 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`core/attribution/pagerank respects explicit arguments 1`] = `
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"parts": Array [
|
||||||
|
"dst",
|
||||||
|
],
|
||||||
|
"probability": 0.25,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"parts": Array [
|
||||||
|
"isolated",
|
||||||
|
],
|
||||||
|
"probability": 0.25,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"parts": Array [
|
||||||
|
"loop",
|
||||||
|
],
|
||||||
|
"probability": 0.25,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"parts": Array [
|
||||||
|
"src",
|
||||||
|
],
|
||||||
|
"probability": 0.25,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`core/attribution/pagerank snapshots as expected on the advanced graph 1`] = `
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"parts": Array [
|
||||||
|
"dst",
|
||||||
|
],
|
||||||
|
"probability": 0.4999999999687968,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"parts": Array [
|
||||||
|
"isolated",
|
||||||
|
],
|
||||||
|
"probability": 0.25,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"parts": Array [
|
||||||
|
"loop",
|
||||||
|
],
|
||||||
|
"probability": 0.25,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"parts": Array [
|
||||||
|
"src",
|
||||||
|
],
|
||||||
|
"probability": 3.120317183596679e-11,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
|
@ -0,0 +1,49 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import {type Edge, Graph} from "../graph";
|
||||||
|
import {
|
||||||
|
type PagerankResult,
|
||||||
|
distributionToPagerankResult,
|
||||||
|
graphToOrderedSparseMarkovChain,
|
||||||
|
type EdgeWeight,
|
||||||
|
} from "./graphToMarkovChain";
|
||||||
|
|
||||||
|
import {findStationaryDistribution} from "./markovChain";
|
||||||
|
|
||||||
|
export type PagerankOptions = {|
|
||||||
|
+selfLoopWeight?: number,
|
||||||
|
+verbose?: boolean,
|
||||||
|
+convergenceThreshold?: number,
|
||||||
|
+maxIterations?: number,
|
||||||
|
|};
|
||||||
|
|
||||||
|
function defaultOptions(): PagerankOptions {
|
||||||
|
return {
|
||||||
|
verbose: false,
|
||||||
|
selfLoopWeight: 1e-3,
|
||||||
|
convergenceThreshold: 1e-7,
|
||||||
|
maxIterations: 255,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function pagerank(
|
||||||
|
graph: Graph,
|
||||||
|
edgeWeight: (Edge) => EdgeWeight,
|
||||||
|
options?: PagerankOptions
|
||||||
|
): PagerankResult {
|
||||||
|
const fullOptions = {
|
||||||
|
...defaultOptions(),
|
||||||
|
...(options || {}),
|
||||||
|
};
|
||||||
|
const osmc = graphToOrderedSparseMarkovChain(
|
||||||
|
graph,
|
||||||
|
edgeWeight,
|
||||||
|
fullOptions.selfLoopWeight
|
||||||
|
);
|
||||||
|
const distribution = findStationaryDistribution(osmc.chain, {
|
||||||
|
verbose: fullOptions.verbose,
|
||||||
|
convergenceThreshold: fullOptions.convergenceThreshold,
|
||||||
|
maxIterations: fullOptions.maxIterations,
|
||||||
|
});
|
||||||
|
return distributionToPagerankResult(osmc.nodeOrder, distribution);
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import {pagerank} from "./pagerank";
|
||||||
|
import {NodeAddress} from "../graph";
|
||||||
|
import {advancedGraph} from "../graphTestUtil";
|
||||||
|
|
||||||
|
function snapshotPagerankResult(result) {
|
||||||
|
const partsToProbability = [];
|
||||||
|
const sortedKeys = Array.from(result.keys()).sort();
|
||||||
|
for (const key of sortedKeys) {
|
||||||
|
const probability = result.get(key);
|
||||||
|
const parts = NodeAddress.toParts((key: any));
|
||||||
|
partsToProbability.push({parts, probability});
|
||||||
|
}
|
||||||
|
expect(partsToProbability).toMatchSnapshot();
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("core/attribution/pagerank", () => {
|
||||||
|
function edgeWeight(_unused_edge) {
|
||||||
|
return {toWeight: 1, froWeight: 0};
|
||||||
|
}
|
||||||
|
it("snapshots as expected on the advanced graph", () => {
|
||||||
|
const pagerankResult = pagerank(advancedGraph().graph1(), edgeWeight);
|
||||||
|
snapshotPagerankResult(pagerankResult);
|
||||||
|
});
|
||||||
|
it("respects explicit arguments", () => {
|
||||||
|
const pagerankResult = pagerank(advancedGraph().graph1(), edgeWeight, {
|
||||||
|
maxIterations: 0,
|
||||||
|
});
|
||||||
|
snapshotPagerankResult(pagerankResult);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue