Skip to content

Commit

Permalink
DeffuantVectorModel: Removed hardcoded dim
Browse files Browse the repository at this point in the history
The user can now enter the dimensions for the DeffuantVectorModel.
Additionally, ConfigParser also checks to make sure that the
user-defined dim=1, if the basic Deffuant model is being used. We also
added a test for the DeffuantVectorModel.

Co-authored-by: Moritz Sallermann <[email protected]>
  • Loading branch information
amritagos and MSallermann committed Mar 22, 2024
1 parent 8940c15 commit 514215c
Show file tree
Hide file tree
Showing 11 changed files with 109 additions and 11 deletions.
9 changes: 6 additions & 3 deletions include/config_parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,12 @@ struct DeffuantSettings
{
std::optional<int> max_iterations = std::nullopt;
double homophily_threshold
= 0.2; // d in the paper; agents interact if difference in opinion is less than this value
double mu = 0.5; // convergence parameter; similar to social interaction strength K (0,0.5]
bool use_network = false;
= 0.2; // d in the paper; agents interact if difference in opinion is less than this value
double mu = 0.5; // convergence parameter; similar to social interaction strength K (0,0.5]
bool use_network = false; // For using a square lattice network
bool use_binary_vector = false; // For the multi-dimensional DeffuantModelVector; by default set to false
size_t dim
= 1; // The size of the opinions vector. This is used for the multi-dimensional DeffuantModelVector model.
};

struct ActivityDrivenSettings
Expand Down
2 changes: 1 addition & 1 deletion include/connectivity.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class TarjanConnectivityAlgo
{
lowest[v] = std::min( lowest[v], num[u] );
} // u not processed
} // u has been visited
} // u has been visited
}

// Now v has been processed
Expand Down
3 changes: 2 additions & 1 deletion include/model_factory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ inline auto create_model_deffuant( Network<AgentT> & network, const ModelVariant
{
auto deffuant_settings = std::get<Config::DeffuantSettings>( model_settings );
auto model = std::make_unique<DeffuantModel>( deffuant_settings, network, gen );
model->initialize_agents( deffuant_settings.dim );
return model;
}
else
Expand All @@ -85,7 +86,7 @@ create_model_deffuant_vector( Network<AgentT> & network, const ModelVariantT & m
{
auto deffuant_settings = std::get<Config::DeffuantSettings>( model_settings );
auto model = std::make_unique<DeffuantModelVector>( deffuant_settings, network, gen );
model.initialize_agents();
model->initialize_agents( deffuant_settings.dim );
return model;
}
else
Expand Down
2 changes: 1 addition & 1 deletion include/models/DeffuantModel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ class DeffuantModelAbstract : public Model<AgentT_>

// template<typename T>
void update_rule( AgentT & agent1, AgentT & agent2 );
void initialize_agents();
void initialize_agents( size_t dim [[maybe_unused]] );

// void iteration() override;
// bool finished() override;
Expand Down
10 changes: 9 additions & 1 deletion include/simulation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,15 @@ class Simulation : public SimulationInterface
}
else if( options.model == Config::Model::DeffuantModel )
{
model = ModelFactory::create_model_deffuant( network, options.model_settings, gen );
auto deffuant_settings = std::get<Config::DeffuantSettings>( options.model_settings );
if( deffuant_settings.use_binary_vector )
{
model = ModelFactory::create_model_deffuant_vector( network, options.model_settings, gen );
}
else
{
model = ModelFactory::create_model_deffuant( network, options.model_settings, gen );
}
}

if( cli_agent_file.has_value() )
Expand Down
14 changes: 14 additions & 0 deletions src/config_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ SimulationOptions parse_config_file( std::string_view config_file_path )
set_if_specified( model_settings.homophily_threshold, tbl[options.model_string]["homophily_threshold"] );
set_if_specified( model_settings.mu, tbl[options.model_string]["mu"] );
set_if_specified( model_settings.use_network, tbl[options.model_string]["use_network"] );
// Options for the DeffuantModelVector model
set_if_specified( model_settings.use_binary_vector, tbl[options.model_string]["binary_vector"] );
set_if_specified( model_settings.dim, tbl[options.model_string]["dim"] );
options.model_settings = model_settings;
}
else if( options.model == Model::ActivityDrivenModel )
Expand Down Expand Up @@ -215,6 +218,15 @@ void validate_settings( const SimulationOptions & options )
auto model_settings = std::get<DeffuantSettings>( options.model_settings );
check( name_and_var( model_settings.homophily_threshold ), g_zero );
check( name_and_var( model_settings.mu ), []( auto x ) { return x >= 0 && x <= 1; } );
// DeffuantModelVector settings
check( name_and_var( model_settings.dim ), g_zero );
// @TODO: maybe make this check nicer?
if( !model_settings.use_binary_vector )
{
const std::string basic_deff_msg
= "The basic Deffuant model has not been implemented with multiple dimensions";
check( name_and_var( model_settings.dim ), []( auto x ) { return x == 1; }, basic_deff_msg );
}
}
}

