Skip to content

Commit

Permalink
Merge pull request #20 from seldon-code/unit_tests
Browse files Browse the repository at this point in the history
Unit tests
  • Loading branch information
MSallermann authored Mar 13, 2024
2 parents 4501c1d + b086caf commit fd1f00f
Show file tree
Hide file tree
Showing 14 changed files with 298 additions and 55 deletions.
1 change: 1 addition & 0 deletions examples/ActivityDriven/conf.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ model = "ActivityDriven"

[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]
Expand Down
1 change: 1 addition & 0 deletions examples/ActivityDrivenBot/conf.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,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 # Write the opinions of agents after every iteration
print_progress = true # Print the iteration time; if not set, then always print

[model]
Expand Down
1 change: 1 addition & 0 deletions examples/ActivityDrivenMeanField/conf.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ rng_seed = 12345678 # 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]
Expand Down
2 changes: 2 additions & 0 deletions examples/DeGroot/conf.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ model = "DeGroot"
# 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 = false # Print the iteration time ; if not set, then always prints

[model]
Expand Down
11 changes: 9 additions & 2 deletions include/model_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,15 @@ class ModelBase
public:
int n_iterations = 0;
virtual AgentBase * get_agent( int idx ) = 0; // Use this to get an abstract representation of the agent at idx
virtual void iteration() = 0;
virtual bool finished() = 0;

template<typename AgentT>
AgentT * get_agent_as( int idx )
{
return dynamic_cast<AgentT *>( get_agent( idx ) );
}

virtual void iteration() = 0;
virtual bool finished() = 0;
virtual void agents_from_file( const std::string & file ) = 0;
virtual ~ModelBase() = default;
};
Expand Down
1 change: 1 addition & 0 deletions include/models/ActivityDrivenModel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ inline void Agent<ActivityAgentData>::from_string( const std::string & str )

class ActivityAgentModel : public Model<Agent<ActivityAgentData>>
{
public:
using AgentT = Agent<ActivityAgentData>;

private:
Expand Down
3 changes: 3 additions & 0 deletions include/simulation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
#include "config_parser.hpp"
#include "model_base.hpp"
#include "network.hpp"
#include <filesystem>
#include <memory>
#include <optional>
#include <string>
namespace fs = std::filesystem;

namespace Seldon
{
Expand All @@ -23,6 +25,7 @@ class Simulation
Simulation(
const Config::SimulationOptions & options, const std::optional<std::string> & cli_network_file,
const std::optional<std::string> & cli_agent_file );
void run( fs::path output_dir_path );
};

} // namespace Seldon
3 changes: 2 additions & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ exe = executable('seldon', sources_seldon + 'src/main.cpp',
tests = [
['Test Tarjan', 'test/test_tarjan.cpp'],
['Test DeGroot', 'test/test_deGroot.cpp'],
['Test Activity Driven', 'test/test_activity.cpp'],
['Test Network', 'test/test_network.cpp'],
['Test Network Generation', 'test/test_network_generation.cpp'],
['Test Sampling', 'test/test_sampling.cpp'],
Expand All @@ -39,5 +40,5 @@ foreach t : tests
dependencies : [dependency('fmt'), dependency('tomlplusplus'), Catch2],
include_directories : incdir
)
test(t.get(0), exe)
test(t.get(0), exe, workdir : meson.project_source_root())
endforeach
54 changes: 2 additions & 52 deletions src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
#include "config_parser.hpp"
#include "models/DeGroot.hpp"
#include "simulation.hpp"
#include <fmt/chrono.h>
#include <fmt/format.h>
#include <fmt/ostream.h>
#include <argparse/argparse.hpp>
#include <chrono>
#include <filesystem>
#include <string>
#include <util/io.hpp>
Expand Down Expand Up @@ -66,56 +64,8 @@ int main( int argc, char * argv[] )
auto filename = fmt::format( "opinions_{}.txt", 0 );
Seldon::IO::opinions_to_file( simulation, ( output_dir_path / fs::path( filename ) ).string() );

const std::optional<size_t> n_output_agents = simulation.output_settings.n_output_agents;
const std::optional<size_t> n_output_network = simulation.output_settings.n_output_network;

fmt::print( "-----------------------------------------------------------------\n" );
fmt::print( "Starting simulation\n" );
fmt::print( "-----------------------------------------------------------------\n" );

typedef std::chrono::milliseconds ms;
auto t_simulation_start = std::chrono::high_resolution_clock::now();
do
{
auto t_iter_start = std::chrono::high_resolution_clock::now();

simulation.model->iteration();

auto t_iter_end = std::chrono::high_resolution_clock::now();
auto iter_time = std::chrono::duration_cast<ms>( t_iter_end - t_iter_start );

// Print the iteration time?
if( simulation.output_settings.print_progress )
{
fmt::print(
"Iteration {} iter_time = {:%Hh %Mm %Ss} \n", simulation.model->n_iterations,
std::chrono::floor<ms>( iter_time ) );
}

// Write out the opinion?
if( n_output_agents.has_value() && ( simulation.model->n_iterations % n_output_agents.value() == 0 ) )
{
filename = fmt::format( "opinions_{}.txt", simulation.model->n_iterations );
Seldon::IO::opinions_to_file( simulation, ( output_dir_path / fs::path( filename ) ).string() );
}

// Write out the network?
if( n_output_network.has_value() && ( simulation.model->n_iterations % n_output_network.value() == 0 ) )
{
filename = fmt::format( "network_{}.txt", simulation.model->n_iterations );
Seldon::IO::network_to_file( simulation, ( output_dir_path / fs::path( filename ) ).string() );
}

} while( !simulation.model->finished() );

auto t_simulation_end = std::chrono::high_resolution_clock::now();
auto total_time = std::chrono::duration_cast<ms>( t_simulation_end - t_simulation_start );

fmt::print( "-----------------------------------------------------------------\n" );
fmt::print(
"Finished after {} iterations, total time = {:%Hh %Mm %Ss}\n", simulation.model->n_iterations,
std::chrono::floor<ms>( total_time ) );
fmt::print( "=================================================================\n" );
// Perform the iterations using the model
simulation.run( output_dir_path );

return 0;
}
56 changes: 56 additions & 0 deletions src/simulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@
#include "models/DeGroot.hpp"
#include "network_generation.hpp"
#include "util/tomlplusplus.hpp"
#include <fmt/chrono.h>
#include <fmt/format.h>
#include <fmt/ostream.h>
#include <fmt/ranges.h>
#include <chrono>
#include <cstddef>
#include <iostream>
#include <optional>
#include <set>
#include <stdexcept>
#include <util/io.hpp>

