Skip to content

Latest commit

 

History

27 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

rGrAnt — a byte-exact Rust port of the GrAnt / rGrAnt DTN routing experiments

This repository is a self-contained Rust reimplementation of the simulation stack used in the master's dissertation described below. It fuses the parts of The ONE simulator that the experiments touch with the GrAnt and rGrAnt routing protocols, and reproduces the dissertation's reported results byte-for-byte against report files dumped from the original Java code.

Everything needed to re-run the experiments — maps, scenario configuration files, and the Java golden reports used as ground truth — is committed here. No Java, no The ONE checkout, and no network access are required.


1. The dissertation

Uma Nova Versão do Protocolo GrAnt para Redes Tolerantes a Atrasos (A New Version of the GrAnt Protocol for Delay Tolerant Networks)

Luís Guilherme Bergamini Mendes

Advisor: Profª. Drª. Ana Cristina Barreiras Kochem Vendramin

Universidade Tecnológica Federal do Paraná (UTFPR) — PPGCA

Concentration area: Redes e Sistemas Distribuídos — Curitiba, 2018

Delay/Disruption Tolerant Networks (DTNs) have no guarantee of an end-to-end path. Nodes meet sporadically, and messages travel by store-carry-forward: a node holds a message until it encounters a peer that looks like a better custodian. The whole problem is deciding, at each contact, whether handing the message over improves its chance of delivery.

The dissertation's thesis is that a single forwarding policy cannot suit every connectivity regime. A node that meets one peer per hour and a node that meets eighty need opposite strategies:

  • Sparsely connected nodes must exploit the few contacts they get, so forwarding should be permissive — otherwise a strict rule rejects the only peer available and the message dies in the buffer.
  • Densely connected nodes must avoid flooding the same popular peers and links, so forwarding should be restrictive — otherwise a handful of nodes absorb all the traffic, buffers overflow and messages are dropped.

The proposal, rGrAnt (Rule-based Greedy Ant), gives GrAnt the ability to measure its own connectivity online and switch forwarding policy accordingly.


2. What is GrAnt?

GrAnt (Greedy Ant) is an existing DTN routing protocol that applies the Ant Colony Optimization (ACO) metaheuristic. Like ants laying trails, nodes accumulate pheromone on paths that have proven to deliver messages, while a heuristic function provides a greedy estimate of how promising a candidate next hop looks right now.

Two quantities drive every decision:

Quantity Meaning
Heuristic Heu Immediate desirability of a neighbour, from social metrics: betweenness centrality, social proximity to the destination, free buffer percentage, and path quality.
Pheromone Fer Learned, reinforced by backward ants that retrace the route after a successful delivery, and decayed by evaporation.

The combined score FerHeu ranks candidate next hops. GrAnt also tracks U_Best — the best score any node has achieved for that message so far — and only forwards to a neighbour that beats it. U_Best is what makes GrAnt greedy: it keeps replication low, but it is also a hard limiter, because a node holding the message may never meet anyone better than the current best.

The dissertation's variations all attack the same question: when should that limiter be relaxed, and when should it be tightened?


3. The variations ("Vars")

Each Var is a modified GrAnt forwarding rule. They fall into two families.

3.1 Low-connectivity family — make forwarding less restrictive

Built by removing U_Best, replacing the AND in the forwarding test with OR, relaxing strict > comparisons to ≥, and adding a fallback that lets a message be forwarded at least once.

Var What it changes relative to GrAnt
Var1 Removes U_Best; AND→OR; fallback (FerHeu_J ≥ FerHeu_I) and message never forwarded.
Var2 Forwards immediately if pheromone exists; otherwise keeps U_Best logic.
Var2a GrAnt's test, but accepts ties against U_Best (≥ instead of >).
Var2b Var2 plus tie-acceptance on both U_Best and the fallback.
Var2c AND→OR; fallback compares the heuristic rather than FerHeu.
Var2d AND→OR, ≥ against U_Best, heuristic fallback.
Var3 Removes U_Best; AND→OR; fallback (Heu_J ≥ Heu_I) and message never forwarded.
Var4 Removes U_Best; OR with ≥; fallback is simply message never forwarded.
Var4a Var4, but a repeat forward additionally requires FerHeu_J ≥ FerHeu_I.
Var4b Removes U_Best; OR with ≥; no extra fallback.
Var4c Var4b using FerHeu instead of Heu.
Var4d Var4b with strict >.
Var4e Var4d using FerHeu instead of Heu.

