Generalize optimize using optim - jointly minimize differences

Viewed 189

I'd like to minimize several differences. For one difference, this seems straight forward:

target1 <- 1.887
data <- seq(0,1, by=.001)

#Step 1
somefunction <- function(dat, target1, X){

  #some random function...
  t <- sum(dat)
  y <- t * X

  #minimize this difference
  diff <- target1-y

  return(diff)
}

V1 <- optimize(f = somefunction,
              interval = c(0,1),
              dat=data, 
              target1=target,
              maximum = T)
V1$maximum 
6.610696e-05
#--> This value for `X` should minimize the difference...

V1$maximum * sum(data)
#0.03308653
#--> as close to zero we get

Now, I'd like to minimize several differences in one step relying on optim but this does not work properly:

#Step 2
set.seed(1)
data2 <- data.frame(dat1=seq(0,1, by=.01),
                    dat2=runif(101),
                    dat3=runif(101))

somefunction_general <- function(dat, target1, target2, target3, X){

  #some random function...
  y <- sum(dat[,1]) * X[1]
  y1 <- sum(dat[,2]) * X[2]
  y2 <- sum(dat[,3]) * X[3]

  #minimize these differences...
  diff1 <- target1-y
  diff2 <- target2-y1
  diff3 <- target3-y2

  #almost certain that this is wrong...
  vtr <- sum(abs(diff1), abs(diff2), abs(diff3))

  return(vtr)
}


V2 <- optim(par=c(1,1,1),
           fn = somefunction_general,
           dat=data2, 
           target1=1.8, 
           target2=2, 
           target3=4,
           control = list(fnscale = -1))

sum(data2[,1])
[1] 50.5
sum(data2[,2])
[1] 44.27654
sum(data2[,3])
[1] 51.73668

V2$par[1]*sum(data2[,1])
#[1] 1.469199e+45
V2$par[2]*sum(data2[,2])
#[1] 1.128977e+45
V2$par[3]*sum(data2[,3])
[1] 2.923681e+45
2 Answers

Looks like there's some disagreement between the first function and the second? In the first function, you're returning target1-sum(dat)*X and then trying to find the maximum over X values in [0, 1].

But since you're returning the raw difference and not the absolute value, you're actually just maximizing -sum(dat)*X, or, equivalently, minimizing sum(dat)*X. Since the dat is constant, naturally the optimize function is going to return the smallest value on the interval each time (0 in the example).

For the first function, I think what you want to do is return the absolute value of the difference and then find the minimum and not the maximum. The fix for the second function, somefunction_general, is even simpler, since you're already returning sum(abs(diff1), abs(diff2), abs(diff3)): just make sure the minimum is returned by getting rid of control = list(fnscale = -1)

V2 <- optim(par=c(1,1,1),
            fn = somefunction_general,
            dat=data2, 
            target1=1.8, 
            target2=2, 
            target3=4)

V2$par
[1] 0.03564358 0.03837754 0.07748929

You should write a function such that whether there is one parameter or more, optim should work on it:

somefunction_general <- function(X, dat, target){
  dat <- as.matrix(dat)
  y <- colSums(dat) * X
  sum((target-y)^2) # Often use the MSE
}

let us test this

data2 <- data.frame(dat1=seq(0,1, by=.01),
                dat2=runif(101),
                dat3=runif(101))


data <- seq(0,1, by=.001)



(a <-optim(0,somefunction_general,dat = data,target = 1.887,method = "BFGS"))
$par
[1] 0.00377023

$value
[1] 3.64651e-28

$counts
function gradient 
      25        3 

$convergence
[1] 0

$message
NULL

We can not that the function value is zero. thus the parameter a$par is what we want. check this out

a$par*sum(data)
[1] 1.887

We can also have 3 parameters 1 target eg:

(b<-optim(c(0,0,0),somefunction_general,dat = data2,target = 1.887))
$par
[1] 0.03736837 0.04262253 0.03647203

$value
[1] 4.579334e-08

$counts
function gradient 
     100       NA 

$convergence
[1] 0

$message
NULL
b$par*colSums(data2)
    dat1     dat2     dat3 
1.887103 1.887178 1.886942 

Each almost got to the target of 1.887. note that this is similar to running the first one 3 times.

lastly:

 (d<-optim(c(0,0,0),somefunction_general,dat = data2,target = c(1.8, 2, 4)))
$par
[1] 0.03564672 0.04516916 0.07730660

$value
[1] 2.004725e-07

$counts
function gradient 
      88       NA 

$convergence
[1] 0

$message
NULL

the target was achieved:

d$par*colSums(data2)
    dat1     dat2     dat3 
1.800160 1.999934 3.999587 

This one function can work on n dimensions. please use the method BFGS unless it does not converge.

What if there is one parameter with three targets? well this is quite difficult. Unless there is such a parameter, then it wont converge.

suppose we say the parameter is 0.01, what is the target?

colSums(data2)*0.01
     dat1      dat2      dat3 
0.5050000 0.4427654 0.5173668 

Okay, suppose we were given this target, can we get the 0.01 back?

(e<-optim(10,somefunction_general,dat = data2,target = c(0.505, 0.4427654, 0.5173668),method = "BFGS"))
$par
[1] 0.01

$value
[1] 7.485697e-16

$counts
function gradient 
      12        3 

$convergence
[1] 0

$message
NULL

Huh, we were able to converge. this is because there was a parameter that could take us there. note that i did change the starting point to 10.

Related