首页 > 解决方案 > 使用 nloptr 包进行更灵活的目标定义

问题描述

我正在使用该nloptr软件包,一切正常。但我需要一种方法来以更快的方式定义目标函数和约束。我不能每次都手写所有的设置。

例如,我想解决这个问题:

library(nloptr)

eval_f <- function(x){
  return(x[4]^2+x[7]^2+x[9]^2)
}
x0 = c(1,1,1,1,0.5,0,0.5,1,0)

hin <- function(x){
  h <- numeric(6)
  h[1] = x[1]+x[4]-x[2]-x[5]-0.01
  h[2] = x[1]+x[4]-x[3]-x[6]-0.01
  h[3] = x[2]+x[5]-x[3]-x[6]-0.01
  h[4] = x[2]+x[8]-x[1]-x[7]-0.01
  h[5] = x[2]+x[8]-x[3]-x[9]-0.01
  h[6] = x[1]+x[7]-x[3]-x[9]-0.01
  return(h)
}

heq <- function(x){
  h <- numeric(1)
  h[1] <- x[1]+x[2]+x[3]-3
  return(h)
}


res <- slsqp(x0=x0,fn=eval_f,hin = hin,heq = heq)

一切正常。但我想以更快的方式定义目标函数。我可以以自动方式将另一个参数(索引)传递给函数吗?例如:

eval_f <- function(x,indices){
      return(x[indices]^2)
    }

我试过了,但我有一个错误。

标签: r

解决方案


...参数slsqp允许您将任意参数传递给目标函数。所以定义一个新的目标函数indices作为参数:

eval_f2 <- function(x,indices){
  return(sum(x[indices]^2))
}

...并包括indices=c(4,7,9)(以匹配您之前的目标函数的定义):

res2 <- slsqp(x0=x0,fn=eval_f2, hin = hin,heq = heq, indices=c(4,7,9))

检查解决方案:

all.equal(res$par,res2$par) ## TRUE

工厂

更一般地说,您可以定义一个工厂- 一个返回函数的函数。这是有效的,因为函数具有可以存储变量(例如索引)的相关环境。即使在顶级函数不允许传递任意参数的情况下,这也将起作用(例如,如果您想为目标和约束函数使用不同的索引集,这可能很重要......)

eval_factory <- function(indices) {
    fun <- function(x) {
        return(sum(x[indices]^2))
    }
    return(fun)
}

res3 <- slsqp(x0=x0, fn=eval_factory(indices=c(4,7,9)),
              hin = hin,heq = heq)
all.equal(res$par,res3$par) ## TRUE

欣工厂

hin_factory <- function(A,b) {
    fun <- function(x) {
        return((A %*% x) + b)
    }
    return(fun)
}

A0 <- matrix(c(1, -1,  0, 1,-1,  0, 0, 0, 0,
               1,  0, -1, 1, 0, -1, 0, 0, 0,
               0,  1, -1, 0, 1, -1, 0, 0, 0,
              -1,  1,  0, 0, 0,  0,-1, 1, 0,
               0,  1, -1, 0, 0,  0, 0, 1, -1,
               1,  0, -1, 0, 0,  0, 1, 0, -1),
             byrow=TRUE,ncol=9)

all.equal(c(hin_factory(A0,-0.01)(x0)),hin(x0))

res4 <- slsqp(x0=x0, fn=eval_factory(indices=c(4,7,9)),
              hin = hin_factory(A0,b=-0.01), heq = heq)

all.equal(res$par, res4$par)

推荐阅读