Separate in-edge and out-edge contribution types (#491)

Summary:
This change is motivated by the behavior of loops, but applies more
generally to edges. Previously, a loop would induce two contributions
with the same contributor, but possibly different weights: one with the
to-weight of the edge and one with the fro-weight. For one, this is
annoying to downstream clients, who would like to use the contributor as
a superkey. But it is also somewhat strange that a single contributor
could have two different weights.

The same applies to edges in general: every edge induces two
contributions with the same contributor, of type `NEIGHBOR`.

As of this commit, we replace `NEIGHBOR` with `IN_EDGE` and `OUT_EDGE`,
one of each induced by each edge. This has the effect that a contributor
maps to at most one contribution.

Test Plan:
Existing unit tests updated.

wchargin-branch: separate-in-out-edge-contributions
This commit is contained in:
William Chargin 2018-07-05 18:58:47 -07:00 committed by GitHub
parent 761a44c561
commit 47eecef7b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 19 additions and 24 deletions

View File

@ -1,18 +1,13 @@
// @flow // @flow
import { import {type Edge, type Graph, type NodeAddressT, NodeAddress} from "../graph";
type Edge,
type Graph,
type Neighbor,
type NodeAddressT,
NodeAddress,
} from "../graph";
import type {Distribution, SparseMarkovChain} from "./markovChain"; import type {Distribution, SparseMarkovChain} from "./markovChain";
export type Probability = number; export type Probability = number;
export type Contributor = export type Contributor =
| {|+type: "SYNTHETIC_LOOP"|} | {|+type: "SYNTHETIC_LOOP"|}
| {|+type: "NEIGHBOR", +neighbor: Neighbor|}; | {|+type: "IN_EDGE", +edge: Edge|}
| {|+type: "OUT_EDGE", +edge: Edge|};
export type Contribution = {| export type Contribution = {|
+contributor: Contributor, +contributor: Contributor,
// This `weight` is a conditional probability: given that you're at // This `weight` is a conditional probability: given that you're at
@ -28,8 +23,10 @@ export function contributorSource(
switch (contributor.type) { switch (contributor.type) {
case "SYNTHETIC_LOOP": case "SYNTHETIC_LOOP":
return target; return target;
case "NEIGHBOR": case "IN_EDGE":
return contributor.neighbor.node; return contributor.edge.src;
case "OUT_EDGE":
return contributor.edge.dst;
default: default:
throw new Error((contributor.type: empty)); throw new Error((contributor.type: empty));
} }
@ -104,11 +101,11 @@ export function createContributions(
const {toWeight, froWeight} = edgeWeight(edge); const {toWeight, froWeight} = edgeWeight(edge);
const {src, dst} = edge; const {src, dst} = edge;
processContribution(dst, { processContribution(dst, {
contributor: {type: "NEIGHBOR", neighbor: {node: src, edge}}, contributor: {type: "IN_EDGE", edge},
weight: toWeight, weight: toWeight,
}); });
processContribution(src, { processContribution(src, {
contributor: {type: "NEIGHBOR", neighbor: {node: dst, edge}}, contributor: {type: "OUT_EDGE", edge},
weight: froWeight, weight: froWeight,
}); });
} }

View File

@ -109,44 +109,42 @@ describe("core/attribution/graphToMarkovChain", () => {
.set(n1, [ .set(n1, [
{contributor: {type: "SYNTHETIC_LOOP"}, weight: 1 / 13}, {contributor: {type: "SYNTHETIC_LOOP"}, weight: 1 / 13},
{ {
contributor: {type: "NEIGHBOR", neighbor: {node: n2, edge: e1}}, contributor: {type: "OUT_EDGE", edge: e1},
weight: 3 / 10, weight: 3 / 10,
}, },
{ {
contributor: {type: "NEIGHBOR", neighbor: {node: n3, edge: e3}}, contributor: {type: "OUT_EDGE", edge: e3},
weight: 3 / 16, weight: 3 / 16,
}, },
]) ])
.set(n2, [ .set(n2, [
{contributor: {type: "SYNTHETIC_LOOP"}, weight: 1 / 10}, {contributor: {type: "SYNTHETIC_LOOP"}, weight: 1 / 10},
{ {
contributor: {type: "NEIGHBOR", neighbor: {node: n1, edge: e1}}, contributor: {type: "IN_EDGE", edge: e1},
weight: 6 / 13, weight: 6 / 13,
}, },
{ {
contributor: {type: "NEIGHBOR", neighbor: {node: n3, edge: e2}}, contributor: {type: "OUT_EDGE", edge: e2},
weight: 3 / 16, weight: 3 / 16,
}, },
]) ])
.set(n3, [ .set(n3, [
{contributor: {type: "SYNTHETIC_LOOP"}, weight: 1 / 16}, {contributor: {type: "SYNTHETIC_LOOP"}, weight: 1 / 16},
{ {
contributor: {type: "NEIGHBOR", neighbor: {node: n2, edge: e2}}, contributor: {type: "IN_EDGE", edge: e2},
weight: 6 / 10, weight: 6 / 10,
}, },
{ {
contributor: {type: "NEIGHBOR", neighbor: {node: n1, edge: e3}}, contributor: {type: "IN_EDGE", edge: e3},
weight: 6 / 13, weight: 6 / 13,
}, },
{ {
contributor: {type: "NEIGHBOR", neighbor: {node: n3, edge: e4}}, contributor: {type: "IN_EDGE", edge: e4},
// this loop, as an out-edge weight: 6 / 16,
weight: 3 / 16,
}, },
{ {
contributor: {type: "NEIGHBOR", neighbor: {node: n3, edge: e4}}, contributor: {type: "OUT_EDGE", edge: e4},
// this loop, as an in-edge weight: 3 / 16,
weight: 6 / 16,
}, },
]); ]);
const canonicalize = (map) => const canonicalize = (map) =>