# Derivative of matrix product
A <- matrix(rnorm(3 * 3), 3, 3)
A <- A + t(A)
B <- matrix(rnorm(3 * 4), 3, 4)

# F(A) = A B
# DF(A) = B' %x% I
exprs.all.equal({
    t(B) %x% diag(nrow(A))
    num.deriv(function(X) X %*% B, A)
    num.deriv(A %*% B, A)
    num.deriv(X %*% B, X = A)
})

# Symmetric case, constraint A = A' (equiv to being a function of vech(A) only)
# F(A) = A B     for A = A'
# DF(A) = B' %x% I
stopifnot(all.equal(
    num.deriv(function(X) X %*% B, A, sym = TRUE),
    (t(B) %x% diag(nrow(A))) %*% D(nrow(A)) %*% t(D(nrow(A)))
))

# Derivative of Kronecker Product
A <- matrix(rnorm(3 * 7), 3)
B <- matrix(rnorm(5 * 4), 5)

P <- diag(ncol(A)) %x% K(ncol(B), nrow(A)) %x% diag(nrow(B))
# DF(A) numeric approximation and exact solution
stopifnot(all.equal(
    num.deriv(function(X) X %x% B, A),
    P %*% (diag(prod(dim(A))) %x% c(B))
))
# DF(B) numeric approximation and exact solution
stopifnot(all.equal(
    num.deriv(function(X) A %x% X, B),
    P %*% (c(A) %x% diag(prod(dim(B))))
))
