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:
parent
761a44c561
commit
47eecef7b2
|
@ -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,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) =>
|
||||||
|
|
Loading…
Reference in New Issue