3.2 High-connectivity family — make forwarding more restrictive

These drop betweenness centrality, and use only 1/nHops for path quality (GrAnt also factors in popularity). The trailing letter selects the heuristic:

  • a → free-buffer percentage only
  • b → social proximity only
  • c → free-buffer percentage and social proximity
Var What it changes relative to GrAnt
Var5a/5b/5c An initial test (pheromone exists) AND (Heu_J > Heu_I) seeds U_Best; thereafter FerHeu_J > U_Best; strict > throughout.
Var6a/6b/6c Stricter than Var5: the seeding test is removed, so U_Best gates from the start.
Var7a/7b/7c Strictest: pheromone is removed entirely, decisions use the heuristic alone (Heu_J > U_Best). Motivated by pheromone concentrating traffic onto a few overused links when connectivity is high.

4. What is rGrAnt?

rGrAnt is not a new algorithm — it is GrAnt plus a rule layer that selects, per node and per moment, which of the Vars to behave as.

Each selected Var is renamed to a behaviour. The dissertation states it directly:

"O comportamento chamado Esparso1 representa a utilização do Var3, o comportamento Esparso2 representa a utilização do Var1, o comportamento Conectado1 representa a utilização do Var5b, o comportamento Conectado2 representa a utilização do Var6c e, por fim, o comportamento Conectado3 representa a utilização do Var7b."

Behaviour Var Family
Esparso1 Var3 low connectivity, permissive
Esparso2 Var1 low connectivity, permissive
(none) GrAnt baseline, middle ground
Conectado1 Var5b high connectivity, restrictive
Conectado2 Var6c high connectivity, more restrictive
Conectado3 Var7b high connectivity, most restrictive

So rGrAnt = { Var3, Var1, GrAnt, Var5b, Var6c, Var7b } arbitrated at runtime. The other Vars were evaluated and discarded during tuning.

4.1 The decision inputs

Input Definition
NC — Número de Contatos Number of contacts a node had in a sliding 5000-second window, averaged with the previous window to smooth abrupt swings.
TB — Tamanho do Buffer The node's buffer size in MB.

Linguistic terms, with the intervals fixed by the tuning experiments:

  • TB = { baixo = [0, 4] MB, alto = [5, ∞) MB }
  • NC = { muito baixo = [1, 3], baixo = [4, 5], médio = [6, 16], moderadamente alto = [17, 55], alto = [56, 65], muito alto = [66, ∞) }

4.2 The rule set

IF TB is alto AND NC is muito baixo                        -> Esparso1   (Var3)
IF TB is alto AND NC is baixo                              -> Esparso2   (Var1)
IF TB is alto AND NC is médio                              -> GrAnt
IF TB is baixo AND NC is (muito baixo | baixo | médio)     -> GrAnt
IF NC is moderadamente alto                                -> Conectado1 (Var5b)
IF NC is alto                                              -> Conectado2 (Var6c)
IF NC is muito alto                                        -> Conectado3 (Var7b)

Note the asymmetry: buffer size only gates the sparse behaviours. A node with a small buffer (≤ 4 MB) falls back to plain GrAnt at low connectivity, because permissive forwarding into an already-scarce buffer just causes drops. Once connectivity is high, the restrictive behaviours apply regardless of buffer size.


5. How the Vars were chosen

The selection was driven by a single metric — message delivery ratio (taxa de entrega) — using the WD scenario to tune and the PoI scenario to validate. A Var was adopted for an NC band only if it beat GrAnt in both. All figures are means of 30 runs at a 95% confidence interval.

