article about a simple arithmetic inversion operation utilizing a standard AST
For the past couple of months I have been fixated on learning the field of topology as a kind of hobby. In that time the use of function inversion of the form $f^{-1}$ has caught my eye. This kind of notation is seen often to describe the inverse of a function $f$, especially in
Node : Operation or Lexeme
AST : Node []
-- where the AST is a tree with node equal to an operation or lexeme
f : AST -> AST
f (node []) = invert node -- leaf case
f (node ls) = AST (invert node) (map f ls)
-- f would invert the AST meaning to take the inverse of each piece recursively
-- assume that the inverse of any AST is the inverse of its parts
I have some initial problems with this approach. First simply mapping $f$ over all children is begging for a stack overflow. Secondly is my assumption that the inverse of any function body AST (thus the inverse of any function) is constructed via the inverse of its parts? I convinced myself that speed optimization could wait until after I had seen if the idea could work. The assumption did have me worried, thus I constrained the math to arithmetic where I found only edge cases where my assumption was untrue. The edge cases though difficult to ignore can be countered with constraints. Our problem space is currently bound by the following.
\begin{equation} \label{ax1} \text{Axiom 1: }AST^{-1} = [ x = x_i^{-1} | \forall x \in AST] \end{equation}
\begin{equation} \label{ax2} \text{Axiom 2: Arithmetic space only} \end{equation}
That is to make explicit my assumption that the inverse of any function AST is simply the inverse of its parts, thus Axiom \eqref{ax1}. For this proof of concept I wanted to limit the math to trivialities only. Thus my limiting the problem space to arithmetic in Axiom \eqref{ax2}. In particular it is the case that while testing the code I only used basic operators and integers. The basic operators being: multiply, divide, addition, subtraction.
The problem of function inversion seems to be widely studied in the field of security. The literature on encryption has a detailed history of looking into inversion of functions. I skimmed into the security literature to no avail for my specific problem. I found a nice overview of the encryption literature surrounding my problem in
I had the idea to utilize ASTs for the reasons of refreshing my understanding of the fundamental theory and to investigate newer literature. My hypothesis being to emulate a mathematicians chalk work on the problem of inversion as it applies to arithmetic functions. In particular my algorithm is meant to solve the equation using similar intuition. When a mathematician is given some function $f$ such that.
\begin{equation} \label{initial_f} f(x) = x + 5 \end{equation}
The first thing the mathematician examines is the structure of the equation. Here in Eq \eqref{initial_f} it is clear that $f$ performs the addition operation on $x$ and the number $5$. The inversion is trivially found to be $f^{-1}(x) = x - 5$ such that we have simply taken the inverse of all operations in Eq \eqref{initial_f}. This thought prompted the idea that the inverse of an arbitrary arithmetic function is the sum inverse of that functions parts. The testing for this proved it to be partially correct with the exception of edge cases where operations themselves are non-invertible. Examples of these can be seen with any equation utilizing the modulo operation. Thus giving us our third axiom for this problem (further constraining the problem space).
\begin{equation} \text{Axiom 3: Arithmetic functions s.t the modulo operation is not present} \end{equation}
It turns out (In my short study) that as long as all the operations in an arithmetic equation are invertible, then the over all inversion is possible. Making this a kind of “sum of its parts” problem. Using this intuition we can derive a trivial algorithm to solve an arbitrary equation bound by our $\text{Axioms}_{1-3}$. Such an algorithm would seemingly just perform inversion on all operations (in place).
AST :: lexeme | operation [children]
inv :: AST -> AST
inv lexeme = lexeme
inv (op ls) = (inverse op) (map inv ls)
inverse :: operation -> operation
inverse op = op -- hard coded inversion
The above code symbolizes my version one design. Upon thinking about the idea for a few hours I came to the conclusion this would not preserve order of operations. So I came up with a version two of the code which does a BFS-traversal prior to inversion. This ensures a snap shot of the current state of the AST given to inversion. This snap shot will contain a unique id ordering for each node, providing a way to rearrange a test structure then apply the arrangement to the AST. I plan on introducing a kind of verification step later, this architecture better allows that as well as providing the ability to preserve order. It is the case that the algorithm remains primitive in its current form, this allows for the easiest proof of concept. Given that the inversion function described above can be thought of as $I$ this is our second algorithm.
\begin{equation} \label{inv_2} I’ = (I \circ BFS)(AST) \end{equation}
It is the case that Eq \eqref{inv_2} represents our new inversion function. In particular it is true that the inversion function will receive not an AST, but a BFS searched tree structure. A change which makes unique rearrangement and verification possible
If you have only ever written a tree search algorithm in an imperative language I recommend using a function language once. It is not fun to think about the problem without the use of pointers or at least boxes. The code I came up with is neither optimal nor pretty, though it does have a certain functional beauty to it.
The pseudocode for BFS is well known, and the Haskell I wrote is viewable on my Github repo.
I feel that there is merit in exploring the outcomes of a verification system surrounding the inversion function. As stated in the above sections I had a thought previously about inserting verification into this function. I see several problems with this idea in the projects current state. It is not obvious to me that one can create a programmatic structure for a proof of inversabilty. Such that creating this structure for an arbitrary function might be asking to much (which is what makes it sound fun). Such a structure would need to be powerful enough to differentiate between arithmetic functions and moving forward, geometric functions. The future I see for this type of inversion verification might be closer to a user defined inversion definition system. Thus a system where the user defines a function and its proper inverse function. This allows our verification problem to be simplified into something of a logical verification. In conclusion (because I am now thinking whilst I write) the planned future work for this project are as follows.
This project served to get me into compiler like projects again in a non-academic sense. Thus the main proponent of this project was to do some: reading, research, coding, and writing (thus my measly three references below). I also used this post to start my blog; creating a reference for current students; lastly to begin a personal hobby of writing about my pet projects.
In the event you read this and have any thoughts on the project, do not hesitate to message me on Github or email me about it.
Extra information that is not necessary to the article or is (relevant) assumed knowledge.