Add Weights.merge (#1584)

This adds a `merge` method to the weights module, which allows combining
multiple weights together. If any of the weights overlap (i.e. the same
address is specified in multiple Weights), then an error will be thrown.

In the future, we will likely extend the API so that the client can
specify how to resolve cases where the same address is present in
multiple weights.

The method is thoroughly unit tested.

This is part of work on #1557 (we'll want to be able to merge
`WeightedGraph`s.)

Test plan: Run `yarn test`
This commit is contained in:
Dandelion Mané 2020-01-25 09:56:36 -08:00 committed by GitHub
parent 325487a3ea
commit 9784927402
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 83 additions and 0 deletions

View File

@ -48,6 +48,21 @@ export function copy(w: Weights): Weights {
};
}
/**
* Merge multiple Weights together.
*
* The resultant Weights will have every weight specified by each of the
* input weights. If there are any overlaps (i.e. the same address is present
* in two or more of the input weights), an error will be thrown. In the future,
* we will likely modify this function to add a resolver that determines how to
* combine multiple overlapping weights.
*/
export function merge(ws: $ReadOnlyArray<Weights>): Weights {
const nodeWeights = MapUtil.merge(ws.map((x) => x.nodeWeights));
const edgeWeights = MapUtil.merge(ws.map((x) => x.edgeWeights));
return {nodeWeights, edgeWeights};
}
export type WeightsJSON = Compatible<{|
+nodeWeights: {[NodeAddressT]: NodeWeight},
+edgeWeights: {[EdgeAddressT]: EdgeWeight},

View File

@ -2,6 +2,7 @@
import stringify from "json-stable-stringify";
import {NodeAddress, EdgeAddress} from "../core/graph";
import {type Weights as WeightsT} from "./weights";
import * as Weights from "./weights";
describe("core/weights", () => {
@ -69,4 +70,71 @@ describe("core/weights", () => {
expect(weights).toEqual(Weights.fromJSON(json));
});
});
describe("merge", () => {
function simpleWeights(
nodeWeights: [string, number][],
edgeWeights: [string, number, number][]
): WeightsT {
const w = Weights.empty();
for (const [addrPart, weight] of nodeWeights) {
w.nodeWeights.set(NodeAddress.fromParts([addrPart]), weight);
}
for (const [addrPart, forwards, backwards] of edgeWeights) {
const weight = {forwards, backwards};
w.edgeWeights.set(EdgeAddress.fromParts([addrPart]), weight);
}
return w;
}
it("produces empty weights when given an empty array", () => {
expect(Weights.merge([])).toEqual(Weights.empty());
});
it("produces empty weights when given empty weights", () => {
expect(Weights.merge([Weights.empty(), Weights.empty()])).toEqual(
Weights.empty()
);
});
it("returns a copy when given only one weights", () => {
const w = simpleWeights([["foo", 3]], [["bar", 2, 3]]);
const wc = Weights.copy(w);
const merged = Weights.merge([w]);
expect(merged).toEqual(wc);
expect(merged).not.toBe(wc);
});
it("can merge two non-overlapping weights", () => {
const w1 = simpleWeights([["foo", 3]], [["bar", 2, 3]]);
const w2 = simpleWeights([["zod", 4]], [["zoink", 4, 5]]);
const w3 = simpleWeights(
[
["foo", 3],
["zod", 4],
],
[
["bar", 2, 3],
["zoink", 4, 5],
]
);
const merged = Weights.merge([w1, w2]);
expect(merged).toEqual(w3);
});
it("throws an error on overlapping weights with no conflicts", () => {
const w1 = simpleWeights([["foo", 3]], [["bar", 2, 3]]);
const w2 = simpleWeights([["foo", 3]], [["bar", 2, 3]]);
expect(() => Weights.merge([w1, w2])).toThrowError("duplicate key");
});
it("errors on conflicting node weights", () => {
const w1 = simpleWeights([["foo", 3]], []);
const w2 = simpleWeights([["foo", 4]], []);
expect(() => Weights.merge([w1, w2])).toThrowError("duplicate key");
});
it("errors on conflicting edge weights (forwards)", () => {
const w1 = simpleWeights([], [["foo", 3, 4]]);
const w2 = simpleWeights([], [["foo", 4, 4]]);
expect(() => Weights.merge([w1, w2])).toThrowError("duplicate key");
});
it("errors on conflicting edge weights (backwards)", () => {
const w1 = simpleWeights([], [["foo", 4, 4]]);
const w2 = simpleWeights([], [["foo", 4, 5]]);
expect(() => Weights.merge([w1, w2])).toThrowError("duplicate key");
});
});
});