# Representing mathematical tree structures using software in a compact manner

In my work I frequently come across systems of interdependent equations. I have contrived a toy example as follows. The terminal values `w`, `x`, `y` and `z` are given:

`e(y) = A+B`

`A(y) = x*log(y)+y^z`

`B(y) = alpha*y`

`alpha(y) = x*y+w`

We could then consider the function `e(y)` as the root of an arithmetic tree with the following heirarchy:

Previously, in python I would have done something like this to evaluate the result:

``````import numpy as np

def root(B, A):
return B+A

def A(x,y,z):
return x*np.log(y)+y**z

def B(alpha, y):
return alpha*y

def alpha(x,y,w):
return x*y+w

if __name__=='__main__':

x,y,z,w = 1,2,3,4
result = root(B(alpha(x,y,w),y), A(x,y,z))
``````

This will give me the right result, but I have come to really despise this way of doing things. It requires me to keep track of which arguments each function needs and how the tree itself is built up. Also, suppose I wanted to modify the tree itself by adding branches and leaves. For example, say I wanted to redefine `alpha` as `v+x+y` with the new variable `v`. I'd have to make a new function and a new call, which is not very efficient as I sometimes need to make pervasive and numerous changes.

I tried different approaches to solve this problem as outlined by this question and this question.

I came across a couple of ideas which looked promising, namely function objects and the Interpreter Pattern. However I was disappointed by the Interpreter Pattern. Suppose I didn't create a parser, and went straight for the underlying composite architecture, wouldn't I still have to do something like this?

``````root = root_obj(B_obj(alpha_obj(x_obj,y_obj,w_obj),y_obj), A(x_obj,y_obj,z_obj))
root.interpret()
``````

The above would require a lot of added complexity for no added value. My question is as follows: What is a simple and useful object oriented paradigm in which I could define, modify and evaluate a mathematical heirarchy in a dynamic manner?

EDIT

Here's an example of what I would like to achieve:

``````tree = FunctionTree()
tree.add_nodes(root, A, B, alpha, w, x, y, z)
tree.evaluate()
``````

Yes, this is less "compact" but it is much more flexible. Imaging having methods for deleting and adding new edges. replacing definitions at nodes and reevaluating the result. I am looking for something like this.

• It really depends on what you mean by "compact". Using Expression object is most common way of representing and evaluating equations. Are those not "compact" enough? Why not? Why not implement a simple parser and represent the equations as simple strings that are then parsed? Commented Oct 23, 2019 at 13:38
• Only mention of "compactness" is in end of last sentence. Nothing else in your question implies any relation to "compactness". Could you provide some pseudo-code how you imagine defining or modifying an expression that you would consider compact? Commented Oct 23, 2019 at 13:51
• Okay. In your representation, where do you put the mathematical operations? Also, how would changing the tree look like after it was created? And where are parameters coming from in evaluate method? Commented Oct 23, 2019 at 14:08
• It could be that the nodes of the trees are function objects which are aware of their own arithmetic and arguments. Arguments can either be terminal or non-terminal. functions which have only terminal arguments are closer to the leaves and functions which have only non-terminal arguments are closer to the root. With regards to changing the tree, it would be a simple matter of replacing a node and redefining the connectivity to terminal/nonterminal arguments. Commented Oct 23, 2019 at 14:15
• The tree you draw is a simple example of an Abstract Syntax Tree (en.wikipedia.org/wiki/Abstract_syntax_tree), writing a parser that takes a string representing a mathematical expression and construct its Syntax Tree is pretty straightforward. Is this maybe what you are looking for?
– Dtex
Commented Oct 23, 2019 at 14:41

This seems to be a continuation of your previous question. The recommendation made in this answer is still valid. But maybe I was not clear enough.

I'm not python fluent, but:

• Create a class `AbstractExpression`.
• Create a concrete specialisation class for every specific function you have: `Function_e`, `Function_A`, `Function_B`, `Function_Alpha`. Instances of these class would correspond to your orange boxes.
• Create a concrete class for the terminal expression. Call it `Variable`, and imagine that every instance of this class has a name. Instance of this class would correspond to your green circles.
• For clarity, let's use the pattern with a function `eval(context)` instead of `interpret(context)`

Now to the point on which I was not clear enough:

• Of course, `Function_e`'s constructor would construct a `Function_A` instance called `fA` and a `Function_B` instance. Absolutely no parsing is required here !
• Of course, `Function_A` would create `Variable` instances, `vy` with the names "y", `vz` for "z" and `vx` for "x". Again, no parsing is needed: the class constructor construct the needed objects (you code it).
• The `Function_e`'s `eval()` would do what it needs and call `fA.eval()` where the result of this functions is needed in the formula. I'll insist: absolutely no parsing takes place here! It's your implementation of `e` that will call a method of your implementation of `A`
• in your implementation of `fA.eval()`, you would call `vy.eval()`, `vz.eval()` and `vx.eval()` in the formula, where you would need each of these variables.

Now we have constructed an interpreter corresponding to your system, (without any parsing), and that is able to calculate the result, if only `Variable.eval()` could know the values to be used for these parameters. And here enters the `context`:

• `context` would be a dictionary that assigns fixed values to variable names.
• `context` is forwarded as single parameter through all the `eval()` calls explained above.
• the last implementation needed is `eval()` for `Variable`. This would just return the value associated to the variable's name in the `context` dictionary.

Sorry if I insisted on the absence of parsing. But many web site provide examples of the interpreter pattern without understanding real use cases. So they all are about parsing, which creates significant confusion. You have here a perfect example of use :-)

• One thing Python specific here that could change the answer is that there's a special function `__call__()` that (I think) obviates the need to define an `exec` method. You could then use the root/top level object like any python function. It could even take parameters but I'm not sure if that's helpful here or not. Commented Oct 23, 2019 at 19:28
• @Christophe I appreciate your answer, but how would my `main()` method look like with your proposal? Also would I be able to redefine the abstract tree with relative ease? I.e without having to go back into the classes themselves? Commented Oct 24, 2019 at 5:06
• @user32882 The abstract tree is the result of the cascaded construction process described above (so taking place in the `__init__(self)` of the classes). As said, I'm not python fluent, but your main would look like `root = Function_e()` followed by `context = { "x":1, ....}` followed by `result = root.eval(context)` Commented Oct 24, 2019 at 6:03
• I think you have the right idea and the fact I am not catching on is mainly due to my own ignorance about design patterns. I will continue to look through Interpreter/Composite to learn more. If/when I am able to implement one or both for this use case, I will accept both your answers. Thanks for your help. Commented Oct 24, 2019 at 8:11
• I'd say `Composite` and a `Visitor` pattern would be more adequate for what I am trying to achieve. I'll accept your answers though because they have pointed me to the right direction: stackoverflow.com/questions/58571505/… Commented Oct 30, 2019 at 14:33