5.1 Low connectivity — sweeping the NC ceiling

All 13 low-connectivity Vars were run on WD with the Var applied for NC below a threshold and GrAnt elsewhere. Delivery ratio, GrAnt baseline 0.5699:

Band Best Var Best value Runner-up
NC = 1 Var1 / Var3 (tie) 0.5971 Var4d 0.5946
NC ≤ 2 Var2d 0.6064 Var3 0.6039, Var1 0.6001
NC ≤ 3 Var3 0.6089 Var1 0.6004
NC ≤ 4 Var1 0.6101 Var2d 0.6045

Var3 owned the very sparse band and Var1 took over at NC = 4 — which is exactly where the muito baixo / baixo boundary was drawn.

PoI validation confirmed the choice, and also revealed the buffer dependency:

Test GrAnt Var
Var3, PoI 8 MB, NC ≤ 3 0.8052 0.8176 ✔
Var1, PoI 8 MB, NC = 4 0.8052 0.8181 ✔
Var3, PoI 4 MB, NC ≤ 3 0.6259 0.6161 ✘
Var1, PoI 4 MB, NC = 4 0.6259 0.6071 ✘

The sparse behaviours lose to GrAnt at a 4 MB buffer but win at 8 MB. This is why TB entered the rule set and why TB baixo = [0, 4] MB falls back to GrAnt.

Extending Var1 upward settled the ceiling at NC ≤ 5:

Band WD (GrAnt 0.5699) PoI (GrAnt 0.8052)
NC ≤ 5 0.6082 0.8211
NC ≤ 6 0.6078 0.8163

5.2 High connectivity — sweeping the NC floor downward

The opposite procedure: start at a very high NC floor and lower it until the gain disappears.

Step 1 — NC ≥ 70, all nine high-connectivity Vars (GrAnt 0.5699):

Var5a Var5b Var5c Var6a Var6b Var6c Var7a Var7b Var7c
0.5621 0.5671 0.5694 0.5621 0.5671 0.5694 0.5702 0.5715 0.5702

Step 2 — lower Var7b's floor until it stops beating GrAnt:

NC ≥ 70 NC ≥ 68 NC ≥ 66 NC ≥ 65
0.5715 0.5763 0.5721 0.5679 ✘

Var7b holds down to NC ≥ 66 → muito alto = [66, ∞).

Step 3 — re-run all Vars at NC ≥ 65. Var6c now wins (0.5760), and lowering its floor keeps it ahead down to the mid-50s, fixing alto = [56, 65].

Step 4 — drop to NC ≥ 15. Var5b wins decisively (0.5880 vs GrAnt 0.5699); several other Vars collapse here (Var5a 0.4038, Var7a 0.4060) — evidence that the strictest rules are actively harmful at moderate connectivity.

Step 5 — PoI validation of Var5b exposed the lower bound:

NC ≥ 17 NC ≥ 15 NC ≥ 14 NC ≥ 10 NC ≥ 6
PoI (GrAnt 0.8052) 0.8062 ✔ 0.8047 ✘ 0.7995 ✘ 0.7945 ✘ 0.7813 ✘

Var5b only beats GrAnt on PoI from NC ≥ 17 upward → moderadamente alto = [17, 55], and the gap [6, 16] stays with plain GrAnt.


6. Final results: GrAnt vs rGrAnt

Means of 30 runs, 95 % confidence interval. Tx. Entrega = delivery ratio; Rel. Redundância = redundancy (replication) ratio. Decimal separators normalised to ..

6.1 WD scenario — varying buffer (339 nodes)

Buffer GrAnt delivery rGrAnt delivery GrAnt redund. rGrAnt redund.
4 MB 45.7537 ± 0.4058 % 47.1343 ± 0.3825 % 14.4765 16.4345
6 MB 53.7443 ± 0.4357 % 58.3313 ± 0.4715 % 13.9483 23.1636
8 MB 57.8180 ± 0.4532 % 63.2810 ± 0.4784 % 13.7716 22.6404
10 MB 59.9593 ± 0.4309 % 66.0020 ± 0.4759 % 13.6233 22.3564
12 MB 61.1870 ± 0.4552 % 67.6203 ± 0.5142 % 13.4517 21.9697
14 MB 61.7153 ± 0.5033 % 68.6107 ± 0.4954 % 13.3705 21.9697
16 MB 61.9923 ± 0.4932 % 69.0790 ± 0.5072 % 13.2846 21.8503

