-
Notifications
You must be signed in to change notification settings - Fork 164
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
Initial PythonCallable implementation #1984
Conversation
We need to handle the array as a return variable. |
@certik Am I in the right direction? |
src/runtime/lpython/lpython.py
Outdated
with open(filename + ".py", "w") as file: | ||
# Write the Python source code to the file | ||
file.write("@pythoncallable") | ||
file.write(source_code) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, exactly!
@Thirumalai-Shaktivel yes, I think that is exactly what I was imagining. Once this works, we remove |
I would take this:
and produce just one function in C:
|
// Implementations
double fast_sum(int32_t n, struct r64* x, struct r64* y)
{
int32_t __1_k;
double _lpython_return_variable;
int32_t i;
double s;
s = 0.00000000000000000e+00;
for (i=0; i<=n - 1; i++) {
s = s + x->data[(i - x->dims[0].lower_bound)];
}
for (__1_k=((int32_t)y->dims[1-1].lower_bound); __1_k<=((int32_t) y->dims[1-1].length + y->dims[1-1].lower_bound - 1); __1_k++) {
printf("%lf%s", y->data[(__1_k - y->dims[0].lower_bound)], " ");
}
printf("%s", "\b");
printf("\n");
_lpython_return_variable = s;
return _lpython_return_variable;
}
// Define the Python module and method mappings
static PyObject* fast_sum_define_module(PyObject* self, PyObject* args) {
// Initialize NumPy
import_array();
// Declare arguments and return variable
int32_t n;
PyArrayObject *x;
PyArrayObject *y;
double _lpython_return_variable;
// Parse the arguments from Python
if (!PyArg_ParseTuple(args, "iOO", &n, &x, &y)) {
PyErr_SetString(PyExc_TypeError, "An error occurred in the `lpython` decorator: "
"Failed to parse or receive arguments from Python");
return NULL;
}
// fill array details for x
if (PyArray_NDIM(x) != 1) {
PyErr_SetString(PyExc_TypeError, "An error occurred in the `lpython` decorator: "
"Only 1 dimension array is supported for now.");
return NULL;
}
struct r64 *s_array_x = malloc(sizeof(struct r64));
{
double *array;
// Create C arrays from numpy objects:
PyArray_Descr *descr = PyArray_DescrFromType(PyArray_TYPE(x));
npy_intp dims[1];
if (PyArray_AsCArray((PyObject **)&x, (void *)&array, dims, 1, descr) < 0) {
PyErr_SetString(PyExc_TypeError, "An error occurred in the `lpython` decorator: "
"Failed to create a C array");
return NULL;
}
s_array_x->data = array;
s_array_x->n_dims = 1;
s_array_x->dims[0].lower_bound = 0;
s_array_x->dims[0].length = dims[0];
s_array_x->is_allocated = false;
}
// fill array details for y
if (PyArray_NDIM(y) != 1) {
PyErr_SetString(PyExc_TypeError, "An error occurred in the `lpython` decorator: "
"Only 1 dimension array is supported for now.");
return NULL;
}
struct r64 *s_array_y = malloc(sizeof(struct r64));
{
double *array;
// Create C arrays from numpy objects:
PyArray_Descr *descr = PyArray_DescrFromType(PyArray_TYPE(y));
npy_intp dims[1];
if (PyArray_AsCArray((PyObject **)&y, (void *)&array, dims, 1, descr) < 0) {
PyErr_SetString(PyExc_TypeError, "An error occurred in the `lpython` decorator: "
"Failed to create a C array");
return NULL;
}
s_array_y->data = array;
s_array_y->n_dims = 1;
s_array_y->dims[0].lower_bound = 0;
s_array_y->dims[0].length = dims[0];
s_array_y->is_allocated = false;
}
// Call the C function
_lpython_return_variable = fast_sum(n, s_array_x, s_array_y);
// Build and return the result as a Python object
return Py_BuildValue("d", _lpython_return_variable);
}
// Define the module's method table
static PyMethodDef fast_sum_module_methods[] = {
{"fast_sum", fast_sum_define_module, METH_VARARGS,
"Handle arguments & return variable and call the function"},
{NULL, NULL, 0, NULL}
};
// Define the module initialization function
static struct PyModuleDef fast_sum_module_def = {
PyModuleDef_HEAD_INIT,
"lpython_module_fast_sum",
"Shared library to use LPython generated functions",
-1,
fast_sum_module_methods
};
PyMODINIT_FUNC PyInit_lpython_module_fast_sum(void) {
PyObject* module;
// Create the module object
module = PyModule_Create(&fast_sum_module_def);
if (!module) {
return NULL;
}
return module;
} |
Let's rename |
Later, the
This will be in ASR, so the arguments "x" will be BindPython in fast_sum, but LPython in internal_fast_sum. |
C contents for --show-c#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include <Python.h>
#include <inttypes.h>
#include <numpy/ndarrayobject.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;
};
struct r64
{
double *data;
struct dimension_descriptor dims[32];
int32_t n_dims;
bool is_allocated;
};
// Implementations
void _xx_internal_multiply_xx(int32_t n, struct r64* x, struct r64* _lpython_return_variable)
{
int32_t __1_t;
int32_t __1_v;
int32_t i;
for (i=0; i<=n - 1; i++) {
x->data[(i - x->dims[0].lower_bound)] = x->data[(i - x->dims[0].lower_bound)]* 5.00000000000000000e+00;
}
__1_v = ((int32_t)x->dims[1-1].lower_bound);
for (__1_t=0; __1_t<=n + 0 - 1; __1_t++) {
_lpython_return_variable->data[(__1_t - _lpython_return_variable->dims[0].lower_bound)] = x->data[(__1_v - x->dims[0].lower_bound)];
__1_v = __1_v + 1;
}
return;
}
// Define the Python module and method mappings
static PyObject* multiply(PyObject* self, PyObject* args) {
// Initialize NumPy
import_array();
// Declare arguments and return variable
int32_t n;
PyArrayObject *x;
struct r64 *_lpython_return_variable = malloc(sizeof(struct r64));
// Parse the arguments from Python
if (!PyArg_ParseTuple(args, "iO", &n, &x)) {
PyErr_SetString(PyExc_TypeError, "An error occurred in the `lpython` decorator: "
"Failed to parse or receive arguments from Python");
return NULL;
}
// Fill array details for x
if (PyArray_NDIM(x) != 1) {
PyErr_SetString(PyExc_TypeError, "An error occurred in the `lpython` decorator: "
"Only 1 dimension array is supported for now.");
return NULL;
}
struct r64 *s_array_x = malloc(sizeof(struct r64));
{
double *array;
// Create C arrays from numpy objects:
PyArray_Descr *descr = PyArray_DescrFromType(PyArray_TYPE(x));
npy_intp dims[1];
if (PyArray_AsCArray((PyObject **)&x, (void *)&array, dims, 1, descr) < 0) {
PyErr_SetString(PyExc_TypeError, "An error occurred in the `lpython` decorator: "
"Failed to create a C array");
return NULL;
}
s_array_x->data = array;
s_array_x->n_dims = 1;
s_array_x->dims[0].lower_bound = 0;
s_array_x->dims[0].length = dims[0];
s_array_x->is_allocated = false;
}
// Fill _lpython_return_variable
_lpython_return_variable->data = malloc(n * sizeof(double));
_lpython_return_variable->n_dims = 1;
_lpython_return_variable->dims[0].lower_bound = 0;
_lpython_return_variable->dims[0].length = n;
_lpython_return_variable->is_allocated = false;
// Call the C function
_xx_internal_multiply_xx(n, s_array_x, _lpython_return_variable);
// Copy the array elements and return the result as a Python object
{
npy_intp dims[] = {n};
PyObject* numpy_array = PyArray_SimpleNewFromData(1, dims, NPY_DOUBLE,
_lpython_return_variable->data);
if (numpy_array == NULL) {
PyErr_SetString(PyExc_TypeError, "An error occurred in the `lpython` decorator: "
"Failed to create an array that was used as a return variable");
return NULL;
}
return numpy_array;
}
}
// Define the module's method table
static PyMethodDef multiply_module_methods[] = {
{"multiply", multiply, METH_VARARGS,
"Handle arguments & return variable and call the function"},
{NULL, NULL, 0, NULL}
};
// Define the module initialization function
static struct PyModuleDef multiply_module_def = {
PyModuleDef_HEAD_INIT,
"lpython_module_multiply",
"Shared library to use LPython generated functions",
-1,
multiply_module_methods
};
PyMODINIT_FUNC PyInit_lpython_module_multiply(void) {
PyObject* module;
// Create the module object
module = PyModule_Create(&multiply_module_def);
if (!module) {
return NULL;
}
return module;
}
void _xx_internal_multiply_x_xx(int32_t n, struct r64* x, int32_t __1x, int32_t __2x, struct r64* _lpython_return_variable)
{
int32_t __1_t;
int32_t __1_v;
int32_t i;
for (i=0; i<=n - 1; i++) {
x->data[(i - x->dims[0].lower_bound)] = x->data[(i - x->dims[0].lower_bound)]* 5.00000000000000000e+00;
}
__1_v = __1x;
for (__1_t=0; __1_t<=n + 0 - 1; __1_t++) {
_lpython_return_variable->data[(__1_t - _lpython_return_variable->dims[0].lower_bound)] = x->data[(__1_v - x->dims[0].lower_bound)];
__1_v = __1_v + 1;
}
return;
}
// Define the Python module and method mappings
static PyObject* multiply_x(PyObject* self, PyObject* args) {
// Initialize NumPy
import_array();
// Declare arguments and return variable
int32_t n;
PyArrayObject *x;
int32_t __1x;
int32_t __2x;
struct r64 *_lpython_return_variable = malloc(sizeof(struct r64));
// Parse the arguments from Python
if (!PyArg_ParseTuple(args, "iOii", &n, &x, &__1x, &__2x)) {
PyErr_SetString(PyExc_TypeError, "An error occurred in the `lpython` decorator: "
"Failed to parse or receive arguments from Python");
return NULL;
}
// Fill array details for x
if (PyArray_NDIM(x) != 1) {
PyErr_SetString(PyExc_TypeError, "An error occurred in the `lpython` decorator: "
"Only 1 dimension array is supported for now.");
return NULL;
}
struct r64 *s_array_x = malloc(sizeof(struct r64));
{
double *array;
// Create C arrays from numpy objects:
PyArray_Descr *descr = PyArray_DescrFromType(PyArray_TYPE(x));
npy_intp dims[1];
if (PyArray_AsCArray((PyObject **)&x, (void *)&array, dims, 1, descr) < 0) {
PyErr_SetString(PyExc_TypeError, "An error occurred in the `lpython` decorator: "
"Failed to create a C array");
return NULL;
}
s_array_x->data = array;
s_array_x->n_dims = 1;
s_array_x->dims[0].lower_bound = 0;
s_array_x->dims[0].length = dims[0];
s_array_x->is_allocated = false;
}
// Fill _lpython_return_variable
_lpython_return_variable->data = malloc(n * sizeof(double));
_lpython_return_variable->n_dims = 1;
_lpython_return_variable->dims[0].lower_bound = 0;
_lpython_return_variable->dims[0].length = n;
_lpython_return_variable->is_allocated = false;
// Call the C function
_xx_internal_multiply_x_xx(n, s_array_x, __1x, __2x, _lpython_return_variable);
// Copy the array elements and return the result as a Python object
{
npy_intp dims[] = {n};
PyObject* numpy_array = PyArray_SimpleNewFromData(1, dims, NPY_DOUBLE,
_lpython_return_variable->data);
if (numpy_array == NULL) {
PyErr_SetString(PyExc_TypeError, "An error occurred in the `lpython` decorator: "
"Failed to create an array that was used as a return variable");
return NULL;
}
return numpy_array;
}
}
// Define the module's method table
static PyMethodDef multiply_x_module_methods[] = {
{"multiply_x", multiply_x, METH_VARARGS,
"Handle arguments & return variable and call the function"},
{NULL, NULL, 0, NULL}
};
// Define the module initialization function
static struct PyModuleDef multiply_x_module_def = {
PyModuleDef_HEAD_INIT,
"lpython_module_multiply_x",
"Shared library to use LPython generated functions",
-1,
multiply_x_module_methods
};
PyMODINIT_FUNC PyInit_lpython_module_multiply_x(void) {
PyObject* module;
// Create the module object
module = PyModule_Create(&multiply_x_module_def);
if (!module) {
return NULL;
}
return module;
} |
This PR is ready for review! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that it looks good. I don't see any major issues.
Is this ready for a final review & merge?
Yup, this is ready for final review and merge! |
I will merge this and add more tests in a separate PR! |
The changes in the PR: #2012, alter the return type handling. So the seg fault. I will look into it and report back. |
lpython/src/libasr/pass/pass_utils.h Lines 670 to 672 in c459bb5
Maybe you could change the above as follows: if (ASRUtils::get_FunctionType(x)->m_abi == ASR::abiType::BindPython
&& ASRUtils::get_FunctionType(x)->m_deftype == ASR::deftypeType::Interface) {
return;
} I guess it might fix the issue you are facing. |
Ready |
Thank you @Shaikh-Ubaid, but I modified the code to use your changes! |
169bcab
to
e7c9a9c
Compare
eca1a9f
to
73e24ba
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this looks great.
Right now we use the C backend anyway, so this PR is an improvement.
To later use the LLVM backend, I think we need to refactor this into an ASR->ASR pass, see #1996 for details. This PR is the first step towards this (it moves the Python C/API logic from lpython.py into the C backend). The next step will be to lift it from the C backend into an ASR->ASR pass. Then LLVM should work out of the box.
Towards: #703