2024-12-03 20:39:46 +09:00

521 lines
19 KiB
Rust

use protocol::queue::QueueType;
use rand::{rngs::StdRng, seq::SliceRandom, SeedableRng};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum ExperimentId {
Experiment1 = 1,
Experiment2 = 2,
Experiment3 = 3,
Experiment4 = 4,
Experiment5 = 5,
}
impl std::str::FromStr for ExperimentId {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"1" | "Experiment1" => Ok(ExperimentId::Experiment1),
"2" | "Experiment2" => Ok(ExperimentId::Experiment2),
"3" | "Experiment3" => Ok(ExperimentId::Experiment3),
"4" | "Experiment4" => Ok(ExperimentId::Experiment4),
"5" | "Experiment5" => Ok(ExperimentId::Experiment5),
_ => Err(format!("Invalid experiment ID: {}", s)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum SessionId {
Session1 = 1,
Session2 = 2,
Session2_1 = 21,
Session3 = 3,
}
impl std::str::FromStr for SessionId {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"1" | "Session1" => Ok(SessionId::Session1),
"2" | "Session2" => Ok(SessionId::Session2),
"2.1" | "Session21" => Ok(SessionId::Session2_1),
"3" | "Session3" => Ok(SessionId::Session3),
_ => Err(format!("Invalid session ID: {}", s)),
}
}
}
pub const PARAMSET_CSV_COLUMNS: &[&str] = &[
"paramset",
"num_nodes",
"peering_degree",
"min_queue_size",
"transmission_rate",
"num_sent_msgs",
"num_senders",
"random_senders_every_time",
"queue_type",
"num_iterations",
];
#[derive(Debug, Clone, PartialEq)]
pub struct ParamSet {
pub id: u16,
pub num_nodes: u32,
pub peering_degree_rates: PeeringDegreeRates,
pub min_queue_size: u16,
pub transmission_rate: u16,
pub num_sent_msgs: u32,
pub num_senders: u32,
pub random_senders_every_time: bool,
pub queue_type: QueueType,
pub num_iterations: usize,
}
// peering_degree -> rate
// Use Vec instead of HashMap to avoid unexpected undeterministic behavior
type PeeringDegreeRates = Vec<(u32, f32)>;
impl ParamSet {
pub fn new_all_paramsets(
exp_id: ExperimentId,
session_id: SessionId,
queue_type: QueueType,
) -> Vec<Self> {
match session_id {
SessionId::Session1 => Self::new_session1_paramsets(exp_id, queue_type),
SessionId::Session2 => Self::new_session2_paramsets(exp_id, queue_type),
SessionId::Session2_1 => Self::new_session2_1_paramsets(exp_id, queue_type),
SessionId::Session3 => Self::new_session3_paramsets(exp_id, queue_type),
}
}
fn new_session1_paramsets(exp_id: ExperimentId, queue_type: QueueType) -> Vec<ParamSet> {
let mut start_id: u16 = 1;
let mut paramsets: Vec<ParamSet> = Vec::new();
for &num_nodes in &[20u32, 40u32, 80u32] {
let peering_degrees_list = &[
vec![(num_nodes.checked_div(5).unwrap(), 1.0)],
vec![(num_nodes.checked_div(4).unwrap(), 1.0)],
vec![(num_nodes.checked_div(2).unwrap(), 1.0)],
];
let min_queue_size_list = &[
num_nodes.checked_div(2).unwrap().try_into().unwrap(),
num_nodes.try_into().unwrap(),
num_nodes.checked_mul(2).unwrap().try_into().unwrap(),
];
let transmission_rate_list = &[
num_nodes.checked_div(2).unwrap().try_into().unwrap(),
num_nodes.try_into().unwrap(),
num_nodes.checked_mul(2).unwrap().try_into().unwrap(),
];
let num_sent_msgs_list = |_| match exp_id {
ExperimentId::Experiment1 | ExperimentId::Experiment3 => vec![1],
ExperimentId::Experiment2
| ExperimentId::Experiment4
| ExperimentId::Experiment5 => vec![8, 16, 32],
};
let num_senders_list = match exp_id {
ExperimentId::Experiment1 | ExperimentId::Experiment2 => vec![1],
ExperimentId::Experiment3
| ExperimentId::Experiment4
| ExperimentId::Experiment5 => {
vec![
num_nodes.checked_div(10).unwrap(),
num_nodes.checked_div(5).unwrap(),
num_nodes.checked_div(2).unwrap(),
]
}
};
let random_senders_every_time = exp_id == ExperimentId::Experiment5;
let num_iterations = num_nodes.checked_div(2).unwrap().try_into().unwrap();
let (mut new_paramsets, next_start_id) = Self::new_paramsets(
start_id,
num_nodes,
peering_degrees_list,
min_queue_size_list,
transmission_rate_list,
num_sent_msgs_list,
num_senders_list.as_slice(),
random_senders_every_time,
queue_type,
num_iterations,
);
paramsets.append(&mut new_paramsets);
start_id = next_start_id;
}
paramsets
}
fn new_session2_paramsets(exp_id: ExperimentId, queue_type: QueueType) -> Vec<ParamSet> {
let mut start_id: u16 = 1;
let mut paramsets: Vec<ParamSet> = Vec::new();
for &num_nodes in &[100u32, 1000u32, 10000u32] {
let peering_degrees_list = &[vec![(4, 1.0)], vec![(8, 1.0)], vec![(16, 1.0)]];
let min_queue_size_list = &[10, 50, 100];
let transmission_rate_list = &[1, 10, 100];
let num_sent_msgs_list = |min_queue_size: u16| match exp_id {
ExperimentId::Experiment1 | ExperimentId::Experiment3 => vec![1],
ExperimentId::Experiment2
| ExperimentId::Experiment4
| ExperimentId::Experiment5 => {
vec![
min_queue_size.checked_div(2).unwrap().into(),
min_queue_size.into(),
min_queue_size.checked_mul(2).unwrap().into(),
]
}
};
let num_senders_list = match exp_id {
ExperimentId::Experiment1 | ExperimentId::Experiment2 => vec![1],
ExperimentId::Experiment3
| ExperimentId::Experiment4
| ExperimentId::Experiment5 => {
vec![
num_nodes.checked_div(10).unwrap(),
num_nodes.checked_div(5).unwrap(),
num_nodes.checked_div(2).unwrap(),
]
}
};
let random_senders_every_time = exp_id == ExperimentId::Experiment5;
let num_iterations = 20;
let (mut new_paramsets, next_start_id) = Self::new_paramsets(
start_id,
num_nodes,
peering_degrees_list,
min_queue_size_list,
transmission_rate_list,
num_sent_msgs_list,
num_senders_list.as_slice(),
random_senders_every_time,
queue_type,
num_iterations,
);
paramsets.append(&mut new_paramsets);
start_id = next_start_id;
}
paramsets
}
fn new_session2_1_paramsets(exp_id: ExperimentId, queue_type: QueueType) -> Vec<ParamSet> {
let mut start_id: u16 = 1;
let mut paramsets: Vec<ParamSet> = Vec::new();
for &num_nodes in &[20u32, 200u32, 2000u32] {
let peering_degrees_list = &[vec![(4, 1.0)], vec![(6, 1.0)], vec![(8, 1.0)]];
let min_queue_size_list = &[10, 50, 100];
let transmission_rate_list = &[1];
let num_sent_msgs_list = |_| match exp_id {
ExperimentId::Experiment1 | ExperimentId::Experiment3 => vec![1],
ExperimentId::Experiment2
| ExperimentId::Experiment4
| ExperimentId::Experiment5 => vec![1000],
};
let num_senders_list = match exp_id {
ExperimentId::Experiment1 | ExperimentId::Experiment2 => vec![1],
ExperimentId::Experiment3
| ExperimentId::Experiment4
| ExperimentId::Experiment5 => {
vec![
num_nodes.checked_div(10).unwrap(),
num_nodes.checked_div(5).unwrap(),
num_nodes.checked_div(2).unwrap(),
]
}
};
let random_senders_every_time = exp_id == ExperimentId::Experiment5;
let num_iterations = 20;
let (mut new_paramsets, next_start_id) = Self::new_paramsets(
start_id,
num_nodes,
peering_degrees_list,
min_queue_size_list,
transmission_rate_list,
num_sent_msgs_list,
num_senders_list.as_slice(),
random_senders_every_time,
queue_type,
num_iterations,
);
paramsets.append(&mut new_paramsets);
start_id = next_start_id;
}
paramsets
}
fn new_session3_paramsets(exp_id: ExperimentId, queue_type: QueueType) -> Vec<ParamSet> {
let start_id: u16 = 1;
let num_nodes: u32 = 100000;
let peering_degrees = vec![(4, 0.87), (129, 0.123), (500, 0.07)];
let min_queue_size_list = &[10, 50, 100];
let transmission_rate_list = &[1];
let num_sent_msgs_list = |min_queue_size: u16| match exp_id {
ExperimentId::Experiment1 | ExperimentId::Experiment3 => vec![1],
ExperimentId::Experiment2 | ExperimentId::Experiment4 | ExperimentId::Experiment5 => {
vec![
min_queue_size.checked_div(2).unwrap().into(),
min_queue_size.into(),
min_queue_size.checked_mul(2).unwrap().into(),
]
}
};
let num_senders_list = match exp_id {
ExperimentId::Experiment1 | ExperimentId::Experiment2 => vec![1],
ExperimentId::Experiment3 | ExperimentId::Experiment4 | ExperimentId::Experiment5 => {
vec![
num_nodes.checked_div(10).unwrap(),
num_nodes.checked_div(5).unwrap(),
num_nodes.checked_div(2).unwrap(),
]
}
};
let random_senders_every_time = exp_id == ExperimentId::Experiment5;
let num_iterations = 100;
let (paramsets, _) = Self::new_paramsets(
start_id,
num_nodes,
&[peering_degrees],
min_queue_size_list,
transmission_rate_list,
num_sent_msgs_list,
num_senders_list.as_slice(),
random_senders_every_time,
queue_type,
num_iterations,
);
paramsets
}
#[allow(clippy::too_many_arguments)]
fn new_paramsets(
start_id: u16,
num_nodes: u32,
peering_degrees_list: &[PeeringDegreeRates],
min_queue_size_list: &[u16],
transmission_rate_list: &[u16],
num_sent_msgs_list: impl Fn(u16) -> Vec<u32>,
num_senders_list: &[u32],
random_senders_every_time: bool,
queue_type: QueueType,
num_iterations: usize,
) -> (Vec<ParamSet>, u16) {
let mut id = start_id;
let mut paramsets: Vec<ParamSet> = Vec::new();
for peering_degrees in peering_degrees_list {
for &min_queue_size in min_queue_size_list {
for &transmission_rate in transmission_rate_list {
for &num_sent_msgs in num_sent_msgs_list(min_queue_size).iter() {
for &num_senders in num_senders_list {
if !Self::is_min_queue_size_applicable(&queue_type)
&& min_queue_size != min_queue_size_list[0]
{
id += 1;
continue;
}
paramsets.push(ParamSet {
id,
num_nodes,
peering_degree_rates: peering_degrees.clone(),
min_queue_size,
transmission_rate,
num_sent_msgs,
num_senders,
random_senders_every_time,
queue_type,
num_iterations,
});
id += 1;
}
}
}
}
}
(paramsets, id)
}
pub fn is_min_queue_size_applicable(queue_type: &QueueType) -> bool {
matches!(
queue_type,
QueueType::PureCoinFlipping
| QueueType::PureRandomSampling
| QueueType::PermutedCoinFlipping
)
}
pub fn total_num_messages(&self) -> u32 {
self.num_sent_msgs.checked_mul(self.num_senders).unwrap()
}
pub fn as_csv_record(&self) -> Vec<String> {
let peering_degrees = self
.peering_degree_rates
.iter()
.map(|(degree, rate)| format!("({degree}:{rate})"))
.collect::<Vec<String>>()
.join(",");
vec![
self.id.to_string(),
self.num_nodes.to_string(),
format!("[{peering_degrees}]"),
self.min_queue_size.to_string(),
self.transmission_rate.to_string(),
self.num_sent_msgs.to_string(),
self.num_senders.to_string(),
self.random_senders_every_time.to_string(),
format!("{:?}", self.queue_type),
self.num_iterations.to_string(),
]
}
pub fn gen_peering_degrees(&self, seed: u64) -> Vec<u32> {
let mut vec = Vec::with_capacity(self.num_nodes as usize);
self.peering_degree_rates.iter().for_each(|(degree, rate)| {
let num_nodes = std::cmp::min(
(self.num_nodes as f32 * rate).round() as u32,
self.num_nodes - vec.len() as u32,
);
vec.extend(std::iter::repeat(*degree).take(num_nodes as usize));
});
assert_eq!(vec.len(), self.num_nodes as usize);
vec.shuffle(&mut StdRng::seed_from_u64(seed));
vec
}
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
use strum::IntoEnumIterator;
use crate::paramset::ParamSet;
use super::*;
#[test]
fn test_new_all_paramsets() {
let cases = vec![
(
(ExperimentId::Experiment1, SessionId::Session1),
3u32.pow(4),
),
(
(ExperimentId::Experiment2, SessionId::Session1),
3u32.pow(5),
),
(
(ExperimentId::Experiment3, SessionId::Session1),
3u32.pow(5),
),
(
(ExperimentId::Experiment4, SessionId::Session1),
3u32.pow(6),
),
(
(ExperimentId::Experiment5, SessionId::Session1),
3u32.pow(6),
),
(
(ExperimentId::Experiment1, SessionId::Session2),
3u32.pow(4),
),
(
(ExperimentId::Experiment4, SessionId::Session2),
3u32.pow(6),
),
(
(ExperimentId::Experiment5, SessionId::Session2),
3u32.pow(6),
),
(
(ExperimentId::Experiment1, SessionId::Session2_1),
3u32.pow(3),
),
(
(ExperimentId::Experiment4, SessionId::Session2_1),
3u32.pow(4),
),
(
(ExperimentId::Experiment5, SessionId::Session2_1),
3u32.pow(4),
),
(
(ExperimentId::Experiment5, SessionId::Session3),
3u32.pow(3),
),
];
for queue_type in QueueType::iter() {
for ((exp_id, session_id), mut expected_cnt) in cases.clone().into_iter() {
let paramsets = ParamSet::new_all_paramsets(exp_id, session_id, queue_type);
// Check if the number of parameter sets is correct
if !ParamSet::is_min_queue_size_applicable(&queue_type) {
expected_cnt /= 3;
}
assert_eq!(paramsets.len(), expected_cnt as usize);
// Check if all parameter sets are unique
let unique_paramsets: HashSet<Vec<String>> = paramsets
.iter()
.map(|paramset| paramset.as_csv_record())
.collect();
assert_eq!(unique_paramsets.len(), paramsets.len());
// Check if paramset IDs are correct.
if ParamSet::is_min_queue_size_applicable(&queue_type) {
for (i, paramset) in paramsets.iter().enumerate() {
assert_eq!(paramset.id as usize, i + 1);
}
}
}
}
}
#[test]
fn test_id_consistency() {
let cases = vec![
(ExperimentId::Experiment1, SessionId::Session1),
(ExperimentId::Experiment2, SessionId::Session1),
(ExperimentId::Experiment3, SessionId::Session1),
(ExperimentId::Experiment4, SessionId::Session1),
(ExperimentId::Experiment5, SessionId::Session1),
(ExperimentId::Experiment1, SessionId::Session2),
(ExperimentId::Experiment4, SessionId::Session2),
(ExperimentId::Experiment5, SessionId::Session2),
(ExperimentId::Experiment1, SessionId::Session2_1),
(ExperimentId::Experiment4, SessionId::Session2_1),
(ExperimentId::Experiment5, SessionId::Session2_1),
(ExperimentId::Experiment5, SessionId::Session3),
];
for (exp_id, session_id) in cases.into_iter() {
let paramsets_with_min_queue_size =
ParamSet::new_all_paramsets(exp_id, session_id, QueueType::PureCoinFlipping);
let paramsets_without_min_queue_size =
ParamSet::new_all_paramsets(exp_id, session_id, QueueType::NonMix);
for (i, paramset) in paramsets_with_min_queue_size.iter().enumerate() {
assert_eq!(paramset.id as usize, i + 1);
}
for mut paramset in paramsets_without_min_queue_size.into_iter() {
// To compare ParameterSet instances, use the same queue type.
paramset.queue_type = QueueType::PureCoinFlipping;
assert_eq!(
paramset,
paramsets_with_min_queue_size[paramset.id as usize - 1]
);
}
}
}
}