r-quantities / units

Measurement units for R
https://r-quantities.github.io/units
175 stars 28 forks source link

matrix multiplication #226

Open MaPaw opened 4 years ago

MaPaw commented 4 years ago

I was looking for this function and found the commented code block at the end of the arith.R file. It requires just a few changes to make it work (see below code). The changes are (1) use %*%.numeric as the name of the default method and (2) use as_units() on the other argument to ensure that it has units. With the blow code the examples for %*% work. I hope this is useful.

#' matrix multiplication
#' @name matmult
#' @param x numeric matrix or vector
#' @param y numeric matrix or vector
#' @export
#' @details see \code{"\link[base]{\%*\%}"} for the base function, reimplemented
#'   as default (numeric) method
`%*%` = function(x, y) UseMethod("%*%")

#' @name matmult
#' @export
`%*%.numeric` = function(x, y) {
    if (inherits(y, "units"))
        `%*%.units`(as_units(x), y)
    else
        base::`%*%`(x, y)
}

#' @name matmult
#' @export
#' @examples
#' a = set_units(1:5, m)
#' a %*% a
#' a %*% t(a)
#' a %*% set_units(1:5, 1)
#' set_units(1:5, 1) %*% a
`%*%.units` = function(x, y) {
    ret = base::`%*%`(unclass(x), unclass(y))
    units(ret) = .multiply_symbolic_units(1, units(x), units(as_units(y)))
    ret
}
edzer commented 4 years ago

To do this, you need to overwrite a function in base with a generic, which generates a warning on package load. I think for that reason we decided against this.

Enchufa2 commented 4 years ago

Correct, matrix multiplication is S4 generic, but not S3, so it's not a good idea to overwrite it. We could take a look at this again if we manage at some point to make units compatible with S4. I recall trying to setOldClass units, but I couldn't make it work at that time, and I hadn't have the time to revisit it yet.

billdenney commented 2 years ago

As suggested in #311, I have some thoughts on how matrices may be implemented with units and mixed_units objects to hopefully enable matrix multiplication and generally more complex operations.

My ideas are two-fold.

For units matrices, they will be handled very similarly to how they are already handled by adding the attribute for the units.

For mixed_units matrices, they could be handled very differently than they are right now. As noted in #311, creating a mixed_units matrix works now by creating a list-matrix. I think that this would be simplest, but it would require more effort for doing the conversions after the multiplication.

Another option would be to try to make a mixed_units_matrix_by_row object and a mixed_units_matrix_by_column object. Those would likely be the more common case where multiple units vectors would be combined (e.g. with cbind() or rbind()) or created (e.g. with poly()). That would also simplify some of the work because unit conversions would only be required once per row. But, it would not handle the most general case.

And, these options are not exclusive, but due to maintenance effort, I assume that only supporting one would be preferable.

Enchufa2 commented 2 years ago

A list-matrix is not very useful and a kind of an aberration IMHO. :) And matrix operations would be extremely inefficient. So theoretically we could make it work... for toy examples.

Enchufa2 commented 1 year ago

Thanks to @t-kalinowski, matrix multiplication is now an S3 generic starting R 4.3.0. :) So we'll revisit this.