1

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' )

plot of curve showing values diverging at x ~ 0.3

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?

Ben Bolker
  • 211,554
  • 25
  • 370
  • 453
Alien
  • 116
  • 8

1 Answers1

3

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)

plot showing roots and poles

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.

Ben Bolker
  • 211,554
  • 25
  • 370
  • 453
  • I'm trying to reply something similar to equation 14 in the paper [The Allocation of Talent and U.S. Economic Growth](https://onlinelibrary.wiley.com/doi/abs/10.3982/ECTA11427), in the 2012 version. The authors reports θ(1 − η) average 3.12. But they do not provide cv value. Thanks for your advice. – Alien Jun 18 '21 at 00:39
  • Can you edit your question to include more details? The link you include above is to a 2019 paper; I have no idea how I would go about finding a 2012 version of it, and equation 14 in the version you link to looks nothing like your problem. A [Google scholar search](https://scholar.google.com/scholar?hl=en&as_sdt=0%2C5&q=author%3Ahsieh+author%3Ahurst+author%3Ajones&btnG=) doesn't find anything useful (there are references to a 2013 version, but none have any links). – Ben Bolker Jun 18 '21 at 00:54
  • Sorry, 2013 version can be view in this [link](https://economics.sas.upenn.edu/sites/default/files/filevault/event_papers/HHJK_april_2012.pdf) . – Alien Jun 18 '21 at 01:12
  • I looked at the paper a little bit. I think I would start by trying to estimate (theta*(1-eta)) as a single parameter, as the authors did, and *then* retrieve eta by guessing values of theta. That seems less likely to get you into weird ranges where the gamma function behaves oddly. In particular, if you happen to be evaluating the gamma function for negative arguments, weird stuff is going to happen. – Ben Bolker Jun 19 '21 at 01:26
  • I think the key to the issue is the CV value. When CV equals 13.32 a root for the problem is found. And you're right, estimating (theta * (1-eta)) as a single parameter is a good strategy. Thanks for your advice. – Alien Jun 19 '21 at 14:02