R shiny 非线性编程 - nloptr 中的错误:REAL() 只能应用于 'numeric',而不是 'list'

R shiny nonlinear programming - Error in nloptr: REAL() can only be applied to a 'numeric', not a 'list'

我尝试使用库 nloptr 制作简单的非线性规划应用程序,仅根据用户输入计算非线性优化。

如果我尝试添加 objective 函数的梯度和来自输入的约束,我会得到一个错误:nloptr 中的错误:REAL() 只能应用于 'numeric',而不是 'list'。感谢您的帮助。

library(shiny)
library(shinythemes)
library(nloptr)

ui <- fluidPage(theme = shinytheme("united"),
                navbarPage(" Optimization",
                           tabPanel("Nonlinear programming",
                                    sidebarLayout(
                                      sidebarPanel(
                                        h3('Please enter nonlinear problem for solving'),
                                        textInput('obj', 'Objective  function ', "x[1]*x[4]*(x[1] +x[2] + x[3]) + x[3]"),
                                        textInput('gobj', 'Gradient of objective  function ', " x[1] * x[4] + x[4] * (x[1] + x[2] + x[3]), x[1] * x[4], x[1] * x[4] + 1.0, x[1] * (x[1] + x[2] + x[3])"),
                                        textInput('eq', 'Equality constraints ', "x[1]^2 + x[2]^2 + x[3]^2 + x[4]^2 - 40"),
                                        textInput('geq', 'Gradient of equality constraints ', "2.0*x[1], 2.0*x[2], 2.0*x[3], 2.0*x[4]"),
                                        textInput('ineq', 'Inequality constraints', "25 - x[1]*x[2]*x[3]*x[4]"),
                                        textInput('gineq', 'Gradient of inequality constraints', "-x[2]*x[3]*x[4], -x[1]*x[3]*x[4], -x[1]*x[2]*x[4], -x[1]*x[2]*x[3]"),
                                        textInput('lb', 'Lower bounds (comma separated)', "1,1,1,1"),
                                        textInput('ub', 'Upper bounds (comma separated)', "5,5,5,5"),
                                        textInput('x0', 'Initial values (comma separated)', "1,5,5,1"),
                                        actionButton('submit',"Submit")
                                      ),
                                      
                                      mainPanel(
                                        h4('The result is:'),
                                        verbatimTextOutput("res")
                                      )
                                    ))))

server <- function(input, output, session) {
  
  eval_f <- function( x ) {
    req(input$obj)
    return( list( "objective" = rlang::eval_tidy(rlang::parse_expr(as.character(input$obj))), 
                  "gradient" =  rlang::eval_tidy(rlang::parse_exprs(as.character(unlist(strsplit(input$gobj, ",")))))
    ) )
  }
  
  # constraint functions
  # inequalities
  eval_g_ineq <- function( x ) {
    constr <- rlang::eval_tidy(rlang::parse_expr(as.character(input$ineq))) # c( 25 - x[1] * x[2] * x[3] * x[4] )
    grad <- rlang::eval_tidy(rlang::parse_exprs(as.character(unlist(strsplit(input$gineq, ",")))))
    return( list( "constraints"=constr, "jacobian"=grad ) )
  }
  
  # equalities
  eval_g_eq <- function( x ) {
    constr <- rlang::eval_tidy(rlang::parse_expr(as.character(input$eq)))  # c( x[1]^2 + x[2]^2 + x[3]^2 + x[4]^2 - 40 )
    grad <- rlang::eval_tidy(rlang::parse_exprs(as.character(unlist(strsplit(input$geq, ",")))))
    return( list( "constraints"=constr, "jacobian"=grad ) )
  }
  
  res <- eventReactive(input$submit, {
    req(input$obj,input$ineq,input$eq,input$lb,input$ub,input$x0,input$gobj,input$gineq,input$geq)
    lb <<- as.numeric(unlist(strsplit(input$lb,",")))
    ub <<- as.numeric(unlist(strsplit(input$ub,",")))
    x0 <<- as.numeric(unlist(strsplit(input$x0,",")))
    
    local_opts <- list( "algorithm" = "NLOPT_LD_MMA", "xtol_rel" = 1.0e-15 )
    opts <- list( "algorithm"= "NLOPT_LD_AUGLAG",
                  "xtol_rel"= 1.0e-15,
                  "maxeval"= 16000,
                  "local_opts" = local_opts,
                  "print_level" = 0 )
    
    res <- nloptr ( x0 = x0,
                    eval_f = eval_f,
                    lb = lb,
                    ub = ub,
                    eval_g_ineq = eval_g_ineq,
                    eval_g_eq = eval_g_eq,
                    opts = opts)
    res  
  })
  output$res<-renderPrint({
    
    cat("Result:\n")
    print(res())
  })
}

shinyApp(ui = ui, server = server)

您需要为 gradient 执行与为 objective 相同的操作。但是,由于输入是元素向量,您可以使用 lapply。现在,lapply 给出了一个列表,因此我们将其转换回向量。

试试这个

server <- function(input, output, session) {

  eval_f <- function( x ) {
    req(input$obj)
    return( list( "objective" = rlang::eval_tidy(rlang::parse_expr(as.character(input$obj))),
                  "gradient" =  as.numeric(as.character(lapply(unlist(strsplit(input$gobj, ",")), function(par) { 
                    val <<- rlang::eval_tidy(rlang::parse_expr(as.character(par)))
                    return(val)})))
    ) )
  }

  # constraint functions
  # inequalities
  eval_g_ineq <- function( x ) {
    constr <- rlang::eval_tidy(rlang::parse_expr(as.character(input$ineq)))
    grad <- as.numeric(as.character(lapply(unlist(strsplit(input$gineq, ",")), function(par) { 
      val <<- rlang::eval_tidy(rlang::parse_expr(as.character(par)))
      return(val)})))
    return( list( "constraints"=constr, "jacobian"=grad ) )
  }

  # equalities
  eval_g_eq <- function( x ) {
    constr <- rlang::eval_tidy(rlang::parse_expr(as.character(input$eq)))
    grad <- as.numeric(as.character(lapply(unlist(strsplit(input$geq, ",")), function(par) { 
      val <<- rlang::eval_tidy(rlang::parse_expr(as.character(par)))
      return(val)})))
    return( list( "constraints"=constr, "jacobian"=grad ) )
  }

  res <- eventReactive(input$submit, {
    req(input$obj,input$ineq,input$eq,input$lb,input$ub,input$x0,input$gobj,input$gineq,input$geq)
    lb <<- as.numeric(unlist(strsplit(input$lb,",")))
    ub <<- as.numeric(unlist(strsplit(input$ub,",")))
    x0 <<- as.numeric(unlist(strsplit(input$x0,",")))

    local_opts <- list( "algorithm" = "NLOPT_LD_MMA", "xtol_rel" = 1.0e-15 )
    opts <- list( "algorithm"= "NLOPT_LD_AUGLAG",
                  "xtol_rel"= 1.0e-15,
                  "maxeval"= 16000,
                  "local_opts" = local_opts,
                  "print_level" = 0 )

    res <- nloptr ( x0 = x0,
                    eval_f = eval_f,
                    lb = lb,
                    ub = ub,
                    eval_g_ineq = eval_g_ineq,
                    eval_g_eq = eval_g_eq,
                    opts = opts)
    res
  })
  
  output$res<-renderPrint({
    cat("Result:\n")
    print(res())
  })
}