library(knitr) opts_chunk$set(out.extra='style="display:block; margin: auto"' #, fig.align="center" , fig.width=4.3, fig.height=3.2, dev.args=list(pointsize=10) ) knit_hooks$set(spar = function(before, options, envir) { if (before){ par( las=1 ) #also y axis labels horizontal par(mar=c(2.0,3.3,0,0)+0.3 ) #margins par(tck=0.02 ) #axe-tick length inside plots par(mgp=c(1.1,0.2,0) ) #positioning of axis title, axis labels, axis } })
This vignette details the sampling of different subsets, i.e. blocks, of the parameter vector by different log-Density functions. The background is described in Wutzler & Carvalhais 2014 (doi: 10.1002/2014jg002650).
The used model and snythethic observations data are provided with data set twTwoDenEx1
.
library(twDEMC) data(twTwoDenEx1) set.seed(0815) # for reproducable results
twTwoDenEx1$fModel = function( theta ##<< model parameters a and b , xSparse ##<< numeric vector of Sparse input/output relationship , xRich ##<< numeric vector of rich input/output relationship , thresholdCovar=0 ##<< model structural deficiency ){ list( y1 = as.numeric(theta[1]*xSparse + theta[2]*mean(xRich)/10) ,y2 = as.numeric(theta[1]*xSparse[1] + theta[2]*pmax(0,xRich-thresholdCovar) ) ) }
To give some meaning to this artificial example, model output y1
may represents a sparse (n=10) series of annual observations. It uses all covariates xSparse
but is based solely on a long-term average of fast changing covariate xRich
. Model output y2
may represent a short measurement campaign, during which much information was collected (n=1000). During this campaign, the output varies with covariate xRich
but xSparse
is assumed to be constant during the campaign.
The synthetic observations in twTwoDenEx1
have been generated with parameter thresholdCovar=0.3
. However, in order to demonstrate some effects of using a slighly biased model, this parameter is assumed to be zero during the model inversion.
#thresholdCovar = 0.3 # the true value thresholdCovar = 0 # the effective biased model that glosses over this threshold
The first parameter, a
(theta[1]
), is more closely related to one observation stream y1
and the other model parameter, b
(theta[2]
), is more closely related to the other observation stream y2
. Here, the two parameters will be calibrated by different observation data streams.
The first density is based on the misfit to the sparse observations y1
only. Similarly, the second density is based on the rich observations y2
only.
denSparse <- function( theta, twTwoDenEx=twTwoDenEx1, ... ){ pred <- twTwoDenEx$fModel(theta, xSparse=twTwoDenEx$xSparse, xRich=twTwoDenEx$xRich, ...) misfit <- twTwoDenEx$obs$y1 - pred$y1 structure( -1/2 * sum((misfit/twTwoDenEx$sdObs$y1)^2), names='y1' ) } denRich <- function( theta, twTwoDenEx=twTwoDenEx1, ... ){ pred <- twTwoDenEx$fModel(theta, xSparse=twTwoDenEx$xSparse, xRich=twTwoDenEx$xRich, ...) misfit <- twTwoDenEx$obs$y2 - pred$y2 structure( -1/2 * sum((misfit/twTwoDenEx$sdObs$y2)^2), names='y2' ) }
The description of used densities to in argument dInfos
of twDEMCSA
has to include two entries.
dInfos=list( dSparse=list(fLogDen=denSparse, argsFLogDen=list(twTwoDenEx=twTwoDenEx1, thresholdCovar=thresholdCovar)), dRich=list(fLogDen=denRich, argsFLogDen=list(twTwoDenEx=twTwoDenEx1, thresholdCovar=thresholdCovar)) )
Argument blocks
of function twDEMCSA
describes which parameter are updated by which block-update functions. By default, the Metropolis-rule is based on a single density for all parameters. The following specification tells to use Metropolis-update rule but with different densities for the two parameter blocks (each consisting of a single parameter). The identifiers given by dInfoPos must correspond to list entries in log-Density specification dInfos
.
blocks = list( a=list(compPos="a", dInfoPos="dSparse"), b=list(compPos="b", dInfoPos="dRich") )
With these specifications the simulated annealing and the sampling of the converged chains at target temperature can be performed, plot the progress, and assess the properties of the posterior parameter from the sample.
resBlock <- resBlock0 <- twDEMCSA( theta=twTwoDenEx1$thetaTrue, covarTheta=diag((twTwoDenEx1$thetaTrue*0.3)^2), dInfos=list( dSparse=list(fLogDen=denSparse, argsFLogDen=list(twTwoDenEx=twTwoDenEx1, thresholdCovar=thresholdCovar)), dRich=list(fLogDen=denRich, argsFLogDen=list(twTwoDenEx=twTwoDenEx1, thresholdCovar=thresholdCovar)) ), blocks = list( a=list(compPos="a", dInfoPos="dSparse"), b=list(compPos="b", dInfoPos="dRich") ), nObs = c( y1=length(twTwoDenEx1$obs$y1), y2=length(twTwoDenEx1$obs$y2) ), nGen=2048 ) resConv <- twDEMCBlock( resBlock0, nGen=ceiling(256 * resBlock$thin / getNChains(resBlock)), extendRun=FALSE )
plot(as.mcmc.list(resBlock), smooth=FALSE) # plot(as.mcmc.list(resConv), smooth=FALSE) ss <- stackChains(resConv) summary(ss)
The two data streams are conflicting giving the somewhat biased model. Therefore, the two pdfs dSparse
and dRich
peak at different locations in parameter space. The color code of the following figure ranks the samples by different criteria.
oldPar <- par(mfrow=c(1,5), par(mar=c(2.0,3.3,2.0,0)+0.3 ) ) colh <- heat.colors(nrow(ss)) plot( b ~ a, data=as.data.frame(ss[ rev(order(ss[,"dSparse"])),]) , col=colh, main="rank dSparse" ) plot( b ~ a, data=as.data.frame(ss[ rev(order(ss[,"dRich"])),]) , col=colh, main="rank dRich" ) plot( b ~ a, data=as.data.frame(ss[ rev(order(rowSums(ss[,c("dSparse","dRich")]))),]) , col=colh, main="rank sum" ) plot( b ~ a, data=as.data.frame(ss[ rev(order(rowSums(apply(ss[,c("dSparse","dRich")],2,rank)) )),]) , col=colh, main="sum ranks" ) plot( b ~ a, data=as.data.frame(ss[ orderLogDen(ss),]) , col=colh, main="min ranks" )
The right plot shows that the sparse data stream effectively constrains parameter a. The second plot shows that the rich data stream constrains a combination of both parameters with best parameters having very low values of a.
The third plot shows that the influcence of the sparse data stream is negligible in the sum of the two densities.
Only with the last two plots, the color code roughly matches the density of the sample.
Instead of ranking the sum of the densities, the sum or the minimum of both density ranks is a good criterion for determining the best parameters based on given set of densities. The last criterion is implemented in function orderLogDen
.
Note that the forward model twTwoDenEx$fModel
is called from both blocks in respective log-Density-functions denSparse
and denRich
. Often, running the forward model is computationally expensive, so it would be wise to save unnecessary runs.
E.g. when block b changes parameter b, the log-Density of block a needs to be recalculated with updated parameter b. However, the corresponding model forward model run has already been performed in block b. This information should be transferred to block a.
The package provides a way of transferring intermediate results that are in common between blocks. Blocks can communicate the intermediate results by attaching an attribute intermediate
to its return value. This will then be provided to other block update functions with argument intermediate
.
If several blocks share an intermediate result, care must be taken, that the intermediate is updated, each time when parameters for the forward run have changed. E.g., the Metropolis update function will provide a NULL intermediate to the density function after proposing a new set of parameters.
The updated log-Density function of the parameters and the updated residual function read:
denSparseInt <- function( theta, intermediate=list(), twTwoDenEx=twTwoDenEx1, ... ){ if( !length(intermediate) ){ intermediate <- twTwoDenEx$fModel(theta, xSparse=twTwoDenEx$xSparse, xRich=twTwoDenEx$xRich, ...) } pred <- intermediate misfit <- twTwoDenEx$obs$y1 - pred$y1 structure( -1/2 * sum((misfit/twTwoDenEx$sdObs$y1)^2), names='y1', intermediate=intermediate ) } denRichInt <- function( theta, intermediate=list(), twTwoDenEx=twTwoDenEx1, ... ){ if( !length(intermediate) ){ intermediate <- twTwoDenEx$fModel(theta, xSparse=twTwoDenEx$xSparse, xRich=twTwoDenEx$xRich, ...) } pred <- intermediate misfit <- twTwoDenEx$obs$y2 - pred$y2 structure( -1/2 * sum((misfit/twTwoDenEx$sdObs$y2)^2), names='y2', intermediate=intermediate ) }
Note the additional function argument intermediate
, and the attachment as an attribute to the result of the function.
In order to tell twDEMCSA
which intermediates need to be exchanged, specify the same entry intermediateId
in the blocks descriptions.
resBlock <- resBlockInt <- twDEMCSA( theta=twTwoDenEx1$thetaTrue, covarTheta=diag((twTwoDenEx1$thetaTrue*0.3)^2), dInfos=list( dSparse=list(fLogDen=denSparseInt, argsFLogDen=list(twTwoDenEx=twTwoDenEx1, thresholdCovar=thresholdCovar)), dRich=list(fLogDen=denRichInt, argsFLogDen=list(twTwoDenEx=twTwoDenEx1, thresholdCovar=thresholdCovar)) ), blocks = list( a=list(compPos="a", dInfoPos="dSparse", intermediateId="resFModel"), b=list(compPos="b", dInfoPos="dRich", intermediateId="resFModel") ), nObs = c( y1=length(twTwoDenEx1$obs$y1), y2=length(twTwoDenEx1$obs$y2) ), nGen=2048 ) resConvInt <- twDEMCBlock( resBlockInt, extendRun=FALSE, nGen=ceiling(256 * resBlock$thin / getNChains(resBlock)))
During the sampling of the converged chains, only about 2000 of the 3000 calls to the forward functions are required compared to not reusing the intermediate result.
ssInt <- stackChains(resConvInt) summary(ssInt)
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.