From d836bd2ec58f845b957dc75d7814632798d10b41 Mon Sep 17 00:00:00 2001 From: Amrita Goswami Date: Sat, 9 Mar 2024 16:45:52 +0000 Subject: [PATCH 1/4] WIP: bot homophily added but not tested --- examples/ActivityDrivenBot/conf.toml | 3 ++- include/models/ActivityDrivenModel.hpp | 11 ++++++----- src/models/ActivityDrivenModel.cpp | 17 +++++++++++++++-- src/simulation.cpp | 14 +++++++++----- 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/examples/ActivityDrivenBot/conf.toml b/examples/ActivityDrivenBot/conf.toml index 9910da1..c9d8d67 100644 --- a/examples/ActivityDrivenBot/conf.toml +++ b/examples/ActivityDrivenBot/conf.toml @@ -4,7 +4,7 @@ rng_seed = 120 # Leaving this empty will pick a random seed [io] n_output_network = 1 # Write the network every 20 iterations -print_progress = false # Print the iteration time ; if not set, then always print +print_progress = true # Print the iteration time ; if not set, then always print [model] max_iterations = 1000 # If not set, max iterations is infinite @@ -25,6 +25,7 @@ bot_present = true n_bots = 2 bot_m = [300, 300] bot_activity = [1.0, 1.0] +#bot_homophily = [0.0, 0.0] bot_opinion = [-2, 2] [network] diff --git a/include/models/ActivityDrivenModel.hpp b/include/models/ActivityDrivenModel.hpp index 00d9465..7b42d4f 100644 --- a/include/models/ActivityDrivenModel.hpp +++ b/include/models/ActivityDrivenModel.hpp @@ -98,11 +98,12 @@ class ActivityAgentModel : public Model> double convergence_tol = 1e-12; // TODO: ?? // bot @TODO: less hacky - bool bot_present = false; - size_t n_bots = 0; // The first n_bots agents are bots - std::vector bot_m = std::vector( 0 ); - std::vector bot_activity = std::vector( 0 ); - std::vector bot_opinion = std::vector( 0 ); + bool bot_present = false; + size_t n_bots = 0; // The first n_bots agents are bots + std::vector bot_m = std::vector( 0 ); + std::vector bot_activity = std::vector( 0 ); + std::vector bot_opinion = std::vector( 0 ); + std::vector bot_homophily = std::vector( 0 ); ActivityAgentModel( int n_agents, Network & network, std::mt19937 & gen ); diff --git a/src/models/ActivityDrivenModel.cpp b/src/models/ActivityDrivenModel.cpp index faeaa78..42fdc74 100644 --- a/src/models/ActivityDrivenModel.cpp +++ b/src/models/ActivityDrivenModel.cpp @@ -64,10 +64,16 @@ void Seldon::ActivityAgentModel::update_network_probabilistic() // Not normalised since this is taken care of by the reservoir sampling auto weight_callback = [idx_agent, this]( size_t j ) { + double homophily = this->homophily; + + if( bot_present && idx_agent < n_bots ) + { + homophily = this->bot_homophily[idx_agent]; + } if( idx_agent == j ) // The agent does not contact itself return 0.0; return std::pow( - std::abs( this->agents[idx_agent].data.opinion - this->agents[j].data.opinion ), -this->homophily ); + std::abs( this->agents[idx_agent].data.opinion - this->agents[j].data.opinion ), -homophily ); }; int m_temp = this->m; @@ -143,11 +149,18 @@ void Seldon::ActivityAgentModel::update_network_mean() // Not normalised since this is taken care of by the reservoir sampling auto weight_callback = [idx_agent, this]( size_t j ) { + double homophily = this->homophily; + + if( bot_present && idx_agent < n_bots ) + { + homophily = this->bot_homophily[idx_agent]; + } + constexpr double tolerance = 1e-16; auto opinion_diff = std::abs( this->agents[idx_agent].data.opinion - this->agents[j].data.opinion ); if( opinion_diff < tolerance ) return 0.0; - return std::pow( opinion_diff, -this->homophily ); + return std::pow( opinion_diff, -homophily ); }; double normalization = 0; diff --git a/src/simulation.cpp b/src/simulation.cpp index 5776a81..36811f7 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -135,20 +135,24 @@ Seldon::Simulation::Simulation( fmt::print( "Using {} bots\n", model_activityDriven->n_bots ); - auto bot_opinion = tbl["ActivityDriven"]["bot_opinion"]; - auto bot_m = tbl["ActivityDriven"]["bot_m"]; - auto bot_activity = tbl["ActivityDriven"]["bot_activity"]; + auto bot_opinion = tbl["ActivityDriven"]["bot_opinion"]; + auto bot_m = tbl["ActivityDriven"]["bot_m"]; + auto bot_activity = tbl["ActivityDriven"]["bot_activity"]; + auto bot_homophily = tbl["ActivityDriven"]["bot_homophily"]; for( size_t i = 0; i < model_activityDriven->n_bots; i++ ) { model_activityDriven->bot_opinion.push_back( bot_opinion[i].value_or( 0.0 ) ); - model_activityDriven->bot_m.push_back( bot_m[i].value_or( 0 ) ); + model_activityDriven->bot_m.push_back( bot_m[i].value_or( size_t( model_activityDriven->m ) ) ); model_activityDriven->bot_activity.push_back( bot_activity[i].value_or( 0.0 ) ); + model_activityDriven->bot_homophily.push_back( + bot_homophily[i].value_or( double( model_activityDriven->homophily ) ) ); } fmt::print( "Bot opinions {}\n", model_activityDriven->bot_opinion ); fmt::print( "Bot m {}\n", model_activityDriven->bot_m ); - fmt::print( "Bot activities {}\n", model_activityDriven->bot_activity ); + fmt::print( "Bot activity(s) {}\n", model_activityDriven->bot_activity ); + fmt::print( "Bot homophily(s) {}\n", model_activityDriven->bot_homophily ); } model_activityDriven->get_agents_from_power_law(); From b48001f54871c53e208f45c91acd8a6230e2d572 Mon Sep 17 00:00:00 2001 From: Moritz Sallermann Date: Sat, 9 Mar 2024 18:20:57 +0000 Subject: [PATCH 2/4] ActivityDrivenModel: got rid of bot_present member and made it a function. Also added some input verification for bots and more comments in the example conf file Co-authored-by: Amrita Goswami --- examples/ActivityDrivenBot/conf.toml | 19 ++++++++++--------- include/models/ActivityDrivenModel.hpp | 7 ++++++- src/models/ActivityDrivenModel.cpp | 12 ++++++------ src/simulation.cpp | 21 ++++++++++++++++----- 4 files changed, 38 insertions(+), 21 deletions(-) diff --git a/examples/ActivityDrivenBot/conf.toml b/examples/ActivityDrivenBot/conf.toml index c9d8d67..ee0d07f 100644 --- a/examples/ActivityDrivenBot/conf.toml +++ b/examples/ActivityDrivenBot/conf.toml @@ -4,7 +4,7 @@ rng_seed = 120 # Leaving this empty will pick a random seed [io] n_output_network = 1 # Write the network every 20 iterations -print_progress = true # Print the iteration time ; if not set, then always print +print_progress = true # Print the iteration time; if not set, then always print [model] max_iterations = 1000 # If not set, max iterations is infinite @@ -16,17 +16,18 @@ eps = 0.01 # Minimum activity epsilon; a_i belongs to [epsilon,1] gamma = 2.1 # Exponent of activity power law distribution of activities reciprocity = 0.5 # probability that when agent i contacts j via weighted reservoir sampling, j also sends feedback to i. So every agent can have more than m incoming connections homophily = 3.0 # aka beta. if zero, agents pick their interaction partners at random -alpha = 0.25 # Controversialness of the issue, must be greater than 0. +alpha = 0.25 # Controversialness of the issue, must be greater than 0. K = 3.0 # Social interaction strength mean_activities = false # Use the mean value of the powerlaw distribution for the activities of all agents -mean_weights = false +mean_weights = false -bot_present = true -n_bots = 2 -bot_m = [300, 300] -bot_activity = [1.0, 1.0] -#bot_homophily = [0.0, 0.0] -bot_opinion = [-2, 2] +n_bots = 2 # The number of bots to be used; if not specified defaults to 0 (which means bots are deactivated) +# Bots are agents with fixed opinions and different parameters, the parameters are specified in the following lists +# If n_bots is smaller than the length of any of the lists, the first n_bots entries are used. If n_bots is greater the code will throw an exception. +bot_m = [300, 300] # If not specified, defaults to `m` +bot_homophily = [0.7, 0.2] # If not specified, defaults to `homophily` +bot_activity = [1.0, 1.0] # If not specified, defaults to 0 +bot_opinion = [-2, 2] # The fixed opinions of the bots [network] number_of_agents = 1000 diff --git a/include/models/ActivityDrivenModel.hpp b/include/models/ActivityDrivenModel.hpp index 7b42d4f..e1be158 100644 --- a/include/models/ActivityDrivenModel.hpp +++ b/include/models/ActivityDrivenModel.hpp @@ -98,13 +98,18 @@ class ActivityAgentModel : public Model> double convergence_tol = 1e-12; // TODO: ?? // bot @TODO: less hacky - bool bot_present = false; + size_t n_bots = 0; // The first n_bots agents are bots std::vector bot_m = std::vector( 0 ); std::vector bot_activity = std::vector( 0 ); std::vector bot_opinion = std::vector( 0 ); std::vector bot_homophily = std::vector( 0 ); + [[nodiscard]] bool bot_present() const + { + return n_bots > 0; + } + ActivityAgentModel( int n_agents, Network & network, std::mt19937 & gen ); void get_agents_from_power_law(); // This needs to be called after eps and gamma have been set diff --git a/src/models/ActivityDrivenModel.cpp b/src/models/ActivityDrivenModel.cpp index 42fdc74..56a2402 100644 --- a/src/models/ActivityDrivenModel.cpp +++ b/src/models/ActivityDrivenModel.cpp @@ -35,7 +35,7 @@ void Seldon::ActivityAgentModel::get_agents_from_power_law() } } - if( bot_present ) + if( bot_present() ) { for( size_t bot_idx = 0; bot_idx < n_bots; bot_idx++ ) { @@ -66,7 +66,7 @@ void Seldon::ActivityAgentModel::update_network_probabilistic() { double homophily = this->homophily; - if( bot_present && idx_agent < n_bots ) + if( bot_present() && idx_agent < n_bots ) { homophily = this->bot_homophily[idx_agent]; } @@ -78,7 +78,7 @@ void Seldon::ActivityAgentModel::update_network_probabilistic() int m_temp = this->m; - if( bot_present && idx_agent < n_bots ) + if( bot_present() && idx_agent < n_bots ) { m_temp = bot_m[idx_agent]; } @@ -151,7 +151,7 @@ void Seldon::ActivityAgentModel::update_network_mean() { double homophily = this->homophily; - if( bot_present && idx_agent < n_bots ) + if( bot_present() && idx_agent < n_bots ) { homophily = this->bot_homophily[idx_agent]; } @@ -173,7 +173,7 @@ void Seldon::ActivityAgentModel::update_network_mean() // Calculate the probability of i contacting j (in 1 to m rounds, assuming // the agent is activated int m_temp = m; - if( bot_present && idx_agent < n_bots ) + if( bot_present() && idx_agent < n_bots ) { m_temp = bot_m[idx_agent]; } @@ -249,7 +249,7 @@ void Seldon::ActivityAgentModel::iteration() / 6.0; } - if( bot_present ) + if( bot_present() ) { for( size_t bot_idx = 0; bot_idx < n_bots; bot_idx++ ) { diff --git a/src/simulation.cpp b/src/simulation.cpp index 36811f7..4c7cf81 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -127,12 +127,9 @@ Seldon::Simulation::Simulation( model_activityDriven->max_iterations = max_iterations; // bot - model_activityDriven->bot_present = tbl["ActivityDriven"]["bot_present"].value_or( false ); - - if( model_activityDriven->bot_present ) + model_activityDriven->n_bots = tbl["ActivityDriven"]["n_bots"].value_or( 0 ); + if( model_activityDriven->bot_present() ) { - model_activityDriven->n_bots = tbl["ActivityDriven"]["n_bots"].value_or( 0 ); - fmt::print( "Using {} bots\n", model_activityDriven->n_bots ); auto bot_opinion = tbl["ActivityDriven"]["bot_opinion"]; @@ -140,6 +137,20 @@ Seldon::Simulation::Simulation( auto bot_activity = tbl["ActivityDriven"]["bot_activity"]; auto bot_homophily = tbl["ActivityDriven"]["bot_homophily"]; + auto n_bots = model_activityDriven->n_bots; + + if( + // clang-format off + n_bots > bot_opinion.as_array()->size() + || n_bots > bot_m.as_array()->size() + || n_bots > bot_homophily.as_array()->size() + || n_bots > bot_activity.as_array()->size() + // clang-format on + ) + { + throw std::runtime_error( "One of the bot parameter arrays is smaller than n_bots" ); + } + for( size_t i = 0; i < model_activityDriven->n_bots; i++ ) { model_activityDriven->bot_opinion.push_back( bot_opinion[i].value_or( 0.0 ) ); From 5bda02c29d7ac725f1fcc84a8efe6ac2419392d5 Mon Sep 17 00:00:00 2001 From: Moritz Sallermann Date: Sat, 9 Mar 2024 18:58:59 +0000 Subject: [PATCH 3/4] Fixed an issue, where the code would segfault due --- src/simulation.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/simulation.cpp b/src/simulation.cpp index 4c7cf81..bc18674 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -12,6 +12,16 @@ #include #include +template +bool check_bot_param_size( size_t n_bots, auto & arr ) +{ + if( arr.template value() ) + { + return n_bots > arr.as_array()->size(); + } + return false; +} + Seldon::Simulation::Simulation( const std::string & config_file, const std::optional & cli_network_file, const std::optional & cli_agent_file ) @@ -141,10 +151,10 @@ Seldon::Simulation::Simulation( if( // clang-format off - n_bots > bot_opinion.as_array()->size() - || n_bots > bot_m.as_array()->size() - || n_bots > bot_homophily.as_array()->size() - || n_bots > bot_activity.as_array()->size() + check_bot_param_size(n_bots, bot_opinion) + || check_bot_param_size(n_bots, bot_m) + || check_bot_param_size(n_bots, bot_activity) + || check_bot_param_size(n_bots, bot_homophily) // clang-format on ) { From 9b26bf53418c0f9fa213fe6a3b2476797aa95092 Mon Sep 17 00:00:00 2001 From: Moritz Sallermann Date: Sat, 9 Mar 2024 19:10:51 +0000 Subject: [PATCH 4/4] ActivityDrivenModel: Fixed an issue where bot_m=0 would lead to a segfault --- include/util/math.hpp | 3 +++ src/models/ActivityDrivenModel.cpp | 9 +++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/util/math.hpp b/include/util/math.hpp index c6dfaf7..dcd0690 100644 --- a/include/util/math.hpp +++ b/include/util/math.hpp @@ -59,6 +59,9 @@ template void reservoir_sampling_A_ExpJ( size_t k, size_t n, WeightCallbackT weight, std::vector & buffer, std::mt19937 & mt ) { + if( k == 0 ) + return; + std::uniform_real_distribution distribution( 0.0, 1.0 ); std::vector reservoir( k ); diff --git a/src/models/ActivityDrivenModel.cpp b/src/models/ActivityDrivenModel.cpp index 56a2402..ef8631a 100644 --- a/src/models/ActivityDrivenModel.cpp +++ b/src/models/ActivityDrivenModel.cpp @@ -62,8 +62,7 @@ void Seldon::ActivityAgentModel::update_network_probabilistic() { // Implement the weight for the probability of agent `idx_agent` contacting agent `j` // Not normalised since this is taken care of by the reservoir sampling - auto weight_callback = [idx_agent, this]( size_t j ) - { + auto weight_callback = [idx_agent, this]( size_t j ) { double homophily = this->homophily; if( bot_present() && idx_agent < n_bots ) @@ -135,8 +134,7 @@ void Seldon::ActivityAgentModel::update_network_mean() contact_prob_list[idx_agent] = weights; // set to zero } - auto probability_helper = []( double omega, size_t m ) - { + auto probability_helper = []( double omega, size_t m ) { double p = 0; for( size_t i = 1; i <= m; i++ ) p += ( std::pow( -omega, i + 1 ) + omega ) / ( omega + 1 ); @@ -147,8 +145,7 @@ void Seldon::ActivityAgentModel::update_network_mean() { // Implement the weight for the probability of agent `idx_agent` contacting agent `j` // Not normalised since this is taken care of by the reservoir sampling - auto weight_callback = [idx_agent, this]( size_t j ) - { + auto weight_callback = [idx_agent, this]( size_t j ) { double homophily = this->homophily; if( bot_present() && idx_agent < n_bots )