Add core/weightedGraph (#1590)
This module is a simple data type which contains a graph and associated weights. It provides methods for JSON (de)serialization, constructing new WeightedGraphs, and for merging them. Test plan: See included unit tests. The unit tests are simple because the data type and associated methods are quite simple; the underlying functions for Graphs and Weights have more extensive testing. Progress towards #1557.
This commit is contained in:
parent
6415da7bc8
commit
50a6a5f6a7
|
@ -0,0 +1,54 @@
|
|||
// @flow
|
||||
|
||||
import {Graph, type GraphJSON} from "./graph";
|
||||
import {type Weights as WeightsT, type WeightsJSON} from "./weights";
|
||||
import * as Weights from "./weights";
|
||||
import {toCompat, fromCompat, type Compatible} from "../util/compat";
|
||||
|
||||
/** The WeightedGraph a Graph alongside associated Weights
|
||||
*
|
||||
* Any combination of Weights and Graph can make a valid WeightedGraph. If the
|
||||
* Weights contains weights for node or edge addresses that are not present in
|
||||
* the graph, then those weights will be ignored. If the graph contains nodes
|
||||
* or edges which do not correspond to any weights, then default weights will
|
||||
* be inferred.
|
||||
*/
|
||||
export type WeightedGraph = {|+graph: Graph, +weights: WeightsT|};
|
||||
|
||||
const COMPAT_INFO = {type: "sourcecred/weightedGraph", version: "0.1.0"};
|
||||
|
||||
export type WeightedGraphJSON = Compatible<{|
|
||||
+graphJSON: GraphJSON,
|
||||
+weightsJSON: WeightsJSON,
|
||||
|}>;
|
||||
|
||||
/**
|
||||
* Create a new, empty WeightedGraph.
|
||||
*/
|
||||
export function empty(): WeightedGraph {
|
||||
return {graph: new Graph(), weights: Weights.empty()};
|
||||
}
|
||||
|
||||
export function toJSON(w: WeightedGraph): WeightedGraphJSON {
|
||||
const graphJSON = w.graph.toJSON();
|
||||
const weightsJSON = Weights.toJSON(w.weights);
|
||||
return toCompat(COMPAT_INFO, {graphJSON, weightsJSON});
|
||||
}
|
||||
|
||||
export function fromJSON(j: WeightedGraphJSON): WeightedGraph {
|
||||
const {graphJSON, weightsJSON} = fromCompat(COMPAT_INFO, j);
|
||||
const graph = Graph.fromJSON(graphJSON);
|
||||
const weights = Weights.fromJSON(weightsJSON);
|
||||
return {graph, weights};
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge multiple WeightedGraphs together.
|
||||
*
|
||||
* This delegates to the semantics of Graph.merge and Weights.merge.
|
||||
*/
|
||||
export function merge(ws: $ReadOnlyArray<WeightedGraph>): WeightedGraph {
|
||||
const graph = Graph.merge(ws.map((w) => w.graph));
|
||||
const weights = Weights.merge(ws.map((w) => w.weights));
|
||||
return {graph, weights};
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
// @flow
|
||||
|
||||
import * as Weights from "./weights";
|
||||
import {Graph} from "./graph";
|
||||
import * as WeightedGraph from "./weightedGraph";
|
||||
import * as GraphTest from "./graphTestUtil";
|
||||
|
||||
describe("core/weightedGraph", () => {
|
||||
function expectEqual(wg1, wg2) {
|
||||
expect(wg1.graph.equals(wg2.graph)).toBe(true);
|
||||
expect(wg1.weights).toEqual(wg2.weights);
|
||||
}
|
||||
|
||||
describe("empty", () => {
|
||||
it("empty produces an empty WeightedGraph", () => {
|
||||
const {weights, graph} = WeightedGraph.empty();
|
||||
expect(graph.equals(new Graph())).toBe(true);
|
||||
expect(weights).toEqual(Weights.empty());
|
||||
});
|
||||
});
|
||||
|
||||
describe("toJSON/fromJSON", () => {
|
||||
it("works for an empty WeightedGraph", () => {
|
||||
const wg = WeightedGraph.empty();
|
||||
const wgJSON = WeightedGraph.toJSON(wg);
|
||||
const wg_ = WeightedGraph.fromJSON(wgJSON);
|
||||
expectEqual(wg, wg_);
|
||||
});
|
||||
it("works for a non-empty WeightedGraph", () => {
|
||||
const node = GraphTest.node("foo");
|
||||
const graph = new Graph().addNode(node);
|
||||
const weights = Weights.empty();
|
||||
weights.nodeWeights.set(node.address, 5);
|
||||
const wg = {graph, weights};
|
||||
const wgJSON = WeightedGraph.toJSON(wg);
|
||||
const wg_ = WeightedGraph.fromJSON(wgJSON);
|
||||
expectEqual(wg, wg_);
|
||||
});
|
||||
});
|
||||
|
||||
describe("merge", () => {
|
||||
it("merge works on nontrivial graph", () => {
|
||||
// Not attempting to validate edge case semantics here, since this is just
|
||||
// a wrapper around Graph.merge and WeightedGraph.merge; those functions
|
||||
// are tested more thoroughly.
|
||||
const n1 = GraphTest.node("foo");
|
||||
const n2 = GraphTest.node("bar");
|
||||
const g1 = new Graph().addNode(n1);
|
||||
const g2 = new Graph().addNode(n2);
|
||||
const w1 = Weights.empty();
|
||||
w1.nodeWeights.set(n1.address, 1);
|
||||
const w2 = Weights.empty();
|
||||
w2.nodeWeights.set(n2.address, 2);
|
||||
const wg1 = {graph: g1, weights: w1};
|
||||
const wg2 = {graph: g2, weights: w2};
|
||||
const g = Graph.merge([g1, g2]);
|
||||
const w = Weights.merge([w1, w2]);
|
||||
const wg = WeightedGraph.merge([wg1, wg2]);
|
||||
const wg_ = {weights: w, graph: g};
|
||||
expectEqual(wg, wg_);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue