Skip to content

Commit 0198511

Browse files
committed
feat: C API for the DeGroot iteration
seldon_degroot_settle is the same sweep as DeGrootModel::iteration so a seat crate can call it without linking the whole engine.
1 parent b4c479a commit 0198511

4 files changed

Lines changed: 101 additions & 0 deletions

File tree

‎include/seldon_capi.h‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#pragma once
2+
#include <stddef.h>
3+
4+
#ifdef __cplusplus
5+
extern "C" {
6+
#endif
7+
8+
/* One DeGroot sweep of x <- W x until ||dx||_inf < tol or max_iter.
9+
* For each agent i, n_in[i] incoming edges start at off = sum_{k<i} n_in[k].
10+
* neigh[off+j] and weight[off+j] are the neighbour and the incoming weight.
11+
* opinions is length n, overwritten in place.
12+
* Returns 0 on success, -1 on a bad argument.
13+
* This is the same iteration as Seldon::DeGrootModel::iteration. */
14+
int seldon_degroot_settle( size_t n, const size_t * n_in, const size_t * neigh,
15+
const double * weight, double * opinions, double tol,
16+
int max_iter, int * rounds_out );
17+
18+
#ifdef __cplusplus
19+
}
20+
#endif

‎meson.build‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ sources_seldon = [
2828
'src/models/DeffuantModelVector.cpp',
2929
'src/models/InertialModel.cpp',
3030
'src/util/tomlplusplus.cpp',
31+
'src/capi.cpp',
3132
]
3233

3334
# both static and shared library for bindings
@@ -39,6 +40,8 @@ if get_option('default_library') == 'static'
3940
symbol_visibility = 'inlineshidden'
4041
endif
4142

43+
install_headers('include/seldon_capi.h')
44+
4245
seldon_lib = both_libraries('seldon',
4346
sources_seldon,
4447
install:true,
@@ -79,6 +82,7 @@ if get_option('build_tests')
7982
['Test_IO', 'test/test_io.cpp'],
8083
['Test_Util', 'test/test_util.cpp'],
8184
['Test_Prob', 'test/test_probability_distributions.cpp'],
85+
['Test_CAPI', 'test/test_capi.cpp'],
8286
]
8387

8488
Catch2 = dependency('Catch2', method : 'cmake', modules : ['Catch2::Catch2WithMain', 'Catch2::Catch2'])

‎src/capi.cpp‎

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#include "seldon_capi.h"
2+
#include <cmath>
3+
#include <vector>
4+
5+
int seldon_degroot_settle( size_t n, const size_t * n_in, const size_t * neigh,
6+
const double * weight, double * opinions, double tol,
7+
int max_iter, int * rounds_out )
8+
{
9+
if( n == 0 || n_in == nullptr || opinions == nullptr || max_iter < 1 )
10+
{
11+
return -1;
12+
}
13+
size_t edges = 0;
14+
for( size_t i = 0; i < n; i++ )
15+
{
16+
edges += n_in[i];
17+
}
18+
if( edges > 0 && ( neigh == nullptr || weight == nullptr ) )
19+
{
20+
return -1;
21+
}
22+
23+
std::vector<double> nxt( n, 0.0 );
24+
int rounds = 0;
25+
for( int r = 1; r <= max_iter; r++ )
26+
{
27+
size_t off = 0;
28+
for( size_t i = 0; i < n; i++ )
29+
{
30+
double acc = 0.0;
31+
for( size_t j = 0; j < n_in[i]; j++ )
32+
{
33+
size_t k = neigh[off + j];
34+
if( k >= n )
35+
{
36+
return -1;
37+
}
38+
acc += weight[off + j] * opinions[k];
39+
}
40+
nxt[i] = acc;
41+
off += n_in[i];
42+
}
43+
double err = 0.0;
44+
for( size_t i = 0; i < n; i++ )
45+
{
46+
err = std::max( err, std::abs( nxt[i] - opinions[i] ) );
47+
opinions[i] = nxt[i];
48+
}
49+
rounds = r;
50+
if( err < tol )
51+
{
52+
break;
53+
}
54+
}
55+
if( rounds_out != nullptr )
56+
{
57+
*rounds_out = rounds;
58+
}
59+
return 0;
60+
}

‎test/test_capi.cpp‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include "seldon_capi.h"
2+
#include <catch2/catch_test_macros.hpp>
3+
#include <catch2/matchers/catch_matchers_floating_point.hpp>
4+
5+
TEST_CASE( "C API DeGroot matches the two-agent symmetric case", "[capi]" )
6+
{
7+
using namespace Catch::Matchers;
8+
size_t n_in[2] = { 2, 2 };
9+
size_t neigh[4] = { 1, 0, 0, 1 };
10+
double weight[4] = { 0.2, 0.8, 0.2, 0.8 };
11+
double x[2] = { 0.0, 1.0 };
12+
int rounds = 0;
13+
REQUIRE( seldon_degroot_settle( 2, n_in, neigh, weight, x, 1e-6, 100, &rounds ) == 0 );
14+
REQUIRE( rounds > 0 );
15+
REQUIRE_THAT( x[0], WithinAbs( 0.5, 1e-5 ) );
16+
REQUIRE_THAT( x[1], WithinAbs( 0.5, 1e-5 ) );
17+
}

0 commit comments

Comments
 (0)