R/vam.R

Defines functions init.fixed.param init.par0 priors.from.vam.formula substitute.vam.formula parse.vam.formula summary.bayesian.vam hist.bayesian.vam coef.bayesian.vam run.bayesian.vam bayesian.vam coef.mle.vam run.mle.vam logLik.mle.vam contrast.mle.vam update.mle.vam formula.mle.vam params.bayesian.vam mle.vam make.censorship check.data.vam data.frame.to.list.multi.vam check.censorship update.model.vam model.vam simulate.sim.vam sim.vam

Documented in bayesian.vam coef.bayesian.vam coef.mle.vam contrast.mle.vam formula.mle.vam hist.bayesian.vam logLik.mle.vam mle.vam model.vam run.bayesian.vam run.mle.vam simulate.sim.vam sim.vam summary.bayesian.vam update.mle.vam update.model.vam

# Simulation: sim.vam

sim.vam <- function(formula,data.covariates) {
	if(missing(data.covariates)) data.covariates<-NULL
	self <- newEnv(sim.vam,formula=formula(formula),data.covariates=data.covariates)

	PersistentRcppObject(self,new = {
		model <- parse.vam.formula(self$formula)
		## specify names of variables inside data.frame
		if(is.null(model$response)) {
			self$response.names <- c("Time","Type")
			self$system.name <- "System"
		} else {
			if(length(model$response)==3)  {
				self$system.name <- model$response[1]
				self$response.names <- tail(model$response,2)
			} else if(length(model$response)==2)  {
				self$system.name <- "System"
				self$response.names <- model$response
			}
		}
		self$model<-model#debuggage LD
		self$formula <- substitute.vam.formula(model=model)
		if(!is.null(model$covariates)) {
			 model$covariates$data <- model.frame(model$covariates$formula,data=self$data.covariates) #ok even if data.covariates is null
		}
		self$model.parsed <- model #just to keep some info notably on covariates
		rcpp <- new(SimVam,model)
		rcpp
	})

	self

}


# TODO: when data provided, complete the data!
simulate.sim.vam <- function(sim, stop.policy, nb.system, cache.size=500,as.list=FALSE,data) {

	# To have a first argument more readable
	self <- sim

	rcpp <- self$rcpp()

	if(missing(stop.policy)) stop.policy <- if(is.null(sim$model.parsed$covariates)) 10 else 1
	if(missing(nb.system)) nb.system <- if(is.null(sim$model.parsed$covariates)) 1 else nrow(sim$model.parsed$covariates$data)

	self$stop.policy.last <- parse.stop.policy(deparse(substitute(stop.policy)))
	stop.policy <- eval(self$stop.policy.last,parent.frame(1))
	if(is.numeric(stop.policy)) {
		if(stop.policy == as.integer(stop.policy)) {#integer
			stop.policy <- EndAt(size=stop.policy)
		} else stop.policy <- NULL
	} else if(!inherits(stop.policy,"stop.policy")) {
		stop.policy <- NULL
	}
	if(is.null(stop.policy)) {
		warning("Argument stop.policy is not a proper one!")
		return(invisible(NULL))
	}
	# default cache size
	if(is.null(stop.policy$cache.size)) stop.policy$cache.size <- cache.size

	# add stop.policy object
	rcpp$add_stop_policy(stop.policy)

	if(nb.system>1) {
		# multisystem
		if(as.list) df<-list()
		for(i in 1:nb.system) {
			rcpp$select_current_system(i-1) # i-1 because it is c++
			df2 <- rcpp$simulate(stop.policy$cache.size)[-1,]
			names(df2) <- sim$response.names
			if(as.list) {
				rownames(df2)<-1:nrow(df2)
				df[[i]] <- df2 #rbind(data.frame(Time=0,Type=1),df2)
			} else {
				df2[[sim$system.name]] <- i
				df2<-df2[c(3,1:2)]
				df <- if(i==1) df2 else rbind(df,df2)
			}
		}
	} else {
		rcpp$select_current_system(0) # added for covariates case
		df <- rcpp$simulate(stop.policy$cache.size)[-1,]
		names(df) <- sim$response.names
		if(as.list) {rownames(df)<-1:nrow(df);df<-list(df)}
	}
	if(!as.list) {
		rownames(df) <- 1:nrow(df)
		rcpp$set_data(data.frame.to.list.multi.vam(df,names(df))) #if already list, response not used inside data.frame.to.list.multi.vam
	}
	else {
		names(df) <- paste0(sim$system.name,1:length(df))
		rcpp$set_data(unname(df))
	}
	## put the final result transformed as in mle.vam and model.vam to the model
		## return the result as a data.frame
	df
}

# Model part

model.vam <- function(formula,data,data.covariates) {
	if(missing(data)) data<-NULL
	if(missing(data.covariates)) data.covariates<-NULL
	self <- newEnv(model.vam,formula=formula(formula),data=data,data.covariates=data.covariates)

	PersistentRcppObject(self,new = {
		model <- parse.vam.formula(self$formula)
		self$formula <- substitute.vam.formula(model=model)
		if(!is.null(model$covariates)) {
			 model$covariates$data <- model.frame(model$covariates$formula,data=self$data.covariates) #ok even if data.covariates is null
		}
		self$model.parsed <- model #just to keep some info notably on covariates
		if(is.null(self$data)) {## No data
			rcpp <- new(ModelVam,model)
			rcpp
		} else {
			response <- model$response
			data <- data.frame.to.list.multi.vam(self$data,response)
			rcpp <- new(ModelVam,model,data)
			rcpp
		}
	})

	self
}

update.model.vam <- function(model,data) {
	if(!missing(data)) {
		self <- model #to have an argument more readable
		response <- parse.vam.formula(self$formula)$response
		self$data <- data
		data2 <- data.frame.to.list.multi.vam(self$data,response)
		self$rcpp()$set_data(data2)
	}
}

