The UFZ services GitLab and Mattermost will be unavailable on Monday, December 06 from 06:00 AM to 08:00 AM due to maintenance work.

Commit 0cdb62a4 authored by Adam Reichold's avatar Adam Reichold
Browse files

WIP: Abuse the vectors measurement to trace the temporal structure of the...

WIP: Abuse the vectors measurement to trace the temporal structure of the contact and transmission networks.
parent 9d38408d
Pipeline #40423 failed with stage
in 5 minutes and 45 seconds
......@@ -6,6 +6,17 @@ version = 3
name = "access"
version = "0.1.0"
[[package]]
name = "ahash"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98"
dependencies = [
"getrandom",
"once_cell",
"version_check",
]
[[package]]
name = "autocfg"
version = "1.0.1"
......@@ -28,6 +39,50 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "crossbeam-channel"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd"
dependencies = [
"cfg-if",
"crossbeam-utils",
"lazy_static",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
dependencies = [
"cfg-if",
"lazy_static",
]
[[package]]
name = "dump"
version = "0.1.0"
......@@ -35,6 +90,12 @@ dependencies = [
"model",
]
[[package]]
name = "either"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "experiments"
version = "0.1.0"
......@@ -56,10 +117,35 @@ dependencies = [
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
dependencies = [
"ahash",
"rayon",
]
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "kdtree"
version = "0.1.0"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.101"
......@@ -78,8 +164,10 @@ version = "0.1.0"
dependencies = [
"access",
"dump",
"hashbrown",
"memmap2",
"model",
"rayon",
]
[[package]]
......@@ -91,12 +179,22 @@ dependencies = [
"libc",
]
[[package]]
name = "memoffset"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
dependencies = [
"autocfg",
]
[[package]]
name = "model"
version = "0.1.0"
dependencies = [
"access",
"bitmap",
"hashbrown",
"kdtree",
"process",
"queue",
......@@ -117,6 +215,22 @@ dependencies = [
"libm",
]
[[package]]
name = "num_cpus"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "once_cell"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
[[package]]
name = "pkg-config"
version = "0.3.19"
......@@ -197,6 +311,43 @@ dependencies = [
"memmap2",
]
[[package]]
name = "rayon"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90"
dependencies = [
"autocfg",
"crossbeam-deque",
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
"lazy_static",
"num_cpus",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "version_check"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
......
......@@ -16,6 +16,7 @@ path = "src/lib.rs"
[dependencies]
access = { path = "libraries/access" }
bitmap = { path = "libraries/bitmap" }
hashbrown = "0.11"
kdtree = { path = "libraries/kdtree" }
process = { path = "libraries/process" }
queue = { path = "libraries/queue" }
......
......@@ -61,6 +61,8 @@ pub fn sample(params: Params, duration: Tick, interval: Tick) {
save(&model.world, buffer);
},
);
model.world.vectors.clear();
}
});
}
......
......@@ -99,6 +99,8 @@ unsafe impl Dump for InsectSpecies {}
unsafe impl Dump for usize {}
unsafe impl Dump for u32 {}
unsafe impl Dump for u64 {}
unsafe impl Dump for Tick {}
......
......@@ -8,5 +8,7 @@ edition = "2018"
[dependencies]
access = { path = "../libraries/access" }
dump = { path = "../libraries/dump" }
hashbrown = { version = "0.11", features = ["rayon"] }
memmap2 = "0.3"
model = { path = ".." }
rayon = "1.5"
......@@ -34,12 +34,12 @@ fn main() {
let mut contaminated = HashMap::<FlowerPatchSpecies, (usize, usize)>::new();
for (flower_patch, flowers) in flower_patches(worlds) {
for (flower_patch, _flowers) in flower_patches(worlds) {
let (all_flowers, all_contaminated_flowers) =
contaminated.entry(flower_patch.species).or_default();
*all_flowers += flower_patch.flowers as usize;
*all_contaminated_flowers += flower_patch.contaminated_flowers.count(flowers) as usize;
*all_contaminated_flowers += flower_patch.contaminated_flowers.len();
}
let mut contaminated = contaminated.into_iter().collect::<Vec<_>>();
......
use std::array::IntoIter;
use std::collections::BTreeMap;
use std::env::args_os;
use std::process::exit;
use access::dictionary;
use measurements::collect;
use model::Vectors;
use hashbrown::{HashMap, HashSet};
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
fn main() {
let mut vectors: Vectors = Default::default();
use measurements::{collect, insects};
use model::insect::Species as InsectSpecies;
collect(args_os().nth(1).unwrap(), |_tick, worlds| {
vectors =
worlds
.iter()
.map(|world| &world.vectors)
.fold(Default::default(), |mut acc, val| {
for (key, val) in val {
*dictionary(&mut acc, *key) += val;
}
acc
});
});
fn main() {
let mut nodes = HashMap::<u32, u8>::default();
let mut graphs = BTreeMap::<i64, Graph>::new();
let all_infections = vectors.iter().map(|(_, val)| val).sum::<usize>();
let mut unfolded_graph = Graph::default();
let mut flower_patch_species = vectors
.iter()
.map(|((species, _, _), _)| *species)
.collect::<Vec<_>>();
print!("time,paths");
flower_patch_species.sort_unstable();
flower_patch_species.dedup();
for cont in 0..2 {
for inf in 0..2 {
print!(",paths_{}_{}", cont, inf);
}
}
let mut insect_species = vectors
.iter()
.flat_map(|((_, contaminated_by_species, infected_species), _)| {
IntoIter::new([*contaminated_by_species, *infected_species])
})
.collect::<Vec<_>>();
println!();
insect_species.sort_unstable();
insect_species.dedup();
collect(args_os().nth(1).unwrap(), |_tick, worlds| {
if nodes.is_empty() {
for insect in insects(worlds) {
match insect.species {
InsectSpecies::Hoverfly(species) => {
nodes.insert(insect.id, species);
}
_ => unreachable!(),
}
}
println!("infections = {}", all_infections);
println!();
for &node in nodes.keys() {
unfolded_graph.insert(node, node);
}
}
for flower_patch_species in &flower_patch_species {
println!("vectors =");
for vectors in worlds.iter().map(|world| &world.vectors) {
for &(time, src_node, dst_node) in vectors {
let graph = graphs.entry(time.as_hours()).or_default();
for contaminated_by_species in &insect_species {
print!("\t");
graph.insert(src_node, dst_node);
}
}
for infected_species in &insect_species {
let infections = dictionary(
&mut vectors,
(
*flower_patch_species,
*contaminated_by_species,
*infected_species,
),
for (time, graph) in &graphs {
unfolded_graph.unfold(graph);
let all_paths = unfolded_graph.edge_count();
print!("{},{}", time, all_paths);
let paths = unfolded_graph
.edges()
.fold(
|| [[0; 2]; 2],
|mut acc, (src_node, dst_node)| {
acc[nodes[&src_node] as usize][nodes[&dst_node] as usize] += 1;
acc
},
)
.reduce(
|| [[0; 2]; 2],
|mut lhs, rhs| {
for cont in 0..2 {
for inf in 0..2 {
lhs[cont][inf] += rhs[cont][inf];
}
}
lhs
},
);
print!("{:.3} ", *infections as f64 / all_infections as f64);
for paths in paths {
for paths in paths {
print!(",{}", paths);
}
}
println!();
if all_paths == nodes.len().pow(2) {
exit(0);
}
}
println!();
graphs.clear();
});
}
#[derive(Default)]
struct Graph {
outgoing: HashMap<u32, HashSet<u32>>,
}
impl Graph {
fn insert(&mut self, src_node: u32, dst_node: u32) {
let outgoing = self.outgoing.entry(src_node).or_default();
outgoing.insert(dst_node);
}
fn unfold(&mut self, other: &Self) {
self.outgoing.par_values_mut().for_each_with(
HashSet::<u32>::default(),
|all_newly_reachable, reachable| {
for node in reachable.iter() {
if let Some(newly_reachable) = other.outgoing.get(node) {
all_newly_reachable.extend(newly_reachable);
}
}
reachable.extend(all_newly_reachable.drain());
},
);
}
fn edge_count(&self) -> usize {
self.outgoing.values().map(|outgoing| outgoing.len()).sum()
}
fn edges(&self) -> impl ParallelIterator<Item = (u32, u32)> + '_ {
self.outgoing.par_iter().flat_map(|(src_node, outgoing)| {
outgoing
.par_iter()
.map(move |dst_node| (*src_node, *dst_node))
})
}
}
......@@ -59,6 +59,7 @@ pub enum Activity {
\begin{code}
impl Bumblebee {
pub fn new(
id: u32,
params: &Params,
colony_location: ColonyLocation,
species: u8,
......@@ -69,6 +70,7 @@ impl Bumblebee {
let pollen = bumblebee_params.pollen_per_bout * rng.gen();
let insect = Insect::new(
id,
params,
&bumblebee_params.insect,
colony_location.position,
......
......@@ -12,8 +12,8 @@ While this small-scale description is spatially implicit, statistic correlations
\begin{hiddencode}
use std::cmp::Ordering;
use std::collections::BinaryHeap;
use std::mem::MaybeUninit;
use hashbrown::HashMap;
use rand::{
distributions::{Distribution, OpenClosed01},
Rng as _,
......@@ -23,7 +23,6 @@ use bitmap::Bitmap;
use kdtree::Object;
use crate::{
insect::Species as InsectSpecies,
params::Params,
space::Vector,
units::{Area, Mass, Tick},
......@@ -40,15 +39,14 @@ Having both a continuous representation of available resources by tracking necta
The auxiliary variable \inlinecode{contaminated\_by\_species} is used to track which insect species was responsible for last contamination of a flower in this patch and does not affect the pathogen dynamics but is used as a micro-observable as described in \autoref{micro-observables}.
\begin{code}
#[derive(Clone, Copy)]
pub struct FlowerPatch {
pub species: Species,
pub nectar: Mass,
pub flowers: u16,
pub nectar_production: Mass,
pub empty_flowers: Bitmap,
pub contaminated_flowers: Bitmap,
pub contaminated_by_species: MaybeUninit<InsectSpecies>,
pub contaminated_flowers: HashMap<u16, u32>,
pub visited_flowers: HashMap<u16, HashMap<u32, Tick>>,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
......@@ -76,7 +74,7 @@ impl FlowerPatch {
let nectar_production = flower_patch_params.nectar_production(rng);
let mut empty_flowers = Bitmap::new(flowers1, flowers as usize);
let contaminated_flowers = Bitmap::new(flowers1, flowers as usize);
let contaminated_flowers = HashMap::new();
\end{code}
To decrease the time until an equilibrium between nectar production and consumption is reached, the flower patches are initialised stochastically so that the fraction given by the parameter \inlinecode{empty\_flower\_patches} is empty with a uniformly distributed time-to-refill while the rest is given a uniformly distributed amount of nectar left.
......@@ -117,7 +115,7 @@ For the non-depleted flower patches, the discrete representation of available ne
nectar_production,
empty_flowers,
contaminated_flowers,
contaminated_by_species: MaybeUninit::uninit(),
visited_flowers: Default::default(),
}
}
}
......@@ -170,11 +168,9 @@ impl Event {
flower_patch,
flower,
}) if *happens_at <= tick => {
flower_patches[flower_patch.get()].contaminated_flowers.set(
flowers,
*flower as usize,
false,
);
flower_patches[flower_patch.get()]
.contaminated_flowers
.remove(flower);
events.pop();
}
......
......@@ -72,7 +72,7 @@ During initialisation \marginpar{initialisation}, a species within the hoverfly
\begin{code}
impl Hoverfly {
pub fn new(params: &Params, position: Vector, rng: &mut Rng) -> Self {
pub fn new(id: u32, params: &Params, position: Vector, rng: &mut Rng) -> Self {
let species = params.init.hoverfly_species.sample(rng);
let hoverfly_params = &params.hoverflies[species as usize];
......@@ -80,6 +80,7 @@ impl Hoverfly {
let pollen = hoverfly_params.pollen_per_clutch * rng.gen();
let insect = Insect::new(
id,
params,
&hoverfly_params.insect,
position,
......
......@@ -182,10 +182,13 @@ As entities are identified by the rank of the process they are located at and th
Hoverflies are mobile entities which perform their reproductive activities using the stationary clutch entities. Clutches are placed randomly, indexed spatially and discard if they do not support at least a single clutch of eggs in the same manner as the flower patches.
\begin{code}
let mut id = 0;
let mut hoverflies = Vec::new();
for _ in 0..params.init.hoverflies {
let hoverfly = Hoverfly::new(&params, position(&mut rng), &mut rng);
let hoverfly = Hoverfly::new(id, &params, position(&mut rng), &mut rng);
id += 1;
let dest = grid.location(&hoverfly.insect.position);
......@@ -238,7 +241,8 @@ Solitary bees are mobile entities which perform their reproductive activities us
let mut solitary_bees = Vec::new();
for _ in 0..params.init.solitary_bees {
let solitary_bee = SolitaryBee::new(&params, position(&mut rng), &mut rng);
let solitary_bee = SolitaryBee::new(id, &params, position(&mut rng), &mut rng);
id += 1;
let dest = grid.location(&solitary_bee.insect.position);
......@@ -306,7 +310,9 @@ Bumblebees are mobile entities which are always associated to single colony. Hen
}
for _ in 0..colony.capacity {
let bumblebee = Bumblebee::new(&params, colony.location, colony.species, &mut rng);
let bumblebee =
Bumblebee::new(id, &params, colony.location, colony.species, &mut rng);
id += 1;
if dest == rank {
bumblebees.push(bumblebee);
......
......@@ -69,6 +69,7 @@ The parameter \inlinecode{species} is also associated with each instance and is
\begin{code}
#[derive(Clone, Copy)]
pub struct Insect {
pub id: u32,
pub position: Vector,
pub movement: Vector,
pub species: Species,
......@@ -107,6 +108,7 @@ Finally, the infection status is randomly initialised here using the strategy gi
\begin{code}
impl Insect {
pub fn new(
id: u32,
params: &Params,
insect_params: &InsectParams,
position: Vector,
......@@ -120,6 +122,7 @@ impl Insect {
.status(species, params, insect_params, rng);
Self {
id,
position,
movement: Default::default(),
species,
......@@ -269,6 +272,7 @@ As the scarcity of nectar is primarily used as a driver for the exploration of t
}
infection_contamination(
self.id,
params,
insect_params,
self.species,
......
......@@ -162,7 +162,7 @@ pub struct World {
pub type Contacts = Vec<((FlowerPatchSpecies, InsectSpecies), (usize, Vec<usize>))>;
pub type Vectors = Vec<((FlowerPatchSpecies, InsectSpecies, InsectSpecies), usize)>;
pub type Vectors = Vec<(Tick, u32, u32)>;
\end{code}
\section{Process overview and scheduling}
......
......@@ -173,7 +173,7 @@ impl Observer {
visited_flowers: &mut [Vec<Vec<u16>>; 3],
tick: Tick,
flower_patches: &[FlowerPatch],
flowers: &[u64],
_flowers: &[u64],
hoverflies: &mut Vec<Hoverfly>,
solitary_bees: &mut Vec<SolitaryBee>,
bumblebees: &mut Vec<Bumblebee>,
......@@ -241,7 +241,7 @@ If there are any such insects, the one that is closest to the observer is remove
position: self.position,
flower_patch_species: flower_patch.species,