Hi, Man page for 'convolve' says: conj: logical; if 'TRUE', take the complex _conjugate_ before back-transforming (default, and used for usual convolution). The complex conjugate of 'x', of 'y', of both? In fact it seems that it takes the complex conjugate of 'y' only which is OK but might be worth mentioning because (1) conj=TRUE is the default and (2) with this default then convolve(x,y) is not the same as convolve(y,x). Note that 'convolve' is commutative with conj=FALSE and would be too if conj=TRUE was taking the complex conjugate of x _and_ y... Cheers, H.
Herve Pages
2007-Feb-03 01:03 UTC
[Rd] convolve: request for "usual" behaviour + some improvements + some fixes
Hi again, There are many problems with current 'convolve' function. The author of the man page seems to be aware that 'convolve' does _not_ the usual thing: Note that the usual definition of convolution of two sequences 'x' and 'y' is given by 'convolve(x, rev(y), type = "o")'. and indeed, it does not: > x <- 1:3 > y <- 3:1 > convolve(x, y, type="o") [1] 1 4 10 12 9 The "usual" convolution would rather give: > convolve(x, rev(y), type="o") [1] 3 8 14 8 3 Also the "usual" convolution is commutative: > convolve(y, rev(x), type="o") [1] 3 8 14 8 3 but convolve is not: > convolve(y, x, type="o") [1] 9 12 10 4 1 Of course I could write the following wrapper: usual_convolve <- function(x, y, ...) convolve(x, rev(y)) to work around those issues but 'convolve' has other problems: (1) The input sequences shouldn't need to have the same length when type = "circular" (the shortest can be right-padded with 0s up to the length of the longest). (2) If the input sequences are both integer vectors, then the result should be an integer vector too. (3) The "filter" feature seems to be broken (it's not even clear what it should do or why we need it though): > x <- 1:9 > y <- 1 > convolve(x, y, type="f") Error in convolve(x, y, type = "f") : subscript out of bounds > convolve(y, x, type="f") numeric(0) (4) If you look at the source code, you'll see that 'x' is first left-padded with '0's. The "usual" convolution doesn't do that: it always padd sequences on the _same_ side (generally on the right). (5) It's not clear why we need a 'conj' arg. All what it does is take the conjugate of fft(y) before it does the product with fft(x). But this has the "non-usual" effect of reverting the expected result: > round(convolve(as.integer(c(0,0,0,1)), 1:7, type="o")) [1] 0 0 0 7 6 5 4 3 2 1 Here below is my version of 'convolve' just in case. It does the "usual" convolution plus: - no need to have 'x' and 'y' of the same length when 'type' is "circular", - when 'x' and 'y' are integer vectors, the output is an integer vector, - no more 'conj' arg (not needed, only leads to confusion), - when type is "filter", the output sequence is the same as with type="open" but is truncated to the length of 'x' (the original signal) It can be seen has the result of 'x' filtered by 'y' (the filter). convolve2 <- function(x, y, type = c("circular", "open", "filter")) { type <- match.arg(type) nx <- length(x) ny <- length(y) if (type == "circular") nz <- max(nx, ny) else nz <- nx + ny - 1 if (nz > nx) x[(nx+1):nz] <- as.integer(0) if (nz > ny) y[(ny+1):nz] <- as.integer(0) fx <- fft(x) fy <- fft(y) fz <- fx * fy z <- fft(fz, inverse=TRUE) / nz if (is.numeric(x) && is.numeric(y)) z <- Re(z) if (is.integer(x) && is.integer(y)) z <- as.integer(round(z)) if (type == "filter") z[1:nx] else z } Cheers, H.