使用Rcpp在C ++中的R中应用optim函数

时间:2018-01-19 19:18:01

标签: r optimization rcpp armadillo

我正在尝试在 Rcpp 中调用 R 函数optim()。我在Calling R's optim function from within C++ using Rcpp中看到了一个示例,但我无法为我的用例正确修改它。基本上,目标函数取决于xy,但我想根据b对其进行优化。

以下是执行我想要的 R 代码:

example_r = function(b, x, y) {
  phi = rnorm(length(x))

  tar_val = (x ^ 2 + y ^ 2) * b * phi

  objftn_r = function(beta, x, y) {
    obj_val = (x ^ 2 + y ^ 2) * beta

    return(obj_val)
  }

  b1 = optim(b, function(beta) {
    sum((objftn_r(beta, x, y) - tar_val) ^ 2)
  }, method = "BFGS")$par

  result = (x ^ 2 + y ^ 2) * b1

  return(b1)
}

这是我尝试将其翻译为_RcppArmadillo:

#include <RcppArmadillo.h>
using namespace Rcpp;

// [[Rcpp::depends(RcppArmadillo)]]

arma::vec example_rcpp(arma::vec b, arma::vec x, arma::vec y){

  arma::vec tar_val = pow(x,2)%b-pow(y,2);

  return tar_val;
}

// [[Rcpp::export]]
arma::vec optim_rcpp(const arma::vec& init_val, arma::vec& x, arma::vec& y){

  Rcpp::Environment stats("package:stats"); 
  Rcpp::Function optim = stats["optim"];

  Rcpp::List opt_results = optim(Rcpp::_["par"]    = init_val,
                                 Rcpp::_["fn"]     = Rcpp::InternalFunction(&example_rcpp),
                                 Rcpp::_["method"] = "BFGS");

  arma::vec out = Rcpp::as<arma::vec>(opt_results[0]);

  return out;
} 

但是,此代码正在返回:

> optim_rcpp(1:3,2:4,3:5)
Error in optim_rcpp(1:3, 2:4, 3:5) : not compatible with requested type

我不确定这里的错误是什么。

1 个答案:

答案 0 :(得分:10)

