diff --git a/include/network.hpp b/include/network.hpp index 63c4d63..03aba1b 100644 --- a/include/network.hpp +++ b/include/network.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include @@ -13,15 +14,19 @@ class Network Network( std::vector> && neighbour_list, std::vector> && weight_list ); + // Gives the total number of nodes in the network std::size_t n_agents() const; + /* Gives the number of edges going out from agent_idx. + If agent_idx is nullopt, give the total number of edges + */ + std::size_t n_edges( std::optional agent_idx = std::nullopt ) const; + std::span get_neighbours( std::size_t agent_idx ) const; std::span get_neighbours( std::size_t agent_idx ); std::span get_weights( std::size_t agent_idx ) const; std::span get_weights( std::size_t agent_idx ); - std::size_t get_n_edges( std::size_t agent_idx ) const; - void set_weights( std::size_t agent_idx, std::span weights ); void set_neighbours_and_weights( diff --git a/include/network_generation.hpp b/include/network_generation.hpp index fba8004..d7a6d50 100644 --- a/include/network_generation.hpp +++ b/include/network_generation.hpp @@ -6,8 +6,11 @@ namespace Seldon { -// Returns a unique pointer to a new network with n_connections per agent -std::unique_ptr generate_n_connections( size_t n_agents, int n_connections, std::mt19937 & gen ); +/* 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 +*/ +std::unique_ptr +generate_n_connections( size_t n_agents, size_t n_connections, bool self_interaction, std::mt19937 & gen ); std::unique_ptr generate_fully_connected( size_t n_agents, Network::WeightT weight = 0.0 ); std::unique_ptr generate_fully_connected( size_t n_agents, std::mt19937 & gen ); std::unique_ptr generate_from_file( const std::string & file ); diff --git a/src/models/ActivityDrivenModel.cpp b/src/models/ActivityDrivenModel.cpp index 081b8c4..3974fb9 100644 --- a/src/models/ActivityDrivenModel.cpp +++ b/src/models/ActivityDrivenModel.cpp @@ -60,7 +60,8 @@ void Seldon::ActivityAgentModel::update_network_probabilistic() { // Implement the weight for the probability of agent `idx_agent` contacting agent `j` // Not normalised since this is taken care of by the reservoir sampling - auto weight_callback = [idx_agent, this]( size_t j ) { + auto weight_callback = [idx_agent, this]( size_t j ) + { if( idx_agent == j ) // The agent does not contact itself return 0.0; return std::pow( @@ -126,7 +127,8 @@ void Seldon::ActivityAgentModel::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 ); @@ -137,7 +139,8 @@ void Seldon::ActivityAgentModel::update_network_mean() { // Implement the weight for the probability of agent `idx_agent` contacting agent `j` // Not normalised since this is taken care of by the reservoir sampling - auto weight_callback = [idx_agent, this]( size_t j ) { + auto weight_callback = [idx_agent, this]( size_t j ) + { constexpr double tolerance = 1e-16; auto opinion_diff = std::abs( this->agents[idx_agent].data.opinion - this->agents[j].data.opinion ); if( opinion_diff < tolerance ) diff --git a/src/network.cpp b/src/network.cpp index a2aa294..95c4a31 100644 --- a/src/network.cpp +++ b/src/network.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include Seldon::Network::Network( @@ -27,6 +28,20 @@ size_t Seldon::Network::n_agents() const return neighbour_list.size(); } +std::size_t Seldon::Network::n_edges( std::optional agent_idx ) const +{ + if( agent_idx.has_value() ) + { + return neighbour_list[agent_idx.value()].size(); + } + else + { + return std::transform_reduce( + neighbour_list.cbegin(), neighbour_list.cend(), 0, std::plus{}, + []( const auto & neigh_list ) { return neigh_list.size(); } ); + } +} + std::span Seldon::Network::get_neighbours( std::size_t agent_idx ) const { return std::span( neighbour_list[agent_idx].cbegin(), neighbour_list[agent_idx].cend() ); @@ -76,11 +91,6 @@ std::span Seldon::Network::get_weights( std::size_t ag return std::span( weight_list[agent_idx].begin(), weight_list[agent_idx].end() ); } -std::size_t Seldon::Network::get_n_edges( std::size_t agent_idx ) const -{ - return neighbour_list[agent_idx].size(); -} - void Seldon::Network::set_weights( std::size_t agent_idx, std::span weights ) { if( neighbour_list[agent_idx].size() != weights.size() ) diff --git a/src/network_generation.cpp b/src/network_generation.cpp index 0dea075..a5e181e 100644 --- a/src/network_generation.cpp +++ b/src/network_generation.cpp @@ -13,7 +13,7 @@ #include std::unique_ptr -Seldon::generate_n_connections( size_t n_agents, int n_connections, std::mt19937 & gen ) +Seldon::generate_n_connections( size_t n_agents, size_t n_connections, bool self_interaction, std::mt19937 & gen ) { using WeightT = Network::WeightT; @@ -34,7 +34,7 @@ Seldon::generate_n_connections( size_t n_agents, int n_connections, std::mt19937 incoming_neighbour_weights.clear(); // Get the vector of sorted adjacencies, excluding i (include i later) - // TODO: option for making the n_conections variable + // TODO: option for making the n_connections variable Seldon::draw_unique_k_from_n( i_agent, n_connections, n_agents, incoming_neighbour_buffer, gen ); incoming_neighbour_weights.resize( incoming_neighbour_buffer.size() ); @@ -44,13 +44,15 @@ Seldon::generate_n_connections( size_t n_agents, int n_connections, std::mt19937 outgoing_norm_weight += incoming_neighbour_weights[j]; } - // Put the self-interaction as the last entry - auto self_interaction_weight = dis( gen ); - outgoing_norm_weight += self_interaction_weight; - - // outgoing_norm_weights += self_interaction_weight; - incoming_neighbour_buffer.push_back( i_agent ); // Add the agent itself - incoming_neighbour_weights.push_back( self_interaction_weight ); + if( self_interaction ) + { + // Put the self-interaction as the last entry + auto self_interaction_weight = dis( gen ); + outgoing_norm_weight += self_interaction_weight; + // outgoing_norm_weights += self_interaction_weight; + incoming_neighbour_buffer.push_back( i_agent ); // Add the agent itself + incoming_neighbour_weights.push_back( self_interaction_weight ); + } // --------- // Normalize the weights so that the row sums to 1 diff --git a/src/simulation.cpp b/src/simulation.cpp index 43022f5..5776a81 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -89,7 +89,7 @@ Seldon::Simulation::Simulation( } else { - network = generate_n_connections( n_agents, n_connections, gen ); + network = generate_n_connections( n_agents, n_connections, true, gen ); } } diff --git a/test/test_network.cpp b/test/test_network.cpp index 6550336..f857c62 100644 --- a/test/test_network.cpp +++ b/test/test_network.cpp @@ -11,13 +11,15 @@ TEST_CASE( "Testing the network class" ) using namespace Seldon; // Generate some network - const int n_agents = 20; - const int n_connections = 10; + const size_t n_agents = 20; + const size_t n_connections = 10; std::mt19937 gen( 0 ); - auto network = generate_n_connections( n_agents, n_connections, gen ); + auto network = generate_n_connections( n_agents, n_connections, false, gen ); // Does n_agents work? REQUIRE( network->n_agents() == n_agents ); + // Does n_edges work? + REQUIRE( network->n_edges() == n_agents * n_connections ); // Check that the function for setting neighbours and a single weight work // Agent 3 @@ -30,11 +32,12 @@ TEST_CASE( "Testing the network class" ) SECTION( "Checking that set_weight, get_neighbour work" ) { + weight = { 0.25, 0.55 }; network->set_weights( 3, weight ); auto buffer_w_get = network->get_weights( 3 ); REQUIRE_THAT( weight, Catch::Matchers::UnorderedRangeEquals( buffer_w_get ) ); - REQUIRE( network->get_n_edges( 3 ) == 2 ); + REQUIRE( network->n_edges( 3 ) == 2 ); size_t & n = network->get_neighbours( 3 )[0]; REQUIRE( n == neigh[0] );