From 0198511b42bf8b36be6a91cc93881408f94de9af Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Fri, 11 Sep 2026 06:33:25 -0500 Subject: [PATCH] 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. --- include/seldon_capi.h | 20 +++++++++++++++ meson.build | 4 +++ src/capi.cpp | 60 +++++++++++++++++++++++++++++++++++++++++++ test/test_capi.cpp | 17 ++++++++++++ 4 files changed, 101 insertions(+) create mode 100644 include/seldon_capi.h create mode 100644 src/capi.cpp create mode 100644 test/test_capi.cpp diff --git a/include/seldon_capi.h b/include/seldon_capi.h new file mode 100644 index 0000000..e58f967 --- /dev/null +++ b/include/seldon_capi.h @@ -0,0 +1,20 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* One DeGroot sweep of x <- W x until ||dx||_inf < tol or max_iter. + * For each agent i, n_in[i] incoming edges start at off = sum_{k +#include + +int seldon_degroot_settle( size_t n, const size_t * n_in, const size_t * neigh, + const double * weight, double * opinions, double tol, + int max_iter, int * rounds_out ) +{ + if( n == 0 || n_in == nullptr || opinions == nullptr || max_iter < 1 ) + { + return -1; + } + size_t edges = 0; + for( size_t i = 0; i < n; i++ ) + { + edges += n_in[i]; + } + if( edges > 0 && ( neigh == nullptr || weight == nullptr ) ) + { + return -1; + } + + std::vector nxt( n, 0.0 ); + int rounds = 0; + for( int r = 1; r <= max_iter; r++ ) + { + size_t off = 0; + for( size_t i = 0; i < n; i++ ) + { + double acc = 0.0; + for( size_t j = 0; j < n_in[i]; j++ ) + { + size_t k = neigh[off + j]; + if( k >= n ) + { + return -1; + } + acc += weight[off + j] * opinions[k]; + } + nxt[i] = acc; + off += n_in[i]; + } + double err = 0.0; + for( size_t i = 0; i < n; i++ ) + { + err = std::max( err, std::abs( nxt[i] - opinions[i] ) ); + opinions[i] = nxt[i]; + } + rounds = r; + if( err < tol ) + { + break; + } + } + if( rounds_out != nullptr ) + { + *rounds_out = rounds; + } + return 0; +} diff --git a/test/test_capi.cpp b/test/test_capi.cpp new file mode 100644 index 0000000..f234272 --- /dev/null +++ b/test/test_capi.cpp @@ -0,0 +1,17 @@ +#include "seldon_capi.h" +#include +#include + +TEST_CASE( "C API DeGroot matches the two-agent symmetric case", "[capi]" ) +{ + using namespace Catch::Matchers; + size_t n_in[2] = { 2, 2 }; + size_t neigh[4] = { 1, 0, 0, 1 }; + double weight[4] = { 0.2, 0.8, 0.2, 0.8 }; + double x[2] = { 0.0, 1.0 }; + int rounds = 0; + REQUIRE( seldon_degroot_settle( 2, n_in, neigh, weight, x, 1e-6, 100, &rounds ) == 0 ); + REQUIRE( rounds > 0 ); + REQUIRE_THAT( x[0], WithinAbs( 0.5, 1e-5 ) ); + REQUIRE_THAT( x[1], WithinAbs( 0.5, 1e-5 ) ); +}