-
Notifications
You must be signed in to change notification settings - Fork 167
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Interface with Python (CPython) #703
Comments
So, here's what I understand from our discussions and the issue above,
Please let me know if I interpreted your thoughts correctly. |
Yes. When you say "you will be required", it means LPython will do it for you automatically. The user will not be required to do that. |
Yeah. By "you will be required" I mean we will be required to add it in LPython so that it can create a CPython extension for the user. |
In my understanding, for
Is that correct? I guess, |
I think so. I think |
This should be called @lpython.jit
def fast_sum(x: f64[n]) -> f64:
s: f64 = 0
for i in range(n):
s += x[i]
return s This will call LPython under the hood, give it the source code of the function, LPython compiles via LLVM into a shared library, and it also emits Python wrappers of this function, all into the same shared library. Then the decorator loads this shared library and calls this function, so when you do later in CPython: x = fast_sum(np.array([1, 2, 3])) It will call into the function implemented in the shared library. To get started, we can just generate C code (or even Cython) for the Python wrappers part, and compile everything in. Later we can maybe represent such C code using ASR itself. So here are a list of steps:
Once this works, we will then make some of these steps more robust, such as 4. should be later done by LPython instead of the decorator, and 5. should also be done by LPython with the appropriate option. Also 2. should be done by calling LPython directly from CPython, not going via a file. |
How are these decorators like |
lpython/src/runtime/lpython/lpython.py Line 246 in ae6c547
A simple code like this would work: from lpython import f64, ccall
@ccall
def _lfortran_ssin(x: f64 ) -> f64:
pass
print(_lfortran_ssin(0.8)) # Calls in the function from lfortran_intrinsics.c
|
I was trying to make a decorator on my system first. It covers the step 1 and 2 as mentioned by @certik. Am I on the right track here @Thirumalai-Shaktivel ? It basically creates a new file called
|
I think so. |
How should I register |
For @cpython
def f(n: i32, x: str) -> str:
import my_module
return my_module.run(n, x) So we need:
The decorator should be called |
For
Is this the way? |
We can start with: @cpython
def f() -> str:
import my_module
return my_module.run() Then the next step is: @cpython
def f(n: i32, x: str) -> str:
import my_module
return my_module.run(n, x) |
Python allows the following: @cpython
def f(n: i32) -> str:
import sympy
sympy.var("x")
e = ((x+5)**n).expand()
return str(e) |
One idea is to take this: @cpython
def f(n: i32) -> str:
import sympy
sympy.var("x")
e = ((x+5)**n).expand()
return str(e) and this gets converted to the following CPython function (you can even imagine it being in a file): def f(n):
import sympy
sympy.var("x")
e = ((x+5)**n).expand()
return str(e) And then we just call it, by first converting all arguments and then calling the function |
I think we should design first an equivalent of @ccall
def f(n: i32) -> str:
pass So we should also have: @pythoncall(module="mymodule")
def run(n: i32) -> str:
pass Now we import the "mymodule.py" (from the path provided by We'll have to add Once we implement physical types, this is part of the physical type. |
Another idea is to do this at import time:
But somehow specify that |
In the same way we should add |
Current status:
|
@certik it seems that to support
Which approach shall we follow? |
I would call the Python C/API directly from LLVM. If something cannot be done easily, then we can write a simple wrapper in our C runtime library for just that one thing. If (and only if) the Python C/API is called from LLVM, we also need to link with the Python shared library. |
Got it. Thank you! |
Apart from this, which other array handling should we need support? def multiply(n: i32, x: f64[:]) -> f64[:]:
pass def multiply(n: i32, x: f64[:]) -> f64:
pass |
I think these are the main use cases. |
This can't work: def multiply(n: i32, x: f64[:]) -> f64[:]:
pass You have to use: def multiply(n: i32, x: f64[:]) -> f64[n]:
pass |
What can be the difference between My understanding is:
|
The following throws error message: from lpython import f64, lpython
from numpy import sqrt, array
@lpython
def test():
arr: f64[3] = array([1.,2.,3.])
arr = sqrt(arr)
test() Error: $ python examples/expr2.py
semantic error: Function 'sqrt' is not declared and not intrinsic
--> ./lpython_decorator_test/test.py:4:11
|
4 | arr = sqrt(arr)
| ^^^^^^^^^
Note: if any of the above error or warning messages are not clear or are lacking
context please report it to us (we consider that a bug that must be fixed).
Traceback (most recent call last):
File "/Users/thirumalai/Open_Source/lpython/examples/expr2.py", line 5, in <module>
def test():
File "/Users/thirumalai/Open_Source/lpython/src/runtime/lpython/lpython.py", line 674, in __init__
assert r == 0, "Failed to create C file"
AssertionError: Failed to create C file
@pythoncallable
def test():
arr: f64[3] = array([1.,2.,3.])
arr = sqrt(arr) The problem is that we must insert an import statement: |
TODO for
|
We need to design some kind of a decorator similar to
ccall
andccallable
(which interfaces to and from C) but to/from Python. Since our surface language is Python, the design can be different. Such as:LPython will take the contents of the decorated function and turn it into a series of Python C/API calls in ASR, and it will convert the arguments to / from ASR's i32/str into CPython correctly.
This approach will allow calling any Python code naturally from LPython. With
@ccall
we can naturally call any C code.The decorator can be just called
@python
, but since LPython is Python (subset at first, but we are keeping the door open to possibly support all of Python later), it might be confusing what exactly is meant by it.The second decorator we need is to call a function from Python. Something like:
The contents of the function is regular LPython code that will get optimized by our optimizers to produce top performing (vectorized) machine code. But the interface will get exposed to CPython, so LPython will do the conversion from a NumPy array into
f64[n]
, and it will convert the return valuef64
from ASR to CPython. Not sure about all the details yet in ASR, but with the C backend it will produce code that when compiled would create a CPython extension module (shared library) that you just import in Python and it will just work.With these two decorartors (
@cpython
andcpython_callable
) we can use LPython to create extension modules to CPython. We can easily call any C library if we want to (thus from this perspective it is very similar to Cython), and we can also just implement things in regular LPython code which gets highly optimized. Everything is just Python, so you can take any such LPython code and if you run it with CPython, it will work (just slower).This is a very powerful design that allows to 100% stay in Python. One starts with CPython, implements some code, then takes parts of it, extract into a separate module that LPython can compile, decorate the API with
@cpython_callable
and use LPython to compile. Since@ccall
works from both CPython and LPython, one can interface any C code easily from both. It's a very simple gradual approach, and it allows to achieve the top performance, as we deliver more on LPython as a regular production optimizing compiler, that is very good with arrays.The text was updated successfully, but these errors were encountered: