-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Small world network implementation. WIP commit prior to FLAMEGPU_INIT…
…_FUNC refactor
- Loading branch information
Showing
13 changed files
with
722 additions
and
86 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
rng_seed,param_id,duration,n_total,n_seed_infection,population_0_9,population_10_19,population_20_29,population_30_39,population_40_49,population_50_59,population_60_69,population_70_79,population_80,household_size_1,household_size_2,household_size_3,household_size_4,household_size_5,household_size_6,p_interaction_susceptible_to_exposed,mean_time_to_infected,sd_time_to_infected,mean_time_to_recovered,sd_time_to_recovered,mean_time_to_susceptible,sd_time_to_susceptible,relative_susceptibility_0_9,relative_susceptibility_10_19,relative_susceptibility_20_29,relative_susceptibility_30_39,relative_susceptibility_40_49,relative_susceptibility_50_59,relative_susceptibility_60_69,relative_susceptibility_70_79,relative_susceptibility_80,child_network_adults,elderly_network_adults,relative_transmission_household,relative_transmission_occupation,relative_transmission_random,daily_fraction_work,mean_random_interactions_0_19,sd_random_interactions_0_19,mean_random_interactions_20_69,sd_random_interactions_20_69,mean_random_interactions_70plus,sd_random_interactions_70plus | ||
12,0,60,32,4,331,370,374,371,361,415,377,270,142,442,492,208,165,51,27,0.10,3.5,2.0,7.0,2.0,28,10.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.2,0.2,2.0,0.5,0.5,0.001,2,2,4,4,3,3 | ||
12,1,365,32,4,331,370,374,371,361,415,377,270,142,442,492,208,165,51,27,0.10,3.5,2.0,7.0,2.0,28,10.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.2,0.2,2.0,0.5,0.5,0.001,2,2,4,4,3,3 | ||
12,2,365,1024,8,331,370,374,371,361,415,377,270,142,442,492,208,165,51,27,0.10,3.5,2.0,7.0,2.0,28,10.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.2,0.2,2.0,0.5,0.5,0.001,2,2,4,4,3,3 | ||
12,3,365,4096,8,331,370,374,371,361,415,377,270,142,442,492,208,165,51,27,0.10,3.5,2.0,7.0,2.0,28,10.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.2,0.2,2.0,0.5,0.5,0.001,2,2,4,4,3,3 | ||
12,4,365,4096,8,331,370,374,371,361,415,377,270,142,442,492,208,165,51,27,0.10,3.5,2.0,7.0,2.0,28,10.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.2,0.2,2.0,0.5,0.5,0.001,2,2,4,4,3,3 | ||
12,5,365,65536,8,331,370,374,371,361,415,377,270,142,442,492,208,165,51,27,0.10,3.5,2.0,7.0,2.0,28,10.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.2,0.2,2.0,0.5,0.5,0.001,2,2,4,4,3,3 | ||
rng_seed,param_id,duration,n_total,n_seed_infection,population_0_9,population_10_19,population_20_29,population_30_39,population_40_49,population_50_59,population_60_69,population_70_79,population_80,household_size_1,household_size_2,household_size_3,household_size_4,household_size_5,household_size_6,p_interaction_susceptible_to_exposed,mean_time_to_infected,sd_time_to_infected,mean_time_to_recovered,sd_time_to_recovered,mean_time_to_susceptible,sd_time_to_susceptible,relative_susceptibility_0_9,relative_susceptibility_10_19,relative_susceptibility_20_29,relative_susceptibility_30_39,relative_susceptibility_40_49,relative_susceptibility_50_59,relative_susceptibility_60_69,relative_susceptibility_70_79,relative_susceptibility_80,child_network_adults,elderly_network_adults,relative_transmission_household,relative_transmission_occupation,relative_transmission_random,mean_work_interactions_child,mean_work_interactions_adult,mean_work_interactions_elderly,daily_fraction_work,work_network_rewire,mean_random_interactions_0_19,sd_random_interactions_0_19,mean_random_interactions_20_69,sd_random_interactions_20_69,mean_random_interactions_70plus,sd_random_interactions_70plus | ||
12,0,60,32,4,331,370,374,371,361,415,377,270,142,442,492,208,165,51,27,0.10,3.5,2.0,7.0,2.0,28,10.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.2,0.2,2.0,0.5,0.5,10,7,3,0.5,0.1,2,2,4,4,3,3 | ||
12,1,365,32,4,331,370,374,371,361,415,377,270,142,442,492,208,165,51,27,0.10,3.5,2.0,7.0,2.0,28,10.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.2,0.2,2.0,0.5,0.5,10,7,3,0.5,0.1,2,2,4,4,3,3 | ||
12,2,365,1024,8,331,370,374,371,361,415,377,270,142,442,492,208,165,51,27,0.10,3.5,2.0,7.0,2.0,28,10.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.2,0.2,2.0,0.5,0.5,10,7,3,0.5,0.1,2,2,4,4,3,3 | ||
12,3,365,4096,8,331,370,374,371,361,415,377,270,142,442,492,208,165,51,27,0.10,3.5,2.0,7.0,2.0,28,10.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.2,0.2,2.0,0.5,0.5,10,7,3,0.5,0.1,2,2,4,4,3,3 | ||
12,4,365,4096,8,331,370,374,371,361,415,377,270,142,442,492,208,165,51,27,0.10,3.5,2.0,7.0,2.0,28,10.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.2,0.2,2.0,0.5,0.5,10,7,3,0.5,0.1,2,2,4,4,3,3 | ||
12,5,365,65536,8,331,370,374,371,361,415,377,270,142,442,492,208,165,51,27,0.10,3.5,2.0,7.0,2.0,28,10.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.2,0.2,2.0,0.5,0.5,10,7,3,0.5,0.1,2,2,4,4,3,3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
#include "exateppabm/network.h" | ||
|
||
#include <exception> | ||
#include <iostream> | ||
#include <string> | ||
#include <utility> | ||
#include <vector> | ||
#include <unordered_set> | ||
#include <random> | ||
|
||
#include "flamegpu/flamegpu.h" | ||
#include "fmt/core.h" | ||
|
||
namespace exateppabm { | ||
namespace network { | ||
|
||
|
||
UndirectedNetwork generateFullyConnectedUndirectedNetwork(std::vector<flamegpu::id_t> nodes) { | ||
UndirectedNetwork network = {}; | ||
network.nodes = nodes; | ||
if (nodes.size() <= 1) { | ||
return network; | ||
} | ||
std::uint32_t N = static_cast<std::int32_t>(nodes.size()); | ||
std::uint32_t E = (N * (N - 1)) / 2; | ||
network.edges.reserve(E); | ||
// Generate edges in ascending order for the undirected graph. | ||
for (std::uint32_t i = 0; i < N; ++i) { | ||
for (std::uint32_t j = i + 1; j < N; ++j) { | ||
network.edges.push_back({i, j}); | ||
} | ||
} | ||
return network; | ||
} | ||
|
||
// @todo - should the initial lattice be randomised rather than sequential? | ||
// @todo - should the graph be directed or undirected? Data structure is directed, so do pairs need rewiring together? | ||
UndirectedNetwork generateSmallWorldUndirectedNetwork(std::vector<flamegpu::id_t> nodes, std::uint32_t K, double p_rewire, std::mt19937_64 rng) { | ||
// fmt::print("@todo - check if small world network should be directed or undirected\n"); | ||
UndirectedNetwork network = {}; | ||
network.nodes = nodes; | ||
|
||
// Return early with an empty network if 0 or 1 nodes, so no room for non-self edges | ||
if (network.nodes.size() <= 1) { | ||
return network; | ||
} | ||
|
||
// If K is 0 (or 1), no edges so do nothing | ||
if (K <= 1) { | ||
// throw std::runtime_error("@todo values - small world network K (" + std::to_string(K) + ") must be > 1"); | ||
return network; | ||
} else if (K == network.nodes.size()) { | ||
// If K == Node count, the graph is fully connected, so return a fully connected graph | ||
return generateFullyConnectedUndirectedNetwork(nodes); | ||
} else if (K >= network.nodes.size()) { | ||
// Raise an exception if K is too large | ||
throw std::runtime_error(std::string("@todo values - small world network K(") + std::to_string(K) + ") must be less than |N| (" + std::to_string(network.nodes.size()) + ")"); | ||
} | ||
// If K is odd, use K-1 | ||
if (K % 2 == 1) { | ||
K = K - 1; | ||
} | ||
|
||
// p_rewire must be between 0 and 1. | ||
if (p_rewire < 0 || p_rewire > 1) { | ||
throw std::runtime_error("generateSmallWorldNetwork p must be in [0, 1]. @todo include value"); | ||
} | ||
|
||
// Initialise the edges as a lattice network, going K edges in each direction. | ||
// Use signed integers to make modulo possible | ||
std::int32_t N = static_cast<std::int32_t>(nodes.size()); | ||
std::int32_t E = (N * K) / 2; | ||
network.edges.reserve(E); | ||
for (std::int32_t i = 0; i < N; ++i) { | ||
for (std::int32_t j = 1; j <= static_cast<std::int32_t>(K / 2); ++j) { | ||
// only add the positive undirected edges | ||
std::uint32_t s = static_cast<std::uint32_t>(i); | ||
std::uint32_t d = static_cast<std::uint32_t>((i + j) % N); | ||
// Ensure that the edge source node is lower than the destination, to simplify avoiding duplicate edges | ||
if (s > d) { | ||
std::swap(s, d); | ||
} | ||
network.edges.push_back({s, d}); | ||
// network.edges.push_back({static_cast<std::uint32_t>(i), static_cast<std::uint32_t>((i + j) % N)}); | ||
// network.edges.push_back({static_cast<std::uint32_t>(i), static_cast<std::uint32_t>((i - j + N) % N)}); | ||
} | ||
} | ||
|
||
// If the network is fully connected (enough edges for each node to be connected to every other node), rewiring is not needed, so adjust the probability to prevent infinite loops. | ||
std::int32_t MAX_E = (N * (N - 1)) / 2; | ||
if (E >= MAX_E) { | ||
p_rewire = 0.0; | ||
} | ||
|
||
// If p_rewire is 0, no need to loop over the edges | ||
if (p_rewire >= 0.0) { | ||
// Get a uniform dist [0, 1) | ||
std::uniform_real_distribution<double> p_dist(0.0, 1.0); | ||
// Get a uniform integer distribution from [0, N) for generating new edge indices | ||
std::uniform_int_distribution<std::uint32_t> dest_dist(0, N-1); | ||
// Randomly rewire edges | ||
for (auto& edge : network.edges) { | ||
if (p_dist(rng) < p_rewire) { | ||
std::uint32_t newDest = dest_dist(rng); | ||
// Repeat the generation until the edge is not a self-edge, nor duplicate | ||
// Not using do while so the search for the edge is ok. | ||
// @note - this will be a challenge to parallelise efficiently without atomics (i.e. stable matching) | ||
// @note - this will be expensive for large networks, an edge matrix would be cheaper (at the cost of N*N memory) | ||
// @todo - need to avoid directed edge duplicates. | ||
while (newDest == edge.source || std::find(network.edges.begin(), network.edges.end(), UndirectedNetwork::Edge{edge.source, newDest}) != network.edges.end()) { | ||
newDest = dest_dist(rng); | ||
} | ||
edge.dest = newDest; | ||
} | ||
} | ||
} | ||
|
||
return network; | ||
} | ||
|
||
} // namespace network | ||
} // namespace exateppabm |
Oops, something went wrong.