diff --git a/examples/ActivityDrivenReluctance/conf.toml b/examples/ActivityDrivenReluctance/conf.toml new file mode 100644 index 0000000..52667af --- /dev/null +++ b/examples/ActivityDrivenReluctance/conf.toml @@ -0,0 +1,33 @@ +[simulation] +model = "ActivityDriven" +# rng_seed = 120 # Leaving this empty will pick a random seed + +[io] +n_output_network = 20 # Write the network every 20 iterations +n_output_agents = 1 # Write the opinions of agents after every iteration +print_progress = true # Print the iteration time ; if not set, then always print + +[model] +max_iterations = 500 # If not set, max iterations is infinite + +[ActivityDriven] +dt = 0.01 # Timestep for the integration of the coupled ODEs +m = 10 # Number of agents contacted, when the agent is active +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 = 0.5 # aka beta. if zero, agents pick their interaction partners at random +alpha = 3.0 # 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 # Use the meanfield approximation of the network edges + +reluctances = true # Assigns a "reluctance" (m_i) to each agent. By default; false and every agent has a reluctance of 1 +reluctance_mean = 1.0 # Mean of distribution before drawing from a truncated normal distribution (default set to 1.0) +reluctance_sigma = 0.25 # Width of normal distribution (before truncating) +reluctance_eps = 0.01 # Minimum such that the normal distribution is truncated at this value +covariance_factor = 0.0 # Determines whether activities and reluctances will be correlated. If set to 0.0 (default) then they will not be correlated. + +[network] +number_of_agents = 1000 +connections_per_agent = 10 diff --git a/include/config_parser.hpp b/include/config_parser.hpp index d2dfefe..c3df298 100644 --- a/include/config_parser.hpp +++ b/include/config_parser.hpp @@ -58,6 +58,11 @@ struct ActivityDrivenSettings std::vector bot_activity = std::vector( 0 ); std::vector bot_opinion = std::vector( 0 ); std::vector bot_homophily = std::vector( 0 ); + bool use_reluctances = false; + double reluctance_mean = 1.0; + double reluctance_sigma = 0.25; + double reluctance_eps = 0.01; + double covariance_factor = 0.0; }; struct InitialNetworkSettings diff --git a/include/simulation.hpp b/include/simulation.hpp index 91c8d17..d74298e 100644 --- a/include/simulation.hpp +++ b/include/simulation.hpp @@ -85,7 +85,8 @@ class Simulation : public SimulationInterface auto degroot_settings = std::get( options.model_settings ); // DeGroot specific parameters - model = [&]() { + model = [&]() + { auto model = std::make_unique( network ); model->max_iterations = degroot_settings.max_iterations; model->convergence_tol = degroot_settings.convergence_tol; @@ -101,7 +102,8 @@ class Simulation : public SimulationInterface { auto activitydriven_settings = std::get( options.model_settings ); - model = [&]() { + model = [&]() + { auto model = std::make_unique( network, gen ); model->dt = activitydriven_settings.dt; model->m = activitydriven_settings.m; @@ -114,6 +116,12 @@ class Simulation : public SimulationInterface model->mean_activities = activitydriven_settings.mean_activities; model->mean_weights = activitydriven_settings.mean_weights; model->max_iterations = activitydriven_settings.max_iterations; + // Reluctance + model->use_reluctances = activitydriven_settings.use_reluctances; + model->reluctance_mean = activitydriven_settings.reluctance_mean; + model->reluctance_sigma = activitydriven_settings.reluctance_sigma; + model->reluctance_eps = activitydriven_settings.reluctance_eps; + model->covariance_factor = activitydriven_settings.covariance_factor; // bot model->n_bots = activitydriven_settings.n_bots; model->bot_opinion = activitydriven_settings.bot_opinion; diff --git a/src/config_parser.cpp b/src/config_parser.cpp index ea79586..564dcf4 100644 --- a/src/config_parser.cpp +++ b/src/config_parser.cpp @@ -70,27 +70,36 @@ SimulationOptions parse_config_file( std::string_view config_file_path ) model_settings.K = tbl["ActivityDriven"]["K"].value_or( 3.0 ); model_settings.mean_activities = tbl["ActivityDriven"]["mean_activities"].value_or( false ); model_settings.mean_weights = tbl["ActivityDriven"]["mean_weights"].value_or( false ); + // Reluctances + model_settings.use_reluctances = tbl["ActivityDriven"]["reluctances"].value_or( false ); + model_settings.reluctance_mean = tbl["ActivityDriven"]["reluctance_mean"].value_or( 1.0 ); + model_settings.reluctance_sigma = tbl["ActivityDriven"]["reluctance_sigma"].value_or( 0.25 ); + model_settings.reluctance_eps = tbl["ActivityDriven"]["reluctance_eps"].value_or( 0.01 ); + model_settings.covariance_factor = tbl["ActivityDriven"]["covariance_factor"].value_or( 0.0 ); model_settings.max_iterations = tbl["model"]["max_iterations"].value(); // bot model_settings.n_bots = tbl["ActivityDriven"]["n_bots"].value_or( 0 ); - auto push_back_bot_array = [&]( auto toml_node, auto & options_array, auto default_value ) { + auto push_back_bot_array = [&]( auto toml_node, auto & options_array, auto default_value ) + { if( toml_node.is_array() ) { toml::array * toml_arr = toml_node.as_array(); - toml_arr->for_each( [&]( auto && elem ) { - if( elem.is_integer() ) - { - options_array.push_back( elem.as_integer()->get() ); - } - else if( elem.is_floating_point() ) + toml_arr->for_each( + [&]( auto && elem ) { - options_array.push_back( elem.as_floating_point()->get() ); - } - } ); + if( elem.is_integer() ) + { + options_array.push_back( elem.as_integer()->get() ); + } + else if( elem.is_floating_point() ) + { + options_array.push_back( elem.as_floating_point()->get() ); + } + } ); } else { @@ -155,7 +164,12 @@ void validate_settings( const SimulationOptions & options ) check( name_and_var( model_settings.alpha ), geq_zero ); // check( name_and_var( model_settings.homophily ), geq_zero ); check( name_and_var( model_settings.reciprocity ), geq_zero ); - + // Reluctance options + check( name_and_var( model_settings.reluctance_mean ), g_zero ); + check( name_and_var( model_settings.reluctance_sigma ), g_zero ); + check( name_and_var( model_settings.reluctance_eps ), g_zero ); + check( name_and_var( model_settings.covariance_factor ), geq_zero ); + // Bot options size_t n_bots = model_settings.n_bots; auto check_bot_size = [&]( auto x ) { return x.size() >= n_bots; }; const std::string bot_msg = "Length needs to be >= n_bots"; @@ -176,7 +190,7 @@ void print_settings( const SimulationOptions & options ) fmt::print( "INFO: Seeding with seed {}\n", options.rng_seed ); fmt::print( "Model type: {}\n", options.model_string ); fmt::print( "Network has {} agents\n", options.network_settings.n_agents ); - + // @TODO: Optionally print *all* settings to the console, including defaults that were set if( options.model == Model::ActivityDrivenModel ) { auto model_settings = std::get( options.model_settings ); diff --git a/src/models/ActivityDrivenModel.cpp b/src/models/ActivityDrivenModel.cpp index b7de1a7..91525de 100644 --- a/src/models/ActivityDrivenModel.cpp +++ b/src/models/ActivityDrivenModel.cpp @@ -155,7 +155,8 @@ void ActivityDrivenModel::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 ); diff --git a/test/res/10_agents_meanfield_activity.toml b/test/res/10_agents_meanfield_activity.toml index 63191c8..66d8463 100644 --- a/test/res/10_agents_meanfield_activity.toml +++ b/test/res/10_agents_meanfield_activity.toml @@ -3,7 +3,7 @@ model = "ActivityDriven" # rng_seed = 120 # Leaving this empty will pick a random seed [io] -n_output_network = 1 # Write the network every 20 iterations +# n_output_network = 1 # Write the network every 20 iterations # n_output_agents = 1 print_progress = false # Print the iteration time ; if not set, then always print diff --git a/test/res/opinions.txt b/test/res/opinions.txt index 6669744..4cede8c 100644 --- a/test/res/opinions.txt +++ b/test/res/opinions.txt @@ -2,5 +2,5 @@ # comment 0, 2.1127107987061544, 0.044554683389757696 1,0.8088982488089491, 0.015813166022685163 -2,-0.8802809369462433 , 0.015863953902810535 +2,-0.8802809369462433 , 0.015863953902810535, 2.3 diff --git a/test/test_activity.cpp b/test/test_activity.cpp index 42b99e3..900de56 100644 --- a/test/test_activity.cpp +++ b/test/test_activity.cpp @@ -167,7 +167,8 @@ TEST_CASE( "Test the meanfield activity driven model with 10 agents", "[activity auto mean_activity = dist.mean(); // Calculate the critical controversialness - auto set_opinions_and_run = [&]( bool above_critical_controversialness ) { + auto set_opinions_and_run = [&]( bool above_critical_controversialness ) + { auto simulation = Simulation( options, std::nullopt, std::nullopt ); auto initial_opinion_delta = 0.1; // Set the initial opinion in the interval [-delta, delta] @@ -184,7 +185,7 @@ TEST_CASE( "Test the meanfield activity driven model with 10 agents", "[activity // We need an output path for Simulation, but we won't write anything out there fs::path output_dir_path = proj_root_path / fs::path( "test/output_meanfield_test" ); - fs::create_directories( output_dir_path ); + // fs::create_directories( output_dir_path ); // run that mofo simulation.run( output_dir_path ); diff --git a/test/test_sampling.cpp b/test/test_sampling.cpp index acf0c25..f5f55ce 100644 --- a/test/test_sampling.cpp +++ b/test/test_sampling.cpp @@ -98,7 +98,8 @@ TEST_CASE( "Testing sampling functions" ) std::vector histogram( n, 0 ); // Count how often each element occurs amongst all samples - auto weight_callback = []( size_t idx ) { + auto weight_callback = []( size_t idx ) + { if( ( idx == ignore_idx ) || ( idx == ignore_idx2 ) ) { return 0.0;