Expand Down Expand Up @@ -272,6 +284,8 @@ void print_settings( const SimulationOptions & options )
fmt::print( " homophily_threshold {}\n", model_settings.homophily_threshold );
fmt::print( " mu {}\n", model_settings.mu );
fmt::print( " use_network {}\n", model_settings.use_network );
fmt::print( " use_binary_vector {}\n", model_settings.use_binary_vector );
fmt::print( " dim {}\n", model_settings.dim );
}

fmt::print( "[Network]\n" );
Expand Down
2 changes: 1 addition & 1 deletion src/models/DeffuantModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Seldon
{

template<>
void DeffuantModelAbstract<SimpleAgent>::initialize_agents()
void DeffuantModelAbstract<SimpleAgent>::initialize_agents( size_t )
{
for( size_t i = 0; i < network.agents.size(); i++ )
{
Expand Down
3 changes: 1 addition & 2 deletions src/models/DeffuantModelVector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@ namespace Seldon
{

template<>
void DeffuantModelAbstract<DiscreteVectorAgent>::initialize_agents()
void DeffuantModelAbstract<DiscreteVectorAgent>::initialize_agents( size_t dim )
{
std::uniform_int_distribution<int> dist( 0, 1 );
int dim = 5;

for( size_t i = 0; i < network.agents.size(); i++ )
{
Expand Down
24 changes: 24 additions & 0 deletions test/res/deffuant_vector_2agents.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[simulation]
model = "Deffuant"
# 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 does not prints
# output_initial = true # Print the initial opinions and network file from step 0. If not set, this is true by default.
# start_output = 1 # Start writing out opinions and/or network files from this iteration. If not set, this is 1.

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

[Deffuant]
homophily_threshold = 2 # d in the paper; agents interact if difference in opinion is less than this value
mu = 0.5 # convergence parameter; similar to social interaction strength K (0,0.5]
use_network = false # If true, will use a square lattice Will throw if sqrt(n_agents) is not an integer
binary_vector = true # If true, this will be the multi-dimensional binary vector Deffuant model
dim = 3 # For the multi-dimensional binary vector Deffuant model, define the number of dimensions in each opinion vector

[network]
number_of_agents = 2
connections_per_agent = 0
49 changes: 49 additions & 0 deletions test/test_deffuant.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "catch2/matchers/catch_matchers.hpp"
#include "catch2/matchers/catch_matchers_range_equals.hpp"
#include "models/DeffuantModel.hpp"
#include <sys/types.h>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <config_parser.hpp>
Expand Down Expand Up @@ -112,4 +114,51 @@ TEST_CASE( "Test the lattice deffuant model for 16x16 agents", "[deffuantLattice
{
REQUIRE_THAT( simulation.network.agents[idx_agent].data.opinion, WithinRel( -avg_opinion ) );
}
}

TEST_CASE(
"Test the multi-dimensional Deffuant vector model, with 3-dimensional binary opinions, for two agents",
"[deffuantVectorTwoAgents]" )
{
using namespace Seldon;
using namespace Catch::Matchers;
using AgentT = DeffuantModelVector::AgentT;

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

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

auto simulation = Simulation<AgentT>( options, std::nullopt, std::nullopt );

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

// references to agent opinion
std::vector<int> & agent1_opinion = simulation.network.agents[0].data.opinion;
std::vector<int> & agent2_opinion = simulation.network.agents[1].data.opinion;

// agents are too far apart, we dont expect any change with the iterations
auto agent1_init = std::vector<int>{ 0, 1, 0 };
auto agent2_init = std::vector<int>{ 1, 0, 1 };

agent1_opinion = agent1_init;
agent2_opinion = agent2_init;

simulation.run( output_dir_path );

REQUIRE_THAT( agent1_opinion, Catch::Matchers::RangeEquals( agent1_init ) );
REQUIRE_THAT( agent2_opinion, Catch::Matchers::RangeEquals( agent2_init ) );

// agents are close enough, they should converge
// dim-1 or 2 opinions should be the same
agent1_init = std::vector<int>{ 0, 1, 1 };
agent2_init = std::vector<int>{ 1, 1, 1 };

agent1_opinion = agent1_init;
agent2_opinion = agent2_init;

simulation.run( output_dir_path );

REQUIRE_THAT( agent1_opinion, Catch::Matchers::RangeEquals( agent2_opinion ) );
}
2 changes: 1 addition & 1 deletion test/test_network.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ TEST_CASE( "Testing the network class" )
auto weight = buffer_w[i_neighbour];
std::tuple<size_t, size_t, Network::WeightT> edge{
neighbour, i_agent, weight
}; // Note that i_agent and neighbour are flipped compared to before
}; // Note that i_agent and neighbour are flipped compared to before
REQUIRE( old_edges.contains( edge ) ); // can we find the transposed edge?
old_edges.extract( edge ); // extract the edge afterwards
}
Expand Down

0 comments on commit 514215c

Please sign in to comment.