Skip to content

Commit

Permalink
Network: Init of refactor templated network class
Browse files Browse the repository at this point in the history
Change Network to a class templated on the AgentType.
All functions were moved to the hpp file.

Co-authored-by: Moritz Sallermann <[email protected]>
  • Loading branch information
amritagos and MSallermann committed Mar 14, 2024
1 parent 7f77d24 commit 8cd3a2c
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 165 deletions.
140 changes: 123 additions & 17 deletions include/network.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
#pragma once
#include "connectivity.hpp"
#include <fmt/format.h>
#include <algorithm>
#include <cstddef>
#include <numeric>
#include <optional>
#include <span>
#include <stdexcept>
#include <vector>

namespace Seldon
Expand All @@ -22,6 +27,7 @@ namespace Seldon
Note: switch is equivalent to toggle + transpose, but much cheaper!
*/
// template<typename AgentType>
class Network
{
public:
Expand All @@ -35,81 +41,181 @@ class Network

Network(
std::vector<std::vector<size_t>> && neighbour_list, std::vector<std::vector<WeightT>> && weight_list,
EdgeDirection direction );
EdgeDirection direction )
: neighbour_list( neighbour_list ), weight_list( weight_list ), _direction( direction )
{
}

/*
Gives the total number of nodes in the network
*/
[[nodiscard]] std::size_t n_agents() const;
[[nodiscard]] std::size_t n_agents() const
{
return neighbour_list.size();
}

/*
Gives the number of edges going out/coming in at agent_idx, depending on the value of direction().
If agent_idx is nullopt, gives the total number of edges
*/
[[nodiscard]] std::size_t n_edges( std::optional<std::size_t> agent_idx = std::nullopt ) const;
[[nodiscard]] std::size_t n_edges( std::optional<std::size_t> agent_idx = std::nullopt ) 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(); } );
}
}

/*
Returns the current directionality of the adjacency list. That is if incoming or outgoing edges are stored.
*/
[[nodiscard]] const EdgeDirection & direction() const;
[[nodiscard]] const EdgeDirection & direction() const
{
return _direction;
}

/*
Gives the strongly connected components in the graph
*/
[[nodiscard]] std::vector<std::vector<size_t>> strongly_connected_components() const;
[[nodiscard]] std::vector<std::vector<size_t>> 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;
}

/*
Gives a view into the neighbour indices going out/coming in at agent_idx
*/
[[nodiscard]] std::span<const size_t> get_neighbours( std::size_t agent_idx ) const;
[[nodiscard]] std::span<size_t> get_neighbours( std::size_t agent_idx );
[[nodiscard]] std::span<const size_t> get_neighbours( std::size_t agent_idx ) const
{
return std::span( neighbour_list[agent_idx].data(), neighbour_list[agent_idx].size() );
}
[[nodiscard]] std::span<size_t> get_neighbours( std::size_t agent_idx )
{
return std::span( neighbour_list[agent_idx].data(), neighbour_list[agent_idx].size() );
}

/*
Gives a view into the edge weights going out/coming in at agent_idx
*/
[[nodiscard]] std::span<const WeightT> get_weights( std::size_t agent_idx ) const;
[[nodiscard]] std::span<WeightT> get_weights( std::size_t agent_idx );
[[nodiscard]] std::span<const WeightT> get_weights( std::size_t agent_idx ) const
{
return std::span<const WeightT>( weight_list[agent_idx].data(), weight_list[agent_idx].size() );
}
[[nodiscard]] std::span<WeightT> get_weights( std::size_t agent_idx )
{
return std::span<WeightT>( weight_list[agent_idx].data(), weight_list[agent_idx].size() );
}

/*
Gives a view into the edge weights going out/coming in at agent_idx
*/
void set_weights( std::size_t agent_idx, std::span<const WeightT> weights );
void set_weights( std::size_t agent_idx, std::span<const WeightT> weights )
{
if( neighbour_list[agent_idx].size() != weights.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() );
}

/*
Sets the neighbour indices and sets the weight to a constant value at agent_idx
*/
void set_neighbours_and_weights(
std::size_t agent_idx, std::span<const size_t> buffer_neighbours, const WeightT & weight );

std::size_t agent_idx, std::span<const size_t> buffer_neighbours, const WeightT & weight )
{
neighbour_list[agent_idx].assign( buffer_neighbours.begin(), buffer_neighbours.end() );
weight_list[agent_idx].resize( buffer_neighbours.size() );
std::fill( weight_list[agent_idx].begin(), weight_list[agent_idx].end(), weight );
}
/*
Sets the neighbour indices and weights at agent_idx
*/
void set_neighbours_and_weights(
std::size_t agent_idx, std::span<const size_t> buffer_neighbours, std::span<const WeightT> buffer_weights );
std::size_t agent_idx, std::span<const size_t> buffer_neighbours, std::span<const WeightT> buffer_weights )
{
if( buffer_neighbours.size() != buffer_weights.size() )
{
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() );
}

/*
Adds an edge between agent_idx_i and agent_idx_j with weight w
*/
void push_back_neighbour_and_weight( size_t agent_idx_i, size_t agent_idx_j, WeightT w );
void push_back_neighbour_and_weight( size_t agent_idx_i, size_t agent_idx_j, WeightT w )
{
neighbour_list[agent_idx_i].push_back( agent_idx_j );
weight_list[agent_idx_i].push_back( w );
}

/*
Transposes the network, without switching the direction flag (expensive).
Example: N(inc) -> N(inc)^T
*/
void transpose();
void transpose()
{
toggle_incoming_outgoing();
switch_direction_flag();
}

/*
Switches the direction flag *without* transposing the network (expensive)
Example: N(inc) -> N(out)
*/
void toggle_incoming_outgoing();
void toggle_incoming_outgoing()
{
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++ )
{
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 );

// Swap the edge direction
switch_direction_flag();
}

/*
Only switches the direction flag. This effectively transposes the network and, simultaneously, changes its
representation.
Example: N(inc) -> N^T(out)
*/
void switch_direction_flag();
void switch_direction_flag()
{
// Swap the edge direction
if( direction() == EdgeDirection::Incoming )
{
_direction = EdgeDirection::Outgoing;
}
else
{
_direction = EdgeDirection::Incoming;
}
}

private:
std::vector<std::vector<size_t>> neighbour_list; // Neighbour list for the connections
Expand Down
1 change: 0 additions & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ incdir = include_directories('include')

sources_seldon = [
'src/simulation.cpp',
'src/network.cpp',
'src/network_generation.cpp',
'src/config_parser.cpp',
'src/model.cpp',
Expand Down
3 changes: 2 additions & 1 deletion src/models/ActivityDrivenModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ void 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 );
Expand Down
145 changes: 0 additions & 145 deletions src/network.cpp

This file was deleted.

3 changes: 2 additions & 1 deletion test/test_activity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,8 @@ TEST_CASE( "Test the meanfield activity driven model with 10 agents", "[activity
auto mean_activity = dist.mean();

// Calculate the critical controversialness
auto set_opinions_and_run = [&]( bool above_critical_controversialness ) {
auto set_opinions_and_run = [&]( bool above_critical_controversialness )
{
auto simulation = Simulation( options, std::nullopt, std::nullopt );
auto initial_opinion_delta = 0.1; // Set the initial opinion in the interval [-delta, delta]

Expand Down

0 comments on commit 8cd3a2c

Please sign in to comment.