diff --git a/include/network.hpp b/include/network.hpp index f433b63..7ea7e87 100644 --- a/include/network.hpp +++ b/include/network.hpp @@ -14,9 +14,16 @@ class Network Network( std::vector> && neighbour_list, std::vector> && weight_list ); std::size_t n_agents() const; + void get_neighbours( std::size_t agent_idx, std::vector & buffer ) const; void get_weights( std::size_t agent_idx, std::vector & buffer ) const; + void set_neighbours_and_weights( + std::size_t agent_idx, const std::vector & buffer_neighbours, + const std::vector & buffer_weights ); + + void transpose(); + private: std::vector> neighbour_list; // Neighbour list for the connections std::vector> weight_list; // List for the interaction weights of each connection diff --git a/meson.build b/meson.build index 36d17a8..8459dfc 100644 --- a/meson.build +++ b/meson.build @@ -23,7 +23,8 @@ exe = executable('seldon', sources_seldon + 'src/main.cpp', tests = [ ['Test Tarjan', 'test/test_tarjan.cpp'], - ['Test DeGroot', 'test/test_deGroot.cpp'] + ['Test DeGroot', 'test/test_deGroot.cpp'], + ['Test Netowrk', 'test/test_network.cpp'] ] Catch2 = dependency('Catch2', method : 'cmake', modules : ['Catch2::Catch2WithMain', 'Catch2::Catch2']) diff --git a/src/network.cpp b/src/network.cpp index 721809a..01ac92a 100644 --- a/src/network.cpp +++ b/src/network.cpp @@ -3,6 +3,7 @@ #include #include #include +#include Seldon::Network::Network( std::vector> && neighbour_list, std::vector> && weight_list ) @@ -28,22 +29,45 @@ size_t Seldon::Network::n_agents() const void Seldon::Network::get_neighbours( std::size_t agent_idx, std::vector & buffer ) const { - // TODO: rewrite this using std::span - const size_t n_edges = neighbour_list[agent_idx].size(); - buffer.resize( n_edges ); - for( size_t i_edge = 0; i_edge < n_edges; i_edge++ ) - { - buffer[i_edge] = neighbour_list[agent_idx][i_edge]; - } + buffer = neighbour_list[agent_idx]; +} + +void Seldon::Network::set_neighbours_and_weights( + std::size_t agent_idx, const std::vector & buffer_neighbours, + const std::vector & 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!" ); + } + + neighbour_list[agent_idx] = buffer_neighbours; + weight_list[agent_idx] = buffer_weights; } -void Seldon::Network::get_weights( std::size_t agent_idx, std::vector & buffer ) const +void Seldon::Network::get_weights( std::size_t agent_idx, std::vector & buffer ) const { - // TODO: rewrite this using std::span - const size_t n_edges = weight_list[agent_idx].size(); - buffer.resize( n_edges ); - for( size_t i_edge = 0; i_edge < n_edges; i_edge++ ) + buffer = weight_list[agent_idx]; +} + +void Seldon::Network::transpose() +{ + std::vector> neighbour_list_transpose( n_agents(), std::vector( 0 ) ); + std::vector> weight_list_transpose( n_agents(), std::vector( 0 ) ); + + for( size_t i_agent = 0; i_agent < n_agents(); i_agent++ ) { - buffer[i_edge] = weight_list[agent_idx][i_edge]; + for( size_t i_neighbour = 0; i_neighbour < neighbour_list[i_agent].size(); i_neighbour++ ) + { + const auto neighbour = neighbour_list[i_agent][i_neighbour]; + const auto weight = weight_list[i_agent][i_neighbour]; + neighbour_list_transpose[neighbour].push_back( i_agent ); + weight_list_transpose[neighbour].push_back( weight ); + } } + + neighbour_list = std::move( neighbour_list_transpose ); + weight_list = std::move( weight_list_transpose ); } \ No newline at end of file diff --git a/test/test_network.cpp b/test/test_network.cpp new file mode 100644 index 0000000..839ee8d --- /dev/null +++ b/test/test_network.cpp @@ -0,0 +1,79 @@ +#include "network.hpp" +#include "network_generation.hpp" +#include +#include +#include +#include + +TEST_CASE( "Testing the network class" ) +{ + using namespace Seldon; + + // Generate some network + const int n_agents = 20; + const int n_connections = 10; + std::mt19937 gen( 0 ); + auto network = generate_n_connections( n_agents, n_connections, gen ); + + // Does n_agents work? + REQUIRE( network->n_agents() == n_agents ); + + // Change the connections for agent 3 + std::vector buffer_n{ { 0, 10, 15 } }; + std::vector buffer_w{ 0.1, 0.2, 0.3 }; + network->set_neighbours_and_weights( 3, buffer_n, buffer_w ); + + // Make sure the changes worked + std::vector buffer_n_get{}; + std::vector buffer_w_get{}; + network->get_neighbours( 3, buffer_n_get ); + network->get_weights( 3, buffer_w_get ); + for( size_t i = 0; i < buffer_n_get.size(); i++ ) + { + // CAREFUL: this assumes that the order of edges has been preserved + // which at the moment seems reasonable ... but you never know + REQUIRE( buffer_n[i] == buffer_n_get[i] ); + REQUIRE( buffer_w[i] == buffer_w_get[i] ); + } + + // Now we test the transpose() function + + // First record all the old edges as tupels (i,j,w) where this edge goes from j -> i with weight w + std::set> old_edges; + for( size_t i_agent = 0; i_agent < network->n_agents(); i_agent++ ) + { + network->get_neighbours( i_agent, buffer_n ); + network->get_weights( i_agent, buffer_w ); + + for( size_t i_neighbour = 0; i_neighbour < buffer_n.size(); i_neighbour++ ) + { + auto neighbour = buffer_n[i_neighbour]; + auto weight = buffer_w[i_neighbour]; + std::tuple edge{ i_agent, neighbour, weight }; + old_edges.insert( edge ); + } + } + + network->transpose(); + + // Now we go over the transposed network and try to re-identify all edges + for( size_t i_agent = 0; i_agent < network->n_agents(); i_agent++ ) + { + network->get_neighbours( i_agent, buffer_n ); + network->get_weights( i_agent, buffer_w ); + + for( size_t i_neighbour = 0; i_neighbour < buffer_n.size(); i_neighbour++ ) + { + auto neighbour = buffer_n[i_neighbour]; + auto weight = buffer_w[i_neighbour]; + std::tuple edge{ + neighbour, i_agent, weight + }; // 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 + } + } + + REQUIRE( old_edges.empty() ); +} \ No newline at end of file