Find root using uniroot - r

I'm trying to find a root of the following function (based on the Gamma (gamma()) function) using the uniroot() function:
cv = 0.056924/1.024987^2
fx2 = function(theta, eta){
p1 = 1 - 2/(theta*(1-eta))
p2 = 1 - 1/(theta*(1-eta))
return(( gamma(p1)/(gamma(p2))^2 ) - (cv+1) )
}
This function gives me the following plot:
v = seq(0, 1, 0.01)
plot(v, fx2(3.0, v), type='l' )
It seems to me that the root of this function is close to 0.33, but the uniroot() function doesn't find the root, returning the following result:
uniroot(fx2, interval = c(0,0.3), theta=3 )
Error in uniroot(fx2, interval = c(0, 0.3), theta = 3) :
f() values at end points not of opposite sign
How do I find the root of this function? Are there any other packages with a more accurate algorithm?

I first rewrote your function to (optionally) express gamma(p1)/gamma(p2)^2 in terms of a computation that's first done on the log scale (via lgamma()) and then exponentiated. This is more numerically stable, and the consequences will become clear below ... (It's possible that I screwed up the log-scale computation — you should double-check it. Update/warning: reading the documentation more carefully (!!), lgamma() evaluates to the log of the absolute value of the gamma function. So there may be some weird sign stuff going on in the answer below. The fact remains that if you are evaluating ratios of gamma functions for x<0 (i.e. in the regime where the value can go negative), Bad Stuff is very likely going to happen.
cv = 0.056924/1.024987^2
fx3 <- function(theta, eta, lgamma = FALSE) {
p1 <- 1 - 2/(theta*(1-eta))
p2 <- 1 - 1/(theta*(1-eta))
if (lgamma) {
val <- exp(lgamma(p1) - 2*lgamma(p2)) - (cv+1)
} else {
val <- ( gamma(p1)/(gamma(p2))^2 ) - (cv+1)
}
}
Compute the function with and without log-scaling:
x <- seq(0, 1, length.out = 20001)
v <- sapply(x, fx3, theta = 3.0, lgamma = TRUE)
v2 <- sapply(x, fx3, theta = 3.0, lgamma = FALSE)
Find root (more explanation below):
uu <- uniroot(function(eta) fx3(3.0, eta, lgamma = TRUE),
c(0.4, 0.5))
Plot it:
par(las=1, bty="l")
plot(x, abs(v), col = as.numeric(v<0) + 1, type="p", log="y",
pch=".", cex=3)
abline(v = uu$root, lty=2)
cvec <- sapply(c("blue","magenta"), adjustcolor, alpha.f = 0.2)
points(x, abs(v2), col=cvec[as.numeric(v2<0) + 1], pch=".", cex=3)
Here I'm plotting the absolute value on a log scale, with sign indicated by colour (black/blue >0, red>magenta <0). Black/red is the log-scale calculation, blue/magenta is the original calculation. I also plotted the function at very high resolution to try to avoid missing or mischaracterizing features.
There's a lot of weird stuff going on here.
both versions of the function do something interesting near x=1/3; the original version looks like a pole (value diverges to +∞, "returns" from -∞), while the log-scale computation goes up to +∞ and returns without changing sign.
the log-scale computation has a root near x=0.45 (absolute value becomes small while the sign flips), but the original computation doesn't — presumably because of some kind of catastrophic loss of precision? If we give uniroot bounds that don't include the pole, it can find this root.
there are further poles and/or roots at larger values of x that I didn't explore.
All of this basically says that it's pretty dangerous to mess around with this function without knowing what its mathematical properties are. I discovered some stuff by numerical exploration, but it would be best to analyze the function so that you really know what's happening; any numerical exploration can be fooled if the function is sufficiently strangely behaved.

Related

Plotting an 'n' sized vector between a given function with given interval in R

