#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include "lbfgs.h"
#ifdef __ENABLE_TRACING__
#include "time.h"
#define TRACE(msg) \
printf
(msg); \
printf
(
": %0.10f s\n"
, 1.0 * (
clock
() - _c) / CLOCKS_PER_SEC); \
fflush
(stdout); \
_c =
clock
()
#define dTRACE clock_t _c = clock()
#else
#define TRACE(msg)
#define dTRACE
#endif
#define newSVpv_(x) newSVpv(x, strlen(x))
lbfgsfloatval_t lbfgs_evaluation_cb(
void
* instance,
const
lbfgsfloatval_t* x,
lbfgsfloatval_t* g,
const
int
n,
const
lbfgsfloatval_t step)
{
int
i;
SV *lbfgs_eval, *user_data, *sv_f;
AV *av_x, *av_g;
lbfgsfloatval_t f;
dSP;
dTRACE;
TRACE(
"lbfgs_evaluation_cb: enter"
);
lbfgs_eval = ((SV**)instance)[0];
user_data = ((SV**)instance)[2];
av_x = newAV();
av_extend(av_x, n - 1);
for
(i = 0; i < n; i++) av_store(av_x, i, newSVnv(x[i]));
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newRV_noinc((SV*)av_x)));
XPUSHs(sv_2mortal(newSVnv(step)));
XPUSHs(user_data);
PUTBACK;
TRACE(
"lbfgs_evaluation_cb: finish arguments preparation"
);
call_sv(lbfgs_eval, G_ARRAY);
TRACE(
"lbfgs_evaluation_cb: finish calling"
);
SPAGAIN;
av_g = (AV*)SvRV(POPs);
sv_f = POPs;
f = SvNV(sv_f);
for
(i = 0; i < n; i++)
g[i] = SvNV(*av_fetch(av_g, i, 0));
PUTBACK;
FREETMPS;
LEAVE;
if
(SvREFCNT(av_g) > 0) av_undef(av_g);
if
(SvREFCNT(sv_f) > 0) SvREFCNT_dec(sv_f);
TRACE(
"lbfgs_evaluation_cb: leave"
);
return
f;
}
int
lbfgs_progress_cb(
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)
{
int
i, r;
SV *lbfgs_prgr, *user_data, *sv_r;
AV *av_x, *av_g;
dSP;
dTRACE;
TRACE(
"lbfgs_progress_cb: enter"
);
lbfgs_prgr = ((SV**)instance)[1];
user_data = ((SV**)instance)[2];
av_x = newAV();
for
(i = 0; i < n; i++) av_store(av_x, i, newSVnv(x[i]));
av_g = newAV();
for
(i = 0; i < n; i++) av_store(av_g, i, newSVnv(g[i]));
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newRV_noinc((SV*)av_x)));
XPUSHs(sv_2mortal(newRV_noinc((SV*)av_g)));
XPUSHs(sv_2mortal(newSVnv(fx)));
XPUSHs(sv_2mortal(newSVnv(xnorm)));
XPUSHs(sv_2mortal(newSVnv(gnorm)));
XPUSHs(sv_2mortal(newSVnv(step)));
XPUSHs(sv_2mortal(newSViv(k)));
XPUSHs(sv_2mortal(newSViv(ls)));
XPUSHs(user_data);
PUTBACK;
TRACE(
"lbfgs_progress_cb: finish arguments preparation"
);
call_sv(lbfgs_prgr, G_ARRAY);
TRACE(
"lbfgs_progress_cb: finish calling"
);
SPAGAIN;
sv_r = POPs;
r = SvIV(sv_r);
PUTBACK;
FREETMPS;
LEAVE;
if
(SvREFCNT(sv_r) > 0) SvREFCNT_dec(sv_r);
TRACE(
"lbfgs_progress_cb: leave"
);
return
r;
}
MODULE = Algorithm::LBFGS PACKAGE = Algorithm::LBFGS
void
*
create_lbfgs_instance(lbfgs_eval, lbfgs_prgr, user_data)
SV* lbfgs_eval
SV* lbfgs_prgr
SV* user_data
PREINIT:
void
* instance =
malloc
(3 *
sizeof
(SV*));
CODE:
((SV**)instance)[0] = lbfgs_eval;
((SV**)instance)[1] = lbfgs_prgr;
((SV**)instance)[2] = user_data;
RETVAL = instance;
OUTPUT:
RETVAL
void
destroy_lbfgs_instance(li)
void
* li
CODE:
free
(li);
void
*
create_lbfgs_param()
PREINIT:
void
* lp =
malloc
(
sizeof
(lbfgs_parameter_t));
CODE:
lbfgs_parameter_init((lbfgs_parameter_t*)lp);
RETVAL = lp;
OUTPUT:
RETVAL
void
destroy_lbfgs_param(lp)
void
* lp
CODE:
free
(lp);
SV*
set_lbfgs_param(lp, name, val)
void
* lp
char
* name
SV* val
PREINIT:
lbfgs_parameter_t* p = (lbfgs_parameter_t*)lp;
SV* r = &PL_sv_undef;
CODE:
if
(
strcmp
(name,
"m"
) == 0) {
if
(SvIOK(val)) p->m = SvIV(val);
r = newSViv(p->m);
}
else
if
(
strcmp
(name,
"epsilon"
) == 0) {
if
(SvNOK(val)) p->epsilon = SvNV(val);
r = newSVnv(p->epsilon);
}
else
if
(
strcmp
(name,
"max_iterations"
) == 0) {
if
(SvIOK(val)) p->max_iterations = SvIV(val);
r = newSViv(p->max_iterations);
}
else
if
(
strcmp
(name,
"max_linesearch"
) == 0) {
if
(SvIOK(val)) p->max_linesearch = SvIV(val);
r = newSViv(p->max_linesearch);
}
else
if
(
strcmp
(name,
"min_step"
) == 0) {
if
(SvNOK(val)) p->min_step = SvNV(val);
r = newSVnv(p->min_step);
}
else
if
(
strcmp
(name,
"max_step"
) == 0) {
if
(SvNOK(val)) p->max_step = SvNV(val);
r = newSVnv(p->max_step);
}
else
if
(
strcmp
(name,
"ftol"
) == 0) {
if
(SvNOK(val)) p->ftol = SvNV(val);
r = newSVnv(p->ftol);
}
else
if
(
strcmp
(name,
"gtol"
) == 0) {
if
(SvNOK(val)) p->gtol = SvNV(val);
r = newSVnv(p->gtol);
}
else
if
(
strcmp
(name,
"xtol"
) == 0) {
if
(SvNOK(val)) p->xtol = SvNV(val);
r = newSVnv(p->xtol);
}
else
if
(
strcmp
(name,
"orthantwise_c"
) == 0) {
if
(SvNOK(val)) p->orthantwise_c = SvNV(val);
r = newSVnv(p->orthantwise_c);
}
RETVAL = r;
OUTPUT:
RETVAL
SV*
do_lbfgs(param, instance, x0)
void
* param
void
* instance
SV* x0
PREINIT:
AV* av_x0 = (AV*)SvRV(x0);
int
n = av_len(av_x0) + 1;
int
i, s;
CODE:
lbfgsfloatval_t* carr_x0 = (lbfgsfloatval_t*)
malloc
(n *
sizeof
(lbfgsfloatval_t));
for
(i = 0; i < n; i++) carr_x0[i] = SvNV(*av_fetch(av_x0, i, 0));
s = lbfgs(n, carr_x0, NULL,
SvOK(((SV**)instance)[0]) ? &lbfgs_evaluation_cb : NULL,
SvOK(((SV**)instance)[1]) ? &lbfgs_progress_cb : NULL,
instance, (lbfgs_parameter_t*)param);
for
(i = 0; i < n; i++) av_store(av_x0, i, newSVnv(carr_x0[i]));
free
(carr_x0);
RETVAL = newSViv(s);
OUTPUT:
RETVAL
SV*
status_2pv(status)
int
status
CODE:
switch
(status) {
case
0:
RETVAL = newSVpv_(
"LBFGS_OK"
);
break
;
case
LBFGSERR_UNKNOWNERROR:
RETVAL = newSVpv_(
"LBFGSERR_UNKNOWNERROR"
);
break
;
case
LBFGSERR_LOGICERROR:
RETVAL = newSVpv_(
"LBFGSERR_LOGICERROR"
);
break
;
case
LBFGSERR_OUTOFMEMORY:
RETVAL = newSVpv_(
"LBFGSERR_OUTOFMEMORY"
);
break
;
case
LBFGSERR_CANCELED:
RETVAL = newSVpv_(
"LBFGSERR_CANCELED"
);
break
;
case
LBFGSERR_INVALID_N:
RETVAL = newSVpv_(
"LBFGSERR_INVALID_N"
);
break
;
case
LBFGSERR_INVALID_N_SSE:
RETVAL = newSVpv_(
"LBFGSERR_INVALID_N_SSE"
);
break
;
case
LBFGSERR_INVALID_MINSTEP:
RETVAL = newSVpv_(
"LBFGSERR_INVALID_MINSTEP"
);
break
;
case
LBFGSERR_INVALID_MAXSTEP:
RETVAL = newSVpv_(
"LBFGSERR_INVALID_MAXSTEP"
);
break
;
case
LBFGSERR_INVALID_FTOL:
RETVAL = newSVpv_(
"LBFGSERR_INVALID_FTOL"
);
break
;
case
LBFGSERR_INVALID_GTOL:
RETVAL = newSVpv_(
"LBFGSERR_INVALID_GTOL"
);
break
;
case
LBFGSERR_INVALID_XTOL:
RETVAL = newSVpv_(
"LBFGSERR_INVALID_XTOL"
);
break
;
case
LBFGSERR_INVALID_MAXLINESEARCH:
RETVAL = newSVpv_(
"LBFGSERR_INVALID_MAXLINESEARCH"
);
break
;
case
LBFGSERR_INVALID_ORTHANTWISE:
RETVAL = newSVpv_(
"LBFGSERR_INVALID_ORTHANTWISE"
);
break
;
case
LBFGSERR_OUTOFINTERVAL:
RETVAL = newSVpv_(
"LBFGSERR_OUTOFINTERVAL"
);
break
;
case
LBFGSERR_INCORRECT_TMINMAX:
RETVAL = newSVpv_(
"LBFGSERR_INCORRECT_TMINMAX"
);
break
;
case
LBFGSERR_ROUNDING_ERROR:
RETVAL = newSVpv_(
"LBFGSERR_ROUNDING_ERROR"
);
break
;
case
LBFGSERR_MINIMUMSTEP:
RETVAL = newSVpv_(
"LBFGSERR_MINIMUMSTEP"
);
break
;
case
LBFGSERR_MAXIMUMSTEP:
RETVAL = newSVpv_(
"LBFGSERR_MAXIMUMSTEP"
);
break
;
case
LBFGSERR_MAXIMUMLINESEARCH:
RETVAL = newSVpv_(
"LBFGSERR_MAXIMUMLINESEARCH"
);
break
;
case
LBFGSERR_MAXIMUMITERATION:
RETVAL = newSVpv_(
"LBFGSERR_MAXIMUMITERATION"
);
break
;
case
LBFGSERR_WIDTHTOOSMALL:
RETVAL = newSVpv_(
"LBFGSERR_WIDTHTOOSMALL"
);
break
;
case
LBFGSERR_INVALIDPARAMETERS:
RETVAL = newSVpv_(
"LBFGSERR_INVALIDPARAMETERS"
);
break
;
case
LBFGSERR_INCREASEGRADIENT:
RETVAL = newSVpv_(
"LBFGSERR_INCREASEGRADIENT"
);
break
;
default
:
RETVAL = newSVpv_(
""
);
break
;
}
OUTPUT:
RETVAL