#include #include #include #include "c_optim.h" namespace rstpm2 { // import namespaces using namespace Rcpp; using namespace arma; // typedefs typedef bool constraintfn(int, double *, void *); // enums enum IntervalCensoring {RightCensored, ExactTime, LeftCensored, IntervalCensored}; // structs struct li_constraint { vec li; double constraint; }; struct gradli_constraint { mat gradli; mat constraint; }; li_constraint operator+(li_constraint const &left, li_constraint const &right) { li_constraint out = {left.li+right.li,left.constraint+right.constraint}; return out; } gradli_constraint operator+(gradli_constraint const &left, gradli_constraint const &right) { gradli_constraint out = {left.gradli+right.gradli,left.constraint+right.constraint}; return out; } // Hadamard element-wise multiplication for the _columns_ of a matrix with a vector mat rmult(mat m, vec v) { mat out(m); out.each_col() %= v; return out; } mat lmult(vec v, mat m) { return rmult(m,v); } // print utilities void Rprint(NumericMatrix const & m) { for (int i=0; i out; for (iy = y.begin(), igroup = group.begin(); iy != y.end(); ++iy, ++igroup) { out[*igroup] += *iy; } return wrap(out); } // Various link objects class LogLogLink { public: vec link(vec S) { return log(-log(S)); } vec ilink(vec x) { return exp(-exp(x)); } vec h(vec eta, vec etaD) { return etaD % exp(eta); } vec H(vec eta) { return exp(eta); } mat gradH(vec eta, mat X) { return rmult(X,exp(eta)); } mat gradh(vec eta, vec etaD, mat X, mat XD) { return rmult(XD, exp(eta)) + rmult(X, etaD % exp(eta)); } cube hessianH(vec beta, mat X) { cube c(beta.size(), beta.size(), X.n_rows); for (size_t i=0; i double optimfunction(int n, double * beta, void * void_obj) { T * obj = static_cast(void_obj); vec coef(beta,n); double value = obj->objective(coef % obj->parscale); if (obj->bfgs.trace>1) { Rprintf("beta="); Rprint(coef); Rprintf("objective=%g\n",value); }; return value; } template void optimgradient(int n, double * beta, double * grad, void * void_obj) { T * obj = static_cast(void_obj); vec coef(beta,n); if (obj->bfgs.trace>1) { Rprintf("beta="); Rprint(coef); } if (obj->bfgs.trace>2) { Rprintf("parscale="); Rprint(obj->parscale); } vec gr = obj->gradient(coef % obj->parscale); if (obj->bfgs.trace>1) { Rprintf("gradient="); Rprint(gr); } if (obj->bfgs.trace>2) { Rprintf("fdgradient="); Rprint(obj->fdgradient(coef % obj->parscale)); } for (int i=0; i void optimfunction_nlm(int n, double * beta, double * f, void * void_obj) { T * obj = static_cast(void_obj); vec coef(beta,n); *f = obj->objective(coef % obj->parscale); } class BFGS2 : public BFGS { public: NumericMatrix calc_hessian(optimgr gr, void * ex) { if (parscale.size()==0) REprintf("parscale is not defined for BFGS2::calc_hessian.\n"); int n = coef.size(); NumericVector df1(n); NumericVector df2(n); NumericMatrix hess(n,n); double tmp; for(int i=0; i class Stpm2 : public Link { public: typedef Stpm2 This; using Link::h; using Link::H; using Link::gradh; using Link::gradH; Stpm2(SEXP sexp) : bfgs() { List list = as(sexp); bfgs.coef = init = as(list["init"]); X = as(list["X"]); XD = as(list["XD"]); bhazard = as(list["bhazard"]); wt = as(list["wt"]); event = as(list["event"]); time = as(list["time"]); delayed = as(list["delayed"]); interval = as(list["interval"]); n = init.size(); // number of parameters N = X.n_rows; // number of observations X1 = as(list["X1"]); X0 = as(list["X1"]); wt0.set_size(N); wt0.fill(0.0); if (delayed) { X0 = as(list["X0"]); wt0 = as(list["wt0"]); } if (interval) { X1 = as(list["X1"]); } ind0 = as(list["ind0"]); // length N boolean map0 = as(list["map0"]); // length N map from individuals to row of X0 which0 = as(list["which0"]); // length N0 indicator for X0 kappa = as(list["kappa"]); optimiser = as(list["optimiser"]); bfgs.trace = as(list["trace"]); reltol = bfgs.reltol = as(list["reltol"]); bfgs.hessianp = false; bfgs.parscale = parscale = as(list["parscale"]); } // log-likelihood components and constraints // Note: the li and gradli components depend on other variables (bhazard, wt, wt0, wt, event); // calculations should *not* be done on subsets li_constraint li_right_censored(vec eta, vec etaD) { vec h = Link::h(eta,etaD) + bhazard; vec H = Link::H(eta); vec eps = h*0.0 + 1.0e-16; double constraint = kappa/2.0 * (sum(h % h % (h<0)) + sum(H % H % (H<0))); h = max(h,eps); H = max(H,eps); vec li = wt % event % log(h) - wt % H; li_constraint out = {li, constraint}; return out; } li_constraint li_left_truncated(vec eta0) { vec H0 = Link::H(eta0); double constraint = kappa/2.0 * sum(H0 % H0 % (H0<0)); vec eps = H0*0.0 + 1e-16; vec li = wt0 % max(H0,eps); li_constraint out = {li, constraint}; return out; } li_constraint li_interval(vec eta, vec etaD, vec eta1) { vec H = Link::H(eta); vec H1 = Link::H(eta1); vec h = Link::h(eta, etaD) + bhazard; double constraint = kappa/2.0 * (sum(H % H % (H<0))+ sum(h % h % (h<0))+ sum(H1 % H1 % (H1<0))); vec eps = H*0.0 + 1e-16; H = max(H,eps); H1 = max(H1,eps); h = max(h,eps); vec li(N,fill::zeros); uvec index; index = find(ttype == 0); // right censored if (any(index)) li(index) -= wt(index) % H(index); index = find(ttype == 1); // exact if (any(index)) li(index) += wt(index) % (log(h(index)) - H(index)); index = find(ttype == 2); // left censored if (any(index)) li(index) += wt(index) % log(1-exp(-H(index))); index = find(ttype == 3); // interval censored if (any(index)) li(index) += wt(index) % log(exp(-H(index)) - exp(-H1(index))); li_constraint out = {li, constraint}; return out; } li_constraint li(vec eta, vec etaD, vec eta0, vec eta1) { if (interval) { return li_interval(eta, etaD, eta1); } else { li_constraint s = li_right_censored(eta, etaD); if (delayed) { li_constraint s0 = li_left_truncated(eta0); s.constraint += s0.constraint; s.li(which0) += s0.li; } return s; } } // negative log-likelihood double objective(vec beta) { li_constraint s = li(X * beta, XD * beta, X0 * beta, X1 * beta); return -sum(s.li) + s.constraint; } // finite-differencing of the gradient for the objective vec fdgradient(vec beta, double eps = 1.0e-8) { int n = beta.size(); vec grad(n); for (int i=0; i0) % (H>0)); if (delayed) { vec eta0 = X0 * beta; // vec etaD0 = XD0 * beta; vec H0 = Link::H(eta0); condition = condition && all(H0>0); } return condition; } void pre_process() { for (int i = 0; i, &optimgradient, init, (void *) this); vec vcoef(&bfgs.coef[0],n); satisfied = feasible(vcoef % parscale); if (!satisfied) kappa *= 2.0; } while ((!satisfied) && kappa < 1.0e3); } void optimWithConstraintNM(NumericVector init) { bool satisfied; NelderMead2 nm; nm.hessianp = false; nm.parscale = parscale; do { nm.optim(&optimfunction, init, (void *) this); vec vcoef(&nm.coef[0],n); satisfied = feasible(vcoef % parscale); if (!satisfied) kappa *= 2.0; } while ((!satisfied) && kappa < 1.0e3); nm.hessian = nm.calc_hessian(&optimfunction, (void *) this); bfgs.coef = nm.coef; bfgs.hessian = nm.hessian; } void optimWithConstraint(NumericVector init) { if (this->optimiser == "NelderMead") optimWithConstraintNM(init); else optimWithConstraintBFGS(init); } // find the left truncated values for a given index uvec find0() { return map0(find(ind0)); } uvec find0(vec index) { return (map0(index))(find(ind0(index))); } NumericVector init; mat X, XD, X0, X1; vec bhazard,wt,wt0,event,time,parscale,ttype; double kappa, reltol; bool delayed, interval; int n, N; BFGS2 bfgs; uvec ind0, map0, which0; std::string optimiser; }; // RcppExport SEXP optim_stpm2_nlm(SEXP args) { // stpm2 data(args); // data.init = ew_div(data.init,data.parscale); // int n = data.init.size(); // Nlm nlm; // nlm.gradtl = nlm.steptl = data.reltol; // //nlm.method=2; nlm.stepmx=0.0; // bool satisfied; // do { // nlm.optim(& fminfn_nlm, & grfn, data.init, (void *) &data); // satisfied = fminfn_constraint(n,&nlm.coef[0],(void *) &data); // if (!satisfied) data.kappa *= 2.0; // } while (!satisfied && data.kappa<1.0e5); // nlm.coef = ew_mult(nlm.coef, data.parscale); // return List::create(_("itrmcd")=wrap(nlm.itrmcd), // _("niter")=wrap(nlm.itncnt), // _("coef")=wrap(nlm.coef), // _("hessian")=wrap(nlm.hessian)); // } // penalised smoothers // _Not_ sub-classes of Pstpm2, hence the longer function signatures class SmoothLogH { public: struct Smoother { int first_para, last_para; mat S; }; SmoothLogH(SEXP sexp) { List list = as(sexp); List lsmooth = as(list["smooth"]); for(int i=0; i(lsmooth[i]); List lsmoothiS = as(lsmoothi["S"]); Smoother smoothi = { as(lsmoothi["first.para"]) - 1, as(lsmoothi["last.para"]) - 1, as(lsmoothiS[0]) }; smooth.push_back(smoothi); } } double penalty(vec vbeta, vec sp) { double value = 0.0; for (size_t i=0; i < smooth.size(); ++i) { Smoother smoothi = smooth[i]; value += sp[i]/2 * dot(vbeta.subvec(smoothi.first_para,smoothi.last_para), smoothi.S * vbeta.subvec(smoothi.first_para,smoothi.last_para)); } return value; } vec penalty_gradient(vec vbeta, vec sp) { int n = vbeta.size(); rowvec vgr(n, fill::zeros); for (size_t i=0; i < smooth.size(); ++i) { Smoother smoothi = smooth[i]; vgr.subvec(smoothi.first_para,smoothi.last_para) += sp[i] * (smoothi.S * vbeta.subvec(smoothi.first_para,smoothi.last_para)).t(); } vec gr(n); for (int i=0; i(smoothi.last_para); ++j) values[i] += X(j,j); } return values; } std::vector smooth; }; // // TODO: include first_para and last_para class data for the traces() method // class SmoothHaz { // public: // struct Smoother { // mat X0, X1, X2, X3; // vec w; // double lambda; // }; // SmoothHaz(SEXP sexp) { // List list = as(sexp); // List lsmooth = as(list["smooth"]); // for(int i=0; i(lsmooth[i]); // Smoother smoothi = { // as(lsmoothi["X0"]), // as(lsmoothi["X1"]), // as(lsmoothi["X2"]), // as(lsmoothi["X3"]), // as(lsmoothi["w"]), // as(lsmoothi["lambda"]) // }; // smooth.push_back(smoothi); // } // } // double penalty(vec vbeta, vec sp) { // double value = 0.0; // for (size_t i=0; i < smooth.size(); ++i) { // Smoother obj = smooth[i]; // vec s0 = obj.X0 * vbeta; // vec s1 = obj.X1 * vbeta; // vec s2 = obj.X2 * vbeta; // vec s3 = obj.X3 * vbeta; // vec h2 = exp(s0) % (s3 + 3*s1%s2 + s1%s1%s1); // value += sp[i]/2 * obj.lambda * sum(obj.w % h2 % h2); // } // return value; // } // vec penalty_gradient(vec vbeta, vec sp) { // int n = vbeta.size(); // rowvec vgr(n, fill::zeros); // for (size_t i=0; i < smooth.size(); ++i) { // Smoother obj = smooth[i]; // vec s0 = obj.X0 * vbeta; // vec s1 = obj.X1 * vbeta; // vec s2 = obj.X2 * vbeta; // vec s3 = obj.X3 * vbeta; // vec h2 = exp(s0) % (s3 + 3*s1%s2 + s1%s1%s1); // mat dh2sq_dbeta = // 2*lmult(h2 % exp(s0), // obj.X3+3*(rmult(obj.X1,s2)+rmult(obj.X2,s1))+3*lmult(s1%s1,obj.X1))+ // 2*lmult(h2 % h2,obj.X0); // vgr += sp[i]*obj.lambda*sum(lmult(obj.w, dh2sq_dbeta),0); // } // vec gr(n); // for (int i = 0; i smooth; // }; template double pstpm2_multivariate_step(int n, double * logsp_ptr, void * model_ptr) { T * model = static_cast(model_ptr); vec logsp(logsp_ptr,n); R_CheckUserInterrupt(); /* be polite -- did the user hit ctrl-C? */ return model->multivariate_step(logsp); } template double pstpm2_first_step(double logsp, void * model_ptr) { T * model = static_cast(model_ptr); R_CheckUserInterrupt(); /* be polite -- did the user hit ctrl-C? */ return model->first_step(logsp); } template void pstpm2_multivariate_stepNlm(int n, double * logsp_ptr, double * objective, void * model_ptr) { T * model = static_cast(model_ptr); vec logsp(logsp_ptr,n); R_CheckUserInterrupt(); /* be polite -- did the user hit ctrl-C? */ *objective = model->multivariate_step(logsp); } // Penalised link-based models template, class Smooth = SmoothLogH> class Pstpm2 : public Stpm2Type, public Smooth { public: typedef Pstpm2 This; Pstpm2(SEXP sexp) : Stpm2Type(sexp), Smooth(sexp) { List list = as(sexp); sp = as(list["sp"]); reltol_search = as(list["reltol_search"]); reltol_outer = as(list["reltol_outer"]); alpha = as(list["alpha"]); criterion = as(list["criterion"]); outer_optim = as(list["outer_optim"]); } double objective(vec beta) { return Stpm2Type::objective(beta) + Smooth::penalty(beta,sp); } double objective0(vec beta) { return Stpm2Type::objective(beta); } vec gradient(vec beta) { return Stpm2Type::gradient(beta) + Smooth::penalty_gradient(beta,sp); } vec gradient0(vec beta) { return Stpm2Type::gradient(beta); } // is the following strictly needed - or even correct? void optimWithConstraint(NumericVector init) { bool satisfied; if (this->bfgs.trace > 0) Rprintf("Starting optimization\n"); do { this->bfgs.optim(&optimfunction, &optimgradient, init, (void *) this); vec vcoef(&this->bfgs.coef[0],this->n); satisfied = Stpm2Type::feasible(vcoef % this->parscale); if (!satisfied) this->kappa *= 2.0; } while ((!satisfied) && this->kappa < 1.0e3); } double first_step(double logsp) { sp[0] = exp(logsp); this->pre_process(); this->optimWithConstraint(this->init); this->bfgs.hessian = this->bfgs.calc_hessian(&optimgradient, (void *) this); NumericMatrix hessian0 = this->bfgs.calc_hessian(&optimgradient, (void *) this); if (this->bfgs.trace > 1) { Rprintf("Debug on trace calculation. Coef:\n"); Rprint(this->bfgs.coef); if (this->bfgs.trace > 1) { Rprintf("Hessian0:\n"); Rprint(hessian0); Rprintf("Hessian:\n"); Rprint(this->bfgs.hessian); } } double edf = arma::trace(solve(as(this->bfgs.hessian),as(hessian0))); double negll = this->bfgs.calc_objective(&optimfunction, (void *) this); double gcv = negll + alpha*edf; double bic = negll + alpha*edf*log(sum(this->event)); this->init = this->bfgs.coef; if (this->bfgs.trace > 0) Rprintf("sp=%f\tedf=%f\tnegll=%f\tgcv=%f\tbic=%f\talpha=%f\n",sp[0],edf,negll,gcv,bic,alpha); this->post_process(); return criterion==1 ? gcv : bic; } double multivariate_step(vec logsp) { this->pre_process(); double lsp = -9.0, usp = 9.0; for (size_t i=0; i < sp.size(); ++i) sp[i] = exp(bound(logsp[i],lsp,usp)); if (this->bfgs.trace > 0) { for (size_t i = 0; i < sp.size(); ++i) Rprintf("sp[%i]=%f\t",i,sp[i]); } this->optimWithConstraint(this->init); this->bfgs.hessian = this->bfgs.calc_hessian(&optimgradient, (void *) this); NumericMatrix hessian0 = this->bfgs.calc_hessian(&optimgradient, (void *) this); double edf = arma::trace(solve(as(this->bfgs.hessian),as(hessian0))); double negll = this->bfgs.calc_objective(&optimfunction, (void *) this); double gcv = negll + alpha*edf; double bic = 2.0*negll + alpha*edf*log(sum(this->event)); this->init = this->bfgs.coef; // simple boundary constraints double constraint = 0.0; // double kappa = 1.0; for (size_t i=0; i < sp.size(); ++i) { if (logsp[i] < lsp) constraint += pow(logsp[i]-lsp,2.0); if (logsp[i] > usp) constraint += pow(logsp[i]-usp,2.0); } double objective = criterion==1 ? gcv + this->kappa/2.0*constraint : bic + this->kappa/2.0*constraint; if (this->bfgs.trace > 0) { Rprintf("edf=%f\tnegll=%f\tgcv=%f\tbic=%f\tobjective=%f\n",edf,negll,gcv,bic,objective); } this->post_process(); return objective; } SEXP optim_fixed() { this->pre_process(); this->optimWithConstraint(this->init); this->bfgs.hessian = this->bfgs.calc_hessian(&optimgradient, (void *) this); NumericMatrix hessian0 = this->bfgs.calc_hessian(&optimgradient, (void *) this); mat Proj = solve(as(wrap(this->bfgs.hessian)),as(wrap(hessian0))); double edf = trace(Proj); NumericVector edf_var = as(wrap(Smooth::traces(Proj))); this->post_process(); return List::create(_("sp")=wrap(sp), _("coef")=wrap(this->bfgs.coef), _("hessian")=wrap(this->bfgs.hessian), _("edf")=wrap(edf), _("edf_var")=wrap(edf_var) ); } SEXP optim_first() { this->bfgs.reltol = reltol_search; double opt_sp = exp(Brent_fmin(log(0.001),log(1000.0),&(pstpm2_first_step),(void *) this,reltol_outer)); sp[0] = opt_sp; this->bfgs.reltol = this->reltol; return optim_fixed(); } SEXP optim_multivariate() { if (outer_optim==1) return optim_multivariate_NelderMead(); else return optim_multivariate_Nlm(); } SEXP optim_multivariate_NelderMead() { this->kappa = 10.0; NelderMead2 nm; nm.reltol = reltol_outer; nm.maxit = 500; nm.hessianp = false; nm.parscale = this->parscale; this->bfgs.reltol = reltol_search; NumericVector logsp(sp.size()); for (size_t i=0; i < sp.size(); ++i) logsp[i] = log(sp[i]); bool satisfied; do { nm.optim(&pstpm2_multivariate_step, logsp, (void *) this); satisfied = true; for (size_t i=0; i < sp.size(); ++i) if (logsp[i]< -7.0 || logsp[i] > 7.0) satisfied = false; if (!satisfied) this->kappa *= 2.0; } while (!satisfied && this->kappa<1.0e5); for (int i=0; i < nm.coef.size(); ++i) sp[i] = exp(nm.coef[i]); this->bfgs.coef = this->init; this->bfgs.reltol = this->reltol; return optim_fixed(); } SEXP optim_multivariate_Nlm() { this->kappa = 10.0; Nlm2 nm; nm.gradtl = nm.steptl = reltol_outer; nm.itnlim = 100; // nm.hessianp = false; nm.parscale = this->parscale; this->bfgs.reltol = reltol_search; NumericVector logsp(sp.size()); for (size_t i=0; i < sp.size(); ++i) logsp[i] = log(sp[i]); bool satisfied; do { nm.optim(&pstpm2_multivariate_stepNlm, logsp, (void *) this); satisfied = true; for (size_t i=0; i < sp.size(); ++i) if (logsp[i]< -7.0 || logsp[i] > 7.0) satisfied = false; if (!satisfied) this->kappa *= 2.0; } while (!satisfied && this->kappa<1.0e5); for (int i=0; i < nm.coef.size(); ++i) sp[i] = exp(nm.coef[i]); this->bfgs.coef = this->init; this->bfgs.reltol = this->reltol; return optim_fixed(); } vec sp; double alpha, reltol_search, reltol_outer; int criterion, outer_optim; }; // template // void pstpm2_step_multivariate_nlm(int n, double * logsp, double * f, void *ex) { // *f = pstpm2_step_multivariate(n, logsp, ex); // }; /** Extension of stpm2 and pstpm2 to include gamma shared frailties with a cluster variable **/ template class GammaSharedFrailty : public Base { public: typedef std::vector Index; typedef std::map IndexMap; typedef GammaSharedFrailty This; GammaSharedFrailty(SEXP sexp) : Base(sexp) { List list = as(sexp); ivec cluster = as(list["cluster"]); // wragged array indexed by a map of vectors for (size_t id=0; idevent[id]==1) cluster_events[cluster[id]].push_back(id); } } /** Objective function for a Gamma shared frailty Assumes that weights == 1. **/ double objective(vec beta) { int n = beta.size(); if (this->bfgs.trace>0) { Rprint(beta); } vec vbeta(beta); // logtheta is the last parameter in beta vbeta.resize(n-1); double theta = exp(beta[n-1]); vec eta = this->X * vbeta; vec etaD = this->XD * vbeta; vec h = Base::h(eta,etaD) + this->bhazard; vec H = Base::H(eta); double constraint = this->kappa/2.0 * (sum(h % h % (h<0)) + sum(H % H % (H<0))); vec eps = h*0.0 + 1.0e-16; h = max(h,eps); H = max(H,eps); vec H0; if (this->delayed) { vec eta0 = this->X0 * vbeta; H0 = Base::H(eta0); constraint += this->kappa/2.0 * sum(H0 % H0 % (H0<0)); } double ll = 0.0; for (IndexMap::iterator it=clusters.begin(); it!=clusters.end(); ++it) { uvec index = conv_to::from(it->second); int mi = sum(this->event(index)); double sumH = sum(H(index)); double sumHenter = 0.0; ll += sum(log(h(index)) % this->event(index)); if (this->delayed && cluster_events.count(it->first) > 0) { uvec ind00 = conv_to::from(cluster_events[it->first]); sumHenter = sum(H0(ind00)); } ll += -(1.0/theta+mi)*log(1.0+theta*(sumH)) + 1.0/theta*log(1.0+theta*sumHenter); // Rondeau et al // ll -= (1.0/theta+mi)*log(1.0+theta*(sumH - sumHenter)); // conditional (Gutierrez 2002) if (mi>0) { for (int k=1; k<=mi; ++k) ll += log(1.0+theta*(mi-k)); } } ll -= constraint; return -ll; } vec gradient(vec beta) { int n = beta.size(); vec gr = zeros(n); vec grconstraint = zeros(n); vec vbeta(beta); // theta is the last parameter in beta vbeta.resize(n-1); double theta = exp(beta[n-1]); vec eta = this->X * vbeta; vec etaD = this->XD * vbeta; vec h = Base::h(eta,etaD); vec H = Base::H(eta); mat gradh = Base::gradh(eta,etaD,this->X,this->XD); mat gradH = Base::gradH(eta,this->X); vec eps = h*0.0 + 1.0e-16; h = max(h,eps); H = max(H,eps); vec H0; mat gradH0; if (this->delayed) { vec eta0 = this->X0 * vbeta; // vec etaD0 = this->XD0 * vbeta; H0 = Base::H(eta0); gradH0 = Base::gradH(eta0,this->X0); } for (IndexMap::iterator it=clusters.begin(); it!=clusters.end(); ++it) { int mi=0; double sumH = 0.0, sumHenter = 0.0; vec gradi = zeros(n-1); vec gradHi = zeros(n-1); vec gradH0i = zeros(n-1); vec grconstraint = zeros(n-1); for (Index::iterator j=it->second.begin(); j!=it->second.end(); ++j) { sumH += H(*j); gradHi += gradH.row(*j).t(); gradi += gradh.row(*j).t() / h(*j) * this->event(*j); if (this->event(*j)==1) mi++; if (this->delayed && this->ind0[*j]) { int jj = this->map0[*j]; sumHenter += H0(jj); gradH0i += gradH0.row(jj).t(); grconstraint += this->kappa * (gradH0.row(jj).t() * H0(jj) * (H0(jj)<0)); } grconstraint += this->kappa * ((gradh.row(*j).t() * h(*j) * (h(*j)<0)) + (gradH.row(*j).t() * H(*j) * (H(*j)<0))); } for (int k=0; k0) { for (int k=1; k<=mi; ++k) lastterm += theta*(mi-k)/(1.0+theta*(mi-k)); } // gr(n-1) += log(1.0+theta*(sumH-sumHenter))/theta - // (1.0/theta+mi)*theta*(sumH-sumHenter)/(1.0+theta*(sumH-sumHenter)) + // lastterm; // Gutierrez gr(n-1) += log(1.0+theta*sumH)/theta - (1.0+mi*theta)*sumH/(1.0+theta*sumH) + sumHenter/(1.0+theta*sumHenter) - log(1.0+theta*sumHenter)/theta + lastterm; // Rondeau et al } if (this->bfgs.trace>1) { Rprintf("Calculating fdgradient:\n"); Rprint(this->fdgradient(beta)); Rprintf("(=fdgradient)\n"); } return -gr; } bool feasible(vec beta) { vec coef(beta); coef.resize(this->n-1); return Base::feasible(coef); } void optimWithConstraint(NumericVector init) { bool satisfied; if (this->bfgs.trace > 0) Rprintf("Starting optimization\n"); do { this->bfgs.optim(&optimfunction, &optimgradient, this->init, (void *) this); vec vcoef(&this->bfgs.coef[0],this->n); satisfied = feasible(vcoef % this->parscale); if (!satisfied) this->kappa *= 2.0; } while ((!satisfied) && this->kappa < 1.0e3); } void optimWithConstraintNM(NumericVector init) { bool satisfied; NelderMead2 nm; nm.hessianp = false; nm.parscale = this->parscale; do { nm.optim(&optimfunction, this->init, (void *) this); vec vcoef(&this->bfgs.coef[0],this->n); satisfied = feasible(vcoef % this->parscale); if (!satisfied) this->kappa *= 2.0; } while ((!satisfied) && this->kappa < 1.0e3); nm.hessian = nm.calc_hessian(&optimfunction, (void *) this); this->bfgs.coef = nm.coef; this->bfgs.hessian = nm.hessian; } IndexMap clusters, cluster_events; }; /** Extension of stpm2 and pstpm2 to include log-normal shared frailties with a cluster variable **/ template class NormalSharedFrailty : public Base { public: typedef std::vector Index; typedef std::map IndexMap; typedef NormalSharedFrailty This; NormalSharedFrailty(SEXP sexp) : Base(sexp) { List list = as(sexp); IntegerVector cluster = as(list["cluster"]); gauss_x = as(list["gauss_x"]); // node values gauss_w = as(list["gauss_w"]); // probability weights // wragged array indexed by a map of vectors for (int id=0; idevent[id]==1) { cluster_events[cluster[id]].push_back(id); cluster_event_indices[cluster[id]].push_back(id); } } } /** Objective function for a log-normal shared frailty Assumes that weights == 1. **/ double objective(vec beta) { int n = beta.size(); vec vbeta(beta); // nu=log(variance) is the last parameter in beta; exp(nu)=sigma^2 vbeta.resize(n-1); double sigma = exp(0.5*beta[n-1]); // standard deviation int K = gauss_x.size(); // number of nodes mat lijk(this->N,K); double constraint = 0.0; vec wstar = gauss_w/sqrt(datum::pi); vec eta = this->X * vbeta; vec etaD = this->XD * vbeta; vec eta0 = this->X0 * vbeta; vec eta1 = this->X1 * vbeta; for (int k=0; kgauss_x[k]; li_constraint lik = Base::li(eta+bi,etaD,eta0+bi,eta1+bi); lijk.col(k) = lik.li; constraint += lik.constraint; } double ll = 0.0; for (IndexMap::iterator it=clusters.begin(); it!=clusters.end(); ++it) { uvec index = conv_to::from(it->second); double Li = dot(exp(sum(lijk.rows(index),0)),wstar); ll += log(Li); } // ll -= constraint; return -ll; } /// Another way for gradients /// gradient of objective vec gradient_new(vec beta) { int n = beta.size(); double sigma = exp(0.5*beta[n-1]); mat Z(this->N,1,fill::ones); mat ZD(this->N,1,fill::zeros); mat Z0(this->X0.n_rows,1,fill::ones); mat Z1(this->X1.n_rows,1,fill::ones); mat Xstar = join_horiz(this->X, Z); mat XDstar = join_horiz(this->XD, ZD); // assumes time-invariant random effects mat X0star = join_horiz(this->X0, Z0); mat X1star = join_horiz(this->X1, Z1); int K = gauss_x.size(); // number of nodes vec wstar = gauss_w/sqrt(datum::pi); int G = clusters.size(); vec Li(G,fill::zeros); mat gradLi(G,n,fill::zeros); vec betastar = beta; rowvec dconstraint(n,fill::zeros); mat likeli_jk(this->N,K,fill::zeros); vec likeli_k(K,fill::zeros); cube grad_jk(this->N,n,K,fill::zeros); mat grad_kn(K,n,fill::zeros); // for K nodes calculation for (int k=0; k::from(it->second); vec likeli_k = exp(sum(likeli_jk.rows(index),0)).t(); // sum index components at all nodes for cluster i for(int k=0; kbfgs.trace>0) { Rprintf("fdgradient="); Rprint(this->fdgradient(beta)); } vec gr(n,fill::zeros); for (int i=0; iN,1,fill::ones); mat ZD(this->N,1,fill::zeros); mat Z0(this->X0.n_rows,1,fill::ones); mat Z1(this->X1.n_rows,1,fill::ones); mat Xstar = join_horiz(this->X, Z); mat XDstar = join_horiz(this->XD, ZD); // assumes time-invariant random effects mat X0star = join_horiz(this->X0, Z0); mat X1star = join_horiz(this->X1, Z1); int K = gauss_x.size(); // number of nodes vec wstar = gauss_w/sqrt(datum::pi); int G = clusters.size(); vec Li(G,fill::zeros); mat gradLi(G,n,fill::zeros); vec betastar = beta; rowvec dconstraint(n,fill::zeros); for (int k=0; k::from(it->second); double Lgk = exp(sum(lik.li(index))); Li(g) += Lgk*wstar[k]; gradLi.row(g) += wstar[k]*Lgk*sum(gradlik.gradli.rows(index),0); } } rowvec grad = sum(rmult(gradLi,1/Li),0); if (this->bfgs.trace>0) { Rprintf("fdgradient="); Rprint(this->fdgradient(beta)); } vec gr(n,fill::zeros); for (int i=0; ibfgs.trace > 0) Rprintf("Starting optimization\n"); do { this->bfgs.optim(&optimfunction, &optimgradient, init, (void *) this); vec vcoef(&this->bfgs.coef[0],n); satisfied = feasible(vcoef % this->parscale); if (!satisfied) this->kappa *= 2.0; } while ((!satisfied) && this->kappa < 1.0e3); } void optimWithConstraintNM(NumericVector init) { bool satisfied; NelderMead2 nm; nm.hessianp = false; nm.parscale = this->parscale; do { nm.optim(&optimfunction, this->init, (void *) this); vec vcoef(&this->bfgs.coef[0],this->n); satisfied = feasible(vcoef % this->parscale); if (!satisfied) this->kappa *= 2.0; } while ((!satisfied) && this->kappa < 1.0e3); nm.hessian = nm.calc_hessian(&optimfunction, (void *) this); this->bfgs.coef = nm.coef; this->bfgs.hessian = nm.hessian; } IndexMap clusters, cluster_events, cluster_event_indices; vec gauss_x, gauss_w; }; template SEXP stpm2_model_output_(SEXP args) { T model(args); List list = as(args); std::string return_type = as(list["return_type"]); vec beta(&model.init[0],model.n); if (return_type == "optim") { model.pre_process(); model.optimWithConstraint(model.init); model.bfgs.hessian = model.bfgs.calc_hessian(&optimgradient, (void *) &model); model.post_process(); return List::create(_("fail")=model.bfgs.fail, _("coef")=wrap(model.bfgs.coef), _("hessian")=wrap(model.bfgs.hessian)); } else if (return_type == "objective") return wrap(model.objective(beta)); else if (return_type == "gradient") return wrap(model.gradient(beta)); else if (return_type == "feasible") return wrap(model.feasible(beta)); else { REprintf("Unknown return_type.\n"); return wrap(-1); } } template SEXP pstpm2_model_output_(SEXP args) { T model(args); List list = as(args); std::string return_type = as(list["return_type"]); vec beta(&model.init[0],model.n); if (return_type == "optim_fixed") return(model.optim_fixed()); else if (return_type == "optim_first") return(model.optim_first()); else if (return_type == "optim_multivariate") return(model.optim_multivariate()); else if (return_type == "objective") return wrap(model.objective(beta)); else if (return_type == "objective0") return wrap(model.objective0(beta)); else if (return_type == "gradient") return wrap(model.gradient(beta)); else if (return_type == "gradient0") return wrap(model.gradient0(beta)); else if (return_type == "constraint") return wrap(model.feasible(beta)); else if (return_type == "feasible") return wrap(model.feasible(beta)); else { REprintf("Unknown return_type.\n"); return wrap(-1); } } RcppExport SEXP model_output(SEXP args) { List list = as(args); std::string link = as(list["link"]); std::string type = as(list["type"]); if (type=="stpm2") { if (link == "PH") return stpm2_model_output_ >(args); else if (link == "PO") return stpm2_model_output_ >(args); else if (link == "AH") return stpm2_model_output_ >(args); else if (link == "probit") return stpm2_model_output_ >(args); else { REprintf("Unknown link function.\n"); return wrap(-1); } } else if (type=="pstpm2") { if (link == "PH") return pstpm2_model_output_,SmoothLogH> >(args); else if (link == "PO") return pstpm2_model_output_,SmoothLogH> >(args); else if (link == "AH") return pstpm2_model_output_,SmoothLogH> >(args); else if (link == "probit") return pstpm2_model_output_,SmoothLogH> >(args); else { REprintf("Unknown link function.\n"); return wrap(-1); } } else if (type=="stpm2_gamma_frailty") { if (link == "PH") return stpm2_model_output_ > >(args); else if (link == "PO") return stpm2_model_output_ > >(args); else if (link == "AH") return stpm2_model_output_ > >(args); else if (link == "probit") return stpm2_model_output_ > >(args); else { REprintf("Unknown link function.\n"); return wrap(-1); } } else if (type=="pstpm2_gamma_frailty") { if (link == "PH") return pstpm2_model_output_ >,SmoothLogH> >(args); else if (link == "PO") return pstpm2_model_output_ >,SmoothLogH> >(args); else if (link == "AH") return pstpm2_model_output_ >,SmoothLogH> >(args); else if (link == "probit") return pstpm2_model_output_ >,SmoothLogH> >(args); else { REprintf("Unknown link function.\n"); return wrap(-1); } } else if (type=="stpm2_normal_frailty") { if (link == "PH") return stpm2_model_output_ > >(args); else if (link == "PO") return stpm2_model_output_ > >(args); else if (link == "AH") return stpm2_model_output_ > >(args); else if (link == "probit") return stpm2_model_output_ > >(args); else { REprintf("Unknown link function.\n"); return wrap(-1); } } else if (type=="pstpm2_normal_frailty") { if (link == "PH") return pstpm2_model_output_ >,SmoothLogH> >(args); else if (link == "PO") return pstpm2_model_output_ >,SmoothLogH> >(args); else if (link == "AH") return pstpm2_model_output_ >,SmoothLogH> >(args); else if (link == "probit") return pstpm2_model_output_ >,SmoothLogH> >(args); else { REprintf("Unknown link function.\n"); return wrap(-1); } } else { REprintf("Unknown model type.\n"); return wrap(-1); } } // Proof of concept for a Weibull cure model struct CureModel { int n0, n1, n2; mat Xshape, Xscale, Xcure; vec time, status; }; double fminfn_cureModel(int n, double * beta, void *ex) { double ll = 0.0; CureModel * data = (CureModel *) ex; vec vbeta(&beta[0],n); vec shape = exp(data->Xshape * vbeta(span(0,data->n0-1))); vec scale = exp(data->Xscale * vbeta(span(data->n0,data->n1-1))); vec cure = 1.0/(1.0+exp(-data->Xcure * vbeta(span(data->n1,data->n2-1)))); for (size_t i=0; i < data->time.size(); ++i) { ll += data->status(i)==1.0 ? log(1.0-cure(i)) + R::dweibull(data->time(i),shape(i),scale(i),1) : log(cure(i)+(1.0-cure(i)) * R::pweibull(data->time(i),shape(i),scale(i),0,0)); } R_CheckUserInterrupt(); /* be polite -- did the user hit ctrl-C? */ return -ll; } RcppExport SEXP fitCureModel(SEXP stime, SEXP sstatus, SEXP sXshape, SEXP sXscale, SEXP sXcure, SEXP sbeta) { mat Xshape = as(sXshape); mat Xscale = as(sXscale); mat Xcure = as(sXcure); vec time = as(stime); vec status = as(sstatus); NumericVector init = as(sbeta); int n0=Xshape.n_cols; int n1=n0+Xscale.n_cols; int n2=n1+Xcure.n_cols; CureModel data = {n0,n1,n2,Xshape,Xscale,Xcure,time,status}; NelderMead nm; nm.reltol = 1.0e-8; nm.maxit = 1000; nm.optim(& fminfn_cureModel, init, (void *) &data); for (int i = 0; i