check.censorship <- function(data) {
		lapply(data,function(data.syst) {
			ty <- data.syst$Type[-1]
			check<-list(type="none",index=0)
			if(any(ty==0)) {
				if(which(ty==0)[1]==length(ty)) check$type <- 'right' #meaning: right only
				else if(!any(ty<0) || (which(ty<0)[1] > which(ty==0)[1])) {
					check$type <- 'left' #can have also a right censorship
					check$index <- which(ty==0)[1]
				}
				else check$type <- 'unknown'
			}
			check
		})
}


data.frame.to.list.multi.vam <- function(data,response) {
	# return data if it is already only a list!
	if(is.list(data) && !is.data.frame(data)) {
		data.frame(Time=0,Type=1)->tmp
		names(tmp) <- names(data[[1]])
		return(lapply(data,function(df) rbind(tmp,df)))
	}
	if(!("System" %in% response) && ("System" %in% names(data)) ) {
		warning(paste0("WARNING: data has variable 'System' when response in formula does not contain this variable!"))
	}
	# otherwise
	if(length(response)==2) {
		if(length(intersect(response,names(data))) != 2) stop(paste0("Bad response:",response))
		tmp <- data[[response[1]]]
		data2 <- list(data.frame(Time=c(0,tmp[order(tmp)]),Type=c(1,data[[response[2]]][order(tmp)])))
	} else {
		if(length(intersect(response,names(data))) != 3) stop(paste0("Bad response:",response))
		syst0 <- unique(syst<-data[[response[1]]])
		data2 <- list()
		for(i in seq_along(syst0)) {
			df <- data[syst==syst0[i],response]
			tmp <- df[[response[2]]]
			data2[[i]] <- data.frame(Time=c(0,tmp[order(tmp)]),Type=c(1,df[[response[3]]][order(tmp)]))
		}
	}
	data2
}

# TODO: check data
check.data.vam <-function(data,response) {
	if(all(data[[response[[-length(response)]]]])) {

	}
}

make.censorship <- function(data,rcpp) {
	if(any(sapply(censorship <- check.censorship(data),function(e) e$type == 'unknown'))) stop("VAM package does know how to deal with this kind of censorship!")
	if(any(sapply(censorship,function(e) e$type == 'left'))) {
		leftCensors <- sapply(censorship,function(e) e$index)
		rcpp$set_leftCensors(leftCensors)
	}
}

mle.vam <- function(formula,data,data.covariates) {
	if(missing(data.covariates)) data.covariates<-NULL
	self <- newEnv(mle.vam,formula=formula(formula),data=data,data.covariates=data.covariates)

	PersistentRcppObject(self,new = {
		model <- parse.vam.formula(self$formula)
		self$formula <- substitute.vam.formula(model=model)
		if(!is.null(model$covariates)) {
			 model$covariates$data <- model.frame(model$covariates$formula,data=self$data.covariates) #ok even if data.covariates is null
		}
		self$model.parsed <- model #just to keep some info notably on covariates
		response <- model$response
		data <- data.frame.to.list.multi.vam(self$data,response)
		rcpp <- new(MLEVam,model,data)
		make.censorship(data,rcpp)
		rcpp
	})

	self
}

# to convert in Rcpp



params.model.vam <- params.sim.vam <- params.mle.vam <- params.bayesian.vam <- function(self,param) {
	if(missing(param)) {
		 self$rcpp()$get_params()
	} else {
		self$rcpp()$set_params(param)
	}
}

## Useless since stats:::formula.default do that by default
# formula.model.vam <- formula.sim.vam <- function(self) {
# 	self$formula
# }

formula.mle.vam <- function(self,origin=FALSE) {
	form <- substitute.vam.formula(self$formula,coef(self))
	if(origin) list(formula=form,origin=self$formula)
	else form
}

update.mle.vam <- function(mle,data) {
	if(!missing(data)) {
		self <- mle
		model <- parse.vam.formula(self$formula)
		response <- model$response
		self$data <- data
		data2 <- data.frame.to.list.multi.vam(self$data,response)
		self$rcpp()$set_data(data2)
		self$rcpp()$reset_leftCensors()
		make.censorship(data2,self$rcpp())
		## estimation has to be computed again!
		self$mle.coef<-NULL
	}
	## with
}

#fonction de LD2
contrast.mle.vam <-function(obj,par0,with_value=TRUE,with_gradient=FALSE,with_hessian=FALSE){
	type <- c(with_value,with_gradient,with_hessian)
	rcpp <- obj$rcpp()
	## save the initial param
	if(is.null(obj$par0)) obj$par0 <- params(obj)
	## parameters stuff!
	if(missing(par0))  {
		if("par" %in% names(obj)) {
			param <- obj$par[-1]
			alpha <- obj$par[1]#LD2
		} #not the first run
		else {
			param<-params(obj)[-1] #first run
			alpha<-params(obj)[1]#LD2
		}
	} else if(is.null(par0)) {
		param<-obj$par0[-1]
		alpha<-obj$par0[1]#LD2
	} else {
		param<-par0[-1]
		alpha<-par0[1]#LD2
	}

	if(sum(type)==1) {
		if(type[1]){
			res<-rcpp$contrast(c(alpha,param),FALSE)
		} else if(type[2]){
			res<-rcpp$gradient(c(alpha,param),FALSE)[-1]
		} else if(type[3]){
			res<-rcpp$hessian(c(alpha,param),FALSE)[-1,-1]
		}
	} else {
		res<-list()
		if (type[1]){
			res$contrast<-rcpp$contrast(c(alpha,param),FALSE)
		}
		if(type[2]){
			res$gradient<-rcpp$gradient(c(alpha,param),FALSE)[-1]
		}
		if(type[3]){
			res$hessian<-rcpp$hessian(c(alpha,param),FALSE)[-1,-1]
		}
	}
	res
}

