Skip to content
This repository was archived by the owner on Jun 18, 2026. It is now read-only.

Commit 0fb81ee

Browse files
refactor: incremental active-neighbor tracking in LT diffusion simulation
Replace O(rounds × V × avg_degree) full-scan LT activation loop with incremental active-neighbor counter + candidate set approach: - Pre-compute neighbor sizes once (immutable per graph) - Maintain activeNbCount map, updated incrementally when nodes activate - Track candidate set of only inactive nodes with ≥1 active neighbor - Iterate candidates (not all vertices) per round; update on activation Reduces per-trial cost from O(rounds × V) to O(rounds × |candidates|) where |candidates| << V in typical sparse networks. Especially impactful for large graphs with low spread probability where most nodes never enter the candidate pool.
1 parent 1fd2a7a commit 0fb81ee

1 file changed

Lines changed: 47 additions & 10 deletions

File tree

Gvisual/src/gvisual/GraphInformationDiffusionEngine.java

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -270,31 +270,68 @@ private LTResult runLT(List<String> vertices, Map<String, Set<String>> adj, Set<
270270
for (String v : vertices) activationCount.put(v, new int[]{0});
271271
double totalSize = 0;
272272

273+
// Pre-compute neighbor sizes (immutable per trial)
274+
Map<String, Integer> neighborSize = new LinkedHashMap<>(n * 2);
275+
for (String v : vertices) {
276+
neighborSize.put(v, adj.getOrDefault(v, Collections.emptySet()).size());
277+
}
278+
273279
for (int trial = 0; trial < monteCarloTrials; trial++) {
274280
// Random thresholds
275-
Map<String, Double> thresholds = new LinkedHashMap<>();
281+
Map<String, Double> thresholds = new LinkedHashMap<>(n * 2);
276282
for (String v : vertices) {
277283
thresholds.put(v, rng.nextDouble());
278284
}
279285

280286
Set<String> active = new LinkedHashSet<>(seeds);
287+
288+
// Incremental active-neighbor counts: only recompute on activation
289+
Map<String, Integer> activeNbCount = new LinkedHashMap<>(n * 2);
290+
for (String v : vertices) activeNbCount.put(v, 0);
291+
292+
// Initialize counts from seed nodes
293+
for (String s : seeds) {
294+
for (String nb : adj.getOrDefault(s, Collections.emptySet())) {
295+
if (!active.contains(nb)) {
296+
activeNbCount.put(nb, activeNbCount.get(nb) + 1);
297+
}
298+
}
299+
}
300+
301+
// Candidate set: inactive nodes with at least one active neighbor
302+
Set<String> candidates = new LinkedHashSet<>();
303+
for (String v : vertices) {
304+
if (!active.contains(v) && activeNbCount.get(v) > 0) {
305+
candidates.add(v);
306+
}
307+
}
308+
281309
boolean changed = true;
282310
while (changed) {
283311
changed = false;
284-
for (String v : vertices) {
285-
if (active.contains(v)) continue;
286-
Set<String> neighbors = adj.getOrDefault(v, Collections.emptySet());
287-
if (neighbors.isEmpty()) continue;
288-
int activeNeighbors = 0;
289-
for (String nb : neighbors) {
290-
if (active.contains(nb)) activeNeighbors++;
291-
}
292-
double fraction = (double) activeNeighbors / neighbors.size();
312+
List<String> newlyActivated = new ArrayList<>();
313+
Iterator<String> it = candidates.iterator();
314+
while (it.hasNext()) {
315+
String v = it.next();
316+
int nSize = neighborSize.get(v);
317+
if (nSize == 0) { it.remove(); continue; }
318+
double fraction = (double) activeNbCount.get(v) / nSize;
293319
if (fraction >= thresholds.get(v)) {
294320
active.add(v);
321+
it.remove();
322+
newlyActivated.add(v);
295323
changed = true;
296324
}
297325
}
326+
// Update counts and candidates for newly activated nodes
327+
for (String v : newlyActivated) {
328+
for (String nb : adj.getOrDefault(v, Collections.emptySet())) {
329+
if (!active.contains(nb)) {
330+
activeNbCount.put(nb, activeNbCount.get(nb) + 1);
331+
candidates.add(nb);
332+
}
333+
}
334+
}
298335
}
299336
totalSize += active.size();
300337
for (String v : active) {

0 commit comments

Comments
 (0)