Skip to content

Instantly share code, notes, and snippets.

@philipphager
Last active May 15, 2019 06:16
Show Gist options
  • Save philipphager/892fc84c7e8eb2fc5b524c14410fb81e to your computer and use it in GitHub Desktop.
Save philipphager/892fc84c7e8eb2fc5b524c14410fb81e to your computer and use it in GitHub Desktop.
Basic Twitter SALSA implementation
import java.util.*;
public class BipartiteGraph {
private final Map<String, List<String>> leftToRight;
private final Map<String, List<String>> rightToLeft;
public BipartiteGraph() {
leftToRight = new HashMap<>();
rightToLeft = new HashMap<>();
}
public void addEdge(String start, String end) {
List<String> leftOutEdges = leftToRight.getOrDefault(start, new ArrayList<>(5));
leftOutEdges.add(end);
leftToRight.put(start, leftOutEdges);
List<String> rightOutEdges = rightToLeft.getOrDefault(end, new ArrayList<>(5));
rightOutEdges.add(start);
rightToLeft.put(end, rightOutEdges);
}
public List<String> getLeftOutEdges(String start) {
return leftToRight.getOrDefault(start, new ArrayList<>());
}
public List<String> getRightOutEdges(String end) {
return rightToLeft.getOrDefault(end, new ArrayList<>());
}
}
import java.util.Random;
public class Main {
public static void main(String[] args) {
BipartiteGraph graph = new BipartiteGraph();
graph.addEdge("UserA", "Tweet1");
graph.addEdge("UserA", "Tweet2");
graph.addEdge("UserB", "Tweet1");
graph.addEdge("UserB", "Tweet2");
graph.addEdge("UserB", "Tweet3");
graph.addEdge("UserB", "Tweet4");
graph.addEdge("UserC", "Tweet3");
graph.addEdge("UserC", "Tweet4");
graph.addEdge("UserC", "Tweet5");
graph.addEdge("UserC", "Tweet6");
graph.addEdge("UserC", "Tweet7");
graph.addEdge("UserD", "Tweet3");
graph.addEdge("UserD", "Tweet4");
Salsa salsa = new Salsa(graph, new Random());
salsa.compute("UserA", 100, 50, 0.15);
}
}
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
public class Salsa {
private Map<String, Integer> currentLeftNodeVisits;
private Map<String, Integer> currentRightNodeVisits;
private Map<String, Integer> totalRightNodeVisits;
private BipartiteGraph graph;
private Random random;
private int totalVisits = 0;
public Salsa(BipartiteGraph graph, Random random) {
this.graph = graph;
this.random = random;
this.currentLeftNodeVisits = new HashMap<>();
this.currentRightNodeVisits = new HashMap<>();
this.totalRightNodeVisits = new HashMap<>();
}
public Map<String, Integer> compute(String rootNode, int walks, int length, double resetProbability) {
boolean isLeftToRight = true;
// Initialize seed set on left side
currentLeftNodeVisits.put(rootNode, walks);
// Perform forward and backward iterations between users and tweets
for (int i = 0; i < length; i++) {
if (isLeftToRight) {
leftIteration(rootNode, resetProbability);
} else {
rightIteration();
}
isLeftToRight = !isLeftToRight;
}
// Print out results (unordered)
for (Map.Entry<String, Integer> rightNodeVisit : totalRightNodeVisits.entrySet()) {
float visitPercentage = (float) rightNodeVisit.getValue() / totalVisits;
System.out.printf("Visited %s %d times, %s%%%n", rightNodeVisit.getKey(), rightNodeVisit.getValue(), visitPercentage);
}
// Todo filter out tweets that are already known to root user
return totalRightNodeVisits;
}
private void leftIteration(String rootNode, double resetProbability) {
int totalResets = 0;
// For each left node
for (String node : currentLeftNodeVisits.keySet()) {
// Get previous visits
Integer visits = currentLeftNodeVisits.get(node);
int walks = 0;
int resets = 0;
// Calculate how many new walks should be performed and how many resets will happen
for (int i = 0; i < visits; i++) {
if (random.nextDouble() > resetProbability) {
walks++;
} else {
resets++;
}
}
// Sample out edges and pick random walks to the right side
List<String> edges = graph.getLeftOutEdges(node);
// Perform walks to the right side
for (int i = 0; i < walks; i++) {
// Ignore nodes without out links
if (edges.size() > 0) {
int randomPosition = random.nextInt(edges.size());
String edge = edges.get(randomPosition);
currentRightNodeVisits.put(edge, currentRightNodeVisits.getOrDefault(edge, 0) + 1);
totalRightNodeVisits.put(edge, totalRightNodeVisits.getOrDefault(edge, 0) + 1);
}
}
totalVisits += walks;
// Add resets to currentLeftNodeVisits
totalResets += resets;
}
currentLeftNodeVisits.clear();
currentLeftNodeVisits.put(rootNode, totalResets);
}
private void rightIteration() {
for (String node : currentRightNodeVisits.keySet()) {
Integer visits = currentRightNodeVisits.get(node);
// Sample left edges for all walks
List<String> edges = graph.getRightOutEdges(node);
// Perform walks back to the left side
for (int i = 0; i < visits; i++) {
// Ignore nodes without out links
if (edges.size() > 0) {
int randomPosition = random.nextInt(edges.size());
String edge = edges.get(randomPosition);
currentLeftNodeVisits.put(edge, currentLeftNodeVisits.getOrDefault(edge, 0) + 1);
}
}
}
currentRightNodeVisits.clear();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment