diff --git a/include/network.hpp b/include/network.hpp index 03aba1b..074a3c0 100644 --- a/include/network.hpp +++ b/include/network.hpp @@ -10,15 +10,24 @@ namespace Seldon class Network { public: + enum class EdgeDirection + { + Incoming, + Outgoing + }; + using WeightT = double; - Network( std::vector> && neighbour_list, std::vector> && weight_list ); + Network( + std::vector> && neighbour_list, std::vector> && weight_list, + EdgeDirection direction ); // 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 + /* + Gives the number of edges going out/coming in from/at 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; @@ -27,6 +36,8 @@ class Network std::span get_weights( std::size_t agent_idx ) const; std::span get_weights( std::size_t agent_idx ); + std::vector> strongly_connected_components() const; + void set_weights( std::size_t agent_idx, std::span weights ); void set_neighbours_and_weights( @@ -39,9 +50,13 @@ class Network void transpose(); + const EdgeDirection & direction() const; + private: std::vector> neighbour_list; // Neighbour list for the connections std::vector> weight_list; // List for the interaction weights of each connection + EdgeDirection _direction; + void swap_direction(); }; } // namespace Seldon \ No newline at end of file diff --git a/src/models/ActivityDrivenModel.cpp b/src/models/ActivityDrivenModel.cpp index 3974fb9..298c3c3 100644 --- a/src/models/ActivityDrivenModel.cpp +++ b/src/models/ActivityDrivenModel.cpp @@ -47,6 +47,8 @@ void Seldon::ActivityAgentModel::get_agents_from_power_law() void Seldon::ActivityAgentModel::update_network_probabilistic() { + network = Network( {}, {}, Network::EdgeDirection::Outgoing ); + std::uniform_real_distribution<> dis_activation( 0.0, 1.0 ); std::uniform_real_distribution<> dis_reciprocation( 0.0, 1.0 ); std::vector contacted_agents{}; diff --git a/src/models/DeGroot.cpp b/src/models/DeGroot.cpp index af14e9f..c0be310 100644 --- a/src/models/DeGroot.cpp +++ b/src/models/DeGroot.cpp @@ -5,6 +5,14 @@ Seldon::DeGrootModel::DeGrootModel( int n_agents, Network & network ) : Model( n_agents ), network( network ), agents_current_copy( std::vector( n_agents ) ) { + // For a strongly connected network, the number of SCCs should be 1 + // Print a warning if this is not true + auto n_components = network.strongly_connected_components().size(); + if( n_components != 1 ) + { + fmt::print( "WARNING: You have {} strongly connected components in your network!\n", n_components ); + } + for( size_t i = 0; i < agents.size(); i++ ) { agents[i].data = double( i ) / double( agents.size() ); diff --git a/src/network.cpp b/src/network.cpp index 95c4a31..672e853 100644 --- a/src/network.cpp +++ b/src/network.cpp @@ -7,20 +7,10 @@ #include Seldon::Network::Network( - std::vector> && neighbour_list, std::vector> && weight_list ) - : neighbour_list( neighbour_list ), weight_list( weight_list ) + std::vector> && neighbour_list, std::vector> && weight_list, + EdgeDirection direction ) + : neighbour_list( neighbour_list ), weight_list( weight_list ), _direction( direction ) { - // Now that we have the neighbour list (or adjacency list) - // Run Tarjan's algorithm for strongly connected components - auto tarjan_scc = TarjanConnectivityAlgo( neighbour_list ); - - // For a strongly connected network, the number of SCCs should be 1 - // Print a warning if this is not true - if( tarjan_scc.scc_list.size() != 1 ) - { - fmt::print( - "WARNING: You have {} strongly connected components in your network!\n", tarjan_scc.scc_list.size() ); - } } size_t Seldon::Network::n_agents() const @@ -42,6 +32,19 @@ std::size_t Seldon::Network::n_edges( std::optional agent_idx ) con } } +const Seldon::Network::EdgeDirection & Seldon::Network::direction() const +{ + return _direction; +} + +std::vector> Seldon::Network::strongly_connected_components() const +{ + // Now that we have the neighbour list (or adjacency list) + // Run Tarjan's algorithm for strongly connected components + auto tarjan_scc = TarjanConnectivityAlgo( neighbour_list ); + return tarjan_scc.scc_list; +} + 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() ); @@ -65,11 +68,9 @@ void Seldon::Network::set_neighbours_and_weights( std::span buffer_weights ) { if( buffer_neighbours.size() != buffer_weights.size() ) - [[unlikely]] - { - throw std::runtime_error( - "Network::set_neighbours_and_weights: both buffers need to have the same length!" ); - } + { + throw std::runtime_error( "Network::set_neighbours_and_weights: both buffers need to have the same length!" ); + } neighbour_list[agent_idx].assign( buffer_neighbours.begin(), buffer_neighbours.end() ); weight_list[agent_idx].assign( buffer_weights.begin(), buffer_weights.end() ); @@ -94,13 +95,25 @@ std::span Seldon::Network::get_weights( std::size_t ag void Seldon::Network::set_weights( std::size_t agent_idx, std::span weights ) { if( neighbour_list[agent_idx].size() != weights.size() ) - [[unlikely]] - { - throw std::runtime_error( "Network::set_weights: tried to set weights of the wrong size!" ); - } + { + throw std::runtime_error( "Network::set_weights: tried to set weights of the wrong size!" ); + } weight_list[agent_idx].assign( weights.begin(), weights.end() ); } +void Seldon::Network::swap_direction() +{ + // Swap the edge direction + if( direction() == EdgeDirection::Incoming ) + { + _direction = EdgeDirection::Outgoing; + } + else + { + _direction = EdgeDirection::Incoming; + } +} + void Seldon::Network::transpose() { std::vector> neighbour_list_transpose( n_agents(), std::vector( 0 ) ); @@ -117,6 +130,7 @@ void Seldon::Network::transpose() } } + swap_direction(); neighbour_list = std::move( neighbour_list_transpose ); weight_list = std::move( weight_list_transpose ); } \ No newline at end of file diff --git a/src/network_generation.cpp b/src/network_generation.cpp index a5e181e..a282dd2 100644 --- a/src/network_generation.cpp +++ b/src/network_generation.cpp @@ -69,7 +69,8 @@ Seldon::generate_n_connections( size_t n_agents, size_t n_connections, bool self } // end of loop through n_agents - return std::make_unique( std::move( neighbour_list ), std::move( weight_list ) ); + return std::make_unique( + std::move( neighbour_list ), std::move( weight_list ), Network::EdgeDirection::Incoming ); } std::unique_ptr Seldon::generate_from_file( const std::string & file ) @@ -148,7 +149,8 @@ std::unique_ptr Seldon::generate_from_file( const std::string & } } - return std::make_unique( std::move( neighbour_list ), std::move( weight_list ) ); + return std::make_unique( + std::move( neighbour_list ), std::move( weight_list ), Network::EdgeDirection::Incoming ); } // Create a fully connected network, with each weight initialized to the same user-defined @@ -180,7 +182,8 @@ std::unique_ptr Seldon::generate_fully_connected( size_t n_agen } // end of loop through n_agents - return std::make_unique( std::move( neighbour_list ), std::move( weight_list ) ); + return std::make_unique( + std::move( neighbour_list ), std::move( weight_list ), Network::EdgeDirection::Incoming ); } // Create a fully connected network, with each incoming weight initialized to some value from a @@ -231,5 +234,6 @@ std::unique_ptr Seldon::generate_fully_connected( size_t n_agen } // end of loop through n_agents - return std::make_unique( std::move( neighbour_list ), std::move( weight_list ) ); + return std::make_unique( + std::move( neighbour_list ), std::move( weight_list ), Network::EdgeDirection::Incoming ); } \ No newline at end of file diff --git a/test/test_deGroot.cpp b/test/test_deGroot.cpp index 781d70c..f85c4c7 100644 --- a/test/test_deGroot.cpp +++ b/test/test_deGroot.cpp @@ -21,7 +21,7 @@ TEST_CASE( "Test the DeGroot Model Symmetric", "[DeGroot]" ) { 0.2, 0.8 }, }; - auto network = Network( std::move( neighbour_list ), std::move( weight_list ) ); + auto network = Network( std::move( neighbour_list ), std::move( weight_list ), Network::EdgeDirection::Incoming ); auto model = DeGrootModel( n_agents, network ); model.convergence_tol = 1e-6;