Skip to content

Commit

Permalink
Merge pull request #23 from seldon-code/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
MSallermann authored Mar 14, 2024
2 parents 76b7f44 + 3f1b7c5 commit d79eb37
Show file tree
Hide file tree
Showing 28 changed files with 967 additions and 304 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
50 changes: 50 additions & 0 deletions include/agent_generation.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#pragma once
#include "util/misc.hpp"
#include <cstddef>
#include <vector>

namespace Seldon::AgentGeneration
{

template<typename AgentT>
std::vector<AgentT> generate_from_file( const std::string & file )
{
std::vector<AgentT> agents{};

std::string file_contents = get_file_contents( file );
bool finished = false;
size_t start_of_line = 0;

while( !finished )
{
// Find the end of the current line
auto end_of_line = file_contents.find( '\n', start_of_line );
if( end_of_line == std::string::npos )
{
finished = true;
}
// Get the current line as a substring
auto line = file_contents.substr( start_of_line, end_of_line - start_of_line );
start_of_line = end_of_line + 1;
// TODO: check if empty or comment
if( line.empty() )
{
break;
}
if( line[0] == '#' )
{
continue;
}

agents.push_back( AgentT() );

// First column is the index of the agent
auto end_of_first_column = line.find( ',', 0 );
auto opinion_substring = line.substr( end_of_first_column + 1, end_of_line );
agents.back().from_string( opinion_substring );
}

return agents;
}

} // namespace Seldon::AgentGeneration
84 changes: 84 additions & 0 deletions include/config_parser.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#pragma once
#include <fmt/format.h>
#include <fmt/ostream.h>
#include <fmt/ranges.h>
#include <optional>
#include <random>
#include <string_view>
#include <variant>
#include <vector>

namespace Seldon::Config
{

/*
As a convention: When a setting has type std::optional,
nullopt signifies to not perform the operation associated
with that setting. Also, the default value of any optional should
be nullopt.
If you feel tempted to make an optional setting with a different default,
just make the setting not optional.
*/

enum class Model
{
DeGroot,
ActivityDrivenModel
};

struct OutputSettings
{
// Write out the agents/network every n iterations, nullopt means never
std::optional<size_t> n_output_agents = std::nullopt;
std::optional<size_t> n_output_network = std::nullopt;
bool print_progress = true; // Print the iteration time, by default always prints
};

struct DeGrootSettings
{
std::optional<int> max_iterations = std::nullopt;
double convergence_tol;
};

struct ActivityDrivenSettings
{
std::optional<int> max_iterations = std::nullopt;
double dt = 0.01;
int m = 10;
double eps = 0.01;
double gamma = 2.1;
double alpha = 3.0;
double homophily = 0.5;
double reciprocity = 0.5;
double K = 3.0;
bool mean_activities = false;
bool mean_weights = false;
size_t n_bots = 0; //@TODO why is this here?
std::vector<int> bot_m = std::vector<int>( 0 );
std::vector<double> bot_activity = std::vector<double>( 0 );
std::vector<double> bot_opinion = std::vector<double>( 0 );
std::vector<double> bot_homophily = std::vector<double>( 0 );
};

struct InitialNetworkSettings
{
std::optional<std::string> file;
size_t n_agents = 200;
size_t n_connections = 10;
};

struct SimulationOptions
{
Model model;
std::string model_string;
int rng_seed = std::random_device()();
OutputSettings output_settings;
std::variant<DeGrootSettings, ActivityDrivenSettings> model_settings;
InitialNetworkSettings network_settings;
};

SimulationOptions parse_config_file( std::string_view config_file_path );
void validate_settings( const SimulationOptions & options );
void print_settings( const SimulationOptions & options );

} // namespace Seldon::Config
39 changes: 0 additions & 39 deletions include/model.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#pragma once
#include "model_base.hpp"
#include "util/misc.hpp"
#include <optional>

namespace Seldon
Expand All @@ -19,44 +18,6 @@ class Model : public ModelBase
Model( size_t n_agents ) : agents( std::vector<AgentT>( int( n_agents ), AgentT() ) ) {}
Model( std::vector<AgentT> && agents ) : agents( agents ) {}

void agents_from_file( const std::string & file ) override
{
agents.clear();

std::string file_contents = get_file_contents( file );
bool finished = false;
size_t start_of_line = 0;

while( !finished )
{
// Find the end of the current line
auto end_of_line = file_contents.find( '\n', start_of_line );
if( end_of_line == std::string::npos )
{
finished = true;
}
// Get the current line as a substring
auto line = file_contents.substr( start_of_line, end_of_line - start_of_line );
start_of_line = end_of_line + 1;
// TODO: check if empty or comment
if( line.empty() )
{
break;
}
if( line[0] == '#' )
{
continue;
}

agents.push_back( AgentT() );

// First column is the index of the agent
auto end_of_first_column = line.find( ',', 0 );
auto opinion_substring = line.substr( end_of_first_column + 1, end_of_line );
agents.back().from_string( opinion_substring );
}
}