Let me make my question clear because I don't know how to ask it properly (therefore I don't know if it was answered already or not), I will go through my whole problem:
There is a given function (which is the right side of an explicit first order differential equation if it matters):
f = function(t,y){
-2*y+3*t
}
Then there's a given interval from 'a' to 'b', this is the range the function is calculated in with 'n' steps, so the step size in the interval (dt) is:
dt=abs(a-b)/n
In this case 'a' is always 0 and 'b' is always positive, so 'b' is always greater than 'a' but I tried to be generic.
The initial condition:
yt0=y0
The calculation that determines the vector:
yt=vector("numeric",n)
for (i in 1:(n-1))
{
yt[1]=f(0,yt0)*dt+yt0
yt[i+1]=(f(dt*i,yt[i]))*dt+yt[i]
}
The created vector is 'n' long, but this is an approximate solution to the differential equation between the interval ranging from 'a' to 'b'. And here comes my problem:
When I try plotting it alongside the exact solution (using deSolve), it is not accurate. The values of the vector are accurate, but it does not know that these values belong to an approximate function that's between the interval range 'a' to 'b' .
That's why the graphs of the exact and approximate solution are not matching at all. I feel pretty burnt out, so I might not describe my issue properly, but is there a solution to this? To make it realise that its values are between 'a' and 'b' on the 'x' axis and not between '1' and 'n'?
I thank you all for the answers in advance!
The deSolve lines I used (regarding 'b' is greater than 'a'):
df = function(t, y, params) list(-2*y+3*t)
t = seq(a, b, length.out = n)
ddf = as.data.frame(ode(yt0, t, df, parms=NULL))
I tried to reconstruct the comparison between an "approximate" solution using a loop (that is in fact the Euler method), and a solution with package deSolve. It uses the lsoda solver by default that is more precise than Euler'S method, but it is of course also an approximation (default relative and absolute tolerance set to 1e-6).
As the question missed some concrete values and the plot functions, it was not clear where the original problem was, but the following example may help to re-formulate the question. I assume that the problem may be confusion between t (absolute time) and dt between the two approaches. Compare the lines marked as "original code" with the "suggestion":
library(deSolve)
f = function(t, y){
-2 * y + 3 * t
}
## some values
y0 <- 0.1
a <- 3
b <- 5
n <- 100
## Euler method using a loop
dt <- abs(a-b)/n
yt <- vector("numeric", n)
yt[1] <- f(0, y0) * dt + y0 # written before the loop
for (i in 1:(n-1)) {
#yt[i+1] = (f( dt * i, yt[i])) * dt + yt[i] # original code
yt[i+1] <- (f(a + dt * i, yt[i])) * dt + yt[i] # suggestion
}
## Lsoda integration wit package deSolve
df <- function(t, y, params) list(-2*y + 3*t)
t <- seq(a, b, length.out = n)
ddf = as.data.frame(ode(y0, t, df, parms=NULL))
## Plot of both solutions
plot(ddf, type="l", lwd=5, col="orange", ylab="y", las=1)
lines(t, yt, lwd=2, lty="dashed", col="blue")
legend("topleft", c("deSolve", "for loop"),
lty=c("solid", "dashed"), lwd=c(5, 2), col=c("orange", "blue"))

R: Inverse fft() to confirm my manual DFT algorithm inaccurate?

