% -*- mode: noweb; noweb-default-code-mode: R-mode; -*-


\documentclass{article}

\usepackage{arxiv}

\usepackage[utf8]{inputenc} % allow utf-8 input
\usepackage[T1]{fontenc}    % use 8-bit T1 fonts
\usepackage{hyperref}       % hyperlinks
\usepackage{url}            % simple URL typesetting
\usepackage{booktabs}       % professional-quality tables
\usepackage{amsfonts}       % blackboard math symbols
\usepackage{amssymb}        % needed for \leqslant
\usepackage{amsmath}        % needed for cases
\usepackage{nicefrac}       % compact symbols for 1/2, etc.
\usepackage{microtype}      % microtypography
\usepackage{lipsum}		% Can be removed after putting your text content
\usepackage{graphicx}
\usepackage[numbers]{natbib}
\usepackage{doi}
\usepackage{wrapfig}



\title{Quantum algebra in R: the weyl package}

%\date{September 9, 1985}	% Here you can change the date presented in the paper title
%\date{} 					% Or removing it

\author{ \href{https://orcid.org/0000-0001-5982-0415}{\includegraphics[width=0.03\textwidth]{orcid.pdf}\hspace{1mm}Robin K. S.~Hankin}\thanks{\href{https://www.stir.ac.uk/people/1966824}{work};  
\href{https://www.youtube.com/watch?v=JzCX3FqDIOc&list=PL9_n3Tqzq9iWtgD8POJFdnVUCZ_zw6OiB&ab_channel=TrinTragulaGeneralRelativity}{play}} \\
 University of Stirling\\
	\texttt{hankin.robin@gmail.com} \\
}

% Uncomment to remove the date
%\date{}

% Uncomment to override  the `A preprint' in the header
%\renewcommand{\headeright}{Technical Report}
%\renewcommand{\undertitle}{Technical Report}
%\renewcommand{\shorttitle}{Quantum algebra in R}

%%% Add PDF metadata to help others organize their library
%%% Once the PDF is generated, you can check the metadata with
%%% $ pdfinfo template.pdf
\hypersetup{
pdftitle={Quantum algebra in R},
pdfsubject={q-bio.NC, q-bio.QM},
pdfauthor={Robin K. S.~Hankin},
pdfkeywords={Quantum algebra, Weyl algebra, noncommutative algebra}
}

\begin{document}
\maketitle

\begin{abstract}
Weyl algebra is a simple noncommutative system used in quantum
mechanics.  Here I introduce the {\tt weyl} package, written in the R
computing language, which furnishes functionality for working with
univariate and multivariate Weyl algebras.  The package is available
on CRAN at \url{https://CRAN.R-project.org/package=weyl}.
\end{abstract}

\SweaveOpts{}

\section{Introduction}

\setlength{\intextsep}{0pt}
\begin{wrapfigure}{r}{0.2\textwidth}
  \begin{center}
\includegraphics[width=1in]{weyl.png}
  \end{center}
\end{wrapfigure}
Consider the vector space ${\mathcal A}$ of linear operators on
univariate functions; ${\mathcal A}$ can be made into an algebra,
conventionally called a Weyl algebra~\cite{coutinho1997}, where
multiplication (denoted by juxtaposition) is defined as operator
composition.  That is, given operators ${\mathcal O}_1,{\mathcal O}_2$
we define their product ${\mathcal O}_1{\mathcal O}_2$ by

\[({\mathcal O}_1{\mathcal O}_2)f={\mathcal O}_1({\mathcal O}_2f)\]

where $f$ is any univariate function.  Here we consider the algebra
generated by the set $\left\lbrace\partial,x\right\rbrace$ where
$\partial\colon f\longrightarrow f'$ [that is, $(\partial f)(x) =
f'(x)$] and $x\colon f\longrightarrow xf$ [that is, $(xf)(x) =
xf(x)$].  This is known as the (first) Weyl algebra.  We observe that
the Weyl algebra is not commutative: $\partial xf=(xf)'=f+xf'$ but
$x\partial f=xf'$, so $\partial x=x\partial+1$.  The algebra generated
by $\left\lbrace x,\partial\right\rbrace$ will include elements such
as $7\partial + 4\partial x\partial^3 x$, which would map $f$ to $7f'
+ 4\left(x\left(xf\right)'''\right)'$.  It can be shown that any
element of the Weyl algebra can be expressed in the standard form

\[
\sum_i a_i \partial^{p_i}x^{q_i}
\]

for real $a_i$ and nonnegative integers $p_i,q_i$.  Converting a
general word to standard form is not straightforward but we have

\[
\partial x^n = x^n\partial +  nx^{n-1}\]

and

\[
\partial^n x = x\partial^n +  n\partial^{n-1}.\]

We can apply these rules recursively to find standard form for
products $(\partial^i x^j)(\partial^l x^m)$.  Alternatively we may
follow Wolf~\cite{wolf1975} and use the fact that

\[
(\partial^i x^j)(\partial^lx^m)=
\sum_{r=0}^j{j\choose r}{l\choose r}\partial^{i+l-r}x^{j+m-r}.\]

These rules can be used to show, for example, that $7\partial +
4\partial x\partial^3 x$ can be expressed as $7\partial +
12x\partial^2 + 4x^2\partial^3$, which is in standard form.

\subsection{The package in use}

The above operators form part of the {\tt weyl} package, written in
the R computing language~\citep{rcore2022}, which is available on CRAN
at
\url{https://CRAN.R-project.org/package=weyl}~\cite{hankin2022_weyl}.
The package includes functionality to automate the above calculations
in an R-centric manner.  In particular, package idiom represents the
generating elements $\partial$ and $x$ of the first Weyl algebra as R
objects {\tt d} and {\tt x} respectively.  These may be manipulated
with standard arithemetic operations, and considering the example from
above we would have:

<<loadlib>>=
library("weyl")
7*d + 4*x*d^3*x
@

Above, the result is given in standard form.  We see the terms, one
per row, with coefficients in the rightmost column (viz $7,12,4$).
Thus the first row is $7\partial$, the second is $12x\partial^2$, and
the third is $4x^2\partial^3$.  We may choose to display the result in
symbolic form rather than matrix form:

<<firstpolyform>>=
options(polyform=TRUE)
7*d + 4*x*d^3*x
@

which is arguably a more natural representation.  The package allows
one to use R semantics.  For example, consider $d_1=\partial x +
2\partial^3$ and $d_2=3+7\partial -5x^2\partial^2$.  Observing that
$d_1$ and $d_2$ are in standard form, package idiom to create these
operators would be:


<<defineandprintd1d2>>=
(d1 <- d*x + 2*d^3)
(d2 <- 3 + 7*d  -5*x^2*d^2)
@

(object {\tt d1} is converted to standard form automatically).  Observe
that, like the {\tt spray} package, the order of the terms is not defined.
We may apply the usual rules of arithmetic to these objects:

<<showmultiplication>>=
d1*d2
@

Standard R semantics operate, and it is possible to work with more
complicated expressions:

<<showinpolyform>>=
options(polyform=TRUE)
(d1^2 + d2) * (d2 - 3*d1)
@

\subsection{Comparison with mathematica}

Mathematica can deal with operators and we may compare the two
systems' results for $\partial^2x\partial x^2$:


\begin{verbatim}
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]
\end{verbatim}


<<>>=
x <- weyl(cbind(0,1))
D <- weyl(cbind(1,0))
x^2*D*x*D^2
@

Above, we see agreement between {\tt weyl} and Mathematica, although the
terms are presented in a different order.

\section{Further Weyl algebras}

The package supports arbitrary multivariate Weyl algebras.  Consider:

<<showmultivariateweyl>>=
options(polyform=FALSE)    # revert to default print method
set.seed(0)
x <- rweyl()
x
@

Above, object {\tt x} is a member of the operator algebra generated by
$\left\lbrace\partial_x,\partial_y,\partial_z,x,y,z\right\rbrace$.
Object {\tt x} might be expressed as $xz^2\partial_x\partial_z +
3x^2z\partial_x^2\partial_z + 2yz^2\partial_x^2\partial_z^2$ although
as ever the rows are presented in an implementation-dependent order.
We may verify associativity of multiplication:

<<associativityisnontrivial>>=
x <- rweyl(n=1,d=2)
y <- rweyl(n=2,d=2)
z <- rweyl(n=3,d=2)
options(polyform=TRUE)
x*(y*z)
(x*y)*z
@

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:

<<verifyassociativity>>=
x*(y*z) - (x*y)*z
options(polyform=FALSE)  # revert to default print method
@

The package can deal with arbitrarily high dimensional Weyl algebras.
For exmaple:

<<arb9>>=
(x9 <- rweyl(dim=9))
@

Above we see a member of the ninth Weyl algebra; see how the column
headings no longer use the {\tt x y z} notation and revert to numeric
labels.  Symbolic notation is available but can be difficult to read:

<<polyformuser>>=
options(polyform=TRUE)
x9
options(polyform=FALSE)  # revert to default print method
@

\section{Derivations}

A {\em derivation} $D$ of an algebra ${\mathcal A}$ is a linear operator
that satisfies $D(d_1d_2)=d_1D(d_2) + D(d_1)d_2$, for every
$d_1,d_2\in{\mathcal A}$.  If a derivation is of the form $D(d) =
[d,f] = df-fd$ for some fixed $f\in{\mathcal A}$, we say that $D$ is
an {\em inner} derivation:

\[
D(d_1d_2) =
d_1d_2f-fd_1d_2 =
d_1d_2f-d_1fd_2 + d_1fd_2-fd_1d_2 = 
d_1(d_2f-fd_2) + (d_1f-fd_1)d_2 =
d_1D(d_2) + D(d_1)d_2
\]

Dirac showed that all derivations are inner derivations for some
$f\in{\mathcal A}$.  The package supports derivations:

<<define_derivation_D>>=
f <- rweyl()
D <- as.der(f)  # D(x) = xf-fx
@

Then

<<show_D_is_a_derivation,cache=TRUE>>=
d1 <- rweyl()
d2 <- rweyl()
D(d1*d2) == d1*D(d2) + D(d1)*d2
@

\section{Low-level considerations and generalizations}

In the package, the product is customisable.  In general, product {\tt
a*b} [where {\tt a} and {\tt b} are {\tt weyl} objects] is dispatched
to the following sequence of functions:

\begin{itemize}
\item {\tt weyl\_prod\_multivariate\_nrow\_allcolumns()}
\item {\tt weyl\_prod\_multivariate\_onerow\_allcolumns()}
\item {\tt weyl\_prod\_multivariate\_onerow\_singlecolumn()}
\item {\tt weyl\_prod\_univariate\_onerow()}
\item {\tt weyl\_prod\_helper3()} (default)
\end{itemize}

In the above, ``univariate" means ``generated by $\left\lbrace
x,\partial_x\right\rbrace$" [so the corresponding {\tt spray} object
has {\em two} columns]; and ``multivariate" means that the algebra is
generated by more than one variable, typically something like
$\left\lbrace x,y,z,\partial_x,\partial_y,\partial_z\right\rbrace$.

The penultimate function {\tt weyl\_prod\_univariate\_onerow()} is
sensitive to option {\tt prodfunc()} which specifies the recurrence
relation used.  This defaults to {\tt weyl\_prod\_helper3()}:

<<>>=
weyl_prod_helper3
@

Function {\tt weyl\_prod\_helper3()} follows Wolf.  This gives the
univariate concatenation product $(\partial^a x^b)(\partial^c x^d)$ in
terms of standard generators:

\[
\partial^a x^b \partial^c x^d=\sum_{r=0}^b
r!{b\choose r}{c\choose r}
\partial^{a+c-r}x^{b+d-r}
\]

The package also includes lower-level function {\tt weyl\_prod\_helper1()}
implementing $\partial^a x^b \partial^c
x^d=\partial^ax^{b-1}\partial^cx^{d+1} +
c\partial^ax^{b-1}\partial^{c-1}x^d$ (together with suitable
bottoming-out).  I expected function {\tt weyl\_prod\_helper3()} to be much
faster than {\tt weyl\_prod\_helper1()} but there doesn't seem to be much
difference between the two.

\section{Generalized commutator relations}

We can exploit this package customisability by considering, instead of
$\left\lbrace x,\partial\right\rbrace$, the algebra generated by
$\left\lbrace e,\partial\right\rbrace$, where $e$ maps $f$ to $e^xf$:
if $f$ maps $x$ to $f(x)$, then $ef$ maps $x$ to $e^xf(x)$.  We see
that $\partial e-e\partial=e$.  With this, we can prove that
$\partial^ne=e(1+\partial)^n$ and $e^n\partial=e^n\partial+ne^n$ and,
thus

\[
(e^a\partial^b)(e^c\partial^d)
=e^{a+1}(1+\partial)^be^{c-1}\partial^d
=e^{a}\partial^{b-1}e^{c}\partial^{d+1}+ce^{a}\partial^{b-1}e^{c}\partial^d
\]

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 $\partial^2e=e(1+2\partial+\partial^2)$:

<<>>=
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
d^2*e
@

By way of verification:

<<>>=
d^5*e == e*(1+d)^5
@

which verifies that indeed $\partial^5e=e(1+\partial)^5$.  Another
verification would be to cross-check with Mathematica, here working with
$\partial e\partial^2e$:

\begin{verbatim}
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]
\end{verbatim}


<<>>=
options(polyform = TRUE)
d*e*d^2*e
@

We can manipulate more complicated expressions too.  Suppose we want to
evaluate $(1+e^2\partial)(1-5e^3\partial^3)$:

<<>>=
o1 <- weyl(spray(cbind(2,1)))
o2 <- weyl(spray(cbind(3,3)))
options(polyform = FALSE)
(1+o1)*(1-5*o2)
@

And of course we can display the result in symbolic form:

<<>>=
options(polyform = TRUE)
(1+o1)*(1-5*o2)
@


<<polyformnull>>=
options(polyform = NULL) # restore default print method
@

\section{Computational implementation and notes on {\tt disordR}
discipline}

The package stores {\tt weyl} objects using the {\tt
spray}~\cite{hankin2022_spray_arxiv} class for sparse arrays.
Addition is inherited from {\tt spray}; multiplication is specific to
the {\tt weyl} package.  Thus the coefficients of a {\tt weyl} object,
and the rows of its index matrix, are stored in an
implementation-specific order.  Extraction and replacement use {\tt
disordR} discipline~\cite{hankin2022_disordR_arxiv}.  A short example
follows in the context of the {\tt weyl} package; much more extensive
and detailed discussions are given by
Hankin~\cite{hankin2022_disordR_arxiv,hankin2022_mvp_arxiv}.


\subsection{An illustrative session}

Here I show how {\tt disordR} discipline is used in a typical R
session.  First we create a moderately complicated {\tt weyl} object:

<<makearandomweyl>>=
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)
@

The coefficients of {\tt W} may be extracted:

<<showcoeffsinuse>>=
coeffs(W)
@

The object returned is a {\tt 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:

\begin{Schunk}
\begin{Sinput}
> coeffs(W)[1]
\end{Sinput}
\begin{Soutput}
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)
\end{Soutput}
\end{Schunk}

However, questions such as ``give all coefficients greater than 6" are
perfectly well defined:

<<ogreaterthantwo>>=
o <- coeffs(W)
o[o>6]
@

Extraction works as expected.  Using recent improvements in the
{\tt disordR} package, we take all coefficients less than 7 and add 100 to
them:

<<showextractionworking>>=
coeffs(W)[coeffs(W)<7] <- coeffs(W)[coeffs(W)<7] + 100
W
@

\section{Conclusions and further work}

The {\tt weyl} package implements Weyl algebra using real
coefficients.  However, in quantum mechanics one typically works with
the momentum operator $p_j$, defined as $-i\partial_j$ (where $i$ is
the imaginary unit of the complex plane, $i^2=-1$); thus
$x_jp_j-p_jx_j=-i$.  Further work might include admitting complex
coefficients to accommodate this parametrization.

\bibliographystyle{apalike}
\bibliography{weyl}

\end{document}
