RobinHankin / partitions

R package for integer partitions
9 stars 5 forks source link

bug in multiset() #10

Closed RobinHankin closed 5 years ago

RobinHankin commented 5 years ago

Bug in multiset(). Argument n should give the number of rows of the output matrix but multiset() fails if the user specifies n=length(v) (which should be the default):

> library(partitions)
> multiset(c(1,2,2,3))

[1,] 1 1 1 2 2 2 2 2 2 3 3 3
[2,] 2 2 3 1 1 2 2 3 3 1 2 2
[3,] 2 3 2 2 3 1 3 1 2 2 1 2
[4,] 3 2 2 3 2 3 1 2 1 2 2 1
> multiset(c(1,2,2,3),3)

[1,] 1 2 2 1 1 2 2 3 3 2 2 3
[2,] 2 1 2 2 3 1 3 1 2 2 3 2
[3,] 2 2 1 3 2 3 1 2 1 3 2 2
> multiset(c(1,2,2,3),2)

[1,] 1 2 2 1 3 2 3
[2,] 2 1 2 3 1 3 2
> multiset(c(1,2,2,3),4)
Error in do.call("cbind", apply(apply(blockparts(table(v), n), 2, function(u) { : 
  second argument must be a list
> 

To see the problem, look at the following code. In the function, the output of apply() is sent to cbind() by do.call():

> v <- c(1,2,2,3)
> n <- 3
> apply(apply(blockparts(table(v),n),2,function(u){rep(unique(v),u)}),2,mset)
[[1]]

[1,] 1 2 2
[2,] 2 1 2
[3,] 2 2 1

[[2]]

[1,] 1 1 2 2 3 3
[2,] 2 3 1 3 1 2
[3,] 3 2 3 1 2 1

[[3]]

[1,] 2 2 3
[2,] 2 3 2
[3,] 3 2 2

> 

But look what happens if n <- 4:

> v <- c(1,2,2,3)
> n <- 4
> apply(apply(blockparts(table(v),n),2,function(u){rep(unique(v),u)}),2,mset)
      [,1]
 [1,]    1
 [2,]    2
 [3,]    2
 [4,]    3
 [snip]
[47,]    2
[48,]    1
> 

This is becauseapply() is simplifying its output, as documented: "If each call to FUN returns a vector of length n, then ‘apply’ returns an array of dimension c(n, dim(X)[MARGIN]) if n > 1".

Here is a simpler example:

> apply(cbind(1:3,c(1,3,3)),2,blockparts)  # works as intended
[[1]]

[1,] 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
[2,] 0 0 1 1 2 2 0 0 1 1 2 2 0 0 1 1 2 2 0 0 1 1 2 2
[3,] 0 0 0 0 0 0 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3

[[2]]

[1,] 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
[2,] 0 0 1 1 2 2 3 3 0 0 1 1 2 2 3 3 0 0 1 1 2 2 3 3 0 0 1 1 2 2 3 3
[3,] 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3

> apply(cbind(1:3,c(1,3,2)),2,blockparts)  # unwanted "simplification"
      [,1] [,2]
 [1,]    0    0
 [2,]    0    0
 [3,]    0    0
 [4,]    1    1
 [5,]    0    0
 [snip]
[71,]    2    3
[72,]    3    2

On R-devel, Brian Ripley said: "you seem to want a list result from apply(). If that is what you want, why not use lapply()? E.g.

lapply(split(m, row(m)), max) 

is the list equivalent of apply(m, 1, max).

RobinHankin commented 5 years ago

Ironically the difficult part of the fix, wherein apply() is replaced with lapply() is unnecessry because the code only broke when n==length(v) and this case is caught on the second line which, as it says in its own comment, is unnecessary.