For any two matrices \(\textbf{A} = \{a_{ij}\}\) and \(\textbf{B} = \{b_{ij}\}\) of the same dimensions \(m\times n\), the Hadamard product between them is defined as the element-wise or entry-wise product

\[ \textbf{A}\odot\textbf{B} = \{a_{ij}b_{ij}\} \]

This can be performed using the Râ€™s product operator
(`*`

). Alternatively, the `Hadamard()`

function
from the â€˜tensorEVDâ€™ R-package can be used.

Direct product of compatible matrices, i.e., having the same dimension.

```
# Simulating matrices A and B
m = 100; n = 150
A <- matrix(rnorm(m*n), ncol=n)
B <- matrix(rnorm(m*n), ncol=n)
# Making the Hadamard product
K1 <- A*B
K2 <- Hadamard(A, B)
all.equal(K1, K2) # should be equal
```

`## [1] TRUE`

**Simple scalar multiplication**. Let \(a\) be a scalar and \(\textbf{B}\) any matrix. Then, using the
Râ€™s product operator (`*`

) will multiply \(\textbf{B}\) by the scalar. However, the
`Hadamard()`

function will produce an error because
incompatibility. In this case, the `Kronecker()`

function can
be used.

```
a <- 10
( B <- matrix(1:6, ncol=2) )
```

```
## [,1] [,2]
## [1,] 1 4
## [2,] 2 5
## [3,] 3 6
```

`a*B # using the product operator`

```
## [,1] [,2]
## [1,] 10 40
## [2,] 20 50
## [3,] 30 60
```

`try(Hadamard(a, B), silent=TRUE)[[1]]`

`## [1] "Error in Hadamard(a, B) : 'nrow(A)' should be the same as 'nrow(B)'.\nOtherwise, provide parameter(s) 'rowsA' and 'rowsB'\n"`

`Kronecker(a, B) # Kronecker instead of Hadamard`

```
## [,1] [,2]
## [1,] 10 40
## [2,] 20 50
## [3,] 30 60
```

If \(\textbf{A}\) and \(\textbf{B}\) are not of the same dimensions (e.g., \(\textbf{A}\) is \(m \times n\) and \(\textbf{B}\) is \(p \times q\)), the Hadamard product is not defined

```
# Simulating A and B of different dimensions
m = 100; n = 150
p = 200; q = 120
A <- matrix(rnorm(m*n), ncol=n) # m x n
B <- matrix(rnorm(p*q), ncol=q) # p x q
try(A*B, silent=TRUE)[[1]]
```

`## [1] "Error in A * B : non-conformable arrays\n"`

However, a Hadamard product can be still performed between \(\textbf{A}\) and \(\textbf{B}\) using incidence matrices of appropriate dimensions

A Hadamard product between \(\textbf{B}\) and a submatrix of \(\textbf{A}\) can be performed by doing

