tensorprod
function (U, ...) 
{
    if (nargs() < 3) {
        tensorprod2(U, ...)
    }
    else {
        tensorprod2(U, Recall(...))
    }
}
tensorprod2
function (U1, U2) 
{
    stopifnot(is.ktensor(U1) & is.ktensor(U2))
    if (is.empty(U1) | is.empty(U2)) {
        return(as.ktensor(cbind(index(U1)[0, ], index(U2)[0, 
            ])))
    }
    return(ktensor(spraycross(U1, U2)))
}

To cite the stokes package in publications, please use Hankin (2022b); this function monograph discusses tensorprod(). Function tensorprod() returns the tensor cross product of any number of ktensor objects; tensorprod2() is a lower-level helper function that returns the product of two such objects. These functions use spraycross() from the spray package (Hankin 2022a).

The tensor cross product

In a memorable passage, Spivak (1965) states:

Integration on chains

If VV is a vector space over \mathbb{R}, we denote the kk-fold product V××VV\times\cdots\times V by VkV^k. A function T:VkT\colon V^k\longrightarrow\mathbb{R} is called multilinear if for each ii with 1ik1\leqslant i\leqslant k we have

$$ T\left(v_1,\ldots, v_i + {v'}_i,\ldots, v_k\right)= T\left(v_1,\ldots,v_i,\ldots,v_k\right)+ T\left(v_1,\ldots,{v'}_i,\ldots,v_k\right),\\ T\left(v_1,\ldots,av_i,\ldots,v_k\right)=aT\left(v_1,\ldots,v_i,\ldots,v_k\right). $$

A multilinear function T:VkT\colon V^k\longrightarrow\mathbb{R} is called a kk-tensor on VV and the set of all kk-tensors, denoted by 𝒥k(V)\mathcal{J}^k(V), becomes a vector space (over \mathbb{R}) if for S,T𝒥k(V)S,T\in\mathcal{J}^k(V) and aa\in\mathbb{R} we define

$$ (S+T)(v_1,\ldots,v_k) = S(v_1,\ldots,v_k) + T(v_1,\ldots,v_k)\\ (aS)(v_1,\ldots,v_k) = a\cdot S(v_1,\ldots,v_k). $$

There is also an operation connecting the various spaces 𝒥(V)\mathcal{J}(V). If S𝒥k(V)S\in\mathcal{J}^k(V) and T𝒥l(V)T\in\mathcal{J}^l(V), we define the tensor product ST𝒥k+l(V)S\otimes T\in\mathcal{J}^{k+l}(V) by

ST(v1,,vk,vk+1,,vk+l)=S(v1,,vk)T(vk+1,,vk+l). S\otimes T(v_1,\ldots,v_k,v_{k+1},\ldots,v_{k+l})= S(v_1,\ldots,v_k)\cdot T(v_{k+1},\ldots,v_{k+l}).

- Michael Spivak, 1969 (Calculus on Manifolds, Perseus books). Page 75

Spivak goes on to observe that the tensor product is distributive and associative but not commutative. He then proves that the set of all kk-fold tensor products

ϕi1ϕik,1i1,,ikn \phi_{i_1}\otimes\cdots\otimes\phi_{i_k},\qquad 1\leqslant i_1,\ldots,i_k\leqslant n

[where ϕi(vj)=δij\phi_i(v_j)=\delta_{ij},v1,,vkv_1,\ldots,v_k being a basis for VV] is a basis for 𝒥k(V)\mathcal{J}^k(V), which therefore has dimension nkn^k. Function tensorprod() evaluates the tensor product and I give examples here.

(a <- ktensor(spray(matrix(c(1,1,2,1),2,2),3:4)))
## A linear map from V^2 to R with V=R^2:
##          val
##  1 1  =    4
##  1 2  =    3
(b <- ktensor(spray(matrix(c(3,4,7,5,4,3),3,2),7:9)))
## A linear map from V^2 to R with V=R^7:
##          val
##  4 4  =    8
##  7 3  =    9
##  3 5  =    7

Thus a=4ϕ1ϕ1+3ϕ1ϕ2a=4\phi_1\otimes\phi_1+3\phi_1\otimes\phi_2 and b=7ϕ3ϕ5+8ϕ4ϕ4+9ϕ7ϕ3b=7\phi_3\otimes\phi_5+8\phi_4\otimes\phi_4+9\phi_7\otimes\phi_3. Now the cross product aba\otimes b is given by tensorprod():

## A linear map from V^4 to R with V=R^7:
##              val
##  1 2 3 5  =   21
##  1 2 7 3  =   27
##  1 1 7 3  =   36
##  1 1 3 5  =   28
##  1 2 4 4  =   24
##  1 1 4 4  =   32

We can see that the product includes the term 21ϕ1ϕ2ϕ3ϕ521\phi_1\otimes\phi_2\otimes\phi_3\otimes\phi_5 and five others.

Verification

Spivak proves that the tensor product is associative and distributive, which are demonstrated here.

S <- rtensor()
T <- rtensor()
U <- rtensor()
c( left_distributive = S %X% (T+U) == S*T + S*U,
  right_distributive = (S+T) %X% U == S %X% U + T %X% U,
  associative        = S %X% (T %X% U) == (S %X% T) %X% U
  )
##  left_distributive right_distributive        associative 
##               TRUE               TRUE               TRUE

Note on associativity

It is interesting to note that, while the tensor product is associative, disord discipline obscures this fact. Consider the following:

x <- ktensor(spray(matrix(c(1,1,2,1),2,2),1:2))
y <- ktensor(spray(matrix(c(3,4,7,5,4,3),3,2),1:3))
z <- ktensor(spray(matrix(c(1,1,2,1),2,2),1:2))
tensorprod(x, tensorprod(y, z))
## A linear map from V^6 to R with V=R^7:
##                  val
##  1 1 4 4 1 1  =    8
##  1 2 3 5 1 1  =    2
##  1 1 7 3 1 1  =   12
##  1 1 3 5 1 1  =    4
##  1 2 4 4 1 2  =    2
##  1 1 4 4 1 2  =    4
##  1 2 7 3 1 2  =    3
##  1 2 7 3 1 1  =    6
##  1 1 7 3 1 2  =    6
##  1 2 4 4 1 1  =    4
##  1 2 3 5 1 2  =    1
##  1 1 3 5 1 2  =    2
## A linear map from V^6 to R with V=R^7:
##                  val
##  1 1 4 4 1 2  =    4
##  1 2 4 4 1 2  =    2
##  1 1 3 5 1 2  =    2
##  1 2 7 3 1 2  =    3
##  1 1 4 4 1 1  =    8
##  1 2 3 5 1 2  =    1
##  1 2 4 4 1 1  =    4
##  1 1 3 5 1 1  =    4
##  1 1 7 3 1 1  =   12
##  1 1 7 3 1 2  =    6
##  1 2 7 3 1 1  =    6
##  1 2 3 5 1 1  =    2

The two products are algebraically identical but the terms appear in a different order.

References

Hankin, R. K. S. 2022a. “Sparse Arrays in R: The spray Package.” https://arxiv.org/abs/2210.03856; arXiv. https://doi.org/10.48550/ARXIV.2210.10848.
———. 2022b. “Stokes’s Theorem in R.” arXiv. https://doi.org/10.48550/ARXIV.2210.17008.
Spivak, M. 1965. Calculus on Manifolds. Addison-Wesley.