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

Commit 6797ec6

Browse files
perf: track infected nodes in SIR simulation instead of scanning all vertices
The SIR simulateSIR() method previously iterated all V vertices twice per round: once to find infected nodes for spreading, and once for recovery checks, plus a third pass to check if any infected remain. Replace with an explicit currentlyInfected set that is maintained incrementally. This reduces per-round cost from O(V) to O(|infected|) for the recovery check and termination test, which is significant for large sparse graphs where only a small fraction of nodes are infected at any given time. The infection spreading loop also benefits since it now only iterates currently infected nodes. This is especially impactful during Monte Carlo simulations where simulateSIR is called thousands of times.
1 parent e7a2803 commit 6797ec6

1 file changed

Lines changed: 16 additions & 14 deletions

File tree

Gvisual/src/gvisual/InfluenceSpreadSimulator.java

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -315,20 +315,27 @@ public SimulationResult simulateSIR(Collection<String> seeds,
315315
List<RoundSnapshot> snapshots = new ArrayList<>();
316316
List<InfectionEvent> timeline = new ArrayList<>();
317317

318+
// Track currently infected nodes explicitly to avoid iterating
319+
// all V vertices each round just to find the infected subset.
320+
// For large graphs with low infection rates, this reduces per-round
321+
// cost from O(V) to O(|infected| * avg_degree).
322+
Set<String> currentlyInfected = new LinkedHashSet<>();
323+
for (String seed : seeds) {
324+
if (graph.containsVertex(seed)) currentlyInfected.add(seed);
325+
}
326+
318327
int round = 0;
319328
snapshots.add(createSnapshot(round, state));
320329

321-
boolean hasInfected = true;
322-
while (hasInfected) {
330+
while (!currentlyInfected.isEmpty()) {
323331
round++;
324332
if (maxRounds > 0 && round > maxRounds) break;
325333

326334
Set<String> toRecover = new LinkedHashSet<>();
327335
Set<String> toInfect = new LinkedHashSet<>();
328336
Map<String, String> infectedBy = new LinkedHashMap<>();
329337

330-
for (String node : graph.getVertices()) {
331-
if (state.get(node) != NodeState.INFECTED) continue;
338+
for (String node : currentlyInfected) {
332339
for (String neighbor : getNeighbors(node)) {
333340
if (state.get(neighbor) == NodeState.SUSCEPTIBLE &&
334341
!toInfect.contains(neighbor)) {
@@ -341,31 +348,26 @@ public SimulationResult simulateSIR(Collection<String> seeds,
341348
}
342349
}
343350

344-
for (String node : graph.getVertices()) {
345-
if (state.get(node) == NodeState.INFECTED) {
346-
if (random.nextDouble() < recoveryRate) {
347-
toRecover.add(node);
348-
}
351+
for (String node : currentlyInfected) {
352+
if (random.nextDouble() < recoveryRate) {
353+
toRecover.add(node);
349354
}
350355
}
351356

352357
for (String node : toInfect) {
353358
state.put(node, NodeState.INFECTED);
359+
currentlyInfected.add(node);
354360
String source = infectedBy.get(node);
355361
if (source != null) {
356362
timeline.add(new InfectionEvent(source, node, round));
357363
}
358364
}
359365
for (String node : toRecover) {
360366
state.put(node, NodeState.RECOVERED);
367+
currentlyInfected.remove(node);
361368
}
362369

363370
snapshots.add(createSnapshot(round, state));
364-
365-
hasInfected = false;
366-
for (NodeState s : state.values()) {
367-
if (s == NodeState.INFECTED) { hasInfected = true; break; }
368-
}
369371
}
370372

371373
return new SimulationResult(Model.SIR, seeds,

0 commit comments

Comments
 (0)