|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H |
|
#include <config.h> |
|
#endif |
|
|
|
#include <stdint.h> |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <math.h> |
|
|
|
#include "../include/lbfgs.h" |
|
|
|
#ifdef _MSC_VER |
|
#define inline __inline |
|
#endif |
|
|
|
#if defined(USE_SSE) && defined(__SSE2__) && LBFGS_FLOAT == 64 |
|
|
|
#include "../include/arithmetic_sse_double.h" |
|
|
|
#elif defined(USE_SSE) && defined(__SSE__) && LBFGS_FLOAT == 32 |
|
|
|
#include "../include/arithmetic_sse_float.h" |
|
|
|
#else |
|
|
|
#include "../include/arithmetic_ansi.h" |
|
|
|
#endif |
|
|
|
#define min2(a, b) ((a) <= (b) ? (a) : (b)) |
|
#define max2(a, b) ((a) >= (b) ? (a) : (b)) |
|
#define max3(a, b, c) max2(max2((a), (b)), (c)); |
|
|
|
struct tag_callback_data { |
|
int n; |
|
void *instance; |
|
lbfgs_evaluate_t proc_evaluate; |
|
lbfgs_progress_t proc_progress; |
|
}; |
|
typedef struct tag_callback_data callback_data_t; |
|
|
|
struct tag_iteration_data { |
|
lbfgsfloatval_t alpha; |
|
lbfgsfloatval_t *s; |
|
lbfgsfloatval_t *y; |
|
lbfgsfloatval_t ys; |
|
}; |
|
typedef struct tag_iteration_data iteration_data_t; |
|
|
|
static const lbfgs_parameter_t _defparam = { |
|
6, 1e-5, 0, 1e-5, |
|
0, LBFGS_LINESEARCH_DEFAULT, 40, |
|
1e-20, 1e20, 1e-4, 0.9, 0.9, 1.0e-16, |
|
0.0, 0, -1, |
|
}; |
|
|
|
|
|
|
|
typedef int (*line_search_proc)( |
|
int n, |
|
lbfgsfloatval_t *x, |
|
lbfgsfloatval_t *f, |
|
lbfgsfloatval_t *g, |
|
lbfgsfloatval_t *s, |
|
lbfgsfloatval_t *stp, |
|
const lbfgsfloatval_t* xp, |
|
const lbfgsfloatval_t* gp, |
|
lbfgsfloatval_t *wa, |
|
callback_data_t *cd, |
|
const lbfgs_parameter_t *param |
|
); |
|
|
|
static int line_search_backtracking( |
|
int n, |
|
lbfgsfloatval_t *x, |
|
lbfgsfloatval_t *f, |
|
lbfgsfloatval_t *g, |
|
lbfgsfloatval_t *s, |
|
lbfgsfloatval_t *stp, |
|
const lbfgsfloatval_t* xp, |
|
const lbfgsfloatval_t* gp, |
|
lbfgsfloatval_t *wa, |
|
callback_data_t *cd, |
|
const lbfgs_parameter_t *param |
|
); |
|
|
|
static int line_search_backtracking_owlqn( |
|
int n, |
|
lbfgsfloatval_t *x, |
|
lbfgsfloatval_t *f, |
|
lbfgsfloatval_t *g, |
|
lbfgsfloatval_t *s, |
|
lbfgsfloatval_t *stp, |
|
const lbfgsfloatval_t* xp, |
|
const lbfgsfloatval_t* gp, |
|
lbfgsfloatval_t *wp, |
|
callback_data_t *cd, |
|
const lbfgs_parameter_t *param |
|
); |
|
|
|
static int line_search_morethuente( |
|
int n, |
|
lbfgsfloatval_t *x, |
|
lbfgsfloatval_t *f, |
|
lbfgsfloatval_t *g, |
|
lbfgsfloatval_t *s, |
|
lbfgsfloatval_t *stp, |
|
const lbfgsfloatval_t* xp, |
|
const lbfgsfloatval_t* gp, |
|
lbfgsfloatval_t *wa, |
|
callback_data_t *cd, |
|
const lbfgs_parameter_t *param |
|
); |
|
|
|
static int update_trial_interval( |
|
lbfgsfloatval_t *x, |
|
lbfgsfloatval_t *fx, |
|
lbfgsfloatval_t *dx, |
|
lbfgsfloatval_t *y, |
|
lbfgsfloatval_t *fy, |
|
lbfgsfloatval_t *dy, |
|
lbfgsfloatval_t *t, |
|
lbfgsfloatval_t *ft, |
|
lbfgsfloatval_t *dt, |
|
const lbfgsfloatval_t tmin, |
|
const lbfgsfloatval_t tmax, |
|
int *brackt |
|
); |
|
|
|
static lbfgsfloatval_t owlqn_x1norm( |
|
const lbfgsfloatval_t* x, |
|
const int start, |
|
const int n |
|
); |
|
|
|
static void owlqn_pseudo_gradient( |
|
lbfgsfloatval_t* pg, |
|
const lbfgsfloatval_t* x, |
|
const lbfgsfloatval_t* g, |
|
const int n, |
|
const lbfgsfloatval_t c, |
|
const int start, |
|
const int end |
|
); |
|
|
|
static void owlqn_project( |
|
lbfgsfloatval_t* d, |
|
const lbfgsfloatval_t* sign, |
|
const int start, |
|
const int end |
|
); |
|
|
|
|
|
#if defined(USE_SSE) && (defined(__SSE__) || defined(__SSE2__)) |
|
static int round_out_variables(int n) |
|
{ |
|
n += 7; |
|
n /= 8; |
|
n *= 8; |
|
return n; |
|
} |
|
#endif |
|
|
|
lbfgsfloatval_t* lbfgs_malloc(int n) |
|
{ |
|
#if defined(USE_SSE) && (defined(__SSE__) || defined(__SSE2__)) |
|
n = round_out_variables(n); |
|
#endif |
|
return (lbfgsfloatval_t*)vecalloc(sizeof(lbfgsfloatval_t) * n); |
|
} |
|
|
|
void lbfgs_free(lbfgsfloatval_t *x) |
|
{ |
|
vecfree(x); |
|
} |
|
|
|
void lbfgs_parameter_init(lbfgs_parameter_t *param) |
|
{ |
|
memcpy(param, &_defparam, sizeof(*param)); |
|
} |
|
|
|
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 |
|
) |
|
{ |
|
int ret; |
|
int i, j, k, ls, end, bound; |
|
lbfgsfloatval_t step; |
|
|
|
|
|
lbfgs_parameter_t param = (_param != NULL) ? (*_param) : _defparam; |
|
const int m = param.m; |
|
|
|
lbfgsfloatval_t *xp = NULL; |
|
lbfgsfloatval_t *g = NULL, *gp = NULL, *pg = NULL; |
|
lbfgsfloatval_t *d = NULL, *w = NULL, *pf = NULL; |
|
iteration_data_t *lm = NULL, *it = NULL; |
|
lbfgsfloatval_t ys, yy; |
|
lbfgsfloatval_t xnorm, gnorm, beta; |
|
lbfgsfloatval_t fx = 0.; |
|
lbfgsfloatval_t rate = 0.; |
|
line_search_proc linesearch = line_search_morethuente; |
|
|
|
|
|
callback_data_t cd; |
|
cd.n = n; |
|
cd.instance = instance; |
|
cd.proc_evaluate = proc_evaluate; |
|
cd.proc_progress = proc_progress; |
|
|
|
#if defined(USE_SSE) && (defined(__SSE__) || defined(__SSE2__)) |
|
|
|
n = round_out_variables(n); |
|
#endif |
|
|
|
|
|
if (n <= 0) { |
|
return LBFGSERR_INVALID_N; |
|
} |
|
#if defined(USE_SSE) && (defined(__SSE__) || defined(__SSE2__)) |
|
if (n % 8 != 0) { |
|
return LBFGSERR_INVALID_N_SSE; |
|
} |
|
if ((uintptr_t)(const void*)x % 16 != 0) { |
|
return LBFGSERR_INVALID_X_SSE; |
|
} |
|
#endif |
|
if (param.epsilon < 0.) { |
|
return LBFGSERR_INVALID_EPSILON; |
|
} |
|
if (param.past < 0) { |
|
return LBFGSERR_INVALID_TESTPERIOD; |
|
} |
|
if (param.delta < 0.) { |
|
return LBFGSERR_INVALID_DELTA; |
|
} |
|
if (param.min_step < 0.) { |
|
return LBFGSERR_INVALID_MINSTEP; |
|
} |
|
if (param.max_step < param.min_step) { |
|
return LBFGSERR_INVALID_MAXSTEP; |
|
} |
|
if (param.ftol < 0.) { |
|
return LBFGSERR_INVALID_FTOL; |
|
} |
|
if (param.linesearch == LBFGS_LINESEARCH_BACKTRACKING_WOLFE || |
|
param.linesearch == LBFGS_LINESEARCH_BACKTRACKING_STRONG_WOLFE) { |
|
if (param.wolfe <= param.ftol || 1. <= param.wolfe) { |
|
return LBFGSERR_INVALID_WOLFE; |
|
} |
|
} |
|
if (param.gtol < 0.) { |
|
return LBFGSERR_INVALID_GTOL; |
|
} |
|
if (param.xtol < 0.) { |
|
return LBFGSERR_INVALID_XTOL; |
|
} |
|
if (param.max_linesearch <= 0) { |
|
return LBFGSERR_INVALID_MAXLINESEARCH; |
|
} |
|
if (param.orthantwise_c < 0.) { |
|
return LBFGSERR_INVALID_ORTHANTWISE; |
|
} |
|
if (param.orthantwise_start < 0 || n < param.orthantwise_start) { |
|
return LBFGSERR_INVALID_ORTHANTWISE_START; |
|
} |
|
if (param.orthantwise_end < 0) { |
|
param.orthantwise_end = n; |
|
} |
|
if (n < param.orthantwise_end) { |
|
return LBFGSERR_INVALID_ORTHANTWISE_END; |
|
} |
|
if (param.orthantwise_c != 0.) { |
|
switch (param.linesearch) { |
|
case LBFGS_LINESEARCH_BACKTRACKING: |
|
linesearch = line_search_backtracking_owlqn; |
|
break; |
|
default: |
|
|
|
return LBFGSERR_INVALID_LINESEARCH; |
|
} |
|
} else { |
|
switch (param.linesearch) { |
|
case LBFGS_LINESEARCH_MORETHUENTE: |
|
linesearch = line_search_morethuente; |
|
break; |
|
case LBFGS_LINESEARCH_BACKTRACKING_ARMIJO: |
|
case LBFGS_LINESEARCH_BACKTRACKING_WOLFE: |
|
case LBFGS_LINESEARCH_BACKTRACKING_STRONG_WOLFE: |
|
linesearch = line_search_backtracking; |
|
break; |
|
default: |
|
return LBFGSERR_INVALID_LINESEARCH; |
|
} |
|
} |
|
|
|
|
|
xp = (lbfgsfloatval_t*)vecalloc(n * sizeof(lbfgsfloatval_t)); |
|
g = (lbfgsfloatval_t*)vecalloc(n * sizeof(lbfgsfloatval_t)); |
|
gp = (lbfgsfloatval_t*)vecalloc(n * sizeof(lbfgsfloatval_t)); |
|
d = (lbfgsfloatval_t*)vecalloc(n * sizeof(lbfgsfloatval_t)); |
|
w = (lbfgsfloatval_t*)vecalloc(n * sizeof(lbfgsfloatval_t)); |
|
if (xp == NULL || g == NULL || gp == NULL || d == NULL || w == NULL) { |
|
ret = LBFGSERR_OUTOFMEMORY; |
|
goto lbfgs_exit; |
|
} |
|
|
|
if (param.orthantwise_c != 0.) { |
|
|
|
pg = (lbfgsfloatval_t*)vecalloc(n * sizeof(lbfgsfloatval_t)); |
|
if (pg == NULL) { |
|
ret = LBFGSERR_OUTOFMEMORY; |
|
goto lbfgs_exit; |
|
} |
|
} |
|
|
|
|
|
lm = (iteration_data_t*)vecalloc(m * sizeof(iteration_data_t)); |
|
if (lm == NULL) { |
|
ret = LBFGSERR_OUTOFMEMORY; |
|
goto lbfgs_exit; |
|
} |
|
|
|
|
|
for (i = 0;i < m;++i) { |
|
it = &lm[i]; |
|
it->alpha = 0; |
|
it->ys = 0; |
|
it->s = (lbfgsfloatval_t*)vecalloc(n * sizeof(lbfgsfloatval_t)); |
|
it->y = (lbfgsfloatval_t*)vecalloc(n * sizeof(lbfgsfloatval_t)); |
|
if (it->s == NULL || it->y == NULL) { |
|
ret = LBFGSERR_OUTOFMEMORY; |
|
goto lbfgs_exit; |
|
} |
|
} |
|
|
|
|
|
if (0 < param.past) { |
|
pf = (lbfgsfloatval_t*)vecalloc(param.past * sizeof(lbfgsfloatval_t)); |
|
} |
|
|
|
|
|
fx = cd.proc_evaluate(cd.instance, x, g, cd.n, 0); |
|
if (0. != param.orthantwise_c) { |
|
|
|
xnorm = owlqn_x1norm(x, param.orthantwise_start, param.orthantwise_end); |
|
fx += xnorm * param.orthantwise_c; |
|
owlqn_pseudo_gradient( |
|
pg, x, g, n, |
|
param.orthantwise_c, param.orthantwise_start, param.orthantwise_end |
|
); |
|
} |
|
|
|
|
|
if (pf != NULL) { |
|
pf[0] = fx; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (param.orthantwise_c == 0.) { |
|
vecncpy(d, g, n); |
|
} else { |
|
vecncpy(d, pg, n); |
|
} |
|
|
|
|
|
|
|
|
|
vec2norm(&xnorm, x, n); |
|
if (param.orthantwise_c == 0.) { |
|
vec2norm(&gnorm, g, n); |
|
} else { |
|
vec2norm(&gnorm, pg, n); |
|
} |
|
if (xnorm < 1.0) xnorm = 1.0; |
|
if (gnorm / xnorm <= param.epsilon) { |
|
ret = LBFGS_ALREADY_MINIMIZED; |
|
goto lbfgs_exit; |
|
} |
|
|
|
|
|
|
|
|
|
vec2norminv(&step, d, n); |
|
|
|
k = 1; |
|
end = 0; |
|
for (;;) { |
|
|
|
veccpy(xp, x, n); |
|
veccpy(gp, g, n); |
|
|
|
|
|
if (param.orthantwise_c == 0.) { |
|
ls = linesearch(n, x, &fx, g, d, &step, xp, gp, w, &cd, ¶m); |
|
} else { |
|
ls = linesearch(n, x, &fx, g, d, &step, xp, pg, w, &cd, ¶m); |
|
owlqn_pseudo_gradient( |
|
pg, x, g, n, |
|
param.orthantwise_c, param.orthantwise_start, param.orthantwise_end |
|
); |
|
} |
|
if (ls < 0) { |
|
|
|
veccpy(x, xp, n); |
|
veccpy(g, gp, n); |
|
ret = ls; |
|
goto lbfgs_exit; |
|
} |
|
|
|
|
|
vec2norm(&xnorm, x, n); |
|
if (param.orthantwise_c == 0.) { |
|
vec2norm(&gnorm, g, n); |
|
} else { |
|
vec2norm(&gnorm, pg, n); |
|
} |
|
|
|
|
|
if (cd.proc_progress) { |
|
if ((ret = cd.proc_progress(cd.instance, x, g, fx, xnorm, gnorm, step, cd.n, k, ls))) { |
|
goto lbfgs_exit; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
if (xnorm < 1.0) xnorm = 1.0; |
|
if (gnorm / xnorm <= param.epsilon) { |
|
|
|
ret = LBFGS_SUCCESS; |
|
break; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
if (pf != NULL) { |
|
|
|
if (param.past <= k) { |
|
|
|
rate = (pf[k % param.past] - fx) / fx; |
|
|
|
|
|
if (rate < param.delta) { |
|
ret = LBFGS_STOP; |
|
break; |
|
} |
|
} |
|
|
|
|
|
pf[k % param.past] = fx; |
|
} |
|
|
|
if (param.max_iterations != 0 && param.max_iterations < k+1) { |
|
|
|
ret = LBFGSERR_MAXIMUMITERATION; |
|
break; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
it = &lm[end]; |
|
vecdiff(it->s, x, xp, n); |
|
vecdiff(it->y, g, gp, n); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vecdot(&ys, it->y, it->s, n); |
|
vecdot(&yy, it->y, it->y, n); |
|
it->ys = ys; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bound = (m <= k) ? m : k; |
|
++k; |
|
end = (end + 1) % m; |
|
|
|
|
|
if (param.orthantwise_c == 0.) { |
|
|
|
vecncpy(d, g, n); |
|
} else { |
|
vecncpy(d, pg, n); |
|
} |
|
|
|
j = end; |
|
for (i = 0;i < bound;++i) { |
|
j = (j + m - 1) % m; |
|
it = &lm[j]; |
|
|
|
vecdot(&it->alpha, it->s, d, n); |
|
it->alpha /= it->ys; |
|
|
|
vecadd(d, it->y, -it->alpha, n); |
|
} |
|
|
|
vecscale(d, ys / yy, n); |
|
|
|
for (i = 0;i < bound;++i) { |
|
it = &lm[j]; |
|
|
|
vecdot(&beta, it->y, d, n); |
|
beta /= it->ys; |
|
|
|
vecadd(d, it->s, it->alpha - beta, n); |
|
j = (j + 1) % m; |
|
} |
|
|
|
|
|
|
|
|
|
if (param.orthantwise_c != 0.) { |
|
for (i = param.orthantwise_start;i < param.orthantwise_end;++i) { |
|
if (d[i] * pg[i] >= 0) { |
|
d[i] = 0; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
step = 1.0; |
|
} |
|
|
|
lbfgs_exit: |
|
|
|
if (ptr_fx != NULL) { |
|
*ptr_fx = fx; |
|
} |
|
|
|
vecfree(pf); |
|
|
|
|
|
if (lm != NULL) { |
|
for (i = 0;i < m;++i) { |
|
vecfree(lm[i].s); |
|
vecfree(lm[i].y); |
|
} |
|
vecfree(lm); |
|
} |
|
vecfree(pg); |
|
vecfree(w); |
|
vecfree(d); |
|
vecfree(gp); |
|
vecfree(g); |
|
vecfree(xp); |
|
|
|
return ret; |
|
} |
|
|
|
|
|
|
|
static int line_search_backtracking( |
|
int n, |
|
lbfgsfloatval_t *x, |
|
lbfgsfloatval_t *f, |
|
lbfgsfloatval_t *g, |
|
lbfgsfloatval_t *s, |
|
lbfgsfloatval_t *stp, |
|
const lbfgsfloatval_t* xp, |
|
const lbfgsfloatval_t* gp, |
|
lbfgsfloatval_t *wp, |
|
callback_data_t *cd, |
|
const lbfgs_parameter_t *param |
|
) |
|
{ |
|
int count = 0; |
|
lbfgsfloatval_t width, dg; |
|
lbfgsfloatval_t finit, dginit = 0., dgtest; |
|
const lbfgsfloatval_t dec = 0.5, inc = 2.1; |
|
|
|
|
|
if (*stp <= 0.) { |
|
return LBFGSERR_INVALIDPARAMETERS; |
|
} |
|
|
|
|
|
vecdot(&dginit, g, s, n); |
|
|
|
|
|
if (0 < dginit) { |
|
return LBFGSERR_INCREASEGRADIENT; |
|
} |
|
|
|
|
|
finit = *f; |
|
dgtest = param->ftol * dginit; |
|
|
|
for (;;) { |
|
veccpy(x, xp, n); |
|
vecadd(x, s, *stp, n); |
|
|
|
|
|
*f = cd->proc_evaluate(cd->instance, x, g, cd->n, *stp); |
|
|
|
++count; |
|
|
|
if (*f > finit + *stp * dgtest) { |
|
width = dec; |
|
} else { |
|
|
|
if (param->linesearch == LBFGS_LINESEARCH_BACKTRACKING_ARMIJO) { |
|
|
|
return count; |
|
} |
|
|
|
|
|
vecdot(&dg, g, s, n); |
|
if (dg < param->wolfe * dginit) { |
|
width = inc; |
|
} else { |
|
if(param->linesearch == LBFGS_LINESEARCH_BACKTRACKING_WOLFE) { |
|
|
|
return count; |
|
} |
|
|
|
|
|
if(dg > -param->wolfe * dginit) { |
|
width = dec; |
|
} else { |
|
|
|
return count; |
|
} |
|
} |
|
} |
|
|
|
if (*stp < param->min_step) { |
|
|
|
return LBFGSERR_MINIMUMSTEP; |
|
} |
|
if (*stp > param->max_step) { |
|
|
|
return LBFGSERR_MAXIMUMSTEP; |
|
} |
|
if (param->max_linesearch <= count) { |
|
|
|
return LBFGSERR_MAXIMUMLINESEARCH; |
|
} |
|
|
|
(*stp) *= width; |
|
} |
|
} |
|
|
|
|
|
|
|
static int line_search_backtracking_owlqn( |
|
int n, |
|
lbfgsfloatval_t *x, |
|
lbfgsfloatval_t *f, |
|
lbfgsfloatval_t *g, |
|
lbfgsfloatval_t *s, |
|
lbfgsfloatval_t *stp, |
|
const lbfgsfloatval_t* xp, |
|
const lbfgsfloatval_t* gp, |
|
lbfgsfloatval_t *wp, |
|
callback_data_t *cd, |
|
const lbfgs_parameter_t *param |
|
) |
|
{ |
|
int i, count = 0; |
|
lbfgsfloatval_t width = 0.5, norm = 0.; |
|
lbfgsfloatval_t finit = *f, dgtest; |
|
|
|
|
|
if (*stp <= 0.) { |
|
return LBFGSERR_INVALIDPARAMETERS; |
|
} |
|
|
|
|
|
for (i = 0;i < n;++i) { |
|
wp[i] = (xp[i] == 0.) ? -gp[i] : xp[i]; |
|
} |
|
|
|
for (;;) { |
|
|
|
veccpy(x, xp, n); |
|
vecadd(x, s, *stp, n); |
|
|
|
|
|
owlqn_project(x, wp, param->orthantwise_start, param->orthantwise_end); |
|
|
|
|
|
*f = cd->proc_evaluate(cd->instance, x, g, cd->n, *stp); |
|
|
|
|
|
norm = owlqn_x1norm(x, param->orthantwise_start, param->orthantwise_end); |
|
*f += norm * param->orthantwise_c; |
|
|
|
++count; |
|
|
|
dgtest = 0.; |
|
for (i = 0;i < n;++i) { |
|
dgtest += (x[i] - xp[i]) * gp[i]; |
|
} |
|
|
|
if (*f <= finit + param->ftol * dgtest) { |
|
|
|
return count; |
|
} |
|
|
|
if (*stp < param->min_step) { |
|
|
|
return LBFGSERR_MINIMUMSTEP; |
|
} |
|
if (*stp > param->max_step) { |
|
|
|
return LBFGSERR_MAXIMUMSTEP; |
|
} |
|
if (param->max_linesearch <= count) { |
|
|
|
return LBFGSERR_MAXIMUMLINESEARCH; |
|
} |
|
|
|
(*stp) *= width; |
|
} |
|
} |
|
|
|
|
|
|
|
static int line_search_morethuente( |
|
int n, |
|
lbfgsfloatval_t *x, |
|
lbfgsfloatval_t *f, |
|
lbfgsfloatval_t *g, |
|
lbfgsfloatval_t *s, |
|
lbfgsfloatval_t *stp, |
|
const lbfgsfloatval_t* xp, |
|
const lbfgsfloatval_t* gp, |
|
lbfgsfloatval_t *wa, |
|
callback_data_t *cd, |
|
const lbfgs_parameter_t *param |
|
) |
|
{ |
|
int count = 0; |
|
int brackt, stage1, uinfo = 0; |
|
lbfgsfloatval_t dg; |
|
lbfgsfloatval_t stx, fx, dgx; |
|
lbfgsfloatval_t sty, fy, dgy; |
|
lbfgsfloatval_t fxm, dgxm, fym, dgym, fm, dgm; |
|
lbfgsfloatval_t finit, ftest1, dginit, dgtest; |
|
lbfgsfloatval_t width, prev_width; |
|
lbfgsfloatval_t stmin, stmax; |
|
|
|
|
|
if (*stp <= 0.) { |
|
return LBFGSERR_INVALIDPARAMETERS; |
|
} |
|
|
|
|
|
vecdot(&dginit, g, s, n); |
|
|
|
|
|
if (0 < dginit) { |
|
return LBFGSERR_INCREASEGRADIENT; |
|
} |
|
|
|
|
|
brackt = 0; |
|
stage1 = 1; |
|
finit = *f; |
|
dgtest = param->ftol * dginit; |
|
width = param->max_step - param->min_step; |
|
prev_width = 2.0 * width; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
stx = sty = 0.; |
|
fx = fy = finit; |
|
dgx = dgy = dginit; |
|
|
|
for (;;) { |
|
|
|
|
|
|
|
|
|
if (brackt) { |
|
stmin = min2(stx, sty); |
|
stmax = max2(stx, sty); |
|
} else { |
|
stmin = stx; |
|
stmax = *stp + 4.0 * (*stp - stx); |
|
} |
|
|
|
|
|
if (*stp < param->min_step) *stp = param->min_step; |
|
if (param->max_step < *stp) *stp = param->max_step; |
|
|
|
|
|
|
|
|
|
|
|
if ((brackt && ((*stp <= stmin || stmax <= *stp) || param->max_linesearch <= count + 1 || uinfo != 0)) || (brackt && (stmax - stmin <= param->xtol * stmax))) { |
|
*stp = stx; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
veccpy(x, xp, n); |
|
vecadd(x, s, *stp, n); |
|
|
|
|
|
*f = cd->proc_evaluate(cd->instance, x, g, cd->n, *stp); |
|
vecdot(&dg, g, s, n); |
|
|
|
ftest1 = finit + *stp * dgtest; |
|
++count; |
|
|
|
|
|
if (brackt && ((*stp <= stmin || stmax <= *stp) || uinfo != 0)) { |
|
|
|
return LBFGSERR_ROUNDING_ERROR; |
|
} |
|
if (*stp == param->max_step && *f <= ftest1 && dg <= dgtest) { |
|
|
|
return LBFGSERR_MAXIMUMSTEP; |
|
} |
|
if (*stp == param->min_step && (ftest1 < *f || dgtest <= dg)) { |
|
|
|
return LBFGSERR_MINIMUMSTEP; |
|
} |
|
if (brackt && (stmax - stmin) <= param->xtol * stmax) { |
|
|
|
return LBFGSERR_WIDTHTOOSMALL; |
|
} |
|
if (param->max_linesearch <= count) { |
|
|
|
return LBFGSERR_MAXIMUMLINESEARCH; |
|
} |
|
if (*f <= ftest1 && fabs(dg) <= param->gtol * (-dginit)) { |
|
|
|
return count; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (stage1 && *f <= ftest1 && min2(param->ftol, param->gtol) * dginit <= dg) { |
|
stage1 = 0; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (stage1 && ftest1 < *f && *f <= fx) { |
|
|
|
fm = *f - *stp * dgtest; |
|
fxm = fx - stx * dgtest; |
|
fym = fy - sty * dgtest; |
|
dgm = dg - dgtest; |
|
dgxm = dgx - dgtest; |
|
dgym = dgy - dgtest; |
|
|
|
|
|
|
|
|
|
|
|
uinfo = update_trial_interval( |
|
&stx, &fxm, &dgxm, |
|
&sty, &fym, &dgym, |
|
stp, &fm, &dgm, |
|
stmin, stmax, &brackt |
|
); |
|
|
|
|
|
fx = fxm + stx * dgtest; |
|
fy = fym + sty * dgtest; |
|
dgx = dgxm + dgtest; |
|
dgy = dgym + dgtest; |
|
} else { |
|
|
|
|
|
|
|
|
|
uinfo = update_trial_interval( |
|
&stx, &fx, &dgx, |
|
&sty, &fy, &dgy, |
|
stp, f, &dg, |
|
stmin, stmax, &brackt |
|
); |
|
} |
|
|
|
|
|
|
|
|
|
if (brackt) { |
|
if (0.66 * prev_width <= fabs(sty - stx)) { |
|
*stp = stx + 0.5 * (sty - stx); |
|
} |
|
prev_width = width; |
|
width = fabs(sty - stx); |
|
} |
|
} |
|
|
|
return LBFGSERR_LOGICERROR; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
#define USES_MINIMIZER \ |
|
lbfgsfloatval_t a, d, gamma, theta, p, q, r, s; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define CUBIC_MINIMIZER(cm, u, fu, du, v, fv, dv) \ |
|
d = (v) - (u); \ |
|
theta = ((fu) - (fv)) * 3 / d + (du) + (dv); \ |
|
p = fabs(theta); \ |
|
q = fabs(du); \ |
|
r = fabs(dv); \ |
|
s = max3(p, q, r); \ |
|
\ |
|
a = theta / s; \ |
|
gamma = s * sqrt(a * a - ((du) / s) * ((dv) / s)); \ |
|
if ((v) < (u)) gamma = -gamma; \ |
|
p = gamma - (du) + theta; \ |
|
q = gamma - (du) + gamma + (dv); \ |
|
r = p / q; \ |
|
(cm) = (u) + r * d; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define CUBIC_MINIMIZER2(cm, u, fu, du, v, fv, dv, xmin, xmax) \ |
|
d = (v) - (u); \ |
|
theta = ((fu) - (fv)) * 3 / d + (du) + (dv); \ |
|
p = fabs(theta); \ |
|
q = fabs(du); \ |
|
r = fabs(dv); \ |
|
s = max3(p, q, r); \ |
|
\ |
|
a = theta / s; \ |
|
gamma = s * sqrt(max2(0, a * a - ((du) / s) * ((dv) / s))); \ |
|
if ((u) < (v)) gamma = -gamma; \ |
|
p = gamma - (dv) + theta; \ |
|
q = gamma - (dv) + gamma + (du); \ |
|
r = p / q; \ |
|
if (r < 0. && gamma != 0.) { \ |
|
(cm) = (v) - r * d; \ |
|
} else if (a < 0) { \ |
|
(cm) = (xmax); \ |
|
} else { \ |
|
(cm) = (xmin); \ |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define QUARD_MINIMIZER(qm, u, fu, du, v, fv) \ |
|
a = (v) - (u); \ |
|
(qm) = (u) + (du) / (((fu) - (fv)) / a + (du)) / 2 * a; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define QUARD_MINIMIZER2(qm, u, du, v, dv) \ |
|
a = (u) - (v); \ |
|
(qm) = (v) + (dv) / ((dv) - (du)) * a; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int update_trial_interval( |
|
lbfgsfloatval_t *x, |
|
lbfgsfloatval_t *fx, |
|
lbfgsfloatval_t *dx, |
|
lbfgsfloatval_t *y, |
|
lbfgsfloatval_t *fy, |
|
lbfgsfloatval_t *dy, |
|
lbfgsfloatval_t *t, |
|
lbfgsfloatval_t *ft, |
|
lbfgsfloatval_t *dt, |
|
const lbfgsfloatval_t tmin, |
|
const lbfgsfloatval_t tmax, |
|
int *brackt |
|
) |
|
{ |
|
int bound; |
|
int dsign = fsigndiff(dt, dx); |
|
lbfgsfloatval_t mc; |
|
lbfgsfloatval_t mq; |
|
lbfgsfloatval_t newt; |
|
USES_MINIMIZER; |
|
|
|
|
|
if (*brackt) { |
|
if (*t <= min2(*x, *y) || max2(*x, *y) <= *t) { |
|
|
|
return LBFGSERR_OUTOFINTERVAL; |
|
} |
|
if (0. <= *dx * (*t - *x)) { |
|
|
|
return LBFGSERR_INCREASEGRADIENT; |
|
} |
|
if (tmax < tmin) { |
|
|
|
return LBFGSERR_INCORRECT_TMINMAX; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
if (*fx < *ft) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
*brackt = 1; |
|
bound = 1; |
|
CUBIC_MINIMIZER(mc, *x, *fx, *dx, *t, *ft, *dt); |
|
QUARD_MINIMIZER(mq, *x, *fx, *dx, *t, *ft); |
|
if (fabs(mc - *x) < fabs(mq - *x)) { |
|
newt = mc; |
|
} else { |
|
newt = mc + 0.5 * (mq - mc); |
|
} |
|
} else if (dsign) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
*brackt = 1; |
|
bound = 0; |
|
CUBIC_MINIMIZER(mc, *x, *fx, *dx, *t, *ft, *dt); |
|
QUARD_MINIMIZER2(mq, *x, *dx, *t, *dt); |
|
if (fabs(mc - *t) > fabs(mq - *t)) { |
|
newt = mc; |
|
} else { |
|
newt = mq; |
|
} |
|
} else if (fabs(*dt) < fabs(*dx)) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bound = 1; |
|
CUBIC_MINIMIZER2(mc, *x, *fx, *dx, *t, *ft, *dt, tmin, tmax); |
|
QUARD_MINIMIZER2(mq, *x, *dx, *t, *dt); |
|
if (*brackt) { |
|
if (fabs(*t - mc) < fabs(*t - mq)) { |
|
newt = mc; |
|
} else { |
|
newt = mq; |
|
} |
|
} else { |
|
if (fabs(*t - mc) > fabs(*t - mq)) { |
|
newt = mc; |
|
} else { |
|
newt = mq; |
|
} |
|
} |
|
} else { |
|
|
|
|
|
|
|
|
|
|
|
|
|
bound = 0; |
|
if (*brackt) { |
|
CUBIC_MINIMIZER(newt, *t, *ft, *dt, *y, *fy, *dy); |
|
} else if (*x < *t) { |
|
newt = tmax; |
|
} else { |
|
newt = tmin; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (*fx < *ft) { |
|
|
|
*y = *t; |
|
*fy = *ft; |
|
*dy = *dt; |
|
} else { |
|
|
|
if (dsign) { |
|
*y = *x; |
|
*fy = *fx; |
|
*dy = *dx; |
|
} |
|
|
|
*x = *t; |
|
*fx = *ft; |
|
*dx = *dt; |
|
} |
|
|
|
|
|
if (tmax < newt) newt = tmax; |
|
if (newt < tmin) newt = tmin; |
|
|
|
|
|
|
|
|
|
|
|
if (*brackt && bound) { |
|
mq = *x + 0.66 * (*y - *x); |
|
if (*x < *y) { |
|
if (mq < newt) newt = mq; |
|
} else { |
|
if (newt < mq) newt = mq; |
|
} |
|
} |
|
|
|
|
|
*t = newt; |
|
return 0; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
static lbfgsfloatval_t owlqn_x1norm( |
|
const lbfgsfloatval_t* x, |
|
const int start, |
|
const int n |
|
) |
|
{ |
|
int i; |
|
lbfgsfloatval_t norm = 0.; |
|
|
|
for (i = start;i < n;++i) { |
|
norm += fabs(x[i]); |
|
} |
|
|
|
return norm; |
|
} |
|
|
|
static void owlqn_pseudo_gradient( |
|
lbfgsfloatval_t* pg, |
|
const lbfgsfloatval_t* x, |
|
const lbfgsfloatval_t* g, |
|
const int n, |
|
const lbfgsfloatval_t c, |
|
const int start, |
|
const int end |
|
) |
|
{ |
|
int i; |
|
|
|
|
|
for (i = 0;i < start;++i) { |
|
pg[i] = g[i]; |
|
} |
|
|
|
|
|
for (i = start;i < end;++i) { |
|
if (x[i] < 0.) { |
|
|
|
pg[i] = g[i] - c; |
|
} else if (0. < x[i]) { |
|
|
|
pg[i] = g[i] + c; |
|
} else { |
|
if (g[i] < -c) { |
|
|
|
pg[i] = g[i] + c; |
|
} else if (c < g[i]) { |
|
|
|
pg[i] = g[i] - c; |
|
} else { |
|
pg[i] = 0.; |
|
} |
|
} |
|
} |
|
|
|
for (i = end;i < n;++i) { |
|
pg[i] = g[i]; |
|
} |
|
} |
|
|
|
static void owlqn_project( |
|
lbfgsfloatval_t* d, |
|
const lbfgsfloatval_t* sign, |
|
const int start, |
|
const int end |
|
) |
|
{ |
|
int i; |
|
|
|
for (i = start;i < end;++i) { |
|
if (d[i] * sign[i] <= 0) { |
|
d[i] = 0; |
|
} |
|
} |
|
} |
|
|