Function phi() function in the stokes package (original) (raw)
function (n)
{
ktensor(spray(n, 1))
}To cite the stokes package in publications, please useHankin (2022b). Functionphi() returns a tensor dual to the standard basis of \(V=\mathbb{R}^n\). Here I discussphi() but there is some overlap between this vignette and the tensorprod vignette.
In a memorable passage, Spivak (1965) states (theorem 4.1):
Integration on chains
Let \(v_1,\ldots,v_n\) be a basis for \(V\), and let \(\phi_1,\ldots,\phi_n\) be the dual basis,\(\phi_i(v_j)=\delta_{ij}\). Then the set of all \(k\)-fold tensor products
\[ \phi_{i_1}\otimes\cdots\otimes\phi_{i_k}\qquad 1\leqslant i_1,\ldots,i_k\leqslant n \]
is a basis for \(\mathcal{J}(V)\), which therefore has dimension \(n^k\).
- Michael Spivak, 1969 (Calculus on Manifolds, Perseus books). Page 75
Function phi() returns a very simple tensor:
## A linear map from V^1 to R with V=R^4:
## val
## 4 = 1First we will verify the properties of phi(), using\(V=\mathbb{R}^5\), specifically
\[ \phi_i(e_j) = \delta_{ij} = \begin{cases} 1, & i=j\\ 0 & i\neq j. \end{cases} \]
(package idiom is to use e() for basis vectors as opposed to Spivak’s \(v\)). As numerical verification, we will check that \(\phi_3(e_2)=0\), \(\phi_3(e_3)=1\), \(\phi_3(e_4)=0\):
f <- as.function(phi(3))
c(f(as.matrix(e(2,5))), f(as.matrix(e(3,5))), f(as.matrix(e(4,5))))## [1] 0 1 0A more severe test might be
aa <- function(n){
outer(seq_len(n), seq_len(n),
Vectorize(function(i, j){as.function(phi(i))(as.matrix(e(j, n)))}))
}
aa(5)## [,1] [,2] [,3] [,4] [,5]
## [1,] 1 0 0 0 0
## [2,] 0 1 0 0 0
## [3,] 0 0 1 0 0
## [4,] 0 0 0 1 0
## [5,] 0 0 0 0 1Above, we see that the matrix is \(I_5\), as expected. Further:
## [1] TRUEThe objects created by phi() may be multiplied together using tensorprod() or its binary operator%X%:
phi(4) %X% phi(3) %X% phi(5)## A linear map from V^3 to R with V=R^5:
## val
## 4 3 5 = 1If we want to create arbitrary tensor products of \(\phi\) objects the most natural way would be to use tensorprod() repeatedly:
Reduce(`%X%`,sapply(4:8,phi))## A linear map from V^5 to R with V=R^8:
## val
## 4 5 6 7 8 = 1However, function phi() simply takes a vector:
## A linear map from V^3 to R with V=R^5:
## val
## 2 5 1 = 1This creates an element of the basis set, in this case \(\phi_2\otimes\phi_5\otimes\phi_1\). Verification is straightforward:
## [1] 9 4 7 1 2 6 3 8 5phi(v) == Reduce(`%X%`,sapply(v,phi))## [1] TRUEWe will consider an element \(X\) of\(\mathcal{J}^{2}(V)\) where \(V=\mathbb{R}^3\) and construct an explicit basis for it along the lines of Spivak’s observation above.
(X <- ktensor(spray(matrix(c(1,2,3,2,1,1),3,2),1:3)))## A linear map from V^2 to R with V=R^3:
## val
## 3 1 = 3
## 2 1 = 2
## 1 2 = 1Thus \(X=\phi_1\otimes\phi_2 +2\phi_2\otimes\phi_1 +3\phi_3\otimes\phi_1\). Spivak asserts that \(\mathcal{J}^{2}(V)\) has dimension \(n^k=3^2=9\).
1*phi(c(1,2)) + 2*phi(c(2,1)) + 3*phi(c(3,1))## A linear map from V^2 to R with V=R^3:
## val
## 2 1 = 2
## 1 2 = 1
## 3 1 = 3With a little effort, we can create all \(3^2=9\) elements of a basis as follows:
apply(expand.grid(rep(list(seq_len(3)),2)),1,phi)## [[1]]
## A linear map from V^2 to R with V=R^1:
## val
## 1 1 = 1
##
## [[2]]
## A linear map from V^2 to R with V=R^2:
## val
## 2 1 = 1
##
## [[3]]
## A linear map from V^2 to R with V=R^3:
## val
## 3 1 = 1
##
## [[4]]
## A linear map from V^2 to R with V=R^2:
## val
## 1 2 = 1
##
## [[5]]
## A linear map from V^2 to R with V=R^2:
## val
## 2 2 = 1
##
## [[6]]
## A linear map from V^2 to R with V=R^3:
## val
## 3 2 = 1
##
## [[7]]
## A linear map from V^2 to R with V=R^3:
## val
## 1 3 = 1
##
## [[8]]
## A linear map from V^2 to R with V=R^3:
## val
## 2 3 = 1
##
## [[9]]
## A linear map from V^2 to R with V=R^3:
## val
## 3 3 = 1Or it might be logically better to use ellipsis constructs to pass multiple arguments:
s <- function(...){phi(unlist(list(...)))}
s(3,4,6)## A linear map from V^3 to R with V=R^6:
## val
## 3 4 6 = 1Then we could have
1*s(1,2) + 2*s(2,1) + 3*s(3,1)## A linear map from V^2 to R with V=R^3:
## val
## 2 1 = 2
## 1 2 = 1
## 3 1 = 31*s(1,2) + 2*s(2,1) + 3*s(3,1) == X## [1] TRUEDistributivity
The tensor product is left- and right distributive. To illustrate this we can use the package to calculate, say, \((2\phi_1+3\phi_2)\otimes(5\phi_3+7\phi_4)\):
(2*phi(1) + 3*phi(2)) %X% (5*phi(3) + 7*phi(4) )## A linear map from V^2 to R with V=R^4:
## val
## 2 4 = 21
## 1 4 = 14
## 2 3 = 15
## 1 3 = 10Above we see package form for the result which is \(10\phi_1\phi_3 + 14\phi_1\phi_4 + 15\phi_2\phi_3 + 21\phi_2\phi_4\) in algebraic notation.
Reconstruction of a given tensor
Consider the following tensor
(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 = 7We may express \(b\) as the sum of its three terms, each with a coefficient:
7*phi(c(3,5)) + 8*phi(c(4,4)) + 9*phi(c(7,3))## A linear map from V^2 to R with V=R^7:
## val
## 4 4 = 8
## 3 5 = 7
## 7 3 = 9Above, observe that the order of the terms may differ between the two methods, as per disordR discipline (Hankin 2022a), but they are algebraically identical:
b == 7*phi(c(3,5)) + 8*phi(c(4,4)) + 9*phi(c(7,3))## [1] TRUEFunction Alt()
Function Alt() returns an alternating tensor as documented in the Alt vignette in the package. It works nicely with phi():
## A linear map from V^3 to R with V=R^3:
## val
## 1 2 3 = 1## A linear map from V^3 to R with V=R^3:
## val
## 3 2 1 = -1
## 3 1 2 = 1
## 2 3 1 = 1
## 2 1 3 = -1
## 1 3 2 = -1
## 1 2 3 = 1