From 47eecef7b2e8021d26e6d195d8cca258dc4c32d9 Mon Sep 17 00:00:00 2001 From: William Chargin Date: Thu, 5 Jul 2018 18:58:47 -0700 Subject: [PATCH] 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 --- src/core/attribution/graphToMarkovChain.js | 21 ++++++++---------- .../attribution/graphToMarkovChain.test.js | 22 +++++++++---------- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/src/core/attribution/graphToMarkovChain.js b/src/core/attribution/graphToMarkovChain.js index 8a7be60..46b283c 100644 --- a/src/core/attribution/graphToMarkovChain.js +++ b/src/core/attribution/graphToMarkovChain.js @@ -1,18 +1,13 @@ // @flow -import { - type Edge, - type Graph, - type Neighbor, - type NodeAddressT, - NodeAddress, -} from "../graph"; +import {type Edge, type Graph, type NodeAddressT, NodeAddress} from "../graph"; import type {Distribution, SparseMarkovChain} from "./markovChain"; export type Probability = number; export type Contributor = | {|+type: "SYNTHETIC_LOOP"|} - | {|+type: "NEIGHBOR", +neighbor: Neighbor|}; + | {|+type: "IN_EDGE", +edge: Edge|} + | {|+type: "OUT_EDGE", +edge: Edge|}; export type Contribution = {| +contributor: Contributor, // This `weight` is a conditional probability: given that you're at @@ -28,8 +23,10 @@ export function contributorSource( switch (contributor.type) { case "SYNTHETIC_LOOP": return target; - case "NEIGHBOR": - return contributor.neighbor.node; + case "IN_EDGE": + return contributor.edge.src; + case "OUT_EDGE": + return contributor.edge.dst; default: throw new Error((contributor.type: empty)); } @@ -104,11 +101,11 @@ export function createContributions( const {toWeight, froWeight} = edgeWeight(edge); const {src, dst} = edge; processContribution(dst, { - contributor: {type: "NEIGHBOR", neighbor: {node: src, edge}}, + contributor: {type: "IN_EDGE", edge}, weight: toWeight, }); processContribution(src, { - contributor: {type: "NEIGHBOR", neighbor: {node: dst, edge}}, + contributor: {type: "OUT_EDGE", edge}, weight: froWeight, }); } diff --git a/src/core/attribution/graphToMarkovChain.test.js b/src/core/attribution/graphToMarkovChain.test.js index 91498fa..a8c6f0f 100644 --- a/src/core/attribution/graphToMarkovChain.test.js +++ b/src/core/attribution/graphToMarkovChain.test.js @@ -109,44 +109,42 @@ describe("core/attribution/graphToMarkovChain", () => { .set(n1, [ {contributor: {type: "SYNTHETIC_LOOP"}, weight: 1 / 13}, { - contributor: {type: "NEIGHBOR", neighbor: {node: n2, edge: e1}}, + contributor: {type: "OUT_EDGE", edge: e1}, weight: 3 / 10, }, { - contributor: {type: "NEIGHBOR", neighbor: {node: n3, edge: e3}}, + contributor: {type: "OUT_EDGE", edge: e3}, weight: 3 / 16, }, ]) .set(n2, [ {contributor: {type: "SYNTHETIC_LOOP"}, weight: 1 / 10}, { - contributor: {type: "NEIGHBOR", neighbor: {node: n1, edge: e1}}, + contributor: {type: "IN_EDGE", edge: e1}, weight: 6 / 13, }, { - contributor: {type: "NEIGHBOR", neighbor: {node: n3, edge: e2}}, + contributor: {type: "OUT_EDGE", edge: e2}, weight: 3 / 16, }, ]) .set(n3, [ {contributor: {type: "SYNTHETIC_LOOP"}, weight: 1 / 16}, { - contributor: {type: "NEIGHBOR", neighbor: {node: n2, edge: e2}}, + contributor: {type: "IN_EDGE", edge: e2}, weight: 6 / 10, }, { - contributor: {type: "NEIGHBOR", neighbor: {node: n1, edge: e3}}, + contributor: {type: "IN_EDGE", edge: e3}, weight: 6 / 13, }, { - contributor: {type: "NEIGHBOR", neighbor: {node: n3, edge: e4}}, - // this loop, as an out-edge - weight: 3 / 16, + contributor: {type: "IN_EDGE", edge: e4}, + weight: 6 / 16, }, { - contributor: {type: "NEIGHBOR", neighbor: {node: n3, edge: e4}}, - // this loop, as an in-edge - weight: 6 / 16, + contributor: {type: "OUT_EDGE", edge: e4}, + weight: 3 / 16, }, ]); const canonicalize = (map) =>