#this chunk is to install and require the knitr package, that will produce an html or pdf output report
if (!require("knitr")) {
install.packages("knitr")
require("knitr")
}
## Loading required package: knitr
The setup of this example is that described in section 5 of the article “Differentiability and approximation of probability functions under Gaussian mixture Models” using parameters, sample sizes and other numerical specifications chosen arbitrarily. You may download the file GMMpf.Rmd and compile it using R to change these parameters to your taste.
The example
Suppose \[\xi \sim \mathcal{N}\left(\begin{pmatrix} 0 \\ C \end{pmatrix},\begin{bmatrix} 1-C^2 & 0 \\ 0& 1-C^2 \end{bmatrix}\right), \] where the y coordinate \(C \in [-1,1]\) is such that \(\frac{C+1}{2}\) has, for some \(\delta\in (0,1)\), the Beta(\(\delta,\delta\)) distribution, i.e. the density of random variable \(C\) is \(\frac{\Gamma(2\delta)}{2^{2\delta-1}\Gamma(\delta)^2}(1-c^2)^{\delta-1}\) for \(c\in [-1,1]\). The chance constraint \(g:\mathbb{R} \times \mathbb{R}^2\) is given by \[g(x,\xi)=||\xi||^2-x^2-2.\] It is clear that \(g(x,(0,c)^\top) \leq 0\) for each \(c\in [-1,1]\) and that, for \(r>0\) and \(v=(v_1,v_2)^\top\) such that \(v_1^2+v_2^2=1\), using the spherical-radial decomposition $=(0,c)^+r(c) v $ with \(r>0\) and \(\mathcal{L}(c)= \begin{bmatrix} \sqrt{1-C^2} & 0 \\ 0& \sqrt{1-C^2} \end{bmatrix}\) it holds that
\[ g(x,(0,c)^\top +r\mathcal{L}(c) v ) \leq 0 \iff r\leq \rho_c(x,v)=\frac{-cv_2+\sqrt{x^2+2-c^2v_1^2}}{\sqrt{1-c^2}}.\]
The radial probability function and its gradient are therefore given by \[e_c(x,v)= \int_0^{\rho_c(x,v)}re^{-r^2/2}dt=1-e^{-\rho_c(x,v)^2/2},\]
and \[\nabla_x e_c(x,v)=\frac{ x\rho_c(x,v)e^{-\rho_c(x,v)^2/2}}{ \sqrt{ [1-c^2] [ x ^2+2 - c^2v_1 ^2] } }\]
The probability function and its gradient are given by
\[\Phi(x) =\frac{\Gamma(2\delta)}{2^{2\delta-1}\Gamma(\delta)^2}\int_{-1}^1(1-c^2)^{\delta-1} F_\chi^{2,c^2/(1-c^2)}([1-c^2][x^2+2])dc,\] and \[\nabla \Phi (x)=\frac{x\Gamma(2\delta)}{2^{2\delta}\Gamma(\delta)^2}\int_{-1}^1(1-c^2)^{\delta-1} f_\chi^{2,c^2/(1-c^2)}([1-c^2][x^2+2])dc,\]
where \(F_\chi^{k,\lambda}\) denotes the cumulative distribution function of a non-central chi-squared random variable with \(k\) degrees of freedom and noncentrality parameter \(\lambda\), and \(f_\chi^{k,\lambda}\) denoting its corresponding density. We now conduct a simulation based estimation of \(\Phi\) and its gradient via
\[\Phi_N(x)=\frac{1}{N}\sum_{i=1}^N e_{c_i}(x,v_i) \quad \text{ and } \quad \nabla\Phi_N(x)=\frac{1}{N}\sum_{i=1}^N \nabla_x e_{c_i}(x,v_i).\] comparing \(\Phi_N\) with the estimator based on the proportion of simulations verifying the constraint
\[\tilde{\Phi}_N(x)=\frac{1}{N}\sum_{i=1}^N 1_{ \{ g(x,\xi_i)\leq 0 \}}.\]
Parameter setup
set.seed(681986) #seeded for replicability, change or remove for alternate samples
N<-100 #sample size
d<-0.5 #delta parameter, see more details at end
xlow<-0 #lower bound for range of decision variable x
xup<-4 #upper bound for range of decision variable x
xvalues<-200 #number of different values of x in range to simulate the probability function and estimators
Simulation of probability function curves and functional estimates
estradial<-rep(0,xvalues)
estgradient<-rep(0,xvalues)
estprop<-rep(0,xvalues)
trueprob<-rep(0,xvalues)
truegradient<-rep(0,xvalues)
v<-runif(N,0,2*pi)
c<-2*rbeta(N,d,d)-1
x<-seq(xlow,xup, length.out=xvalues)
rs<-rep(0,N)
for(w in 1:N){
rs[w]<-rnorm(1,0,sqrt(1-c[w]^2))^2+rnorm(1,c[w],sqrt(1-c[w]^2))^2
}
for(i in 1:xvalues){
rcrit<-(-c*sin(v)+sqrt(x[i]^2+2 -c^2*cos(v)^2))/sqrt(1-c^2)
estradial[i]<-mean(1-exp(-rcrit^2/2))
estgradient[i]<-mean(x[i]*rcrit*exp(-rcrit^2/2)/sqrt((x[i]^2+2 -c^2*cos(v)^2)*(1-c^2)))
estprop[i]<-sum(rs < 2+x[i]^2)/N
cdf<-function(y){(1-y^2)^(d-1)*pchisq((x[i]^2+2)/(1-y^2),2,ncp = y^2/(1-y^2))}
trueprob[i]<-integrate(cdf,-1,1,abs.tol = 0.000001)$value
dens<-function(y){(1-y^2)^(d-2)*dchisq((x[i]^2+2)/(1-y^2),2,ncp = y^2/(1-y^2))}
truegradient[i]<-2*integrate(dens,0,1,abs.tol = 0.000000001)$value*x[i]
}
trueprob<-trueprob/(2^(2*d-1)*beta(d,d))
truegradient<-truegradient/(2^(2*d-2)*beta(d,d))
Plots
par(mfrow=c(1,2))
plot(x,trueprob,type="l", ylab = "Probability", main=paste("Constraint probability. N=", N))
lines(x,estprop,col="blue")
lines(x,estradial,col="red")
legend("bottomright", col = c("black", "red", "blue"), lty = 1, legend = c(expression(Phi * " (true)"), expression(Phi[N]* " (prop)"), expression(tilde(Phi)[N]* " (naive)")))
plot(x,truegradient,type="l", ylab = "Derivative", main="Gradient of probability function")
lines(x,estgradient,col="red")
legend("topright", col = c("black", "red"), lty = 1, legend = c(expression(paste(nabla,Phi* " (true)")), expression(paste(nabla,Phi[N]* " (prop)"))))
Visualization of mixing distributions
This part contains a visualization of the density of the vector \(\xi\) for different Beta parameter \(\delta\), both as a heatplot and as a 3 dimensional plot. Default is the value in the example \(\delta=0.5\). A value \(\delta \geq 1\) will not produce a bimodal distribution.
###Heatplot
d<-0.5 # try values d<1 (bimodal) or d>1 (unimodal)
B <- beta(d, d)
# density of \xi at a single point (z1, z2)
fZ <- function(z1, z2) {
integrand <- function(c) {
w<-(1-c^2)^(d-2)
expo<-exp(-(z1^2+(z2-c)^2)/(2*(1-c^2)))
w*expo
}
value <- integrate(integrand, lower = -1, upper = 1,
rel.tol = 1e-6, subdivisions = 200L)$value
const<-1/(2*pi*2^(2*d-1)*B)
const*value
}
# Grid for plotting in the box [-2,2]x[-2,2], modify to taste
z1_seq<-seq(-2, 2, length.out = 80)
z2_seq<-seq(-2, 2, length.out = 80)
Zmat<-outer(z1_seq, z2_seq, Vectorize(function(x, y) fZ(x, y)))
filled.contour(z1_seq, z2_seq, Zmat,
color.palette = terrain.colors,
xlab = "z1", ylab = "z2",
main = bquote("Joint density of " * xi * " for " * delta == .(d)))
### 3D Plot, run previous chunk to obtain parameters
persp(z1_seq, z2_seq, Zmat,
theta = 30, phi = 30, expand = 0.6,
col = "blue", border="lightblue",
xlab = "z1", ylab = "z2", zlab = "density",
main =bquote("Joint density of " * xi * " for " * delta == .(d)))
runtime and error bound comparison for N
##### source true prob and true gradient at chunk starting in line 65
estN50<-rep(0,1000)
estN100<-rep(0,1000)
estN500<-rep(0,1000)
estN1000<-rep(0,1000)
estN5000<-rep(0,1000)
estN10000<-rep(0,1000)
estpN50<-rep(0,1000)
estpN100<-rep(0,1000)
estpN500<-rep(0,1000)
estpN1000<-rep(0,1000)
estpN5000<-rep(0,1000)
estpN10000<-rep(0,1000)
estgN50<-rep(0,1000)
estgN100<-rep(0,1000)
estgN500<-rep(0,1000)
estgN1000<-rep(0,1000)
estgN5000<-rep(0,1000)
estgN10000<-rep(0,1000)
estn50<-rep(0,1000)
estn100<-rep(0,1000)
estn500<-rep(0,1000)
estn1000<-rep(0,1000)
estn5000<-rep(0,1000)
estn10000<-rep(0,1000)
estpn50<-rep(0,1000)
estpn100<-rep(0,1000)
estpn500<-rep(0,1000)
estpn1000<-rep(0,1000)
estpn5000<-rep(0,1000)
estpn10000<-rep(0,1000)
estgn50<-rep(0,1000)
estgn100<-rep(0,1000)
estgn500<-rep(0,1000)
estgn1000<-rep(0,1000)
estgn5000<-rep(0,1000)
estgn10000<-rep(0,1000)
set.seed(123)
for(k in 1:1000){
estradial50<-rep(0,xvalues)
estgradient50<-rep(0,xvalues)
estprop50<-rep(0,xvalues)
estradial100<-rep(0,xvalues)
estgradient100<-rep(0,xvalues)
estprop100<-rep(0,xvalues)
estradial500<-rep(0,xvalues)
estgradient500<-rep(0,xvalues)
estprop500<-rep(0,xvalues)
estradial1000<-rep(0,xvalues)
estgradient1000<-rep(0,xvalues)
estprop1000<-rep(0,xvalues)
estradial5000<-rep(0,xvalues)
estgradient5000<-rep(0,xvalues)
estprop5000<-rep(0,xvalues)
estradial10000<-rep(0,xvalues)
estgradient10000<-rep(0,xvalues)
estprop10000<-rep(0,xvalues)
v<-runif(10000,0,2*pi)
c<-2*rbeta(10000,d,d)-1
x<-seq(xlow,xup, length.out=xvalues)
rs<-rep(0,10000)
for(w in 1:10000){
rs[w]<-rnorm(1,0,sqrt(1-c[w]^2))^2+rnorm(1,c[w],sqrt(1-c[w]^2))^2
}
for(i in 1:xvalues){
rcrit<-(-c*sin(v)+sqrt(x[i]^2+2 -c^2*cos(v)^2))/sqrt(1-c^2)
estradial50[i]<-mean(1-exp(-rcrit[1:50]^2/2))
estgradient50[i]<-mean(x[i]*rcrit[1:50]*exp(-rcrit[1:50]^2/2)/sqrt((x[i]^2+2 -c[1:50]^2*cos(v[1:50])^2)*(1-c[1:50]^2)))
estprop50[i]<-sum(rs[1:50] < 2+x[i]^2)/50
estradial100[i]<-mean(1-exp(-rcrit[1:100]^2/2))
estgradient100[i]<-mean(x[i]*rcrit[1:100]*exp(-rcrit[1:100]^2/2)/sqrt((x[i]^2+2 -c[1:100]^2*cos(v[1:100])^2)*(1-c[1:100]^2)))
estprop100[i]<-sum(rs[1:100] < 2+x[i]^2)/100
estradial500[i]<-mean(1-exp(-rcrit[1:500]^2/2))
estgradient500[i]<-mean(x[i]*rcrit[1:500]*exp(-rcrit[1:500]^2/2)/sqrt((x[i]^2+2 -c[1:500]^2*cos(v[1:500])^2)*(1-c[1:500]^2)))
estprop500[i]<-sum(rs[1:500] < 2+x[i]^2)/500
estradial1000[i]<-mean(1-exp(-rcrit[1:1000]^2/2))
estgradient1000[i]<-mean(x[i]*rcrit[1:1000]*exp(-rcrit[1:1000]^2/2)/sqrt((x[i]^2+2 -c[1:1000]^2*cos(v[1:1000])^2)*(1-c[1:1000]^2)))
estprop1000[i]<-sum(rs[1:1000] < 2+x[i]^2)/1000
estradial5000[i]<-mean(1-exp(-rcrit[1:5000]^2/2))
estgradient5000[i]<-mean(x[i]*rcrit[1:5000]*exp(-rcrit[1:5000]^2/2)/sqrt((x[i]^2+2 -c[1:5000]^2*cos(v[1:5000])^2)*(1-c[1:5000]^2)))
estprop5000[i]<-sum(rs[1:5000] < 2+x[i]^2)/5000
estradial10000[i]<-mean(1-exp(-rcrit[1:10000]^2/2))
estgradient10000[i]<-mean(x[i]*rcrit[1:10000]*exp(-rcrit[1:10000]^2/2)/sqrt((x[i]^2+2 -c[1:10000]^2*cos(v[1:10000])^2)*(1-c[1:10000]^2)))
estprop10000[i]<-sum(rs < 2+x[i]^2)/10000
}
estN50[k]<-sum((trueprob-estradial50)^2)*(xup-xlow)/xvalues
estN100[k]<-sum((trueprob-estradial100)^2)*(xup-xlow)/xvalues
estN500[k]<-sum((trueprob-estradial500)^2)*(xup-xlow)/xvalues
estN1000[k]<-sum((trueprob-estradial1000)^2)*(xup-xlow)/xvalues
estN5000[k]<-sum((trueprob-estradial5000)^2)*(xup-xlow)/xvalues
estN10000[k]<-sum((trueprob-estradial10000)^2)*(xup-xlow)/xvalues
estpN50[k]<-sum((trueprob-estprop50)^2)*(xup-xlow)/xvalues
estpN100[k]<-sum((trueprob-estprop100)^2)*(xup-xlow)/xvalues
estpN500[k]<-sum((trueprob-estprop500)^2)*(xup-xlow)/xvalues
estpN1000[k]<-sum((trueprob-estprop1000)^2)*(xup-xlow)/xvalues
estpN5000[k]<-sum((trueprob-estprop5000)^2)*(xup-xlow)/xvalues
estpN10000[k]<-sum((trueprob-estprop10000)^2)*(xup-xlow)/xvalues
estgN50[k]<-sum((truegradient-estgradient50)^2)*(xup-xlow)/xvalues
estgN100[k]<-sum((truegradient-estgradient100)^2)*(xup-xlow)/xvalues
estgN500[k]<-sum((truegradient-estgradient500)^2)*(xup-xlow)/xvalues
estgN1000[k]<-sum((truegradient-estgradient1000)^2)*(xup-xlow)/xvalues
estgN5000[k]<-sum((truegradient-estgradient5000)^2)*(xup-xlow)/xvalues
estgN10000[k]<-sum((truegradient-estgradient10000)^2)*(xup-xlow)/xvalues
estn50[k]<-max(abs(trueprob-estradial50))
estn100[k]<-max(abs(trueprob-estradial100))
estn500[k]<-max(abs(trueprob-estradial500))
estn1000[k]<-max(abs(trueprob-estradial1000))
estn5000[k]<-max(abs(trueprob-estradial5000))
estn10000[k]<-max(abs(trueprob-estradial10000))
estpn50[k]<-max(abs(trueprob-estprop50))
estpn100[k]<-max(abs(trueprob-estprop100))
estpn500[k]<-max(abs(trueprob-estprop500))
estpn1000[k]<-max(abs(trueprob-estprop1000))
estpn5000[k]<-max(abs(trueprob-estprop5000))
estpn10000[k]<-max(abs(trueprob-estprop10000))
estgn50[k]<-max(abs(truegradient-estgradient50))
estgn100[k]<-max(abs(truegradient-estgradient100))
estgn500[k]<-max(abs(truegradient-estgradient500))
estgn1000[k]<-max(abs(truegradient-estgradient1000))
estgn5000[k]<-max(abs(truegradient-estgradient5000))
estgn10000[k]<-max(abs(truegradient-estgradient10000))
}
par(mfrow=c(2,2))
boxplot(estN50,estN100,estN500,estN1000,estN5000,estN10000, names=c("50","100","500","1000","5000","10000"), main=bquote("Integrated Squared Error of "*Phi[N]), ylim=c(0,0.006), col = "red1")
boxplot(estpN50,estpN100,estpN500,estpN1000,estpN5000,estpN10000, names=c("50","100","500","1000","5000","10000"), main=bquote("Integrated Squared Error of "*tilde(Phi[N])), ylim=c(0,0.006), col = "red1")
boxplot(log(estN50),log(estN100),log(estN500),log(estN1000),log(estN5000),log(estN10000), names=c("50","100","500","1000","5000","10000"), main=bquote("(Log)-Integrated Squared Error of "*Phi[N]),col="palevioletred", ylim=c(-23,-3))
boxplot(log(estpN50),log(estpN100),log(estpN500),log(estpN1000),log(estpN5000),log(estpN10000), names=c("50","100","500","1000","5000","10000"), main=bquote("(Log)-Integrated Squared Error of "*tilde(Phi)[N]),col="palevioletred",ylim=c(-23,-3))
par(mfrow=c(2,1))
boxplot(estgN50,estgN100,estgN500,estgN1000,estgN5000,estgN10000, names=c("50","100","500","1000","5000","10000"), main=bquote("Integrated Squared Error of "*nabla*Phi[N]), col = "plum1")
boxplot(log(estgN50),log(estgN100),log(estgN500),log(estgN1000),log(estgN5000),log(estgN10000), names=c("50","100","500","1000","5000","10000"), main=bquote("(Log-) Integrated Squared Error of "*nabla*Phi[N]), col = "plum1")
par(mfrow=c(2,2))
boxplot(estn50,estn100,estn500,estn1000,estn5000,estn10000, names=c("50","100","500","1000","5000","10000"), main=bquote("Maximum Error of "*Phi[N]), ylim=c(0,0.1), col = "blue")
boxplot(estpn50,estpn100,estpn500,estpn1000,estpn5000,estpn10000, names=c("50","100","500","1000","5000","10000"), main=bquote("Maximum Error of "*tilde(Phi[N])), ylim=c(0,0.1), col = "blue")
boxplot(log(estn50),log(estn100),log(estn500),log(estn1000),log(estn5000),log(estn10000), names=c("50","100","500","1000","5000","10000"), main=bquote("(Log)-Maximum Error of "*Phi[N]),col="lightblue3", ylim=c(-23,-3))
boxplot(log(estpn50),log(estpn100),log(estpn500),log(estpn1000),log(estpn5000),log(estpn10000), names=c("50","100","500","1000","5000","10000"), main=bquote("(Log)-Maximum Error of "*tilde(Phi)[N]),col="lightblue3",ylim=c(-23,-3))
par(mfrow=c(2,1))
boxplot(estgn50,estgn100,estgn500,estgn1000,estgn5000,estgn10000, names=c("50","100","500","1000","5000","10000"), main=bquote("Integrated Squared Error of "*nabla*Phi[N]), col = "skyblue1")
boxplot(log(estgn50),log(estgn100),log(estgn500),log(estgn1000),log(estgn5000),log(estgn10000), names=c("50","100","500","1000","5000","10000"), main=bquote("(Log-) Integrated Squared Error of "*nabla*Phi[N]), col = "skyblue1")