I have a loop in R that that's quite slow (but works). Currently, this calculation takes about ~3 minutes on my laptop, and I think it can be improved. Eventually, I'll loop through many data files running calculations based on the results of this code, and I'd like to make the current code faster if possible.
Basically, for each date, for 11 different values of X, the loop grabs the last X years' worth of rainfall values (Y), finds a linear inverse weighting (Z) so that the oldest rainfall values are weighted least, multiples the rain (Y) and weights (Z) to get a vector A, then takes the sum of A as the final result. This is done for thousands of dates.
However, I couldn't think of or find advice for any way to make this faster in R, so I attempted to rewrite it in Rcpp, in which I have limited knowledge of. My Rcpp code does not duplicate the R code exactly, as the resulting matrix is different (wrong) from what it should be (out1 vs out2; I know out1 is correct). It seems like the Rcpp code is faster, but I can only test it using a few columns because it begins crashing (fatal error in RStudio) if I attempt to run all 11 columns (i <= 10).
I'm looking for feedback on how I can improve the R code and/or correct the Rcpp code to provide the correct result and not crash in the process.
(Although the code I've posted below doesn't show it, the data is loaded into R the way it is [as a dataframe] for a few calculations done outside of the code shown. For the specific calculation shown here, only column 2 of the dataframe is used.)
The data file is here: https://drive.google.com/file/d/0Bw_Ca37oxVmJekFBR2t4eDdKeGM/view?usp=sharing
Attempt in R
library(readxl)
library(readxl)
library(Rcpp)
file = data.frame(read_excel("lake.xlsx", trim_ws=T)
col_types=c("date","numeric","numeric","date",rep("numeric",4),"text")))
file[,1] = as.Date(file[,1], "%Y/%m/%d", tz="UTC")
file[,4] = as.Date(file[,4], "%Y/%m/%d", tz="UTC")
rainSUM = function(df){
rainsum = data.frame("6m"=as.numeric(), "1yr"=as.numeric(), "2yr"=as.numeric(), "3yr"=as.numeric(), "4yr"=as.numeric(), "5yr"=as.numeric(), "6yr"=as.numeric(), "7yr"=as.numeric(), "8yr"=as.numeric(), "9yr"=as.numeric(), "10yr"=as.numeric()) # create dataframe for storing the sum of weighted last d values
Tdays <- length(df[,1])
for(i in 1:11) { # loop through the lags
if (i==1) {
d <- 183 # 6 month lag only has 183 days,
} else {
d <- (i-1)*366 # the rest have 366 days times the number of years
}
w <- 0:(d-1)/d
for(k in 1:Tdays) { # loop through rows of rain dataframe (k = row)
if(d>k){ # get number of rain values needed for the lag
rainsum[k,i] <- sum(df[1:k,2] * w[(d-k+1):d])
} else{
rainsum[k,i] <- sum(df[(k-d+1):k,2] * w)
}
}
}
return(rainsum)
}
out1 <- rainSUM(file)
Attempt in Rcpp
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericVector myseq(int first, int last) { // simulate R's X:Y sequence (step of 1)
NumericVector y(0);
for (int i=first; i<=last; ++i)
y.push_back(i);
return(y);
}
// [[Rcpp::export]]
NumericVector splicer(NumericVector vec, int first, int last) { // splicer
NumericVector y(0);
for (int i=first; i<=last; ++i)
y.push_back(vec[i]);
return(y);
}
// [[Rcpp::export]]
NumericVector weighty(int d) { // calculate inverse linear weight according to the number of days in lag
NumericVector a = myseq(1,d); // sequence 1:d; length d
NumericVector b = (a-1)/a; // inverse linear
return(b); // return vector
}
// [[Rcpp::export]]
NumericMatrix rainsumCPP(DataFrame df, int raincol) {
NumericVector q(0);
NumericMatrix rainsum(df.nrows(), 11); // matrix with number of row days as data file and 11 columns
NumericVector p = df( raincol-1 ); // grab rain values (remember C++ first index is 0)
for(int i = 0; i <= 10; i++) { // loop through 11 columns (C++ index starts at 0!)
if (i==0) {
int d = 183; // 366*years lag days
NumericVector w = weighty(d); // get weights for this lag series
for(int k = 0; k < df.nrows(); k++) { // loop through days (rows)
if(d>k){ // if not enough lag days for row, use what's available
NumericVector m = splicer(p, 0, k); // subset rain values according to the day being considered
NumericVector u = splicer(w, (d-k), (d-1)); // same for weight
m = m*u; // multiply rain values by weights
rainsum(k,i) = sum(m); // add the sum of the weighted rain to the rainsum matrix
} else{
NumericVector m = splicer(p, k-d+1, k);
m = m*w;
rainsum(k,i) = sum(m);
}
}
}
else {
int d = i*366; // 183 lag days if column 0
NumericVector w = weighty(d); // get weights for this lag series
for(int k = 0; k < df.nrows(); k++) { // loop through days (rows)
if(d>k){ // if not enough lag days for row, use what's available
NumericVector m = splicer(p, 0, k); // subset rain values according to the day being considered
NumericVector u = splicer(w, (d-k), (d-1)); // same for weight
m = m*u; // multiply rain values by weights
rainsum(k,i) = sum(m); // add the sum of the weighted rain to the rainsum matrix
} else{
NumericVector m = splicer(p, k-d+1, k);
m = m*w;
rainsum(k,i) = sum(m);
}
}
}
}
return(rainsum);
}
/*** R
out2 = rainsumCPP(file, raincol) # raincol currently = 2
*/
Congratulations! You have an index out of bounds (OOB) error causing an undefined behavior (UB)! You can detect this in the future by changing the vector accessor from [] to () and for the matrix accessor from () to .at().
Switching to these accessors yields:
Error in rainsumCPP(file, 2) :
Index out of bounds: [index=182; extent=182].
which indicates an index is out of bounds as the index must always be between 0 and 1 less than the extent (e.g. length of vector - 1).
Preliminary glances indicates that this issue is largely caused by not correctly mapping one-based indexing to zero-based indexing.
Upon playing around with the myseq(), splicer(), and weighty() functions, they do not match their R equivalent given inputs. This can be checked by using all.equal(R_result, Rcpp_Result). This mismatch is in two parts: 1. the bounds of both myseq and splicer and 2. inversion of done inside weighty.
So, by using the following functions that were modified, you should be on a good basis for obtaining the correct results.
// [[Rcpp::export]]
NumericVector myseq(int first, int last) { // simulate R's X:Y sequence (step of 1)
int vec_len = abs(last - first);
NumericVector y = no_init(vec_len);
int count = 0;
for (int i = first; i < last; ++i) {
y(count) = count;
count++;
}
return y;
}
// [[Rcpp::export]]
NumericVector splicer(NumericVector vec, int first, int last) { // splicer
int vec_len = abs(last - first);
NumericVector y = no_init(vec_len);
int count = 0;
for (int i = first; i < last; ++i) {
y(count) = vec(i);
count++;
}
return y;
}
// [[Rcpp::export]]
NumericVector weighty(int d) { // calculate inverse linear weight according to the number of days in lag
NumericVector a = myseq(0, d - 1); // (fixed) sequence 1:d; length d
NumericVector b = a / d; // (fixed) inverse linear
return(b); // return vector
}
From there, you will likely need to modify the rainsumCpp as no output was given for what the R equivalent was.
Related
In a very first attempt at creating a C++ function which can be called from R using Rcpp, I have a simple function to compute a minimum spanning tree from a distance matrix using Prim's algorithm. This function has been converted into C++ from a former version in ANSI C (which works fine).
Here it is:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
DataFrame primlm(const int n, NumericMatrix d)
{
double const din = 9999999.e0;
long int i1, nc, nc1;
double dlarge, dtot;
NumericVector is, l, lp, dist;
l(1) = 1;
is(1) = 1;
for (int i=2; i <= n; i++) {
is(i) = 0;
}
for (int i=2; i <= n; i++) {
dlarge = din;
i1 = i - 1;
for (int j=1; j <= i1; j++) {
for (int k=1; k <= n; k++) {
if (l(j) == k)
continue;
if (d[l(j), k] > dlarge)
continue;
if (is(k) == 1)
continue;
nc = k;
nc1 = l(j);
dlarge = d(nc1, nc);
}
}
is(nc) = 1;
l(i) = nc;
lp(i) = nc1;
dist(i) = dlarge;
}
dtot = 0.e0;
for (int i=2; i <= n; i++){
dtot += dist(i);
}
return DataFrame::create(Named("l") = l,
Named("lp") = lp,
Named("dist") = dist,
Named("dtot") = dtot);
}
When I compile this function using Rcpp under RStudio, I get two warnings, complaining that variables 'nc' and 'nc1' have not been initialized. Frankly, I could not understand that, as it seems to me that both variables are being initialized inside the third loop. Also, why there is no similar complaint about variable 'i1'?
Perhaps it comes as no surprise that, when attempting to call this function from R, using the below code, what I get is a crash of the R system!
# Read test data
df <- read.csv("zygo.csv", header=TRUE)
lonlat <- data.frame(df$Longitude, df$Latitude)
colnames(lonlat) <- c("lon", "lat")
# Compute distance matrix using geosphere library
library(geosphere)
d <- distm(lonlat, lonlat, fun=distVincentyEllipsoid)
# Calls Prim minimum spanning tree routine via Rcpp
library(Rcpp)
sourceCpp("Prim.cpp")
n <- nrow(df)
p <- primlm(n, d)
Here is the dataset I use for testing purposes:
"Scientific name",Locality,Longitude,Latitude Zygodontmys,Bush Bush
Forest,-61.05,10.4 Zygodontmys,Cerro Azul,-79.4333333333,9.15
Zygodontmys,Dividive,-70.6666666667,9.53333333333 Zygodontmys,Hato El
Frio,-63.1166666667,7.91666666667 Zygodontmys,Finca Vuelta
Larga,-63.1166666667,10.55 Zygodontmys,Isla
Cebaco,-81.1833333333,7.51666666667 Zygodontmys,Kayserberg
Airstrip,-56.4833333333,3.1 Zygodontmys,Limao,-60.5,3.93333333333
Zygodontmys,Montijo Bay,-81.0166666667,7.66666666667
Zygodontmys,Parcela 200,-67.4333333333,8.93333333333 Zygodontmys,Rio
Chico,-65.9666666667,10.3166666667 Zygodontmys,San Miguel
Island,-78.9333333333,8.38333333333
Zygodontmys,Tukuko,-72.8666666667,9.83333333333
Zygodontmys,Urama,-68.4,10.6166666667
Zygodontmys,Valledup,-72.9833333333,10.6166666667
Could anyone give me a hint?
The initializations of ncand nc1 are never reached if one of the three if statements is true. It might be that this is not possible with your data, but the compiler has no way knowing that.
However, this is not the reason for the crash. When I run your code I get:
Index out of bounds: [index=1; extent=0].
This comes from here:
NumericVector is, l, lp, dist;
l(1) = 1;
is(1) = 1;
When declaring a NumericVector you have to tell the required size if you want to assign values by index. In your case
NumericVector is(n), l(n), lp(n), dist(n);
might work. You have to analyze the C code carefully w.r.t. memory allocation and array boundaries.
Alternatively you could use the C code as is and use Rcpp to build a wrapper function, e.g.
#include <array>
#include <Rcpp.h>
using namespace Rcpp;
// One possibility for the function signature ...
double prim(const int n, double *d, double *l, double *lp, double *dist) {
....
}
// [[Rcpp::export]]
List primlm(NumericMatrix d) {
int n = d.nrow();
std::array<double, n> lp; // adjust size as needed!
std::array<double, n> dist; // adjust size as needed!
double dtot = prim(n, d.begin(), l.begin(), lp.begin(), dist.begin());
return List::create(Named("l") = l,
Named("lp") = lp,
Named("dist") = dist,
Named("dtot") = dtot);
}
Notes:
I am returning a List instead of a DataFrame since dtot is a scalar value.
The above code is meant to illustrate the idea. Most likely it will not work without adjustments!
The problem I am having is that I have to calculate a Euclidean distance matrix between shapes that can range from 20,000 up to 60,000 points, which produces 10-20GB amounts of data. I have to run each of these calculates thousands of times so 20GB x 7,000 (each calculation is a different point cloud). The shapes can be either 2D or 3D.
EDITED (Updated questions)
Is there a more efficient way to calculate the forward and backward distances without using two separate nested loops?
I know I could save the data matrix and calculate the minimum
distances in each direction, but then there is a huge memory issue
with large point clouds.
Is there a way to speed up this calculation and/or clean up the code to trim off time?
The irony is that I only need the matrix to calculate a very simple metric, but it requires the entire matrix to find that metric (Average Hausdorff distance).
Data example where each column represents a dimension of the shape and each row is a point in the shape:
first_configuration <- matrix(1:6,2,3)
second_configuration <- matrix(6:11,2,3)
colnames(first_configuration) <- c("x","y","z")
colnames(second_configuration) <- c("x","y","z")
This code calculates a Euclidean distance between between coordinates:
m <- nrow(first_configuration)
n <- nrow(second_configuration)
D <- sqrt(pmax(matrix(rep(apply(first_configuration * first_configuration, 1, sum), n), m, n, byrow = F) + matrix(rep(apply(second_configuration * second_configuration, 1, sum), m), m, n, byrow = T) - 2 * first_configuration %*% t(second_configuration), 0))
D
Output:
[,1] [,2]
[1,] 8.660254 10.392305
[2,] 6.928203 8.660254
EDIT: included hausdorff average code
d1 <- mean(apply(D, 1, min))
d2 <- mean(apply(D, 2, min))
average_hausdorff <- mean(d1, d2)
EDIT (Rcpp solution):
Here is my attempt to implement it in Rcpp so the matrix is never saved to memory. Working now but very slow.
sourceCpp(code=
#include <Rcpp.h>
#include <limits>
using namespace Rcpp;
// [[Rcpp::export]]
double edist_rcpp(NumericVector x, NumericVector y){
double d = sqrt( sum( pow(x - y, 2) ) );
return d;
}
// [[Rcpp::export]]
double avg_hausdorff_rcpp(NumericMatrix x, NumericMatrix y){
int nrowx = x.nrow();
int nrowy = y.nrow();
double new_low_x = std::numeric_limits<int>::max();
double new_low_y = std::numeric_limits<int>::max();
double mean_forward = 0;
double mean_backward = 0;
double mean_hd;
double td;
//forward
for(int i = 0; i < nrowx; i++) {
for(int j = 0; j < nrowy; j++) {
NumericVector v1 = x.row(i);
NumericVector v2 = y.row(j);
td = edist_rcpp(v1, v2);
if(td < new_low_x) {
new_low_x = td;
}
}
mean_forward = mean_forward + new_low_x;
new_low_x = std::numeric_limits<int>::max();
}
//backward
for(int i = 0; i < nrowy; i++) {
for(int j = 0; j < nrowx; j++) {
NumericVector v1 = y.row(i);
NumericVector v2 = x.row(j);
td = edist_rcpp(v1, v2);
if(td < new_low_y) {
new_low_y = td;
}
}
mean_backward = mean_backward + new_low_y;
new_low_y = std::numeric_limits<int>::max();
}
//hausdorff mean
mean_hd = (mean_forward / nrowx + mean_backward / nrowy) / 2;
return mean_hd;
}
)
EDIT (RcppParallel solution):
Definitely faster than the serial Rcpp solution and most certainly the R solution. If anyone has tips on how to improve my RcppParallel code to trim off some extra time it would be much appreciated!
sourceCpp(code=
#include <Rcpp.h>
#include <RcppParallel.h>
#include <limits>
// [[Rcpp::depends(RcppParallel)]]
struct minimum_euclidean_distances : public RcppParallel::Worker {
//Input
const RcppParallel::RMatrix<double> a;
const RcppParallel::RMatrix<double> b;
//Output
RcppParallel::RVector<double> medm;
minimum_euclidean_distances(const Rcpp::NumericMatrix a, const Rcpp::NumericMatrix b, Rcpp::NumericVector medm) : a(a), b(b), medm(medm) {}
void operator() (std::size_t begin, std::size_t end) {
for(std::size_t i = begin; i < end; i++) {
double new_low = std::numeric_limits<double>::max();
for(std::size_t j = 0; j < b.nrow(); j++) {
double dsum = 0;
for(std::size_t z = 0; z < b.ncol(); z++) {
dsum = dsum + pow(a(i,z) - b(j,z), 2);
}
dsum = pow(dsum, 0.5);
if(dsum < new_low) {
new_low = dsum;
}
}
medm[i] = new_low;
}
}
};
// [[Rcpp::export]]
double mean_directional_hausdorff_rcpp(Rcpp::NumericMatrix a, Rcpp::NumericMatrix b){
Rcpp::NumericVector medm(a.nrow());
minimum_euclidean_distances minimum_euclidean_distances(a, b, medm);
RcppParallel::parallelFor(0, a.nrow(), minimum_euclidean_distances);
double results = Rcpp::sum(medm);
results = results / a.nrow();
return results;
}
// [[Rcpp::export]]
double max_directional_hausdorff_rcpp(Rcpp::NumericMatrix a, Rcpp::NumericMatrix b){
Rcpp::NumericVector medm(a.nrow());
minimum_euclidean_distances minimum_euclidean_distances(a, b, medm);
RcppParallel::parallelFor(0, a.nrow(), minimum_euclidean_distances);
double results = Rcpp::max(medm);
return results;
}
)
Benchmarks using large point clouds of sizes 37,775 and 36,659:
//Rcpp serial solution
system.time(avg_hausdorff_rcpp(ll,rr))
user system elapsed
409.143 0.000 409.105
//RcppParallel solution
system.time(mean(mean_directional_hausdorff_rcpp(ll,rr), mean_directional_hausdorff_rcpp(rr,ll)))
user system elapsed
260.712 0.000 33.265
I try to use JuliaCall to do the calculation for the average Hausdorff distance.
JuliaCall embeds Julia in R.
I only try a serial solution in JuliaCall. It seems to be faster than the RcppParallel and the Rcpp serial solution in the question, but I don't have the benchmark data. Since ability for parallel computation is built in Julia. A parallel computation version in Julia should be written without much difficulty. I will update my answer after finding that out.
Below is the julia file I wrote:
# Calculate the min distance from the k-th point in as to the points in bs
function min_dist(k, as, bs)
n = size(bs, 1)
p = size(bs, 2)
dist = Inf
for i in 1:n
r = 0.0
for j in 1:p
r += (as[k, j] - bs[i, j]) ^ 2
## if r is already greater than the upper bound,
## then there is no need to continue doing the calculation
if r > dist
continue
end
end
if r < dist
dist = r
end
end
sqrt(dist)
end
function avg_min_dist_from(as, bs)
distsum = 0.0
n1 = size(as, 1)
for k in 1:n1
distsum += min_dist_from(k, as, bs)
end
distsum / n1
end
function hausdorff_avg_dist(as, bs)
(avg_min_dist_from(as, bs) + avg_min_dist_from(bs, as)) / 2
end
And this is the R code to use the julia function:
first_configuration <- matrix(1:6,2,3)
second_configuration <- matrix(6:11,2,3)
colnames(first_configuration) <- c("x","y","z")
colnames(second_configuration) <- c("x","y","z")
m <- nrow(first_configuration)
n <- nrow(second_configuration)
D <- sqrt(matrix(rep(apply(first_configuration * first_configuration, 1, sum), n), m, n, byrow = F) + matrix(rep(apply(second_configuration * second_configuration, 1, sum), m), m, n, byrow = T) - 2 * first_configuration %*% t(second_configuration))
D
d1 <- mean(apply(D, 1, min))
d2 <- mean(apply(D, 2, min))
average_hausdorff <- mean(d1, d2)
library(JuliaCall)
## the first time of julia_setup could be quite time consuming
julia_setup()
## source the julia file which has our hausdorff_avg_dist function
julia_source("hausdorff.jl")
## check if the julia function is correct with the example
average_hausdorff_julia <- julia_call("hausdauff_avg_dist",
first_configuration,
second_configuration)
## generate some large random point clouds
n1 <- 37775
n2 <- 36659
as <- matrix(rnorm(n1 * 3), n1, 3)
bs <- matrix(rnorm(n2 * 3), n2, 3)
system.time(julia_call("hausdauff_avg_dist", as, bs))
The time on my laptop was less than 20 seconds, note this is performance of the serial version of JuliaCall! I used the same data to test RCpp serial solution in the question, which took more than 10 minutes to run. I don't have RCpp parallel on my laptop now so I can't try that. And as I said, Julia has built-in ability to do parallel computation.
Deep inside an MCMC algorithm I need to multiply a user-provided list of matrices with a vector, i.e., the following piece of Rcpp and RcppArmadillo code is called multiple times per MCMC iteration:
List mat_vec1 (const List& Mats, const vec& y) {
int n_list = Mats.size();
Rcpp::List out(n_list);
for (int i = 0; i < n_list; ++i) {
out[i] = as<mat>(Mats[i]) * y;
}
return(out);
}
The user-provided list Mats remains fixed during the MCMC, vector y changes in each iteration. Efficiency is paramount and I'm trying to see if I can speed up the code by not having to convert the elements of Mats to arma::mat that many times (it needs to be done only once). I tried the following approach
List arma_Mats (const List& Mats) {
int n_list = Mats.size();
Rcpp::List res(n_list);
for (int i = 0; i < n_list; ++i) {
res[i] = as<mat>(Mats[i]);
}
return(res);
}
and then
List mat_vec2 (const List& Mats, const vec& y) {
int n_list = Mats.size();
Rcpp::List aMats = arma_Mats(Mats);
Rcpp::List out(n_list);
for (int i = 0; i < n_list; ++i) {
out[i] = aMats[i] * y;
}
return(out);
}
but this does not seem to work. Any pointers of alternative/better solutions are much welcome.
Ok, I basically wrote the answer in the comment but it then occurred to me that we already provide a working example in the stub created by RcppArmadillo.package.skeleton():
// [[Rcpp::export]]
Rcpp::List rcpparma_bothproducts(const arma::colvec & x) {
arma::mat op = x * x.t();
double ip = arma::as_scalar(x.t() * x);
return Rcpp::List::create(Rcpp::Named("outer")=op,
Rcpp::Named("inner")=ip);
}
This returns a list the outer product (a matrix) and the inner product (a scalar) of the given vector.
As for what is fast and what is not: I recommend to not conjecture but rather profile and measure as much as you can. My inclination would be to do more (standalone) C++ code in Armadillo and only return at the very end minimizing conversions.
Let Y and K be an n-dimensional (column) vector and n by n matrix, respectively. Think of Y and K as a sample vector and its covariance matrix.
Corresponding to each entry of Y (say Yi) there is a row vector (of size 2) Si encoding the location of the sample in a two dimensional space. Construct the n by 2 matrix S by concatenating all the Si vectors. The ij-th entry of K is of the form
Kij= f( |si-sj|, b )
in which |.| denotes the usual Euclidean norm, f is the covariance function and b represents the covariance parameters. For instance for powered exponential covariance we have f(x) = exp( (-|x|/r)q ) and b = (r,q).
The goal is to compute the following quantity in Rcpp, using a parallel fashion. (YT stands for Y transpose and ||.||2 denotes the sum of square entries of K).
YTKY ⁄ ||K||2
Here is the piece of code I've written to do the job. While running, Rstudio runs out of memory after a few seconds and the following massage displays: "R encountered a fatal error. The session was terminated". I've very recently started using open MP in Rcpp and I have no idea why this happens! Can anybody tell me what have I done wrong here?
#include <Rcpp.h>
#include<math.h>
#include<omp.h>
// [[Rcpp::plugins(openmp)]]
using namespace Rcpp;
// [[Rcpp::export]]
double InnerProd(NumericVector x, NumericVector y) {
int n = x.size();
double total = 0;
for(int i = 0; i < n; ++i) {
total += x[i]*y[i];
}
return total;
}
// [[Rcpp::export]]
double CorFunc(double r, double range_param, double beta) {
double q,x;
x = r/range_param;
q = exp( -pow(x,beta) );
return(q);
}
// [[Rcpp::export]]
double VarianceComp( double range, NumericVector Y, NumericMatrix s, double
beta, int t ){
int n,i,j;
double Numer = 0, Denom = 0, dist, CorVal, ObjVal;
NumericVector DistVec;
n = Y.size();
omp_set_num_threads(t);
# pragma omp parallel for private(DistVec,CorVal,dist,j) \
reduction(+:Numer,Denom)
for( i = 0; i < n; ++i) {
for( j = 0; j < n; ++j){
DistVec = ( s(i,_)-s(j,_) );
dist = sqrt( InnerProd(DistVec,DistVec) );
CorVal = CorFunc(dist,range,beta);
Numer += Y[i]*Y[j]*CorVal/n;
Denom += pow( CorVal, 2 )/n;
}
}
ObjVal = Numer/Denom;
return( ObjVal );
}
I have recently begun using the Rcpp package to write some segments of my R code into C++.
Given a matrix of data, I have the following Rcpp function which calculates a kernel reweighted estimate of the covariance for some observation.
cppFunction('
NumericVector get_cov_1obs(NumericMatrix cdata, int ID, float radius){
int nrow = cdata.nrow(), ncol = cdata.ncol();
float norm_ = 0;
float w;
NumericMatrix out(ncol, ncol);
NumericMatrix outer_prod(ncol, ncol);
for (int i=0; i<ncol;i++){
for (int j=0;j<ncol;j++){
out(i,j) = 0;
outer_prod(i,j) = 0;
}
}
for (int i=0; i<nrow;i++){
w = exp( -(i-ID)*(i-ID)/(2*radius));
norm_ += w;
for (int j=0; j<ncol;j++){
for (int k=0;k<ncol;k++){
outer_prod(j,k) = cdata(i,j) * cdata(i,k);
}
}
for (int j=0; j<ncol;j++){
for (int k=0;k<ncol;k++){
out(j,k) += outer_prod(j,k)*w;
}
}
}
for (int i=0; i<ncol;i++){
for (int j=0;j<ncol;j++){
out(i,j) /= norm_;
}
}
return out;
}')
I would like to quickly estimated the kernel rewieghted covariance matricies for all observations in a dataset and store them as an array. Since Rcpp doesn't handle arrays I have written the following R function:
get_kern_cov_C = function(data, radius){
# data is data for which we wish to estimate covariances
# radius is the radius of the gaussian kernel
# calculate covariances:
kern_cov = array(0, c(ncol(data),ncol(data),nrow(data)))
for (i in 1:nrow(data)){
kern_cov[,,i] = get_cov_1obs(cdata=data, ID = i-1, radius=radius)
}
return(kern_cov)
}
This seems to work fine (and much, MUCH faster than R) however the problem is that every now and then (seemingly at random) I get an error of the following form:
Error in kern_cov[, , i] = get_cov_1obs(cdata = data, ID = i - 1, radius = radius) :
incompatible types (from X to Y)
where X is either builtin or NULL and Y is double.
I roughly understand why this is happening (I am trying to place a builtin/NULL variable into a double) but I am not sure were in the code the bug is. I suspect this might be something related to memory management as it only occurs every now and again.
You can test for NULL at the C(++) level too, and in this case probably should do that.
As to why it is occurring: I am afraid you will need to debug this.