Skip to content

Commit

Permalink
Network: added some functions plus a unit test
Browse files Browse the repository at this point in the history
- set_neighbours_and_weights()
- transpose()
  • Loading branch information
Moritz Sallermann committed Oct 12, 2023
1 parent 7a6f589 commit cc0adc9
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 14 deletions.
7 changes: 7 additions & 0 deletions include/network.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,16 @@ class Network
Network( std::vector<std::vector<size_t>> && neighbour_list, std::vector<std::vector<WeightT>> && weight_list );

std::size_t n_agents() const;

void get_neighbours( std::size_t agent_idx, std::vector<size_t> & buffer ) const;
void get_weights( std::size_t agent_idx, std::vector<WeightT> & buffer ) const;

void set_neighbours_and_weights(
std::size_t agent_idx, const std::vector<size_t> & buffer_neighbours,
const std::vector<WeightT> & buffer_weights );

void transpose();

private:
std::vector<std::vector<size_t>> neighbour_list; // Neighbour list for the connections
std::vector<std::vector<WeightT>> weight_list; // List for the interaction weights of each connection
Expand Down
3 changes: 2 additions & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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'])
Expand Down
50 changes: 37 additions & 13 deletions src/network.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <fmt/format.h>
#include <cstddef>
#include <optional>
#include <stdexcept>

Seldon::Network::Network(
std::vector<std::vector<size_t>> && neighbour_list, std::vector<std::vector<WeightT>> && weight_list )
Expand All @@ -28,22 +29,45 @@ size_t Seldon::Network::n_agents() const

void Seldon::Network::get_neighbours( std::size_t agent_idx, std::vector<size_t> & 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<size_t> & buffer_neighbours,
const std::vector<Seldon::Network::WeightT> & 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<double> & buffer ) const
void Seldon::Network::get_weights( std::size_t agent_idx, std::vector<Seldon::Network::WeightT> & 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<std::vector<size_t>> neighbour_list_transpose( n_agents(), std::vector<size_t>( 0 ) );
std::vector<std::vector<WeightT>> weight_list_transpose( n_agents(), std::vector<WeightT>( 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 );
}
79 changes: 79 additions & 0 deletions test/test_network.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#include "network.hpp"
#include "network_generation.hpp"
#include <catch2/catch_test_macros.hpp>
#include <cstddef>
#include <random>
#include <set>

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<size_t> buffer_n{ { 0, 10, 15 } };
std::vector<Seldon::Network::WeightT> 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<size_t> buffer_n_get{};
std::vector<Seldon::Network::WeightT> 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<std::tuple<size_t, size_t, Network::WeightT>> 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<size_t, size_t, Network::WeightT> 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<size_t, size_t, Network::WeightT> 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() );
}

0 comments on commit cc0adc9

Please sign in to comment.