Week 2
Do y’all remember my progress from the first week ? Reiterating, I had introduced the SymbolicExpression
ttype and the Introducing SymbolicSymbol
intrinsic function node . So my goal for the second week was adding support for symbolic expressions in visit_print
and visit_assignment
functions.
Do y’all remember how I had discussed my plan on tackling this ? Well my disucssion with Ondřej had led us to a conclusion that supporting the above operations through the LLVM backend might be a suitable approach for the problem. Though I had given y’all a big disclaimer saying that this method might or might not work and we’re still open to exploring new approaches
This is what ended up happening precisely. Halway through exploring this appraoch and trying to get the LLVM backend running, we realized that dealing with LLVM here is more stringent and imposes a lot of strict regulations and conditions on how we can solve the problem.
- We would have to create an LLVM structure first as symbolic expressions cannot be handled using primite LLVM types. Hence would have to define a structure as follows
llvm::StructType *symbolic_expr_type
. - Then we would have to create the structure and add components based on primitive types.
std::vector<llvm::Type*> els_symbolic = {
llvm::Type::getInt8PtrTy(context)};
std::vector<llvm::Type*> els_symbolic_ptr = {
llvm::Type::getInt8PtrTy(context)->getPointerTo()};
symbolic_expr_type = llvm::StructType::create(context, els_symbolic, "symbolic_expr");
symbolic_expr_type_ptr = llvm::StructType::create(context, els_symbolic_ptr, "symbolic_expr_ptr");
- Finally we would have to frame the
generate_SymbolicSymbol
function to return aLLVM:Value
object with typesymbolic_expr
.
This was making the implementation more rigid an hence we decided to switch to the C backend. LPython’s C backend is as good as the LLVM backend. The C backend may also offer certain advantages over the LLVM backend, such as simpler code generation, better compatibility with existing C-based tools and libraries (such as SymEngine’s C interface), and potentially faster development time. Also we haven’t yet given up on the LLVM approach, we’ve just shifted it for the later weeks.
For supporting the C backend I had to address the following
- Implement the
SymbolicSymbol::verify_args
function - Implement the
instantiate_SymbolicSymbol
function . This is because we would like to replace theIntrinsicFunction
node with aFunctionCall
node through our replace intrinsic function pass.
[(=
(Var 2 x)
(IntrinsicFunction
SymbolicSymbol
[(StringConstant
"x"
(Character 1 1 () [])
)]
0
(SymbolicExpression)
()
)
()
)]
# From the above to something like the following
[(=
(Var 2 x)
(FunctionCall
1 _lcompilers_symbolic_str
1 _lcompilers_symbolic_str
[((StringConstant
"x"
(Character 1 1 () [])
))]
(SymbolicExpression)
()
()
)
()
)]
- Adding support in
visit_print
andvisit_assignment
to handle symbolic expressions to finally generate the C code. Demonstrating the assignment statement below
from lpython import S
from sympy import Symbol
def main0():
x: S = Symbol('x')
main0()
(lf) anutosh491@spbhat68:~/lpython/lpython$ lpython --show-c examples/expr2.py
#include <symengine/cwrapper.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <lfortran_intrinsics.h>
struct dimension_descriptor
{
int32_t lower_bound, length;
};
// Implementations
basic _lcompilers_symbolic_str(char * x)
{
basic _lcompilers_symbolic_str;
_lcompilers_symbolic_str = _lfortran_set_symbol(x);
return _lcompilers_symbolic_str;
}
void main0()
{
basic x;
x = _lcompilers_symbolic_str("x");
}
void _lpython_main_program()
{
main0();
}
int main(int argc, char* argv[])
{
_lpython_set_argv(argc, argv);
_lpython_main_program();
return 0;
}
Finally, I would like to point out some tasks which I plan to tackle for the upcoming week.
- Address all nitty-gritties and generate an output for the assignment and print statements from the LPython compiler.
- Start introducing binary operators compatible with symbolic expressions.
- Also introduce some casting functions for converting primitive data types into their symbolic forms.
Thank You for going through the blog. I hope you like it. Stick around for what’s next to come. Moving into Week 3!