#fonction de LD2
logLik.mle.vam <-function(obj,par0,with_value=TRUE,with_gradient=FALSE,with_hessian=FALSE){
	type=c(with_value,with_gradient,with_hessian)
	rcpp <- obj$rcpp()
	## save the initial param
	if(is.null(obj$par0)) obj$par0 <- params(obj)
	## parameters stuff!
	if(missing(par0))  {
		if("par" %in% names(obj)) {
			param <- obj$par[-1]
			alpha <- obj$par[1]#LD2
		} #not the first run
		else {
			param<-params(obj)[-1] #first run
			alpha<-params(obj)[1]#LD2
		}
	} else if(is.null(par0)) {
		param<-obj$par0[-1]
		alpha<-obj$par0[1]#LD2
	} else {
		param<-par0[-1]
		alpha<-par0[1]#LD2
	}

	if(sum(type)==1) {
		if(type[1]){
			res<-rcpp$contrast(c(alpha,param),TRUE)
		} else if(type[2]){
			res<-rcpp$gradient(c(alpha,param),TRUE)
		} else if(type[3]){
			res<-rcpp$hessian(c(alpha,param),TRUE)
		}
	} else {
		res<-list()
		if (type[1]){
			res$contrast<-rcpp$contrast(c(alpha,param),TRUE)
		}
		if(type[2]){
			res$gradient<-rcpp$gradient(c(alpha,param),TRUE)
		}
		if(type[3]){
			res$hessian<-rcpp$hessian(c(alpha,param),TRUE)
		}
	}
	res
}

# alpha is not considered in the estimation!
run.mle.vam <-function(obj,par0,fixed,method=NULL,verbose=FALSE,...) {
	rcpp <- obj$rcpp()

	par0.tmp <- init.par0(obj,par0)
	param <- par0.tmp$param
	alpha <- par0.tmp$alpha

	# ## save the initial param
	# if(is.null(obj$par0)) obj$par0 <- params(obj)
	# ## parameters stuff!
	# if(missing(par0))  {
	# 	if("par" %in% names(obj)) {
	# 		param <- obj$par[-1]
	# 		alpha <- obj$par[1] #LD2
	# 	} else {#not the first run
	# 		param<-params(obj)[-1] #first run
	# 		alpha<-params(obj)[1] #LD2
	# 	}
	# } else if(is.null(par0)) {
	# 	param<-obj$par0[-1]
	# 	alpha<-obj$par0[1] #LD2
	# } else {
	# 	param<-par0[-1]
	# 	alpha<-par0[1] #LD2
	# }

	## fixed and functions stuff!
	fixed.tmp <- init.fixed.param(param,fixed)
	fixed <- fixed.tmp$fixed
	alpha_fixed <- fixed.tmp$alpha_fixed

	fn<-function(par) {
		##cat("param->");print(par);print(param[!fixed])
		param[!fixed]<-par
		#cat("param->");print(param)
		## All the commented part allows us to save the param when value is NaN
		#res<-
		-rcpp$contrast(c(alpha,param),alpha_fixed) #LD2
		# if(is.nan(res)) {
		# 	mode_param<-"contrast"
		# 	data_param<- rcpp$get_data(0)
		# 	data_param2 <- obj$data
		# 	save(param,mode_param,data_param,data_param2,file="/Users/remy/tmp/VAM/res.RData")
		# }
		# res
	}

	gr <- function(par) {
	  param[!fixed]<-par
		#cat("param2->");print(param)
		## All the commented part allows us to save the param when value is NaN
		#res <-

	  (-rcpp$gradient(c(alpha,param),alpha_fixed)[-1])[!fixed] #LD2
		# if(any(is.nan(res))) {
		# 	mode_param<-"gradient"
		# 	save(param,mode_param,file="/Users/remy/tmp/VAM/res.RData")
		# }
		# res
	}

	## optim stuff!
	if(is.null(method) || method=="fast") {
		if(length(param[!fixed])>1) param[!fixed]<-(res <- optim(param[!fixed],fn,gr,method="Ne",...))$par
		if(is.null(method)) res<-optim(param[!fixed],fn,gr,method="CG",...)
	} else {
		res<-optim(param[!fixed],fn,gr,method=method,...)
	}

  #fixed tips
  param[!fixed]<-res$par

  if(!alpha_fixed){#LD2
	## complete the scale parameter
	alpha <- rcpp$alpha_est(c(1,param)) #LD2
  } #LD2
  res$par<-c(alpha,param) #LD2

  if(verbose) print(res)

  ## save stuff
  obj$fixed <- c(alpha_fixed,fixed) #LD2
  obj$optim<-res
  obj$par<-res$par

  obj$mle.coef <- res$par
  params(obj,obj$mle.coef) #put the result in the c++ part

  ##obj$mle.coef
	obj$optim
}

## Rmk: run.mle.vam is supposed to run many times to get the best estimate!
## Here, par=NULL forces initialisation update but does not ensure that it is the best estimate.
## TODO: try to find a best strategy or many strategies...
## ... added to deal with fixed by example!

coef.mle.vam <- function(obj,par=NULL,method=NULL,verbose=FALSE,...) {
	if(is.null(obj$mle.coef) || !is.null(par)) {
		res <-run.mle.vam(obj,par,verbose=verbose,method=method,...)
		if(verbose && obj$optim$convergence>0) cat("convergence=",obj$optim$convergence,"\n",sep="")
	}
	obj$mle.coef
}

# bayesian.vam class

