To cite this work or the weyl
package in publications
please use Hankin (2022b). In this short
document I show how Weyl algebras are implemented in the
weyl
package. Consider the vector space
of linear operators on univariate functions;
can be made into an algebra where multiplication (denoted by
juxtaposition) is defined as operator composition (Coutinho 1997).
That is, given operators
and scalar
we define the following compounds:
where is any univariate function. Here we consider the algebra generated by the set where [that is, ] and [that is, ]. This is known as the (first) Weyl algebra. We observe that the Weyl algebra is not commutative: but , so . The algebra generated by will include elements such as , which would map to . It can be shown that any element of the Weyl algebra can be expressed in the standard form
for real and nonnegative integers . Converting a general word to standard form is not straightforward but we have
and
We can apply these rules recursively to find standard form for products . Alternatively we may follow Wolf (1975) and use the fact that
These rules can be used to show that can be expressed as , which is in standard form.
The package in use
The package includes functionality to automate the above
calculations. In particular, package idiom represents the generating
elements
and
of the first Weyl algebra as R objects d
and x
respectively. These may be manipulated with standard arithmetic
operations:
7*d + 4*x*d^3*x
## A member of the Weyl algebra:
## x d val
## 0 1 = 7
## 1 2 = 12
## 2 3 = 4
Above, the result of the input is given in standard form. We see the terms, one per row, with coefficients in the rightmost column (viz ). Thus the first row is , the second is , and the third is . We may choose to display the result in symbolic form rather than matrix form:
options(polyform=TRUE)
7*d + 4*x*d^3*x
## A member of the Weyl algebra:
## +7*d +12*x*d^2 +4*x^2*d^3
which is arguably a more natural representation. The package allows one to use R semantics. For example, consider and . Observing that and are in standard form, package idiom to create these operators would be:
(d1 <- d*x + 2*d^3)
## A member of the Weyl algebra:
## 1 +x*d +2*d^3
(d2 <- 3 + 7*d -5*x^2*d^2)
## A member of the Weyl algebra:
## 3 +7*d -5*x^2*d^2
(object d1
is converted to standard form automatically).
Observe that, like the spray
package, the order of the
terms is not defined. We may apply the usual rules of arithmetic to
these objects:
d1*d2
## A member of the Weyl algebra:
## 3 +7*x*d^2 +3*x*d -15*x^2*d^2 -60*x*d^4 -5*x^3*d^3 +7*d +14*d^4 -54*d^3
## -10*x^2*d^5
Standard R semantics operate, and it is possible to work with more complicated expressions:
options(polyform=TRUE)
(d1^2 + d2) * (d2 - 3*d1)
## A member of the Weyl algebra:
## -276*x*d^7 +28*d^7 +5*x^2*d^2 -732*d^6 -636*x*d^4 +28*d^4 -414*d^3
## -63*x^2*d^3 +7*d -20*x^3*d^6 -24*d^9 -70*x*d^2 -20*x^2*d^8 +77*x^3*d^3
## +20*x^4*d^4 -21*x*d +49*d^2 -198*x^2*d^5 +28*x*d^5
Comparison with mathematica
Mathematica can deal with operators and we may compare the two systemsβ results for :
In[1] := D[D[x*D[x^2*f[x],x],x],x] // Expand
Out[1] := 4 f[x] + 14 x f'[x] + 8 x^2 f''[x] + x^3f'''[x]
## A member of the Weyl algebra:
## 4 +x^3*d^3 +8*x^2*d^2 +14*x*d
Above, we see agreement between weyl
and Mathematica,
although the terms are presented in a different order.
Further Weyl algebras
The package supports arbitrary multivariate Weyl algebras. Consider:
## A member of the Weyl algebra:
## x y z dx dy dz val
## 2 0 1 2 0 1 = 3
## 0 1 2 2 0 1 = 2
## 1 0 2 1 0 1 = 1
Above, object x
is a member of the operator algebra
generated by
.
Object x
might be expressed as
although as ever the rows are presented in an implementation-dependent
order. We may verify associativity of multiplication:
## A member of the Weyl algebra:
## +6*x*y^2*dx^2*dy +36*x*y^3*dx*dy^3 +2*x^2*y^4*dx*dy^4
## +4*x^2*y^2*dx*dy^5 +36*x*y^2*dx*dy^2 +x*y^4*dx*dy^3 +4*x*y^3*dx*dy^2
## +x^2*y^4*dx^2*dy^3 +12*x^2*y^2*dx*dy^2 +2*x^2*y^2*dx^2*dy^4
## +2*x*y^2*dx*dy^4 +2*x*y^2*dx*dy +2*x^2*y^2*dx^2*dy +4*x^2*y^3*dx^2*dy^2
## +3*x*y^4*dx^2*dy^3 +12*x^2*y^3*dx*dy^3 +6*x*y^4*dx*dy^4
## +12*x*y^3*dx^2*dy^2
(x*y)*z
## A member of the Weyl algebra:
## +2*x*y^2*dx*dy^4 +2*x^2*y^2*dx^2*dy^4 +3*x*y^4*dx^2*dy^3
## +x^2*y^4*dx^2*dy^3 +12*x^2*y^2*dx*dy^2 +x*y^4*dx*dy^3
## +2*x^2*y^4*dx*dy^4 +4*x^2*y^3*dx^2*dy^2 +2*x^2*y^2*dx^2*dy
## +2*x*y^2*dx*dy +4*x*y^3*dx*dy^2 +12*x^2*y^3*dx*dy^3 +6*x*y^4*dx*dy^4
## +12*x*y^3*dx^2*dy^2 +6*x*y^2*dx^2*dy +36*x*y^3*dx*dy^3
## +36*x*y^2*dx*dy^2 +4*x^2*y^2*dx*dy^5
Comparing the two results above, we see that they apparently differ. But the apparent difference is due to the fact that the terms appear in a different order, a feature that is not algebraically meaningful. We may verify that the expressions are indeed algebraically identical:
x*(y*z) - (x*y)*z
## A member of the Weyl algebra:
## the NULL multinomial of arity 4
The package can deal with arbitrarily high dimensional Weyl algebras. For example:
## A member of the Weyl algebra:
## 1 2 3 4 5 6 7 8 9 d1 d2 d3 d4 d5 d6 d7 d8 d9 val
## 1 2 2 1 0 1 2 2 2 0 2 0 0 0 0 2 1 1 = 3
## 2 0 1 1 1 1 0 1 1 0 0 2 1 0 1 2 1 2 = 2
## 0 0 1 2 1 1 1 2 2 2 0 1 0 0 2 0 2 1 = 1
Above we see a member of the ninth Weyl algebra; see how the column
headings no longer use the x y z
notation and revert to
numeric labels. Symbolic notation is available but can be difficult to
read:
options(polyform=TRUE)
x9
## A member of the Weyl algebra:
## +3*x1*x2^2*x3^2*x4*x6*x7^2*x8^2*x9^2*d2^2*d7^2*d8*d9
## +2*x1^2*x3*x4*x5*x6*x8*x9*d3^2*d4*d6*d7^2*d8*d9^2
## +x3*x4^2*x5*x6*x7*x8^2*x9^2*d1^2*d3*d6^2*d8^2*d9
options(polyform=FALSE) # revert to default
Derivations
A derivation of an algebra is a linear operator that satisfies , for every . If a derivation is of the form for some fixed , we say that is an inner derivation:
Dirac showed that all derivations are inner derivations for some . The package supports derivations:
Then
## [1] TRUE
Low-level considerations and generalizations
In the package, the product is customisable. In general, product
a*b
[where a
and b
are
weyl
objects] is dispatched to the following sequence of
functions:
weyl_prod_multivariate_nrow_allcolumns()
weyl_prod_multivariate_onerow_allcolumns()
weyl_prod_multivariate_onerow_singlecolumn()
weyl_prod_univariate_onerow()
-
weyl_prod_helper3()
[default]
In the above, βunivariateβ means βgenerated by
β [so the
corresponding spray
object has two columns]; and
βmultivariateβ means that the algebra is generated by more than one
variable, typically something like
.
The penultimate function weyl_prod_univariate_onerow()
is sensitive to option prodfunc
which specifies the
recurrence relation used. This defaults to
weyl_prod_helper3()
:
weyl_prod_helper3
## function (a, b, c, d)
## {
## f <- function(r) {
## factorial(r) * choose(b, r) * choose(c, r)
## }
## ind <- numeric(0)
## val <- numeric(0)
## for (r in 0:b) {
## ind <- rbind(ind, c(a + c - r, b + d - r))
## val <- c(val, f(r))
## }
## spray(ind, val, addrepeats = TRUE)
## }
## <bytecode: 0x55a6d1a5e0f8>
## <environment: namespace:weyl>
Function weyl_prod_helper3()
follows Wolf. This gives
the univariate concatenation product
in terms of standard generators:
The package also includes lower-level function
weyl_prod_helper1()
implementing
(together with suitable bottoming-out). I expected function
weyl_prod_helper3()
to be much faster than
weyl_prod_helper1()
but there doesnβt seem to be much
difference between the two.
Generalized commutator relations
We can exploit this package customisability by considering, instead of , the algebra generated by , where maps to : if maps to , then maps to . We see that . With this, we can prove that and and, thus
We may implement this set in package idiom as follows:
`weyl_e_prod` <- function(a,b,c,d){
if(c==0){return(spray(cbind(a,b+d)))}
if(b==0){return(spray(cbind(a+c,d)))}
return(
Recall(a,b-1,c,d+1) +
c*Recall(a,b-1,c,d) # cf: c*Recall(a,b-1,c-1,d)) for regular Weyl algebra
)
}
Then, for example, to calculate :
options(prodfunc = weyl_e_prod)
options(weylvars = "e") # changes print method
d <- weyl(spray(cbind(0,1)))
e <- weyl(spray(cbind(1,0)))
d*d*e
## A member of the Weyl algebra:
## e d val
## 1 0 = 1
## 1 1 = 2
## 1 2 = 1
d^2*e
## A member of the Weyl algebra:
## e d val
## 1 0 = 1
## 1 1 = 2
## 1 2 = 1
By way of verification:
d^5*e == e*(1+d)^5
## [1] TRUE
which verifies that indeed . Another verification would be to cross-check with Mathematica, here working with :
In[1] := D[Exp[x]*D[D[Exp[x]*f[x],x],x],x]
Out[1] := 2E^2x f[x] + 5E^2x f'[x] + 4E^2xf''[x] + E^2x f'''[x]
options(polyform = TRUE)
d*e*d^2*e
## A member of the Weyl algebra:
## +2*e^2 +5*e^2*d +4*e^2*d^2 +e^2*d^3
We can manipulate more complicated expressions too. Suppose we want to evaluate :
o1 <- weyl(spray(cbind(2,1)))
o2 <- weyl(spray(cbind(3,3)))
options(polyform = FALSE)
(1+o1)*(1-5*o2)
## A member of the Weyl algebra:
## e d val
## 5 3 = -15
## 3 3 = -5
## 5 4 = -5
## 2 1 = 1
## 0 0 = 1
And of course we can display the result in symbolic form:
options(polyform = TRUE)
(1+o1)*(1-5*o2)
## A member of the Weyl algebra:
## 1 -15*e^5*d^3 -5*e^3*d^3 -5*e^5*d^4 +e^2*d
Note on use of disordR
package
The coefficients of a weyl
object, and the rows of its
spray
matrix, are stored in an implementation-specific
order. Thus, extraction and replacement use the disordR
package (Hankin 2022a). A short
example follows in the context of the weyl
package; a much
more extensive and detailed discussion is given in the
disordR
vignette and in Hankin (2022a). First we create
a moderately complicated weyl
object:
options(weylvars = NULL) # revert to default names
(W <- weyl(spray(matrix(c(0,1,1,1,1,2,1,0),2,4),2:3))^2)
## A member of the Weyl algebra:
## x y dx dy val
## 2 2 4 0 = 9
## 2 2 3 0 = 18
## 0 2 2 2 = 4
## 2 2 2 0 = 9
## 1 2 3 1 = 12
## 1 2 2 0 = 6
## 1 2 3 0 = 6
## 1 2 2 1 = 6
## 0 2 2 1 = 4
The coefficients of W
may be extracted:
coeffs(W)
## A disord object with hash ef3b76da15a19ac6dd3ba83e2ec6b436a0f975f6 and elements
## [1] 9 18 4 9 12 6 6 6 4
## (in some order)
The object returned is a disord
object. There is no way
to extract (e.g.) the first coefficient, for the order of the matrix
rows is not defined. If we try we will get an error:
## Error in .local(x, i, j = j, ..., drop) :
## if using a regular index to extract, must extract each element once and once only (or none of them)
However, it is perfectly well defined to ask βgive all coefficients greater than 6β:
o <- coeffs(W)
o[o>6]
## A disord object with hash 908bdb13a2f83dd05aa5c66195a4fe0b570d42f1 and elements
## [1] 9 18 9 12
## (in some order)
Extraction works as expected. Using recent improvements in the
disordR
package, we take all coefficients less than 7 and
add 100 to them:
## x y dx dy val
## 0 2 2 1 = 104
## 1 2 2 1 = 106
## 1 2 3 0 = 106
## 1 2 2 0 = 106
## 1 2 3 1 = 12
## 2 2 2 0 = 9
## 0 2 2 2 = 104
## 2 2 3 0 = 18
## 2 2 4 0 = 9
References
disordR
Package.β https://arxiv.org/abs/2210.03856; arXiv. https://doi.org/10.48550/ARXIV.2210.03856.