Nothing
# ' @importFrom MCMCpack rinvgamma # ported to this package because of dependency problems (invGamma.R)
#' @importFrom mvtnorm rmvnorm
#' @export
compileDiscrepancyBlocksForStream <- function(
### compile the intermediates and blocks for GP model error of one stream
x ##<< locations of observations
,obs ##<< observations
,sd2Obs ##<< variance of observation uncertainty
, predProcSpec ##<< named list of one intermediate specification for model prediction
, predProcSpecEntry=NULL ##<< if predProcSpec returns a list, can provide a name or an index to pick predictions for this data stream
, streamSuffix="" ##<< suffix applied to parameters, and specifications to distinguish data streams
,priorGammaPsiPars = getGammaParsFromMeanAndVariance(1,0.2) ##<< prior for variance of model discrepancy as a factor of average observation uncertainty
,priorInvGammaSd2DiscrPars = getInvGammaParsFromMeanAndMode( 20, 0.05 ) ##<< prior for corellation length
#,priorInvGammaSd2DiscrPars = c( alpha=0.001, beta=0.001 ) ##<< prior for signal variance
,minSd2DiscrFraction = 0 #1/20 ##<< minimum of uncertainty of GP training data as a fraction of model error variance, deprecated: was used to prevent inflating signal variance by
,xPred=numeric(0) ##<< locations at which to sample model discrepancies, mapped to paramter vector md_1 to md_<nPred>
,isSigmaBlockReturned=TRUE ##<< set to FALSE to omit block for updating sigma_streamSuffix
#,isUsingExpectedDelta=TRUE ##<< if TRUE, then deltaO is set to expected value instead of drawn from distribution for performance
,isUsingDataBasedSigma=FALSE ##<< set to TRUE to use an independent Likelihood estimate of signal variance in the calculation of discrepancies, instead of the current signal variance. This reduces performance.
,weightPSd2Discr=length(x) ##<< the weight for signal variance in the likelihood of correlation length psi
,sd2ObsFactor=1 ##<< if sampled discrepancy goes to zero, use this factor to decrease the observation uncertainty during estimation of model discrepancy
,nSupportingSet=1L ##<< integer scalar: number of sets of supporting conditions to examine
,isMarkUpdatedPsiBlock=TRUE ##<< if TRUE, block is marked as updated when not accepted, in order to trigger recomputation of supporting locations that involve some randomness
){
if( !is.list(predProcSpec) || (length(names(predProcSpec)) != 1L) || !is(predProcSpec[[1]],"IntermediateSpecification")) stopDemac(
"predProcSpec must be a named list with entry of class IntermediateSpecification.")
if( length(x) != length(obs) ) stopDemac("locations x and observations obs must be of the same length.")
GPLimits <- .computeGPLimits(x)
# names of used intermediates and parameters including suffix
predProcName <- names(predProcSpec)[1]
iOName <- paste("iO",streamSuffix,sep="_")
deltaAName <- paste("deltaA",streamSuffix,sep="_")
logPsiVar <- paste("logPsi",streamSuffix,sep="_")
logSd2DiscrVar <- if( isSigmaBlockReturned ){
paste("logSd2Discr",streamSuffix,sep="_")
} else {
"logSd2Discr"
}
ksiVars <- if(length(xPred)) paste("ksi",seq_along(xPred),streamSuffix,sep="_") else character(0)
#deltaPredVars <- paste("md",seq_along(xPred),streamSuffix,sep="_")
meanSd2ObsClosure <- mean(sd2Obs) # average observation variance
# training locations based on psi
xPredVal <- force(xPred) # force evaluation to assign to closure
xVal <- force(x)
iOSpec <- intermediateSpec(
function(theta, intermediates ){
psi=exp(theta[logPsiVar])
if( !is.finite(psi) && is.finite(theta[logPsiVar]) ) stopDemacInvalidChainState("iOSpec: given logPsi yield infinite psi.")
.computeGPTrainingLocations(psi=psi, x=xVal, minXSpacing=GPLimits$minXSpacing, maxXSpacing=GPLimits$maxXSpacing, xPred=xPredVal
,nSet = nSupportingSet
)
}
,argsUpdateFunction=list()
,parameters = c(logPsiVar)
,intermediates = character(0) # the default
)
deltaASpec <- intermediateSpec(
function(theta, intermediates, xVal, obsVal, sd2ObsVal, minSd2DiscrFractionVal, priorInvGammaParsVal ){
predProc <- intermediates[[predProcName]]
if( length(attr(predProc,"problems")) ) return( structure(list(NA),problems=attr(predProc,"problems")))
if( length(predProcSpecEntry) ){
predProc <- predProc[[predProcSpecEntry]]
}
if( !length(predProc) ) return(structure( list(NA), problems=paste("deltaASpec: got empty prediction vector")))
.computeModelDiscrepancies(
psi=exp(theta[logPsiVar])
, sd2DiscrFac=exp(theta[logSd2DiscrVar])
, iORes=intermediates[[iOName]]
, predProc=predProc
, x=xVal, obs=obsVal, sd2Obs=sd2ObsVal
, meanSd2Obs=meanSd2ObsClosure
, xPred=xPredVal
, minSd2DiscrFraction = minSd2DiscrFractionVal
, priorInvGammaPars = priorInvGammaParsVal
#,isUsingExpectedDelta = isUsingExpectedDelta
, sd2ObsFactor=sd2ObsFactor
#, ...
)
}
,argsUpdateFunction=list(xVal=x, obsVal=obs, sd2ObsVal=sd2Obs, minSd2DiscrFractionVal = minSd2DiscrFraction
, priorInvGammaParsVal=priorInvGammaSd2DiscrPars
)
,parameters = c(logPsiVar, logSd2DiscrVar)
#,parameters = c(logPsiVar)
,intermediates = c( predProcName, iOName)
)
intermediateSpecs = c( predProcSpec, list( iOSpec, deltaASpec))
names(intermediateSpecs) <- c(predProcName, iOName, deltaAName )
#
# Metropolis block for logPsi
force(priorGammaPsiPars) # will be needed in closure
namesResLogDenPsi <- paste(c("mD","obs","priorPsi"),streamSuffix,sep="_")
nX <- length(x)
# also must depend on a parameter that is updated each cycle, so that
# logDen of current state is recalculated based on changed delta
logDenLogPsiSpec <- blockSpec(logPsiVar, c(logPsiVar),
new("MetropolisBlockUpdater",
fLogDensity=function(theta, intermediates, logDensityComponents, obsVal, sd2ObsVal){
predProc <- intermediates[[predProcName]]
if( length(predProcSpecEntry) ) predProc <- predProc[[predProcSpecEntry]]
res <- .computeLogDenLogPsi( logPsi=theta[logPsiVar]
#, sd2Discr = exp(theta[logSd2DiscrVar])*meanSd2ObsClosure
, sd2Discr = intermediates[[deltaAName]]$sd2Discr
, predProcX=predProc[1:nX]
, deltaAIntermediate=intermediates[[ deltaAName]]
, obs=obsVal, sd2Obs=sd2ObsVal
, priorGammaPsiPars=priorGammaPsiPars
,weightPSd2Discr=weightPSd2Discr
)
names(res) <- namesResLogDenPsi # to distinguish from other streams
res
}
,argsFLogDensity=list(obsVal=obs, sd2ObsVal=sd2Obs) #priorPsiPars by closure
,logDensityComponentNames = namesResLogDenPsi
,isMarkUpdatedWhenNotAccepted = isMarkUpdatedPsiBlock # to trigger recomputation of supporting locations
)
,intermediatesUsed=c(predProcName, deltaAName)
)
# Gibbs InverseGamma block for sd2Discr
logSd2DiscrSpec <- blockSpec(logSd2DiscrVar,logSd2DiscrVar,
new("FunctionBasedBlockUpdater"
, fUpdateBlock=function(...){.updateLogSigmaDiscrByGibbsSamplingGamma(...)} # allows tracing called function
, argsFUpdateBlock=list( priorInvGammaPars=priorInvGammaSd2DiscrPars, intermediateName=deltaAName
, sd2Obs=structure(meanSd2ObsClosure,names=deltaAName)
,isUsingDataBasedSigma=isUsingDataBasedSigma
)
#, argsFUpdateBlock=list( priorInvGammaPars=priorInvGammaSd2DiscrPars, intermediateName=rep(interMediateNames[3],2), sdObs=rep(structure(meanSd2ObsClosure,names=interMediateNames[3]),2))
)
,intermediatesUsed=deltaAName
)
# Sampling model discrepancies at other locations
sampleModelPlusDiscrepancies <- function(
### update Variance by sampling from a scaled inverse Chi-square distribution with using prior information on sigma
theta ##<< numeric vector (nParm): current state of parameters used by this block
,iParms=seq_along(theta) ##<< integer vector (nParmToUpdate): index of parameters to update within theta
,upperParBounds ##<< named numeric vector, upper limits of the parameters to update
,lowerParBounds ##<< named numeric vector, lower limits of the parameters to update
,intermediates=list() ##<< intermediate result for current state xC, see end of vignette on using two Densities
# by closure,IGPars0 ##<< vector with entries "alpha" and "beta"
){
#print("recover at sampleModelDiscrepancies"); recover()
deltaA <- intermediates[[ deltaAName ]] # actual sampling of deltaP and ksiP was done together with deltaO in intermediate
##value<< list with components
list( ##describe<<
isUpdated=TRUE ##<< boolean, if changed block parameters, always true with Gibbs sampling
, xC=deltaA$ksiP ##<< numeric vector: components of position in parameter space that are being updated
) ##end<<
}
ksiSpec <- blockSpec(ksiVars,c(ksiVars),
new("FunctionBasedBlockUpdater"
, fUpdateBlock=sampleModelPlusDiscrepancies
#, argsFUpdateBlock=list( IGPars0=priorInvGammaSd2DiscrPars)
)
,intermediatesUsed=deltaAName
)
#
blockSpecs <- list(logDenLogPsi=logDenLogPsiSpec, logSd2Discr=logSd2DiscrSpec, ksi=ksiSpec)
if( length(xPred)==0 ) blockSpecs$ksi <- NULL
if( !isTRUE(isSigmaBlockReturned) ) blockSpecs$logSd2Discr <- NULL
names(blockSpecs) <- paste(names(blockSpecs),streamSuffix,sep="_")
#
fConstrainingSubSpace <- .getFConstrainingSubSpace(GPLimits$logPsiMin,GPLimits$logPsiMax, logPsiVar )
##value<< list with entries
list(
intermediateSpecs=intermediateSpecs ##<< list with intermediateSpecifications predProc_suffix, iO_suffix, and deltaA_suffix
,blockSpecs=blockSpecs ##<< update block specifications logDenLogPsi_suffix, and logSd2Discr_suffix
,GPLimits=GPLimits ##<< results of \code{\link{computeGPLimits}}
,fConstrainingSubSpace=fConstrainingSubSpace
,namesProcessSamples=ksiVars
,meanSd2Obs=structure( meanSd2ObsClosure,names=deltaAName) ##<< named numeric scalar: average observation uncertainty, with name of deltaA intermediate for this stream
)
}
attr(compileDiscrepancyBlocksForStream,"ex") <- function(){
if(FALSE){
require(twMisc) # dumpOnError
#---- defining the model and observations
set.seed(0815) # for reproducible results
simpleModel <- function(theta,xval){ theta[1]*xval }
sdObsTrue <- 0.01
thetaTrue <- c(theta=0.65) # the true parameter vector
nObs <- 11
#nObs <- 61
x <- seq(0,4,length.out=nObs+1)[-1]
obsTrue <- simpleModel(thetaTrue,x)/(1+x/20)
obs <- obsTrue + rnorm(length(x), sd=sdObsTrue)
sd2Obs <- sdObsTrue^2
thetaHat <- structure(coef(lm(obs ~x-1)), names="theta")
theta0 = c(theta=as.numeric(thetaHat), logPsi=log(0.6), logSd2Discr=log(0.5))
covarTheta0 = diag(c(theta0*0.2)^2, nrow=length(theta0)) # spread for initial population
# ---- define the prediction intermediate
xPred <- c(1.5,3.8,6)
nPred <- length(xPred)
predProcSpec <- intermediateSpec(
dumpOnError(function(theta, intermediates, xVal ){
list(pred = simpleModel(theta["theta"],xVal))
})
,argsUpdateFunction=list(xVal=c(x,xPred))
,parameters = c("theta")
,intermediates = character(0)
)
streamSuffix <- "obs1"
theta0N <- c(theta0, structure(rep(0, length(xPred)),names=paste("ksi",seq_along(xPred),sep="_")))
names(theta0N)[2:(3+nPred)] <- paste(names(theta0N)[2:(3+nPred)],streamSuffix,sep="_")
covarTheta0 = diag(c(theta0N[1:3]*0.2,rep(0.2,nPred))^2, nrow=length(theta0N)) # spread for initial population
#
res <- compileDiscrepancyBlocksForStream(x, obs, sd2Obs
, predProcSpec = list(predSpeed=predProcSpec), predProcSpecEntry = 1L
, streamSuffix, xPred=xPred)
#
predProcRes0 <- getUpdateFunction(res$intermediateSpecs$predSpeed)(theta0, list(), xVal=c(x,xPred))
iORes0 <- getUpdateFunction(res$intermediateSpecs$iO_obs1)(theta0N, list())
deltaARes0 <- getUpdateFunction(res$intermediateSpecs$deltaA_obs1)(theta0N, list(iO_obs1=iORes0, predSpeed=predProcRes0)
, xVal=x, obsVal=obs, sd2ObsVal=sd2Obs, minSd2Discr=1/20, priorInvGammaParsVal=getInvGammaParsFromMeanAndMode( 20, 0.05 ))
#predicting model discrepancy at other x
#KKp <- outer(xPred, deltaARes0$xO, .corG, psi=exp(theta0N["logPsi_obs1"]))
#as.vector(KKp %*% deltaARes0$KyInvY)
intermediates0 <- list(predSpeed=predProcRes0, iO_obs1=iORes0, deltaA_obs1=deltaARes0)
testLogDenLogPsi <- getFLogDensity(getBlockUpdater(res$blockSpec$logDenLogPsi_obs1))(
theta0N
, intermediates0, numeric(1)
, obs=obs, sd2Obs=sd2Obs
#, priorPsiPars=priorPsiPars
)
argsFUpdate <- getArgsFUpdateBlock(getBlockUpdater(res$blockSpec$logSd2Discr_obs1))
testUpdateSigma <- do.call(getFUpdateBlock(getBlockUpdater(res$blockSpec$logSd2Discr_obs1)), c(list(
theta0N, iParms=match("logSd2Discr_obs1",names(theta0N))
, upperParBounds=c(), lowerParBounds=c()
, intermediates=intermediates0
),argsFUpdate) )
testSampleKsi <- getFUpdateBlock(getBlockUpdater(res$blockSpec$ksi_obs1))(
theta0N, iParms=grep("^ksi_.*_obs1$",names(theta0N))
, upperParBounds=c(), lowerParBounds=c()
, intermediates=intermediates0
)
#
# Metropolis block sampling theta, based on intermediate predProcSpec
logDenTheta <- function( theta, intermediates, logDensityComponents, obs, sd2Obs ){
predProc <- intermediates$predSpeed[[1]]
deltaA <- intermediates$deltaA_obs1$deltaA # heed the streamSuffix
logPriorInvGammaSd2Discr <- intermediates$deltaA_obs1$logPriorInvGammaSd2Discr # heed the streamSuffix
residuals <- obs - (predProc[seq_along(x)] + deltaA)
#sd2Obs <- exp(theta["logSd2Obs"])
return( c(obs1= -1/2 * sum( ((residuals)^2)/sd2Obs ), md_obs1=logPriorInvGammaSd2Discr) )
}
testLogDenTheta <- logDenTheta(theta0, intermediates0, numeric(1), obs=obs, sd2Obs=sd2Obs )
logDenThetaSpec <- blockSpec("theta",c("theta","logPsi_obs1","logSd2Discr_obs1"),
new("MetropolisBlockUpdater",
fLogDensity=function(...){logDenTheta(...)},
argsFLogDensity=list( obs=obs, sd2Obs=sd2Obs),
logDensityComponentNames = names(testLogDenTheta)
)
,intermediatesUsed=c("predSpeed","deltaA_obs1")
)
#
res <- compileDiscrepancyBlocksForStream(x, obs, sd2Obs
, predProcSpec = list(predSpeed=predProcSpec), predProcSpecEntry = 1L
, streamSuffix=streamSuffix, xPred=xPred
,minSd2DiscrFraction = 0 #1/20
)
intermediateSpecs <- c(res$intermediateSpecs)
blockSpecs <- c(list(bTheta=logDenThetaSpec), res$blockSpecs)
#mtrace(logDenTheta, recover) #untrace(logDenTheta)
sampler <- newPopulationSampler( blockSpecs, theta0N, covarTheta0
,intermediateSpecifications=intermediateSpecs
, subSpace=res$fConstrainingSubSpace(new("SubSpace"))
)
sampler <- setupAndSample(sampler, nSample=60L)
sampler <- setupAndSample(sampler, nSample=60L)
#sampler <- setupAndSample(sampler, nSample=8L)
#sampler <- setupAndSample(sampler, nSample=200L)
sampleLogs <- getSampleLogs(sampler)
plot( asMcmc(sampleLogs), smooth=FALSE )
stacked <- adrop(getParametersForPopulation(stackChainsInPopulation(subsetTail(sampleLogs, 0.6)),1L),3)
summary(t(stacked))
plot(density(stacked["theta",]))
thetaMean <- rowMeans(stacked)
thetaCf <- apply(stacked, 1, quantile, probs=c(0.05,0.95))
yPredTrue <- simpleModel(thetaTrue,xPred)/(1+xPred/20)
yPredSampled <- thetaMean[3+(1:length(xPred))]
cfYPredSampled <- thetaCf[,3+(1:length(xPred)), drop=FALSE]
plot(obsTrue ~ x, col="blue", xlim=range(c(x,xPred)), ylim=range(c(obs,yPredTrue,yPredSampled)))
points(obs ~ x, col="green")
#lines(simpleModel(thetaHat,x) ~ x)
lines(simpleModel(thetaMean["theta"],x) ~ x)
points(yPredTrue ~ xPred, col="blue")
points(yPredSampled ~ xPred, col="orange")
arrows(xPred,cfYPredSampled[1,], xPred, cfYPredSampled[2,], angle=90, code=3, length=0.05, col="orange")
legend("topleft",inset=c(0.01,0.01)
, legend=c("observations","true values","model prediction","process prediction")
,col=c("green","blue","black","orange"), pch=c("o","o","","o")
,lty=c(0,0,1,1)
)
}
}
.computeGPLimits <- function(
### computing limits on estimates of correlation length psi and spacing of trainign points for Gaussian Process
x ##<< locations of observations
, maxKernelDim=60 ##<< maximum of dimensionality of the Kernal matrix
){
##details<<
## For large psi, some matrices become indeterminate, hence bound by range of x-values.
## For very small psi, model error becomes zero between supporting points. However,
## we want to have a smooth model error between supporting points, else we model the noise.
## Hence, bound psi by only a bit lower than minimal allowed average distance between supporting points.
## If every observation becomes a supporting point, then we model the noise.
## Hence, we ensure not allow smaller distance between supporting points than 4 times the average distance
## covered by the observations.
xRange <- diff(range(x))
maxXSpacing <- xRange / (3+2)
minXSpacing = max( xRange/maxKernelDim, min( maxXSpacing, xRange/length(x)*4))
#logPsiMin = log(xRange/(length(x)/2))
logPsiMin = log(minXSpacing / 1.5) # not much smaller than minimal xSpacing
logPsiMax = log(xRange) # for larger psi, inversion of matrix becomes singlular
##value<< list with entries
list(
logPsiMin = logPsiMin ##<< minimum correlation length about halv the distance between supporting points
,logPsiMax = logPsiMax ##<< maximum correlation length of about the range of x
,minXSpacing = minXSpacing ##<< minimum distrance of supporting points so that there are about 2 to 3 points in between
,maxXSpacing = maxXSpacing ##<< maximum distance of training points so that there are at least 3 points in addition to start and end
,psi0 = min(exp(logPsiMax),5*exp(logPsiMin)) ##<< initial estimate of correlation length, of about 10 oints
)
}
# need to export to work on cluster
#' @export
.corG <- function(x1,x2,psi){
exp(-((x1-x2)/psi)^2)
}
# need to export to work on cluster
#' @export
.computeGPTrainingLocations <- function(
### compute subsets of locations, xO, where observation error will be sampled from Gaussian Process
psi ##<< current estimate of correlation length of Gaussian Process
,x ##<< locations, e.g. time, at witch observations are available
, minXSpacing ##<< minium distance between neighboring xO
, maxXSpacing ##<< maximum distance between neighboring xO
, xPred=numeric(0) ##<< numeric vector: locations where predictions of GP are recorded
, psiSpacingFac=1.5 ##<< multiple of psi, of xSpacing
, avgFrac=1/10 ##<< length of the range over which to average observations, as fraction of distance between supporting locations
, nSet=1L ##<< number of alternative sets of supporting locations returned
){
# determine subset of xO by taking points all every ~psi locations
if( !is.finite(psi) ) stopDemac("provided a psi that is not finite. Check variable nameing in theta.")
xOSpacing <- xOSpacing0 <- min(max((psiSpacingFac*psi), minXSpacing), maxXSpacing) # to prevent too large matrices do not go beyond a minimum of spacing between x
xd0 <- xd <- c(0,cumsum(diff(x))) # distances to x[1]
# choose the second point o2 among those locations that are less than 1/4 away from x0Spacing
# keep the first points, else GP goes to zero at the edge
o2Min <- which.min(abs(xd0 - xOSpacing*3/4))
o2Max <- which.min(abs(xd0 - xOSpacing*5/4))
nChooseFrom <- (o2Max-o2Min)+1
o2Offsets <- sample.int(nChooseFrom, size=nSet, replace=TRUE)-1
# introduce some jitter to select different locations by varying the distance between supporting locations
xOSpacings <- matrix( runif( nSet* (2L+ceiling(diff(range(x))/xOSpacing)), min=xOSpacing*0.9, max=xOSpacing*1.1 ), ncol=nSet )
xOTargetCums <- apply(xOSpacings, 2, cumsum)
#iSet <- 1L
ans <- lapply( 1:nSet, function(iSet){
o2 <- o2Min + o2Offsets[iSet]
xd <- xd0[-(1:o2)]-xd0[o2] # distances of other points to o2
xOTargetCum <- xOTargetCums[,iSet]
i <- 0L; j <- 1L
iO2 <- integer(length(xd))
while( j < length(xd)){
i <- i+1L
jn <- j+which.min( (xd[-(1:j)]-xOTargetCum[i])^2 )
if( !length(jn) ) stop(".computeGPTrainingLocations: encontered jn of zero length.")
j <- jn
iO2[i] <- j
}
iO <- c(1L, o2, o2+iO2[1:i])
# make sure last point is in set
#nX <- length(x)
#mIO <- max(iO)
#if( x[nX] > (x[mIO]+xOSpacing/2) ) iO <- c(iO,nX) else iO[length(iO)] <- nX
iAvg <- rep(NA_real_, length(x))
for( i in seq_along(iO)){
ii <- ( if(i==1) TRUE else (x > x[iO[i-1L]]) ) & # only consider x above previous supporting location
( if(i==length(iO)) TRUE else (x < x[iO[i+1L]]) ) & # below next supporting location
abs(x - x[iO[i]] ) < xOSpacing*avgFrac/2
iAvg[ii] <- i
}
if( length(na.omit(unique(iAvg))) != length(iO) ){ print("encountered problems averaging locations for supporing points."); recover() }
xO <- x[iO]
#plot(1*x ~ x, pch="+", col="grey"); points(1*xO~xO, col="red")
xOP <- c(xO,xPred)
LambdaOO <- outer(xO, xO, .corG, psi=psi)
LambdaRO <- outer(x[-iO], xO, .corG, psi=psi)
#LambdaRR <- outer(x[-iO], x[-iO], .corG, psi=psi)
#LambdaPP <- outer(xOP, xOP, .corG, psi=psi)
#LambdaPO <- outer(xOP, xO, .corG, psi=psi)
LambdaPP <- outer(xPred, xPred, .corG, psi=psi)
LambdaPO <- outer(xPred, xO, .corG, psi=psi)
##value<< list of nSet sets of supporting locaitons. Each is a lists with entries
list(
iO = iO ##<< indices within x, for which model discrepancies are sampled
,iAvg = iAvg ##<< index vector (length(x)): giving indices of locations to average observations for training the GP
,LambdaOO = LambdaOO
,LambdaRO = LambdaRO
#,LambdaRR = LambdaRR
,LambdaPP = LambdaPP ##<< correlations among c(x,xPred)
,LambdaPO = LambdaPO
)
})
}
.tmp.f <- function(){
plot( seq_along(x) ~ x )
abline(v=x[c(1,o2)])
abline(v=x[iO], col="green")
xR <- x[-(1:o2)]-x[o2]
plot(xd ~ xR )
abline(v=xR[iOR])
abline(v=xR[iOR], col="red")
}
# need to export to work on cluster
#' @export
.getFConstrainingSubSpace <- function(
### provide a function that constrains a subspace for given logPsiMin and logPsiMax
logPsiMin ##<< numeric scalar: lower bound
,logPsiMax ##<< numeric scalar: upper bound
,varName="logPsi" ##<< name of the parameter to constrain
){
##value<< a function that constrains a subspace to the given bounds
constrainSubSpace <- function(
### constrain subspace low lower and upper parameter bound
subSpace=new("SubSpace")
){
getSplitSubSpaces(
getSplitSubSpaces(subSpace, structure(logPsiMax,names=varName) )$lower
,structure(logPsiMin,names=varName)
)$upper
}
}
attr(.getFConstrainingSubSpace,"ex") <- function(){
fConstr <- .getFConstrainingSubSpace(0.5,1.5,"logPsi")
cSubSpace <- fConstr(new("SubSpace"))
# add this subspace to the sampler: by providing it to newSampler
}
# need to export to work on cluster
#' @export
.computeModelDiscrepancies <- function(
### compute model discrepancies MD given locations, sigma, psi and model discrepancies
#sd2DiscrFac ##<< estimated variance of factor of MD/sdObs
psi ##<< estimated correlation length
,sd2DiscrFac ##<< numeric scalar: current signal variance (normalized by meanSd2Obs)
,iORes ##<< indices within x to sample from gaussion process GP
,predProc ##<< intermediate giving model predictions at c(x, xPred)
,x ##<< locations, e.g. time, of observations
,obs ##<< observations
,sd2Obs ##<< numeric scalar: variance of observation uncertainty
,meanSd2Obs ##<< numeric scalar: mean of observation uncertainty variance
,minSd2DiscrFraction = 1/20 ##<< minimum of uncertainty of GP training data as a fraction of model error variance
,xPred=numeric(0) ##<< locations at which to sample model discrepancies, mapped to paramter vector md_1 to md_<nPred>
,priorInvGammaPars ##<< prior of signal variance s2Discr #= getInvGammaParsFromMeanAndMode( 0.3^2, 0.2^2 )
,sd2ObsFactor=1 ##<< if sampled discrepancy goes to zero, decrease the estimate of observation uncertainty by lowering this factor
){
#isUsingMLESigma=TRUE ##<< set to TRUE, if estimate sd2Discr by maximising likelihood instead of expecting misifit of n
if( length(sd2Obs) == 1L ) sd2Obs <- rep(sd2Obs, length(obs))
#if( !is.finite(sd2DiscrFac) ) stopDemac("provided a sd2Discr that is not finite. Check variable nameing in theta.")
nX <- length(x)
nP <- length(xPred)
z <- (obs - predProc[1:nX]) # actual differences
#iOResI <- iORes[[1L]]
resSets <- lapply( iORes, function(iOResI){
iO <- iOResI$iO
xO <- x[iO]
nO <- length(iO)
iAvg <- iOResI$iAvg
#
##details<<
## In order to diminish dependence of the discrepancy on the
## choice of supporting locations and their random observation error,
## Observations and their variance are over neighboring locations.
zO <- tapply(z,iAvg, mean)
sd2ObsO <- as.vector(tapply(sd2Obs, iAvg, function(sd2){sum(sd2)/length(sd2)^2}))
sd2Discr <- sd2DiscrSampled <- as.vector(sd2DiscrFac * meanSd2Obs)
#
KOO <- sd2Discr * iOResI$LambdaOO
KRO <- sd2Discr * iOResI$LambdaRO
##details<<
## If the model error is fluctuating, the discrepancies and their variance
## may be estimated too low and go to zero.
## A workaround is using a smaller observation variance when computing expected discrepancies
## (argument sd2ObsFactor)
Ky <- KOO + diag(sd2ObsO*sd2ObsFactor, nrow=length(xO))
if( nP != 0 ){
xOP <- c(xO,xPred) # prediction at xO and xP
if( length(xOP) > length(predProc)) stopDemac(
"predProc intermediate does not provide enough model predictions (maybe does not including xPred?)")
Kpp <- sd2Discr * iOResI$LambdaPP
Kpo <- sd2Discr * iOResI$LambdaPO
KySolved <- solve(Ky, cbind(zO, t(Kpo) )) # call solve only once
solvedKyZ <- KySolved[,1]
solvedKyKop <- KySolved[,1+seq_along(xPred)]
#
##details<<
## When predicting model discrepancies at prediction locations,
## They are sampled from the GP. Also if the discrepancy at
## supporting locations is set to the expected value
deltaPMu <- as.vector(Kpo %*% solvedKyZ) # expected value at prediction locations
deltaPCov <- Kpp - Kpo %*% solvedKyKop
deltaPCovSymm <- (deltaPCov + t(deltaPCov)) / 2 # else sometimes rmvnorm does complain
deltaP <- pred <-
as.vector(rmvnorm(1, deltaPMu, deltaPCovSymm, method="svd")) # realization
ksiP <- predProc[nX+(1:nP)] + deltaP
if( any(!is.finite(ksiP))) stopDemac("encountered non-finite process predictions")
} else {
solvedKyZ <- solve(Ky, zO)
deltaP <- ksiP <- numeric(0)
}
deltaA <- numeric(length(x)) # observation errors of all x
deltaO <- deltaA[iO] <- deltaOMu <- as.vector(KOO %*% solvedKyZ) # maybe smoothed compared to zO
# in computing deltaR, do not account for observation uncertainty (use KOO and instead of Ky), because using deltaO instead of y
# but if deltaO is the expected value, then solvedKooDeltaO corresponds to solvedKyZ
solvedKooDeltaO <- solvedKyZ
deltaR <- deltaA[-iO] <- as.vector(KRO %*% solvedKooDeltaO)
SSQpred <- sum( (z - deltaA)^2/sd2Obs )
#------- plotting discrepancies and model
# plot(deltaA ~ x, type="l", ylim=range(c(z)) ); points( z ~ x, pch="+", col="grey"); points(z[iO] ~ x[iO], col="red", pch="+"); points(zO ~ x[iO], col="red"); abline(h=0, col="grey"); points(deltaOMu ~ xO, col="maroon" ); points(deltaO~xO,pch="o", col="green");
# plot(deltaA ~ x, type="l" ); points(deltaOMu ~ xO, col="maroon" ); points(deltaO~xO,pch="o", col="green")
#if( nX > 20 ) recover()
##details<<
## The product delta %*% K^-1 %*% delta is approximated by delta_S %*% KSS^-1 delta_S.
## Otherwise the inversion of the K matrix involves an inversion of a submatrix of dimension n_R
## Additionally, this becomes numerically singular easily.
## With some preliminary testing (both with sparse and rich streams), the approximation was precise
## within a tolerance of 1permill of the original product.
#solvedKooDeltaO <- solve(KOO, deltaO)
normDelta <- tmp <- as.vector(deltaO %*% solvedKooDeltaO ) # rely on the approximation by deltaO for all delta
#invCovDeltaBlocks <- computeInverseOfBlockedMatrix( KOO, t(KRO), KRO, KRR )
#normDelta <- blockedMatrixNorm( deltaO, deltaR, invCovDeltaBlocks )
#if( abs(normDelta - tmp) > normDelta/100 ) recover()
logPDeltaO <- -1/2 * normDelta
#
##value<< a list with values
res <- list(
deltaA=deltaA ##<< sample of model discrepancies at all observations locations (xO and xR)
,deltaP=deltaP ##<< sample of model discrepancies at xPred
,ksiP=ksiP ##<< current model predictions + deltaP, i.e. sample of process value
#,Ky=Ky ##<< GP kernal with uncertain observations at all xO (subset of x)
,xO=xO ##<< the positions of the training data
,iO=iO ##<< index of subset of training points xO among x
,psi=psi ##<< hyperparameter used in the current sample
,sd2Discr=sd2Discr ##<< hyperparameter used in the current sample
,meanSd2Obs=meanSd2Obs ##<< mean observation variance, used to normalize sd2Discr
,zO=zO ##<< model-data residual at supporting locations (aggregate of neighboring locations)
,sd2ObsO=sd2ObsO ##<< observation variance of zO (aggregate of neighboring locations)
#,logPriorInvGammaSd2Discr=logPriorInvGammaSd2Discr ##<< unnormalized prior probability of this expected signal variance
,LambdaOO = iOResI$LambdaOO ##<< correlation matrix used for deltaO
,solvedLambdaOODeltaO = sd2Discr * solvedKooDeltaO ##<< Lambda^-1 deltaO
#,solvedKyy = KySolved[,1] ##<< Ky^-1 (obs - pred)
#,solvedKooDeltaO = solvedKooDeltaO ##<< KOO^-1 deltaO
#,KOO = KOO
,logPDeltaO = logPDeltaO ##<< penalty term for model discrepancies in log-Density
,SSQpred = SSQpred ##<< sum( (obs-(pred+delta))^2/sdObs^2 )
)
})
#logPDeltaOs <- sapply( resSets, "[[", "logPDeltaO")
#quant80 <- sort(logPDeltaOs)[ round(0.8*length(logPDeltaOs)) ]
#i <- which(logPDeltaOs == quant80)[1]
##details<<
## Model discrepancies are computed for several sets of supporting locations.
## For each the weighted Sum of Squares of misfits is computed
## From all the supporting locations sets, the result is chosen that corresponds to the 80percentile of misfits
SSQs <- sapply( resSets, "[[", "SSQpred")
quant80 <- sort(SSQs)[ round(0.8*length(SSQs)) ]
i <- which(SSQs == quant80)[1]
ans <- resSets[[i]]
# the following for testing update of sd2DiscrFac
#intermediates <- list(deltaA_obs1=res); sd2Obs <- list(deltaA_obs1=meanSd2Obs)
ans
}
.tmp.f <- function(){
expectedSd2DiscrFac <- seq(0.01,0.7,by=0.01)
plot( dinvgamma(expectedSd2DiscrFac, priorInvGammaPars["alpha"], priorInvGammaPars["beta"]) ~ expectedSd2DiscrFac )
plot( log(dinvgamma(expectedSd2DiscrFac, priorInvGammaPars["alpha"], priorInvGammaPars["beta"])) ~ expectedSd2DiscrFac )
#
logPriorInvGammaSd2Discr <- as.vector(log(expectedSd2DiscrFac)*(-priorInvGammaPars["alpha"]-1) - priorInvGammaPars["beta"]/expectedSd2DiscrFac)
plot(logPriorInvGammaSd2Discr ~ expectedSd2DiscrFac)
}
.estimateSignalVariance <- function(
### estimate signal variance for given correlations and data
zO ##<< numeric vector: model data misfit
, LambdaOO=outer(xO, xO, .corG, psi=psi) ##<< correlation matrix
, sd2ObsO ##<< observation variance (vector of length zO)
, psi ##<< alternatively to specifying LambdaOO, correlation length psi can be specified together with xO or spacing ds
, xO=.computeGPTrainingLocations(ds, psi, x) ##<< supporting locations of the GP
, ds ##<< distance between supporting locations
, x ##<< locations of observations
){
#solvedLambdaOOZO <- solve(LambdaOO, zO)
#sd2Discr <- as.vector(zO %*% solvedLambdaOOZO) / length(zO)
nO <- length(zO)
invLambdaOO <- solve(LambdaOO)
sd2Discr1 <- sd2Discr0 <- sd2Discr <- as.vector(zO %*% invLambdaOO %*% zO) / nO
if( sd2Discr < 0 ){
stop("encountered negative variance.")
sd2Discr <- sd2Discr0
}
sd2Discr <- if( sd2Discr0 > 6*mean(sd2ObsO)){
sd2Discr0
} else {
# Correct by part of variance explained by sd2_eps
sd2DiscrI <- numeric(10)
sd2DiscrI[1L] <- mean(sd2ObsO)/2
sd2DiscrI[2L] <- sd2Discr0
tol=sd2Discr0/20
sdObsInv <- diag(1/sd2ObsO, nrow=nO)
i <- 3L
while( (i <= 10) && (abs(sd2DiscrI[i-1L] - sd2DiscrI[i-2L]) > tol) ){
sd2Discr <- (1*sd2DiscrI[i-1L] + 1*sd2DiscrI[i-2L])/2
W <- invLambdaOO/sd2Discr + sdObsInv
invW <- solve(W)
W2 <- invLambdaOO %*% invW %*% invLambdaOO /sd2Discr
sd2DiscrI[i] <- (sd2Discr0 - as.vector(zO %*% W2 %*% zO) / nO)
i <- i +1L
}
#plot( sd2DiscrI )
sd2Discr <- (1*sd2DiscrI[i-1L] + 1*sd2DiscrI[i-2L])/2
}
#Ky <- sd2Discr*LambdaOO + diag(sd2ObsO, nrow=nO); zO %*% solve(Ky,zO)
sd2Discr
}
.estimateSignalVarianceOpt <- function(
### estimate signal variance for given correlations and data
zO ##<< numeric vector: model data misfit
, LambdaOO=outer(xO, xO, .corG, psi=psi) ##<< correlation matrix
, sd2ObsO ##<< observation variance (vector of length zO)
, psi ##<< alternatively to specifying LambdaOO, correlation length psi can be specified together with xO or spacing ds
, xO=.computeGPTrainingLocations(ds, psi, x) ##<< supporting locations of the GP
, ds ##<< distance between supporting locations
, x ##<< locations of observations
, solvedLamdaOOzO = solve( LambdaOO, zO) ##<< may provide for performance reasons
#, sd2Discr0 = as.vector(zO %*% solvedLamdaOOzO) / length(zO) ##<< initial estimate of discrepancy variance
){
#solvedLambdaOOZO <- solve(LambdaOO, zO)
#sd2Discr <- as.vector(zO %*% solvedLambdaOOZO) / length(zO)
nO <- length(zO)
#x <- seq(2, 2*sd2Discr0, length.out=31)
#logPZO <- sapply(x, .logPZO, LambdaOO=LambdaOO, sd2ObsO=sd2ObsO, zO=zO )
#plot( logPZO ~ x)
#abline(v=sd2Discr)
sd2DiscrUpper <- as.vector(zO %*% solvedLamdaOOzO) / length(zO)
resOpt <- optimize(.logPZO, interval=c(0, sd2DiscrUpper), maximum = TRUE
,tol=sd2DiscrUpper/20
,LambdaOO=LambdaOO, sd2ObsO=sd2ObsO, zO=zO
)
sd2Discr <- resOpt$maximum
sd2Discr
}
.logPZO <- function(
### Likelihood of zO from Gaussian process
sd2Discr
, LambdaOO ##<< correlation matrix
, sd2ObsO ##<< observation variance (vector of length zO)
, zO ##<< observed model-data residuals
){
nO <- length(zO)
Ky <- sd2Discr*LambdaOO + diag(sd2ObsO, nrow=nO)
U <- chol(Ky)
alpha <- backsolve( t(U), backsolve(U,zO))
logPZO <- -1/2*zO%*%alpha -sum(log(diag(U))) #-nO/2*log(2*pi)
}
#' @export
.updateLogSigmaDiscrByGibbsSamplingGamma <- function(
### update Variance by sampling from a scaled inverse Chi-square distribution with using prior information on sigma
theta ##<< numeric vector (nParm): current state of parameters used by this block
,iParms=seq_along(theta) ##<< integer vector (nParmToUpdate): index of parameters to update within theta
,upperParBounds ##<< named numeric vector, upper limits of the parameters to update
,lowerParBounds ##<< named numeric vector, lower limits of the parameters to update
,intermediates=list() ##<< intermediate result for current state xC, see end of vignette on using two Densities
,intermediateNames="deltaA" ##<< name of the intermediate, usually combined with a suffix for given stream
,sd2Obs ##<< named numeric vector: average observation uncertainty. Names must correspond to argument intermediateNames
,priorInvGammaPars #= getInvGammaParsFromMeanAndMode( 0.3^2, 0.2^2 )
,isUsingDataBasedSigma=FALSE ##<< set to TRUE to use an independent Likelihood estimate of signal variance in the calculation of discrepancies, instead of the current signal variance. This reduces performance.
){
#intermediateName <- intermediateNames[2]
#intermediateName <- "deltaA_obs1"
# deltaO%*%solve(LambdaOO,deltaO) / length(deltaO) # estimate of signal variance
# allow using one discrepancy as the mean over estimates of several blocks, denoted by intermediateNames
sd2DiscrFacStreams <- sapply(intermediateNames, function(intermediateName){
int <- intermediates[[ intermediateName ]]
if( length(attr(int,"problems")) ) return(exp(theta)) # do not resample, because have no deltaO
iO <- int$iO
solvedLambdaOODeltaO <- int$solvedLambdaOODeltaO
deltaO <- int$deltaA[iO]
#LambdaOO <- int$LambdaOO
#normDeltaO <- tmp <- deltaO%*%solve(LambdaOO,deltaO)
normDeltaO <- tmp <- deltaO %*% solvedLambdaOODeltaO
#sd2Discr0 = exp(theta)*sd2Obs[[intermediateName]] # value used for current delta
if( isUsingDataBasedSigma ){
sd2DiscrData <- .estimateSignalVarianceOpt( int$zO, int$LambdaOO, int$sd2ObsO)
KOO <- sd2DiscrData*int$LambdaOO
Ky <- KOO + diag(int$sd2ObsO, nrow=length(iO))
solvedKyZ <- solve(Ky, int$zO)
deltaOD <- as.vector(KOO %*% solvedKyZ)
solvedLambdaDeltaO <- sd2DiscrData * solvedKyZ # only if deltaOD are expected values
normDeltaO <- deltaOD %*% solvedLambdaDeltaO
}
#
IGParsAdd <- c( length(deltaO)/2, 1/2/sd2Obs[[intermediateName]] * normDeltaO)
IGPars <- priorInvGammaPars + IGParsAdd
sd2DiscrFac <- sd2IG <- as.vector(rInvGamma(1, IGPars["alpha"], IGPars["beta"] ))
})
sd2DiscrFac <- mean(sd2DiscrFacStreams)
##value<< list with components
list( ##describe<<
isUpdated=TRUE ##<< boolean, if changed block parameters, always true with Gibbs sampling
, xC=log(sd2DiscrFac) ##<< numeric vector: components of position in parameter space that are being updated
) ##end<<
}
.tmp.f <- function(){
#IGPars <- getInvGammaParsFromMeanAndMode( 20, 0.05 )
#IGPars <- c(alpha=0.001,beta=0.001)
xGrid <- seq(0.01, 4, length.out=101)
plot( (tmpd <- sqrt(dinvgamma(xGrid, IGPars["alpha"], IGPars["beta"]))) ~ xGrid)
plot( head(tmpd) ~ head(xGrid))
tmpMean <- IGPars["beta"]/(IGPars["alpha"]-1)
tmp <- rInvGamma(100, IGPars["alpha"], IGPars["beta"] )
abline(v=tmpMean, col="grey")
c(mean(tmp))
# plotting rInvGamma distribution
}
.depr <- function(){
#inside .updateLogSigmaDiscrByGibbsSamplingGamma tried to base delta only on data instead of former sigma^2
# did not work
#sd2DiscrData <- .estimateSignalVariance( int$zO, int$LambdaOO, int$sd2ObsO)
sd2DiscrData <- .estimateSignalVarianceOpt( int$zO, int$LambdaOO, int$sd2ObsO)
KOO <- sd2DiscrData*int$LambdaOO
Ky <- KOO + diag(int$sd2ObsO, nrow=length(iO))
solvedKyZ <- solve(Ky, int$zO)
deltaOD <- as.vector(KOO %*% solvedKyZ)
solvedLambdaDeltaO <- sd2DiscrData * solvedKyZ # only if detlaOD are expected values
normDeltaO <- deltaOD %*% solvedLambdaDeltaO
}
#' @export
.computeLogDenLogPsi <- function(
logPsi, sd2Discr, predProcX, deltaAIntermediate, obs, sd2Obs, priorGammaPsiPars
#,T=2
,weightPSd2Discr ##<< the weight for signal variance in the likelihood of correlation length psi
){ #, logSd2DiscrVar ){ #, priorPsiPars ){
logPriorPsi <- as.vector((priorGammaPsiPars["k"]-1)*logPsi -1/priorGammaPsiPars["theta"]*exp(logPsi))
if( length(attr(deltaAIntermediate,"problems")) ) return(c(
mD = -Inf
,obs= -Inf
,priorPsi = logPriorPsi
))
deltaA <- deltaAIntermediate$deltaA
iO <- deltaAIntermediate$iO
deltaO <- deltaA[ deltaAIntermediate$iO ]
residuals <- obs - (predProcX + deltaA)
#
logLikObs <- -1/2 * sum( residuals^2/sd2Obs )
logLikGP <- deltaAIntermediate$logPDeltaO
res <- c(
mD = logLikGP
,obs= logLikObs
,priorPsi = logPriorPsi
)
}
.tmp.f <- function(){
library(parallel)
if(exists("cl")) stopCluster(cl)
cl <- makeCluster(2L)
#clusterExport(cl, c("mcCommon", ".sampleRangeOneChain"), envir=environment())
clusterEvalQ(cl, library(blockDemac) )
clusterEvalQ(cl, library(twMisc) )
.tmp.f <- function(){
clusterEvalQ(cl, helloFromBlockDemac() )
dumpedStop <- dumpOnError(stop)
lapply( 1, dumpedStop("dumpedStop"))
load("last.dump.rda")
debugger()
}
clusterExport(cl, c("simpleModel"))
sampler@cluster <- cl
sampler <- setupAndSample(sampler, nSample=30L)
#stopCluster(cl)
sampleLogs <- getSampleLogs(sampler)
plot( asMcmc(sampleLogs), smooth=FALSE )
}
#' @export
compileLogSd2DiscrBlockDescription <- function(
### create Block Descriptions for updating logSd2Discr, the common model discrepancy factor
discrepancyBlocks ##<< list of Discrepancy block compilations by \code{\link{compileDiscrepancyBlocksForStream}}
,priorInvGammaSd2DiscrPars = getInvGammaParsFromMeanAndMode( 20, 0.05 ) ##<< prior for corellation length
){
##details<<
## the entries of discrepancyBlocks must each contain an element \code{meanSdObs}
## giving the sqare root of mean observation variance. Its name must correspond to
## the name of the intermediate providing the model discrepancies deltaO.
# Gibbs IG block for sd2Discr
sd2ObsClosure <- unlist(structure(lapply( discrepancyBlocks, "[[", "meanSd2Obs" ),names=NULL))
namesIntermediatesClosure <- names(sd2ObsClosure)
logSd2DiscrVar <- "logSd2Discr"
logSd2DiscrSpec <- blockSpec(logSd2DiscrVar,logSd2DiscrVar,
new("FunctionBasedBlockUpdater"
, fUpdateBlock=function(...){.updateLogSigmaDiscrByGibbsSamplingGamma(...)} # allows tracing called function
, argsFUpdateBlock=list( priorInvGammaPars=priorInvGammaSd2DiscrPars
, intermediateName=namesIntermediatesClosure
, sd2Obs=sd2ObsClosure )
)
,intermediatesUsed=namesIntermediatesClosure
)
}
computeInverseOfBlockedMatrix <- function(
### invert a blocked matrix [A,B;C,D]
A,B,C,D, invA=solve(A)
){
# see: https://en.wikipedia.org/wiki/Invertible_matrix#Blockwise_inversion
invE <- D - C %*% invA %*% B
E <- solve(invE)
BE <- B %*% E
CinvA <- C %*% invA
##value<< the blocks of the inverted matrix
list(
A = invA + invA %*% BE %*% CinvA
,B = -invA %*% BE
,C = -E %*% CinvA
,D = E
)
}
attr(computeInverseOfBlockedMatrix,"ex") <- function(){
A <- matrix(c(1,.1,.1,1), nrow=2L)
B <- matrix(0.05, nrow=2L, ncol=3L)
C <- t(B)
D <- matrix(0.1, nrow=3L, ncol=3L); diag(D) <- 1L
M <- rbind( cbind(A,B), cbind(C,D) )
invM1 <- solve(M)
invM1 %*% M - diag(nrow=5)
res <- invBlockedMatrix(A,B,C,D)
invM2 <- rbind( cbind(res$A,res$B), cbind(res$C,res$D) )
invM2 %*% M - diag(nrow=5) # all near 0
b1 <- 1:2
b2 <- 3:5
b <- c(b1,b2)
b %*% M %*% b
blockedMatrixNorm(b1,b2, list(A=A,B=B,C=C,D=D)) - as.vector(b %*% M %*% b) # zero difference
}
blockedMatrixNorm <- function(
### compute (b1, b2T)^T M (b1, b2) for M of form [A,B;C,D]
b1, b2
, blocks ##<< list with four entries corresponding to blocks of matrix (by row) [A,B;C,D]
){
b1T <- t(b1)
b2T <- t(b2)
as.vector((b1T %*% blocks[[1]] %*% b1) + (b2T %*% blocks[[3]] %*% b1) + (b1T %*% blocks[[2]] %*% b2) + (b2T %*% blocks[[4]] %*% b2))
}
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.