rGrAnt wins at every buffer size, by up to +7.1 percentage points, at the cost of higher redundancy. The 4 MB row shows the smallest gain and the smallest redundancy penalty — the rule set deliberately falls back to GrAnt for small buffers.

6.2 WD scenario — varying node count (10 MB buffer)

Nodes GrAnt delivery rGrAnt delivery
75 11.0457 ± 0.2470 % 10.9743 ± 0.2653 %
142 16.8773 ± 0.5115 % 18.0230 ± 0.5654 %
339 59.9573 ± 0.4757 % 66.0020 ± 0.4759 %
594 69.4153 ± 0.3356 % 74.4663 ± 0.3105 %
1104 71.0547 ± 0.3847 % 76.7023 ± 0.3371 %

6.3 PoI scenario — varying buffer (120 nodes)

Buffer GrAnt delivery rGrAnt delivery
4 MB 61.8962 ± 0.2170 % 61.5147 ± 0.2266 %
6 MB 74.4397 ± 0.2217 % 74.9370 ± 0.2462 %
8 MB 80.3120 ± 0.1940 % 81.8483 ± 0.1774 %
10 MB 82.5990 ± 0.1680 % 84.5750 ± 0.1654 %
12 MB 83.3267 ± 0.1674 % 85.5360 ± 0.1746 %
14 MB 83.4637 ± 0.1738 % 85.8443 ± 0.1680 %
16 MB 83.4690 ± 0.1867 % 85.8963 ± 0.1592 %

"Com tamanhos de buffer pequenos, percebe-se que o rGrAnt não possui desempenho superior ao GrAnt … Porém, com o aumento do tamanho do buffer, os ganhos passam a ser significativos."

6.4 PoI scenario — varying node count (8 MB buffer)

Nodes GrAnt delivery rGrAnt delivery
12 5.6483 ± 0.1996 % 5.8077 ± 0.1999 %
36 29.1790 ± 0.3286 % 28.1333 ± 0.3549 %
120 80.3120 ± 0.1940 % 81.8483 ± 0.1774 %
240 90.6420 ± 0.1289 % 90.2147 ± 0.1110 %
360 94.0350 ± 0.0953 % 92.7960 ± 0.0836 %

PoI is the scenario where rGrAnt is not uniformly better: at high node counts the network is already dense enough that plain GrAnt suffices.

6.5 Curitiba public-transport scenario

Protocol Delivery ratio Messages delivered Redundancy ratio
GrAnt 22.7390 ± 0.1509 % 3490.0333 ± 23.2107 18.6741 ± 0.1448
rGrAnt 23.4043 ± 0.1746 % 3592.1000 ± 26.8584 8.2283 ± 0.0608

The strongest result in the dissertation: rGrAnt delivers more messages while more than halving redundancy (−55.9 %). Both gains are statistically significant.

6.6 Overall conclusion

"O desempenho do rGrAnt foi comparado com o GrAnt em três cenários diferentes de simulação. Resultados mostram que, nos três cenários, o rGrAnt obtém uma maior taxa de entrega de mensagens do que o GrAnt."

A PoI variant with node stop times scaled ×100 ("tempo de parada") is also reported; rGrAnt gains there for buffers above 4 MB, but the differences are not statistically significant.


7. FuzzyAnt — fuzzy extensions of rGrAnt (future work)

The dissertation's conclusion suggests that rGrAnt's crisp, rule-based forwarding logic "could be adapted to use a fuzzy-based system in order to better explore the connectivity intervals of the nodes," and that other ACO parameters "could be adapted online." FuzzyAnt is that line of work: four fuzzy variants that keep rGrAnt's entire ant-colony substrate byte-for-byte and replace only the forwarding-decision layer, so every measured difference is the fuzzy contribution alone.