Using R, before assessing some metric of accuracy on my own manual implementation of DFT, I wanted to do a sanity check on how well stats::fft() performs by doing the following:
sig.ts = ts( sin(2*pi*freq1*t) + sin(2*pi*freq2*t) );
sig.rt = fft(fft(sig.ts)/N, inverse="true");
#the two plots so perfectly align that you can't see them both
max(abs(sig.ts - sig.rt)) / max(sig.ts);
#arbitrary crude accuracy metric=1.230e-15 - EXCELLENT!
But I wanted to write the code for DFT myself, to ensure I understand it, then invert it in the hopes that it would be the same:
##The following is the slow DFT for now, not the FFT...
sR = 102.4; #the number of Hz at which we sample
freq1=3; freq2=12; #frequency(ies) of the wave
t = seq(1/sR,10, 1/sR);
sig.ts = ts( sin(2*pi*freq1*t) + sin(2*pi*freq2*t) );
N=length(t); kk=seq(0,N/2-1, 1); nn=seq(0,N-1, 1);
for(k in kk){
sig.freqd[k]=0;
for(n in nn){
sig.freqd[k] = sig.freqd[k] + sig.ts[n+1]*exp(-j*2*pi*n*k/N); } }
sig.freqd = (1/N)*sig.freqd; #for Normalization
#Checking the "accuracy" of my manual implementation of DFT...
sig.freqd_inv=Re(fft(sig.freqd, inverse="true"));
plot(t[1:100], window(sig.ts,end=100), col="black", type="l",lty=1,lwd=1, xaxt="n");
lines(t[1:100],window(sig.freqd_inv,end=100), col="red", type="l",lty=1,lwd=1, xaxt="n");
axis(1, at=seq(round(t[1],1),round(t[length(t)],1), by=0.1), las=2);
max(abs(sig.ts[1:(N/2-1)] - sig.freqd_inv)) / max(sig.ts[1:(N/2-1)]); #the metric here =1.482 unfortunately
Even without the metric, the plot makes it obvious that something's off here - it's lower amplitude, maybe out of phase, and more jagged. In all of my self-studying, I will say that I am a bit confused about how sensitive this all is to vector length..as well as how to ensure that the imaginary component's phase information is taken into account when plotting.
Bottom line, any insight into what's wrong with my DFT algorithm would be helpful. I don't want to just blackbox my use of functions - I want to understand these things more deeply before moving on to more complicated functions.
Thanks,
Christian
The main issues arise from the signal indexing. First to get a full transform usable by R's fft(..., inverse = TRUE), you would need to compute all N coefficients (even if the coefficients above N/2-1 could be obtained by symmetry).
Then you should realize that array indexing in R are 1-based. So, while indexing sig.freqd[k], the index k should start at 1 instead of 0. Since the argument to exp(-1i*2*pi*n*k/N) should start with n=0andk=0`, you'll need to adjust the indices:
kk=seq(1,N, 1); nn=seq(1,N, 1);
for(k in kk){
sig.freqd[k]=0i;
for(n in nn){
sig.freqd[k] = sig.freqd[k] + sig.ts[n]*exp(-1i*2*pi*(n-1)*(k-1)/N);
}
}
I've also changed you usage of j to represent the imaginary number 1i since that's the usual notation recognized by R (and R was complaining about it when trying your posted sample as-is). If you had defined j=1i that shouldn't affect the results.
Note also that R's fft is unnormalized. So to obtain the same result for the forward transform, your DFT implementation should not include the 1/N normalization. On the other hand, you will need to add this factor as a final step in order to get the full-circle forward+backward transform to match the original signal.
With these changes you should have the following code:
##The following is the slow DFT for now, not the FFT...
sR = 102.4; #the number of Hz at which we sample
freq1=3; freq2=12; #frequency(ies) of the wave
t = seq(1/sR,10, 1/sR);
sig.ts = ts( sin(2*pi*freq1*t) + sin(2*pi*freq2*t) );
N=length(t); kk=seq(1,N, 1); nn=seq(1,N, 1);
for(k in kk){
sig.freqd[k]=0i;
for(n in nn){
sig.freqd[k] = sig.freqd[k] + sig.ts[n]*exp(-1i*2*pi*(n-1)*(k-1)/N);
}
}
#Checking the "accuracy" of my manual implementation of DFT...
sig.freqd_inv=(1/N)*Re(fft(sig.freqd, inverse="true"));
plot(t[1:100], window(sig.ts,end=100), col="black", type="l",lty=1,lwd=2, xaxt="n");
lines(t[1:100],window(sig.freqd_inv,end=100), col="red", type="l",lty=2,lwd=1, xaxt="n");
axis(1, at=seq(round(t[1],1),round(t[length(t)],1), by=0.1), las=2);
max(abs(sig.ts - sig.freqd_inv)) / max(sig.ts)
This should yield a metric around 1.814886e-13, which is probably more in line with what you were expecting. The corresponding plot should also be showing the orignal signal and the roundtrip signal overlapping:

r density function of convolution of exponentially distributed r.v. sometimes produces wrong values

I'm trying to replicate a plot in figure 3.4 in "Coalescent Theory: An introduction" by John Wakely. It consists of the density function of "T total", which I will not define further.
My problem is that I cant seem to correctly write a function for the distribution function.
The distribution function should be a standard convolution of (n-1) random exponentially distributed variables, where the rate of the i'th r.v. is (i/2). The basic distribution can be written as:
I think I have coded this correctly:
DistExpConv <- function(lambdas, t) {
product = function(vector, entrance) {
sapply(entrance, function(x){prod(vector[-x] / (vector[-x] - vector[x]))})
}
sapply(t, function(y){
sum(sapply(lambdas, function(x){
x * exp(-x * y) * product(lambdas, which(lambdas == x))
}))
})
}
I think this should give the correct response, at least I cannot see, where it should be wrong. I have then implemented the distribution of T total:
T_totaldist <- function(n, t) {
lambdas = sapply(2:n, function(x){ (x-1)/2 })
DistExpConv(lambdas , t)
}
As seen, this function just forms n-1 lambdas and then sends them into DistExpConv.
So far so good. My problem arises when trying to plot T_totaldist for n = 100 using the curve function:
n = c(2, 5, 10, 20, 50, 100)
col = rainbow(length(n))
for(i in 1:length(n)){
curve(T_totaldist(n = n[i], x), 0, 14, col = col[i], add = i!=1)
}
legend(8,0.5,paste("n = ",n, sep = ""), col=col, lty=1)
The (in this case pink) curve produced by n = 100 jumps in and out into the extreme negatives and positives. Thus I must conclude my previous functions are doing something wrong. The weird thing is, that the curves of the other n's look perfectly as they should, and from t >= 2 the pink curve is also correct. So I think I might be producing some error for small values of t?
I have no idea how to proceed in making the plot pretty.
I can see in the x- and y-values of the curve, that e.g. T_totaldist(100, 0.14) outputs 4.252961e+13 while T_totaldist(100, 0.28) outputs -4.982278e+10. This is clearly not what I want, since the density should be very close to zero at these small values for t, and since the density should never be negative.

Getting the ellipse function to match the dataEllipse function in R

So I am sampling from a multivariate normal distribution in R, and am trying to figure out how to calculate its 95% confidence ellipse using the ellipse() function in the package car.
Here is some code I am running:
mu = c(0,0)
sigma = matrix(c(20,0,0,45),nrow=2)
z = rmvnorm(10000,mu,sqrt(sigma))
par(mfrow=c(1,2))
plot(z)
ellipse(mu,sqrt(sigma*qchisq(.05,2)),radius=1)
dataEllipse(z,levels=.95)
So basically I want the ellipse command to replicate the dataEllipse command. If anyone has any suggestions that would be greatly appreciated!
Edit: Using Dwins code and combining it within my own:
library(car)
library(mvtnorm)
mu = c(0,0)
sigma = matrix(c(20,0,0,45),nrow=2)
z = rmvnorm(10000,mu,sqrt(sigma))
dataEllipse(z,levels=.95)
car::ellipse(mu, sigma*qchisq(.05,2), col="blue",
radius=sqrt(2 * qf(.975, 2, 9998)) )
So as you can see, the ellipses are still not the same...
I'm guessing (although you should not have made me do so) that rmvnorm is from 'mixtools' and it was loaded after 'car'. I do not think the sqrt() function is needed since the argument to ellipse is supposed to be a covariance matrix. Also at the moment it is plotting but you cannot see it, because you didn't color it red (or anything). Furthermore both 'mixtools' and 'car' have ellipse functions, so if you want the car-version ( which does have a radius argument unlike the mixtools version) then you need to call it with the double colon convention:
library(car); library(mixtools)
car::ellipse(mu, sigma*qchisq(.05,2), col="red",
radius=sqrt(2 * qf(.975, 2, 9998)) )
Since this post is still getting views, I'll provide the actual answer. The last three lines of this code snippet replicates car::dataEllipse exactly:
library(car)
library(mvtnorm)
mu = c(0,0)
sigma = matrix(c(20,0,0,45),nrow=2)
z = rmvnorm(10000,mu,sigma)
dataEllipse(z,levels=.95)
center <- apply(z, 2, mean)
cov_mat <- cov(z)
ellipse(center, cov_mat, col="red", radius=sqrt(2 * qf(.95, 2, 9999)))
Note that both car::dataEllipse and car::ellipse return the coordinates of the points silently, so one can confirm that the points are indeed equal.

R plot implicit function outer command

I would like to plot an implicit function of x and y: 1 - 0.125 * y ^ 2 - x ^ 2 = 0.005
I know it can be plotted as a contour plot but have trouble with the "outer" command
in the following:
x<-seq(0.4,1.01,length=1000)
y<-seq(0,3,length=1000)
z<-outer(x,y,FUN="1-0.125*y^2-x^2=0.005")
contour(x,y,z,levels=0,drawpoints=FALSE)
I read the FAQ (7.17) regarding the "outer" command and the need to vectorize the function but am still in a quandry.
I think you're a little confused about the meaning of 'function'.
All the operations (+,-,^) in your function are vectorized so that all works just fine.
x <- seq(0.4,1.01,length=1000)
y <- seq(0,3,length=1000)
z <- outer(x,y,function(x,y) 1-0.125*y^2-x^2-0.005)
contour(x,y,z,levels=0,drawlabels=FALSE)
Or if you want a minor shortcut:
library(emdbook)
curve3d(1-0.125*y^2-x^2-0.005,
xlim=c(0.4,1.01),
ylim=c(0,3),
n=c(100,100),
sys3d="contour",drawlabels=FALSE,levels=0)
This actually is slower because it uses a for loop internally rather than outer(), so I set it to 100x100 rather than 1000x1000 (which is overkill for this example anyway), but it will work on more complex examples that can't be vectorized easily ...
plotting an implicit equation as a contour plot is overkill. You are essentially throwing away 99.99% of the computations you did.
Better way is to find the value of y for a given x that will make the equation 0. Here is the code using uniroot in base R.
R code using uniroot from base R
x = seq(0, 0.995, length = 100) # no real root above x = 0.995
root <- function(a) {
uniroot(function(x,y) 1 - 0.125 * y^2 - x^2 - 0.005, c(0, 3), x = a )$root #only care about the root
}
y <- sapply(x, root)
plot(x,y, type = "l")
Ok the c(0, 3) in the uniroot argument is the range of y values where the root lies. so for every given x value uniroot will look for a y value between 0 and 3 for a root.
R code using fsolve from pracma package
library("pracma")
x <- seq(0,0.995, length=100)
fun <- function(y) c(1 - 0.125 * y^2 - x^2 - 0.005)
y_sol <- fsolve(fun, rep(1, length(x)))$x
plot(x,y_sol, type="l")
fsolve takes in the function whose root is sought and guess values of y for every given value of x. Here we are saying that the y values lie near 1. we are giving it a guess value of 1's
uniroot wants a bound range to look for root, fsolve requires a guess where the root might be.
These are faster ways to plot implicit equations. You can then use any graph package like ggplot2/rbokeh to do the plotting.
I haven't done any benchmarks so cannot tell which one method is faster. Though for such a simple implicit function it won't matter

Resources