Add `weightsToEdgeEvaluator` (#785)

This commit adds `weightsToEdgeEvaluator`, a function for converting
weighted node types into an `EdgeEvaluator`. This replaces the
`edgeWeights` module (which was untested, and an outmoded API).

Test plan: The new `weightsToEdgeEvaluator` method is well-tested.
Since `WeightConfig` is still not tested, I manually verified that it
still works as anticipated.
This commit is contained in:
Dandelion Mané 2018-09-05 13:40:40 -07:00 committed by GitHub
parent 3dda4ab35c
commit f7383bbc90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 126 additions and 60 deletions

View File

@ -3,7 +3,7 @@
import React from "react"; import React from "react";
import {type EdgeEvaluator} from "../../core/attribution/pagerank"; import {type EdgeEvaluator} from "../../core/attribution/pagerank";
import {byEdgeType, byNodeType} from "./edgeWeights"; import {weightsToEdgeEvaluator} from "./weights/weightsToEdgeEvaluator";
import type {StaticAdapterSet} from "../adapters/adapterSet"; import type {StaticAdapterSet} from "../adapters/adapterSet";
import { import {
type WeightedTypes, type WeightedTypes,
@ -104,18 +104,8 @@ export class WeightConfig extends React.Component<Props, State> {
edgeWeights = edgeWeights.concat(newEdgeWeights); edgeWeights = edgeWeights.concat(newEdgeWeights);
} }
const edgePrefixes = edgeWeights.map( const weights = {nodes: nodeWeights, edges: edgeWeights};
({type, forwardWeight, backwardWeight}) => ({ const edgeEvaluator = weightsToEdgeEvaluator(weights);
prefix: type.prefix,
forwardWeight,
backwardWeight,
})
);
const nodePrefixes = nodeWeights.map(({type, weight}) => ({
prefix: type.prefix,
weight,
}));
const edgeEvaluator = byNodeType(byEdgeType(edgePrefixes), nodePrefixes);
this.props.onChange(edgeEvaluator); this.props.onChange(edgeEvaluator);
} }
} }

View File

@ -1,47 +0,0 @@
// @flow
import {
type Edge,
type EdgeAddressT,
type NodeAddressT,
} from "../../core/graph";
import {NodeTrie, EdgeTrie} from "../../core/trie";
import type {EdgeEvaluator} from "../../core/attribution/pagerank";
export function byEdgeType(
prefixes: $ReadOnlyArray<{|
+prefix: EdgeAddressT,
+forwardWeight: number,
+backwardWeight: number,
|}>
): EdgeEvaluator {
const trie = new EdgeTrie();
for (const {prefix, forwardWeight, backwardWeight} of prefixes) {
trie.add(prefix, {toWeight: forwardWeight, froWeight: backwardWeight});
}
return function evaluator(edge: Edge) {
return trie.getLast(edge.address);
};
}
export function byNodeType(
base: EdgeEvaluator,
prefixes: $ReadOnlyArray<{|
+prefix: NodeAddressT,
+weight: number,
|}>
): EdgeEvaluator {
const trie = new NodeTrie();
for (const {weight, prefix} of prefixes) {
trie.add(prefix, weight);
}
return function evaluator(edge: Edge) {
const srcWeight = trie.getLast(edge.src);
const dstWeight = trie.getLast(edge.dst);
const baseResult = base(edge);
return {
toWeight: dstWeight * baseResult.toWeight,
froWeight: srcWeight * baseResult.froWeight,
};
};
}

View File

@ -0,0 +1,27 @@
// @flow
import type {Edge} from "../../../core/graph";
import type {WeightedTypes} from "./PluginWeightConfig";
import type {EdgeEvaluator} from "../../../core/attribution/pagerank";
import {NodeTrie, EdgeTrie} from "../../../core/trie";
export function weightsToEdgeEvaluator(weights: WeightedTypes): EdgeEvaluator {
const nodeTrie = new NodeTrie();
for (const {type, weight} of weights.nodes) {
nodeTrie.add(type.prefix, weight);
}
const edgeTrie = new EdgeTrie();
for (const {type, forwardWeight, backwardWeight} of weights.edges) {
edgeTrie.add(type.prefix, {forwardWeight, backwardWeight});
}
return function evaluator(edge: Edge) {
const srcWeight = nodeTrie.getLast(edge.src);
const dstWeight = nodeTrie.getLast(edge.dst);
const {forwardWeight, backwardWeight} = edgeTrie.getLast(edge.address);
return {
toWeight: dstWeight * forwardWeight,
froWeight: srcWeight * backwardWeight,
};
};
}

View File

@ -0,0 +1,96 @@
// @flow
import * as NullUtil from "../../../util/null";
import {
fallbackNodeType,
fallbackEdgeType,
} from "../../adapters/fallbackAdapter";
import {
inserterNodeType,
machineNodeType,
assemblesEdgeType,
factorioEdges,
} from "../../adapters/demoAdapters";
import {weightsToEdgeEvaluator} from "./weightsToEdgeEvaluator";
describe("app/credExplorer/weights/weightsToEdgeEvaluator", () => {
describe("weightsToEdgeEvaluator", () => {
type WeightArgs = {|
+assemblesForward?: number,
+assemblesBackward?: number,
+baseForward?: number,
+baseBackward?: number,
+inserter?: number,
+machine?: number,
+baseNode?: number,
|};
function weights({
assemblesForward,
assemblesBackward,
baseForward,
baseBackward,
inserter,
machine,
baseNode,
}: WeightArgs) {
const nodes = [
{weight: NullUtil.orElse(inserter, 1), type: inserterNodeType},
{weight: NullUtil.orElse(machine, 1), type: machineNodeType},
{weight: NullUtil.orElse(baseNode, 1), type: fallbackNodeType},
];
const edges = [
{
forwardWeight: NullUtil.orElse(assemblesForward, 1),
backwardWeight: NullUtil.orElse(assemblesBackward, 1),
type: assemblesEdgeType,
},
{
forwardWeight: NullUtil.orElse(baseForward, 1),
backwardWeight: NullUtil.orElse(baseBackward, 1),
type: fallbackEdgeType,
},
];
return {nodes, edges};
}
function exampleEdgeWeights(weightArgs: WeightArgs) {
const ws = weights(weightArgs);
const ee = weightsToEdgeEvaluator(ws);
// src is a machine, dst is an inserter, edge type is assembles
return ee(factorioEdges.assembles1);
}
it("toWeight is affected by the edge's forwardWeight", () => {
expect(exampleEdgeWeights({assemblesForward: 2}).toWeight).toEqual(2);
});
it("froWeight is affected by the edge's backwardWeight", () => {
expect(exampleEdgeWeights({assemblesBackward: 3}).froWeight).toEqual(3);
});
it("toWeight is affected by the dst's weight", () => {
expect(exampleEdgeWeights({inserter: 4}).toWeight).toEqual(4);
});
it("froWeight is affected by the src's weight", () => {
expect(exampleEdgeWeights({machine: 5}).froWeight).toEqual(5);
});
it("only the closest-matching node prefix is considered", () => {
expect(exampleEdgeWeights({baseNode: 6})).toEqual({
toWeight: 1,
froWeight: 1,
});
});
it("only the closest-matching edge prefix is considered", () => {
expect(exampleEdgeWeights({baseBackward: 7})).toEqual({
toWeight: 1,
froWeight: 1,
});
});
it("node and edge weights compose via multiplication", () => {
expect(
exampleEdgeWeights({
inserter: 2,
machine: 3,
assemblesForward: 4,
assemblesBackward: 5,
})
).toEqual({toWeight: 8, froWeight: 15});
});
});
});