bayesian.vam <- function(formula,data,data.covariates) {
	if(missing(data.covariates)) data.covariates<-NULL
	self <- newEnv(bayesian.vam,formula=formula,data=data,data.covariates=data.covariates)

	PersistentRcppObject(self,new = {
		model <- parse.vam.formula(self$formula)
		self$formula <- substitute.vam.formula(model=model)
		response <- model$response
		data <- data.frame.to.list.multi.vam(self$data,response)
		self$priors <- priors.from.vam.formula(model)
		##DEBUG: print("priors");print(priors)
		##DEBUG: print("modelAV");print(model)
		self$prior.params <- sapply(self$priors,mean)
	 	self$mle.formula <- substitute.vam.formula(self$formula,self$prior.params)
		## THIS IS LESS CLEVER THAN THE NEXT LINE: print(model<-bayesian.model.to.mle.model(model,priors))
		model<-parse.vam.formula(self$mle.formula)
		##DEBUG: print("modelAP");print(model)
		if(!is.null(model$covariates)) {
			 model$covariates$data <- model.frame(model$covariates$formula,data=self$data.covariates) #ok even if data.covariates is null
		}
		self$model.parsed <- model #just to keep some info notably on covariates
		rcpp <- new(BayesianVam,model,data,self$priors)
		rcpp
	})

	self
}

run.bayesian.vam <- function(obj,par0,fixed,sigma.proposal,nb=100000,burn=10000,profile.alpha=FALSE,method=NULL,verbose=FALSE,history=FALSE,proposal='norm',...) {
	rcpp <- obj$rcpp()
	obj$history<-history
	obj$nb<-nb
	obj$burn<-burn
	obj$preplots <- NULL

	## init via mle: par0 is supposed first to be initialized by mle
	if(missing(par0)) {
		if(is.null(obj$model.parsed$covariates)) obj$mle <- mle.vam(obj$mle.formula,obj$data) else obj$mle <- mle.vam(obj$mle.formula,obj$data,obj$data.covariates)
		obj$mle.init <- TRUE
		obj$par0 <- coef(obj$mle,fixed=fixed,method=method,verbose=verbose,...)
	} else {
		obj$par0<-par0
		obj$mle.init <- FALSE
	}
	fixed.tmp <- init.fixed.param(obj$par0[-1],fixed)
	obj$fixed <- c(fixed.tmp$alpha_fixed,fixed.tmp$fixed)
	#obj$alpha_fixed <- fixed.tmp$alpha_fixed
	obj$profile_alpha<-profile.alpha
    if (fixed.tmp$alpha_fixed & profile.alpha){
    	warning("Parameter alpha can not simultaneously be fixed and optimized: it is fixed !")
    	obj$profile_alpha<-FALSE
    }
	##print(obj$mle.init)
	if(missing(sigma.proposal)) sigma.proposal <- sapply(obj$priors,sigma)
	else {
		if(length(sigma.proposal)==1) sigma.proposal <- rep(sigma.proposal,length(obj$priors))
	}
	if(length(proposal)==1) proposal <- rep(proposal,length(obj$priors))
	for(i in (1:length(obj$priors))) {
		rcpp$set_sigma(i-1,sigma.proposal[i])
	  rcpp$set_proposal(i-1,switch(proposal[i],'lnorm'=1,0))
	}
	obj$sigma_proposal<-sigma.proposal
	obj$proposal<-proposal
	if(history) {
		res <- rcpp$mcmc_history(obj$par0,nb,burn,obj$fixed,obj$profile_alpha)
		obj$nb<-res[[3+obj$profile_alpha]]
		obj$nb_proposal<-nb
		obj$par<-as.data.frame(res[1:(2+obj$profile_alpha)])
		names(obj$par) <- c("ind","estimate","alpha")[1:(2+obj$profile_alpha)]
	} else {
		obj$par <- rcpp$mcmc(obj$par0,nb,burn,obj$fixed,obj$profile_alpha)
	}
	obj$par
}

coef.bayesian.vam <- function(obj,new.run=FALSE,...) {
	if(new.run || is.null(obj$par)) run(obj,...)
	if(obj$history){
		param<-sapply(obj$profile_alpha:(length(obj$par0)-1),function(j){mean(obj$par$estimate[obj$par$ind==j])})
		if(obj$profile_alpha){
		## complete the scale parameter
			param <- c(mean(obj$par$alpha),param)
		}
	} else {
		param <- sapply(obj$par,mean)
	}
	param[as.logical(obj$fixed)]<-obj$par0[as.logical(obj$fixed)]
	param
}

hist.bayesian.vam <- function(obj,i=1,...) {
	if(is.null(obj$par)) run(obj)
	if(obj$fixed[i]){
		warning("No hist for a fixed parameter!")
	} else {
	  if(obj$history){
		if((obj$profile_alpha)&(i==1)){
			thetak<-obj$par$alpha
		} else {
			thetak<-obj$par$estimate[obj$par$ind==(i-1)]
		}
		hist(thetak,prob=TRUE,...)
		abline(v=mean(thetak),col="blue",lwd=2)
	  } else{
		hist(obj$par[[i]],prob=TRUE,...)
		abline(v=mean(obj$par[[i]]),col="blue",lwd=2)
	  }
	}
}

summary.bayesian.vam <- function(obj,alpha=0.05,new.run=FALSE,digits=4,...) {
	if(new.run || is.null(obj$par)) run(obj,...)
	res1<-obj$par0
	cat("Initial parameters",if(obj$mle.init) " (by MLE)" else "",": ",paste(signif(res1,digits=digits),collapse=", "),"\n",sep="")
	res2<-coef(obj)
	cat("(Mean) Bayesian estimates: ", paste(signif(res2,digits=digits),collapse=", "),"\n",sep="")
	if(obj$history){
		thetak<-lapply(obj$profile_alpha:(length(obj$par0)-1),function(j){obj$par$estimate[obj$par$ind==j]})
		if(obj$profile_alpha){thetak<-c(list(obj$par$alpha),thetak)}
	} else {
		thetak<-obj$par
	}
	sd<-sapply(thetak,sd)
	sd[as.logical(obj$fixed)]<-0
	cat("(SD) Bayesian estimates: ",paste(signif(sd,digits=digits),collapse=", "),"\n",sep="")
	res4<-sapply(thetak,function(x){quantile(x,probs=alpha/2)})
	cat("(", alpha/2,"-Quantile) Bayesian estimates: ",paste(signif(res4,digits=digits),collapse=", "),"\n",sep="")
	res5<-sapply(thetak,function(x){quantile(x,probs=1-alpha/2)})
	cat("(", 1-alpha/2,"-Quantile) Bayesian estimates: ",paste(signif(res5,digits=digits),collapse=", "),"\n",sep="")
	res6<-sapply(thetak,length)
	cat("(Number) Bayesian estimates: ",paste(res6,collapse=", "),"\n",sep="")
	res7<-sapply(thetak,length)/(obj$nb-obj$burn)
	cat("Metropolis-Hasting acceptation rates: ",paste(signif(res7,digits=digits),collapse=", "),"\n",sep="")
	res<-data.frame(res1,res2,sd,res4,res5,res6,res7)
	names(res)<-c(paste("Init",if(obj$mle.init) "(MLE)" else "",sep=""),"Mean","SD",paste(alpha/2,"-Quantile",sep=""),paste(1-alpha/2,"-Quantile",sep=""),"Number","Accept_Rate")
	invisible(res)
}


