---
title: "Elementwise Binary Operators"
output: 
  rmarkdown::html_vignette:
    toc: true
vignette: >
  %\VignetteIndexEntry{Elementwise Binary Operators}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

[![status](https://img.shields.io/badge/status-working%20draft-red)](https://canmod.github.io/macpan2/articles/vignette-status#working-draft)

```{r, include = FALSE}
library(macpan2)
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
set.seed(1L)
```

## The Problem

In the `C++` [engine](https://canmod.github.io/macpan2/articles/cpp_side) every variable is a matrix.  In this simple situation where every variable is a matrix, elementwise binary operations can be defined to have very convenient properties.  The problem is that these properties are not standard in `R`, probably because it is not the case that all numeric variables are matrices.  Differences between standard `R` mathematical functions and the McMasterPandemic `C++` engine make it more difficult to test the engine.  This document describes how to use `R` so that elementwise binary operators are comparable with those in the engine.

Consider the following three related matrices.
```{r}
A = matrix(rnorm(6), 3, 2)  # 3 by 2 matrix
x = matrix(rnorm(3))        # 3 by 1 matrix
y = t(rnorm(2))             # 1 by 2 matrix
```

They relate together in that `A` and `x` have the same number of rows, and `A` and `y` have the same number of columns. Note that although they are dimensionally related, these three objects are of different shape in that `x` and `y` have only one column and row respectively, whereas `A` has more than one row and column.

Because of these relationships we might naturally want to multiply every column of `A` by the column vector `x`, but in `R` we get the following error.
```{r}
try(A * x)
```
Here we define rigorously what convenient properties we expect of elementwise binary operators when all variables are matrices, and show how to convert elementwise binary operators in `R` into operators that have these properties.

## Definition of an Elementwise Binary Operator in the C++ Engine

Consider a generic binary operator, $\otimes$, that operates on two scalars to produce a third. We can overload this operator to take two matrices, $x$ and $y$, and return a third matrix, $z$.
$$
z = x \otimes y
$$
The elements of $z$ are given by the following expression.
$$
z_{i,j} = \cases{ \begin{array}{lll}
  x_{i,j} \otimes y_{i,j} & \text{if } n(x) = n(y) & \text{and } m(x) = m(y) & \text{Standard Hadamard product} \\
  x_{i,j} \otimes y_{i,1} & \text{if } n(x) = n(y) & \text{and } m(y) = 1 & \text{Each matrix column times a column vector} \\
  x_{i,j} \otimes y_{1,j} & \text{if } n(y) = 1 & \text{and } m(x) = m(y) & \text{Each matrix row times a row vector} \\
  x_{i,1} \otimes y_{i,j} & \text{if } n(x) = n(y) & \text{and } m(x) = 1 & \text{Column vector times each matrix column} \\
  x_{1,j} \otimes y_{i,j} & \text{if } n(x) = 1 & \text{and } m(x) = m(y) & \text{Row vector times each matrix row} \\
  x_{1,1} \otimes y_{i,j} & \text{if } n(x) = m(x) = 1  & & \text{Scalar times a matrix, vector or scalar} \\
  x_{i,j} \otimes y_{1,1} & \text{if } n(y) = m(y) = 1 & & \text{Matrix, vector or scalar times a scalar} \\
\end{array}}
$$
Where the functions $n()$ and $m()$ give the numbers of rows and columns respectively.

## Forcing a Binary Operator in R to have these Properties

We consider two matrix-valued operands, `x` and `y`, and a standard binary operator, `op` (e.g. `+`), in R.

### Step 1

If the operands have the same shape then just do the operation.
```{r, eval=FALSE}
eq = dim(x) == dim(y)
if (all(eq)) return(op(x, y))
```
This works because most R numeric operations are vectorized anyways.

### Step 2

If `x` has either one row or one column, define the operation with the arguments swapped otherwise keep the operator unchanged.
```{r, eval=FALSE}
vec_x = any(dim(x) == 1L)
op1 = op
if (vec_x) op1 = function(x, y) op(y, x)
```

### Step 3

Apply the [base-R `sweep` function](https://search.r-project.org/R/refmans/base/html/sweep.html), making sure that the most matrix-like operand comes first.
```{r, eval=FALSE}
if (any(eq) & vec_x) return(sweep(y, which(eq), x, op1))
if (any(eq))         return(sweep(x, which(eq), y, op1))
```

### Implementation and Examples

The `BinaryOperator` constructor uses this algorithm.
```{r}
times = BinaryOperator(`*`)
pow = BinaryOperator(`^`)
```

Here are some examples.
```{r}
(A = matrix(1:6, 3, 2))
(x = matrix(1:3, 3))
(y = matrix(1:2, 1))
times(A, x)
pow(A, y)
```

If we tried to do these operations naively, the R engine would complain.
```{r}
try(A * x)
try(A ^ y)
```

Note that this algorithm does the right thing for both commutative (e.g. `*`) and non-commutative (e.g. `^`) operators.
```{r}
identical(times(A, x), times(x, A))
identical(pow(A, x), pow(x, A))
```