\[ (\textbf{R}\textbf{A}\textbf{C}') \odot \textbf{B} \]

where \(\textbf{R}\) and \(\textbf{C}\) are incidence matrices for rows and columns, respectively.

Product matrix \(\textbf{R}\textbf{A}\textbf{C}'\) can
be obtained by matrix indexing using, for instance, integer vectors
`rowsA`

and `colsA`

, as
`A[rowsA,colsA]`

. Therefore, the Hadamard product can be
obtained by doing `A[rowsA,colsA]*B`

. This Hadamard will be
of the same dimensions as \(\textbf{B}\).

The `Hadamard()`

function computes this Hadamard product
directly from \(\textbf{A}\) without
forming `A[rowsA,colsA]`

matrices. For example

```
# Subsetting rows and columns of A each of length
# equal to nrow(B) and ncol(B), respectively
rowsA <- sample(seq(nrow(A)), nrow(B), replace=TRUE)
colsA <- sample(seq(ncol(A)), ncol(B), replace=TRUE)
# Making the Hadamard product
K1 <- A[rowsA,colsA]*B
K2 <- Hadamard(A, B, rowsA=rowsA, colsA=colsA)
all.equal(K1, K2)
```

`## [1] TRUE`

`dim(K2) == dim(B) # has the same dimension as B`

`## [1] TRUE TRUE`

Likewise, a Hadamard product between \(\textbf{A}\) and a submatrix of \(\textbf{B}\), say
`B[rowsB,colsB]`

, can be performed. For example,

```
rowsB <- sample(seq(nrow(B)), nrow(A), replace=TRUE)
colsB <- sample(seq(ncol(B)), ncol(A), replace=TRUE)
K2 <- Hadamard(A, B, rowsB=rowsB, colsB=colsB)
dim(K2) == dim(A)
```

`## [1] TRUE TRUE`

A Hadamard product between a submatrix of \(\textbf{A}\) and a submatrix of \(\textbf{B}\) can be computed if the indexed
matrices `A[rowsA,colsA]`

and `B[rowsB,colsB]`

are
compatible, i.e., as long as, `length(rowsA)`

=
`length(rowsB)`

and `length(colsA)`

=
`length(colsB).`

Therefore, this allows to obtain a Hadamard
product of any desirable dimension different from that of \(\textbf{A}\) or \(\textbf{B}\).

```
dm <- c(1000, 2000) # a Hadamard of 1000 x 2000
# Obtaining a submatrix from A
rowsA <- sample(seq(nrow(A)), dm[1], replace=TRUE)
colsA <- sample(seq(ncol(A)), dm[2], replace=TRUE)
# Obtaining a submatrix from B
rowsB <- sample(seq(nrow(B)), dm[1], replace=TRUE)
colsB <- sample(seq(ncol(B)), dm[2], replace=TRUE)
# Making the Hadamard product
K1 <- A[rowsA,colsA]*B[rowsB,colsB]
K2 <- Hadamard(A, B, rowsA=rowsA, rowsB=rowsB, colsA=colsA, colsB=colsB)
all.equal(K1, K2)
```

`## [1] TRUE`

Here we compare the speed performance of the `Hadamard()`

function and of the product operator (`*`

) on calculating
small and large Hadamard products by subsetting from matrices \(\textbf{A}\) and \(\textbf{B}\). The following benchmark was
performed using the code provided in this script
run on a Linux environment based on the following system settings:

- Machine: Intel(R) Xeon(R) Gold 6148 CPU @ 2.40GHz
- Memory: 64 GB in RAM
- R version 4.1.1 (2021-08-10)

`

If \(\textbf{A}\) is used as such (i.e., is not indexed), the resulting Hadamard will be of the same dimension as \(\textbf{A}\); therefore, we could overwrite the result on the memory occupied by \(\textbf{A}\)

Usually, assigning an output to an object will occupy a different memory address than inputs:

```
A <- matrix(rnorm(30), ncol=5)
B <- matrix(rnorm(30), ncol=5)
K <- Hadamard(A, B)
c(K=pryr::address(K), A=pryr::address(A))
```

```
## K A
## "0x7fcc50f05a80" "0x7fcc50e651f0"
```

The parameter `inplace`

can be used to store the output at
the same address as the input:

```
A <- Hadamard(A, B, inplace=TRUE)
c(A=pryr::address(A)) # output address remain unchanged
```

```
## A
## "0x7fcc50e651f0"
```

`all.equal(K, A) # contains the desired result `

`## [1] TRUE`

Row and column names for the Hadamard product can be retrieved using
the `make.dimnames`

argument. Attribute `dimnames`

of the Hadamard will be produced by crossing `rownames`

and
`colnames`

of input \(\textbf{A}\) with those of input \(\textbf{B}\). For instance,

```
A <- matrix(1:16, ncol=4)
B <- matrix(10*(1:16), ncol=4)
dimnames(A) <- list(paste("week",1:4), month.abb[1:4])
dimnames(B) <- list(c("chicken","beef","pork","fish"), LETTERS[1:4])
Hadamard(A, B, make.dimnames=TRUE)
```

```
## Jan:A Feb:B Mar:C Apr:D
## week 1:chicken 10 250 810 1690
## week 2:beef 40 360 1000 1960
## week 3:pork 90 490 1210 2250
## week 4:fish 160 640 1440 2560
```

`Hadamard(A, B, make.dimnames=TRUE, colsA=c(1,1,1,1), rowsB=c(2,3,2,3))`

```
## Jan:A Jan:B Jan:C Jan:D
## week 1:beef 20 60 100 140
## week 2:pork 60 140 220 300
## week 3:beef 60 180 300 420
## week 4:pork 120 280 440 600
```