# for both sim and mle

parse.vam.formula <- function(formula) {
	## Needs to have this envir to evaluate params (otherwise, beta was found in baseenv() first before globalenv() for example!)
	envir.eval <- parent.frame(4)
	eval.vam <- function(e) eval(e,envir.eval)
	if(formula[[1]] != as.name("~")) stop("Argument has to be a formula")
	if(length(formula) == 2) {
		response <- NULL
		cm <- formula[[2]]
	} else {
		tmp <- formula[[2]]
		## simplify parenthesis
		while(tmp[[1]] == as.name("(")) tmp <- tmp[[2]]
		if(tmp[[1]] != as.name("&") && length(tmp) != 3) stop("Left part of formula of the form 'Time & Type'!")
		if(length(tmp[[2]])==3 && tmp[[2]][[1]]==as.name("&")) {
			response <- c(as.character(tmp[[2]][[2]]),as.character(tmp[[2]][[3]]),as.character(tmp[[3]]))
		} else response <- c(as.character(tmp[[2]]),as.character(tmp[[3]]))
		cm <- formula[[3]]
	}
	## simplify parenthesis
	while(cm[[1]] == as.name("(")) cm <- cm[[2]]
	pms <- list()
	policy <- NULL
	if(there.is.pm <- (cm[[1]] == as.name("&"))) { # there is a PM part
		pm <- cm[[3]]
		cm <- cm[[2]]
		# deal with PM part
		if(pm[[1]] == as.name("(")) {
			pm <- pm[[2]]
			if(pm[[1]] != as.name("|")) {
				## Case: No maintenance policy
				#stop("Need a policy to manage Preventive Maintenance")
				policy <- NULL
			} else {
				policy <- pm[[3]]
				if(policy[[1]] == as.name("*")) {
					## Case: Composition of maintenance policies
					# recursive function to detect maintenance policies
					run.over.policies<-function(p) {
						if(p[[1]] == as.name("*")) {
							run.over.policies(p[[2]])
							run.over.policies(p[[3]])
						} else if(is.name(p[[1]])) {
							p[[1]] <- as.name(paste0(as.character(p[[1]]),".maintenance.policy"))
							policies <<- c(policies,list(p))
						}
					}
					## init policies and
					policies <- list()
					run.over.policies(policy)
					## print(policies)
					policy <- policies ##[[1]]
				} else if(is.name(policy[[1]])) {
					## Case: One maintenance policy
					policy[[1]] <- as.name(paste0(as.character(policy[[1]]),".maintenance.policy"))
				}

				# PMs
				pm <- pm[[2]]
			}
			# parser for pm
			parse.pm <- function(pm) {
				if(is.name(pm[[1]])) {
					pm[[1]] <- as.name(paste0(as.character(pm[[1]]),".va.model"))
				}
				pm
			}
			cpt.pms <- 0
			while(pm[[1]] == as.name("+") ) {
				if(length(pm) == 3) {
					pms[[cpt.pms <- cpt.pms + 1]] <- parse.pm(pm[[3]])
					pm <- pm[[2]]
				}
			}
			pms[[cpt.pms <- cpt.pms + 1]] <- parse.pm(pm)
		} else stop("Need parenthesis around the Preventive Maintenance terms")
	}
	# deal with CM PART
	cms <- list()

	# parser for cm
	parse.cm <- function(cm) {
		# print(there.is.pm)
		# print(cm)
		if(there.is.pm) {
			if(cm[[1]] == as.name("(")) cm <- cm[[2]]
			else stop("CM needs a family!")
		}
		if(cm[[1]] != as.name("|")) stop("CM needs a family!")
		family <- cm[[3]]
		if(is.name(family[[1]])) {
			family[[1]] <- as.name(paste0(as.character(family[[1]]),".family.cm"))
		}
		cm <- cm[[2]]
		if(is.name(cm[[1]])) {
			cm[[1]] <- as.name(paste0(as.character(cm[[1]]),".va.model"))
		}
		list(model=cm,family=family)
	}
	cpt.cms <- 0
	while( cm[[1]] == as.name("+") ) {
		if(length(cm) == 3) {
			cms[[cpt.cms <- cpt.cms + 1]] <- parse.cm(cm[[3]])
			cm <- cm[[2]]
		}
	}
	cms[[cpt.cms <- cpt.cms + 1]] <- parse.cm(cm)

	## Parse covariates
	parse.covariates <- function(expr) {
		form<-list()
		params <- c()
		add_term <- function(term,sign) {
			##print(term)
			if(term[[1]]==as.name("*")) {
				form <<- c(as.character(term[[3]]),form)
				if(sign == as.name("-")) {
				 	if(!(is.numeric(eval(term[[2]])))) stop("Only + is admitted between covariates definition in Bayes case.") else param_expr <- eval.vam(parse(text=paste0(sign,as.character(eval(term[[2]])))))
				} else param_expr <- as.vector(eval.vam(term[[2]]))
				params<<- c(param_expr,params)
			}
			##print(list(form=form,params=params))
		}
		while(expr[[1]]==as.name("+") || expr[[1]]==as.name("-")) {
			add_term(expr[[3]],as.character(expr[[1]]))
			expr <- expr[[2]]
		}
		add_term(expr,"")
		list(formula=eval(parse(text=paste0("~",paste(form,collapse="+")))),params=params)
	}

	convert.family <- function(fam) {
		# eval.vam is here to evaluate the value if it is a symbol!
		# We want to detect if there is covariates or not. Normally the operator | delimitating covariates has priority, expect possibly in Baysian case.
		if(has.covariates <- (length(fam[[length(fam)]])>1 && fam[[length(fam)]][[1]] == as.name("|"))) {
			covariates_expr <- fam[[length(fam)]][[3]]
			fam[[length(fam)]] <- fam[[length(fam)]][[2]] # first argument of last terms becomes last argument of family
		} else {
		#In Bayesian case the ~ operator of the description of the prior distribution of the last argument of the family can possibly has priority on the operator | delimitating covariates
			if(length(fam[[length(fam)]])>1 && fam[[length(fam)]][[1]] == as.name("~") && length(fam[[length(fam)]][[2]])>1 && fam[[length(fam)]][[2]][[1]] == as.name("|")){
				has.covariates <- TRUE
				covariates_expr <- fam[[length(fam)]][[2]][[3]]
				fam[[length(fam)]][[2]] <- fam[[length(fam)]][[2]][[2]]
			}
		}

		res<-list(
				name=as.character(fam[[1]]),
				params=sapply(fam[-1],function(e) as.vector(eval.vam(e)))
				## instead of : params=sapply(cm$family[-1],as.vector)
				## which does not work with negative real since element of tmp[-1] interpreted as call!
		)
		if(has.covariates) {
			res$covariates <- parse.covariates(covariates_expr)
		}
		return(res)
	}
	convert.pm <- function(pm) {
		n_pip<-c()
		if(length(pm)>1){
			for(i in 2:length(pm)){
				if((length(pm[[i]])==3)&&(pm[[i]][[1]]==as.name("|"))) {
					n_pip<-c(n_pip,i)
				}
			}
		}
		if(length(n_pip)==0) {
			list(
				name=as.character(pm[[1]]),
				params=as.vector(if(length(pm)==1) numeric(0) else sapply(pm[2:length(pm)],function(e) as.vector(eval.vam(e))))
			)
		} else if(length(n_pip)==1) {
			if(n_pip<(length(pm)-1)) {
				stop("Maximum two arguments after a | in a maintenance effect!")
			} else if(n_pip == length(pm)) {
				if( typeof(tryCatch( as.double(eval.vam(pm[[length(pm)]][[3]])) ,error=function(e){FALSE},finally=function(e){TRUE}))!="logical"){
	  				if((round(eval.vam(pm[[length(pm)]][[3]])) != eval.vam(pm[[length(pm)]][[3]]))||(round(eval.vam(pm[[length(pm)]][[3]]))<=0)) {
	  					stop("Memory argument of a maintenance model has to be a strictly positive integer!")
	  				} else {
	  	  				list(
									name=as.character(pm[[1]]),
									params=as.vector(if(length(pm)==2) as.vector(eval.vam(pm[[2]][[2]])) else c(sapply(pm[2:(length(pm)-1)],function(e) as.vector(eval.vam(e))),as.vector(eval.vam(pm[[length(pm)]][[2]])))),
									m=as.integer(eval.vam(pm[[length(pm)]][[3]]))
		  					)
						}
	  			} else {
	  				list(
							name=as.character(pm[[1]]),
							params=as.vector(if(length(pm)==2) as.vector(eval.vam(pm[[2]][[2]])) else c(sapply(pm[2:(length(pm)-1)],function(e) as.vector(eval.vam(e))),as.vector(eval.vam(pm[[length(pm)]][[2]])))),
							extra=as.character(pm[[length(pm)]][[3]])
						)
	  			}
			} else {
				if( typeof(tryCatch( as.double(eval.vam(pm[[length(pm)-1]][[3]])) ,error=function(e){FALSE},finally=function(e){TRUE}))!="logical"){
  				if((round(eval.vam(pm[[length(pm)-1]][[3]]))!=eval.vam(pm[[length(pm)-1]][[3]]))||(round(eval.vam(pm[[length(pm)-1]][[3]]))<0)) {
  					stop("Memory argument of a maintenance model has to be a positive integer!")
  				} else {
  	  				list(
								name=as.character(pm[[1]]),
								params=as.vector(if(length(pm)==3) as.vector(eval.vam(pm[[2]][[2]])) else c(sapply(pm[2:(length(pm)-2)],function(e) as.vector(eval.vam(e))),as.vector(eval.vam(pm[[length(pm)-1]][[2]])))),
								m=as.integer(eval.vam(pm[[length(pm)-1]][[3]])),
								extra=as.character(pm[[length(pm)]])
	  					)
					}
				} else {
					if( typeof(tryCatch( as.double(eval.vam(pm[[length(pm)]])) ,error=function(e){FALSE},finally=function(e){TRUE}))=="logical"){
						stop("At least one of the two argument of maintenance model after a | must be a memory that is to say a non negative positive integer!")
					} else {
						if((round(eval.vam(pm[[length(pm)]]))!=eval.vam(pm[[length(pm)]]))||(round(eval.vam(pm[[length(pm)]]))<0)) {
	  						stop("Memory argument of a maintenance model has to be a positive integer!")
	  					} else {
	  	  					list(
								name=as.character(pm[[1]]),
								params=as.vector(if(length(pm)==3) as.vector(eval.vam(pm[[2]][[2]])) else c(sapply(pm[2:(length(pm)-2)],function(e) as.vector(eval.vam(e))),as.vector(eval.vam(pm[[length(pm)-1]][[2]])))),
								m=as.integer(eval.vam(pm[[length(pm)]])),
								extra=as.character(pm[[length(pm)-1]][[3]])
		  					)
	  	  				}
					}

				}
			}
		} else {
			stop("Maximum one | in a maintenance effect!")
		}



	 #  if((length(pm)==1)||(pm[[length(pm)]][[1]]!=as.name("|"))) {
		# list(
		# 	name=as.character(pm[[1]]),
		# 	params=as.vector(if(length(pm)==1) numeric(0) else sapply(pm[2:length(pm)],function(e) as.vector(eval(e))))
		# )
	 #  } else if ( typeof(tryCatch( as.double(eval(pm[[length(pm)]][[3]])) ,error=function(e){FALSE},finally=function(e){TRUE}))!="logical"){
	 #  	if((round(eval(pm[[length(pm)]][[3]]))!=eval(pm[[length(pm)]][[3]]))||(round(eval(pm[[length(pm)]][[3]]))<0)) {
	 #  		stop("Memory argument of a maintenance model has to be a positive integer!")
	 #  	} else {
	 #  	  list(
		# 	name=as.character(pm[[1]]),
		# 	params=as.vector(if(length(pm)==2) pm[[2]][[2]] else c(sapply(pm[2:(length(pm)-1)],function(e) as.vector(eval(e))),as.vector(eval(pm[[length(pm)]][[2]])))),
		# 	m=as.integer(eval(pm[[length(pm)]][[3]]))
		#   )
		# }
	 #  }	else {
	 #  	list(
		# 	name=as.character(pm[[1]]),
		# 	params=as.vector(if(length(pm)==2) pm[[2]][[2]] else c(sapply(pm[2:(length(pm)-1)],function(e) as.vector(eval(e))),as.vector(eval(pm[[length(pm)]][[2]])))),
		# 	extra=as.character(pm[[length(pm)]][[3]])
		# )
	 #  }
	}
	convert.mp <- function(mp) {#maintenance policy
		if(is.null(mp)) list(name="None")
		else if(is.list(mp)) {
			list(name="MaintenancePolicyList",policies=lapply(mp,convert.mp))
		}
		else {

			## The function defining the maintenance policy
			## (registered in maintenance-policy-register.R or in any other R file)
			mp.fct <- eval.vam(mp[[1]])
			## params used in the call mp
			pars <- as.list(match.call(mp.fct,mp))[-1]

			## Default values are then automatically completed using declaration of maintenance policy
			pars.default <- (as.list(mp.fct)->tmp)[-length(tmp)]
			pars.default <- pars.default[sapply(pars.default,function(e) nchar(as.character(e)))!=0]
			for(e in names(pars.default)) if(is.null(pars[[e]])) pars[[e]] <- pars.default[[e]]

			##print(list(pars=pars))

			## deal with model parameter which has a specific treatment
			mod <- NULL
			if(!is.null(pars[["model"]])) {
				mod <- rcpp(eval.vam(pars[["model"]]))
				pars[["model"]] <- NULL
			}

			res <- list(
				name=as.character(mp[[1]]),
				params=lapply(pars,eval.vam)
			)
			res[["with.model"]] <- !is.null(mod)
			if(!is.null(mod)) res[["model"]] <- mod
			res
		}
	}


	res<-list(
		response=response,
		models=c(list(convert.pm(cms[[1]]$model)),lapply(pms[rev(seq(pms))],convert.pm)),
		family=convert.family(cms[[1]]$family),
		pm.policy=convert.mp(policy)
	)
	## covariates direct acces
	res$covariates <- res$family$covariates
	res$family$covariates <- NULL

	res$max_memory <- max(1,unlist(sapply(res$models,function(e) e$m)),na.rm=TRUE)
	res

}

