Skip to content

Commit

Permalink
Unit test for MF AD model
Browse files Browse the repository at this point in the history
Co-authored-by: Amrita Goswami <[email protected]>
  • Loading branch information
MSallermann and amritagos committed Mar 14, 2024
1 parent cf80425 commit da321af
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 3 deletions.
27 changes: 27 additions & 0 deletions test/res/10_agents_meanfield_activity.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[simulation]
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_agents = 1
print_progress = false # Print the iteration time ; if not set, then always print

[model]
max_iterations = 1000 # 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 = 1.0 # 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.0 # aka beta. if zero, agents pick their interaction partners at random
alpha = 1.01 # Controversialness of the issue, must be greater than 0.
K = 2.0 # Social interaction strength
mean_activities = true # Use the mean value of the powerlaw distribution for the activities of all agents
mean_weights = true # Use the meanfield approximation of the network edges

[network]
number_of_agents = 100
connections_per_agent = 1
2 changes: 1 addition & 1 deletion test/res/1bot_1agent_activity_prob.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ rng_seed = 120 # Leaving this empty will pick a random seed
[io]
# n_output_network = 1 # Write the network every 20 iterations
# n_output_agents = 1
print_progress = true # Print the iteration time ; if not set, then always print
print_progress = false # Print the iteration time ; if not set, then always print

[model]
max_iterations = 1000 # If not set, max iterations is infinite
Expand Down
2 changes: 1 addition & 1 deletion test/res/2_agents_activity_prob.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ rng_seed = 120 # Leaving this empty will pick a random seed
[io]
# n_output_network = 1 # Write the network every 20 iterations
# n_output_agents = 1
print_progress = true # Print the iteration time ; if not set, then always print
print_progress = false # Print the iteration time ; if not set, then always print

[model]
max_iterations = 10000 # If not set, max iterations is infinite
Expand Down
2 changes: 1 addition & 1 deletion test/res/activity_probabilistic_conf.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ rng_seed = 120 # Leaving this empty will pick a random seed
[io]
# n_output_network = 1 # Write the network every 20 iterations
n_output_agents = 1
print_progress = true # Print the iteration time ; if not set, then always print
print_progress = false # Print the iteration time ; if not set, then always print

[model]
max_iterations = 10 # If not set, max iterations is infinite
Expand Down
85 changes: 85 additions & 0 deletions test/test_activity.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "catch2/matchers/catch_matchers.hpp"
#include "models/ActivityDrivenModel.hpp"
#include "util/math.hpp"
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>

Expand Down Expand Up @@ -128,4 +129,88 @@ TEST_CASE( "Test the probabilistic activity driven model with one bot and one ag
auto x_t_analytical = ( x_0 - K * tanh( alpha * x_bot ) ) * exp( -time_elapsed ) + K * tanh( alpha * x_bot );

REQUIRE_THAT( x_t, WithinAbs( x_t_analytical, 1e-6 ) );
}

TEST_CASE( "Test the meanfield activity driven model with 10 agents", "[activityMeanfield10Agents]" )
{
using namespace Seldon;
using namespace Catch::Matchers;

auto proj_root_path = fs::current_path();
auto input_file = proj_root_path / fs::path( "test/res/10_agents_meanfield_activity.toml" );

auto options = Config::parse_config_file( input_file.string() );

// Check that all requirements for this test are fulfilled
REQUIRE( options.model == Config::Model::ActivityDrivenModel );

auto & model_settings = std::get<Config::ActivityDrivenSettings>( options.model_settings );
REQUIRE( model_settings.mean_weights );
REQUIRE( model_settings.mean_activities );
// We require zero homophily, since we only know the critical controversialness in that case
REQUIRE_THAT( model_settings.homophily, WithinAbsMatcher( 0, 1e-16 ) );

auto K = model_settings.K;
auto n_agents = options.network_settings.n_agents;
auto reciprocity = model_settings.reciprocity;
auto m = model_settings.m;
auto eps = model_settings.eps;
auto gamma = model_settings.gamma;

auto dist = power_law_distribution( eps, gamma );
auto mean_activity = dist.mean();

// Calculate the 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]

fmt::print( "Set alpha to {}\n", model_settings.alpha );

// Check that all activities are indeed the expected mean activity and set the opinions in a small interval around 0
for( size_t idx_agent = 0; idx_agent < n_agents; idx_agent++ )
{
auto * agent = simulation.model->get_agent_as<ActivityAgentModel::AgentT>( idx_agent );
REQUIRE_THAT( mean_activity, WithinAbs( agent->data.activity, 1e-16 ) );
agent->data.opinion
= -initial_opinion_delta + 2.0 * idx_agent / ( n_agents - 1 ) * ( initial_opinion_delta );
}

// 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 );

// run that mofo
simulation.run( output_dir_path );

// Check the opinions after the run, if alpha is above the critical controversialness,
// the opinions need to deviate from zero
double avg_deviation = 0.0;
for( size_t idx_agent = 0; idx_agent < n_agents; idx_agent++ )
{
auto * agent = simulation.model->get_agent_as<ActivityAgentModel::AgentT>( idx_agent );
if( above_critical_controversialness )
REQUIRE( std::abs( agent->data.opinion ) > std::abs( initial_opinion_delta ) );
else
REQUIRE( std::abs( agent->data.opinion ) < std::abs( initial_opinion_delta ) );

avg_deviation += std::abs( agent->data.opinion );
}

fmt::print( "Average deviation of agents = {}\n", avg_deviation / n_agents );
};

auto alpha_critical
= double( n_agents ) / ( double( n_agents ) - 1.0 ) * 1.0 / ( ( 1.0 + reciprocity ) * K * m * mean_activity );

fmt::print( "Critical controversialness = {}\n", alpha_critical );
double delta_alpha = 0.1;

// Set the critical controversialness to a little above the critical alpha
model_settings.alpha = alpha_critical + delta_alpha;
set_opinions_and_run( true );

// Set the critical controversialness to a little above the critical alpha
model_settings.alpha = alpha_critical - delta_alpha;
set_opinions_and_run( false );
}

0 comments on commit da321af

Please sign in to comment.