diff --git a/NAMESPACE b/NAMESPACE index 23faf52bf7..88837aec31 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -464,6 +464,7 @@ export(optimizer) export(sampler) export(slc) export(tensor_dataset) +export(tensor_from_buffer) export(torch_abs) export(torch_absolute) export(torch_acos) diff --git a/R/RcppExports.R b/R/RcppExports.R index 3f1091531b..293936d038 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -14597,6 +14597,10 @@ cpp_torch_tensor_print <- function(x, n) { invisible(.Call(`_torch_cpp_torch_tensor_print`, x, n)) } +cpp_tensor_from_buffer <- function(data, shape, options) { + .Call(`_torch_cpp_tensor_from_buffer`, data, shape, options) +} + cpp_torch_tensor_dtype <- function(x) { .Call(`_torch_cpp_torch_tensor_dtype`, x) } diff --git a/R/tensor.R b/R/tensor.R index e0bac2af18..1f2c9dedd0 100644 --- a/R/tensor.R +++ b/R/tensor.R @@ -478,3 +478,17 @@ tensor_to_complex <- function(x) { torch_tensor(Im(x), dtype = torch_double()) ) } + +#' Creates a tensor from a buffer of memory +#' +#' It creates a tensor without taking ownership of the memory it points to. +#' You must call `clone` if you want to copy the memory over a new tensor. +#' +#' @param buffer An R atomic object containing the data in a contiguous array. +#' @param shape The shape of the resulting tensor. +#' @param dtype A torch data type for the tresulting tensor. +#' +#' @export +tensor_from_buffer <- function(buffer, shape, dtype = "float") { + cpp_tensor_from_buffer(buffer, shape, list(dtype=dtype)) +} diff --git a/man/tensor_from_buffer.Rd b/man/tensor_from_buffer.Rd new file mode 100644 index 0000000000..3d59beee9b --- /dev/null +++ b/man/tensor_from_buffer.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/tensor.R +\name{tensor_from_buffer} +\alias{tensor_from_buffer} +\title{Creates a tensor from a buffer of memory} +\usage{ +tensor_from_buffer(buffer, shape, dtype = "float") +} +\arguments{ +\item{buffer}{An R atomic object containing the data in a contiguous array.} + +\item{shape}{The shape of the resulting tensor.} + +\item{dtype}{A torch data type for the tresulting tensor.} +} +\description{ +It creates a tensor without taking ownership of the memory it points to. +You must call \code{clone} if you want to copy the memory over a new tensor. +} diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index 989718a18c..1315b3db27 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -48356,6 +48356,19 @@ BEGIN_RCPP return R_NilValue; END_RCPP } +// cpp_tensor_from_buffer +torch::Tensor cpp_tensor_from_buffer(const SEXP& data, std::vector shape, XPtrTorchTensorOptions options); +RcppExport SEXP _torch_cpp_tensor_from_buffer(SEXP dataSEXP, SEXP shapeSEXP, SEXP optionsSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const SEXP& >::type data(dataSEXP); + Rcpp::traits::input_parameter< std::vector >::type shape(shapeSEXP); + Rcpp::traits::input_parameter< XPtrTorchTensorOptions >::type options(optionsSEXP); + rcpp_result_gen = Rcpp::wrap(cpp_tensor_from_buffer(data, shape, options)); + return rcpp_result_gen; +END_RCPP +} // cpp_torch_tensor_dtype Rcpp::XPtr cpp_torch_tensor_dtype(torch::Tensor x); RcppExport SEXP _torch_cpp_torch_tensor_dtype(SEXP xSEXP) { @@ -52515,6 +52528,7 @@ static const R_CallMethodDef CallEntries[] = { {"_torch_cpp_Tensor_has_storage", (DL_FUNC) &_torch_cpp_Tensor_has_storage, 1}, {"_torch_cpp_Storage_data_ptr", (DL_FUNC) &_torch_cpp_Storage_data_ptr, 1}, {"_torch_cpp_torch_tensor_print", (DL_FUNC) &_torch_cpp_torch_tensor_print, 2}, + {"_torch_cpp_tensor_from_buffer", (DL_FUNC) &_torch_cpp_tensor_from_buffer, 3}, {"_torch_cpp_torch_tensor_dtype", (DL_FUNC) &_torch_cpp_torch_tensor_dtype, 1}, {"_torch_torch_tensor_cpp", (DL_FUNC) &_torch_torch_tensor_cpp, 5}, {"_torch_cpp_as_array", (DL_FUNC) &_torch_cpp_as_array, 1}, diff --git a/src/lantern/src/Tensor.cpp b/src/lantern/src/Tensor.cpp index ab9d9e3f80..3a34fe4696 100644 --- a/src/lantern/src/Tensor.cpp +++ b/src/lantern/src/Tensor.cpp @@ -11,10 +11,20 @@ void *_lantern_from_blob(void *data, int64_t *sizes, size_t sizes_size, int64_t *strides, size_t strides_size, void *options) { LANTERN_FUNCTION_START - return make_raw::Tensor( - torch::from_blob(data, std::vector(sizes, sizes + sizes_size), - std::vector(strides, strides + strides_size), - from_raw::TensorOptions(options))); + if (strides_size == 0) { + return make_raw::Tensor(torch::from_blob( + data, + std::vector(sizes, sizes + sizes_size), + from_raw::TensorOptions(options) + )); + } else { + return make_raw::Tensor(torch::from_blob( + data, + std::vector(sizes, sizes + sizes_size), + std::vector(strides, strides + strides_size), + from_raw::TensorOptions(options) + )); + } LANTERN_FUNCTION_END } diff --git a/src/tensor.cpp b/src/tensor.cpp index 7aa7775bc6..f1918654fb 100644 --- a/src/tensor.cpp +++ b/src/tensor.cpp @@ -48,6 +48,19 @@ void cpp_torch_tensor_print(torch::Tensor x, int n) { Rcpp::Rcout << result; }; +// [[Rcpp::export]] +torch::Tensor cpp_tensor_from_buffer(const SEXP& data, std::vector shape, XPtrTorchTensorOptions options) { + return lantern_from_blob( + DATAPTR(data), + &shape[0], + shape.size(), + // we use the default strides + nullptr, + 0, + options.get() + ); +} + // [[Rcpp::export]] Rcpp::XPtr cpp_torch_tensor_dtype(torch::Tensor x) { XPtrTorchDtype out = lantern_Tensor_dtype(x.get()); diff --git a/tests/testthat/test-tensor.R b/tests/testthat/test-tensor.R index b23b0a4415..340cf65a45 100644 --- a/tests/testthat/test-tensor.R +++ b/tests/testthat/test-tensor.R @@ -524,4 +524,12 @@ test_that("can convert to half using the method `half()`", { x <- torch_tensor(1, dtype="half") expect_equal(as.numeric(x), 1) +}) + +test_that("can create tensor from a buffer", { + x <- runif(10) + y <- tensor_from_buffer(x, shape = 10, dtype = "float64") + expect_equal(as.numeric(y), x) + y$add_(1) + expect_equal(as.numeric(y), x) }) \ No newline at end of file