# use substitute coef in vam formula
substitute.vam.formula <- function(formula,coef,model) {
	if(missing(model)) model <- parse.vam.formula(formula)
	if(missing(coef)) {
		coef <- c(model$family$params,sapply(model$models,function(m) m$params))
		if(!is.null(model$covariates)) coef <- c(coef,model$covariates$params)
	}
	if(!is.null(model$covariates)) {
			nb_paramsCovariates <- length(model$covariates$params)
		} else {
			nb_paramsCovariates <- 0
	}

	coef<-unlist(coef)

	nb_paramsFamily <- length(model$family$params)
	nb_paramsCM <- length(model$models[[1]]$params)
	nb_paramsPM <- sapply(model$models[-1],function(m) length(m$params))
	form <- paste0(
						paste(model$response,collapse=" & "),
						"~ (",
							strsplit(model$models[[1]]$name,"\\.")[[1]][1],
							"(",
							if(nb_paramsCM>0) paste(coef[nb_paramsFamily+(1:nb_paramsCM)],collapse=",") else "",
							if(!is.null(model$models[[1]]$m) || !is.null(model$models[[1]]$extra)) {
								extra <- c()
								if(!is.null(model$models[[1]]$extra)) extra <- c(extra,model$models[[1]]$extra)
								if(!is.null(model$models[[1]]$m)) extra <- c(extra,model$models[[1]]$m)
								paste0("|",paste(extra,collapse=","))
							} else "",
							")",
						"|",
							strsplit(model$family$name,"\\.")[[1]][1],
							"(",
							 paste(coef[1:nb_paramsFamily],collapse=","),
							 if(!is.null(model$covariates)) {
								 # unlist(nb_paramsPM) since nb_paramsPM is list() when no PM
								 tmp<-coef[nb_paramsFamily + nb_paramsCM + sum(unlist(nb_paramsPM)) + (1:nb_paramsCovariates)]
								 if(is.list(tmp)){##Bayesian case
								 	 paste0("| (",
									   paste(tmp,all.vars(model$covariates$formula),sep=") * ",collapse=" + (")
								   )
								 } else {
								 	 tmp[tmp<0] <- paste0("(",tmp[tmp<0],")")
								 	 paste0("|",
								 		 paste(tmp,all.vars(model$covariates$formula),sep=" * ",collapse=" + ")
								 	 )
								 }
							 } else "",
							")",
						")"
					)
	if(length(model$models)>1) {
		pms <- model$models[-1]
		form <- paste0(form,
							" & (",
							paste(
								sapply(seq(pms),function(i) {
									paste0(
										strsplit(pms[[i]]$name,"\\.")[[1]][1],
										"(",
										if(nb_paramsPM[i]>0) paste(coef[nb_paramsFamily+nb_paramsCM+ifelse(i>1,sum(nb_paramsPM[1:(i-1)]),0)+(1:nb_paramsPM[i])],collapse=",") else "",
										if(!is.null(pms[[i]]$m) || !is.null(pms[[i]]$extra)) {
											extra <- c()
											if(!is.null(pms[[i]]$extra)) extra <- c(extra,pms[[i]]$extra)
											if(!is.null(pms[[i]]$m)) extra <- c(extra,pms[[i]]$m)
											paste0("|",paste(extra,collapse=","))
										} else "",
										")"
									)
								}),
								collapse=" + "
							),
							")"
						)
	}
	form <- eval(parse(text=form),envir=globalenv())
	form
}