void iteration() override
{
n_iterations++;
Expand Down
14 changes: 10 additions & 4 deletions include/model_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,16 @@ 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;
virtual void agents_from_file( const std::string & file ) = 0;
virtual ~ModelBase() = default;

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 ~ModelBase() = default;
};

} // namespace Seldon
3 changes: 3 additions & 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 Expand Up @@ -74,6 +75,8 @@ class ActivityAgentModel : public Model<Agent<ActivityAgentData>>
}
}

// The weight for contact between two agents
double homophily_weight( size_t idx_contacter, size_t idx_contacted );
void update_network_probabilistic();
void update_network_mean();
void update_network();
Expand Down
4 changes: 2 additions & 2 deletions include/network_generation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include <memory>
#include <random>

namespace Seldon
namespace Seldon::NetworkGeneration
{
/* Constructs a new network with n_connections per agent
If self_interaction=true, a connection of the agent with itself is included, which is *not* counted in n_connections
Expand All @@ -14,4 +14,4 @@ generate_n_connections( size_t n_agents, size_t n_connections, bool self_interac
std::unique_ptr<Network> generate_fully_connected( size_t n_agents, Network::WeightT weight = 0.0 );
std::unique_ptr<Network> generate_fully_connected( size_t n_agents, std::mt19937 & gen );
std::unique_ptr<Network> generate_from_file( const std::string & file );
} // namespace Seldon
} // namespace Seldon::NetworkGeneration
17 changes: 6 additions & 11 deletions include/simulation.hpp
Original file line number Diff line number Diff line change
@@ -1,36 +1,31 @@
#pragma once

#include "config_parser.hpp"
#include "model_base.hpp"
#include "network.hpp"
#include <filesystem>
#include <memory>
#include <optional>
#include <random>
#include <string>
namespace fs = std::filesystem;

namespace Seldon
{

class Simulation
{
struct OutputSettings
{
// Write out the agents/network every n iterations, nullopt means never
std::optional<size_t> n_output_agents = 1;
std::optional<size_t> n_output_network = std::nullopt;
bool print_progress = true; // Print the iteration time, by default always prints
};

private:
std::mt19937 gen;

public:
int n_agents;
std::unique_ptr<ModelBase> model;
std::unique_ptr<Network> network;
OutputSettings output_settings;
Config::OutputSettings output_settings;
Simulation(
const std::string & config_file, const std::optional<std::string> & cli_network_file,
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
12 changes: 11 additions & 1 deletion include/util/misc.hpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
#pragma once
#include "fmt/format.h"
#include <fmt/ostream.h>
#include <filesystem>
#include <fstream>
#include <stdexcept>
#include <string>

namespace Seldon
{

inline std::string get_file_contents( const std::string & filename )
{
auto path = std::filesystem::path( filename );
if( !std::filesystem::exists( path ) )
{
throw std::runtime_error( fmt::format( "Canot read from {}. File does not exist!", fmt::streamed( path ) ) );
}

std::ifstream in( filename, std::ios::in | std::ios::binary );
if( in )
{
Expand All @@ -18,7 +28,7 @@ inline std::string get_file_contents( const std::string & filename )
in.close();
return ( contents );
}
throw( std::runtime_error( "Cannot read in file." ) );
throw( std::runtime_error( "Cannot read file." ) );
}

} // namespace Seldon
17 changes: 10 additions & 7 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ sources_seldon = [
'src/simulation.cpp',
'src/network.cpp',
'src/network_generation.cpp',
'src/config_parser.cpp',
'src/model.cpp',
'src/models/DeGroot.cpp',
'src/models/ActivityDrivenModel.cpp',
'src/util/tomlplusplus.cpp'
'src/util/tomlplusplus.cpp',
]

exe = executable('seldon', sources_seldon + 'src/main.cpp',
Expand All @@ -24,11 +25,13 @@ exe = executable('seldon', sources_seldon + 'src/main.cpp',


tests = [
['Test Tarjan', 'test/test_tarjan.cpp'],
['Test DeGroot', 'test/test_deGroot.cpp'],
['Test Network', 'test/test_network.cpp'],
['Test Network Generation', 'test/test_network_generation.cpp'],
['Test Sampling', 'test/test_sampling.cpp'],
['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'],
['Test_IO', 'test/test_io.cpp'],
]

Catch2 = dependency('Catch2', method : 'cmake', modules : ['Catch2::Catch2WithMain', 'Catch2::Catch2'])
Expand All @@ -38,5 +41,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
Loading

0 comments on commit d79eb37

Please sign in to comment.