Add graph references
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
igraph library.
|
||||
Copyright (C) 2025 The igraph development team <igraph@igraph.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "random/random_internal.h"
|
||||
|
||||
#include <ctime>
|
||||
#include <random>
|
||||
|
||||
/* This function attempts to produce an unpredictable number suitable for
|
||||
* seeding the RNG upon startup. It makes an effort to avoid producing the
|
||||
* same number when called within different processes, even if called
|
||||
* approximately at the same time. However, it cannot guarantee this on
|
||||
* all systems under all circumstancs.
|
||||
*
|
||||
* This function cannot fail.
|
||||
*/
|
||||
igraph_uint_t igraph_i_get_random_seed(void) {
|
||||
try {
|
||||
// Try to use C++'s std::random_device. This may fail, either because
|
||||
// the systems's random device ran out of entropy or because the system
|
||||
// does not offer this functionality.
|
||||
return std::random_device()();
|
||||
} catch (...) {
|
||||
// If random_device fails, use a time-based seed. A combination of
|
||||
// time() (calendar time, low resolution) and clock() (processor time
|
||||
// used, higher resolution) reduces the risk of seed collisions.
|
||||
return igraph_uint_t(std::clock()) + igraph_uint_t(std::time(NULL));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
igraph library.
|
||||
Copyright (C) 2021-2025 The igraph development team <igraph@igraph.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef IGRAPH_RANDOM_INTERNAL_H
|
||||
#define IGRAPH_RANDOM_INTERNAL_H
|
||||
|
||||
#include "igraph_decls.h"
|
||||
#include "igraph_types.h"
|
||||
#include "igraph_vector.h"
|
||||
|
||||
IGRAPH_BEGIN_C_DECLS
|
||||
|
||||
igraph_error_t igraph_i_random_sample_real(
|
||||
igraph_vector_t *res, igraph_real_t l, igraph_real_t h,
|
||||
igraph_int_t length);
|
||||
|
||||
igraph_uint_t igraph_i_get_random_seed(void);
|
||||
|
||||
IGRAPH_END_C_DECLS
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
igraph library.
|
||||
Copyright (C) 2022 The igraph development team <igraph@igraph.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "igraph_random.h"
|
||||
|
||||
#include "igraph_memory.h"
|
||||
#include "igraph_types.h"
|
||||
|
||||
typedef struct {
|
||||
int i, j;
|
||||
long int x[31];
|
||||
} igraph_i_rng_glibc2_state_t;
|
||||
|
||||
static unsigned long int igraph_i_rng_glibc2_get(int *i, int *j, int n, long int *x) {
|
||||
unsigned long int k;
|
||||
|
||||
/* The original implementation used x[*i] += x[*j] here. Considering that
|
||||
* x is signed, this is undefined behaviour according to the C standard.
|
||||
* Therefore, we temporarily cast to unsigned long int to achieve what the
|
||||
* original intention was */
|
||||
x[*i] = ((unsigned long int)x[*i]) + ((unsigned long int)x[*j]);
|
||||
k = (x[*i] >> 1) & 0x7FFFFFFF;
|
||||
|
||||
(*i)++;
|
||||
if (*i == n) {
|
||||
*i = 0;
|
||||
}
|
||||
|
||||
(*j)++ ;
|
||||
if (*j == n) {
|
||||
*j = 0;
|
||||
}
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
static igraph_uint_t igraph_rng_glibc2_get(void *vstate) {
|
||||
igraph_i_rng_glibc2_state_t *state =
|
||||
(igraph_i_rng_glibc2_state_t*) vstate;
|
||||
return igraph_i_rng_glibc2_get(&state->i, &state->j, 31, state->x);
|
||||
}
|
||||
|
||||
/* this function is independent of the bit size */
|
||||
|
||||
static void igraph_i_rng_glibc2_init(long int *x, int n,
|
||||
unsigned long int s) {
|
||||
int i;
|
||||
|
||||
if (s == 0) {
|
||||
s = 1;
|
||||
}
|
||||
|
||||
x[0] = (long) s;
|
||||
for (i = 1 ; i < n ; i++) {
|
||||
const long int h = s / 127773;
|
||||
const long int t = 16807 * ((long) s - h * 127773) - h * 2836;
|
||||
if (t < 0) {
|
||||
s = (unsigned long) t + 2147483647 ;
|
||||
} else {
|
||||
s = (unsigned long) t ;
|
||||
}
|
||||
|
||||
x[i] = s ;
|
||||
}
|
||||
}
|
||||
|
||||
static igraph_error_t igraph_rng_glibc2_seed(void *vstate, igraph_uint_t seed) {
|
||||
igraph_i_rng_glibc2_state_t *state =
|
||||
(igraph_i_rng_glibc2_state_t*) vstate;
|
||||
int i;
|
||||
|
||||
igraph_i_rng_glibc2_init(state->x, 31, (unsigned long) seed);
|
||||
|
||||
state->i = 3;
|
||||
state->j = 0;
|
||||
|
||||
for (i = 0; i < 10 * 31; i++) {
|
||||
igraph_rng_glibc2_get(state);
|
||||
}
|
||||
|
||||
return IGRAPH_SUCCESS;
|
||||
}
|
||||
|
||||
static igraph_error_t igraph_rng_glibc2_init(void **state) {
|
||||
igraph_i_rng_glibc2_state_t *st;
|
||||
|
||||
st = IGRAPH_CALLOC(1, igraph_i_rng_glibc2_state_t);
|
||||
IGRAPH_CHECK_OOM(st, "Cannot initialize GNU libc 2 RNG.");
|
||||
(*state) = st;
|
||||
|
||||
igraph_rng_glibc2_seed(st, 0);
|
||||
|
||||
return IGRAPH_SUCCESS;
|
||||
}
|
||||
|
||||
static void igraph_rng_glibc2_destroy(void *vstate) {
|
||||
igraph_i_rng_glibc2_state_t *state =
|
||||
(igraph_i_rng_glibc2_state_t*) vstate;
|
||||
IGRAPH_FREE(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* \var igraph_rngtype_glibc2
|
||||
* \brief The random number generator introduced in GNU libc 2.
|
||||
*
|
||||
* This is a linear feedback shift register generator with a 128-byte
|
||||
* buffer. This generator was the default prior to igraph version 0.6,
|
||||
* at least on systems relying on GNU libc.
|
||||
*
|
||||
* This generator was ported from the GNU Scientific Library. It is a
|
||||
* reimplementation and does not call the system glibc generator.
|
||||
*/
|
||||
|
||||
const igraph_rng_type_t igraph_rngtype_glibc2 = {
|
||||
/* name= */ "LIBC",
|
||||
/* bits= */ 31,
|
||||
/* init= */ igraph_rng_glibc2_init,
|
||||
/* destroy= */ igraph_rng_glibc2_destroy,
|
||||
/* seed= */ igraph_rng_glibc2_seed,
|
||||
/* get= */ igraph_rng_glibc2_get,
|
||||
/* get_int= */ NULL,
|
||||
/* get_real= */ NULL,
|
||||
/* get_norm= */ NULL,
|
||||
/* get_geom= */ NULL,
|
||||
/* get_binom= */ NULL,
|
||||
/* get_exp= */ NULL,
|
||||
/* get_gamma= */ NULL,
|
||||
/* get_pois= */ NULL
|
||||
};
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
igraph library.
|
||||
Copyright (C) 2022 The igraph development team <igraph@igraph.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "igraph_random.h"
|
||||
|
||||
#include "igraph_memory.h"
|
||||
#include "igraph_types.h"
|
||||
|
||||
#include <string.h> /* memset() */
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
#define N 624 /* Period parameters */
|
||||
#define M 397
|
||||
|
||||
/* most significant w-r bits */
|
||||
static const uint32_t UPPER_MASK = UINT32_C(0x80000000);
|
||||
|
||||
/* least significant r bits */
|
||||
static const uint32_t LOWER_MASK = UINT32_C(0x7fffffff);
|
||||
|
||||
typedef struct {
|
||||
uint32_t mt[N];
|
||||
int mti;
|
||||
} igraph_i_rng_mt19937_state_t;
|
||||
|
||||
static igraph_uint_t igraph_rng_mt19937_get(void *vstate) {
|
||||
igraph_i_rng_mt19937_state_t *state = vstate;
|
||||
|
||||
uint32_t k;
|
||||
uint32_t *const mt = state->mt;
|
||||
|
||||
#define MAGIC(y) (((y) & 0x1) ? UINT32_C(0x9908b0df) : 0)
|
||||
|
||||
if (state->mti >= N) {
|
||||
/* generate N words at one time */
|
||||
int kk;
|
||||
|
||||
for (kk = 0; kk < N - M; kk++) {
|
||||
uint32_t y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
|
||||
mt[kk] = mt[kk + M] ^ (y >> 1) ^ MAGIC(y);
|
||||
}
|
||||
for (; kk < N - 1; kk++) {
|
||||
uint32_t y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
|
||||
mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ MAGIC(y);
|
||||
}
|
||||
|
||||
{
|
||||
uint32_t y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
|
||||
mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ MAGIC(y);
|
||||
}
|
||||
|
||||
state->mti = 0;
|
||||
}
|
||||
|
||||
#undef MAGIC
|
||||
|
||||
/* Tempering */
|
||||
|
||||
k = mt[state->mti];
|
||||
k ^= (k >> 11);
|
||||
k ^= (k << 7) & UINT32_C(0x9d2c5680);
|
||||
k ^= (k << 15) & UINT32_C(0xefc60000);
|
||||
k ^= (k >> 18);
|
||||
|
||||
state->mti++;
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
static igraph_error_t igraph_rng_mt19937_seed(void *vstate, igraph_uint_t seed) {
|
||||
igraph_i_rng_mt19937_state_t *state = vstate;
|
||||
int i;
|
||||
|
||||
memset(state, 0, sizeof(igraph_i_rng_mt19937_state_t));
|
||||
|
||||
if (seed == 0) {
|
||||
seed = 4357; /* the default seed is 4357 */
|
||||
}
|
||||
state->mt[0] = seed & UINT32_C(0xffffffff);
|
||||
|
||||
for (i = 1; i < N; i++) {
|
||||
/* See Knuth's "Art of Computer Programming" Vol. 2, 3rd
|
||||
Ed. p.106 for multiplier. */
|
||||
state->mt[i] =
|
||||
(UINT32_C(1812433253) * (state->mt[i - 1] ^ (state->mt[i - 1] >> 30)) +
|
||||
(uint32_t) i);
|
||||
state->mt[i] &= UINT32_C(0xffffffff);
|
||||
}
|
||||
|
||||
state->mti = i;
|
||||
return IGRAPH_SUCCESS;
|
||||
}
|
||||
|
||||
static igraph_error_t igraph_rng_mt19937_init(void **state) {
|
||||
igraph_i_rng_mt19937_state_t *st;
|
||||
|
||||
st = IGRAPH_CALLOC(1, igraph_i_rng_mt19937_state_t);
|
||||
IGRAPH_CHECK_OOM(st, "Cannot initialize MT19937 RNG.");
|
||||
(*state) = st;
|
||||
|
||||
igraph_rng_mt19937_seed(st, 0);
|
||||
|
||||
return IGRAPH_SUCCESS;
|
||||
}
|
||||
|
||||
static void igraph_rng_mt19937_destroy(void *vstate) {
|
||||
igraph_i_rng_mt19937_state_t *state =
|
||||
(igraph_i_rng_mt19937_state_t*) vstate;
|
||||
IGRAPH_FREE(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* \var igraph_rngtype_mt19937
|
||||
* \brief The MT19937 random number generator.
|
||||
*
|
||||
* The MT19937 generator of Makoto Matsumoto and Takuji Nishimura is a
|
||||
* variant of the twisted generalized feedback shift-register
|
||||
* algorithm, and is known as the “Mersenne Twister” generator. It has
|
||||
* a Mersenne prime period of 2^19937 - 1 (about 10^6000) and is
|
||||
* equi-distributed in 623 dimensions. It has passed the diehard
|
||||
* statistical tests. It uses 624 words of state per generator and is
|
||||
* comparable in speed to the other generators. The original generator
|
||||
* used a default seed of 4357 and choosing \c s equal to zero in
|
||||
* \c igraph_rng_mt19937_seed() reproduces this. Later versions switched to
|
||||
* 5489 as the default seed, you can choose this explicitly via
|
||||
* \ref igraph_rng_seed() instead if you require it.
|
||||
*
|
||||
* </para><para>
|
||||
* For more information see,
|
||||
* Makoto Matsumoto and Takuji Nishimura, “Mersenne Twister: A
|
||||
* 623-dimensionally equidistributed uniform pseudorandom number
|
||||
* generator”. ACM Transactions on Modeling and Computer Simulation,
|
||||
* Vol. 8, No. 1 (Jan. 1998), Pages 3–30
|
||||
*
|
||||
* </para><para>
|
||||
* The generator \c igraph_rngtype_mt19937 uses the second revision of the
|
||||
* seeding procedure published by the two authors above in 2002. The
|
||||
* original seeding procedures could cause spurious artifacts for some
|
||||
* seed values.
|
||||
*
|
||||
* </para><para>
|
||||
* This generator was ported from the GNU Scientific Library.
|
||||
*/
|
||||
|
||||
const igraph_rng_type_t igraph_rngtype_mt19937 = {
|
||||
/* name= */ "MT19937",
|
||||
/* bits= */ 32,
|
||||
/* init= */ igraph_rng_mt19937_init,
|
||||
/* destroy= */ igraph_rng_mt19937_destroy,
|
||||
/* seed= */ igraph_rng_mt19937_seed,
|
||||
/* get= */ igraph_rng_mt19937_get,
|
||||
/* get_int= */ NULL,
|
||||
/* get_real= */ NULL,
|
||||
/* get_norm= */ NULL,
|
||||
/* get_geom= */ NULL,
|
||||
/* get_binom= */ NULL,
|
||||
/* get_exp= */ NULL,
|
||||
/* get_gamma= */ NULL,
|
||||
/* get_pois= */ NULL
|
||||
};
|
||||
|
||||
#undef N
|
||||
#undef M
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
igraph library.
|
||||
Copyright (C) 2022 The igraph development team <igraph@igraph.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "igraph_random.h"
|
||||
|
||||
#include "igraph_memory.h"
|
||||
#include "igraph_types.h"
|
||||
|
||||
#include "pcg/pcg_variants.h"
|
||||
|
||||
/* The original implementation of the 32-bit PCG random number generator in this
|
||||
* file was obtained from https://github.com/imneme/pcg-c
|
||||
*
|
||||
* PCG is dual-licensed under Apache-2.0 and MIT Licenses. MIT is compatible
|
||||
* with igraph's GPLv2 license. License notices for PCG are to be found in the
|
||||
* pcg_variants.h header
|
||||
*/
|
||||
|
||||
static const pcg32_random_t pcg32_initializer = PCG32_INITIALIZER;
|
||||
|
||||
static igraph_uint_t igraph_rng_pcg32_get(void *vstate) {
|
||||
pcg32_random_t *state = (pcg32_random_t*) vstate;
|
||||
return pcg32_random_r(state);
|
||||
}
|
||||
|
||||
static igraph_error_t igraph_rng_pcg32_seed(void *vstate, igraph_uint_t seed) {
|
||||
pcg32_random_t *state = (pcg32_random_t*) vstate;
|
||||
|
||||
/* PCG32 is seeded by a 64-bit state and a 64-bit sequence number (well, only
|
||||
* 63 bits are used from the sequence number, though). Since the unified
|
||||
* igraph RNG seeding interface provides a single igraph_uint_t as the seed,
|
||||
* we use the seed to fill in the sequence number and use the state from
|
||||
* PCG32_INITIALIZER */
|
||||
if (seed == 0) {
|
||||
/* If you feel the temptation to unify the two branches by running
|
||||
* seed = pcg32_initializer.inc >> 1, don't.
|
||||
* seed is an igraph_uint_t, so it can be 32-bit or 64-bit.
|
||||
* pcg32_initializer.inc is always 64-bit.
|
||||
*/
|
||||
pcg32_srandom_r(state, pcg32_initializer.state, pcg32_initializer.inc >> 1);
|
||||
} else {
|
||||
pcg32_srandom_r(state, pcg32_initializer.state, seed);
|
||||
}
|
||||
|
||||
return IGRAPH_SUCCESS;
|
||||
}
|
||||
|
||||
static igraph_error_t igraph_rng_pcg32_init(void **state) {
|
||||
pcg32_random_t *st;
|
||||
|
||||
st = IGRAPH_CALLOC(1, pcg32_random_t);
|
||||
IGRAPH_CHECK_OOM(st, "Cannot initialize PCG32 RNG.");
|
||||
(*state) = st;
|
||||
|
||||
igraph_rng_pcg32_seed(st, 0);
|
||||
|
||||
return IGRAPH_SUCCESS;
|
||||
}
|
||||
|
||||
static void igraph_rng_pcg32_destroy(void *vstate) {
|
||||
pcg32_random_t *state = (pcg32_random_t*) vstate;
|
||||
IGRAPH_FREE(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* \var igraph_rngtype_pcg32
|
||||
* \brief The PCG random number generator (32-bit version).
|
||||
*
|
||||
* This is an implementation of the PCG random number generator; see
|
||||
* https://www.pcg-random.org for more details. This implementation returns
|
||||
* 32 random bits in a single iteration.
|
||||
*
|
||||
* </para><para>
|
||||
* The generator was ported from the original source code published by the
|
||||
* authors at https://github.com/imneme/pcg-c.
|
||||
*/
|
||||
|
||||
const igraph_rng_type_t igraph_rngtype_pcg32 = {
|
||||
/* name= */ "PCG32",
|
||||
/* bits= */ 32,
|
||||
/* init= */ igraph_rng_pcg32_init,
|
||||
/* destroy= */ igraph_rng_pcg32_destroy,
|
||||
/* seed= */ igraph_rng_pcg32_seed,
|
||||
/* get= */ igraph_rng_pcg32_get,
|
||||
/* get_int= */ NULL,
|
||||
/* get_real= */ NULL,
|
||||
/* get_norm= */ NULL,
|
||||
/* get_geom= */ NULL,
|
||||
/* get_binom= */ NULL,
|
||||
/* get_exp= */ NULL,
|
||||
/* get_gamma= */ NULL,
|
||||
/* get_pois= */ NULL
|
||||
};
|
||||
|
||||
/***** Default RNG, used upon igraph startup *****/
|
||||
|
||||
#define addr(a) (&a)
|
||||
|
||||
static pcg32_random_t igraph_i_rng_default_state = PCG32_INITIALIZER;
|
||||
|
||||
igraph_rng_t igraph_i_rng_default = {
|
||||
addr(igraph_rngtype_pcg32),
|
||||
addr(igraph_i_rng_default_state),
|
||||
/* is_seeded = */ false
|
||||
};
|
||||
|
||||
#undef addr
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
igraph library.
|
||||
Copyright (C) 2022 The igraph development team <igraph@igraph.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "igraph_random.h"
|
||||
|
||||
#include "igraph_memory.h"
|
||||
#include "igraph_types.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
/* The original implementation of the 64-bit PCG random number generator in this
|
||||
* file was obtained from https://github.com/imneme/pcg-c
|
||||
*
|
||||
* PCG is dual-licensed under Apache-2.0 and MIT Licenses. MIT is compatible
|
||||
* with igraph's GPLv2 license. License notices for PCG are to be found in the
|
||||
* pcg_variants.h header
|
||||
*/
|
||||
|
||||
#if IGRAPH_INTEGER_SIZE == 64 && defined(HAVE___UINT128_T)
|
||||
|
||||
#include "pcg/pcg_variants.h"
|
||||
|
||||
static const pcg64_random_t pcg64_initializer = PCG64_INITIALIZER;
|
||||
|
||||
static igraph_uint_t igraph_rng_pcg64_get(void *vstate) {
|
||||
pcg64_random_t *state = (pcg64_random_t*) vstate;
|
||||
return pcg64_random_r(state);
|
||||
}
|
||||
|
||||
static igraph_error_t igraph_rng_pcg64_seed(void *vstate, igraph_uint_t seed) {
|
||||
pcg64_random_t *state = (pcg64_random_t*) vstate;
|
||||
|
||||
if (seed == 0) {
|
||||
seed = (pcg64_initializer.inc >> 1);
|
||||
}
|
||||
|
||||
/* PCG64 is seeded by a 128-bit state and a 128-bit sequence number (well, only
|
||||
* 63 bits are used from the sequence number, though). Since the unified
|
||||
* igraph RNG seeding interface provides a single igraph_uint_t as the seed,
|
||||
* we use the seed to fill in the sequence number and use the state from
|
||||
* PCG64_INITIALIZER */
|
||||
pcg64_srandom_r(state, pcg64_initializer.state, seed);
|
||||
|
||||
return IGRAPH_SUCCESS;
|
||||
}
|
||||
|
||||
static igraph_error_t igraph_rng_pcg64_init(void **state) {
|
||||
pcg64_random_t *st;
|
||||
|
||||
st = IGRAPH_CALLOC(1, pcg64_random_t);
|
||||
IGRAPH_CHECK_OOM(st, "Cannot initialize PCG64 RNG.");
|
||||
(*state) = st;
|
||||
|
||||
igraph_rng_pcg64_seed(st, 0);
|
||||
|
||||
return IGRAPH_SUCCESS;
|
||||
}
|
||||
|
||||
static void igraph_rng_pcg64_destroy(void *vstate) {
|
||||
pcg64_random_t *state = (pcg64_random_t*) vstate;
|
||||
IGRAPH_FREE(state);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* Dummy implementation if the compiler does not support __uint128_t */
|
||||
|
||||
static igraph_uint_t igraph_rng_pcg64_get(void *vstate) {
|
||||
IGRAPH_UNUSED(vstate);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static igraph_error_t igraph_rng_pcg64_seed(void *vstate, igraph_uint_t seed) {
|
||||
IGRAPH_UNUSED(vstate); IGRAPH_UNUSED(seed);
|
||||
IGRAPH_ERROR("64-bit PCG generator needs __uint128_t.", IGRAPH_UNIMPLEMENTED);
|
||||
}
|
||||
|
||||
static igraph_error_t igraph_rng_pcg64_init(void **state) {
|
||||
IGRAPH_UNUSED(state);
|
||||
IGRAPH_ERROR("64-bit PCG generator needs __uint128_t.", IGRAPH_UNIMPLEMENTED);
|
||||
}
|
||||
|
||||
static void igraph_rng_pcg64_destroy(void *vstate) {
|
||||
IGRAPH_UNUSED(vstate);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \var igraph_rngtype_pcg64
|
||||
* \brief The PCG random number generator (64-bit version).
|
||||
*
|
||||
* This is an implementation of the PCG random number generator; see
|
||||
* https://www.pcg-random.org for more details. This implementation returns
|
||||
* 64 random bits in a single iteration. It is only available on 64-bit plaforms
|
||||
* with compilers that provide the __uint128_t type.
|
||||
*
|
||||
* </para><para>
|
||||
* PCG64 typically provides better performance than PCG32 when sampling floating
|
||||
* point numbers or very large integers, as it can provide twice as many random
|
||||
* bits in a single generation round.
|
||||
*
|
||||
* </para><para>
|
||||
* The generator was ported from the original source code published by the
|
||||
* authors at https://github.com/imneme/pcg-c.
|
||||
*/
|
||||
|
||||
const igraph_rng_type_t igraph_rngtype_pcg64 = {
|
||||
/* name= */ "PCG64",
|
||||
/* bits= */ 64,
|
||||
/* init= */ igraph_rng_pcg64_init,
|
||||
/* destroy= */ igraph_rng_pcg64_destroy,
|
||||
/* seed= */ igraph_rng_pcg64_seed,
|
||||
/* get= */ igraph_rng_pcg64_get,
|
||||
/* get_int= */ NULL,
|
||||
/* get_real= */ NULL,
|
||||
/* get_norm= */ NULL,
|
||||
/* get_geom= */ NULL,
|
||||
/* get_binom= */ NULL,
|
||||
/* get_exp= */ NULL,
|
||||
/* get_gamma= */ NULL,
|
||||
/* get_pois= */ NULL
|
||||
};
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
igraph library.
|
||||
Copyright (C) 2025 The igraph development team
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "igraph_sampling.h"
|
||||
|
||||
/**
|
||||
* \function igraph_rng_sample_sphere_surface
|
||||
* \brief Sample points uniformly from the surface of a sphere.
|
||||
*
|
||||
* The center of the sphere is at the origin.
|
||||
*
|
||||
* \param rng The random number generator to use.
|
||||
* \param dim The dimension of the random vectors.
|
||||
* \param n The number of vectors to sample.
|
||||
* \param radius Radius of the sphere, it must be positive.
|
||||
* \param positive Whether to restrict sampling to the positive
|
||||
* orthant.
|
||||
* \param res Pointer to an initialized matrix, the result is
|
||||
* stored here, each column will be a sampled vector. The matrix is
|
||||
* resized, as needed.
|
||||
* \return Error code.
|
||||
*
|
||||
* Time complexity: O(n*dim*g), where g is the time complexity of
|
||||
* generating a standard normal random number.
|
||||
*
|
||||
* \sa \ref igraph_rng_sample_sphere_volume(), \ref
|
||||
* igraph_rng_sample_dirichlet() for other similar samplers.
|
||||
*/
|
||||
igraph_error_t igraph_rng_sample_sphere_surface(
|
||||
igraph_rng_t* rng, igraph_int_t dim, igraph_int_t n, igraph_real_t radius,
|
||||
igraph_bool_t positive, igraph_matrix_t *res
|
||||
) {
|
||||
igraph_int_t i, j;
|
||||
|
||||
if (dim < 2) {
|
||||
IGRAPH_ERROR("Sphere must be at least two dimensional to sample from "
|
||||
"surface.", IGRAPH_EINVAL);
|
||||
}
|
||||
if (n < 0) {
|
||||
IGRAPH_ERROR("Number of samples must be non-negative.", IGRAPH_EINVAL);
|
||||
}
|
||||
if (radius <= 0) {
|
||||
IGRAPH_ERROR("Sphere radius must be positive.", IGRAPH_EINVAL);
|
||||
}
|
||||
|
||||
IGRAPH_CHECK(igraph_matrix_resize(res, dim, n));
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
igraph_real_t *col = &MATRIX(*res, 0, i);
|
||||
igraph_real_t sum = 0.0;
|
||||
for (j = 0; j < dim; j++) {
|
||||
col[j] = igraph_rng_get_normal(rng, 0, 1);
|
||||
sum += col[j] * col[j];
|
||||
}
|
||||
sum = sqrt(sum);
|
||||
for (j = 0; j < dim; j++) {
|
||||
col[j] = radius * col[j] / sum;
|
||||
}
|
||||
if (positive) {
|
||||
for (j = 0; j < dim; j++) {
|
||||
col[j] = fabs(col[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return IGRAPH_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \function igraph_rng_sample_sphere_volume
|
||||
* \brief Sample points uniformly from the volume of a sphere.
|
||||
*
|
||||
* The center of the sphere is at the origin.
|
||||
*
|
||||
* \param rng The random number generator to use.
|
||||
* \param dim The dimension of the random vectors.
|
||||
* \param n The number of vectors to sample.
|
||||
* \param radius Radius of the sphere, it must be positive.
|
||||
* \param positive Whether to restrict sampling to the positive
|
||||
* orthant.
|
||||
* \param res Pointer to an initialized matrix, the result is
|
||||
* stored here, each column will be a sampled vector. The matrix is
|
||||
* resized, as needed.
|
||||
* \return Error code.
|
||||
*
|
||||
* Time complexity: O(n*dim*g), where g is the time complexity of
|
||||
* generating a standard normal random number.
|
||||
*
|
||||
* \sa \ref igraph_rng_sample_sphere_surface(), \ref
|
||||
* igraph_rng_sample_dirichlet() for other similar samplers.
|
||||
*/
|
||||
igraph_error_t igraph_rng_sample_sphere_volume(
|
||||
igraph_rng_t* rng, igraph_int_t dim, igraph_int_t n, igraph_real_t radius,
|
||||
igraph_bool_t positive, igraph_matrix_t *res
|
||||
) {
|
||||
|
||||
igraph_int_t i, j;
|
||||
|
||||
/* Arguments are checked by the following call */
|
||||
|
||||
IGRAPH_CHECK(igraph_rng_sample_sphere_surface(rng, dim, n, radius, positive, res));
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
igraph_real_t *col = &MATRIX(*res, 0, i);
|
||||
igraph_real_t U = pow(igraph_rng_get_unif01(rng), 1.0 / dim);
|
||||
for (j = 0; j < dim; j++) {
|
||||
col[j] *= U;
|
||||
}
|
||||
}
|
||||
|
||||
return IGRAPH_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \function igraph_rng_sample_dirichlet
|
||||
* \brief Sample points from a Dirichlet distribution.
|
||||
*
|
||||
* \param rng The random number generator to use.
|
||||
* \param n The number of vectors to sample.
|
||||
* \param alpha The parameters of the Dirichlet distribution. They
|
||||
* must be positive. The length of this vector gives the dimension
|
||||
* of the generated samples.
|
||||
* \param res Pointer to an initialized matrix, the result is stored
|
||||
* here, one sample in each column. It will be resized, as needed.
|
||||
* \return Error code.
|
||||
*
|
||||
* Time complexity: O(n * dim * g), where dim is the dimension of the
|
||||
* sample vectors, set by the length of alpha, and g is the time
|
||||
* complexity of sampling from a Gamma distribution.
|
||||
*
|
||||
* \sa \ref igraph_rng_sample_sphere_surface() and
|
||||
* \ref igraph_rng_sample_sphere_volume() for other methods to sample
|
||||
* latent vectors.
|
||||
*/
|
||||
igraph_error_t igraph_rng_sample_dirichlet(
|
||||
igraph_rng_t* rng, igraph_int_t n, const igraph_vector_t *alpha,
|
||||
igraph_matrix_t *res
|
||||
) {
|
||||
|
||||
igraph_int_t len = igraph_vector_size(alpha);
|
||||
igraph_int_t i, j;
|
||||
igraph_real_t sum, num;
|
||||
|
||||
if (n < 0) {
|
||||
IGRAPH_ERRORF("Number of samples should be non-negative, got %" IGRAPH_PRId ".",
|
||||
IGRAPH_EINVAL, n);
|
||||
}
|
||||
|
||||
if (len < 2) {
|
||||
IGRAPH_ERRORF("Dirichlet parameter vector too short, must "
|
||||
"have at least two entries, got %" IGRAPH_PRId
|
||||
".", IGRAPH_EINVAL, len);
|
||||
}
|
||||
|
||||
if (igraph_vector_min(alpha) <= 0) {
|
||||
IGRAPH_ERRORF("Dirichlet concentration parameters must be positive, got %g.",
|
||||
IGRAPH_EINVAL, igraph_vector_min(alpha));
|
||||
}
|
||||
|
||||
IGRAPH_CHECK(igraph_matrix_resize(res, len, n));
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
for (j = 0, sum = 0.0; j < len; j++) {
|
||||
num = igraph_rng_get_gamma(rng, VECTOR(*alpha)[j], 1.0);
|
||||
sum += num;
|
||||
MATRIX(*res, j, i) = num;
|
||||
}
|
||||
for (j = 0; j < len; j++) {
|
||||
MATRIX(*res, j, i) /= sum;
|
||||
}
|
||||
}
|
||||
|
||||
return IGRAPH_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user