Seldon::Simulation::Simulation(
const Config::SimulationOptions & options, const std::optional<std::string> & cli_network_file,
Expand Down Expand Up @@ -97,4 +100,57 @@ Seldon::Simulation::Simulation(
{
model->agents_from_file( cli_agent_file.value() );
}
}

void Seldon::Simulation::run( fs::path output_dir_path )
{
auto n_output_agents = this->output_settings.n_output_agents;
auto n_output_network = this->output_settings.n_output_network;

fmt::print( "-----------------------------------------------------------------\n" );
fmt::print( "Starting simulation\n" );
fmt::print( "-----------------------------------------------------------------\n" );

typedef std::chrono::milliseconds ms;
auto t_simulation_start = std::chrono::high_resolution_clock::now();
while( !this->model->finished() )
{
auto t_iter_start = std::chrono::high_resolution_clock::now();

this->model->iteration();

auto t_iter_end = std::chrono::high_resolution_clock::now();
auto iter_time = std::chrono::duration_cast<ms>( t_iter_end - t_iter_start );

// Print the iteration time?
if( this->output_settings.print_progress )
{
fmt::print(
"Iteration {} iter_time = {:%Hh %Mm %Ss} \n", this->model->n_iterations,
std::chrono::floor<ms>( iter_time ) );
}

// Write out the opinion?
if( n_output_agents.has_value() && ( this->model->n_iterations % n_output_agents.value() == 0 ) )
{
auto filename = fmt::format( "opinions_{}.txt", this->model->n_iterations );
Seldon::IO::opinions_to_file( *this, ( output_dir_path / fs::path( filename ) ).string() );
}

// Write out the network?
if( n_output_network.has_value() && ( this->model->n_iterations % n_output_network.value() == 0 ) )
{
auto filename = fmt::format( "network_{}.txt", this->model->n_iterations );
Seldon::IO::network_to_file( *this, ( output_dir_path / fs::path( filename ) ).string() );
}
}

auto t_simulation_end = std::chrono::high_resolution_clock::now();
auto total_time = std::chrono::duration_cast<ms>( t_simulation_end - t_simulation_start );

fmt::print( "-----------------------------------------------------------------\n" );
fmt::print(
"Finished after {} iterations, total time = {:%Hh %Mm %Ss}\n", this->model->n_iterations,
std::chrono::floor<ms>( total_time ) );
fmt::print( "=================================================================\n" );
}
35 changes: 35 additions & 0 deletions test/res/1bot_1agent_activity_prob.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[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 = true # Print the iteration time ; if not set, then always print

[model]
max_iterations = 1000 # If not set, max iterations is infinite

[ActivityDriven]
dt = 0.001 # Timestep for the integration of the coupled ODEs
m = 1 # Number of agents contacted, when the agent is active
eps = 1 # Minimum activity epsilon; a_i belongs to [epsilon,1]
gamma = 2.1 # Exponent of activity power law distribution of activities
reciprocity = 1 # 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 = 1.5 # Controversialness of the issue, must be greater than 0.
K = 2.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

n_bots = 1 # 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 = [1] # If not specified, defaults to `m`
bot_homophily = [0.7] # If not specified, defaults to `homophily`
bot_activity = [1.0] # If not specified, defaults to 0
bot_opinion = [2] # The fixed opinions of the bots

[network]
number_of_agents = 2
connections_per_agent = 1
27 changes: 27 additions & 0 deletions test/res/2_agents_activity_prob.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 = true # Print the iteration time ; if not set, then always print

[model]
max_iterations = 10000 # If not set, max iterations is infinite

[ActivityDriven]
dt = 0.005 # Timestep for the integration of the coupled ODEs
m = 1 # Number of agents contacted, when the agent is active
eps = 1 # Minimum activity epsilon; a_i belongs to [epsilon,1]
gamma = 2.1 # Exponent of activity power law distribution of activities
reciprocity = 1 # 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 = 1.01 # Controversialness of the issue, must be greater than 0.
K = 2.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

[network]
number_of_agents = 2
connections_per_agent = 1
27 changes: 27 additions & 0 deletions test/res/activity_probabilistic_conf.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 = true # Print the iteration time ; if not set, then always print

[model]
max_iterations = 10 # 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

[network]
number_of_agents = 1000
connections_per_agent = 10
Loading

0 comments on commit fd1f00f

Please sign in to comment.