How to correctly change a list of a ListMatrix in rcpp in R

user7283235

I would like to change the elements of a list of a ListMatrix in rcpp, but always failed to do that. Please see the the following toy example:

library("Rcpp")
cppFunction('
ListMatrix ListMatrixType(ListMatrix x){
            NumericMatrix a = x(0,0);
            a(0,0) = 100;
            return x;
            }
            ')
x = matrix(list(matrix(0,3,2)),2,2)
a = ListMatrixType(x)
a[[1,1]]
a[[2,2]]

I expect only a[[1,1] is changed, but why the a[[2,2]] is also changed?

> a[[1,1]]
     [,1] [,2]
[1,]  100    0
[2,]    0    0
[3,]    0    0
> a[[2,2]]
     [,1] [,2]
[1,]  100    0
[2,]    0    0
[3,]    0    0

I must misunderstand the indexing rule in rcpp. So my question is how to correctly change the elements of each list? I suppose each list contains a matrix.

coatless

TL;DR: Welcome to shared environments and Rcpp's use of pointers.

There are two ways to address R's treatment of objects in a shared environment to avoid the domino update associated with the function.

  1. perform a deep copy of the object and then update the matrix list position or
  2. have only unique elements inside the list (e.g. no duplicated matrices)

To begin, let's look at the memory allocation for this shared memory list. For convenience -- and to avoid tracemem() -- we'll use the lobstr package to explore the shared environment. The package is currently GitHub-only and can be obtained using:

install.packages("devtools")
devtools::install_github("r-lib/lobstr")

With lobstr in hand, let's take a look at the underlying memory addresses of the matrix list in R...

x = matrix(list(matrix(0,3,2)),2,2)
lobstr::obj_addrs(x)
# [1] "0x7fceb69757c8" "0x7fceb69757c8" "0x7fceb69757c8" "0x7fceb69757c8"
#      ^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^ 
# identical memory addresses for all objects

Notice that all objects share the same memory location. Thus, R is treating each of these elements inside the list as being the same. R opts for this behavior to reduce the size of data in memory as each element belongs to the same shared environment.

This is particularly problematic for Rcpp as it is manipulating pointers, e.g. a location in memory that shows where a value is stored, and not values, e.g. a variable that holds a value like an int or a double.

Due to this pointer behavior, two actions occur:

  1. all of the matrices in the matrix list are updated simultaneously, and
  2. the overall object x is updated without requiring a return statement.

If we modify your function slightly, the second point is more apparent.

Modified Function to Illustrate Pointers

Note: This function no longer has a return type, yet we are still seeing the x object change in R's environment.

#include<Rcpp.h>

// [[Rcpp::export]]
void ListMatrixType_pointer(Rcpp::ListMatrix x){
  Rcpp::NumericMatrix a = x(0, 0);
  a(0, 0) = 100;
}

Output

ListMatrixType_pointer(x)
str(x)
# List of 4
# $ : num [1:3, 1:2] 100 0 0 0 0 0
# $ : num [1:3, 1:2] 100 0 0 0 0 0
# $ : num [1:3, 1:2] 100 0 0 0 0 0
# $ : num [1:3, 1:2] 100 0 0 0 0 0
# - attr(*, "dim")= int [1:2] 2 2

Notice, we do not have to return a value as x is automatically updated. Furthermore, we still have the same memory location for each of the elements, e.g.

lobstr::obj_addrs(x)
# [1] "0x7fceb69757c8" "0x7fceb69757c8" "0x7fceb69757c8" "0x7fceb69757c8"
#      ^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^ 
# identical memory addresses for all objects

Deep Copies of Matrix

To get around the identical memory addresses, you can deeply clone the object and then save it back into the ListMatrix. The clone() function instantiates a new block of memory to store the values to. Thus, modifying one element no longer will trigger a domino update. Furthermore, when you add a cloned object back to a list, the memory address changes for only that element.

Using clone() to make a deep copy

#include<Rcpp.h>

// [[Rcpp::export]]
void ListMatrixType_clone(Rcpp::ListMatrix x){
  Rcpp::NumericMatrix a = x(0, 0);

  // Perform a deep copy of a into b
  // and, thus, changing the memory address
  Rcpp::NumericMatrix b = Rcpp::clone(a);
  b(0, 0) = 100;

  // Update x with b
  x(0, 0) = b;
}
Output
ListMatrixType_clone(x)
str(x)
# List of 4
# $ : num [1:3, 1:2] 100 0 0 0 0 0
# $ : num [1:3, 1:2] 0 0 0 0 0 0
# $ : num [1:3, 1:2] 0 0 0 0 0 0
# $ : num [1:3, 1:2] 0 0 0 0 0 0
# - attr(*, "dim")= int [1:2] 2 2

lobstr::obj_addrs(x)
# [1] "0x7fceb811a7e8" "0x7fceb69757c8" "0x7fceb69757c8" "0x7fceb69757c8"
#             ^^^^^^^          ^^^^^^^ 
#            different memory addresses

Unique Elements in a List

To emphasize the need for unique elements, consider modifying the matrix in the first position of the matrix list, the memory address for only the first element will change; the rest of the addresses will remain the same. e.g.

x[[1, 1]] = matrix(1, 2, 2)
lobstr::obj_addrs(x)
# [1] "0x7fceb811a7e8" "0x7fceb69757c8" "0x7fceb69757c8" "0x7fceb69757c8"
#             ^^^^^^^          ^^^^^^^ 
#            different memory addresses

Additional Resources

For further reading on this topic please see any of the linked posts below that emphasize pointer behavior associated with Rcpp (Disclaimer: I wrote them):

Miscellaneous note on Object Sizes

Note: object.size() from utils in Base R provides an incorrect calculation of the shared environment sizes. c.f. ?utils::object.size

This function merely provides a rough indication: it should be reasonably accurate for atomic vectors, but does not detect if elements of a list are shared, for example. (Sharing amongst elements of a character vector is taken into account, but not that between character vectors in a single object.)

lobstr::obj_size(x)
# 424 B
utils::object.size(x)
# 1304 bytes

So, due to the shared environment, the object size of the list is about 1/3.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

Fastest way to transpose a list in R / Rcpp

From Dev

How to change and set Rcpp compile arguments

From Dev

How to change header include guards in Rcpp interfaces?

From Dev

Rcpp - how to call R function from Rcpp function in Shiny

From Dev

How to convert Rcpp::List to std::vector<double>

From Dev

R: how to change character encoding of a list object

From Dev

How to change matlab list into raster brick in R

From Dev

How to generate an R warning safely in Rcpp

From Dev

How to return R's NULL in Rcpp code?

From Dev

How to print an R object to stderr in Rcpp?

From Dev

How to include and link with sourceCpp in R Rcpp

From Dev

how to add function in R package into rcpp code

From Dev

Rcpp: "Cannot change working directory" when called with embedded R code

From Dev

Rcpp: "Cannot change working directory" when called with embedded R code

From Dev

R: How to use IF correctly?

From Dev

R: How to use IF correctly?

From Dev

How to use Observable correctly in a list?

From Dev

How to correctly divide List<string>?

From Dev

How to call an R function in a particular R package from Rcpp

From Dev

R: How to change item (or object) names of a data.table list?

From Dev

R: How to change item (or object) names of a data.table list?

From Dev

Rcpp: dynamically update a list

From Dev

Create a list of Rcpp matrices

From Dev

phonegap + jqmobile: how to change page correctly

From Dev

How to correctly handle itemSize of UICollectionViewLayout on orientation change

From Dev

jquery input - how to handle text change correctly

From Dev

How to correctly change encoding to cp866?

From Dev

How can I protect a matrix in R from being altered by Rcpp?

From Dev

How to remove an element in NumericVector for a recursion using R and Rcpp

Related Related

  1. 1

    Fastest way to transpose a list in R / Rcpp

  2. 2

    How to change and set Rcpp compile arguments

  3. 3

    How to change header include guards in Rcpp interfaces?

  4. 4

    Rcpp - how to call R function from Rcpp function in Shiny

  5. 5

    How to convert Rcpp::List to std::vector<double>

  6. 6

    R: how to change character encoding of a list object

  7. 7

    How to change matlab list into raster brick in R

  8. 8

    How to generate an R warning safely in Rcpp

  9. 9

    How to return R's NULL in Rcpp code?

  10. 10

    How to print an R object to stderr in Rcpp?

  11. 11

    How to include and link with sourceCpp in R Rcpp

  12. 12

    how to add function in R package into rcpp code

  13. 13

    Rcpp: "Cannot change working directory" when called with embedded R code

  14. 14

    Rcpp: "Cannot change working directory" when called with embedded R code

  15. 15

    R: How to use IF correctly?

  16. 16

    R: How to use IF correctly?

  17. 17

    How to use Observable correctly in a list?

  18. 18

    How to correctly divide List<string>?

  19. 19

    How to call an R function in a particular R package from Rcpp

  20. 20

    R: How to change item (or object) names of a data.table list?

  21. 21

    R: How to change item (or object) names of a data.table list?

  22. 22

    Rcpp: dynamically update a list

  23. 23

    Create a list of Rcpp matrices

  24. 24

    phonegap + jqmobile: how to change page correctly

  25. 25

    How to correctly handle itemSize of UICollectionViewLayout on orientation change

  26. 26

    jquery input - how to handle text change correctly

  27. 27

    How to correctly change encoding to cp866?

  28. 28

    How can I protect a matrix in R from being altered by Rcpp?

  29. 29

    How to remove an element in NumericVector for a recursion using R and Rcpp

HotTag

Archive