priors.from.vam.formula <- function(model) {
	flatten.params <- c(model$family$params,unlist(sapply(model$models,function(e) e$params)))
	##if(!is.null(model$family$covariates)) flatten.params <- c(flatten.params,mode$family$covariates$params) ##Strange
	if(!is.null(model$covariates)) flatten.params <- c(flatten.params,model$covariates$params)
	if(all(sapply(flatten.params,class) == "formula") ) {
		prior.families <- c("B","Beta","U","Unif","G","Gamma","Norm","N","NonInform","NInf","LNorm","LogNorm","LN")
		## clear "|" expression
		flatten.params <- lapply(flatten.params,function(e) if(!as.character(e[[2]][[1]]) %in% prior.families) e[[2]][[2]] else e[[2]])
		## transform to list
		parse.prior <- function(prior) {
				## declare here all the priors
				Beta <- B <- Be <- function(a,b) list(name="Beta.prior",params=c(a,b))
				Gamma <- G <- function(a,s) list(name="Gamma.prior",params=c(a,s))
				Unif <- U <- function(a=0,b=1) list(name="Unif.prior",params=c(a,b))
				Norm <- N <- function(m=0,s=1) list(name="Norm.prior",params=c(m,s))
				LNorm <- LogNorm <- LN <- function(m=0,s=1) list(name="LNorm.prior",params=c(m,s))
				NonInform <- NInf <- NI <- function(init=1,init_sigma=1) list(name="NonInform.prior",params=c(init,init_sigma))
				res <- eval(prior) ## TODO or NOT TODO: eval(prior,parent.frame())
				class(res) <- res$name #to be accessible as a class in R
				res
		}
		flatten.params <- lapply(flatten.params,parse.prior)
		return(flatten.params)
	} else {
		warning("Not a formula for bayesian.vam object!")
		NULL # means not ok!
	}
}

