contract() and
contract_elementary() in the stokes
packagevignettes/contract.Rmd
contract.Rmd
contractfunction (K, v, lose = TRUE)
{
if (is.vector(v)) {
out <- Reduce("+", Map("*", apply(index(K), 1, contract_elementary,
v), elements(coeffs(K))))
}
else {
stopifnot(is.matrix(v))
out <- K
for (i in seq_len(ncol(v))) {
out <- contract(out, v[, i, drop = TRUE], lose = FALSE)
}
}
if (lose) {
out <- lose(out)
}
return(disordR::drop(out))
}
contract_elementaryfunction (o, v)
{
out <- zeroform(length(o) - 1)
for (i in seq_along(o)) {
out <- out + (-1)^(i + 1) * v[o[i]] * as.kform(rbind(o[-i]),
lose = FALSE)
}
return(out)
}
To cite the stokes package in publications, please use
Hankin (2022); this function monograph
discusses function contract(). Given a
-form
and a
vector
,
the contraction
of
and
is a
-form
with
provided
;
if
we specify
.
If Spivak (1965) does discuss this, I have
forgotten it. Function contract_elementary() is a low-level
helper function that translates elementary
-forms
with coefficient 1 (in the form of an integer vector corresponding to
one row of an index matrix) into its contraction with
;
function contract() is the user-friendly front end. We will
start with some simple examples. I will use phi and
to represent the same object.
(phi <- as.kform(1:5))## An alternating linear map from V^5 to R with V=R^5:
## val
## 1 2 3 4 5 = 1
Thus and we have . We have that is a linear alternating map with
.
The contraction of with any vector is thus a -form mapping to the reals with . Taking the simplest case first, if then
## An alternating linear map from V^4 to R with V=R^5:
## val
## 2 3 4 5 = 1
that is, a linear alternating map from to the reals with
.
(the contraction has the first argument of understood to be ). Now consider :
## An alternating linear map from V^4 to R with V=R^5:
## val
## 1 3 4 5 = -1
.
Contraction is linear, so we may use more complicated vectors:
## An alternating linear map from V^4 to R with V=R^5:
## val
## 2 3 4 5 = 1
## 1 3 4 5 = -3
contract(phi,1:5)## An alternating linear map from V^4 to R with V=R^5:
## val
## 1 2 3 4 = 5
## 1 2 4 5 = 3
## 2 3 4 5 = 1
## 1 3 4 5 = -2
## 1 2 3 5 = -4
We can check numerically that the contraction is linear in its vector argument: .
a <- 1.23
b <- -0.435
v <- 1:5
w <- c(-3, 2.2, 1.1, 2.1, 1.8)
contract(phi,a*v + b*w) == a*contract(phi,v) + b*contract(phi,w)## [1] TRUE
We also have linearity in the alternating form: .
(phi <- rform(2,5))## An alternating linear map from V^5 to R with V=R^7:
## val
## 2 3 4 5 7 = -2
## 1 3 4 6 7 = 1
(psi <- rform(2,5))## An alternating linear map from V^5 to R with V=R^7:
## val
## 1 2 3 6 7 = 2
## 2 3 5 6 7 = 1
## [1] TRUE
Weintraub (2014) gives us the following theorem, for any -form and -form :
We can verify this numerically with :
phi <- rform(terms=5,k=3,n=9)
psi <- rform(terms=9,k=4,n=9)
v <- sample(1:100,9)
contract(phi^psi,v) == contract(phi,v) ^ psi - phi ^ contract(psi,v)## [1] TRUE
The theorem is verified. We note in passing that the object itself is quite complicated:
## A kform object with 47 terms. Summary of coefficients:
##
## a disord object with hash d6cdb7213e60a9d847f1752a839f18b1de98bc57
##
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## -2943.00 -516.00 48.00 44.47 768.00 2625.00
##
##
## Representative selection of index and coefficients:
##
## An alternating linear map from V^6 to R with V=R^9:
## val
## 1 2 4 6 8 9 = 390
## 1 2 3 5 6 7 = 420
## 1 2 3 4 6 8 = -840
## 2 5 6 7 8 9 = 1605
## 1 2 3 6 7 9 = 355
## 1 2 3 6 8 9 = -1200
We may also switch and , remembering to change the sign:
## [1] TRUE
It is of course possible to contract a contraction. If is a -form, then is a form with
And this is straightforward to realise in the package:
(phi <- rform(2,5))## An alternating linear map from V^5 to R with V=R^7:
## val
## 1 4 5 6 7 = -2
## 2 4 5 6 7 = -1
## An alternating linear map from V^3 to R with V=R^7:
## val
## 2 5 6 = 10
## 1 4 7 = 2
## 4 5 7 = 73
## 1 4 5 = 20
## 2 4 7 = 1
## 1 5 6 = 20
## 2 4 6 = -14
## 1 6 7 = -2
## 2 6 7 = -1
## 2 4 5 = 10
## 5 6 7 = 73
## 4 6 7 = -90
## 1 4 6 = -28
## 4 5 6 = -122
But contract() allows us to perform both contractions in
one operation: if we pass a matrix
to contract() then this is interpreted as repeated
contraction with the columns of
:
## [1] TRUE
We can verify directly that the system works as intended. The lines
below strip successively more columns from argument V and
contract with them:
## An alternating linear map from V^4 to R with V=R^9:
## val
## 3 7 8 9 = -0.1482116
## 1 5 6 7 = 0.4314737
V <- matrix(rnorm(36),ncol=4)
jj <- c(
as.function(o)(V),
as.function(contract(o,V[,1,drop=TRUE]))(V[,-1]), # scalar
as.function(contract(o,V[,1:2]))(V[,-(1:2),drop=FALSE]),
as.function(contract(o,V[,1:3]))(V[,-(1:3),drop=FALSE]),
as.function(contract(o,V[,1:4],lose=FALSE))(V[,-(1:4),drop=FALSE])
)
print(jj)## [1] -0.4992204 -0.4992204 -0.4992204 -0.4992204 -0.4992204
## [1] 2.775558e-16
and above we see agreement to within numerical precision. If we pass
three columns to contract() the result is a
-form:
contract(o,V)## [1] -0.4992204
In the above, the result is coerced to a scalar which is returned in
the form of a disord object; in order to work with a formal
-form
(which is represented in the package as a spray with a
zero-column index matrix) we can use the lost=FALSE
argument:
contract(o,V,lose=FALSE)## An alternating linear map from V^0 to R with V=R^0:
## val
## = -0.4992204
thus returning a -form. If we iteratively contract a -dimensional -form, we return the determinant, and this may be verified as follows:
o <- as.kform(1:5)
V <- matrix(rnorm(25),5,5)
LHS <- det(V)
RHS <- contract(o,V)
c(LHS=LHS,RHS=RHS,diff=LHS-RHS)## LHS RHS diff
## 6.355108e+00 6.355108e+00 1.776357e-15
Above we see agreement to within numerical error.
Suppose we wish to contract with vector . Thus we seek with . Writing , we have
where we have exploited linearity. To evaluate this it is easiest and most efficient to express as and cycle through the index , and use various properties of wedge products:
(above, a hat indicates a termโs being omitted). From this, we see that (where is the index set of ), for all the terms kill . On the other hand, if we have
contract_elementary()
Function contract_elementary() is a bare-bones low-level
no-frills helper function that returns
for
an elementary form of the form
.
Suppose we wish to contract
with vector
.
Thus we seek with . Writing we have
(above, the zero terms are because the vectors and are killed by ). We can see that the way to evaluate the contraction is to go through the terms of [that is, , , and ] in turn, and sum the resulting expressions:
o <- c(1,2,5)
v <- c(1,2,10,11,71)
(
(-1)^(1+1) * as.kform(o[-1])*v[o[1]] +
(-1)^(2+1) * as.kform(o[-2])*v[o[2]] +
(-1)^(3+1) * as.kform(o[-3])*v[o[3]]
)## An alternating linear map from V^2 to R with V=R^5:
## val
## 1 5 = -2
## 2 5 = 1
## 1 2 = 71
This is performed more succinctly by
contract_elementary():
contract_elementary(o,v)## An alternating linear map from V^2 to R with V=R^5:
## val
## 1 5 = -2
## 2 5 = 1
## 1 2 = 71
contract()
Given a vector v, and kform object
K, the meat of contract() would be
Reduce("+", Map("*", apply(index(K), 1, contract_elementary, v), elements(coeffs(K))))
I will show this in operation with simple but nontrivial arguments.
## An alternating linear map from V^4 to R with V=R^7:
## val
## 2 4 5 7 = 2
## 1 2 3 6 = 1
v <- 1:7Then the inside bit would be
apply(index(K), 1, contract_elementary, v)## [[1]]
## An alternating linear map from V^3 to R with V=R^7:
## val
## 2 4 7 = 5
## 4 5 7 = 2
## 2 5 7 = -4
## 2 4 5 = -7
##
## [[2]]
## An alternating linear map from V^3 to R with V=R^6:
## val
## 1 2 6 = 3
## 2 3 6 = 1
## 1 3 6 = -2
## 1 2 3 = -6
Above we see a two-element list, one for each elementary term of
K. We then use base Rโs Map() function to
multiply each one by the appropriate coefficient:
## [[1]]
## An alternating linear map from V^3 to R with V=R^7:
## val
## 2 4 5 = -14
## 2 5 7 = -8
## 4 5 7 = 4
## 2 4 7 = 10
##
## [[2]]
## An alternating linear map from V^3 to R with V=R^6:
## val
## 1 2 3 = -6
## 1 3 6 = -2
## 2 3 6 = 1
## 1 2 6 = 3
And finally use Reduce() to sum the terms:
## An alternating linear map from V^3 to R with V=R^7:
## val
## 2 4 7 = 10
## 4 5 7 = 4
## 2 5 7 = -8
## 1 2 3 = -6
## 2 4 5 = -14
## 1 3 6 = -2
## 2 3 6 = 1
## 1 2 6 = 3
However, it might be conceptually easier to use magrittr
pipes to give an equivalent definition:
K %>%
index %>%
apply(1,contract_elementary,v) %>%
Map("*", ., K %>% coeffs %>% elements) %>%
Reduce("+",.)## An alternating linear map from V^3 to R with V=R^7:
## val
## 2 4 7 = 10
## 4 5 7 = 4
## 2 5 7 = -8
## 1 2 3 = -6
## 2 4 5 = -14
## 1 3 6 = -2
## 2 3 6 = 1
## 1 2 6 = 3
Well it might be clearer to Hadley but frankly YMMV.