Variant Idea Curitiba character
FuzzyAntVar1 A Mamdani controller scores each candidate into a forwarding suitability s, turning rGrAnt's crisp nC bands into a smooth ramp. efficiency corner
FuzzyAntVar2 A per-node restrictiveness meta-controller R = f(nC, buffer, sociality) continuously tunes how choosy a node is. delivery corner
FuzzyAntVar3 Unifies Var1 + Var2 and adds the online adaptation the dissertation asked for (a pivot that tracks each node's own history) plus a latency-protecting urgency term. balanced, Pareto-dominant
FuzzyAntVar4 Adds online gain self-tuning and urgency on the ACO substrate — a rigorous negative result (neither helps on Curitiba; Var4 ≈ Var3). confirms Var3

7.1 Curitiba — every variant beats rGrAnt on delivery

On the Curitiba public-transport scenario (rGrAnt's best, §6.5), across the same 30 seeds, all four variants raise delivery over rGrAnt:

Router Delivery ratio Δ vs rGrAnt Redundancy Median latency (s)
rGrAnt 0.2340 — 8.228 2804
FuzzyAntVar1 0.2428 +3.8 % 6.928 2271
FuzzyAntVar2 0.2513 +7.4 % 8.532 3022
FuzzyAntVar3 0.2455 +4.9 % 6.433 2268
FuzzyAntVar4 0.2458 +5.0 % 6.464 2270

7.2 The final FuzzyAnt: FuzzyAntVar2

The crowned final FuzzyAnt is FuzzyAntVar2 (router id FuzzyAnt2). It wins the primary DTN metric — the highest delivery ratio of any router on Curitiba (+7.4 % vs rGrAnt, on every one of the 30 seeds) — at overhead and delay within a few percent of rGrAnt's. It is also the only variant that generalises: on the sparse WD/PoI scenarios the restrictive Var1/Var3/Var4 collapse (−64 % delivery on WD), while FuzzyAntVar2 holds near-parity delivery (−9 % WD, −6 % PoI) at half the relays and lower latency. No fuzzy variant beats rGrAnt on delivery off Curitiba — rGrAnt remains the generalist — but FuzzyAntVar2 is the robust fuzzy alternative.

If the objective is instead efficiency-first on Curitiba — a clean Pareto win, better on every axis — then FuzzyAntVar3 / FuzzyAntVar4 dominate rGrAnt (more delivery and −22 % overhead, −23 % delay, −19 % latency) at slightly lower delivery than Var2.

Full design, calibration, per-seed significance, and the cross-scenario tables are in docs/fuzzyant.md. The variants keep their code/router identifiers FuzzyAnt / FuzzyAnt2 / FuzzyAnt3 / FuzzyAnt4 (= Var1–Var4) in the source tree and parameter files.


8. About this port

The original experiments ran on The ONE simulator (Java) with GrAnt/rGrAnt routing. This repository reimplements that stack in Rust with one hard requirement: bit-exact reproduction. Not statistically similar — identical, down to every digit of every line of the MessageStatsReport.

Meeting that bar meant porting the exact behaviour of the JDK, not idiomatic Rust equivalents:

Concern Approach
java.util.Random Reimplemented LCG, including nextGaussian's cached second value.
HashMap iteration order Ported the JDK's bucket layout, resize behaviour and red-black-tree treeification, because message-buffer iteration order changes drop decisions.
Collections.sort Ported the legacy merge sort, tie-breaking included.
Floating point Simulation clock kept as a float accumulator (clock += updateInterval) exactly as The ONE does, rather than an integer tick counter.
Hashtable Ported separately where The ONE relies on it.

Verification status

Every entry compares the Rust port's MessageStatsReport byte-for-byte (after newline normalisation) against a golden produced by the original Java stack.

Scope Result
Headline results — WD, PoI, Curitiba × GrAnt, rGrAnt × 30 seeds 180 / 180 byte-identical
Full multi-seed sweep — 2,775 runs over 195 configurations (every router variant; buffer, host-count and wait-time sweeps; all 30 seeds) 2,774 / 2,775 byte-identical
Unit tests (cargo test --release) 50 passing

The sweep was executed both locally and on a Linux Azure VM fleet; the Rust port produces identical output on Windows and Linux.

The single exception. Vars_POI12M_WaitTime_120hosts_30seeds, seed 7345, differs by 0.0001 in exactly one derived statistic — hopcount_avg: FR (1,2312 against the golden's 1,2313). Every physical counter (created, delivered, relayed, dropped, aborted, removed, ant relays) is identical, and the port yields the same value on Windows and Linux. The report-aggregation code and the String.format("%.4f") HALF_UP rounding are both verified byte-identical to Java (see src/sim/report.rs), and summing the integer hop counts as f64 is exact and order-independent. The FR hop-count list is built from an overwrite map keyed by message id (last write wins), so the difference is a rare same-tick event-ordering snapshot for a single message. It changes no physical count and no aggregate the dissertation reports.

The empty-golden finding. 40 of the archived goldens were 0-byte. GrAnt sets java.util.Arrays.useLegacyMergeSort=true in every router's initVariaveis() and then sorts with an intentionally non-transitive comparator (OrdenaTupla/OrdenaMensagens). Java 6–8 honour the flag — legacy merge sort, no contract check — while Java 9+ ignore it and always use TimSort, which aborts with "Comparison method violates its general contract!". The goldens had therefore never regenerated on a modern JDK. Re-running the original ONE simulator under Java 8 (jdk1.8.0_202) reproduces them faithfully; all 40 were regenerated and validated byte-identical to the Rust port, which ports the legacy merge sort exactly (src/java/sort.rs). Regeneration is scripted in tools/regen_empty_goldens.py.


9. Runbook

9.1 Requirements

  • Rust (stable, 2021 edition or newer) — cargo build is the only build step.
  • Windows PowerShell for the parity harness scripts.
  • ~12 GB RAM for the Curitiba scenario at high parallelism.

9.2 Build and test

cargo build --release
cargo test  --release      # 50 tests

9.3 Reproduce the headline results

# One seed per configuration - 6 runs, ~35 min
.\tools\run_headline.ps1

# Full dissertation depth - 30 seeds x 6 configurations = 180 runs, ~8 h
.\tools\run_headline.ps1 -SeedMode all -Parallel 10

Each run is compared against goldens/<config>/ and the script prints ALL BYTE-IDENTICAL on success.

9.4 Reproduce any other table

tools\run_parity2.ps1 runs an arbitrary set of configurations. Each -Configs entry is label|paramDir|goldenDir, with paths relative to the repository root.

# WD buffer sweep, GrAnt vs rGrAnt at 4 MB (Section 6.1)
.\tools\run_parity2.ps1 -SeedMode all -Parallel 10 -Ticks 800000 -Configs @(
  "WD4M_GrAnt|params\GrAnt_WD4M_Seeds30|goldens\GrAnt_WD4M_Seeds30",
  "WD4M_rGrAnt|params\Vars_WD4M_339hosts_30seeds|goldens\Vars_WD4M_339hosts_30seeds"
)

-Ticks is the scenario's endTime in simulated seconds and must match the scenario:

Scenario -Ticks
WD 800000
PoI (incl. wait-time variant) 800000
Curitiba 10800

Parameter directories follow a stable naming convention:

Table Parameter directories
WD buffer sweep (6.1) GrAnt_WD{4,6,8,10,12,14,16}M_Seeds30, Vars_WD{...}M_339hosts_30seeds
WD node sweep (6.2) {GrAnt,Vars}_WD10M_{75,142,339,594,1104}hosts_30seeds
PoI buffer sweep (6.3) {GrAnt,Vars}_POI{4,6,8,10,12,14,16}M_120hosts_30seeds
PoI node sweep (6.4) {GrAnt,Vars}_POI8M_{12,36,120,240,360}hosts_30seeds
PoI wait-time variant same names with _WaitTime_
Curitiba (6.5) {GrAnt,Vars}_Curitiba_30seeds
Tuning tables (Section 5) GrAntPlus*, GrAntAllVars* — one directory per Var × NC threshold

Vars_* are the rGrAnt configurations; GrAnt_* are the baseline.

9.5 Run a single simulation by hand

.\target\release\grant_report.exe `
    params\Vars_Curitiba_30seeds\Vars_Seed2045.txt `   # parameter file
    data\HelsinkiMedium\roads.wkt `                    # fallback map (ignored when the param file sets its own)
    . `                                                # base directory for relative data/ paths
    10800 `                                            # endTime in simulated seconds
    out.txt                                            # report destination

Compare against goldens\Vars_Curitiba_30seeds\Curitiba_Vars_Seed2045_MessageStatsReport.txt.

9.6 The 30 seeds

Every multi-seed directory contains one parameter file and one golden report per seed, selected by MovementModel.rngSeed:

2, 2045, 1210, 7361, 4264, 6080, 6806,  972, 8187,  659,
5088, 6772, 2606, 7345, 3921, 6088, 7204, 2061, 4494,   99,
7485, 6681, 5057,  399, 5232, 3396, 1936, 3994, 6771,  482

9.7 Auxiliary dump tools

Nine binaries under src/bin/ dump intermediate simulation state, and exist so that any future parity regression can be bisected to its first divergent stage rather than debugged from the final report. Each has a matching Java probe in validation/:

Binary Dumps
rng_dump java.util.Random draw sequences
wkt_dump Parsed WKT geometry
map_dump The assembled road-network graph
positions_dump Initial host placement
positions_at_time_dump Host positions at an arbitrary time
events_dump The message-generation event schedule
connectivity_dump The contact trace
javamap_dump java.util.HashMap iteration order
grant_report The final MessageStatsReport

9.8 Reproduce the full multi-seed sweep (Linux / Azure VM fleet)

The full dissertation depth is 2,775 runs over 195 configurations (every router variant × every scenario × 30 seeds). tools\parity.py is the portable twin of run_parity2.ps1: it runs each (parameter file, golden) pair, compares the report byte-for-byte, and prints one pass/fail line per run, so verification is inline — no need to fetch reports to know the result. It adds --shard I/N to split the cost-sorted work list across machines.

# On any Linux box with the repo built (cargo build --release):
python3 tools/parity.py --seed-mode all --list          # 2775 runs, total cost 640.9
python3 tools/parity.py --seed-mode all --parallel $(nproc) --skip-headline

tools\azure\fleet.ps1 drives a fleet of Azure Linux VMs that run the sweep in parallel. Nodes may differ in size: each node's vCPU count is read back from Azure and turned into that many shares of --shard, so a 16-core node receives four times the work of a 4-core node and they finish together.

.\tools\azure\fleet.ps1 -Action create -Size Standard_F16als_v7 -Count 1   # or -Spot
.\tools\azure\fleet.ps1 -Action setup     # upload the source tree, build on each node
.\tools\azure\fleet.ps1 -Action run       # start each node's cost-weighted shard
.\tools\azure\fleet.ps1 -Action wait      # block until every shard finishes, then summarise
.\tools\azure\fleet.ps1 -Action fetch     # optional: archive the reports locally
.\tools\azure\fleet.ps1 -Action delete    # tear the fleet down — do this when done

The workload is cost-bound, not count-bound: most runs are trivially cheap while a minority (Curitiba, PoI 360/480-host) dominate, so runtime tracks total cost (≈ 640.9 units) rather than the run count. On the Fasv7 family (AMD EPYC Turin, one thread per core) it calibrates to ≈ 9.6 min/cost-unit, so 20 cores complete the sweep in ≈ 5 h. Azure Batch was tried first and abandoned: its pool allocation failed repeatedly with AllocationFailed, while plain VMs of the same size allocate immediately.

Regenerating the empty goldens (Java 8). 40 goldens ship as 0-byte files because they only reproduce under Java 6–8: GrAnt relies on java.util.Arrays.useLegacyMergeSort=true, which Java 9+ ignore in favour of TimSort — and TimSort aborts on GrAnt's non-transitive comparator (see §8). python tools\regen_empty_goldens.py --tier {cheap,medium,heavy} re-runs those cases with the original ONE simulator under Java 8 (jdk1.8.0_202) and drops the reports back into goldens/.


10. Visualizer

visualizer/ is a small, browser-based companion tool that illustrates the three headline scenarios interactively. It is a self-contained TypeScript re-implementation of the routing — not the byte-identical simulator — whose only job is to make the protocols' behaviour visible: nodes and buses move on the real maps, contacts open and close, and messages hop toward their destinations while live metrics update.

The rGrAnt visualizer running the Working Day scenario with rGrAnt routing

Three presets are included, each in its lighter (non-compute-intensive) variant so it runs smoothly in a browser tab, and each runnable with any of the four routers — rGrAnt, GrAnt, Epidemic and PRoPHET:

Preset Mobility Nodes
WD — Working Day Helsinki road graph, 7 districts × 10 workers + 8 buses 78
PoI — Points of Interest 4 communities × 30 walkers, weighted destinations 120
Curitiba — Bus routes 16-line subset of the 267-route network, 2 buses/line 32

Run it:

cd visualizer
npm install
npm run dev        # then open http://localhost:5173

Pick a scenario and a router, press Start, and use the sidebar to tweak seed, TX range, buffer, message rate and TTL. The map shows nodes/buses moving, active contacts, and nodes carrying a message (yellow); the right panel tracks delivery ratio, overhead, latency and hops live. See visualizer/README.md for the architecture and the rGrAnt summary.

The Curitiba preset builds its graph directly from the bus-route WKT files (visualizer/public/curitiba/), deliberately avoiding the 73 MB Combined.wkt used by the heavy study runs. Because the tool is an independent re-implementation with its own RNG, it is not byte-identical to the Rust port or the Java originals — the parity results in this README come from the Rust port in src/, while the visualizer is for intuition and demonstration.


11. Repository layout

.
├── Cargo.toml
├── src/
│   ├── lib.rs
│   ├── java/         bit-exact java.util primitives (rng, sort, hash_map, hashtable)
│   ├── sim/          The ONE core: settings, coord, message, connection,
│   │                 events, report, world
│   ├── movement/     map, wkt, path, working_day, map_based, host, build,
│   │                 initial_positions
│   ├── routing/      grant.rs — GrAnt, the Vars, and rGrAnt's rule layer
│   ├── scenario/     run.rs (main loop), connectivity.rs
│   └── bin/          9 dump tools (sources; Cargo compiles them to target/release)
├── data/             maps — HelsinkiMedium (WD/PoI) and Curitiba_full (267 bus routes)
├── ee/               external event trace driving the Curitiba scenario
├── params/           230 scenario configuration directories
├── goldens/          246 directories of Java MessageStatsReport ground truth
├── tools/            run_headline.ps1, run_parity2.ps1 and helpers
├── validation/       Java probe programs used to generate the ground truth
├── visualizer/       browser-based TS visual tool for WD / PoI / Curitiba
└── docs/             fuzzyant.md (the FuzzyAnt study) + images referenced by this README

Module names mirror The ONE's Java packages, so the port maps onto the original source one-to-one. src/bin/ is Cargo's convention for sources that compile into binary targets — the executables themselves land in target/release/.


12. Provenance

  • The ONE simulator — Aalto University / Nokia Research Center, GPLv3.
  • GrAnt — Vendramin et al.
  • rGrAnt, the Vars, and the experiment configurations — Mendes (2018), the dissertation above.
  • Golden reports in goldens/ were produced by the original instrumented Java implementation and are committed verbatim as ground truth.

Research and educational use.

About

A byte-exact Rust port of the GrAnt / rGrAnt DTN routing experiments

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages