Skip to content

Commit

Permalink
Test RcppThread myrs on github.
Browse files Browse the repository at this point in the history
  • Loading branch information
Rory Nolan committed Dec 29, 2024
1 parent c70e9de commit 0d4539f
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 120 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ src/*.so
src/*.dll
inst/doc
docs/
tools/
3 changes: 2 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ Suggests:
tidyr
LinkingTo:
Rcpp (>= 1.0.10),
RcppParallel (>= 5.1.7)
RcppParallel (>= 5.1.7),
RcppThread
VignetteBuilder:
knitr
Config/testthat/edition: 3
Expand Down
1 change: 1 addition & 0 deletions src/RcppExports.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Generated by using Rcpp::compileAttributes() -> do not edit by hand
// Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393

#include <RcppThread.h>
#include <Rcpp.h>

using namespace Rcpp;
Expand Down
180 changes: 61 additions & 119 deletions src/rpois_parallel.cc
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
// [[Rcpp::depends(RcppParallel)]]
// [[Rcpp::depends(RcppThread)]]

#include <random>

#include <vector>
#include <Rcpp.h>
#include <RcppParallel.h>
#include <RcppThread.h>

using namespace Rcpp;
using namespace RcppParallel;

// Forward declarations from utils.cpp
IntegerMatrix vec_to_matrix_colmajor(const std::vector<std::vector<int>>& vec);
IntegerMatrix vec_to_matrix_rowmajor(const std::vector<std::vector<int>>& vec);

int mysign(double x) {
if (x >= 0) {
Expand All @@ -17,134 +19,74 @@ int mysign(double x) {
}
}

struct MyRPois : public Worker {

const RVector<double> means;
int seed;

// destination
RVector<int> output;

// initialize with source and destination
MyRPois(NumericVector means, int seed, IntegerVector output) :
means(means), seed(seed), output(output) {}

void operator()(std::size_t begin, std::size_t end) {
std::minstd_rand generator_int(seed + begin);
std::uniform_int_distribution<int> distribution_int(1, RAND_MAX);
for (std::size_t i = begin; i != end; ++i) {
int seed_i = distribution_int(generator_int);
std::minstd_rand generator(seed_i);
std::poisson_distribution<int> distribution(std::abs(means[i]));
output[i] = distribution(generator) * mysign(means[i]);
}
}
};

// [[Rcpp::export]]
IntegerVector myrpois_(NumericVector means, int seed) {

std::size_t n = means.size();

// allocate the matrix we will return
IntegerVector output(n);

// create the worker
MyRPois myRPois(means, seed, output);

// call it with parallelFor
parallelFor(0, n, myRPois);

return output;
}

struct MyRPoisFrames : public Worker {

const RVector<double> means;
std::size_t frame_length;
int seed;

// destination
RMatrix<int> output;

// initialize with source and destination
MyRPoisFrames(NumericVector means, std::size_t frame_length,
int seed, IntegerMatrix output) :
means(means), frame_length(frame_length), seed(seed), output(output) {}

void operator()(std::size_t begin, std::size_t end) {
for (std::size_t i = begin; i != end; ++i) {
std::minstd_rand generator_int(seed + begin);
std::uniform_int_distribution<int> distribution_int(1, RAND_MAX);
int seed_i = distribution_int(generator_int);
std::minstd_rand generator(seed_i);
std::poisson_distribution<int> distribution(std::abs(means[i]));
for (std::size_t j = 0; j != frame_length; ++j) {
output(j, i) = distribution(generator);
}
}
if (n == 0) {
return IntegerVector(0);
}
};

std::vector<int> temp_output(n);

auto worker = [&](std::size_t i) {
std::minstd_rand generator_int(seed + i);
std::uniform_int_distribution<int> distribution_int(1, RAND_MAX);
int seed_i = distribution_int(generator_int);
std::minstd_rand generator(seed_i);
std::poisson_distribution<int> distribution(std::abs(means[i]));
temp_output[i] = distribution(generator) * mysign(means[i]);
};

RcppThread::parallelFor(0, n, worker);
return IntegerVector(temp_output.begin(), temp_output.end());
}

// [[Rcpp::export]]
IntegerMatrix myrpois_frames_(NumericVector means, std::size_t frame_length,
int seed) {

int seed) {
std::size_t ncol = means.size();
if (ncol == 0) {
return IntegerMatrix(frame_length, 0);
}

std::vector<std::vector<int>> temp_output(ncol, std::vector<int>(frame_length));

auto worker = [&](std::size_t i) {
std::minstd_rand generator_int(seed + i);
std::uniform_int_distribution<int> distribution_int(1, RAND_MAX);
int seed_i = distribution_int(generator_int);
std::minstd_rand generator(seed_i);
std::poisson_distribution<int> distribution(std::abs(means[i]));
for (std::size_t j = 0; j != frame_length; ++j) {
temp_output[i][j] = distribution(generator) * mysign(means[i]);
}
};

IntegerMatrix output(frame_length, ncol);

// create the worker
MyRPoisFrames myRPoisFrames(means, frame_length, seed, output);

// call it with parallelFor
parallelFor(0, ncol, myRPoisFrames);

return output;
RcppThread::parallelFor(0, ncol, worker);
return vec_to_matrix_colmajor(temp_output);
}


struct MyRPoisFramesT : public Worker {

const RVector<double> means;
std::size_t frame_length;
int seed;

// destination
RMatrix<int> output;

// initialize with source and destination
MyRPoisFramesT(NumericVector means, std::size_t frame_length,
int seed, IntegerMatrix output) :
means(means), frame_length(frame_length), seed(seed), output(output) {}

void operator()(std::size_t begin, std::size_t end) {
for (std::size_t i = begin; i != end; ++i) {
std::minstd_rand generator_int(seed + begin);
std::uniform_int_distribution<int> distribution_int(1, RAND_MAX);
int seed_i = distribution_int(generator_int);
std::minstd_rand generator(seed_i);
std::poisson_distribution<int> distribution(std::abs(means[i]));
for (std::size_t j = 0; j != frame_length; ++j) {
output(i, j) = distribution(generator);
}
}
}
};

// [[Rcpp::export]]
IntegerMatrix myrpois_frames_t_(NumericVector means, std::size_t frame_length,
int seed) {

int seed) {
std::size_t nrow = means.size();
if (nrow == 0) {
return IntegerMatrix(0, frame_length);
}

std::vector<std::vector<int>> temp_output(nrow, std::vector<int>(frame_length));

auto worker = [&](std::size_t i) {
std::minstd_rand generator_int(seed + i);
std::uniform_int_distribution<int> distribution_int(1, RAND_MAX);
int seed_i = distribution_int(generator_int);
std::minstd_rand generator(seed_i);
std::poisson_distribution<int> distribution(std::abs(means[i]));
for (std::size_t j = 0; j != frame_length; ++j) {
temp_output[i][j] = distribution(generator) * mysign(means[i]);
}
};

IntegerMatrix output(nrow, frame_length);

// create the worker
MyRPoisFramesT myRPoisFramesT(means, frame_length, seed, output);

// call it with parallelFor
parallelFor(0, nrow, myRPoisFramesT);

return output;
RcppThread::parallelFor(0, nrow, worker);
return vec_to_matrix_rowmajor(temp_output);
}
33 changes: 33 additions & 0 deletions src/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,36 @@ IntegerVector vec_add1s(IntegerVector vec, IntegerVector add_pos) {
return vec;
}

// Convert std::vector<std::vector<int>> to IntegerMatrix with column-major layout
// (each inner vector represents a column)
IntegerMatrix vec_to_matrix_colmajor(const std::vector<std::vector<int>>& vec) {
if (vec.empty()) {
return IntegerMatrix(0, 0);
}
std::size_t ncol = vec.size();
std::size_t nrow = vec[0].size();
IntegerMatrix out(nrow, ncol);
for (std::size_t i = 0; i < ncol; ++i) {
for (std::size_t j = 0; j < nrow; ++j) {
out(j, i) = vec[i][j];
}
}
return out;
}

// Convert std::vector<std::vector<int>> to IntegerMatrix with row-major layout
// (each inner vector represents a row)
IntegerMatrix vec_to_matrix_rowmajor(const std::vector<std::vector<int>>& vec) {
if (vec.empty()) {
return IntegerMatrix(0, 0);
}
std::size_t nrow = vec.size();
std::size_t ncol = vec[0].size();
IntegerMatrix out(nrow, ncol);
for (std::size_t i = 0; i < nrow; ++i) {
for (std::size_t j = 0; j < ncol; ++j) {
out(i, j) = vec[i][j];
}
}
return out;
}
25 changes: 25 additions & 0 deletions tests/testthat/test-frame-utils-na.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
test_that("NA handling in frame stats works", {
# Create test array with all NAs
all_na_arr <- array(NA_real_, dim = c(2, 2, 2))

# Test sum_frames_na_omit
expect_true(all(is.na(dbl_sum_frames_na_omit(all_na_arr))))
expect_true(all(is.na(int_sum_frames_na_omit(array(NA_integer_, dim = c(2, 2, 2))))))

# Test mean_frames_na_omit
expect_true(all(is.na(dbl_mean_frames_na_omit(all_na_arr))))
expect_true(all(is.na(int_mean_frames_na_omit(array(NA_integer_, dim = c(2, 2, 2))))))

# Create test array with mix of values and NAs
mixed_arr <- array(c(1, NA, 3, NA, 5, NA, 7, NA), dim = c(2, 2, 2))

# Test sum_frames_na_omit with mixed data
sums <- dbl_sum_frames_na_omit(mixed_arr)
expect_equal(sums[1], 4) # 1 + 3
expect_equal(sums[2], 12) # 5 + 7

# Test mean_frames_na_omit with mixed data
means <- dbl_mean_frames_na_omit(mixed_arr)
expect_equal(means[1], 2) # (1 + 3) / 2
expect_equal(means[2], 6) # (5 + 7) / 2
})
62 changes: 62 additions & 0 deletions tests/testthat/test-myrs.R
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,65 @@ test_that("rfromboxes and rtoboxes doesn't hang with a non-integer n", {
)
expect_equal(sum(from), 2)
})

test_that("myrpois_frames produces consistent results on mac", {
skip_if(getRversion() < "3.6.0")
skip_if_not(get_os() == "mac")
set.seed(1)
means <- 1:5
frame_length <- 3
result <- myrpois_frames(means, frame_length)
expect_equal(dim(result), c(frame_length, length(means)))
expect_equal(result[,1], c(1, 1, 0)) # Updated expected values with seed(1)
# Test parallel vs non-parallel results
set.seed(1)
parallel_result <- myrpois_frames(means, frame_length, parallel = TRUE)
set.seed(1)
serial_result <- myrpois_frames(means, frame_length, parallel = FALSE)
expect_equal(parallel_result, serial_result)
})

test_that("myrpois_frames_t produces consistent results on mac", {
skip_if(getRversion() < "3.6.0")
skip_if_not(get_os() == "mac")
set.seed(1)
means <- 1:5
frame_length <- 3
result <- myrpois_frames_t(means, frame_length)
expect_equal(dim(result), c(length(means), frame_length))
expect_equal(result[1,], c(1, 1, 0)) # Updated expected values with seed(1)
# Test parallel vs non-parallel results
set.seed(1)
parallel_result <- myrpois_frames_t(means, frame_length, parallel = TRUE)
set.seed(1)
serial_result <- myrpois_frames_t(means, frame_length, parallel = FALSE)
expect_equal(parallel_result, serial_result)
})

test_that("myrpois_frames handles edge cases", {
skip_if(getRversion() < "3.6.0")
# Empty means vector - returns empty matrix
result_empty <- myrpois_frames(numeric(0), 3)
expect_equal(dim(result_empty), c(3, 0))
# Zero frame length - returns empty matrix
result_zero <- myrpois_frames(1:5, 0)
expect_equal(dim(result_zero), c(0, 5))
# Negative means
result_neg <- myrpois_frames(c(-2, -1, 0, 1, 2), 2)
expect_equal(dim(result_neg), c(2, 5))
expect_true(all(result_neg[, 4:5] >= 0)) # Positive means give non-negative results
})

test_that("myrpois_frames_t handles edge cases", {
skip_if(getRversion() < "3.6.0")
# Empty means vector - returns empty matrix
result_empty <- myrpois_frames_t(numeric(0), 3)
expect_equal(dim(result_empty), c(0, 3))
# Zero frame length - returns empty matrix
result_zero <- myrpois_frames_t(1:5, 0)
expect_equal(dim(result_zero), c(5, 0))
# Negative means
result_neg <- myrpois_frames_t(c(-2, -1, 0, 1, 2), 2)
expect_equal(dim(result_neg), c(5, 2))
expect_true(all(result_neg[4:5,] >= 0)) # Positive means give non-negative results
})

0 comments on commit 0d4539f

Please sign in to comment.