### FOUND A BETTER WAY: see bayesian.vam
## substitute prior with mean prior to be used inside mle.vam as initialization of run.bayesian.vam
# bayesian.model.to.mle.model <- function(model,priors) {
# 	print(priors)
# 	if(!is.null(priors)) {
# 		k<-0
# 		for(i in 1:length(model$family$params)) {
# 			##DEBUG: print("prior");print(k+1);print(priors[[k+1]]);print(update(priors[[k+1]]))
# 			model$family$params[[i]] <-  update(priors[[k<-k+1]])
# 		 }
# 		model$family$params <- unlist(model$family$params)
# 		for(j in 1:length(model$models)) {
# 			for(i in 1:length(model$models[[j]]$params)) {
# 				##DEBUG: print("prior");print(k+1);print(priors[[k+1]]);print(update(priors[[k+1]]))
# 				model$models[[j]]$params[[i]] <- update(priors[[k<-k+1]])
# 			}
# 			model$models[[j]]$params <- unlist(model$models[[j]]$params)
# 		}
# 		return(model)
# 	}
# 	return(NULL)
# }

## RMK: these 2 init following functions were only used for mle but are useful now for Bayesian too!
init.par0 <- function(obj,par0) {
	## save the initial param
	if(is.null(obj$par0)) obj$par0 <- params(obj)
	## parameters stuff!
	if(missing(par0))  {
		if("par" %in% names(obj)) {
			param <- obj$par[-1]
			alpha <- obj$par[1] #LD2
		} else {#not the first run
			param<-params(obj)[-1] #first run
			alpha<-params(obj)[1] #LD2
		}
	} else if(is.null(par0)) {
		param<-obj$par0[-1]
		alpha<-obj$par0[1] #LD2
	} else {
		param<-par0[-1]
		alpha<-par0[1] #LD2
	}
	list(param=param,alpha=alpha)
}

init.fixed.param <- function(param,fixed) {
	## fixed and functions stuff!
	if(missing(fixed)) {
		fixed<-rep(FALSE,length(param))
		alpha_fixed<-FALSE #LD2
	} else if(is.numeric(fixed)) {
		fixedInd<-fixed
		fixed<-rep(FALSE,length(param))
		fixed[fixedInd-1]<-TRUE #LD2: ajout du -1
		alpha_fixed<-sum(fixedInd==1) #OR ( 1 %in% fixedInd which is more expressive)
	} else {#LD2
		alpha_fixed<-fixed[1] #LD2
		fixed<-fixed[-1] #LD2
	}#LD2
	list(fixed=fixed,alpha_fixed=alpha_fixed)
}
rcqls/VAM documentation built on Jan. 14, 2024, 9:07 p.m.