R optimize multiple parameters - r
I am using R optim() function to estimate set of parameters which optimize user defined function shown below. But optim() out put is:
Error in optim(pstart, llAgedepfn, method = "L-BFGS-B", upper = up, lower = lo) :
L-BFGS-B needs finite values of 'fn'
Please help. The complete script is shown below:
dataM<-cbind(c(1.91,0.29,0.08,0.02,0.01,0.28,0.45,0.36,0.42,0.17,0.16,0.06,0.17,0.17,0.12),
c(0.27,4.54,0.59,0.05,0.04,0.13,0.48,0.68,0.66,0.18,0.11,0.06,0.08,0.08,0.08),
c(0.07,0.57,4.48,0.48,0.02,0.05,0.09,0.43,0.78,0.52,0.17,0.10,0.05,0.05,0.14),
c(0.02,0.04,0.44,4.34,0.36,0.09,0.07,0.11,0.41,0.77,0.43,0.10,0.03,0.04,0.14),
c(0.01,0.04,0.01,0.36,2.20,0.46,0.19,0.15,0.19,0.34,0.62,0.30,0.09,0.03,0.22),
c(0.22,0.11,0.05,0.09,0.45,0.91,0.61,0.43,0.37,0.26,0.41,0.63,0.29,0.16,0.15),
c(0.31,0.35,0.07,0.05,0.16,0.54,0.81,0.59,0.48,0.36,0.33,0.43,0.47,0.26,0.20),
c(0.22,0.45,0.29,0.08,0.11,0.34,0.53,0.85,0.71,0.39,0.27,0.26,0.26,0.28,0.38),
c(0.22,0.36,0.44,0.26,0.12,0.24,0.36,0.59,0.91,0.61,0.35,0.28,0.20,0.22,0.29),
c(0.09,0.10,0.30,0.49,0.22,0.17,0.28,0.33,0.62,0.80,0.52,0.29,0.20,0.11,0.46),
c(0.10,0.07,0.12,0.32,0.48,0.32,0.30,0.27,0.42,0.61,0.78,0.47,0.33,0.23,0.49),
c(0.04,0.04,0.06,0.08,0.24,0.53,0.41,0.28,0.36,0.36,0.50,0.67,0.51,0.19,0.47),
c(0.10,0.05,0.04,0.02,0.07,0.23,0.43,0.26,0.23,0.23,0.33,0.48,0.75,0.51,0.49),
c(0.05,0.04,0.03,0.05,0.02,0.10,0.19,0.22,0.21,0.10,0.18,0.14,0.40,0.79,0.82),
c(0.03,0.02,0.03,0.03,0.06,0.04,0.06,0.12,0.11,0.18,0.16,0.14,0.16,0.34,1.26)
)
NormCM <- dataM/eigen(CMWkday)$values[1] #Normalizing the contact mtrix - divide by the largest eigen value
w <- c(495,528,548,603,617,634,720,801,957,937,798,755,795,1016,2469)
g2 <- c(770,622,726,559,410,547,564,472,399,397,340,308,337,91,84)
h2 <- c(269,426,556,430,271,284,303,207,194,181,126,106,74,24,23)
z2 <- h2/g2
g1 <- c(774,527,665,508,459,539,543,492,402,412,365,342,213,146,152)
h1 <- c(56,31,84,173,103,85,123,70,71,80,55,25,18,12,26)
z1 <- h1/g1
#### Normal loglikelihood #########
llnormfn <- function(q) {
tol <- 1e-9
final.size.start <- 0.8
zeta <- rep(final.size.start, nrow(NormCM))
last.zeta <- rep(0, nrow(NormCM))
first.run <- T
current.diff <- tol+1
loglik <- 0
while (current.diff > tol) {
zeta <- 1-exp(-(q*(zeta%*%NormCM)))
current.diff <- sum(abs(last.zeta-zeta))
last.zeta <-zeta
}
mu <- c(zeta)
zigma <- z1*(1-z1)/g1 + (z1+mu)*(1-(z1+mu))/g2
logliknorm <- -sum((((z2-z1)-mu)**2)/2*zigma + 0.5*log(2*pi*zigma))
return(logliknorm)
}
pstart <- c(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)
up <- c(5,5,5,5,5,5,5,5,5,5,5,5,5,5,5)
lo <- c(0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1)
estm <- optim(pstart, llnormfn, method = "L-BFGS-B", upper = up, lower = lo )
Your llnormfn doesn't return a finite value for all values of its parameters within the range. For example at the upper limit:
> llnormfn(up)
[1] NaN
Warning message:
In log(2 * pi * zigma) : NaNs produced
Because zigma must be less than zero here.
If you restrict the range a bit you can eventually find a spot where it does work...
> llnormfn(up-2)
[1] NaN
Warning message:
In log(2 * pi * zigma) : NaNs produced
> llnormfn(up-3)
[1] 42.96818
Let's check it works at the lower range:
> llnormfn(lo)
[1] 41.92578
that looks fine. So either you've set that upper limit outside the computationally valid range of your function, or you've got a bug in your llnormfn function, or both, or something else.
If you do run the optimisation with a reduced upper bound you do get convergence:
> estm <- optim(pstart, llnormfn, method = "L-BFGS-B", upper = up-3, lower = lo )
> estm
$par
[1] 1.9042672 1.0891264 0.9916916 0.6208685 1.2413983 1.4822433 1.1243878
[8] 1.5224263 1.3686933 1.4876350 1.6231518 2.0000000 2.0000000 2.0000000
[15] 2.0000000
$value
[1] 38.32182
$counts
function gradient
23 23
$convergence
[1] 0
$message
[1] "CONVERGENCE: REL_REDUCTION_OF_F <= FACTR*EPSMCH"
Although you might notice some of those parameters are at the upper value (2.0) which is an alarm bell.
Check your function behaves sensibly for its input values - try fixing all-but-one and plotting how llnormfn behaves while varying one. I just had a quick look and the function does not look smooth at all, with lots of discontinuities, so I doubt BFGS is a good method for optimising.
e.g varying the fifth parameter between 0.1 and 2:
> s = seq(0.1,2,len=300)
> ss = sapply(1:length(s),function(i){ll=lo;ll[5]=s[i];llnormfn(ll)})
> plot(s,ss)
gives:
Related
Pi Estimator in R
The code below estimates pi in R, now I am trying to find the minimum number of terms N_Min you would have to include in your estimate of pie to make it accurate to three decimal places. pi_Est<- function(NTerms){ NTerms = 5 # start with an estimate of just five terms pi_Est = 0 # initialise the value of pi to zero Sum_i = NA # initialise the summation variable to null for(ii in 1:NTerms) { Sum_i[ii] = (-1)^(ii+1)/(2*ii - 1) # this is the series equation for calculating pi } Sum_i = 4*Sum_i # multiply by four as required in the formula (see lecture notes) pi_Est = sum(Sum_i) cat('\nThe estimate of pi with terms = ', NTerms ,' is ',pi_Est) }
First of all, I would change some things about your function. Instead of getting it to print out a message, get it to return a value. Otherwise it becomes very difficult to do anything with its output, including testing it for convergence to pi. Also, no matter what the value of NTerms is you feed this function, you are immediately over-writing NTerms inside the function. You could rewrite the function like this: pi_Est <- function(NTerms) { pi_Est <- 0 Sum_i <- numeric() for(ii in seq(NTerms)) { Sum_i[ii] <- (-1)^(ii+1)/(2*ii - 1) } return(sum(4 * Sum_i)) } And to show it converges to pi, let's test it with 50,000 terms: pi_Est(50000) #> [1] 3.141573 Now, if we want to find the first value of NTerms that is correct to 3 decimal places, we are going to need to be able to call this function on a vector of NTerms - at the moment it is only working on a single number. So let's define the function f that vectorizes pi_Est: f <- Vectorize(pi_Est) Now, let's create the estimate for all values of NTerms between 1 and 2,000 and store them in a vector: estimates <- f(1:2000) We can see that the values of estimates seem to oscillate round and converge to pi if we plot the first 100 values: plot(estimates[1:100], type = 'l') abline(h = pi) Our answer is just the first value which, when rounded to three decimal places, is the same as pi rounded to three decimal places: result <- which(round(estimates, 3) == round(pi, 3))[1] result #> [1] 1103 And we can check this is correct by feeding 1103 into our original function: pi_Est(result) #> [1] 3.142499 You will see that this gives us 3.142, which is the same as pi rounded to 3 decimal places. Created on 2022-01-31 by the reprex package (v2.0.1)
1000 terms are required to make the estimate accurate to within 0.001: pi_Est1 <- function(n) { if (n == 0) return(0) neg <- 1/seq(3, 2*n + 1, 4) if (n%%2) neg[length(neg)] <- 0 4*sum(1/seq(1, 2*n, 4) - neg) } pi_Est2 <- function(tol) { for (i in ceiling(1/tol + 0.5):0) { est <- pi_Est1(i) if (abs(est - pi) > tol) break est1 <- est } list(NTerms = i + 1, Estimate = est1) } tol <- 1e-3 pi_Est2(tol) #> $NTerms #> [1] 1000 #> #> $Estimate #> [1] 3.140593 tol - abs(pi - pi_Est2(tol)$Estimate) #> [1] 2.500001e-10 tol - abs(pi - pi_Est1(pi_Est2(tol)$NTerms - 1)) #> [1] -1.00075e-06 Created on 2022-01-31 by the reprex package (v2.0.1)
Perhaps we can try the code below pi_Est <- function(digits = 3) { s <- 0 ii <- 1 repeat { s <- s + 4 * (-1)^(ii + 1) / (2 * ii - 1) if (round(s, digits) == round(pi, digits)) break ii <- ii + 1 } list(est = s, iter = ii) } and you will see > pi_Est() $est [1] 3.142499 $iter [1] 1103 > pi_Est(5) $est [1] 3.141585 $iter [1] 130658
Why not use a single line of code for the calculation? Pi <- tail(cumsum(4*(1/seq(1,4*50000000,2))*rep(c(1,-1), 50000000)),1)
Optimization of optim() in R ( L-BFGS-B needs finite values of 'fn')
I'm having some trouble using optim() in R to solve for a likelihood involving an integral. I get an error that says "Error in optim(par = c(0.1, 0.1), LLL, method = "L-BFGS-B", lower = c(0, : L-BFGS-B needs finite values of 'fn'". Below is my code: s1=c(1384,1,1219,1597,2106,145,87,1535,290,1752,265,588,1188,160,745,237,479,39,99,56,1503,158,916,651,1064,166,635,19,553,51,79,155,85,1196,142,108,325 ,135,28,422,1032,1018,128,787,1704,307,854,6,896,902) LLL=function (par) { integrand1 <- function(x){ (x-s1[i]+1)*dgamma(x, shape=par[1], rate=par[2]) } integrand2 <- function(x){ (-x+s1[i]+1)*dgamma(x, shape=par[1],rate=par[2]) } likelihood = vector() for(i in 1:length(s1)) {likelihood[i] = log( integrate(integrand1,lower=s1[i]-1,upper=s1[i])$value+ integrate(integrand2,lower=s1[i],upper=s1[i]+1)$value ) } like= -sum(likelihood) return(like) } optim(par=c(0.1,0.1),LLL,method="L-BFGS-B", lower=c(0,0)) Thanks for your help. Best, YM
The objective function evaluated at the lower bounds of the parameters you provided is infinity. LLL(c(0,0)) # [1] Inf That's why L-BFGS-B fails. Try a different lower bound, e.g., c(0.001,0.001) and you will get a solution. optim(par=c(0.1,0.1),LLL,method="L-BFGS-B", lower=c(0.001,0.001)) $par [1] 0.6865841 0.0010000 $value [1] 369.5532 $counts function gradient 14 14 $convergence [1] 0 $message [1] "CONVERGENCE: REL_REDUCTION_OF_F <= FACTR*EPSMCH" To get the 95% confidence intervals for the parameters try this: res <- optim(par=c(0.1,0.1),LLL,method="L-BFGS-B", lower=c(0.005,0.005), hessian=TRUE) n <- length(s1) res$par # solution # [1] 1.900928 0.005000 res$par - 1.96*sqrt(diag(solve(res$hessian)))/n # lower limit for 95% confint # [1] 1.888152372 0.004963286 res$par + 1.96*sqrt(diag(solve(res$hessian)))/n # upper limit for 95% confint # [1] 1.913703040 0.005036714 refer to this article: http://www.ms.uky.edu/~mai/sta321/MLEexample.pdf
Constraint Optimization with one parameter included in the constraint of the other
I want to calculate the following So I want to find Theta and Sigma that maximizes the function. The constraints are: > Theta>-Sigma > -1<Sigma<1 So one of my problem is that I dont know how to deal with the fact that one parameter is included in the constraint of the other Parameter, that I want to optimize over. I tried with optim(), constrOptim and dfoptim! Using optim(): k=8 i=1:(k-1) x=c(5,0.2) n=24 nj=c(3,4,8,1,1,4,2,1) EPPF <- function(x,n,nj) { y=(x[1]+1):(x[1]+1+(n-1)-1) z=-(prod(x[1]+i*x[2])/(prod(y))*prod(sapply(nj, hfun))) return(z)} hfun <- function(p){ h=(1-x[2]):((1-x[2])+p-1) hfun=prod(h) return(hfun) } > optim(c(6,0.3), fn=EPPF,method = "L-BFGS-B", n=n,nj=nj, lower = c(-x[1],-1), upper = c(Inf,1)) $par [1] 6.0 0.3 $value [1] -1.258458e-15 $counts function gradient 2 2 $convergence [1] 0 $message [1] "CONVERGENCE: REL_REDUCTION_OF_F <= FACTR*EPSMCH" I also tried using a constructor function: make.EPPF <- function(n,nj,fixed=c(FALSE,FALSE)){ params <-fixed function(p) { hfun <- function(y){ h=(1-sigma):((1-sigma)+y-1) hfun=prod(h) return(hfun) } params[!fixed] <- p theta <- params[1] sigma <- params[2] y=(theta+1):(theta+1+(n-1)-1) z=(prod(theta+i*sigma)/(prod(y))*prod(sapply(nj, hfun))) z } } EPPF <-make.EPPF (n,nj) > optim(c(theta=6, sigma=0.5), fn=EPPF,method = "L-BFGS-B",lower = c(-sigma,-1), upper = c(Inf,1)) Error in optim(c(theta = 6, sigma = 0.5), fn = EPPF, method = "L-BFGS-B", : object 'sigma' not found Using constrOptim(): > A <- matrix(c(1,1,0,1,0,-1),3,2,byrow=T) > b <- c(0,-1,-1) > > constrOptim(c(3,0.3),EPPF,NULL,A,b, control=list(fnscale=-1)) $par [1] 3.0 0.3 $value [1] 9.712117e-16 $counts [1] 0 $convergence [1] 0 $message NULL $outer.iterations [1] 1 $barrier.value [1] 7.313452e-05 Using Package dfoptim: > library(dfoptim) > nmkb(x=c(6,0.3), EPPF, lower=c(-x[2],-1), upper=c(Inf, 1 )) Error in par < lower : comparison (3) is possible only for atomic and list types Either there is for some reasons no convergence or some other Errors. I am relativ new to programming and R and would really appreciate if someone could help me. Thanks!
These are 3 linear inequality constraints: sigma + theta > 0 sigma + 1 > 0 -sigma + 1 > 0 You can do this in maxLik. But note that maxLik maximizes the function, hence remove the '-' in front of z. Here is the code that works for me (using Rscript): k=8 i=1:(k-1) x=c(5,0.2) n=24 nj=c(3,4,8,1,1,4,2,1) EPPF <- function(x,n,nj) { theta <- x[1] sigma <- x[2] y=(x[1]+1):(x[1]+1+(n-1)-1) z <- prod(x[1]+i*x[2])/(prod(y))*prod(sapply(nj, hfun)) z <- log(z) return(z) } hfun <- function(p){ h=(1-x[2]):((1-x[2])+p-1) hfun=prod(h) return(hfun) } library(maxLik) constraints <- list(ineqA=matrix(c(1,0,0,1,1,-1),3,2), ineqB=c(0,1,1)) m <- maxBFGS(EPPF, start=c(6,0.3), constraints=constraints, n=n, nj=nj) print(summary(m)) I also took logarithm of the result as this leads to more "human" numbers. Otherwise you have to re-tune the stopping conditions. The answer seems to be -1, 1.
A strange error from an algorithm
I have written this function that computes the MLE from a Cauchy distribution numerically based on the Newton-Raphson algorithm: mlec <- function(x,theta0=median(x),numstp=100,eps=0.01){ numfin <- numstp ic <- 0 istop <- 0 while(istop==0){ ic <- ic+1 ltheta <- -2*sum((x-theta0)/(1+(x-theta0)^2)) lprimetheta <- -2*(sum(2*(x-theta0)^2/ (1+(x-theta0)^2)^2-1/(1+(x-theta0)^2)^2)) theta1 <- theta0-(ltheta/lprimetheta) check <- abs((theta1-theta0)/theta1) if(check < eps ) { istop <- 1 } theta0 <- theta1 } list(theta1=theta1,check=check,realnumstps=ic) } The goal is then to generate observations from a Cauchy distribution with scale parameter 2 and see how the MLE performs. The problem is that while for some samples, the MLE runs wonderfully for others I get the strange error Error in if (check < eps) { : missing value where TRUE/FALSE needed What is going on here? I have defined what "check" is so that shouldn't happen. Thank you.
I've added a little bit of instrumentation (see the cat() statement in the middle), and fixed the second-derivative expression (fixed: see below): mlec <- function(x,theta0=median(x),numstp=100,eps=0.01, debug=TRUE,fixed=FALSE){ numfin <- numstp ic <- 0 istop <- 0 while(istop==0){ ic <- ic+1 ltheta <- -2*sum((x-theta0)/(1+(x-theta0)^2)) lprimetheta <- -2*(sum(2*(x-theta0)^2/ (1+(x-theta0)^2)^2-1/(1+(x-theta0)^2)^2)) if (!fixed) { theta1 <- theta0-(ltheta/lprimetheta) } else theta1 <- theta0-ltheta/ff(theta0) check <- abs((theta1-theta0)/theta1) if (debug) cat(ic,ltheta,lprimetheta,theta0,theta1,check,"\n") if(check < eps ) { istop <- 1 } theta0 <- theta1 } list(theta1=theta1,check=check,realnumstps=ic) } set.seed(1) x <- rcauchy(100,2) mlec(x) Here's the tail end of the output: ## ic ltheta lprimetheta theta0 theta1 check ## 427 -4.48838e-75 -2.014555e-151 -4.455951e+76 -6.683926e+76 0.3333333 ## 428 -2.992253e-75 -8.953579e-152 -6.683926e+76 -1.002589e+77 0.3333333 ## 429 -1.994835e-75 -3.979368e-152 -1.002589e+77 -1.503883e+77 0.3333333 ## 430 -1.32989e-75 0 -1.503883e+77 -Inf NaN Now, why is it happening? Either you've got a bug somewhere, or the algorithm is unstable. tl;dr it turns out the answer is actually "both"; your second-derivative expression seems wrong, but even it were correct the N-R algorithm is extremely unstable for this problem. Here are your derivative and second-derivative functions (I'm wrapping them with Vectorize() for convenience so I can evaluate these at multiple theta values simultaneously): lthetafun <- Vectorize(function(theta) { -2*sum((x-theta)/(1+(x-theta)^2)) }) lprimethetafun <- Vectorize(function(theta) { -2*(sum(2*(x-theta)^2/ (1+(x-theta)^2)^2-1/(1+(x-theta)^2)^2)) }) A negative log-likelihood function based on the built-in dcauchy function: thetafun <- Vectorize(function(theta) -sum(dcauchy(x,theta,log=TRUE))) Check differentiation (at an arbitrarily chosen point): library("numDeriv") all.equal(grad(thetafun,2),lthetafun(2)) ## TRUE Check second derivative: hessian(thetafun,2) ## 36.13297 lprimethetafun(2) ## 8.609859: ??? I think your second-derivative expression is wrong. The following alternative second-derivative function is based on lazily cheating with Wolfram Alpha, differentiating your gradient function (which matches with the finite-difference approximation): ff <- Vectorize(function(theta) 2*sum(((x-theta)^2-1)/((x-theta)^2+1)^2)) ff(2) ## matches hessian() above. But it looks like you may have further problems. The negative log-likelihood surface looks OK: curve(thetafun, from=-10,to=10,n=501) But trouble is on the horizon: curve(lthetafun, from=-10,to=10, n=501) This looks irregular, and going up one level to the second derivative shows that it is: curve(ff, from=-10, to=10, n=501) Here's the curve of N-R updates: ff2 <- function(x) x-lthetafun(x)/ff(x) curve(ff2, from=-10, to=10, n=501,ylim=c(-100,100)) Yikes! This indicates why the Newton-Raphson method could go wrong unless you start close enough to the minimum (any time the likelihood surface has an inflection point, the N-R updating curve will have a pole ...). Further analysis of the problem would probably tell you why the second derivative of the Cauchy is so scary. If you just want to find the MLE you can do it by some more robust 1-D method: library("bbmle") mle2(x~dcauchy(location=m), data=data.frame(x), start=list(m=median(x)), method="Brent", lower=-100,upper=100) ## ## Call: ## mle2(minuslogl = x ~ dcauchy(location = m), start = list(m = median(x)), ## method = "Brent", data = data.frame(x), lower = -100, upper = 100) ## ## Coefficients: ## m ## 1.90179 ## ## Log-likelihood: -262.96 ## If you start close enough, N-R seems to work OK: mlec(x,1.85,debug=FALSE,fixed=TRUE,eps=0.0001) ## $theta1 ## [1] 1.901592 ## ## $check ## [1] 5.214763e-05 ## ## $realnumstps ## [1] 37079
constrained optimization in R
I am trying to use http://rss.acs.unt.edu/Rdoc/library/stats/html/constrOptim.html in R to do optimization in R with some given linear constraints but not able to figure out how to set up the problem. For example, I need to maximize $f(x,y) = log(x) + \frac{x^2}{y^2}$ subject to constraints $g_1(x,y) = x+y < 1$, $g_2(x,y) = x > 0$ and $g_3(x,y) = y > 0$. How do I do this in R? This is just a hypothetical example. Do not worry about its structure, instead I am interested to know how to set this up in R. thanks!
Setting up the function was trivial: fr <- function(x) { x1 <- x[1] x2 <- x[2] -(log(x1) + x1^2/x2^2) # need negative since constrOptim is a minimization routine } Setting up the constraint matrix was problematic due to a lack of much documentation, and I resorted to experimentation. The help page says "The feasible region is defined by ui %*% theta - ci >= 0". So I tested and this seemed to "work": > rbind(c(-1,-1),c(1,0), c(0,1) ) %*% c(0.99,0.001) -c(-1,0, 0) [,1] [1,] 0.009 [2,] 0.990 [3,] 0.001 So I put in a row for each constraint/boundary: constrOptim(c(0.99,0.001), fr, NULL, ui=rbind(c(-1,-1), # the -x-y > -1 c(1,0), # the x > 0 c(0,1) ), # the y > 0 ci=c(-1,0, 0)) # the thresholds For this problem there is a potential difficulty in that for all values of x the function goes to Inf as y -> 0. I do get a max around x=.95 and y=0 even when I push the starting values out to the "corner", but I'm somewhat suspicious that this is not the true maximum which I would have guessed was in the "corner". EDIT: Pursuing this I reasoned that the gradient might provide additional "direction" and added a gradient function: grr <- function(x) { ## Gradient of 'fr' x1 <- x[1] x2 <- x[2] c(-(1/x[1] + 2 * x[1]/x[2]^2), 2 * x[1]^2 /x[2]^3 ) } This did "steer" the optimization a bit closer to the c(.999..., 0) corner, instead of moving away from it, as it did for some starting values. I remain somewhat disappointed that the process seems to "head for the cliff" when the starting values are close to the center of the feasible region: constrOptim(c(0.99,0.001), fr, grr, ui=rbind(c(-1,-1), # the -x-y > -1 c(1,0), # the x > 0 c(0,1) ), # the y > 0 ci=c(-1,0, 0) ) $par [1] 9.900007e-01 -3.542673e-16 $value [1] -7.80924e+30 $counts function gradient 2001 37 $convergence [1] 11 $message [1] "Objective function increased at outer iteration 2" $outer.iterations [1] 2 $barrier.value [1] NaN Note: Hans Werner Borchers posted a better example on R-Help that succeeded in getting the corner values by setting the constraint slightly away from the edge: > constrOptim(c(0.25,0.25), fr, NULL, ui=rbind( c(-1,-1), c(1,0), c(0,1) ), ci=c(-1, 0.0001, 0.0001)) $par [1] 0.9999 0.0001