在开始之前,我有几点意见:

  1. 请显示您的所有尝试。
  2. 除非被要求,否则删除或缩短代码。
  3. 保持问题的范围狭窄。
    • C ++ 中使用 R 中的optim与在 C ++ 中使用基础 C ++ 非常不同>来自opt()的{​​{1}}代码。
  4. 避免发送垃圾邮件问题。
    • 如果您发现自己快速连续询问了3个以上的问题,请阅读文档或亲自与熟悉内容的人员交谈。
  5. 我已经清理了你的问题......但是,将来,这种情况很可能不会发生。

    数据生成过程

    数据生成过程似乎分两步完成:首先,在nlopt函数之外,然后在函数内部。

    这应该简化,以便在优化功能之外完成。例如:

    example_r

    目标函数和 R generate_data = function(n, x_mu = 0, y_mu = 1, beta = 1.5) { x = rnorm(n, x_mu) y = rnorm(n, y_mu) phi = rnorm(length(x)) tar_val = (x ^ 2 + y ^ 2) * beta * phi simulated_data = list(x = x, y = y, beta = beta, tar_val = tar_val) return(simulated_data) }

    目标函数必须返回单值,例如在 R 中的标量。在发布的 R 代码下,实际上有两个函数被设计为按顺序作为目标函数,例如。

    optim

    因此,该目标函数应重写为:

    objftn_r = function(beta, x, y) {
      obj_val = (x ^ 2 + y ^ 2) * beta
    
      return(obj_val)
    }
    
    b1 = optim(b, function(beta) {
      sum((objftn_r(beta, x, y) - tar_val) ^ 2)
    }, method = "BFGS")$par
    

    从那里,调用应该对齐:

    objftn_r = function(beta_hat, x, y, tar_val) {
    
      # The predictions generate will be a vector
      est_val = (x ^ 2 + y ^ 2) * beta_hat
    
      # Here we apply sum of squares which changes it
      # from a vector into a single "objective" value
      # that optim can work with.
      obj_val = sum( ( est_val  - tar_val) ^ 2)
    
      return(obj_val)
    }
    

    RcppArmadillo目标函数

    修正了 R 代码的范围和行为后,让我们专注于将其转换为 RcppArmadillo

    特别注意,翻译后定义的异议函数会将向量而不是标量返回到sim_data = generate_data(10, 1, 2, .3) b1 = optim(sim_data$beta, fn = objftn_r, method = "BFGS", x = sim_data$x, y = sim_data$y, tar_val = sim_data$tar_val)$par ,而不是< / em>单个值。同样值得关注的是目标函数中缺少optim参数。考虑到这一点,目标函数将转换为:

    tar_val

    现在,在目标函数设置的情况下,让我们从 C ++ 中将 Rcpp 调用到 R 中,以解析// changed function return type and // the return type of first parameter double obj_fun_rcpp(double& beta_hat, arma::vec& x, arma::vec& y, arma::vec& tar_val){ // Changed from % to * as it is only appropriate if // `beta_hat` is the same length as x and y. // This is because it performs element-wise multiplication // instead of a scalar multiplication on a vector arma::vec est_val = (pow(x, 2) - pow(y, 2)) * beta_hat; // Compute objective value double obj_val = sum( pow( est_val - tar_val, 2) ); // Return a single value return obj_val; } 。在这个函数中,参数了 必须必须显示函数。因此,optim()调用中必须出现xytar_val。因此,我们最终将:

    optim

    一起

    完整功能的代码可以用// [[Rcpp::export]] arma::vec optim_rcpp(double& init_val, arma::vec& x, arma::vec& y, arma::vec& tar_val){ // Extract R's optim function Rcpp::Environment stats("package:stats"); Rcpp::Function optim = stats["optim"]; // Call the optim function from R in C++ Rcpp::List opt_results = optim(Rcpp::_["par"] = init_val, // Make sure this function is not exported! Rcpp::_["fn"] = Rcpp::InternalFunction(&obj_fun_rcpp), Rcpp::_["method"] = "BFGS", // Pass in the other parameters as everything // is scoped environmentally Rcpp::_["x"] = x, Rcpp::_["y"] = y, Rcpp::_["tar_val"] = tar_val); // Extract out the estimated parameter values arma::vec out = Rcpp::as<arma::vec>(opt_results[0]); // Return estimated values return out; } 编写,并通过test_optim.cpp编译为:

    sourceCpp()

    测试用例

    #include <RcppArmadillo.h>
    
    // [[Rcpp::depends(RcppArmadillo)]]
    
    // changed function return type and 
    // the return type of first parameter
    // DO NOT EXPORT THIS FUNCTION VIA RCPP ATTRIBUTES
    double obj_fun_rcpp(double& beta_hat, 
                        arma::vec& x, arma::vec& y, arma::vec& tar_val){
    
      // Changed from % to * as it is only appropriate if  
      // `beta_hat` is the same length as x and y.
      // This is because it performs element-wise multiplication
      // instead of a scalar multiplication on a vector
      arma::vec est_val = (pow(x, 2) - pow(y, 2)) * beta_hat;
    
      // Compute objective value
      double obj_val = sum( pow( est_val - tar_val, 2) );
    
      // Return a single value
      return obj_val;
    }
    
    
    // [[Rcpp::export]]
    arma::vec optim_rcpp(double& init_val,
                         arma::vec& x, arma::vec& y, arma::vec& tar_val){
    
      // Extract R's optim function
      Rcpp::Environment stats("package:stats"); 
      Rcpp::Function optim = stats["optim"];
    
      // Call the optim function from R in C++ 
      Rcpp::List opt_results = optim(Rcpp::_["par"]    = init_val,
                                     // Make sure this function is not exported!
                                     Rcpp::_["fn"]     = Rcpp::InternalFunction(&obj_fun_rcpp),
                                     Rcpp::_["method"] = "BFGS",
                                     // Pass in the other parameters as everything
                                     // is scoped environmentally
                                     Rcpp::_["x"] = x,
                                     Rcpp::_["y"] = y,
                                     Rcpp::_["tar_val"] = tar_val);
    
      // Extract out the estimated parameter values
      arma::vec out = Rcpp::as<arma::vec>(opt_results[0]);
    
      // Return estimated values
      return out;
    }
    

    注意:如果您想避免返回大小为1 x1的矩阵,请使用# Setup some values beta = 2 x = 2:4 y = 3:5 # Set a seed for reproducibility set.seed(111) phi = rnorm(length(x)) tar_val = (x ^ 2 + y ^ 2) * beta * phi optim_rcpp(beta, x, y, tar_val) # [,1] # [1,] 2.033273 作为double的返回参数并切换{{1} } optim_rcpp