Add graph references

This commit is contained in:
Abdelrahman Said
2026-06-28 13:49:01 +01:00
parent 0a9807e448
commit a11edf0c53
2578 changed files with 868045 additions and 0 deletions
+46
View File
@@ -0,0 +1,46 @@
# Declare the files needed to compile our vendored plfit copy
add_library(
plfit_vendored
OBJECT
EXCLUDE_FROM_ALL
gss.c
hzeta.c
kolmogorov.c
lbfgs.c
mt.c
options.c
plfit.c
plfit_error.c
rbinom.c
sampling.c
)
target_include_directories(
plfit_vendored
PRIVATE
${PROJECT_SOURCE_DIR}/include
${PROJECT_BINARY_DIR}/include
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)
if (BUILD_SHARED_LIBS)
set_property(TARGET plfit_vendored PROPERTY POSITION_INDEPENDENT_CODE ON)
endif()
# Since these are included as object files, they should call the
# function as is (without visibility specification)
target_compile_definitions(plfit_vendored PRIVATE IGRAPH_STATIC)
use_all_warnings(plfit_vendored)
if (MSVC)
target_compile_options(
plfit_vendored PRIVATE
/wd4100
) # disable unreferenced parameter warning
endif()
if(IGRAPH_OPENMP_SUPPORT)
target_link_libraries(plfit_vendored PRIVATE OpenMP::OpenMP_C)
endif()
+133
View File
@@ -0,0 +1,133 @@
/*
* ANSI C implementation of vector operations.
*
* Copyright (c) 2007-2010 Naoaki Okazaki
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/* $Id$ */
#include <stdlib.h>
#include <memory.h>
#if LBFGS_FLOAT == 32 && LBFGS_IEEE_FLOAT
#define fsigndiff(x, y) (((*(uint32_t*)(x)) ^ (*(uint32_t*)(y))) & 0x80000000U)
#else
#define fsigndiff(x, y) (*(x) * (*(y) / fabs(*(y))) < 0.)
#endif/*LBFGS_IEEE_FLOAT*/
inline static void* vecalloc(size_t size)
{
void *memblock = malloc(size);
if (memblock) {
memset(memblock, 0, size);
}
return memblock;
}
inline static void vecfree(void *memblock)
{
free(memblock);
}
inline static void vecset(lbfgsfloatval_t *x, const lbfgsfloatval_t c, const int n)
{
int i;
for (i = 0;i < n;++i) {
x[i] = c;
}
}
inline static void veccpy(lbfgsfloatval_t *y, const lbfgsfloatval_t *x, const int n)
{
int i;
for (i = 0;i < n;++i) {
y[i] = x[i];
}
}
inline static void vecncpy(lbfgsfloatval_t *y, const lbfgsfloatval_t *x, const int n)
{
int i;
for (i = 0;i < n;++i) {
y[i] = -x[i];
}
}
inline static void vecadd(lbfgsfloatval_t *y, const lbfgsfloatval_t *x, const lbfgsfloatval_t c, const int n)
{
int i;
for (i = 0;i < n;++i) {
y[i] += c * x[i];
}
}
inline static void vecdiff(lbfgsfloatval_t *z, const lbfgsfloatval_t *x, const lbfgsfloatval_t *y, const int n)
{
int i;
for (i = 0;i < n;++i) {
z[i] = x[i] - y[i];
}
}
inline static void vecscale(lbfgsfloatval_t *y, const lbfgsfloatval_t c, const int n)
{
int i;
for (i = 0;i < n;++i) {
y[i] *= c;
}
}
inline static void vecmul(lbfgsfloatval_t *y, const lbfgsfloatval_t *x, const int n)
{
int i;
for (i = 0;i < n;++i) {
y[i] *= x[i];
}
}
inline static void vecdot(lbfgsfloatval_t* s, const lbfgsfloatval_t *x, const lbfgsfloatval_t *y, const int n)
{
int i;
*s = 0.;
for (i = 0;i < n;++i) {
*s += x[i] * y[i];
}
}
inline static void vec2norm(lbfgsfloatval_t* s, const lbfgsfloatval_t *x, const int n)
{
vecdot(s, x, x, n);
*s = (lbfgsfloatval_t)sqrt(*s);
}
inline static void vec2norminv(lbfgsfloatval_t* s, const lbfgsfloatval_t *x, const int n)
{
vec2norm(s, x, n);
*s = (lbfgsfloatval_t)(1.0 / *s);
}
@@ -0,0 +1,294 @@
/*
* SSE2 implementation of vector oprations (64bit double).
*
* Copyright (c) 2007-2010 Naoaki Okazaki
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/* $Id$ */
#include <stdlib.h>
#ifndef __APPLE__
#include <malloc.h>
#endif
#include <memory.h>
#if 1400 <= _MSC_VER
#include <intrin.h>
#endif/*1400 <= _MSC_VER*/
#if HAVE_EMMINTRIN_H
#include <emmintrin.h>
#endif/*HAVE_EMMINTRIN_H*/
inline static void* vecalloc(size_t size)
{
#if defined(_WIN32)
void *memblock = _aligned_malloc(size, 16);
#elif defined(__APPLE__) /* OS X always aligns on 16-byte boundaries */
void *memblock = malloc(size);
#else
void *memblock = NULL, *p = NULL;
if (posix_memalign(&p, 16, size) == 0) {
memblock = p;
}
#endif
if (memblock != NULL) {
memset(memblock, 0, size);
}
return memblock;
}
inline static void vecfree(void *memblock)
{
#ifdef _MSC_VER
_aligned_free(memblock);
#else
free(memblock);
#endif
}
#define fsigndiff(x, y) \
((_mm_movemask_pd(_mm_set_pd(*(x), *(y))) + 1) & 0x002)
#define vecset(x, c, n) \
{ \
int i; \
__m128d XMM0 = _mm_set1_pd(c); \
for (i = 0;i < (n);i += 8) { \
_mm_store_pd((x)+i , XMM0); \
_mm_store_pd((x)+i+2, XMM0); \
_mm_store_pd((x)+i+4, XMM0); \
_mm_store_pd((x)+i+6, XMM0); \
} \
}
#define veccpy(y, x, n) \
{ \
int i; \
for (i = 0;i < (n);i += 8) { \
__m128d XMM0 = _mm_load_pd((x)+i ); \
__m128d XMM1 = _mm_load_pd((x)+i+2); \
__m128d XMM2 = _mm_load_pd((x)+i+4); \
__m128d XMM3 = _mm_load_pd((x)+i+6); \
_mm_store_pd((y)+i , XMM0); \
_mm_store_pd((y)+i+2, XMM1); \
_mm_store_pd((y)+i+4, XMM2); \
_mm_store_pd((y)+i+6, XMM3); \
} \
}
#define vecncpy(y, x, n) \
{ \
int i; \
for (i = 0;i < (n);i += 8) { \
__m128d XMM0 = _mm_setzero_pd(); \
__m128d XMM1 = _mm_setzero_pd(); \
__m128d XMM2 = _mm_setzero_pd(); \
__m128d XMM3 = _mm_setzero_pd(); \
__m128d XMM4 = _mm_load_pd((x)+i ); \
__m128d XMM5 = _mm_load_pd((x)+i+2); \
__m128d XMM6 = _mm_load_pd((x)+i+4); \
__m128d XMM7 = _mm_load_pd((x)+i+6); \
XMM0 = _mm_sub_pd(XMM0, XMM4); \
XMM1 = _mm_sub_pd(XMM1, XMM5); \
XMM2 = _mm_sub_pd(XMM2, XMM6); \
XMM3 = _mm_sub_pd(XMM3, XMM7); \
_mm_store_pd((y)+i , XMM0); \
_mm_store_pd((y)+i+2, XMM1); \
_mm_store_pd((y)+i+4, XMM2); \
_mm_store_pd((y)+i+6, XMM3); \
} \
}
#define vecadd(y, x, c, n) \
{ \
int i; \
__m128d XMM7 = _mm_set1_pd(c); \
for (i = 0;i < (n);i += 4) { \
__m128d XMM0 = _mm_load_pd((x)+i ); \
__m128d XMM1 = _mm_load_pd((x)+i+2); \
__m128d XMM2 = _mm_load_pd((y)+i ); \
__m128d XMM3 = _mm_load_pd((y)+i+2); \
XMM0 = _mm_mul_pd(XMM0, XMM7); \
XMM1 = _mm_mul_pd(XMM1, XMM7); \
XMM2 = _mm_add_pd(XMM2, XMM0); \
XMM3 = _mm_add_pd(XMM3, XMM1); \
_mm_store_pd((y)+i , XMM2); \
_mm_store_pd((y)+i+2, XMM3); \
} \
}
#define vecdiff(z, x, y, n) \
{ \
int i; \
for (i = 0;i < (n);i += 8) { \
__m128d XMM0 = _mm_load_pd((x)+i ); \
__m128d XMM1 = _mm_load_pd((x)+i+2); \
__m128d XMM2 = _mm_load_pd((x)+i+4); \
__m128d XMM3 = _mm_load_pd((x)+i+6); \
__m128d XMM4 = _mm_load_pd((y)+i ); \
__m128d XMM5 = _mm_load_pd((y)+i+2); \
__m128d XMM6 = _mm_load_pd((y)+i+4); \
__m128d XMM7 = _mm_load_pd((y)+i+6); \
XMM0 = _mm_sub_pd(XMM0, XMM4); \
XMM1 = _mm_sub_pd(XMM1, XMM5); \
XMM2 = _mm_sub_pd(XMM2, XMM6); \
XMM3 = _mm_sub_pd(XMM3, XMM7); \
_mm_store_pd((z)+i , XMM0); \
_mm_store_pd((z)+i+2, XMM1); \
_mm_store_pd((z)+i+4, XMM2); \
_mm_store_pd((z)+i+6, XMM3); \
} \
}
#define vecscale(y, c, n) \
{ \
int i; \
__m128d XMM7 = _mm_set1_pd(c); \
for (i = 0;i < (n);i += 4) { \
__m128d XMM0 = _mm_load_pd((y)+i ); \
__m128d XMM1 = _mm_load_pd((y)+i+2); \
XMM0 = _mm_mul_pd(XMM0, XMM7); \
XMM1 = _mm_mul_pd(XMM1, XMM7); \
_mm_store_pd((y)+i , XMM0); \
_mm_store_pd((y)+i+2, XMM1); \
} \
}
#define vecmul(y, x, n) \
{ \
int i; \
for (i = 0;i < (n);i += 8) { \
__m128d XMM0 = _mm_load_pd((x)+i ); \
__m128d XMM1 = _mm_load_pd((x)+i+2); \
__m128d XMM2 = _mm_load_pd((x)+i+4); \
__m128d XMM3 = _mm_load_pd((x)+i+6); \
__m128d XMM4 = _mm_load_pd((y)+i ); \
__m128d XMM5 = _mm_load_pd((y)+i+2); \
__m128d XMM6 = _mm_load_pd((y)+i+4); \
__m128d XMM7 = _mm_load_pd((y)+i+6); \
XMM4 = _mm_mul_pd(XMM4, XMM0); \
XMM5 = _mm_mul_pd(XMM5, XMM1); \
XMM6 = _mm_mul_pd(XMM6, XMM2); \
XMM7 = _mm_mul_pd(XMM7, XMM3); \
_mm_store_pd((y)+i , XMM4); \
_mm_store_pd((y)+i+2, XMM5); \
_mm_store_pd((y)+i+4, XMM6); \
_mm_store_pd((y)+i+6, XMM7); \
} \
}
#if 3 <= __SSE__ || defined(__SSE3__)
/*
Horizontal add with haddps SSE3 instruction. The work register (rw)
is unused.
*/
#define __horizontal_sum(r, rw) \
r = _mm_hadd_ps(r, r); \
r = _mm_hadd_ps(r, r);
#else
/*
Horizontal add with SSE instruction. The work register (rw) is used.
*/
#define __horizontal_sum(r, rw) \
rw = r; \
r = _mm_shuffle_ps(r, rw, _MM_SHUFFLE(1, 0, 3, 2)); \
r = _mm_add_ps(r, rw); \
rw = r; \
r = _mm_shuffle_ps(r, rw, _MM_SHUFFLE(2, 3, 0, 1)); \
r = _mm_add_ps(r, rw);
#endif
#define vecdot(s, x, y, n) \
{ \
int i; \
__m128d XMM0 = _mm_setzero_pd(); \
__m128d XMM1 = _mm_setzero_pd(); \
__m128d XMM2, XMM3, XMM4, XMM5; \
for (i = 0;i < (n);i += 4) { \
XMM2 = _mm_load_pd((x)+i ); \
XMM3 = _mm_load_pd((x)+i+2); \
XMM4 = _mm_load_pd((y)+i ); \
XMM5 = _mm_load_pd((y)+i+2); \
XMM2 = _mm_mul_pd(XMM2, XMM4); \
XMM3 = _mm_mul_pd(XMM3, XMM5); \
XMM0 = _mm_add_pd(XMM0, XMM2); \
XMM1 = _mm_add_pd(XMM1, XMM3); \
} \
XMM0 = _mm_add_pd(XMM0, XMM1); \
XMM1 = _mm_shuffle_pd(XMM0, XMM0, _MM_SHUFFLE2(1, 1)); \
XMM0 = _mm_add_pd(XMM0, XMM1); \
_mm_store_sd((s), XMM0); \
}
#define vec2norm(s, x, n) \
{ \
int i; \
__m128d XMM0 = _mm_setzero_pd(); \
__m128d XMM1 = _mm_setzero_pd(); \
__m128d XMM2, XMM3, XMM4, XMM5; \
for (i = 0;i < (n);i += 4) { \
XMM2 = _mm_load_pd((x)+i ); \
XMM3 = _mm_load_pd((x)+i+2); \
XMM4 = XMM2; \
XMM5 = XMM3; \
XMM2 = _mm_mul_pd(XMM2, XMM4); \
XMM3 = _mm_mul_pd(XMM3, XMM5); \
XMM0 = _mm_add_pd(XMM0, XMM2); \
XMM1 = _mm_add_pd(XMM1, XMM3); \
} \
XMM0 = _mm_add_pd(XMM0, XMM1); \
XMM1 = _mm_shuffle_pd(XMM0, XMM0, _MM_SHUFFLE2(1, 1)); \
XMM0 = _mm_add_pd(XMM0, XMM1); \
XMM0 = _mm_sqrt_pd(XMM0); \
_mm_store_sd((s), XMM0); \
}
#define vec2norminv(s, x, n) \
{ \
int i; \
__m128d XMM0 = _mm_setzero_pd(); \
__m128d XMM1 = _mm_setzero_pd(); \
__m128d XMM2, XMM3, XMM4, XMM5; \
for (i = 0;i < (n);i += 4) { \
XMM2 = _mm_load_pd((x)+i ); \
XMM3 = _mm_load_pd((x)+i+2); \
XMM4 = XMM2; \
XMM5 = XMM3; \
XMM2 = _mm_mul_pd(XMM2, XMM4); \
XMM3 = _mm_mul_pd(XMM3, XMM5); \
XMM0 = _mm_add_pd(XMM0, XMM2); \
XMM1 = _mm_add_pd(XMM1, XMM3); \
} \
XMM2 = _mm_set1_pd(1.0); \
XMM0 = _mm_add_pd(XMM0, XMM1); \
XMM1 = _mm_shuffle_pd(XMM0, XMM0, _MM_SHUFFLE2(1, 1)); \
XMM0 = _mm_add_pd(XMM0, XMM1); \
XMM0 = _mm_sqrt_pd(XMM0); \
XMM2 = _mm_div_pd(XMM2, XMM0); \
_mm_store_sd((s), XMM2); \
}
@@ -0,0 +1,302 @@
/*
* SSE/SSE3 implementation of vector oprations (32bit float).
*
* Copyright (c) 2007-2010 Naoaki Okazaki
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/* $Id$ */
#include <stdlib.h>
#ifndef __APPLE__
#include <malloc.h>
#endif
#include <memory.h>
#if 1400 <= _MSC_VER
#include <intrin.h>
#endif/*_MSC_VER*/
#if HAVE_XMMINTRIN_H
#include <xmmintrin.h>
#endif/*HAVE_XMMINTRIN_H*/
#if LBFGS_FLOAT == 32 && LBFGS_IEEE_FLOAT
#define fsigndiff(x, y) (((*(uint32_t*)(x)) ^ (*(uint32_t*)(y))) & 0x80000000U)
#else
#define fsigndiff(x, y) (*(x) * (*(y) / fabs(*(y))) < 0.)
#endif/*LBFGS_IEEE_FLOAT*/
inline static void* vecalloc(size_t size)
{
#if defined(_MSC_VER)
void *memblock = _aligned_malloc(size, 16);
#elif defined(__APPLE__) /* OS X always aligns on 16-byte boundaries */
void *memblock = malloc(size);
#else
void *memblock = NULL, *p = NULL;
if (posix_memalign(&p, 16, size) == 0) {
memblock = p;
}
#endif
if (memblock != NULL) {
memset(memblock, 0, size);
}
return memblock;
}
inline static void vecfree(void *memblock)
{
#ifdef _MSC_VER
_aligned_free(memblock);
#else
free(memblock);
#endif
}
#define vecset(x, c, n) \
{ \
int i; \
__m128 XMM0 = _mm_set_ps1(c); \
for (i = 0;i < (n);i += 16) { \
_mm_store_ps((x)+i , XMM0); \
_mm_store_ps((x)+i+ 4, XMM0); \
_mm_store_ps((x)+i+ 8, XMM0); \
_mm_store_ps((x)+i+12, XMM0); \
} \
}
#define veccpy(y, x, n) \
{ \
int i; \
for (i = 0;i < (n);i += 16) { \
__m128 XMM0 = _mm_load_ps((x)+i ); \
__m128 XMM1 = _mm_load_ps((x)+i+ 4); \
__m128 XMM2 = _mm_load_ps((x)+i+ 8); \
__m128 XMM3 = _mm_load_ps((x)+i+12); \
_mm_store_ps((y)+i , XMM0); \
_mm_store_ps((y)+i+ 4, XMM1); \
_mm_store_ps((y)+i+ 8, XMM2); \
_mm_store_ps((y)+i+12, XMM3); \
} \
}
#define vecncpy(y, x, n) \
{ \
int i; \
const uint32_t mask = 0x80000000; \
__m128 XMM4 = _mm_load_ps1((float*)&mask); \
for (i = 0;i < (n);i += 16) { \
__m128 XMM0 = _mm_load_ps((x)+i ); \
__m128 XMM1 = _mm_load_ps((x)+i+ 4); \
__m128 XMM2 = _mm_load_ps((x)+i+ 8); \
__m128 XMM3 = _mm_load_ps((x)+i+12); \
XMM0 = _mm_xor_ps(XMM0, XMM4); \
XMM1 = _mm_xor_ps(XMM1, XMM4); \
XMM2 = _mm_xor_ps(XMM2, XMM4); \
XMM3 = _mm_xor_ps(XMM3, XMM4); \
_mm_store_ps((y)+i , XMM0); \
_mm_store_ps((y)+i+ 4, XMM1); \
_mm_store_ps((y)+i+ 8, XMM2); \
_mm_store_ps((y)+i+12, XMM3); \
} \
}
#define vecadd(y, x, c, n) \
{ \
int i; \
__m128 XMM7 = _mm_set_ps1(c); \
for (i = 0;i < (n);i += 8) { \
__m128 XMM0 = _mm_load_ps((x)+i ); \
__m128 XMM1 = _mm_load_ps((x)+i+4); \
__m128 XMM2 = _mm_load_ps((y)+i ); \
__m128 XMM3 = _mm_load_ps((y)+i+4); \
XMM0 = _mm_mul_ps(XMM0, XMM7); \
XMM1 = _mm_mul_ps(XMM1, XMM7); \
XMM2 = _mm_add_ps(XMM2, XMM0); \
XMM3 = _mm_add_ps(XMM3, XMM1); \
_mm_store_ps((y)+i , XMM2); \
_mm_store_ps((y)+i+4, XMM3); \
} \
}
#define vecdiff(z, x, y, n) \
{ \
int i; \
for (i = 0;i < (n);i += 16) { \
__m128 XMM0 = _mm_load_ps((x)+i ); \
__m128 XMM1 = _mm_load_ps((x)+i+ 4); \
__m128 XMM2 = _mm_load_ps((x)+i+ 8); \
__m128 XMM3 = _mm_load_ps((x)+i+12); \
__m128 XMM4 = _mm_load_ps((y)+i ); \
__m128 XMM5 = _mm_load_ps((y)+i+ 4); \
__m128 XMM6 = _mm_load_ps((y)+i+ 8); \
__m128 XMM7 = _mm_load_ps((y)+i+12); \
XMM0 = _mm_sub_ps(XMM0, XMM4); \
XMM1 = _mm_sub_ps(XMM1, XMM5); \
XMM2 = _mm_sub_ps(XMM2, XMM6); \
XMM3 = _mm_sub_ps(XMM3, XMM7); \
_mm_store_ps((z)+i , XMM0); \
_mm_store_ps((z)+i+ 4, XMM1); \
_mm_store_ps((z)+i+ 8, XMM2); \
_mm_store_ps((z)+i+12, XMM3); \
} \
}
#define vecscale(y, c, n) \
{ \
int i; \
__m128 XMM7 = _mm_set_ps1(c); \
for (i = 0;i < (n);i += 8) { \
__m128 XMM0 = _mm_load_ps((y)+i ); \
__m128 XMM1 = _mm_load_ps((y)+i+4); \
XMM0 = _mm_mul_ps(XMM0, XMM7); \
XMM1 = _mm_mul_ps(XMM1, XMM7); \
_mm_store_ps((y)+i , XMM0); \
_mm_store_ps((y)+i+4, XMM1); \
} \
}
#define vecmul(y, x, n) \
{ \
int i; \
for (i = 0;i < (n);i += 16) { \
__m128 XMM0 = _mm_load_ps((x)+i ); \
__m128 XMM1 = _mm_load_ps((x)+i+ 4); \
__m128 XMM2 = _mm_load_ps((x)+i+ 8); \
__m128 XMM3 = _mm_load_ps((x)+i+12); \
__m128 XMM4 = _mm_load_ps((y)+i ); \
__m128 XMM5 = _mm_load_ps((y)+i+ 4); \
__m128 XMM6 = _mm_load_ps((y)+i+ 8); \
__m128 XMM7 = _mm_load_ps((y)+i+12); \
XMM4 = _mm_mul_ps(XMM4, XMM0); \
XMM5 = _mm_mul_ps(XMM5, XMM1); \
XMM6 = _mm_mul_ps(XMM6, XMM2); \
XMM7 = _mm_mul_ps(XMM7, XMM3); \
_mm_store_ps((y)+i , XMM4); \
_mm_store_ps((y)+i+ 4, XMM5); \
_mm_store_ps((y)+i+ 8, XMM6); \
_mm_store_ps((y)+i+12, XMM7); \
} \
}
#if 3 <= __SSE__ || defined(__SSE3__)
/*
Horizontal add with haddps SSE3 instruction. The work register (rw)
is unused.
*/
#define __horizontal_sum(r, rw) \
r = _mm_hadd_ps(r, r); \
r = _mm_hadd_ps(r, r);
#else
/*
Horizontal add with SSE instruction. The work register (rw) is used.
*/
#define __horizontal_sum(r, rw) \
rw = r; \
r = _mm_shuffle_ps(r, rw, _MM_SHUFFLE(1, 0, 3, 2)); \
r = _mm_add_ps(r, rw); \
rw = r; \
r = _mm_shuffle_ps(r, rw, _MM_SHUFFLE(2, 3, 0, 1)); \
r = _mm_add_ps(r, rw);
#endif
#define vecdot(s, x, y, n) \
{ \
int i; \
__m128 XMM0 = _mm_setzero_ps(); \
__m128 XMM1 = _mm_setzero_ps(); \
__m128 XMM2, XMM3, XMM4, XMM5; \
for (i = 0;i < (n);i += 8) { \
XMM2 = _mm_load_ps((x)+i ); \
XMM3 = _mm_load_ps((x)+i+4); \
XMM4 = _mm_load_ps((y)+i ); \
XMM5 = _mm_load_ps((y)+i+4); \
XMM2 = _mm_mul_ps(XMM2, XMM4); \
XMM3 = _mm_mul_ps(XMM3, XMM5); \
XMM0 = _mm_add_ps(XMM0, XMM2); \
XMM1 = _mm_add_ps(XMM1, XMM3); \
} \
XMM0 = _mm_add_ps(XMM0, XMM1); \
__horizontal_sum(XMM0, XMM1); \
_mm_store_ss((s), XMM0); \
}
#define vec2norm(s, x, n) \
{ \
int i; \
__m128 XMM0 = _mm_setzero_ps(); \
__m128 XMM1 = _mm_setzero_ps(); \
__m128 XMM2, XMM3; \
for (i = 0;i < (n);i += 8) { \
XMM2 = _mm_load_ps((x)+i ); \
XMM3 = _mm_load_ps((x)+i+4); \
XMM2 = _mm_mul_ps(XMM2, XMM2); \
XMM3 = _mm_mul_ps(XMM3, XMM3); \
XMM0 = _mm_add_ps(XMM0, XMM2); \
XMM1 = _mm_add_ps(XMM1, XMM3); \
} \
XMM0 = _mm_add_ps(XMM0, XMM1); \
__horizontal_sum(XMM0, XMM1); \
XMM2 = XMM0; \
XMM1 = _mm_rsqrt_ss(XMM0); \
XMM3 = XMM1; \
XMM1 = _mm_mul_ss(XMM1, XMM1); \
XMM1 = _mm_mul_ss(XMM1, XMM3); \
XMM1 = _mm_mul_ss(XMM1, XMM0); \
XMM1 = _mm_mul_ss(XMM1, _mm_set_ss(-0.5f)); \
XMM3 = _mm_mul_ss(XMM3, _mm_set_ss(1.5f)); \
XMM3 = _mm_add_ss(XMM3, XMM1); \
XMM3 = _mm_mul_ss(XMM3, XMM2); \
_mm_store_ss((s), XMM3); \
}
#define vec2norminv(s, x, n) \
{ \
int i; \
__m128 XMM0 = _mm_setzero_ps(); \
__m128 XMM1 = _mm_setzero_ps(); \
__m128 XMM2, XMM3; \
for (i = 0;i < (n);i += 16) { \
XMM2 = _mm_load_ps((x)+i ); \
XMM3 = _mm_load_ps((x)+i+4); \
XMM2 = _mm_mul_ps(XMM2, XMM2); \
XMM3 = _mm_mul_ps(XMM3, XMM3); \
XMM0 = _mm_add_ps(XMM0, XMM2); \
XMM1 = _mm_add_ps(XMM1, XMM3); \
} \
XMM0 = _mm_add_ps(XMM0, XMM1); \
__horizontal_sum(XMM0, XMM1); \
XMM2 = XMM0; \
XMM1 = _mm_rsqrt_ss(XMM0); \
XMM3 = XMM1; \
XMM1 = _mm_mul_ss(XMM1, XMM1); \
XMM1 = _mm_mul_ss(XMM1, XMM3); \
XMM1 = _mm_mul_ss(XMM1, XMM0); \
XMM1 = _mm_mul_ss(XMM1, _mm_set_ss(-0.5f)); \
XMM3 = _mm_mul_ss(XMM3, _mm_set_ss(1.5f)); \
XMM3 = _mm_add_ss(XMM3, XMM1); \
_mm_store_ss((s), XMM3); \
}
+152
View File
@@ -0,0 +1,152 @@
/* gss.c
*
* Copyright (C) 2012 Tamas Nepusz
*
* 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 <float.h>
#include <math.h>
#include <string.h>
#include "plfit_error.h"
#include "gss.h"
/**
* \def PHI
*
* The golden ratio, i.e. 1+sqrt(5)/2
*/
#define PHI 1.618033988749895
/**
* \def RESPHI
*
* Constant defined as 2 - \c PHI
*/
#define RESPHI 0.3819660112501051
/**
* \const _defparam
*
* Default parameters for the GSS algorithm.
*/
static const gss_parameter_t _defparam = {
/* .epsilon = */ DBL_MIN,
/* .on_error = */ GSS_ERROR_STOP
};
/**
* Stores whether the last optimization run triggered a warning or not.
*/
static unsigned short int gss_i_warning_flag = 0;
void gss_parameter_init(gss_parameter_t *param) {
memcpy(param, &_defparam, sizeof(*param));
}
unsigned short int gss_get_warning_flag(void) {
return gss_i_warning_flag;
}
#define TERMINATE { \
if (_min) { \
*(_min) = min; \
} \
if (_fmin) { \
*(_fmin) = fmin; \
} \
}
#define EVALUATE(x, fx) { \
fx = proc_evaluate(instance, x); \
if (fmin > fx) { \
min = x; \
fmin = fx; \
} \
if (proc_progress) { \
retval = proc_progress(instance, x, fx, min, fmin, \
(a < b) ? a : b, (a < b) ? b : a, k); \
if (retval) { \
TERMINATE; \
return PLFIT_SUCCESS; \
} \
} \
}
int gss(double a, double b, double *_min, double *_fmin,
gss_evaluate_t proc_evaluate, gss_progress_t proc_progress,
void* instance, const gss_parameter_t *_param) {
double c, d, min;
double fa, fb, fc, fd, fmin;
int k = 0;
int retval;
unsigned short int successful = 1;
gss_parameter_t param = _param ? (*_param) : _defparam;
gss_i_warning_flag = 0;
if (a > b) {
c = a; a = b; b = c;
}
min = a;
fmin = proc_evaluate(instance, a);
c = a + RESPHI*(b-a);
EVALUATE(a, fa);
EVALUATE(b, fb);
EVALUATE(c, fc);
if (fc >= fa || fc >= fb) {
if (param.on_error == GSS_ERROR_STOP) {
return PLFIT_FAILURE;
} else {
gss_i_warning_flag = 1;
}
}
while (fabs(a-b) > param.epsilon) {
k++;
d = c + RESPHI*(b-c);
EVALUATE(d, fd);
if (fd >= fa || fd >= fb) {
if (param.on_error == GSS_ERROR_STOP) {
successful = 0;
break;
} else {
gss_i_warning_flag = 1;
}
}
if (fc <= fd) {
b = a; a = d;
} else {
a = c; c = d; fc = fd;
}
}
if (successful) {
c = (a+b) / 2.0;
k++;
EVALUATE(c, fc);
TERMINATE;
}
return successful ? PLFIT_SUCCESS : PLFIT_FAILURE;
}
+138
View File
@@ -0,0 +1,138 @@
/* gss.h
*
* Copyright (C) 2012 Tamas Nepusz
*
* 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.
*/
#ifndef __GSS_H__
#define __GSS_H__
#include "plfit_decls.h"
PLFIT_BEGIN_C_DECLS
/**
* Enum specifying what the search should do when the function is not U-shaped.
*/
typedef enum {
GSS_ERROR_STOP, /**< Stop and return an error code */
GSS_ERROR_WARN /**< Continue and set the warning flag */
} gss_error_handling_t;
/**
* Parameter settings for a golden section search.
*/
typedef struct {
double epsilon;
gss_error_handling_t on_error;
} gss_parameter_t;
/**
* Callback interface to provide objective function evaluations for the golden
* section search.
*
* The gss() function calls this function to obtain the values of the objective
* function when needed. A client program must implement this function to evaluate
* the value of the objective function, given the location.
*
* @param instance The user data sent for the gss() function by the client.
* @param x The current value of the variable.
* @retval double The value of the objective function for the current
* variable.
*/
typedef double (*gss_evaluate_t)(void *instance, double x);
/**
* Callback interface to receive the progress of the optimization process for
* the golden section search.
*
* The gss() function calls this function for each iteration. Implementing
* this function, a client program can store or display the current progress
* of the optimization process.
*
* @param instance The user data sent for the gss() function by the client.
* @param x The current value of the variable.
* @param fx The value of the objective function at x.
* @param min The location of the minimum value of the objective
* function found so far.
* @param fmin The minimum value of the objective function found so far.
* @param left The left side of the current bracket.
* @param right The right side of the current bracket.
* @param k The index of the current iteration.
* @retval int Zero to continue the optimization process. Returning a
* non-zero value will cancel the optimization process.
*/
typedef int (*gss_progress_t)(void *instance, double x, double fx, double min,
double fmin, double left, double right, int k);
/**
* Start a golden section search optimization.
*
* @param a The left side of the bracket to start from
* @param b The right side of the bracket to start from
* @param min The pointer to the variable that receives the location of the
* final value of the objective function. This argument can be set to
* \c NULL if the location of the final value of the objective
* function is unnecessary.
* @param fmin The pointer to the variable that receives the final value of
* the objective function. This argument can be st to \c NULL if the
* final value of the objective function is unnecessary.
* @param proc_evaluate The callback function to evaluate the objective
* function at a given location.
* @param proc_progress The callback function to receive the progress (the
* last evaluated location, the value of the objective
* function at that location, the width of the current
* bracket, the minimum found so far and the step
* count). This argument can be set to \c NULL if
* a progress report is unnecessary.
* @param instance A user data for the client program. The callback
* functions will receive the value of this argument.
* @param param The pointer to a structure representing parameters for
* GSS algorithm. A client program can set this parameter
* to \c NULL to use the default parameters.
* Call the \ref gss_parameter_init() function to fill a
* structure with the default values.
* @retval int The status code. This function returns zero if the
* minimization process terminates without an error. A
* non-zero value indicates an error; in particular,
* \c PLFIT_FAILURE means that the function is not
* U-shaped.
*/
int gss(double a, double b, double *min, double *fmin,
gss_evaluate_t proc_evaluate, gss_progress_t proc_progress,
void* instance, const gss_parameter_t *_param);
/**
* Return the state of the warning flag.
*
* The warning flag is 1 if the last optimization was run on a function that
* was not U-shaped.
*/
unsigned short int gss_get_warning_flag(void);
/**
* Initialize GSS parameters to the default values.
*
* Call this function to fill a parameter structure with the default values
* and overwrite parameter values if necessary.
*
* @param param The pointer to the parameter structure.
*/
void gss_parameter_init(gss_parameter_t *param);
PLFIT_END_C_DECLS
#endif /* __GSS_H__ */
+672
View File
@@ -0,0 +1,672 @@
/* vim:set ts=4 sw=2 sts=2 et: */
/* This file was imported from a private scientific library
* based on GSL coined Home Scientific Libray (HSL) by its author
* Jerome Benoit; this very material is itself inspired from the
* material written by G. Jungan and distributed by GSL.
* Ultimately, some modifications were done in order to render the
* imported material independent from the rest of GSL.
*/
/* `hsl/specfunc/hzeta.c' C source file
// HSL - Home Scientific Library
// Copyright (C) 2017-2022 Jerome Benoit
//
// HSL 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.
*/
/*
// The material in this file is mainly inspired by the material written by
// G. Jungan and distributed under GPLv2 by the GNU Scientific Library (GSL)
// ( https://www.gnu.org/software/gsl/ [specfunc/zeta.c]), itself inspired by
// the material written by Moshier and distributed in the Cephes Mathematical
// Library ( http://www.moshier.net/ [zeta.c]).
//
// More specifically, hsl_sf_hzeta_e is a slightly modifed clone of
// gsl_sf_hzeta_e as found in GSL 2.4; the remaining is `inspired by'.
// [Sooner or later a _Working_Note_ may be deposited at ResearchGate
// ( https://www.researchgate.net/profile/Jerome_Benoit )]
*/
/* Author: Jerome G. Benoit < jgmbenoit _at_ rezozer _dot_ net > */
#ifdef _MSC_VER
#define _USE_MATH_DEFINES
#endif
/* Work around bug in some Windows SDK / MSVC versions where NAN is not a
* constant expression, triggering an error in the definition of
* hsl_sf_hzeta_eulermaclaurin_series_coeffs[] and
* hsl_sf_hzeta_eulermaclaurin_series_majorantratios[] below.
* We re-define NAN to the value it had in earlier MSVC versions.
* See https://github.com/igraph/igraph/issues/2701
* and https://developercommunity.visualstudio.com/t/NAN-is-no-longer-compile-time-constant-i/10688907
*/
#ifdef _MSC_VER
#define _UCRT_NOISY_NAN
#endif
#include <math.h>
#include <stdio.h>
#include "hzeta.h"
#include "plfit_error.h"
/* imported from gsl_machine.h */
#define GSL_LOG_DBL_MIN (-7.0839641853226408e+02)
#define GSL_LOG_DBL_MAX 7.0978271289338397e+02
#define GSL_DBL_EPSILON 2.2204460492503131e-16
/* Math constants are not part of standard C.
* The following are borrowed from igraph/src/core/math.h */
#ifndef M_LOG2E
#define M_LOG2E 1.44269504088896340735992468100189214
#endif
#ifndef M_LN2
#define M_LN2 0.693147180559945309417232121458176568
#endif
/* imported from gsl_sf_result.h */
struct gsl_sf_result_struct {
double val;
double err;
};
typedef struct gsl_sf_result_struct gsl_sf_result;
/* imported and adapted from hsl/specfunc/specfunc_def.h */
#define HSL_SF_EVAL_RESULT(FnE) \
gsl_sf_result result; \
FnE ; \
return (result.val);
#define HSL_SF_EVAL_TUPLE_RESULT(FnET) \
gsl_sf_result result0; \
gsl_sf_result result1; \
FnET ; \
*tuple1=result1.val; \
*tuple0=result0.val; \
return (result0.val);
/* */
#define HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT 10
#define HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER 32
#define HSL_SF_LNHZETA_EULERMACLAURIN_SERIES_SHIFT_MAX 256
// B_{2j}/(2j)
static
double hsl_sf_hzeta_eulermaclaurin_series_coeffs[HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+2]={
+1.0,
+1.0/12.0,
-1.0/720.0,
+1.0/30240.0,
-1.0/1209600.0,
+1.0/47900160.0,
-691.0/1307674368000.0,
+1.0/74724249600.0,
-3.38968029632258286683019539125e-13,
+8.58606205627784456413590545043e-15,
-2.17486869855806187304151642387e-16,
+5.50900282836022951520265260890e-18,
-1.39544646858125233407076862641e-19,
+3.53470703962946747169322997780e-21,
-8.95351742703754685040261131811e-23,
+2.26795245233768306031095073887e-24,
-5.74479066887220244526388198761e-26,
+1.45517247561486490186626486727e-27,
-3.68599494066531017818178247991e-29,
+9.33673425709504467203255515279e-31,
-2.36502241570062993455963519637e-32,
+5.99067176248213430465991239682e-34,
-1.51745488446829026171081313586e-35,
+3.84375812545418823222944529099e-37,
-9.73635307264669103526762127925e-39,
+2.46624704420068095710640028029e-40,
-6.24707674182074369314875679472e-42,
+1.58240302446449142975108170683e-43,
-4.00827368594893596853001219052e-45,
+1.01530758555695563116307139454e-46,
-2.57180415824187174992481940976e-48,
+6.51445603523381493155843485864e-50,
-1.65013099068965245550609878048e-51,
NAN}; // hsl_sf_hzeta_eulermaclaurin_series_coeffs
// 4\zeta(2j)/(2\pi)^(2j)
static
double hsl_sf_hzeta_eulermaclaurin_series_majorantratios[HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+2]={
-2.0,
+1.0/6.0,
+1.0/360.0,
+1.0/15120.0,
+1.0/604800.0,
+1.0/23950080.0,
+691.0/653837184000.0,
+1.0/37362124800.0,
+3617.0/5335311421440000.0,
+1.71721241125556891282718109009e-14,
+4.34973739711612374608303284773e-16,
+1.10180056567204590304053052178e-17,
+2.79089293716250466814153725281e-19,
+7.06941407925893494338645995561e-21,
+1.79070348540750937008052226362e-22,
+4.53590490467536612062190147774e-24,
+1.14895813377444048905277639752e-25,
+2.91034495122972980373252973454e-27,
+7.37198988133062035636356495982e-29,
+1.86734685141900893440651103056e-30,
+4.73004483140125986911927039274e-32,
+1.19813435249642686093198247936e-33,
+3.03490976893658052342162627173e-35,
+7.68751625090837646445889058198e-37,
+1.94727061452933820705352425585e-38,
+4.93249408840136191421280056051e-40,
+1.24941534836414873862975135893e-41,
+3.16480604892898285950216341362e-43,
+8.01654737189787193706002438098e-45,
+2.03061517111391126232614278906e-46,
+5.14360831648374349984963881946e-48,
+1.30289120704676298631168697172e-49,
+3.30026198137930491101219756091e-51,
NAN}; // hsl_sf_hzeta_eulermaclaurin_series_majorantratios
extern
int hsl_sf_hzeta_e(const double s, const double q, gsl_sf_result * result) {
/* CHECK_POINTER(result) */
if ((s <= 1.0) || (q <= 0.0)) {
PLFIT_ERROR("s must be larger than 1.0 and q must be larger than zero", PLFIT_EINVAL);
}
else {
const double max_bits=54.0; // max_bits=\lceil{s}\rceil with \zeta(s,2)=\zeta(s)-1=GSL_DBL_EPSILON
const double ln_term0=-s*log(q);
if (ln_term0 < GSL_LOG_DBL_MIN+1.0) {
PLFIT_ERROR("underflow", PLFIT_UNDRFLOW);
}
else if (GSL_LOG_DBL_MAX-1.0 < ln_term0) {
PLFIT_ERROR("overflow", PLFIT_OVERFLOW);
}
#if 1
else if (((max_bits < s) && (q < 1.0)) || ((0.5*max_bits < s) && (q < 0.25))) {
result->val=pow(q,-s);
result->err=2.0*GSL_DBL_EPSILON*fabs(result->val);
return (PLFIT_SUCCESS);
}
else if ((0.5*max_bits < s) && (q < 1.0)) {
const double a0=pow(q,-s);
const double p1=pow(q/(1.0+q),s);
const double p2=pow(q/(2.0+q),s);
const double ans=a0*(1.0+p1+p2);
result->val=ans;
result->err=GSL_DBL_EPSILON*(2.0+0.5*s)*fabs(result->val);
return (PLFIT_SUCCESS);
}
#endif
else { // Euler-Maclaurin summation formula
const double qshift=HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+q;
const double inv_qshift=1.0/qshift;
const double sqr_inv_qshift=inv_qshift*inv_qshift;
const double inv_sm1=1.0/(s-1.0);
const double pmax=pow(qshift,-s);
double terms[HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={NAN};
double delta=NAN;
double tscp=s;
double scp=tscp;
double pcp=pmax*inv_qshift;
double ratio=scp*pcp;
size_t n=0;
size_t j=0;
double ans=0.0;
double mjr=NAN;
for(j=0;j<HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT;++j) ans+=(terms[n++]=pow(j+q,-s));
ans+=(terms[n++]=0.5*pmax);
ans+=(terms[n++]=pmax*qshift*inv_sm1);
for(j=1;j<=HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER;++j) {
delta=hsl_sf_hzeta_eulermaclaurin_series_coeffs[j]*ratio;
ans+=(terms[n++]=delta);
scp*=++tscp;
scp*=++tscp;
pcp*=sqr_inv_qshift;
ratio=scp*pcp;
if ((fabs(delta/ans)) < (0.5*GSL_DBL_EPSILON)) break;
}
if (HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER<j) PLFIT_ERROR("maximum iterations exceeded",PLFIT_EMAXITER);
ans=0.0; while (n) ans+=terms[--n];
mjr=hsl_sf_hzeta_eulermaclaurin_series_majorantratios[j]*ratio;
result->val=+ans;
result->err=2.0*((HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+1.0)*GSL_DBL_EPSILON*fabs(ans)+mjr);
return (PLFIT_SUCCESS);
}
}
}
extern
double hsl_sf_hzeta(const double s, const double q) {
HSL_SF_EVAL_RESULT(hsl_sf_hzeta_e(s,q,&result)); }
extern
int hsl_sf_hzeta_deriv_e(const double s, const double q, gsl_sf_result * result) {
/* CHECK_POINTER(result) */
if ((s <= 1.0) || (q <= 0.0)) {
PLFIT_ERROR("s must be larger than 1.0 and q must be larger than zero", PLFIT_EINVAL);
}
else {
const double ln_hz_term0=-s*log(q);
if (ln_hz_term0 < GSL_LOG_DBL_MIN+1.0) {
PLFIT_ERROR("underflow", PLFIT_UNDRFLOW);
}
else if (GSL_LOG_DBL_MAX-1.0 < ln_hz_term0) {
PLFIT_ERROR("overflow", PLFIT_OVERFLOW);
}
else { // Euler-Maclaurin summation formula
const double qshift=HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+q;
const double inv_qshift=1.0/qshift;
const double sqr_inv_qshift=inv_qshift*inv_qshift;
const double inv_sm1=1.0/(s-1.0);
const double pmax=pow(qshift,-s);
const double lmax=log(qshift);
double terms[HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={NAN};
double delta=NAN;
double tscp=s;
double scp=tscp;
double pcp=pmax*inv_qshift;
double lcp=lmax-1.0/s;
double ratio=scp*pcp*lcp;
double qs=NAN;
size_t n=0;
size_t j=0;
double ans=0.0;
double mjr=NAN;
for(j=0,qs=q;j<HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT;++qs,++j) ans+=(terms[n++]=log(qs)*pow(qs,-s));
ans+=(terms[n++]=0.5*lmax*pmax);
ans+=(terms[n++]=pmax*qshift*inv_sm1*(lmax+inv_sm1));
for(j=1;j<=HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER;++j) {
delta=hsl_sf_hzeta_eulermaclaurin_series_coeffs[j]*ratio;
ans+=(terms[n++]=delta);
scp*=++tscp; lcp-=1.0/tscp;
scp*=++tscp; lcp-=1.0/tscp;
pcp*=sqr_inv_qshift;
ratio=scp*pcp*lcp;
if ((fabs(delta/ans)) < (0.5*GSL_DBL_EPSILON)) break;
}
if (HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER<j) PLFIT_ERROR("maximum iterations exceeded",PLFIT_EMAXITER);
ans=0.0; while (n) ans+=terms[--n];
mjr=hsl_sf_hzeta_eulermaclaurin_series_majorantratios[j]*ratio;
result->val=-ans;
result->err=2.0*((HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+1.0)*GSL_DBL_EPSILON*fabs(ans)+mjr);
return (PLFIT_SUCCESS);
}
}
}
extern
double hsl_sf_hzeta_deriv(const double s, const double q) {
HSL_SF_EVAL_RESULT(hsl_sf_hzeta_deriv_e(s,q,&result)); }
extern
int hsl_sf_hzeta_deriv2_e(const double s, const double q, gsl_sf_result * result) {
/* CHECK_POINTER(result) */
if ((s <= 1.0) || (q <= 0.0)) {
PLFIT_ERROR("s must be larger than 1.0 and q must be larger than zero", PLFIT_EINVAL);
}
else {
const double ln_hz_term0=-s*log(q);
if (ln_hz_term0 < GSL_LOG_DBL_MIN+1.0) {
PLFIT_ERROR("underflow", PLFIT_UNDRFLOW);
}
else if (GSL_LOG_DBL_MAX-1.0 < ln_hz_term0) {
PLFIT_ERROR("overflow", PLFIT_OVERFLOW);
}
else { // Euler-Maclaurin summation formula
const double qshift=HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+q;
const double inv_qshift=1.0/qshift;
const double sqr_inv_qshift=inv_qshift*inv_qshift;
const double inv_sm1=1.0/(s-1.0);
const double pmax=pow(qshift,-s);
const double lmax=log(qshift);
const double lmax_p_inv_sm1=lmax+inv_sm1;
const double sqr_inv_sm1=inv_sm1*inv_sm1;
const double sqr_lmax=lmax*lmax;
const double sqr_lmax_p_inv_sm1=lmax_p_inv_sm1*lmax_p_inv_sm1;
double terms[HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={NAN};
double delta=NAN;
double tscp=s;
double slcp=NAN;
double plcp=NAN;
double scp=tscp;
double pcp=pmax*inv_qshift;
double lcp=1.0/s-lmax;
double sqr_lcp=lmax*(lmax-2.0/s);
double ratio=scp*pcp*sqr_lcp;
double qs=NAN;
double lqs=NAN;
size_t n=0;
size_t j=0;
double ans=0.0;
double mjr=NAN;
for(j=0,qs=q;j<HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT;++qs,++j) {
lqs=log(qs);
ans+=(terms[n++]=lqs*lqs*pow(qs,-s));
}
ans+=(terms[n++]=0.5*sqr_lmax*pmax);
ans+=(terms[n++]=pmax*qshift*inv_sm1*(sqr_lmax_p_inv_sm1+sqr_inv_sm1));
for(j=1;j<=HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER;++j) {
delta=hsl_sf_hzeta_eulermaclaurin_series_coeffs[j]*ratio;
ans+=(terms[n++]=delta);
scp*=++tscp; slcp=plcp=1.0/tscp;
scp*=++tscp; slcp+=1.0/tscp; plcp/=tscp;
pcp*=sqr_inv_qshift;
sqr_lcp+=2.0*(plcp+slcp*lcp);
ratio=scp*pcp*sqr_lcp;
if ((fabs(delta/ans)) < (0.5*GSL_DBL_EPSILON)) break;
lcp+=slcp;
}
if (HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER<j) PLFIT_ERROR("maximum iterations exceeded",PLFIT_EMAXITER);
ans=0.0; while (n) ans+=terms[--n];
mjr=hsl_sf_hzeta_eulermaclaurin_series_majorantratios[j]*ratio;
result->val=+ans;
result->err=2.0*((HSL_SF_HZETA_EULERMACLAURIN_SERIES_SHIFT+1.0)*GSL_DBL_EPSILON*fabs(ans)+mjr);
return (PLFIT_SUCCESS);
}
}
}
extern
double hsl_sf_hzeta_deriv2(const double s, const double q) {
HSL_SF_EVAL_RESULT(hsl_sf_hzeta_deriv2_e(s,q,&result)); }
static inline
double hsl_sf_hZeta0_zed(const double s, const double q) {
#if 1
const long double ld_q=(long double)(q);
const long double ld_s=(long double)(s);
const long double ld_log1prq=log1pl(1.0L/ld_q);
const long double ld_epsilon=expm1l(-ld_s*ld_log1prq);
const long double ld_z=ld_s+(ld_q+0.5L*ld_s+0.5L)*ld_epsilon;
const double z=(double)(ld_z);
#else
double z=s+(q+0.5*s+0.5)*expm1(-s*log1p(1.0/q));
#endif
return (z); }
// Z_{0}(s,a) = a^s \left(\frac{1}{2}+\frac{a}{s-1}\right)^{-1} \zeta(s,a) - 1
// Z_{0}(s,a) = O\left(\frac{(s-1)s}{6a^{2}}\right)
static
int hsl_sf_hZeta0(const double s, const double q, double * value, double * abserror) {
const double criterion=ceil(10.0*s-q);
const size_t shift=(criterion<0.0)?0:
(criterion<HSL_SF_LNHZETA_EULERMACLAURIN_SERIES_SHIFT_MAX)?(size_t)(llrint(criterion)):
HSL_SF_LNHZETA_EULERMACLAURIN_SERIES_SHIFT_MAX;
const double qshift=(double)(shift)+q;
const double inv_qshift=1.0/qshift;
const double sqr_inv_qshift=inv_qshift*inv_qshift;
const double sm1=s-1.0;
double terms[HSL_SF_LNHZETA_EULERMACLAURIN_SERIES_SHIFT_MAX+HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={NAN};
double delta=NAN;
double tscp=s;
double scp=s*sm1;
double pcp=inv_qshift/(2.0*qshift+sm1);
double ratio=NAN;
size_t n=0;
size_t j=0;
double ans=0.0;
double mjr=NAN;
if (shift) {
const double hsm1=0.5*sm1;
const double inv_q=1.0/q;
const double qphsm1=q+hsm1;
const double inv_qphsm1=1.0/qphsm1;
const double qshiftphsm1=qshift+hsm1;
double qs=q;
double a=1.0;
for(j=0;j<shift;) {
ans+=(terms[n++]=a*hsl_sf_hZeta0_zed(s,qs++)*inv_qphsm1);
a=exp(-s*log1p((++j)*inv_q));
}
pcp*=a*qshiftphsm1*inv_qphsm1;
}
ratio=scp*pcp;
ans+=terms[n++]=ratio/6.0;
scp*=++tscp;
scp*=++tscp;
pcp*=2.0*sqr_inv_qshift;
ratio=scp*pcp;
for(j=2;j<=HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER;++j) {
delta=hsl_sf_hzeta_eulermaclaurin_series_coeffs[j]*ratio;
ans+=(terms[n++]=delta);
scp*=++tscp;
scp*=++tscp;
pcp*=sqr_inv_qshift;
ratio=scp*pcp;
if ((fabs(delta/ans)) < (0.5*GSL_DBL_EPSILON)) break;
}
if (HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER<j) PLFIT_ERROR("maximum iterations exceeded",PLFIT_EMAXITER);
ans=0.0; while (n) ans+=terms[--n];
mjr=hsl_sf_hzeta_eulermaclaurin_series_majorantratios[j]*ratio;
*value=ans;
*abserror=2.0*((shift+1)*GSL_DBL_EPSILON*fabs(ans)+mjr);
return (PLFIT_SUCCESS); }
static inline
double hsl_sf_hZeta1_zed(const double s, const double q) {
#if 1
const long double ld_q=(long double)(q);
const long double ld_s=(long double)(s);
const long double ld_sm1=ld_s-1.0L;
const long double ld_logq=logl(ld_q);
const long double ld_log1prq=log1pl(1.0L/ld_q);
const long double ld_inv_logq=1.0L/ld_logq;
const long double ld_logratiom1=ld_log1prq*ld_inv_logq;
const long double ld_powratiom1=expm1l(-ld_s*ld_log1prq);
const long double ld_varepsilon=expm1l(-ld_sm1*ld_log1prq);
const long double ld_epsilon=ld_logratiom1+ld_powratiom1+ld_logratiom1*ld_powratiom1;
const long double ld_z=ld_s+(ld_q+0.5L*ld_s+0.5L)*ld_epsilon+ld_q/ld_sm1*ld_inv_logq*ld_varepsilon;
const double z=(double)(ld_z);
#else
const double sm1=s-1.0;
const double inv_ln_q=1.0/log(q);
const double log1prq=log1p(1.0/q);
const double logratiom1=log1prq*inv_ln_q;
const double powratiom1=expm1(-s*log1prq);
const double epsilon=logratiom1+powratiom1+logratiom1*powratiom1;
const double z=s+(q+0.5*s+0.5)*epsilon+q/sm1*inv_ln_q*expm1(-sm1*log1prq);
#endif
return (z); }
// Z_{1}(s,a) = -\frac{a^s}{\ln(a)} \left(\frac{1}{2}+\frac{a}{s-1}\,\left[1+\frac{1}{(s-1)\,\ln(a)}\right]\right)^{-1} \zeta^{\prime}(s,a) - 1
// Z_{1}(s,a) = O\left(\frac{(s-1)s}{6a^{2}}\right)
static
int hsl_sf_hZeta1(const double s, const double q, const double ln_q, double * value, double * abserror, double * coeff1) {
const double criterion=ceil(10.0*s-q);
const size_t shift=(criterion<0.0)?0:
(criterion<HSL_SF_LNHZETA_EULERMACLAURIN_SERIES_SHIFT_MAX)?(size_t)(llrint(criterion)):
HSL_SF_LNHZETA_EULERMACLAURIN_SERIES_SHIFT_MAX;
const double qshift=(double)(shift)+q;
const double ln_qshift=log(qshift);
const double inv_qshift=1.0/qshift;
const double inv_ln_q=1.0/ln_q;
const double inv_ln_qshift=1.0/ln_qshift;
const double sqr_inv_qshift=inv_qshift*inv_qshift;
const double sm1=s-1.0;
const double hsm1=0.5*sm1;
const double q_over_ln_q=q*inv_ln_q;
const double qshift_over_ln_qshift=qshift*inv_ln_qshift;
const double qphsm1=q+hsm1;
const double qshiftphsm1=qshift+hsm1;
double terms[HSL_SF_LNHZETA_EULERMACLAURIN_SERIES_SHIFT_MAX+HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={NAN};
double delta=NAN;
double tscp=s;
double scp=s*sm1;
double pcp=inv_qshift*sm1/(qshift_over_ln_qshift+sm1*qshiftphsm1);
double lcp=1.0-inv_ln_qshift/s;
double ratio=NAN;
size_t n=0;
size_t j=0;
double ans=0.0;
double mjr=NAN;
if (shift) {
const double inv_q=1.0/q;
const double inv_sm1=1.0/sm1;
const double w=1.0+inv_sm1*inv_ln_q;
const double wshift=1.0+inv_sm1*inv_ln_qshift;
const double qwphsm1=q*w+hsm1;
const double inv_qwphsm1=1.0/qwphsm1;
const double qshiftwshiftphsm1=qshift*wshift+hsm1;
double qs=q;
double a=1.0;
for(j=0;j<shift;) {
ans+=(terms[n++]=a*hsl_sf_hZeta1_zed(s,qs++)*inv_qwphsm1);
a=log(qs)*inv_ln_q*exp(-s*log1p((++j)*inv_q));
}
pcp*=a*qshiftwshiftphsm1*inv_qwphsm1;
}
ratio=scp*pcp*lcp;
ans+=terms[n++]=ratio/12.0;
scp*=++tscp; lcp-=inv_ln_qshift/tscp;
scp*=++tscp; lcp-=inv_ln_qshift/tscp;
pcp*=sqr_inv_qshift;
ratio=scp*pcp*lcp;
for(j=2;j<=HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER;++j) {
delta=hsl_sf_hzeta_eulermaclaurin_series_coeffs[j]*ratio;
ans+=(terms[n++]=delta);
scp*=++tscp; lcp-=inv_ln_qshift/tscp;
scp*=++tscp; lcp-=inv_ln_qshift/tscp;
pcp*=sqr_inv_qshift;
ratio=scp*pcp*lcp;
if ((fabs(delta/ans)) < (0.5*GSL_DBL_EPSILON)) break;
}
if (HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER<j) PLFIT_ERROR("maximum iterations exceeded",PLFIT_EMAXITER);
ans=0.0; while (n) ans+=terms[--n];
mjr=hsl_sf_hzeta_eulermaclaurin_series_majorantratios[j]*ratio;
*value=ans;
*abserror=2.0*((shift+1)*GSL_DBL_EPSILON*fabs(ans)+mjr);
if (coeff1) *coeff1=1.0+q_over_ln_q/qphsm1/sm1;
return (PLFIT_SUCCESS); }
extern
int hsl_sf_lnhzeta_deriv_tuple_e(const double s, const double q, gsl_sf_result * result, gsl_sf_result * result_deriv) {
/* CHECK_POINTER(result) */
if ((s <= 1.0) || (q <= 0.0)) {
PLFIT_ERROR("s must be larger than 1.0 and q must be larger than zero", PLFIT_EINVAL);
}
else if (q == 1.0) {
const double inv_sm1=1.0/(s-1.0);
const double inv_qsm1=4.0*inv_sm1;
const double hz_coeff0=exp2(s+1.0);
const double hz_coeff1=1.0+inv_qsm1;
double hZeta0_value=NAN;
double hZeta0_abserror=NAN;
hsl_sf_hZeta0(s,2.0,&hZeta0_value,&hZeta0_abserror);
hZeta0_value+=1.0;
if (result) {
const double ln_hz_coeff=hz_coeff1/hz_coeff0;
const double ln_hZeta0_value=ln_hz_coeff*hZeta0_value;
result->val=log1p(ln_hZeta0_value);
result->err=(2.0*GSL_DBL_EPSILON*ln_hz_coeff+hZeta0_abserror)/(1.0+ln_hZeta0_value);
}
if (result_deriv) {
const double ld_hz_coeff2=1.0+inv_sm1*M_LOG2E;
const double ld_hz_coeff1=1.0+inv_qsm1*ld_hz_coeff2;
double hZeta1_value=NAN;
double hZeta1_abserror=NAN;
hsl_sf_hZeta1(s,2.0,M_LN2,&hZeta1_value,&hZeta1_abserror,NULL);
hZeta0_value*=hz_coeff1;
hZeta0_value+=hz_coeff0;
hZeta1_value+=1.0;
hZeta1_value*=-M_LN2*ld_hz_coeff1;
result_deriv->val=hZeta1_value/hZeta0_value;
result_deriv->err=2.0*GSL_DBL_EPSILON*fabs(result_deriv->val)+(hZeta0_abserror+hZeta1_abserror);
}
}
else {
const double ln_q=log(q);
double hZeta0_value=NAN;
double hZeta0_abserror=NAN;
hsl_sf_hZeta0(s,q,&hZeta0_value,&hZeta0_abserror);
if (result) {
const double ln_hz_term0=-s*ln_q;
const double ln_hz_term1=log(0.5+q/(s-1.0));
result->val=ln_hz_term0+ln_hz_term1+log1p(hZeta0_value);
result->err=2.0*GSL_DBL_EPSILON*(fabs(ln_hz_term0)+fabs(ln_hz_term1))+hZeta0_abserror/(1.0+hZeta0_value);
}
if (result_deriv) {
double hZeta1_value=NAN;
double hZeta1_abserror=NAN;
double ld_hz_coeff1=NAN;
hsl_sf_hZeta1(s,q,ln_q,&hZeta1_value,&hZeta1_abserror,&ld_hz_coeff1);
result_deriv->val=-ln_q*ld_hz_coeff1*(1.0+hZeta1_value)/(1.0+hZeta0_value);
result_deriv->err=2.0*GSL_DBL_EPSILON*fabs(result_deriv->val)+(hZeta0_abserror+hZeta1_abserror);
}
}
return (PLFIT_SUCCESS); }
extern
double hsl_sf_lnhzeta_deriv_tuple(const double s, const double q, double * tuple0, double * tuple1) {
HSL_SF_EVAL_TUPLE_RESULT(hsl_sf_lnhzeta_deriv_tuple_e(s,q,&result0,&result1)); }
extern
int hsl_sf_lnhzeta_e(const double s, const double q, gsl_sf_result * result) {
return (hsl_sf_lnhzeta_deriv_tuple_e(s,q,result,NULL)); }
extern
double hsl_sf_lnhzeta(const double s, const double q) {
HSL_SF_EVAL_RESULT(hsl_sf_lnhzeta_e(s,q,&result)); }
extern
int hsl_sf_lnhzeta_deriv_e(const double s, const double q, gsl_sf_result * result) {
return (hsl_sf_lnhzeta_deriv_tuple_e(s,q,NULL,result)); }
extern
double hsl_sf_lnhzeta_deriv(const double s, const double q) {
HSL_SF_EVAL_RESULT(hsl_sf_lnhzeta_deriv_e(s,q,&result)); }
//
// End of file `hsl/specfunc/hzeta.c'.
+88
View File
@@ -0,0 +1,88 @@
/* This file was imported from a private scientific library
* based on GSL coined Home Scientific Libray (HSL) by its author
* Jerome Benoit; this very material is itself inspired from the
* material written by G. Jungan and distributed by GSL.
* Ultimately, some modifications were done in order to render the
* imported material independent from the rest of GSL.
*/
/* `hsl/hsl_sf_zeta.h' C header file
// HSL - Home Scientific Library
// Copyright (C) 2005-2018 Jerome Benoit
//
// HSL 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.
*/
/* For futher details, see its source conterpart src/hzeta.c */
/* Author: Jerome G. Benoit < jgmbenoit _at_ rezozer _dot_ net > */
#ifndef __HZETA_H__
#define __HZETA_H__
#include "plfit_decls.h"
PLFIT_BEGIN_C_DECLS
/* Hurwitz Zeta Function
* zeta(s,q) = Sum[ (k+q)^(-s), {k,0,Infinity} ]
*
* s > 1.0, q > 0.0
*/
double hsl_sf_hzeta(const double s, const double q);
/* First Derivative of Hurwitz Zeta Function
* zeta'(s,q) = - Sum[ Ln(k+q)/(k+q)^(s), {k,0,Infinity} ]
*
* s > 1.0, q > 0.0
*/
double hsl_sf_hzeta_deriv(const double s, const double q);
/* Second Derivative of Hurwitz Zeta Function
* zeta''(s,q) = + Sum[ Ln(k+q)^2/(k+q)^(s), {k,0,Infinity} ]
*
* s > 1.0, q > 0.0
*/
double hsl_sf_hzeta_deriv2(const double s, const double q);
/* Logarithm of Hurwitz Zeta Function
* lnzeta(s,q) = ln(zeta(s,q))
*
* s > 1.0, q > 0.0 (and q >> 1)
*/
double hsl_sf_lnhzeta(const double s, const double q);
/* Logarithmic Derivative of Hurwitz Zeta Function
* lnzeta'(s,q) = zeta'(s,q)/zeta(s,q)
*
* s > 1.0, q > 0.0 (and q >> 1)
*/
double hsl_sf_lnhzeta_deriv(const double s, const double q);
/* Logarithm and Logarithmic Derivative of Hurwitz Zeta Function:
* nonredundant computation version:
* - lnzeta(s,q) and lnzeta'(s,q) are stored in *deriv0 and *deriv1, respectively;
* - the return value and the value stored in *deriv0 are the same;
* - deriv0 and deriv1 must be effective pointers, that is, not the NULL pointer.
*
* s > 1.0, q > 0.0 (and q >> 1)
*/
double hsl_sf_lnhzeta_deriv_tuple(const double s, const double q, double * deriv0, double * deriv1);
PLFIT_END_C_DECLS
#endif // __HZETA_H__
+66
View File
@@ -0,0 +1,66 @@
/* kolmogorov.c
*
* Copyright (C) 2010-2011 Tamas Nepusz
*
* 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 <math.h>
#include "kolmogorov.h"
double plfit_kolmogorov(double z) {
const double fj[4] = { -2, -8, -18, -32 };
const double w = 2.50662827;
const double c1 = -1.2337005501361697; /* -pi^2 / 8 */
const double c2 = -11.103304951225528; /* 9*c1 */
const double c3 = -30.842513753404244; /* 25*c1 */
double u = fabs(z);
double v;
if (u < 0.2)
return 1;
if (u < 0.755) {
v = 1.0 / (u*u);
return 1 - w * (exp(c1*v) + exp(c2*v) + exp(c3*v)) / u;
}
if (u < 6.8116) {
double r[4] = { 0, 0, 0, 0 };
long int maxj = (long int)(3.0 / u + 0.5);
long int j;
if (maxj < 1)
maxj = 1;
v = u*u;
for (j = 0; j < maxj; j++) {
r[j] = exp(fj[j] * v);
}
return 2*(r[0] - r[1] + r[2] - r[3]);
}
return 0;
}
double plfit_ks_test_one_sample_p(double d, size_t n) {
return plfit_kolmogorov(d * sqrt((double) n));
}
double plfit_ks_test_two_sample_p(double d, size_t n1, size_t n2) {
return plfit_kolmogorov(d * sqrt(n1*n2 / ((double)(n1+n2))));
}
+34
View File
@@ -0,0 +1,34 @@
/* kolmogorov.h
*
* Copyright (C) 2010-2011 Tamas Nepusz
*
* 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.
*/
#ifndef __KOLMOGOROV_H__
#define __KOLMOGOROV_H__
#include <stdlib.h>
#include "plfit_decls.h"
PLFIT_BEGIN_C_DECLS
double plfit_kolmogorov(double z);
double plfit_ks_test_one_sample_p(double d, size_t n);
double plfit_ks_test_two_sample_p(double d, size_t n1, size_t n2);
PLFIT_END_C_DECLS
#endif
File diff suppressed because it is too large Load Diff
+753
View File
@@ -0,0 +1,753 @@
/*
* C library of Limited memory BFGS (L-BFGS).
*
* Copyright (c) 1990, Jorge Nocedal
* Copyright (c) 2007-2010 Naoaki Okazaki
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/* $Id$ */
#ifndef __LBFGS_H__
#define __LBFGS_H__
#include "plfit_decls.h"
PLFIT_BEGIN_C_DECLS
/*
* The default precision of floating point values is 64bit (double).
*/
#ifndef LBFGS_FLOAT
#define LBFGS_FLOAT 64
#endif/*LBFGS_FLOAT*/
/*
* Activate optimization routines for IEEE754 floating point values.
*/
#ifndef LBFGS_IEEE_FLOAT
#define LBFGS_IEEE_FLOAT 1
#endif/*LBFGS_IEEE_FLOAT*/
#if LBFGS_FLOAT == 32
typedef float lbfgsfloatval_t;
#elif LBFGS_FLOAT == 64
typedef double lbfgsfloatval_t;
#else
#error "libLBFGS supports single (float; LBFGS_FLOAT = 32) or double (double; LBFGS_FLOAT=64) precision only."
#endif
/**
* \addtogroup liblbfgs_api libLBFGS API
* @{
*
* The libLBFGS API.
*/
/**
* Return values of lbfgs().
*
* Roughly speaking, a negative value indicates an error.
*/
enum {
/** L-BFGS reaches convergence. */
LBFGS_SUCCESS = 0,
LBFGS_CONVERGENCE = 0,
LBFGS_STOP,
/** The initial variables already minimize the objective function. */
LBFGS_ALREADY_MINIMIZED,
/** Unknown error. */
LBFGSERR_UNKNOWNERROR = -1024,
/** Logic error. */
LBFGSERR_LOGICERROR,
/** Insufficient memory. */
LBFGSERR_OUTOFMEMORY,
/** The minimization process has been canceled. */
LBFGSERR_CANCELED,
/** Invalid number of variables specified. */
LBFGSERR_INVALID_N,
/** Invalid number of variables (for SSE) specified. */
LBFGSERR_INVALID_N_SSE,
/** The array x must be aligned to 16 (for SSE). */
LBFGSERR_INVALID_X_SSE,
/** Invalid parameter lbfgs_parameter_t::epsilon specified. */
LBFGSERR_INVALID_EPSILON,
/** Invalid parameter lbfgs_parameter_t::past specified. */
LBFGSERR_INVALID_TESTPERIOD,
/** Invalid parameter lbfgs_parameter_t::delta specified. */
LBFGSERR_INVALID_DELTA,
/** Invalid parameter lbfgs_parameter_t::linesearch specified. */
LBFGSERR_INVALID_LINESEARCH,
/** Invalid parameter lbfgs_parameter_t::max_step specified. */
LBFGSERR_INVALID_MINSTEP,
/** Invalid parameter lbfgs_parameter_t::max_step specified. */
LBFGSERR_INVALID_MAXSTEP,
/** Invalid parameter lbfgs_parameter_t::ftol specified. */
LBFGSERR_INVALID_FTOL,
/** Invalid parameter lbfgs_parameter_t::wolfe specified. */
LBFGSERR_INVALID_WOLFE,
/** Invalid parameter lbfgs_parameter_t::gtol specified. */
LBFGSERR_INVALID_GTOL,
/** Invalid parameter lbfgs_parameter_t::xtol specified. */
LBFGSERR_INVALID_XTOL,
/** Invalid parameter lbfgs_parameter_t::max_linesearch specified. */
LBFGSERR_INVALID_MAXLINESEARCH,
/** Invalid parameter lbfgs_parameter_t::orthantwise_c specified. */
LBFGSERR_INVALID_ORTHANTWISE,
/** Invalid parameter lbfgs_parameter_t::orthantwise_start specified. */
LBFGSERR_INVALID_ORTHANTWISE_START,
/** Invalid parameter lbfgs_parameter_t::orthantwise_end specified. */
LBFGSERR_INVALID_ORTHANTWISE_END,
/** The line-search step went out of the interval of uncertainty. */
LBFGSERR_OUTOFINTERVAL,
/** A logic error occurred; alternatively, the interval of uncertainty
became too small. */
LBFGSERR_INCORRECT_TMINMAX,
/** A rounding error occurred; alternatively, no line-search step
satisfies the sufficient decrease and curvature conditions. */
LBFGSERR_ROUNDING_ERROR,
/** The line-search step became smaller than lbfgs_parameter_t::min_step. */
LBFGSERR_MINIMUMSTEP,
/** The line-search step became larger than lbfgs_parameter_t::max_step. */
LBFGSERR_MAXIMUMSTEP,
/** The line-search routine reaches the maximum number of evaluations. */
LBFGSERR_MAXIMUMLINESEARCH,
/** The algorithm routine reaches the maximum number of iterations. */
LBFGSERR_MAXIMUMITERATION,
/** Relative width of the interval of uncertainty is at most
lbfgs_parameter_t::xtol. */
LBFGSERR_WIDTHTOOSMALL,
/** A logic error (negative line-search step) occurred. */
LBFGSERR_INVALIDPARAMETERS,
/** The current search direction increases the objective function value. */
LBFGSERR_INCREASEGRADIENT,
};
/**
* Line search algorithms.
*/
enum {
/** The default algorithm (MoreThuente method). */
LBFGS_LINESEARCH_DEFAULT = 0,
/** MoreThuente method proposd by More and Thuente. */
LBFGS_LINESEARCH_MORETHUENTE = 0,
/**
* Backtracking method with the Armijo condition.
* The backtracking method finds the step length such that it satisfies
* the sufficient decrease (Armijo) condition,
* - f(x + a * d) <= f(x) + lbfgs_parameter_t::ftol * a * g(x)^T d,
*
* where x is the current point, d is the current search direction, and
* a is the step length.
*/
LBFGS_LINESEARCH_BACKTRACKING_ARMIJO = 1,
/** The backtracking method with the defualt (regular Wolfe) condition. */
LBFGS_LINESEARCH_BACKTRACKING = 2,
/**
* Backtracking method with regular Wolfe condition.
* The backtracking method finds the step length such that it satisfies
* both the Armijo condition (LBFGS_LINESEARCH_BACKTRACKING_ARMIJO)
* and the curvature condition,
* - g(x + a * d)^T d >= lbfgs_parameter_t::wolfe * g(x)^T d,
*
* where x is the current point, d is the current search direction, and
* a is the step length.
*/
LBFGS_LINESEARCH_BACKTRACKING_WOLFE = 2,
/**
* Backtracking method with strong Wolfe condition.
* The backtracking method finds the step length such that it satisfies
* both the Armijo condition (LBFGS_LINESEARCH_BACKTRACKING_ARMIJO)
* and the following condition,
* - |g(x + a * d)^T d| <= lbfgs_parameter_t::wolfe * |g(x)^T d|,
*
* where x is the current point, d is the current search direction, and
* a is the step length.
*/
LBFGS_LINESEARCH_BACKTRACKING_STRONG_WOLFE = 3,
};
/**
* L-BFGS optimization parameters.
* Call lbfgs_parameter_init() function to initialize parameters to the
* default values.
*/
typedef struct {
/**
* The number of corrections to approximate the inverse hessian matrix.
* The L-BFGS routine stores the computation results of previous \ref m
* iterations to approximate the inverse hessian matrix of the current
* iteration. This parameter controls the size of the limited memories
* (corrections). The default value is \c 6. Values less than \c 3 are
* not recommended. Large values will result in excessive computing time.
*/
int m;
/**
* Epsilon for convergence test.
* This parameter determines the accuracy with which the solution is to
* be found. A minimization terminates when
* ||g|| < \ref epsilon * max(1, ||x||),
* where ||.|| denotes the Euclidean (L2) norm. The default value is
* \c 1e-5.
*/
lbfgsfloatval_t epsilon;
/**
* Distance for delta-based convergence test.
* This parameter determines the distance, in iterations, to compute
* the rate of decrease of the objective function. If the value of this
* parameter is zero, the library does not perform the delta-based
* convergence test. The default value is \c 0.
*/
int past;
/**
* Delta for convergence test.
* This parameter determines the minimum rate of decrease of the
* objective function. The library stops iterations when the
* following condition is met:
* (f' - f) / f < \ref delta,
* where f' is the objective value of \ref past iterations ago, and f is
* the objective value of the current iteration.
* The default value is \c 1e-5.
*/
lbfgsfloatval_t delta;
/**
* The maximum number of iterations.
* The lbfgs() function terminates an optimization process with
* ::LBFGSERR_MAXIMUMITERATION status code when the iteration count
* exceedes this parameter. Setting this parameter to zero continues an
* optimization process until a convergence or error. The default value
* is \c 0.
*/
int max_iterations;
/**
* The line search algorithm.
* This parameter specifies a line search algorithm to be used by the
* L-BFGS routine.
*/
int linesearch;
/**
* The maximum number of trials for the line search.
* This parameter controls the number of function and gradients evaluations
* per iteration for the line search routine. The default value is \c 40.
*/
int max_linesearch;
/**
* The minimum step of the line search routine.
* The default value is \c 1e-20. This value need not be modified unless
* the exponents are too large for the machine being used, or unless the
* problem is extremely badly scaled (in which case the exponents should
* be increased).
*/
lbfgsfloatval_t min_step;
/**
* The maximum step of the line search.
* The default value is \c 1e+20. This value need not be modified unless
* the exponents are too large for the machine being used, or unless the
* problem is extremely badly scaled (in which case the exponents should
* be increased).
*/
lbfgsfloatval_t max_step;
/**
* A parameter to control the accuracy of the line search routine.
* The default value is \c 1e-4. This parameter should be greater
* than zero and smaller than \c 0.5.
*/
lbfgsfloatval_t ftol;
/**
* A coefficient for the Wolfe condition.
* This parameter is valid only when the backtracking line-search
* algorithm is used with the Wolfe condition,
* ::LBFGS_LINESEARCH_BACKTRACKING_STRONG_WOLFE or
* ::LBFGS_LINESEARCH_BACKTRACKING_WOLFE .
* The default value is \c 0.9. This parameter should be greater
* the \ref ftol parameter and smaller than \c 1.0.
*/
lbfgsfloatval_t wolfe;
/**
* A parameter to control the accuracy of the line search routine.
* The default value is \c 0.9. If the function and gradient
* evaluations are inexpensive with respect to the cost of the
* iteration (which is sometimes the case when solving very large
* problems) it may be advantageous to set this parameter to a small
* value. A typical small value is \c 0.1. This parameter should be
* greater than the \ref ftol parameter (\c 1e-4) and smaller than
* \c 1.0.
*/
lbfgsfloatval_t gtol;
/**
* The machine precision for floating-point values.
* This parameter must be a positive value set by a client program to
* estimate the machine precision. The line search routine will terminate
* with the status code (::LBFGSERR_ROUNDING_ERROR) if the relative width
* of the interval of uncertainty is less than this parameter.
*/
lbfgsfloatval_t xtol;
/**
* Coeefficient for the L1 norm of variables.
* This parameter should be set to zero for standard minimization
* problems. Setting this parameter to a positive value activates
* Orthant-Wise Limited-memory Quasi-Newton (OWL-QN) method, which
* minimizes the objective function F(x) combined with the L1 norm |x|
* of the variables, {F(x) + C |x|}. This parameter is the coeefficient
* for the |x|, i.e., C. As the L1 norm |x| is not differentiable at
* zero, the library modifies function and gradient evaluations from
* a client program suitably; a client program thus have only to return
* the function value F(x) and gradients G(x) as usual. The default value
* is zero.
*/
lbfgsfloatval_t orthantwise_c;
/**
* Start index for computing L1 norm of the variables.
* This parameter is valid only for OWL-QN method
* (i.e., \ref orthantwise_c != 0). This parameter b (0 <= b < N)
* specifies the index number from which the library computes the
* L1 norm of the variables x,
* |x| := |x_{b}| + |x_{b+1}| + ... + |x_{N}| .
* In other words, variables x_1, ..., x_{b-1} are not used for
* computing the L1 norm. Setting b (0 < b < N), one can protect
* variables, x_1, ..., x_{b-1} (e.g., a bias term of logistic
* regression) from being regularized. The default value is zero.
*/
int orthantwise_start;
/**
* End index for computing L1 norm of the variables.
* This parameter is valid only for OWL-QN method
* (i.e., \ref orthantwise_c != 0). This parameter e (0 < e <= N)
* specifies the index number at which the library stops computing the
* L1 norm of the variables x,
*/
int orthantwise_end;
} lbfgs_parameter_t;
/**
* Callback interface to provide objective function and gradient evaluations.
*
* The lbfgs() function call this function to obtain the values of objective
* function and its gradients when needed. A client program must implement
* this function to evaluate the values of the objective function and its
* gradients, given current values of variables.
*
* @param instance The user data sent for lbfgs() function by the client.
* @param x The current values of variables.
* @param g The gradient vector. The callback function must compute
* the gradient values for the current variables.
* @param n The number of variables.
* @param step The current step of the line search routine.
* @retval lbfgsfloatval_t The value of the objective function for the current
* variables.
*/
typedef lbfgsfloatval_t (*lbfgs_evaluate_t)(
void *instance,
const lbfgsfloatval_t *x,
lbfgsfloatval_t *g,
const int n,
const lbfgsfloatval_t step
);
/**
* Callback interface to receive the progress of the optimization process.
*
* The lbfgs() function call this function for each iteration. Implementing
* this function, a client program can store or display the current progress
* of the optimization process.
*
* @param instance The user data sent for lbfgs() function by the client.
* @param x The current values of variables.
* @param g The current gradient values of variables.
* @param fx The current value of the objective function.
* @param xnorm The Euclidean norm of the variables.
* @param gnorm The Euclidean norm of the gradients.
* @param step The line-search step used for this iteration.
* @param n The number of variables.
* @param k The iteration count.
* @param ls The number of evaluations called for this iteration.
* @retval int Zero to continue the optimization process. Returning a
* non-zero value will cancel the optimization process.
*/
typedef int (*lbfgs_progress_t)(
void *instance,
const lbfgsfloatval_t *x,
const lbfgsfloatval_t *g,
const lbfgsfloatval_t fx,
const lbfgsfloatval_t xnorm,
const lbfgsfloatval_t gnorm,
const lbfgsfloatval_t step,
int n,
int k,
int ls
);
/*
A user must implement a function compatible with ::lbfgs_evaluate_t (evaluation
callback) and pass the pointer to the callback function to lbfgs() arguments.
Similarly, a user can implement a function compatible with ::lbfgs_progress_t
(progress callback) to obtain the current progress (e.g., variables, function
value, ||G||, etc) and to cancel the iteration process if necessary.
Implementation of a progress callback is optional: a user can pass \c NULL if
progress notification is not necessary.
In addition, a user must preserve two requirements:
- The number of variables must be multiples of 16 (this is not 4).
- The memory block of variable array ::x must be aligned to 16.
This algorithm terminates an optimization
when:
||G|| < \epsilon \cdot \max(1, ||x||) .
In this formula, ||.|| denotes the Euclidean norm.
*/
/**
* Start a L-BFGS optimization.
*
* @param n The number of variables.
* @param x The array of variables. A client program can set
* default values for the optimization and receive the
* optimization result through this array. This array
* must be allocated by ::lbfgs_malloc function
* for libLBFGS built with SSE/SSE2 optimization routine
* enabled. The library built without SSE/SSE2
* optimization does not have such a requirement.
* @param ptr_fx The pointer to the variable that receives the final
* value of the objective function for the variables.
* This argument can be set to \c NULL if the final
* value of the objective function is unnecessary.
* @param proc_evaluate The callback function to provide function and
* gradient evaluations given a current values of
* variables. A client program must implement a
* callback function compatible with \ref
* lbfgs_evaluate_t and pass the pointer to the
* callback function.
* @param proc_progress The callback function to receive the progress
* (the number of iterations, the current value of
* the objective function) of the minimization
* process. This argument can be set to \c NULL if
* a progress report is unnecessary.
* @param instance A user data for the client program. The callback
* functions will receive the value of this argument.
* @param param The pointer to a structure representing parameters for
* L-BFGS optimization. A client program can set this
* parameter to \c NULL to use the default parameters.
* Call lbfgs_parameter_init() function to fill a
* structure with the default values.
* @retval int The status code. This function returns zero if the
* minimization process terminates without an error. A
* non-zero value indicates an error.
*/
int lbfgs(
int n,
lbfgsfloatval_t *x,
lbfgsfloatval_t *ptr_fx,
lbfgs_evaluate_t proc_evaluate,
lbfgs_progress_t proc_progress,
void *instance,
lbfgs_parameter_t *param
);
/**
* Initialize L-BFGS parameters to the default values.
*
* Call this function to fill a parameter structure with the default values
* and overwrite parameter values if necessary.
*
* @param param The pointer to the parameter structure.
*/
void lbfgs_parameter_init(lbfgs_parameter_t *param);
/**
* Allocate an array for variables.
*
* This function allocates an array of variables for the convenience of
* ::lbfgs function; the function has a requreiemt for a variable array
* when libLBFGS is built with SSE/SSE2 optimization routines. A user does
* not have to use this function for libLBFGS built without SSE/SSE2
* optimization.
*
* @param n The number of variables.
*/
lbfgsfloatval_t* lbfgs_malloc(int n);
/**
* Free an array of variables.
*
* @param x The array of variables allocated by ::lbfgs_malloc
* function.
*/
void lbfgs_free(lbfgsfloatval_t *x);
/**
* Get string description of an lbfgs() return code.
*
* @param err A value returned by lbfgs().
*/
const char* lbfgs_strerror(int err);
/** @} */
PLFIT_END_C_DECLS
/**
@mainpage libLBFGS: a library of Limited-memory Broyden-Fletcher-Goldfarb-Shanno (L-BFGS)
@section intro Introduction
This library is a C port of the implementation of Limited-memory
Broyden-Fletcher-Goldfarb-Shanno (L-BFGS) method written by Jorge Nocedal.
The original FORTRAN source code is available at:
http://www.ece.northwestern.edu/~nocedal/lbfgs.html
The L-BFGS method solves the unconstrainted minimization problem,
<pre>
minimize F(x), x = (x1, x2, ..., xN),
</pre>
only if the objective function F(x) and its gradient G(x) are computable. The
well-known Newton's method requires computation of the inverse of the hessian
matrix of the objective function. However, the computational cost for the
inverse hessian matrix is expensive especially when the objective function
takes a large number of variables. The L-BFGS method iteratively finds a
minimizer by approximating the inverse hessian matrix by information from last
m iterations. This innovation saves the memory storage and computational time
drastically for large-scaled problems.
Among the various ports of L-BFGS, this library provides several features:
- <b>Optimization with L1-norm (Orthant-Wise Limited-memory Quasi-Newton
(OWL-QN) method)</b>:
In addition to standard minimization problems, the library can minimize
a function F(x) combined with L1-norm |x| of the variables,
{F(x) + C |x|}, where C is a constant scalar parameter. This feature is
useful for estimating parameters of sparse log-linear models (e.g.,
logistic regression and maximum entropy) with L1-regularization (or
Laplacian prior).
- <b>Clean C code</b>:
Unlike C codes generated automatically by f2c (Fortran 77 into C converter),
this port includes changes based on my interpretations, improvements,
optimizations, and clean-ups so that the ported code would be well-suited
for a C code. In addition to comments inherited from the original code,
a number of comments were added through my interpretations.
- <b>Callback interface</b>:
The library receives function and gradient values via a callback interface.
The library also notifies the progress of the optimization by invoking a
callback function. In the original implementation, a user had to set
function and gradient values every time the function returns for obtaining
updated values.
- <b>Thread safe</b>:
The library is thread-safe, which is the secondary gain from the callback
interface.
- <b>Cross platform.</b> The source code can be compiled on Microsoft Visual
Studio 2010, GNU C Compiler (gcc), etc.
- <b>Configurable precision</b>: A user can choose single-precision (float)
or double-precision (double) accuracy by changing ::LBFGS_FLOAT macro.
- <b>SSE/SSE2 optimization</b>:
This library includes SSE/SSE2 optimization (written in compiler intrinsics)
for vector arithmetic operations on Intel/AMD processors. The library uses
SSE for float values and SSE2 for double values. The SSE/SSE2 optimization
routine is disabled by default.
This library is used by:
- <a href="http://www.chokkan.org/software/crfsuite/">CRFsuite: A fast implementation of Conditional Random Fields (CRFs)</a>
- <a href="http://www.chokkan.org/software/classias/">Classias: A collection of machine-learning algorithms for classification</a>
- <a href="http://www.public.iastate.edu/~gdancik/mlegp/">mlegp: an R package for maximum likelihood estimates for Gaussian processes</a>
- <a href="http://infmath.uibk.ac.at/~matthiasf/imaging2/">imaging2: the imaging2 class library</a>
@section download Download
- <a href="https://github.com/downloads/chokkan/liblbfgs/liblbfgs-1.10.tar.gz">Source code</a>
- <a href="https://github.com/chokkan/liblbfgs">GitHub repository</a>
libLBFGS is distributed under the term of the
<a href="http://opensource.org/licenses/mit-license.php">MIT license</a>.
@section modules Third-party modules
- <a href="http://cran.r-project.org/web/packages/lbfgs/index.html">lbfgs: Limited-memory BFGS Optimization (a wrapper for R)</a> maintained by Antonio Coppola.
- <a href="http://search.cpan.org/~laye/Algorithm-LBFGS-0.16/">Algorithm::LBFGS - Perl extension for L-BFGS</a> maintained by Lei Sun.
- <a href="http://www.cs.kuleuven.be/~bernd/yap-lbfgs/">YAP-LBFGS (an interface to call libLBFGS from YAP Prolog)</a> maintained by Bernd Gutmann.
@section changelog History
- Version 1.10 (2010-12-22):
- Fixed compiling errors on Mac OS X; this patch was kindly submitted by
Nic Schraudolph.
- Reduced compiling warnings on Mac OS X; this patch was kindly submitted
by Tamas Nepusz.
- Replaced memalign() with posix_memalign().
- Updated solution and project files for Microsoft Visual Studio 2010.
- Version 1.9 (2010-01-29):
- Fixed a mistake in checking the validity of the parameters "ftol" and
"wolfe"; this was discovered by Kevin S. Van Horn.
- Version 1.8 (2009-07-13):
- Accepted the patch submitted by Takashi Imamichi;
the backtracking method now has three criteria for choosing the step
length:
- ::LBFGS_LINESEARCH_BACKTRACKING_ARMIJO: sufficient decrease (Armijo)
condition only
- ::LBFGS_LINESEARCH_BACKTRACKING_WOLFE: regular Wolfe condition
(sufficient decrease condition + curvature condition)
- ::LBFGS_LINESEARCH_BACKTRACKING_STRONG_WOLFE: strong Wolfe condition
- Updated the documentation to explain the above three criteria.
- Version 1.7 (2009-02-28):
- Improved OWL-QN routines for stability.
- Removed the support of OWL-QN method in MoreThuente algorithm because
it accidentally fails in early stages of iterations for some objectives.
Because of this change, <b>the OW-LQN method must be used with the
backtracking algorithm (::LBFGS_LINESEARCH_BACKTRACKING)</b>, or the
library returns ::LBFGSERR_INVALID_LINESEARCH.
- Renamed line search algorithms as follows:
- ::LBFGS_LINESEARCH_BACKTRACKING: regular Wolfe condition.
- ::LBFGS_LINESEARCH_BACKTRACKING_LOOSE: regular Wolfe condition.
- ::LBFGS_LINESEARCH_BACKTRACKING_STRONG: strong Wolfe condition.
- Source code clean-up.
- Version 1.6 (2008-11-02):
- Improved line-search algorithm with strong Wolfe condition, which was
contributed by Takashi Imamichi. This routine is now default for
::LBFGS_LINESEARCH_BACKTRACKING. The previous line search algorithm
with regular Wolfe condition is still available as
::LBFGS_LINESEARCH_BACKTRACKING_LOOSE.
- Configurable stop index for L1-norm computation. A member variable
::lbfgs_parameter_t::orthantwise_end was added to specify the index
number at which the library stops computing the L1 norm of the
variables. This is useful to prevent some variables from being
regularized by the OW-LQN method.
- A sample program written in C++ (sample/sample.cpp).
- Version 1.5 (2008-07-10):
- Configurable starting index for L1-norm computation. A member variable
::lbfgs_parameter_t::orthantwise_start was added to specify the index
number from which the library computes the L1 norm of the variables.
This is useful to prevent some variables from being regularized by the
OWL-QN method.
- Fixed a zero-division error when the initial variables have already
been a minimizer (reported by Takashi Imamichi). In this case, the
library returns ::LBFGS_ALREADY_MINIMIZED status code.
- Defined ::LBFGS_SUCCESS status code as zero; removed unused constants,
LBFGSFALSE and LBFGSTRUE.
- Fixed a compile error in an implicit down-cast.
- Version 1.4 (2008-04-25):
- Configurable line search algorithms. A member variable
::lbfgs_parameter_t::linesearch was added to choose either MoreThuente
method (::LBFGS_LINESEARCH_MORETHUENTE) or backtracking algorithm
(::LBFGS_LINESEARCH_BACKTRACKING).
- Fixed a bug: the previous version did not compute psuedo-gradients
properly in the line search routines for OWL-QN. This bug might quit
an iteration process too early when the OWL-QN routine was activated
(0 < ::lbfgs_parameter_t::orthantwise_c).
- Configure script for POSIX environments.
- SSE/SSE2 optimizations with GCC.
- New functions ::lbfgs_malloc and ::lbfgs_free to use SSE/SSE2 routines
transparently. It is uncessary to use these functions for libLBFGS built
without SSE/SSE2 routines; you can still use any memory allocators if
SSE/SSE2 routines are disabled in libLBFGS.
- Version 1.3 (2007-12-16):
- An API change. An argument was added to lbfgs() function to receive the
final value of the objective function. This argument can be set to
\c NULL if the final value is unnecessary.
- Fixed a null-pointer bug in the sample code (reported by Takashi Imamichi).
- Added build scripts for Microsoft Visual Studio 2005 and GCC.
- Added README file.
- Version 1.2 (2007-12-13):
- Fixed a serious bug in orthant-wise L-BFGS.
An important variable was used without initialization.
- Version 1.1 (2007-12-01):
- Implemented orthant-wise L-BFGS.
- Implemented lbfgs_parameter_init() function.
- Fixed several bugs.
- API documentation.
- Version 1.0 (2007-09-20):
- Initial release.
@section api Documentation
- @ref liblbfgs_api "libLBFGS API"
@section sample Sample code
@include sample.c
@section ack Acknowledgements
The L-BFGS algorithm is described in:
- Jorge Nocedal.
Updating Quasi-Newton Matrices with Limited Storage.
<i>Mathematics of Computation</i>, Vol. 35, No. 151, pp. 773--782, 1980.
- Dong C. Liu and Jorge Nocedal.
On the limited memory BFGS method for large scale optimization.
<i>Mathematical Programming</i> B, Vol. 45, No. 3, pp. 503-528, 1989.
The line search algorithms used in this implementation are described in:
- John E. Dennis and Robert B. Schnabel.
<i>Numerical Methods for Unconstrained Optimization and Nonlinear
Equations</i>, Englewood Cliffs, 1983.
- Jorge J. More and David J. Thuente.
Line search algorithm with guaranteed sufficient decrease.
<i>ACM Transactions on Mathematical Software (TOMS)</i>, Vol. 20, No. 3,
pp. 286-307, 1994.
This library also implements Orthant-Wise Limited-memory Quasi-Newton (OWL-QN)
method presented in:
- Galen Andrew and Jianfeng Gao.
Scalable training of L1-regularized log-linear models.
In <i>Proceedings of the 24th International Conference on Machine
Learning (ICML 2007)</i>, pp. 33-40, 2007.
Special thanks go to:
- Yoshimasa Tsuruoka and Daisuke Okanohara for technical information about
OWL-QN
- Takashi Imamichi for the useful enhancements of the backtracking method
- Kevin S. Van Horn, Nic Schraudolph, and Tamas Nepusz for bug fixes
Finally I would like to thank the original author, Jorge Nocedal, who has been
distributing the effieicnt and explanatory implementation in an open source
licence.
@section reference Reference
- <a href="http://www.ece.northwestern.edu/~nocedal/lbfgs.html">L-BFGS</a> by Jorge Nocedal.
- <a href="http://research.microsoft.com/en-us/downloads/b1eb1016-1738-4bd5-83a9-370c9d498a03/default.aspx">Orthant-Wise Limited-memory Quasi-Newton Optimizer for L1-regularized Objectives</a> by Galen Andrew.
- <a href="http://chasen.org/~taku/software/misc/lbfgs/">C port (via f2c)</a> by Taku Kudo.
- <a href="http://www.alglib.net/optimization/lbfgs.php">C#/C++/Delphi/VisualBasic6 port</a> in ALGLIB.
- <a href="http://cctbx.sourceforge.net/">Computational Crystallography Toolbox</a> includes
<a href="http://cctbx.sourceforge.net/current_cvs/c_plus_plus/namespacescitbx_1_1lbfgs.html">scitbx::lbfgs</a>.
*/
#endif/*__LBFGS_H__*/
+91
View File
@@ -0,0 +1,91 @@
/* mt.c
*
* Mersenne Twister random number generator, based on the implementation of
* Michael Brundage (which has been placed in the public domain).
*
* Author: Tamas Nepusz (original by Michael Brundage)
*
* See the following URL for the original implementation:
* http://www.qbrundage.com/michaelb/pubs/essays/random_number_generation.html
*
* This file has been placed in the public domain.
*/
#include "igraph_random.h"
#include "plfit_mt.h"
static uint16_t get_random_uint16(void) {
return RNG_INTEGER(0, 0xffff);
}
void plfit_mt_init(plfit_mt_rng_t* rng) {
plfit_mt_init_from_rng(rng, 0);
}
void plfit_mt_init_from_rng(plfit_mt_rng_t* rng, plfit_mt_rng_t* seeder) {
int i;
if (seeder == 0) {
for (i = 0; i < PLFIT_MT_LEN; i++) {
/* RAND_MAX is guaranteed to be at least 32767, so we can use two
* calls to rand() to produce a random 32-bit number */
rng->mt_buffer[i] = (((uint32_t) get_random_uint16()) << 16) + get_random_uint16();
}
} else {
for (i = 0; i < PLFIT_MT_LEN; i++) {
rng->mt_buffer[i] = plfit_mt_random(seeder);
}
}
rng->mt_index = 0;
}
#define MT_IA 397
#define MT_IB (PLFIT_MT_LEN - MT_IA)
#define UPPER_MASK 0x80000000
#define LOWER_MASK 0x7FFFFFFF
#define MATRIX_A 0x9908B0DF
#define TWIST(b,i,j) ((b)[i] & UPPER_MASK) | ((b)[j] & LOWER_MASK)
#define MAGIC(s) (((s)&1)*MATRIX_A)
uint32_t plfit_mt_random(plfit_mt_rng_t* rng) {
uint32_t * b = rng->mt_buffer;
int idx = rng->mt_index;
uint32_t s;
int i;
if (idx == PLFIT_MT_LEN * sizeof(uint32_t)) {
idx = 0;
i = 0;
for (; i < MT_IB; i++) {
s = TWIST(b, i, i+1);
b[i] = b[i + MT_IA] ^ (s >> 1) ^ MAGIC(s);
}
for (; i < PLFIT_MT_LEN-1; i++) {
s = TWIST(b, i, i+1);
b[i] = b[i - MT_IB] ^ (s >> 1) ^ MAGIC(s);
}
s = TWIST(b, PLFIT_MT_LEN-1, 0);
b[PLFIT_MT_LEN-1] = b[MT_IA-1] ^ (s >> 1) ^ MAGIC(s);
}
rng->mt_index = idx + sizeof(uint32_t);
return *(uint32_t *)((unsigned char *)b + idx);
/*
Matsumoto and Nishimura additionally confound the bits returned to the caller
but this doesn't increase the randomness, and slows down the generator by
as much as 25%. So I omit these operations here.
r ^= (r >> 11);
r ^= (r << 7) & 0x9D2C5680;
r ^= (r << 15) & 0xEFC60000;
r ^= (r >> 18);
*/
}
double plfit_mt_uniform_01(plfit_mt_rng_t* rng) {
return ((double)plfit_mt_random(rng)) / PLFIT_MT_RAND_MAX;
}
+52
View File
@@ -0,0 +1,52 @@
/* options.c
*
* Copyright (C) 2012 Tamas Nepusz
*
* 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 "plfit_error.h"
#include "plfit.h"
const plfit_continuous_options_t plfit_continuous_default_options = {
/* .finite_size_correction = */ 0,
/* .xmin_method = */ PLFIT_DEFAULT_CONTINUOUS_METHOD,
/* .p_value_method = */ PLFIT_DEFAULT_P_VALUE_METHOD,
/* .p_value_precision = */ 0.01,
/* .rng = */ 0
};
const plfit_discrete_options_t plfit_discrete_default_options = {
/* .finite_size_correction = */ 0,
/* .alpha_method = */ PLFIT_DEFAULT_DISCRETE_METHOD,
/* .alpha = */ {
/* .min = */ 1.01,
/* .max = */ 5,
/* .step = */ 0.01
},
/* .p_value_method = */ PLFIT_DEFAULT_P_VALUE_METHOD,
/* .p_value_precision = */ 0.01,
/* .rng = */ 0
};
int plfit_continuous_options_init(plfit_continuous_options_t* options) {
*options = plfit_continuous_default_options;
return PLFIT_SUCCESS;
}
int plfit_discrete_options_init(plfit_discrete_options_t* options) {
*options = plfit_discrete_default_options;
return PLFIT_SUCCESS;
}
File diff suppressed because it is too large Load Diff
+132
View File
@@ -0,0 +1,132 @@
/* plfit.h
*
* Copyright (C) 2010-2011 Tamas Nepusz
*
* 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.
*/
#ifndef PLFIT_H
#define PLFIT_H
#include <stdlib.h>
#include "plfit_decls.h"
#include "plfit_error.h"
#include "plfit_mt.h"
#include "plfit_sampling.h"
#include "plfit_version.h"
PLFIT_BEGIN_C_DECLS
typedef unsigned short int plfit_bool_t;
typedef enum {
PLFIT_LINEAR_ONLY,
PLFIT_STRATIFIED_SAMPLING,
PLFIT_GSS_OR_LINEAR,
PLFIT_DEFAULT_CONTINUOUS_METHOD = PLFIT_STRATIFIED_SAMPLING
} plfit_continuous_method_t;
typedef enum {
PLFIT_LBFGS,
PLFIT_LINEAR_SCAN,
PLFIT_PRETEND_CONTINUOUS,
PLFIT_DEFAULT_DISCRETE_METHOD = PLFIT_LBFGS
} plfit_discrete_method_t;
typedef enum {
PLFIT_P_VALUE_SKIP,
PLFIT_P_VALUE_APPROXIMATE,
PLFIT_P_VALUE_EXACT,
PLFIT_DEFAULT_P_VALUE_METHOD = PLFIT_P_VALUE_EXACT
} plfit_p_value_method_t;
typedef struct _plfit_result_t {
double alpha; /* fitted power-law exponent */
double xmin; /* cutoff where the power-law behaviour kicks in */
double L; /* log-likelihood of the sample */
double D; /* test statistic for the KS test */
double p; /* p-value of the KS test */
} plfit_result_t;
/********** structure that holds the options of plfit **********/
typedef struct _plfit_continuous_options_t {
plfit_bool_t finite_size_correction;
plfit_continuous_method_t xmin_method;
plfit_p_value_method_t p_value_method;
double p_value_precision;
plfit_mt_rng_t* rng;
} plfit_continuous_options_t;
typedef struct _plfit_discrete_options_t {
plfit_bool_t finite_size_correction;
plfit_discrete_method_t alpha_method;
struct {
double min;
double max;
double step;
} alpha;
plfit_p_value_method_t p_value_method;
double p_value_precision;
plfit_mt_rng_t* rng;
} plfit_discrete_options_t;
PLFIT_EXPORT int plfit_continuous_options_init(plfit_continuous_options_t* options);
PLFIT_EXPORT int plfit_discrete_options_init(plfit_discrete_options_t* options);
PLFIT_EXPORT extern const plfit_continuous_options_t plfit_continuous_default_options;
PLFIT_EXPORT extern const plfit_discrete_options_t plfit_discrete_default_options;
/********** continuous power law distribution fitting **********/
PLFIT_EXPORT int plfit_log_likelihood_continuous(const double* xs, size_t n, double alpha,
double xmin, double* l);
PLFIT_EXPORT int plfit_estimate_alpha_continuous(const double* xs, size_t n, double xmin,
const plfit_continuous_options_t* options, plfit_result_t* result);
PLFIT_EXPORT int plfit_continuous(const double* xs, size_t n,
const plfit_continuous_options_t* options, plfit_result_t* result);
/*********** discrete power law distribution fitting ***********/
PLFIT_EXPORT int plfit_estimate_alpha_discrete(const double* xs, size_t n, double xmin,
const plfit_discrete_options_t* options, plfit_result_t *result);
PLFIT_EXPORT int plfit_log_likelihood_discrete(const double* xs, size_t n, double alpha, double xmin, double* l);
PLFIT_EXPORT int plfit_discrete(const double* xs, size_t n, const plfit_discrete_options_t* options,
plfit_result_t* result);
/***** resampling routines to generate synthetic replicates ****/
PLFIT_EXPORT int plfit_resample_continuous(const double* xs, size_t n, double alpha, double xmin,
size_t num_samples, plfit_mt_rng_t* rng, double* result);
PLFIT_EXPORT int plfit_resample_discrete(const double* xs, size_t n, double alpha, double xmin,
size_t num_samples, plfit_mt_rng_t* rng, double* result);
/******** calculating the p-value of a fitted model only *******/
PLFIT_EXPORT int plfit_calculate_p_value_continuous(const double* xs, size_t n,
const plfit_continuous_options_t* options, plfit_bool_t xmin_fixed,
plfit_result_t *result);
PLFIT_EXPORT int plfit_calculate_p_value_discrete(const double* xs, size_t n,
const plfit_discrete_options_t* options, plfit_bool_t xmin_fixed,
plfit_result_t *result);
/************* calculating descriptive statistics **************/
PLFIT_EXPORT int plfit_moments(const double* data, size_t n, double* mean, double* variance,
double* skewness, double* kurtosis);
PLFIT_END_C_DECLS
#endif /* PLFIT_H */
+35
View File
@@ -0,0 +1,35 @@
/* plfit_decls.h
*
* Copyright (C) 2024 Tamas Nepusz
*
* 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.
*/
#ifndef PLFIT_DECLS_H
#define PLFIT_DECLS_H
#undef PLFIT_BEGIN_C_DECLS
#undef PLFIT_END_C_DECLS
#ifdef __cplusplus
#define PLFIT_BEGIN_C_DECLS extern "C" {
#define PLFIT_END_C_DECLS }
#else
#define PLFIT_BEGIN_C_DECLS /* empty */
#define PLFIT_END_C_DECLS /* empty */
#endif
#define PLFIT_EXPORT /* empty */
#endif /* PLFIT_DECLS_H */
+66
View File
@@ -0,0 +1,66 @@
/* error.c
*
* Copyright (C) 2010-2011 Tamas Nepusz
*
* 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 <stdio.h>
#include <stdlib.h>
#include "plfit_error.h"
static const char *plfit_i_error_strings[] = {
"No error",
"Failed",
"Invalid value",
"Underflow",
"Overflow",
"Not enough memory",
"Maximum number of iterations exceeded"
};
#ifndef USING_R
static plfit_error_handler_t* plfit_error_handler = plfit_error_handler_printignore;
#else
/* This is overwritten, anyway */
static plfit_error_handler_t* plfit_error_handler = plfit_error_handler_ignore;
#endif
const char* plfit_strerror(const int plfit_errno) {
return plfit_i_error_strings[plfit_errno];
}
plfit_error_handler_t* plfit_set_error_handler(plfit_error_handler_t* new_handler) {
plfit_error_handler_t* old_handler = plfit_error_handler;
plfit_error_handler = new_handler;
return old_handler;
}
void plfit_error(const char *reason, const char *file, int line,
int plfit_errno) {
plfit_error_handler(reason, file, line, plfit_errno);
}
#ifndef USING_R
void plfit_error_handler_printignore(const char *reason, const char *file, int line,
int plfit_errno) {
fprintf(stderr, "Error at %s:%i : %s, %s\n", file, line, reason,
plfit_strerror(plfit_errno));
}
#endif
void plfit_error_handler_ignore(const char* reason, const char* file, int line,
int plfit_errno) {
}
+72
View File
@@ -0,0 +1,72 @@
/* plfit_error.h
*
* Copyright (C) 2010-2011 Tamas Nepusz
*
* 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.
*/
#ifndef PLFIT_ERROR_H
#define PLFIT_ERROR_H
#include "plfit_decls.h"
PLFIT_BEGIN_C_DECLS
enum {
PLFIT_SUCCESS = 0,
PLFIT_FAILURE = 1,
PLFIT_EINVAL = 2,
PLFIT_UNDRFLOW = 3,
PLFIT_OVERFLOW = 4,
PLFIT_ENOMEM = 5,
PLFIT_EMAXITER = 6
};
#if (defined(__GNUC__) && GCC_VERSION_MAJOR >= 3)
# define PLFIT_UNLIKELY(a) __builtin_expect((a), 0)
# define PLFIT_LIKELY(a) __builtin_expect((a), 1)
#else
# define PLFIT_UNLIKELY(a) a
# define PLFIT_LIKELY(a) a
#endif
#define PLFIT_CHECK(a) \
do {\
int plfit_i_ret=(a); \
if (PLFIT_UNLIKELY(plfit_i_ret != PLFIT_SUCCESS)) {\
return plfit_i_ret; \
} \
} while (0)
#define PLFIT_ERROR(reason,plfit_errno) \
do {\
plfit_error (reason, __FILE__, __LINE__, plfit_errno) ; \
return plfit_errno ; \
} while (0)
typedef void plfit_error_handler_t(const char*, const char*, int, int);
PLFIT_EXPORT extern plfit_error_handler_t plfit_error_handler_abort;
PLFIT_EXPORT extern plfit_error_handler_t plfit_error_handler_ignore;
PLFIT_EXPORT extern plfit_error_handler_t plfit_error_handler_printignore;
PLFIT_EXPORT plfit_error_handler_t* plfit_set_error_handler(plfit_error_handler_t* new_handler);
PLFIT_EXPORT void plfit_error(const char *reason, const char *file, int line, int plfit_errno);
PLFIT_EXPORT const char* plfit_strerror(const int plfit_errno);
PLFIT_END_C_DECLS
#endif /* PLFIT_ERROR_H */
+87
View File
@@ -0,0 +1,87 @@
/* plfit_mt.h
*
* Mersenne Twister random number generator, based on the implementation of
* Michael Brundage (which has been placed in the public domain).
*
* Author: Tamas Nepusz (original by Michael Brundage)
*
* See the following URL for the original implementation:
* http://www.qbrundage.com/michaelb/pubs/essays/random_number_generation.html
*
* This file has been placed in the public domain.
*/
#ifndef PLFIT_MT_H
#define PLFIT_MT_H
#include <stdint.h>
#include "plfit_decls.h"
PLFIT_BEGIN_C_DECLS
#define PLFIT_MT_LEN 624
/**
* \def PLFIT_MT_RAND_MAX
*
* The maximum random number that \c plfit_mt_random() can generate.
*/
#define PLFIT_MT_RAND_MAX 0xFFFFFFFF
/**
* Struct that stores the internal state of a Mersenne Twister random number
* generator.
*/
typedef struct {
int mt_index;
uint32_t mt_buffer[PLFIT_MT_LEN];
} plfit_mt_rng_t;
/**
* \brief Initializes a Mersenne Twister random number generator.
*
* The random number generator is seeded with random 32-bit numbers obtained
* from the \em built-in random number generator using consecutive calls to
* \c rand().
*
* \param rng the random number generator to initialize
*/
PLFIT_EXPORT void plfit_mt_init(plfit_mt_rng_t* rng);
/**
* \brief Initializes a Mersenne Twister random number generator, seeding it
* from another one.
*
* The random number generator is seeded with random 32-bit numbers obtained
* from another, initialized Mersenne Twister random number generator.
*
* \param rng the random number generator to initialize
* \param seeder the random number generator that will seed the one being
* initialized. When null, the random number generator will
* be initialized from the built-in RNG as if \ref plfit_mt_init()
* was called.
*/
PLFIT_EXPORT void plfit_mt_init_from_rng(plfit_mt_rng_t* rng, plfit_mt_rng_t* seeder);
/**
* \brief Returns the next 32-bit random number from the given Mersenne Twister
* random number generator.
*
* \param rng the random number generator to use
* \return the next 32-bit random number from the generator
*/
PLFIT_EXPORT uint32_t plfit_mt_random(plfit_mt_rng_t* rng);
/**
* \brief Returns a uniformly distributed double from the interval [0;1)
* based on the next value of the given Mersenne Twister random number
* generator.
*
* \param rng the random number generator to use
* \return a uniformly distributed random number from the interval [0;1)
*/
PLFIT_EXPORT double plfit_mt_uniform_01(plfit_mt_rng_t* rng);
PLFIT_END_C_DECLS
#endif /* PLFIT_MT_H */
+168
View File
@@ -0,0 +1,168 @@
/* plfit_sampling.h
*
* Copyright (C) 2012 Tamas Nepusz
*
* 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.
*/
#ifndef PLFIT_SAMPLING_H
#define PLFIT_SAMPLING_H
#include <stdlib.h>
#include "plfit_decls.h"
#include "plfit_mt.h"
PLFIT_BEGIN_C_DECLS
/**
* Draws a sample from a binomial distribution with the given count and
* probability values.
*
* This function is borrowed from R; see the corresponding license in
* \c rbinom.c. The return value is always an integer.
*
* The function is \em not thread-safe.
*
* \param n the number of trials
* \param p the success probability of each trial
* \param rng the Mersenne Twister random number generator to use
* \return the value drawn from the given binomial distribution.
*/
PLFIT_EXPORT double plfit_rbinom(double n, double p, plfit_mt_rng_t* rng);
/**
* Draws a sample from a Pareto distribution with the given minimum value and
* power-law exponent.
*
* \param xmin the minimum value of the distribution. Must be positive.
* \param alpha the exponent. Must be positive
* \param rng the Mersenne Twister random number generator to use
*
* \return the sample or NaN if one of the parameters is invalid
*/
PLFIT_EXPORT extern double plfit_rpareto(double xmin, double alpha, plfit_mt_rng_t* rng);
/**
* Draws a given number of samples from a Pareto distribution with the given
* minimum value and power-law exponent.
*
* \param xmin the minimum value of the distribution. Must be positive.
* \param alpha the exponent. Must be positive
* \param n the number of samples to draw
* \param rng the Mersenne Twister random number generator to use
* \param result the array where the result should be written. It must
* have enough space to store n items
*
* \return \c PLFIT_EINVAL if one of the parameters is invalid, zero otherwise
*/
PLFIT_EXPORT int plfit_rpareto_array(double xmin, double alpha, size_t n, plfit_mt_rng_t* rng,
double* result);
/**
* Draws a sample from a zeta distribution with the given minimum value and
* power-law exponent.
*
* \param xmin the minimum value of the distribution. Must be positive.
* \param alpha the exponent. Must be positive
* \param rng the Mersenne Twister random number generator to use
*
* \return the sample or NaN if one of the parameters is invalid
*/
PLFIT_EXPORT extern double plfit_rzeta(long int xmin, double alpha, plfit_mt_rng_t* rng);
/**
* Draws a given number of samples from a zeta distribution with the given
* minimum value and power-law exponent.
*
* \param xmin the minimum value of the distribution. Must be positive.
* \param alpha the exponent. Must be positive
* \param n the number of samples to draw
* \param rng the Mersenne Twister random number generator to use
* \param result the array where the result should be written. It must
* have enough space to store n items
*
* \return \c PLFIT_EINVAL if one of the parameters is invalid, zero otherwise
*/
PLFIT_EXPORT int plfit_rzeta_array(long int xmin, double alpha, size_t n, plfit_mt_rng_t* rng,
double* result);
/**
* Draws a sample from a uniform distribution with the given lower and
* upper bounds.
*
* The lower bound is inclusive, the uppoer bound is not.
*
* \param lo the lower bound
* \param hi the upper bound
* \param rng the Mersenne Twister random number generator to use
* \return the value drawn from the given uniform distribution.
*/
PLFIT_EXPORT extern double plfit_runif(double lo, double hi, plfit_mt_rng_t* rng);
/**
* Draws a sample from a uniform distribution over the [0; 1) interval.
*
* The interval is closed from the left and open from the right.
*
* \param rng the Mersenne Twister random number generator to use
* \return the value drawn from the given uniform distribution.
*/
PLFIT_EXPORT extern double plfit_runif_01(plfit_mt_rng_t* rng);
/**
* Random sampler using Walker's alias method.
*/
typedef struct {
long int num_bins; /**< Number of bins */
long int* indexes; /**< Index of the "other" element in each bin */
double* probs; /**< Probability of drawing the "own" element from a bin */
} plfit_walker_alias_sampler_t;
/**
* \brief Initializes the sampler with item probabilities.
*
* \param sampler the sampler to initialize
* \param ps pointer to an array containing a value proportional to the
* sampling probability of each item in the set being sampled.
* \param n the number of items in the array
* \return error code
*/
PLFIT_EXPORT int plfit_walker_alias_sampler_init(plfit_walker_alias_sampler_t* sampler,
double* ps, size_t n);
/**
* \brief Destroys an initialized sampler and frees the allocated memory.
*
* \param sampler the sampler to destroy
*/
PLFIT_EXPORT void plfit_walker_alias_sampler_destroy(plfit_walker_alias_sampler_t* sampler);
/**
* \brief Draws a given number of samples from the sampler and writes them
* to a given array.
*
* \param sampler the sampler to use
* \param xs pointer to an array where the sampled items should be
* written
* \param n the number of samples to draw
* \param rng the Mersenne Twister random number generator to use
* \return error code
*/
PLFIT_EXPORT int plfit_walker_alias_sampler_sample(const plfit_walker_alias_sampler_t* sampler,
long int* xs, size_t n, plfit_mt_rng_t* rng);
PLFIT_END_C_DECLS
#endif /* PLFIT_SAMPLING_H */
+28
View File
@@ -0,0 +1,28 @@
/* plfit_version.h
*
* Copyright (C) 2021 Tamas Nepusz
*
* 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.
*/
#ifndef PLFIT_VERSION_H
#define PLFIT_VERSION_H
#define PLFIT_VERSION_MAJOR 1
#define PLFIT_VERSION_MINOR 0
#define PLFIT_VERSION_PATCH 0
#define PLFIT_VERSION_STRING "1.0.0"
#endif
+206
View File
@@ -0,0 +1,206 @@
/*
* Mathlib : A C Library of Special Functions
* Copyright (C) 1998 Ross Ihaka
* Copyright (C) 2000-2002 The R Core Team
* Copyright (C) 2007 The R Foundation
*
* 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, a copy is available at
* http://www.r-project.org/Licenses/
*
* SYNOPSIS
*
* #include <Rmath.h>
* double rbinom(double nin, double pp)
*
* DESCRIPTION
*
* Random variates from the binomial distribution.
*
* REFERENCE
*
* Kachitvichyanukul, V. and Schmeiser, B. W. (1988).
* Binomial random variate generation.
* Communications of the ACM 31, 216-222.
* (Algorithm BTPEC).
*/
/*
* Modifications for this file were performed by Tamas Nepusz to make it fit
* better with plfit. The license of the original file applies to the
* modifications as well.
*/
#include <math.h>
#include <stdlib.h>
#include "plfit_sampling.h"
#define repeat for(;;)
double plfit_rbinom(double nin, double pp, plfit_mt_rng_t* rng)
{
/* FIXME: These should become THREAD_specific globals : */
static double c, fm, npq, p1, p2, p3, p4, qn;
static double xl, xll, xlr, xm, xr;
static double psave = -1.0;
static int nsave = -1;
static int m;
double f, f1, f2, u, v, w, w2, x, x1, x2, z, z2;
double p, q, np, g, r, al, alv, amaxp, ffm, ynorm;
int i, ix, k, n;
if (!isfinite(nin)) return NAN;
r = floor(nin + 0.5);
if (r != nin) return NAN;
if (!isfinite(pp) ||
/* n=0, p=0, p=1 are not errors <TSL>*/
r < 0 || pp < 0. || pp > 1.) return NAN;
if (r == 0 || pp == 0.) return 0;
if (pp == 1.) return r;
n = (int) r;
p = fmin(pp, 1. - pp);
q = 1. - p;
np = n * p;
r = p / q;
g = r * (n + 1);
/* Setup, perform only when parameters change [using static (globals): */
/* FIXING: Want this thread safe
-- use as little (thread globals) as possible
*/
if (pp != psave || n != nsave) {
psave = pp;
nsave = n;
if (np < 30.0) {
/* inverse cdf logic for mean less than 30 */
qn = pow(q, (double) n);
goto L_np_small;
} else {
ffm = np + p;
m = (int) ffm;
fm = m;
npq = np * q;
p1 = (int)(2.195 * sqrt(npq) - 4.6 * q) + 0.5;
xm = fm + 0.5;
xl = xm - p1;
xr = xm + p1;
c = 0.134 + 20.5 / (15.3 + fm);
al = (ffm - xl) / (ffm - xl * p);
xll = al * (1.0 + 0.5 * al);
al = (xr - ffm) / (xr * q);
xlr = al * (1.0 + 0.5 * al);
p2 = p1 * (1.0 + c + c);
p3 = p2 + c / xll;
p4 = p3 + c / xlr;
}
} else if (n == nsave) {
if (np < 30.0)
goto L_np_small;
}
/*-------------------------- np = n*p >= 30 : ------------------- */
repeat {
u = plfit_runif_01(rng) * p4;
v = plfit_runif_01(rng);
/* triangular region */
if (u <= p1) {
ix = (int)(xm - p1 * v + u);
goto finis;
}
/* parallelogram region */
if (u <= p2) {
x = xl + (u - p1) / c;
v = v * c + 1.0 - fabs(xm - x) / p1;
if (v > 1.0 || v <= 0.)
continue;
ix = (int) x;
} else {
if (u > p3) { /* right tail */
ix = (int)(xr - log(v) / xlr);
if (ix > n)
continue;
v = v * (u - p3) * xlr;
} else {/* left tail */
ix = (int)(xl + log(v) / xll);
if (ix < 0)
continue;
v = v * (u - p2) * xll;
}
}
/* determine appropriate way to perform accept/reject test */
k = abs(ix - m);
if (k <= 20 || k >= npq / 2 - 1) {
/* explicit evaluation */
f = 1.0;
if (m < ix) {
for (i = m + 1; i <= ix; i++)
f *= (g / i - r);
} else if (m != ix) {
for (i = ix + 1; i <= m; i++)
f /= (g / i - r);
}
if (v <= f)
goto finis;
} else {
/* squeezing using upper and lower bounds on log(f(x)) */
amaxp = (k / npq) * ((k * (k / 3. + 0.625) + (1.0 / 6.0)) / npq + 0.5);
ynorm = -k * k / (2.0 * npq);
alv = log(v);
if (alv < ynorm - amaxp)
goto finis;
if (alv <= ynorm + amaxp) {
/* stirling's formula to machine accuracy */
/* for the final acceptance/rejection test */
x1 = ix + 1;
f1 = fm + 1.0;
z = n + 1 - fm;
w = n - ix + 1.0;
z2 = z * z;
x2 = x1 * x1;
f2 = f1 * f1;
w2 = w * w;
if (alv <= xm * log(f1 / x1) + (n - m + 0.5) * log(z / w) + (ix - m) * log(w * p / (x1 * q)) + (13860.0 - (462.0 - (132.0 - (99.0 - 140.0 / f2) / f2) / f2) / f2) / f1 / 166320.0 + (13860.0 - (462.0 - (132.0 - (99.0 - 140.0 / z2) / z2) / z2) / z2) / z / 166320.0 + (13860.0 - (462.0 - (132.0 - (99.0 - 140.0 / x2) / x2) / x2) / x2) / x1 / 166320.0 + (13860.0 - (462.0 - (132.0 - (99.0 - 140.0 / w2) / w2) / w2) / w2) / w / 166320.)
goto finis;
}
}
}
L_np_small:
/*---------------------- np = n*p < 30 : ------------------------- */
repeat {
ix = 0;
f = qn;
u = plfit_runif_01(rng);
repeat {
if (u < f)
goto finis;
if (ix > 110)
break;
u -= f;
ix++;
f *= (g / ix - r);
}
}
finis:
if (psave > 0.5)
ix = n - ix;
return (double)ix;
}
+311
View File
@@ -0,0 +1,311 @@
/* sampling.c
*
* Copyright (C) 2012 Tamas Nepusz
*
* 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 <limits.h>
#include <math.h>
#include "igraph_random.h"
#include "plfit_error.h"
#include "plfit_sampling.h"
inline double plfit_runif(double lo, double hi, plfit_mt_rng_t* rng) {
if (rng == 0) {
return RNG_UNIF(lo, hi);
}
return lo + plfit_mt_uniform_01(rng) * (hi-lo);
}
inline double plfit_runif_01(plfit_mt_rng_t* rng) {
if (rng == 0) {
return RNG_UNIF01();
}
return plfit_mt_uniform_01(rng);
}
inline double plfit_rpareto(double xmin, double alpha, plfit_mt_rng_t* rng) {
if (alpha <= 0 || xmin <= 0)
return NAN;
/* 1-u is used in the base here because we want to avoid the case of
* sampling zero */
return pow(1-plfit_runif_01(rng), -1.0 / alpha) * xmin;
}
int plfit_rpareto_array(double xmin, double alpha, size_t n, plfit_mt_rng_t* rng,
double* result) {
double gamma;
if (alpha <= 0 || xmin <= 0)
return PLFIT_EINVAL;
if (result == 0 || n == 0)
return PLFIT_SUCCESS;
gamma = -1.0 / alpha;
while (n > 0) {
/* 1-u is used in the base here because we want to avoid the case of
* sampling zero */
*result = pow(1-plfit_runif_01(rng), gamma) * xmin;
result++; n--;
}
return PLFIT_SUCCESS;
}
inline double plfit_rzeta(long int xmin, double alpha, plfit_mt_rng_t* rng) {
double u, v, t;
long int x;
double alpha_minus_1 = alpha-1;
double minus_1_over_alpha_minus_1 = -1.0 / (alpha-1);
double b;
double one_over_b_minus_1;
if (alpha <= 0 || xmin < 1)
return NAN;
xmin = (long int) round(xmin);
/* Rejection sampling for the win. We use Y=floor(U^{-1/alpha} * xmin) as the
* envelope distribution, similarly to Chapter X.6 of Luc Devroye's book
* (where xmin is assumed to be 1): http://luc.devroye.org/chapter_ten.pdf
*
* Some notes that should help me recover what I was doing:
*
* p_i = 1/zeta(alpha, xmin) * i^-alpha
* q_i = (xmin/i)^{alpha-1} - (xmin/(i+1))^{alpha-1}
* = (i/xmin)^{1-alpha} - ((i+1)/xmin)^{1-alpha}
* = [i^{1-alpha} - (i+1)^{1-alpha}] / xmin^{1-alpha}
*
* p_i / q_i attains its maximum at xmin=i, so the rejection constant is:
*
* c = p_xmin / q_xmin
*
* We have to accept the sample if V <= (p_i / q_i) * (q_xmin / p_xmin) =
* (i/xmin)^-alpha * [xmin^{1-alpha} - (xmin+1)^{1-alpha}] / [i^{1-alpha} - (i+1)^{1-alpha}] =
* [xmin - xmin^alpha / (xmin+1)^{alpha-1}] / [i - i^alpha / (i+1)^{alpha-1}] =
* xmin/i * [1-(xmin/(xmin+1))^{alpha-1}]/[1-(i/(i+1))^{alpha-1}]
*
* In other words (and substituting i with X, which is the same),
*
* V * (X/xmin) <= [1 - (1+1/xmin)^{1-alpha}] / [1 - (1+1/i)^{1-alpha}]
*
* Let b := (1+1/xmin)^{alpha-1} and let T := (1+1/i)^{alpha-1}. Then:
*
* V * (X/xmin) <= [(b-1)/b] / [(T-1)/T]
* V * (X/xmin) * (T-1) / (b-1) <= T / b
*
* which is the same as in Devroye's book, except for the X/xmin term, and
* the definition of b.
*/
b = pow(1 + 1.0/xmin, alpha_minus_1);
one_over_b_minus_1 = 1.0/(b-1);
do {
do {
u = plfit_runif_01(rng);
v = plfit_runif_01(rng);
/* 1-u is used in the base here because we want to avoid the case of
* having zero in x */
x = (long int) floor(pow(1-u, minus_1_over_alpha_minus_1) * xmin);
} while (x < xmin);
t = pow((x+1.0)/x, alpha_minus_1);
} while (v*x*(t-1)*one_over_b_minus_1*b > t*xmin);
return x;
}
int plfit_rzeta_array(long int xmin, double alpha, size_t n, plfit_mt_rng_t* rng,
double* result) {
double u, v, t;
long int x;
double alpha_minus_1 = alpha-1;
double minus_1_over_alpha_minus_1 = -1.0 / (alpha-1);
double b, one_over_b_minus_1;
if (alpha <= 0 || xmin < 1)
return PLFIT_EINVAL;
if (result == 0 || n == 0)
return PLFIT_SUCCESS;
/* See the comments in plfit_rzeta for an explanation of the algorithm
* below. */
xmin = (long int) round(xmin);
b = pow(1 + 1.0/xmin, alpha_minus_1);
one_over_b_minus_1 = 1.0/(b-1);
while (n > 0) {
do {
do {
u = plfit_runif_01(rng);
v = plfit_runif_01(rng);
/* 1-u is used in the base here because we want to avoid the case of
* having zero in x */
x = (long int) floor(pow(1-u, minus_1_over_alpha_minus_1) * xmin);
} while (x < xmin); /* handles overflow as well */
t = pow((x+1.0)/x, alpha_minus_1);
} while (v*x*(t-1)*one_over_b_minus_1*b > t*xmin);
*result = x;
if (x < 0) return PLFIT_EINVAL;
result++; n--;
}
return PLFIT_SUCCESS;
}
int plfit_walker_alias_sampler_init(plfit_walker_alias_sampler_t* sampler,
double* ps, size_t n) {
double *p, *p2, *ps_end;
double sum;
long int *short_sticks, *long_sticks;
long int num_short_sticks, num_long_sticks;
long int i;
if (n > LONG_MAX) {
return PLFIT_EINVAL;
}
sampler->num_bins = (long int) n;
ps_end = ps + n;
/* Initialize indexes and probs */
sampler->indexes = (long int*)calloc(n > 0 ? n : 1, sizeof(long int));
if (sampler->indexes == NULL) {
return PLFIT_ENOMEM;
}
sampler->probs = (double*)calloc(n > 0 ? n : 1, sizeof(double));
if (sampler->probs == NULL) {
free(sampler->indexes);
return PLFIT_ENOMEM;
}
/* Normalize the probability vector; count how many short and long sticks
* are there initially */
for (sum = 0.0, p = ps; p != ps_end; p++) {
sum += *p;
}
sum = n / sum;
num_short_sticks = num_long_sticks = 0;
for (p = ps, p2 = sampler->probs; p != ps_end; p++, p2++) {
*p2 = *p * sum;
if (*p2 < 1) {
num_short_sticks++;
} else if (*p2 > 1) {
num_long_sticks++;
}
}
/* Allocate space for short & long stick indexes */
long_sticks = (long int*)calloc(num_long_sticks > 0 ? num_long_sticks : 1, sizeof(long int));
if (long_sticks == NULL) {
free(sampler->probs);
free(sampler->indexes);
return PLFIT_ENOMEM;
}
short_sticks = (long int*)calloc(num_short_sticks > 0 ? num_short_sticks : 1, sizeof(long int));
if (short_sticks == NULL) {
free(sampler->probs);
free(sampler->indexes);
free(long_sticks);
return PLFIT_ENOMEM;
}
/* Initialize short_sticks and long_sticks */
num_short_sticks = num_long_sticks = 0;
for (i = 0, p = sampler->probs; i < n; i++, p++) {
if (*p < 1) {
short_sticks[num_short_sticks++] = i;
} else if (*p > 1) {
long_sticks[num_long_sticks++] = i;
}
}
/* Prepare the index table */
while (num_short_sticks && num_long_sticks) {
long int short_index, long_index;
short_index = short_sticks[--num_short_sticks];
long_index = long_sticks[num_long_sticks-1];
sampler->indexes[short_index] = long_index;
sampler->probs[long_index] = /* numerical stability */
(sampler->probs[long_index] + sampler->probs[short_index]) - 1;
if (sampler->probs[long_index] < 1) {
short_sticks[num_short_sticks++] = long_index;
num_long_sticks--;
}
}
/* Fix numerical stability issues */
while (num_long_sticks) {
i = long_sticks[--num_long_sticks];
sampler->probs[i] = 1;
}
while (num_short_sticks) {
i = short_sticks[--num_short_sticks];
sampler->probs[i] = 1;
}
free(short_sticks);
free(long_sticks);
return PLFIT_SUCCESS;
}
void plfit_walker_alias_sampler_destroy(plfit_walker_alias_sampler_t* sampler) {
if (sampler->indexes) {
free(sampler->indexes);
sampler->indexes = 0;
}
if (sampler->probs) {
free(sampler->probs);
sampler->probs = 0;
}
}
int plfit_walker_alias_sampler_sample(const plfit_walker_alias_sampler_t* sampler,
long int *xs, size_t n, plfit_mt_rng_t* rng) {
double u;
long int j;
long int *x;
x = xs;
if (rng == 0) {
/* Using built-in RNG */
while (n > 0) {
u = RNG_UNIF01();
j = RNG_INTEGER(0, sampler->num_bins - 1);
*x = (u < sampler->probs[j]) ? j : sampler->indexes[j];
n--; x++;
}
} else {
/* Using Mersenne Twister */
while (n > 0) {
u = plfit_mt_uniform_01(rng);
j = plfit_mt_random(rng) % sampler->num_bins;
*x = (u < sampler->probs[j]) ? j : sampler->indexes[j];
n--; x++;
}
}
return PLFIT_SUCCESS;
}