diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0f1ba11..af0dff2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,6 +2,10 @@ name: Build and run tests on: [push, pull_request] +env: + CMAKE_COLOR_DIAGNOSTICS: ON + OMP_NUM_THREADS: 1 + jobs: build: name: ${{ matrix.config.name }} @@ -12,21 +16,45 @@ jobs: matrix: config: - { - name: "Windows", + name: "Windows static", + os: windows-latest, + build_type: "Release", + shared_libs: false, + generators: "Visual Studio 17 2022" + } + - { + name: "Ubuntu Linux static", + os: ubuntu-latest, + build_type: "Release", + shared_libs: false, + generators: "Ninja" + } + - { + name: "macOS static", + os: macos-latest, + build_type: "Release", + shared_libs: false, + generators: "Ninja" + } + - { + name: "Windows shared", os: windows-latest, build_type: "Release", + shared_libs: true, generators: "Visual Studio 17 2022" } - { - name: "Ubuntu Linux", + name: "Ubuntu Linux shared", os: ubuntu-latest, build_type: "Release", + shared_libs: true, generators: "Ninja" } - { - name: "macOS", + name: "macOS shared", os: macos-latest, build_type: "Release", + shared_libs: true, generators: "Ninja" } @@ -56,6 +84,7 @@ jobs: cmake \ -S . \ -B build \ + -DBUILD_SHARED_LIBS=${{ matrix.config.shared_libs }} \ -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} \ -G "${{ matrix.config.generators }}" \ -DCMAKE_INSTALL_PREFIX:PATH=instdir @@ -70,13 +99,13 @@ jobs: CTEST_OUTPUT_ON_FAILURE: 1 run: | cd build - ctest --output-junit report.xml + ctest -C ${{ matrix.config.build_type }} --output-junit report.xml - name: Archive unit test results uses: actions/upload-artifact@v4 if: always() with: - name: unit-test-results-${{ matrix.config.os }} + name: unit-test-results-${{ matrix.config.os }}-${{ matrix.config.shared_libs }} path: build/report.xml - name: Install and strip diff --git a/src/arithmetic_ansi.h b/src/arithmetic_ansi.h index 83ed11e..fa390da 100644 --- a/src/arithmetic_ansi.h +++ b/src/arithmetic_ansi.h @@ -23,7 +23,7 @@ * THE SOFTWARE. */ -/* $Id: arithmetic_ansi.h 65 2010-01-29 12:19:16Z naoaki $ */ +/* $Id$ */ #include #include @@ -51,7 +51,7 @@ inline static void vecfree(void *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; } diff --git a/src/arithmetic_sse_double.h b/src/arithmetic_sse_double.h index a5575fe..dcee5a9 100644 --- a/src/arithmetic_sse_double.h +++ b/src/arithmetic_sse_double.h @@ -23,14 +23,12 @@ * THE SOFTWARE. */ -/* $Id: arithmetic_sse_double.h 65 2010-01-29 12:19:16Z naoaki $ */ +/* $Id$ */ #include - -#if !defined(__APPLE__) +#ifndef __APPLE__ #include #endif - #include #if 1400 <= _MSC_VER @@ -43,13 +41,15 @@ inline static void* vecalloc(size_t size) { -#ifdef _WIN32 +#if defined(_WIN32) void *memblock = _aligned_malloc(size, 16); -#elif defined(__APPLE__) - /* Memory on Mac OS X is already aligned to 16 bytes */ - void *memblock = malloc(size); +#elif defined(__APPLE__) /* OS X always aligns on 16-byte boundaries */ + void *memblock = malloc(size); #else - void *memblock = memalign(16, size); + void *memblock = NULL, *p = NULL; + if (posix_memalign(&p, 16, size) == 0) { + memblock = p; + } #endif if (memblock != NULL) { memset(memblock, 0, size); @@ -59,7 +59,7 @@ inline static void* vecalloc(size_t size) inline static void vecfree(void *memblock) { -#ifdef _WIN32 +#ifdef _MSC_VER _aligned_free(memblock); #else free(memblock); @@ -199,7 +199,7 @@ inline static void vecfree(void *memblock) -#if 3 <= __SSE__ +#if 3 <= __SSE__ || defined(__SSE3__) /* Horizontal add with haddps SSE3 instruction. The work register (rw) is unused. diff --git a/src/arithmetic_sse_float.h b/src/arithmetic_sse_float.h index b88f0e2..f5691b1 100644 --- a/src/arithmetic_sse_float.h +++ b/src/arithmetic_sse_float.h @@ -23,14 +23,12 @@ * THE SOFTWARE. */ -/* $Id: arithmetic_sse_float.h 65 2010-01-29 12:19:16Z naoaki $ */ +/* $Id$ */ #include - -#if !defined(__APPLE__) +#ifndef __APPLE__ #include #endif - #include #if 1400 <= _MSC_VER @@ -49,7 +47,16 @@ 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); } @@ -58,7 +65,11 @@ inline static void* vecalloc(size_t size) inline static void vecfree(void *memblock) { +#ifdef _MSC_VER _aligned_free(memblock); +#else + free(memblock); +#endif } #define vecset(x, c, n) \ @@ -189,7 +200,7 @@ inline static void vecfree(void *memblock) -#if 3 <= __SSE__ +#if 3 <= __SSE__ || defined(__SSE3__) /* Horizontal add with haddps SSE3 instruction. The work register (rw) is unused. diff --git a/src/lbfgs.c b/src/lbfgs.c index 3f67094..6472a9a 100644 --- a/src/lbfgs.c +++ b/src/lbfgs.c @@ -24,7 +24,7 @@ * THE SOFTWARE. */ -/* $Id: lbfgs.c 65 2010-01-29 12:19:16Z naoaki $ */ +/* $Id$ */ /* This library is a C port of the FORTRAN implementation of Limited-memory @@ -62,14 +62,19 @@ licence. */ #ifdef HAVE_CONFIG_H -#include "config.h" +#include #endif/*HAVE_CONFIG_H*/ #include #include +#include #include -#include "lbfgs.h" +#include + +#ifdef _MSC_VER +#define inline __inline +#endif/*_MSC_VER*/ #if defined(USE_SSE) && defined(__SSE2__) && LBFGS_FLOAT == 64 /* Use SSE2 optimization for 64bit double precision. */ @@ -89,9 +94,6 @@ licence. #define max2(a, b) ((a) >= (b) ? (a) : (b)) #define max3(a, b, c) max2(max2((a), (b)), (c)); -#define is_aligned(p, bytes) \ - (((uintptr_t)(const void*)(p)) % (bytes) == 0) - struct tag_callback_data { int n; void *instance; @@ -130,7 +132,7 @@ typedef int (*line_search_proc)( callback_data_t *cd, const lbfgs_parameter_t *param ); - + static int line_search_backtracking( int n, lbfgsfloatval_t *x, @@ -224,10 +226,14 @@ static int round_out_variables(int n) lbfgsfloatval_t* lbfgs_malloc(int n) { + if (n < 0) { + return NULL; + } + #if defined(USE_SSE) && (defined(__SSE__) || defined(__SSE2__)) n = round_out_variables(n); #endif/*defined(USE_SSE)*/ - return (lbfgsfloatval_t*)vecalloc(sizeof(lbfgsfloatval_t) * (size_t) n); + return (lbfgsfloatval_t*)vecalloc(sizeof(lbfgsfloatval_t) * (size_t)n); } void lbfgs_free(lbfgsfloatval_t *x) @@ -288,7 +294,7 @@ int lbfgs( if (n % 8 != 0) { return LBFGSERR_INVALID_N_SSE; } - if (!is_aligned(x, 16)) { + if ((uintptr_t)(const void*)x % 16 != 0) { return LBFGSERR_INVALID_X_SSE; } #endif/*defined(USE_SSE)*/ @@ -362,11 +368,11 @@ int lbfgs( } /* Allocate working space. */ - xp = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t)); - g = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t)); - gp = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t)); - d = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t)); - w = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t)); + xp = (lbfgsfloatval_t*)vecalloc((size_t)n * sizeof(lbfgsfloatval_t)); + g = (lbfgsfloatval_t*)vecalloc((size_t)n * sizeof(lbfgsfloatval_t)); + gp = (lbfgsfloatval_t*)vecalloc((size_t)n * sizeof(lbfgsfloatval_t)); + d = (lbfgsfloatval_t*)vecalloc((size_t)n * sizeof(lbfgsfloatval_t)); + w = (lbfgsfloatval_t*)vecalloc((size_t)n * sizeof(lbfgsfloatval_t)); if (xp == NULL || g == NULL || gp == NULL || d == NULL || w == NULL) { ret = LBFGSERR_OUTOFMEMORY; goto lbfgs_exit; @@ -374,7 +380,7 @@ int lbfgs( if (param.orthantwise_c != 0.) { /* Allocate working space for OW-LQN. */ - pg = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t)); + pg = (lbfgsfloatval_t*)vecalloc((size_t)n * sizeof(lbfgsfloatval_t)); if (pg == NULL) { ret = LBFGSERR_OUTOFMEMORY; goto lbfgs_exit; @@ -382,7 +388,7 @@ int lbfgs( } /* Allocate limited memory storage. */ - lm = (iteration_data_t*)vecalloc((size_t) m * sizeof(iteration_data_t)); + lm = (iteration_data_t*)vecalloc((size_t)m * sizeof(iteration_data_t)); if (lm == NULL) { ret = LBFGSERR_OUTOFMEMORY; goto lbfgs_exit; @@ -393,8 +399,8 @@ int lbfgs( it = &lm[i]; it->alpha = 0; it->ys = 0; - it->s = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t)); - it->y = (lbfgsfloatval_t*)vecalloc((size_t) n * sizeof(lbfgsfloatval_t)); + it->s = (lbfgsfloatval_t*)vecalloc((size_t)n * sizeof(lbfgsfloatval_t)); + it->y = (lbfgsfloatval_t*)vecalloc((size_t)n * sizeof(lbfgsfloatval_t)); if (it->s == NULL || it->y == NULL) { ret = LBFGSERR_OUTOFMEMORY; goto lbfgs_exit; @@ -403,7 +409,7 @@ int lbfgs( /* Allocate an array for storing previous values of the objective function. */ if (0 < param.past) { - pf = (lbfgsfloatval_t*)vecalloc((size_t) param.past * sizeof(lbfgsfloatval_t)); + pf = (lbfgsfloatval_t*)vecalloc((size_t)param.past * sizeof(lbfgsfloatval_t)); } /* Evaluate the function value and its gradient. */ @@ -488,8 +494,7 @@ int lbfgs( /* Report the progress. */ if (cd.proc_progress) { - ret = cd.proc_progress(cd.instance, x, g, fx, xnorm, gnorm, step, cd.n, k, ls); - if (ret) { + if ((ret = cd.proc_progress(cd.instance, x, g, fx, xnorm, gnorm, step, cd.n, k, ls))) { goto lbfgs_exit; } } @@ -509,7 +514,7 @@ int lbfgs( /* Test for stopping criterion. The criterion is given by the following formula: - (f(past_x) - f(x)) / f(x) < \delta + |(f(past_x) - f(x))| / f(x) < \delta */ if (pf != NULL) { /* We don't test the stopping criterion while k < past. */ @@ -518,7 +523,7 @@ int lbfgs( rate = (pf[k % param.past] - fx) / fx; /* The stopping criterion. */ - if (rate < param.delta) { + if (fabs(rate) < param.delta) { ret = LBFGS_STOP; break; } @@ -639,6 +644,119 @@ int lbfgs( return ret; } +const char* lbfgs_strerror(int err) +{ + switch(err) { + case LBFGS_SUCCESS: + /* Also handles LBFGS_CONVERGENCE. */ + return "Success: reached convergence (gtol)."; + + case LBFGS_STOP: + return "Success: met stopping criteria (ftol)."; + + case LBFGS_ALREADY_MINIMIZED: + return "The initial variables already minimize the objective function."; + + case LBFGSERR_UNKNOWNERROR: + return "Unknown error."; + + case LBFGSERR_LOGICERROR: + return "Logic error."; + + case LBFGSERR_OUTOFMEMORY: + return "Insufficient memory."; + + case LBFGSERR_CANCELED: + return "The minimization process has been canceled."; + + case LBFGSERR_INVALID_N: + return "Invalid number of variables specified."; + + case LBFGSERR_INVALID_N_SSE: + return "Invalid number of variables (for SSE) specified."; + + case LBFGSERR_INVALID_X_SSE: + return "The array x must be aligned to 16 (for SSE)."; + + case LBFGSERR_INVALID_EPSILON: + return "Invalid parameter lbfgs_parameter_t::epsilon specified."; + + case LBFGSERR_INVALID_TESTPERIOD: + return "Invalid parameter lbfgs_parameter_t::past specified."; + + case LBFGSERR_INVALID_DELTA: + return "Invalid parameter lbfgs_parameter_t::delta specified."; + + case LBFGSERR_INVALID_LINESEARCH: + return "Invalid parameter lbfgs_parameter_t::linesearch specified."; + + case LBFGSERR_INVALID_MINSTEP: + return "Invalid parameter lbfgs_parameter_t::max_step specified."; + + case LBFGSERR_INVALID_MAXSTEP: + return "Invalid parameter lbfgs_parameter_t::max_step specified."; + + case LBFGSERR_INVALID_FTOL: + return "Invalid parameter lbfgs_parameter_t::ftol specified."; + + case LBFGSERR_INVALID_WOLFE: + return "Invalid parameter lbfgs_parameter_t::wolfe specified."; + + case LBFGSERR_INVALID_GTOL: + return "Invalid parameter lbfgs_parameter_t::gtol specified."; + + case LBFGSERR_INVALID_XTOL: + return "Invalid parameter lbfgs_parameter_t::xtol specified."; + + case LBFGSERR_INVALID_MAXLINESEARCH: + return "Invalid parameter lbfgs_parameter_t::max_linesearch specified."; + + case LBFGSERR_INVALID_ORTHANTWISE: + return "Invalid parameter lbfgs_parameter_t::orthantwise_c specified."; + + case LBFGSERR_INVALID_ORTHANTWISE_START: + return "Invalid parameter lbfgs_parameter_t::orthantwise_start specified."; + + case LBFGSERR_INVALID_ORTHANTWISE_END: + return "Invalid parameter lbfgs_parameter_t::orthantwise_end specified."; + + case LBFGSERR_OUTOFINTERVAL: + return "The line-search step went out of the interval of uncertainty."; + + case LBFGSERR_INCORRECT_TMINMAX: + return "A logic error occurred; alternatively, the interval of uncertainty" + " became too small."; + + case LBFGSERR_ROUNDING_ERROR: + return "A rounding error occurred; alternatively, no line-search step" + " satisfies the sufficient decrease and curvature conditions."; + + case LBFGSERR_MINIMUMSTEP: + return "The line-search step became smaller than lbfgs_parameter_t::min_step."; + + case LBFGSERR_MAXIMUMSTEP: + return "The line-search step became larger than lbfgs_parameter_t::max_step."; + + case LBFGSERR_MAXIMUMLINESEARCH: + return "The line-search routine reaches the maximum number of evaluations."; + + case LBFGSERR_MAXIMUMITERATION: + return "The algorithm routine reaches the maximum number of iterations."; + + case LBFGSERR_WIDTHTOOSMALL: + return "Relative width of the interval of uncertainty is at most" + " lbfgs_parameter_t::xtol."; + + case LBFGSERR_INVALIDPARAMETERS: + return "A logic error (negative line-search step) occurred."; + + case LBFGSERR_INCREASEGRADIENT: + return "The current search direction increases the objective function value."; + + default: + return "(unknown)"; + } +} static int line_search_backtracking( @@ -655,6 +773,9 @@ static int line_search_backtracking( const lbfgs_parameter_t *param ) { + (void)gp; + (void)wp; + int count = 0; lbfgsfloatval_t width, dg; lbfgsfloatval_t finit, dginit = 0., dgtest; @@ -822,6 +943,9 @@ static int line_search_morethuente( const lbfgs_parameter_t *param ) { + (void)gp; + (void)wa; + int count = 0; int brackt, stage1, uinfo = 0; lbfgsfloatval_t dg; @@ -1039,9 +1163,9 @@ static int line_search_morethuente( * @param du The value of f'(u). * @param v The value of another point, v. * @param fv The value of f(v). - * @param du The value of f'(v). - * @param xmin The maximum value. + * @param dv The value of f'(v). * @param xmin The minimum value. + * @param xmax The maximum value. */ #define CUBIC_MINIMIZER2(cm, u, fu, du, v, fv, dv, xmin, xmax) \ d = (v) - (u); \ @@ -1059,7 +1183,7 @@ static int line_search_morethuente( r = p / q; \ if (r < 0. && gamma != 0.) { \ (cm) = (v) - r * d; \ - } else if (a < 0) { \ + } else if (d > 0) { \ (cm) = (xmax); \ } else { \ (cm) = (xmin); \ @@ -1074,7 +1198,7 @@ static int line_search_morethuente( * @param v The value of another point, v. * @param fv The value of f(v). */ -#define QUARD_MINIMIZER(qm, u, fu, du, v, fv) \ +#define QUAD_MINIMIZER(qm, u, fu, du, v, fv) \ a = (v) - (u); \ (qm) = (u) + (du) / (((fu) - (fv)) / a + (du)) / 2 * a; @@ -1086,7 +1210,7 @@ static int line_search_morethuente( * @param v The value of another point, v. * @param dv The value of f'(v). */ -#define QUARD_MINIMIZER2(qm, u, du, v, dv) \ +#define QUAD_MINIMIZER2(qm, u, du, v, dv) \ a = (u) - (v); \ (qm) = (v) + (dv) / ((dv) - (du)) * a; @@ -1113,7 +1237,7 @@ static int line_search_morethuente( * @param brackt The pointer to the predicate if the trial value is * bracketed. * @retval int Status value. Zero indicates a normal termination. - * + * * @see * Jorge J. More and David J. Thuente. Line search algorithm with * guaranteed sufficient decrease. ACM Transactions on Mathematical @@ -1139,7 +1263,7 @@ static int update_trial_interval( lbfgsfloatval_t mc; /* minimizer of an interpolated cubic. */ lbfgsfloatval_t mq; /* minimizer of an interpolated quadratic. */ lbfgsfloatval_t newt; /* new trial value. */ - USES_MINIMIZER; /* for CUBIC_MINIMIZER and QUARD_MINIMIZER. */ + USES_MINIMIZER; /* for CUBIC_MINIMIZER and QUAD_MINIMIZER. */ /* Check the input parameters for errors. */ if (*brackt) { @@ -1170,7 +1294,7 @@ static int update_trial_interval( *brackt = 1; bound = 1; CUBIC_MINIMIZER(mc, *x, *fx, *dx, *t, *ft, *dt); - QUARD_MINIMIZER(mq, *x, *fx, *dx, *t, *ft); + QUAD_MINIMIZER(mq, *x, *fx, *dx, *t, *ft); if (fabs(mc - *x) < fabs(mq - *x)) { newt = mc; } else { @@ -1186,7 +1310,7 @@ static int update_trial_interval( *brackt = 1; bound = 0; CUBIC_MINIMIZER(mc, *x, *fx, *dx, *t, *ft, *dt); - QUARD_MINIMIZER2(mq, *x, *dx, *t, *dt); + QUAD_MINIMIZER2(mq, *x, *dx, *t, *dt); if (fabs(mc - *t) > fabs(mq - *t)) { newt = mc; } else { @@ -1206,7 +1330,7 @@ static int update_trial_interval( */ bound = 1; CUBIC_MINIMIZER2(mc, *x, *fx, *dx, *t, *ft, *dt, tmin, tmax); - QUARD_MINIMIZER2(mq, *x, *dx, *t, *dt); + QUAD_MINIMIZER2(mq, *x, *dx, *t, *dt); if (*brackt) { if (fabs(*t - mc) < fabs(*t - mq)) { newt = mc; @@ -1245,7 +1369,7 @@ static int update_trial_interval( x <- x, y <- t. - Case b: if f(t) <= f(x) && f'(t)*f'(x) > 0, x <- t, y <- y. - - Case c: if f(t) <= f(x) && f'(t)*f'(x) < 0, + - Case c: if f(t) <= f(x) && f'(t)*f'(x) < 0, x <- t, y <- x. */ if (*fx < *ft) { diff --git a/src/lbfgs.h b/src/lbfgs.h index f67277e..8390ec3 100644 --- a/src/lbfgs.h +++ b/src/lbfgs.h @@ -24,7 +24,7 @@ * THE SOFTWARE. */ -/* $Id: lbfgs.h 65 2010-01-29 12:19:16Z naoaki $ */ +/* $Id$ */ #ifndef __LBFGS_H__ #define __LBFGS_H__ @@ -59,7 +59,7 @@ typedef double lbfgsfloatval_t; #endif -/** +/** * \addtogroup liblbfgs_api libLBFGS API * @{ * @@ -68,7 +68,7 @@ typedef double lbfgsfloatval_t; /** * Return values of lbfgs(). - * + * * Roughly speaking, a negative value indicates an error. */ enum { @@ -233,7 +233,7 @@ typedef struct { * (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 0. + * The default value is \c 1e-5. */ lbfgsfloatval_t delta; @@ -257,7 +257,7 @@ typedef struct { /** * 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 20. + * per iteration for the line search routine. The default value is \c 40. */ int max_linesearch; @@ -303,7 +303,7 @@ typedef struct { * 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 shuold be + * 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. */ @@ -365,7 +365,7 @@ typedef struct { * 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 @@ -502,19 +502,26 @@ void lbfgs_parameter_init(lbfgs_parameter_t *param); * 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); + /** @} */ __END_DECLS @@ -571,7 +578,7 @@ Among the various ports of L-BFGS, this library provides several features: The library is thread-safe, which is the secondary gain from the callback interface. - Cross platform. The source code can be compiled on Microsoft Visual - Studio 2005, GNU C Compiler (gcc), etc. + Studio 2010, GNU C Compiler (gcc), etc. - Configurable precision: A user can choose single-precision (float) or double-precision (double) accuracy by changing ::LBFGS_FLOAT macro. - SSE/SSE2 optimization: @@ -585,17 +592,28 @@ This library is used by: - Classias: A collection of machine-learning algorithms for classification - mlegp: an R package for maximum likelihood estimates for Gaussian processes - imaging2: the imaging2 class library -- Algorithm::LBFGS - Perl extension for L-BFGS -- YAP-LBFGS (an interface to call libLBFGS from YAP Prolog) @section download Download -- Source code +- Source code +- GitHub repository libLBFGS is distributed under the term of the MIT license. +@section modules Third-party modules +- lbfgs: Limited-memory BFGS Optimization (a wrapper for R) maintained by Antonio Coppola. +- Algorithm::LBFGS - Perl extension for L-BFGS maintained by Lei Sun. +- YAP-LBFGS (an interface to call libLBFGS from YAP Prolog) 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. @@ -716,6 +734,7 @@ 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 diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d5d102e..5ac8e64 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -7,14 +7,49 @@ add_definitions(-DDATADIR=\"${CMAKE_CURRENT_SOURCE_DIR}/../data\") set(TEST_CASES discrete continuous real sampling underflow_handling xmin_too_low) set(TEST_CASES_INTERNAL hzeta kolmogorov gss) +# Borrowed from igraph +function(correct_test_environment TEST_NAME) + if(WIN32 AND BUILD_SHARED_LIBS) + # On Windows the built plfit.dll is not automatically found by the tests. We therefore + # add the dir that contains the built igraph.dll to the path environment variable + # so that igraph.dll is found when running the tests. + set(PLFIT_LIBDIR $) + + # The next line is necessitated by MinGW on Windows. MinGW uses forward slashes in + # PLFIT_LIBDIR, but we need to supply CTest with backslashes because CTest is executed + # in a cmd.exe shell. We therefore explicitly ensure that that path is transformed to a + # native path. + file(TO_NATIVE_PATH "${PLFIT_LIBDIR}" PLFIT_LIBDIR) + + # Semicolons are used as list separators in CMake so we need to escape them in the PATH, + # otherwise the PATH envvar gets split by CMake before it passes the PATH on to CTest. + # We process each path separately to ensure it is a proper path. In particular, we need + # to ensure that a trailing backslash is not incorrectly interpreted as an escape + # character. Presumably, with cmake 3.20, this can be changed to using TO_NATIVE_PATH_LIST. + set(TEST_PATHS) + foreach (PATH $ENV{PATH}) + file(TO_NATIVE_PATH "${PATH}" CORRECT_PATH) + # Remove trailing backslash + string(REGEX REPLACE "\\$" "" CORRECT_PATH ${CORRECT_PATH}) + list(APPEND TEST_PATHS ${CORRECT_PATH}) + endforeach() + + # Join all paths in a single string, separated by an escaped semi-colon. + string(JOIN "\;" CORRECT_PATHS ${TEST_PATHS}) + set_tests_properties(${TEST_NAME} PROPERTIES ENVIRONMENT "PATH=${PLFIT_LIBDIR}\;${CORRECT_PATHS}" ) + endif() +endfunction() + foreach(test ${TEST_CASES}) add_executable(test_${test} test_${test}.c test_common.c) target_link_libraries(test_${test} plfit ${MATH_LIBRARY}) - add_test(test_${test} test_${test}) + add_test(NAME test_${test} COMMAND test_${test}) + correct_test_environment(test_${test}) endforeach(test) foreach(test ${TEST_CASES_INTERNAL}) add_executable(test_${test} test_${test}.c test_common.c ${PROJECT_SOURCE_DIR}/src/${test}.c) target_link_libraries(test_${test} plfit ${MATH_LIBRARY}) - add_test(test_${test} test_${test}) + add_test(NAME test_${test} COMMAND test_${test}) + correct_test_environment(test_${test}) endforeach(test) diff --git a/test/test_common.h b/test/test_common.h index d24831e..e6c4050 100644 --- a/test/test_common.h +++ b/test/test_common.h @@ -17,6 +17,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include #include #include diff --git a/test/test_continuous.c b/test/test_continuous.c index f96d29b..fb8d764 100644 --- a/test/test_continuous.c +++ b/test/test_continuous.c @@ -17,8 +17,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include -#include "plfit.h" +#include + #include "test_common.h" int test_continuous() { diff --git a/test/test_discrete.c b/test/test_discrete.c index 9fac94b..f89f239 100644 --- a/test/test_discrete.c +++ b/test/test_discrete.c @@ -17,8 +17,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include -#include "plfit.h" +#include + #include "test_common.h" int test_discrete() { diff --git a/test/test_gss.c b/test/test_gss.c index 9cb51ca..8d4fc2b 100644 --- a/test/test_gss.c +++ b/test/test_gss.c @@ -17,8 +17,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include #include "gss.h" + #include "test_common.h" double x_squared(void *instance, double x) { diff --git a/test/test_hzeta.c b/test/test_hzeta.c index 0841716..5b9be25 100644 --- a/test/test_hzeta.c +++ b/test/test_hzeta.c @@ -17,8 +17,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include #include "hzeta.h" + #include "test_common.h" int test_hsl_sf_lnhzeta() { diff --git a/test/test_kolmogorov.c b/test/test_kolmogorov.c index eb30f5f..7f5b8e7 100644 --- a/test/test_kolmogorov.c +++ b/test/test_kolmogorov.c @@ -17,8 +17,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include #include "kolmogorov.h" + #include "test_common.h" int test_kolmogorov() { diff --git a/test/test_real.c b/test/test_real.c index 36c7dea..f4e0f97 100644 --- a/test/test_real.c +++ b/test/test_real.c @@ -17,8 +17,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include -#include "plfit.h" +#include + #include "test_common.h" double data[41000]; diff --git a/test/test_sampling.c b/test/test_sampling.c index 0220cb7..e2f43ea 100644 --- a/test/test_sampling.c +++ b/test/test_sampling.c @@ -17,8 +17,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include -#include "plfit_sampling.h" +#include + #include "test_common.h" #define NUM_SAMPLES 1000000 diff --git a/test/test_underflow_handling.c b/test/test_underflow_handling.c index 6ef91be..f653474 100644 --- a/test/test_underflow_handling.c +++ b/test/test_underflow_handling.c @@ -17,8 +17,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include -#include "plfit.h" +#include + #include "test_common.h" int test_underflow_handling() { diff --git a/test/test_xmin_too_low.c b/test/test_xmin_too_low.c index bf4d5dc..2e8c8f5 100644 --- a/test/test_xmin_too_low.c +++ b/test/test_xmin_too_low.c @@ -17,8 +17,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include -#include "plfit.h" +#include + #include "test_common.h" int test_xmin_too_low() {