Skip to content

Commit

Permalink
Merge pull request lcompilers#2017 from Shaikh-Ubaid/pythoncall_arr_o…
Browse files Browse the repository at this point in the history
…ther_simple_types

PythonCall: Support arrays of other simple types
  • Loading branch information
certik authored Jun 23, 2023
2 parents 7a93cbd + a37e519 commit 62a217e
Show file tree
Hide file tree
Showing 9 changed files with 212 additions and 20 deletions.
1 change: 1 addition & 0 deletions integration_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,7 @@ RUN(NAME bindc_06 LABELS llvm c
RUN(NAME bindpy_01 LABELS cpython c_py ENABLE_CPYTHON NOFAST EXTRAFILES bindpy_01_module.py)
RUN(NAME bindpy_02 LABELS cpython c_py LINK_NUMPY EXTRAFILES bindpy_02_module.py)
RUN(NAME bindpy_03 LABELS cpython c_py LINK_NUMPY NOFAST EXTRAFILES bindpy_03_module.py)
RUN(NAME bindpy_04 LABELS cpython c_py LINK_NUMPY NOFAST EXTRAFILES bindpy_04_module.py)
RUN(NAME test_generics_01 LABELS cpython llvm c NOFAST)
RUN(NAME test_cmath LABELS cpython llvm c NOFAST)
RUN(NAME test_complex_01 LABELS cpython llvm c wasm wasm_x64)
Expand Down
150 changes: 150 additions & 0 deletions integration_tests/bindpy_04.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
from lpython import i1, i32, u32, f64, c64, pythoncall, Const, TypeVar
from numpy import empty, uint32, complex64

n = TypeVar("n")
m = TypeVar("m")

# Defining the pythoncall decorator functions
@pythoncall(module = "bindpy_04_module")
def get_uint_array_sum(n: i32, a: u32[:], b: u32[:]) -> u32[n]:
pass

@pythoncall(module = "bindpy_04_module")
def get_bool_array_or(n: i32, a: i1[:], b: i1[:]) -> i1[n]:
pass

@pythoncall(module = "bindpy_04_module")
def get_complex_array_product(n: i32, a: c64[:], b: c64[:]) -> c64[n]:
pass

@pythoncall(module = "bindpy_04_module")
def get_2D_uint_array_sum(n: i32, m: i32, a: u32[:,:], b: u32[:,:]) -> u32[n,m]:
pass

@pythoncall(module = "bindpy_04_module")
def get_2D_bool_array_and(n: i32, m: i32, a: i1[:,:], b: i1[:,:]) -> i1[n,m]:
pass

@pythoncall(module = "bindpy_04_module")
def get_2D_complex_array_sum(n: i32, m: i32, a: c64[:,:], b: c64[:,:]) -> c64[n,m]:
pass

# Unsigned Integers
def test_array_uints():
n: Const[i32] = 5
a: u32[n] = empty([n], dtype=uint32)
b: u32[n] = empty([n], dtype=uint32)

i: i32
for i in range(n):
a[i] = u32(i + 10)
b[i] = u32(i + 20)

c: u32[n] = get_uint_array_sum(n, a, b)
print(c)
for i in range(n):
assert c[i] == u32(i * 2 + 30)

def test_2D_array_uints():
n: Const[i32] = 3
m: Const[i32] = 4
a: u32[n, m] = empty([n, m], dtype=uint32)
b: u32[n, m] = empty([n, m], dtype=uint32)

i: i32
j: i32
for i in range(n):
for j in range(m):
a[i, j] = u32(i * 10 + j)
b[i, j] = u32(i * 20 + j)

c: u32[n, m] = get_2D_uint_array_sum(n, m, a, b)
print(c)
for i in range(n):
for j in range(m):
assert c[i, j] == u32(i * 30 + 2*j)

# Boolean
def test_array_bools():
n: Const[i32] = 5
a: i1[n] = empty([n], dtype=bool)
b: i1[n] = empty([n], dtype=bool)

i: i32
for i in range(n):
a[i] = bool(i % 2 == 0)
b[i] = bool(i % 3 == 0)

c: i1[n] = get_bool_array_or(n, a, b)
print(c)
for i in range(n):
assert c[i] == bool((i % 2 == 0) or (i % 3 == 0))

def test_2D_array_bools():
n: Const[i32] = 3
m: Const[i32] = 4
a: i1[n, m] = empty([n, m], dtype=bool)
b: i1[n, m] = empty([n, m], dtype=bool)

i: i32
j: i32
for i in range(n):
for j in range(m):
a[i, j] = bool(i % 2 == 0)
b[i, j] = bool(j % 2 == 0)

c: i1[n, m] = get_2D_bool_array_and(n, m, a, b)
print(c)
for i in range(n):
for j in range(m):
assert c[i, j] == bool((i % 2 == 0) and (j % 2 == 0))

# Complex
def test_array_complexes():
n: Const[i32] = 5
a: c64[n] = empty([n], dtype=complex64)
b: c64[n] = empty([n], dtype=complex64)

i: i32
for i in range(n):
a[i] = c64(complex(i, i + 10))
b[i] = c64(complex(i + 1, i + 11))

c: c64[n] = get_complex_array_product(n, a, b)
print(c)
for i in range(n):
p: f64 = f64(i)
q: f64 = f64(i + 10)
r: f64 = f64(i + 1)
s: f64 = f64(i + 11)
assert abs(c[i] - c64(complex((p*r - q*s), (p*s + q*r)))) <= 1e-5

def test_2D_array_complexes():
n: Const[i32] = 3
m: Const[i32] = 4
a: c64[n, m] = empty([n, m], dtype=complex64)
b: c64[n, m] = empty([n, m], dtype=complex64)

i: i32
j: i32
for i in range(n):
for j in range(m):
a[i, j] = c64(complex(i, 10*j))
b[i, j] = c64(complex(i + 1, 10 * (j + 1)))

c: c64[n, m] = get_2D_complex_array_sum(n, m, a, b)
print(c)
for i in range(n):
for j in range(m):
assert abs(c[i, j] - c64(complex(2*i + 1, 20*j + 10))) <= 1e-5

def main0():
test_array_uints()
test_array_bools()
test_array_complexes()

test_2D_array_uints()
test_2D_array_bools()
test_2D_array_complexes()

main0()
21 changes: 21 additions & 0 deletions integration_tests/bindpy_04_module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import numpy as np

def get_uint_array_sum(n, a, b):
return np.add(a, b)

def get_bool_array_or(n, a, b):
return np.logical_or(a, b)

def get_complex_array_product(n, a, b):
print(a, b)
print(np.multiply(a, b))
return np.multiply(a, b)

def get_2D_uint_array_sum(n, m, a, b):
return np.add(a, b)

def get_2D_bool_array_and(n, m, a, b):
return np.logical_and(a, b)

def get_2D_complex_array_sum(n, m, a, b):
return np.add(a, b)
2 changes: 1 addition & 1 deletion src/libasr/asr_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -1049,7 +1049,7 @@ static inline std::string get_type_code(const ASR::ttype_t *t, bool use_undersco
break;
}
case ASR::ttypeType::Logical: {
res = "bool";
res = "i1";
break;
}
case ASR::ttypeType::Character: {
Expand Down
5 changes: 2 additions & 3 deletions src/libasr/codegen/asr_to_c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -394,9 +394,8 @@ class ASRToCVisitor : public BaseCCPPVisitor<ASRToCVisitor>
std::string v_m_name = v.m_name;
sub = format_type_c("", type_name, v_m_name, use_ref, dummy);
} else if (ASRUtils::is_logical(*v_m_type)) {
bool is_fixed_size = true;
dims = convert_dims_c(n_dims, m_dims, v_m_type, is_fixed_size);
sub = format_type_c(dims, "bool", v.m_name, use_ref, dummy);
convert_variable_decl_util(v, is_array, declare_as_constant, use_ref, dummy,
force_declare, force_declare_name, n_dims, m_dims, v_m_type, dims, sub);
} else if (ASRUtils::is_character(*v_m_type)) {
bool is_fixed_size = true;
dims = convert_dims_c(n_dims, m_dims, v_m_type, is_fixed_size);
Expand Down
33 changes: 21 additions & 12 deletions src/libasr/codegen/asr_to_c_cpp.h
Original file line number Diff line number Diff line change
Expand Up @@ -395,26 +395,35 @@ R"(#include <stdio.h>
}
}
} else if (ASRUtils::is_logical(*return_var->m_type)) {
sub = "bool ";
if (is_array) {
sub = "struct i1* ";
} else {
sub = "bool ";
}
} else if (ASRUtils::is_character(*return_var->m_type)) {
if (gen_stdstring) {
sub = "std::string ";
} else {
sub = "char* ";
}
} else if (ASRUtils::is_complex(*return_var->m_type)) {
bool is_float = ASR::down_cast<ASR::Complex_t>(return_var->m_type)->m_kind == 4;
if (is_float) {
if (gen_stdcomplex) {
sub = "std::complex<float> ";
} else {
sub = "float complex ";
}
int kind = ASRUtils::extract_kind_from_ttype_t(return_var->m_type);
if (is_array) {
sub = "struct c" + std::to_string(kind * 8) + "* ";
} else {
if (gen_stdcomplex) {
sub = "std::complex<double> ";
bool is_float = kind == 4;
if (is_float) {
if (gen_stdcomplex) {
sub = "std::complex<float> ";
} else {
sub = "float complex ";
}
} else {
sub = "double complex ";
if (gen_stdcomplex) {
sub = "std::complex<double> ";
} else {
sub = "double complex ";
}
}
}
} else if (ASR::is_a<ASR::SymbolicExpression_t>(*return_var->m_type)) {
Expand Down Expand Up @@ -863,7 +872,7 @@ R"(#include <stdio.h>
ASR::is_a<ASR::Var_t>(*arr->m_dims[0].m_length)) {
// name() -> f64[n]: Extract `array_type` and `n`
std::string array_type
= CUtils::get_numpy_c_obj_type_conv_func_from_ttype_t(arr->m_type);
= BindPyUtils::get_numpy_c_obj_type_conv_func_from_ttype_t(arr->m_type);
std::string return_array_size = ASRUtils::EXPR2VAR(
arr->m_dims[0].m_length)->m_name;
fill_return_details += R"(
Expand Down
10 changes: 10 additions & 0 deletions src/libasr/codegen/c_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -1782,6 +1782,16 @@ namespace BindPyUtils {
type_src = "NPY_STRING";
break;
}
case ASR::ttypeType::Complex: {
switch (kind)
{
case 4: type_src = "NPY_COMPLEX64"; break;
case 8: type_src = "NPY_COMPLEX128"; break;
default:
throw CodeGenError("get_numpy_c_obj_type_conv_func_from_ttype_t: Unsupported kind in complex type");
}
break;
}
default: {
throw CodeGenError("get_numpy_c_obj_type_conv_func_from_ttype_t: Type " + ASRUtils::type_to_str_python(t) + " not supported yet.");
}
Expand Down
8 changes: 4 additions & 4 deletions src/lpython/semantics/python_ast_to_asr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1076,7 +1076,7 @@ class CommonVisitor : public AST::BaseVisitor<Struct> {
} else if (var_annotation == "str") {
type = ASRUtils::TYPE(ASR::make_Character_t(al, loc, 1, -2, nullptr));
type = ASRUtils::make_Array_t_util(al, loc, type, dims.p, dims.size());
} else if (var_annotation == "bool") {
} else if (var_annotation == "bool" || var_annotation == "i1") {
type = ASRUtils::TYPE(ASR::make_Logical_t(al, loc, 4));
type = ASRUtils::make_Array_t_util(al, loc, type, dims.p, dims.size());
} else if (var_annotation == "CPtr") {
Expand Down Expand Up @@ -4446,11 +4446,11 @@ class SymbolTableVisitor : public CommonVisitor<SymbolTableVisitor> {
if (AST::is_a<AST::Name_t>(*x.m_targets[0])) {
std::string tvar_name = AST::down_cast<AST::Name_t>(x.m_targets[0])->m_id;
// Check if the type variable name is a reserved type keyword
const char* type_list[14]
const char* type_list[15]
= { "list", "set", "dict", "tuple",
"i8", "i16", "i32", "i64", "f32",
"f64", "c32", "c64", "str", "bool"};
for (int i = 0; i < 14; i++) {
"f64", "c32", "c64", "str", "bool", "i1"};
for (int i = 0; i < 15; i++) {
if (strcmp(s2c(al, tvar_name), type_list[i]) == 0) {
throw SemanticError(tvar_name + " is a reserved type, consider a different type variable name",
x.base.base.loc);
Expand Down
2 changes: 2 additions & 0 deletions src/runtime/lpython/lpython.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# data-types

type_to_convert_func = {
"i1": bool,
"i8": int,
"i16": int,
"i32": int,
Expand Down Expand Up @@ -75,6 +76,7 @@ def __init__(self, type, dims):
self._type = type
self._dims = dims

i1 = Type("i1")
i8 = Type("i8")
i16 = Type("i16")
i32 = Type("i32")
Expand Down

0 comments on commit 62a217e

Please sign in to comment.