Week 5

Talking about this week, this week was both fun and challenging for me at the same time. It was fun because I got my first 2 GSoC Pr’s #1846, #1964 merged within the first 4 days of the week and the remaining 3 days were spent on introducing some functionality which I’ll discuss below. I’ve discussed the work done through my first pr in the past and my second pr was just a top up on this where we introduced more symbolic binary operators and also a casting function to convert an integer of i32 type to a SymbolicInteger of SymbolicExpression type using the S function call.

So by this time we could essentially solve the following program

(lf) ~/lpython/lpython$ cat examples/expr2.py 
from sympy import Symbol

def main0():
    x: S = Symbol('x')
    y: S = Symbol('y')
    z: S = x + y
    a: S = S(20)
    b: S = z ** a
    c: S = a / z
    print(b)
    print(c)

main0()
`
(lf) ~/lpython/lpython$ lpython --backend=c --enable-symengine examples/expr2.py 
(x + y)**20
20/(x + y)

At this point, I could have started working on implementing symbolic elementary functions like abs and sin but we realized that there were few places where our code might crash and not work as expected. I would like to highlight these first

  • Directly executing Print statements without Assignment statements - This is influenced by how the c wrapper file has been framed for SymEngine. The functions are framed to be more supportive towards assignment statements. For example, we could look at the basic_const_pi() function . This takes a basic variable as an argument and assigns the constant pi to it . So now how do we solve this program . Any ideas ??
    from sympy import pi
    def main():
    print(pi)
    
  • Chaining of Symbolic Operators - This problem does take some inspiration from the above problem. As per what a binary operator means, it deals with 2 arguments which is also what SymEngine functions like basic_const_add(), basic_const_pow() and others do but then they assign it to a basic variable target. Conducting operations through the target variable is acheivable but what if we don’t have an intermediate varibale like target at each step ? We would end up in trouble.
    from sympy import pi, Symbol
    def main():
    x: S = Symbol('x')
    y: S = x + pi * S(20)
    print(y)
    

Thinking of what’s going wrong here wasn’t too tough. Well it was clear that we needed to introduce dummy/temporary variables to handle these problems. During our weekly meeting, Ondřej and I did brainstorm on this approach and we managed to develop a solution for this. We thought of introducing a stack that would help us generate temporary variables whenever we would need one or we would run out of variables that have been declared in the program.

Hence if I were to discuss the C code generated for the first program, it would end looking like

// Implementations
void main0()
{
    basic stack0;
    basic_new_stack(stack0);
    basic_const_pi(stack0);
    printf("%s\n", basic_str(stack0));
}

where stack0 was the element extracted from the top of the stack.

Though I did make some progress on this and was able to get it working with Intrinsic Functions which involve either no or a single argument like SymboliPi, SymbolicSymbol etc , I am still working towards introducing compatibility with Intrinsic Functions that take up multiple arguments like the binary operators. Hence I’ll keep the complete explanation of the approach for the upcoming week where I would like to explain it in more detail.

I would now like to point out some tasks which I plan to tackle for the upcoming week.

  • Work towards getting correct C code generated for scenarios involving operator chaining.
  • Introduce a couple elementary functions like abs and sin.
  • Open a third pull request with these changes and get it merged.

Thank You for going through the blog. I hope you like it. Stick around for what’s next to come. Moving into Week 6!

Address

Mumbai, Maharashtra, India