From 85eca031a569255bf01562427677e64a8cd1a3ab Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 17 Aug 2022 20:36:36 +0100 Subject: [PATCH 001/193] added kernel class and cudaext.py --- pyccel/ast/cudaext.py | 217 +++++++++++++++ pyccel/codegen/printing/ccudacode.py | 369 +++++++++++++++++++++++++ pyccel/naming/ccudanameclashchecker.py | 61 ++++ samples/test/test.py | 21 ++ samples/test/tt.cu | 20 ++ 5 files changed, 688 insertions(+) create mode 100644 pyccel/ast/cudaext.py create mode 100644 pyccel/codegen/printing/ccudacode.py create mode 100644 pyccel/naming/ccudanameclashchecker.py create mode 100644 samples/test/test.py create mode 100644 samples/test/tt.cu diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py new file mode 100644 index 0000000000..68701f3667 --- /dev/null +++ b/pyccel/ast/cudaext.py @@ -0,0 +1,217 @@ +from functools import reduce +import operator + +import numpy + +from pyccel.errors.errors import Errors +from pyccel.errors.messages import WRONG_LINSPACE_ENDPOINT, NON_LITERAL_KEEP_DIMS, NON_LITERAL_AXIS + +from pyccel.utilities.stage import PyccelStage + +from .basic import PyccelAstNode +from .builtins import (PythonInt, PythonBool, PythonFloat, PythonTuple, + PythonComplex, PythonReal, PythonImag, PythonList, + PythonType, PythonConjugate) + +from .core import process_shape, Module, Import, PyccelFunctionDef + +from .datatypes import (dtype_and_precision_registry as dtype_registry, + default_precision, datatype, NativeInteger, + NativeFloat, NativeComplex, NativeBool, str_dtype, + NativeNumeric) + +from .internals import PyccelInternalFunction, Slice, max_precision, get_final_precision +from .internals import PyccelArraySize + +from .literals import LiteralInteger, LiteralFloat, LiteralComplex, convert_to_literal +from .literals import LiteralTrue, LiteralFalse +from .literals import Nil +from .mathext import MathCeil +from .operators import broadcast, PyccelMinus, PyccelDiv +from .variable import (Variable, Constant, HomogeneousTupleVariable) + +from .numpyext import process_dtype + +#============================================================================== +__all__ = ( + 'CudaArray', + 'CudaMalloc' +) + + +#------------------------------------------------------------------------------ +# cuda_constants = { +# 'pi': Constant('float', 'pi', value=cuda.), +# } +#============================================================================== +class CudaMemCopy(): + """Represents a call to cuda malloc for code generation. + + arg : list , tuple , PythonTuple, List, Variable + """ + def __init__(self, x, size): + self._shape = () + self._rank = 0 + self._dtype = x.dtype + self._precision = x.precision + + @property + def dest(self): + return self._args[0] + @property + def src(self): + return self._args[1] + @property + def size(self): + return self._args[2] + @property + def copy_mode(self): + return self._args[3] + + +#============================================================================== +class CudaNewArray(PyccelInternalFunction): + """ Class from which all Cuda functions which imply a call to Allocate + inherit + """ + + #-------------------------------------------------------------------------- + @staticmethod + def _process_order(order): + + if not order: + return None + + order = str(order).strip('\'"') + if order not in ('C', 'F'): + raise ValueError('unrecognized order = {}'.format(order)) + return order + +#============================================================================== +class CudaMalloc(CudaNewArray): + """Represents a call to cuda malloc for code generation. + + arg : str + """ + # _attribute_nodes = ('_alloct',) + def __init__(self, size, alloct, dtype): + # Verify dtype and get precision + dtype, prec = process_dtype(dtype) + self._shape = process_shape(size) + self._alloct = alloct + self._rank = len(size) + self._dtype = dtype + self._precision = prec + super().__init__() + + @property + def shape(self): + return self._shape + @property + def dtype(self): + return self._dtype + @property + def precision(self): + return self._precision + +#============================================================================== +class CudaArray(CudaNewArray): + """ + Represents a call to cuda.array for code generation. + + arg : list, tuple, PythonList + + """ + __slots__ = ('_arg','_dtype','_precision','_shape','_rank','_order') + _attribute_nodes = ('_arg',) + name = 'array' + + def __init__(self, arg, dtype=None, order='C'): + + if not isinstance(arg, (PythonTuple, PythonList, Variable)): + raise TypeError('Unknown type of %s.' % type(arg)) + + is_homogeneous_tuple = isinstance(arg, (PythonTuple, PythonList, HomogeneousTupleVariable)) and arg.is_homogeneous + is_array = isinstance(arg, Variable) and arg.is_ndarray + + # TODO: treat inhomogenous lists and tuples when they have mixed ordering + if not (is_homogeneous_tuple or is_array): + raise TypeError('we only accept homogeneous arguments') + + # Verify dtype and get precision + if dtype is None: + dtype = arg.dtype + dtype, prec = process_dtype(dtype) + # ... Determine ordering + order = str(order).strip("\'") + + if order not in ('K', 'A', 'C', 'F'): + raise ValueError("Cannot recognize '{:s}' order".format(order)) + + # TODO [YG, 18.02.2020]: set correct order based on input array + if order in ('K', 'A'): + order = 'C' + # ... + self._arg = arg + self._shape = process_shape(arg.shape) + self._rank = len(self._shape) + self._dtype = dtype + self._order = order + self._precision = prec + super().__init__() + + def __str__(self): + return str(self.arg) + + @property + def arg(self): + return self._arg + +class CudaDeviceSynchronize(PyccelAstNode): + "Represents a call to Cuda.deviceSynchronize for code generation." + _attribute_nodes = () + pass + +class CudaInternalVar(PyccelAstNode): + _attribute_nodes = ('_dim',) + + def __init__(self, dim=None): + if not isinstance(dim, LiteralInteger): + raise TypeError("dimension need to be an integer") + if dim not in (0, 1, 2): + raise ValueError("dimension need to be 0, 1 or 2") + #... + self._dim = dim + self._shape = () + self._rank = 0 + self._dtype = dim.dtype + self._precision = dim.precision + self._order = None + super().__init__() + + @property + def dim(self): + return self._dim + +class CudaThreadIdx(CudaInternalVar) : pass +class CudaBlockDim(CudaInternalVar) : pass +class CudaBlockIdx(CudaInternalVar) : pass +class CudaGridDim(CudaInternalVar) : pass + + +cuda_funcs = { + 'cudaMalloc' : PyccelFunctionDef('cudaMalloc' , CudaMalloc), + # 'deviceSynchronize' : CudaDeviceSynchronize, + 'array' : PyccelFunctionDef('array' , CudaArray), + 'threadIdx' : PyccelFunctionDef('threadIdx' , CudaThreadIdx) + # 'blockDim' : CudaBlockDim, + # 'blockIdx' : CudaBlockIdx, + # 'gridDim' : CudaGridDim +} + +cuda_constants = { + +} +cuda_mod = Module('cuda', + variables = cuda_constants.values(), + funcs = cuda_funcs.values()) \ No newline at end of file diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py new file mode 100644 index 0000000000..0e806efcb1 --- /dev/null +++ b/pyccel/codegen/printing/ccudacode.py @@ -0,0 +1,369 @@ +# coding: utf-8 +#------------------------------------------------------------------------------------------# +# This file is part of Pyccel which is released under MIT License. See the LICENSE file or # +# go to https://github.com/pyccel/pyccel/blob/master/LICENSE for full license details. # +#------------------------------------------------------------------------------------------# +# pylint: disable=missing-function-docstring +import functools +from itertools import chain +import re + +from pyccel.ast.basic import ScopedNode + +from pyccel.ast.builtins import PythonRange, PythonComplex +from pyccel.ast.builtins import PythonPrint, PythonType +from pyccel.ast.builtins import PythonList, PythonTuple + +from pyccel.ast.core import Declare, For, CodeBlock +from pyccel.ast.core import FuncAddressDeclare, FunctionCall, FunctionCallArgument, FunctionDef +from pyccel.ast.core import Deallocate +from pyccel.ast.core import FunctionAddress, FunctionDefArgument +from pyccel.ast.core import Assign, Import, AugAssign, AliasAssign +from pyccel.ast.core import SeparatorComment +from pyccel.ast.core import Module, AsName + +from pyccel.ast.operators import PyccelAdd, PyccelMul, PyccelMinus, PyccelLt, PyccelGt +from pyccel.ast.operators import PyccelAssociativeParenthesis, PyccelMod +from pyccel.ast.operators import PyccelUnarySub, IfTernaryOperator + +from pyccel.ast.datatypes import NativeInteger, NativeBool, NativeComplex +from pyccel.ast.datatypes import NativeFloat, NativeTuple, datatype, default_precision + +from pyccel.ast.internals import Slice, PrecomputedCode, get_final_precision + +from pyccel.ast.literals import LiteralTrue, LiteralFalse, LiteralImaginaryUnit, LiteralFloat +from pyccel.ast.literals import LiteralString, LiteralInteger, Literal +from pyccel.ast.literals import Nil + +from pyccel.ast.mathext import math_constants + +from pyccel.ast.numpyext import NumpyFull, NumpyArray, NumpyArange +from pyccel.ast.numpyext import NumpyReal, NumpyImag, NumpyFloat + +from pyccel.ast.utilities import expand_to_loops + +from pyccel.ast.variable import IndexedElement +from pyccel.ast.variable import PyccelArraySize, Variable +from pyccel.ast.variable import DottedName +from pyccel.ast.variable import InhomogeneousTupleVariable, HomogeneousTupleVariable + +from pyccel.ast.c_concepts import ObjectAddress + +from pyccel.codegen.printing.ccode import CCodePrinter + +from pyccel.errors.errors import Errors +from pyccel.errors.messages import (PYCCEL_RESTRICTION_TODO, INCOMPATIBLE_TYPEVAR_TO_FUNC, + PYCCEL_RESTRICTION_IS_ISNOT, UNSUPPORTED_ARRAY_RANK) + + +errors = Errors() + +# TODO: add examples + +__all__ = ["CCudaCodePrinter", "ccudacode"] + +# dictionary mapping numpy function to (argument_conditions, C_function). +# Used in CCodePrinter._print_NumpyUfuncBase(self, expr) +numpy_ufunc_to_c_float = { + 'NumpyAbs' : 'fabs', + 'NumpyFabs' : 'fabs', + 'NumpyMin' : 'minval', + 'NumpyMax' : 'maxval', + 'NumpyFloor': 'floor', # TODO: might require special treatment with casting + # --- + 'NumpyExp' : 'exp', + 'NumpyLog' : 'log', + 'NumpySqrt': 'sqrt', + # --- + 'NumpySin' : 'sin', + 'NumpyCos' : 'cos', + 'NumpyTan' : 'tan', + 'NumpyArcsin' : 'asin', + 'NumpyArccos' : 'acos', + 'NumpyArctan' : 'atan', + 'NumpyArctan2': 'atan2', + 'NumpySinh' : 'sinh', + 'NumpyCosh' : 'cosh', + 'NumpyTanh' : 'tanh', + 'NumpyArcsinh': 'asinh', + 'NumpyArccosh': 'acosh', + 'NumpyArctanh': 'atanh', +} + +numpy_ufunc_to_c_complex = { + 'NumpyAbs' : 'cabs', + 'NumpyMin' : 'minval', + 'NumpyMax' : 'maxval', + # --- + 'NumpyExp' : 'cexp', + 'NumpyLog' : 'clog', + 'NumpySqrt': 'csqrt', + # --- + 'NumpySin' : 'csin', + 'NumpyCos' : 'ccos', + 'NumpyTan' : 'ctan', + 'NumpyArcsin' : 'casin', + 'NumpyArccos' : 'cacos', + 'NumpyArctan' : 'catan', + 'NumpySinh' : 'csinh', + 'NumpyCosh' : 'ccosh', + 'NumpyTanh' : 'ctanh', + 'NumpyArcsinh': 'casinh', + 'NumpyArccosh': 'cacosh', + 'NumpyArctanh': 'catanh', +} + +# dictionary mapping Math function to (argument_conditions, C_function). +# Used in CCodePrinter._print_MathFunctionBase(self, expr) +# Math function ref https://docs.python.org/3/library/math.html +math_function_to_c = { + # ---------- Number-theoretic and representation functions ------------ + 'MathCeil' : 'ceil', + # 'MathComb' : 'com' # TODO + 'MathCopysign': 'copysign', + 'MathFabs' : 'fabs', + 'MathFloor' : 'floor', + # 'MathFmod' : '???', # TODO + # 'MathRexp' : '???' TODO requires two output + # 'MathFsum' : '???', # TODO + # 'MathIsclose' : '???', # TODO + 'MathIsfinite': 'isfinite', # int isfinite(real-floating x); + 'MathIsinf' : 'isinf', # int isinf(real-floating x); + 'MathIsnan' : 'isnan', # int isnan(real-floating x); + # 'MathIsqrt' : '???' TODO + 'MathLdexp' : 'ldexp', + # 'MathModf' : '???' TODO return two value + # 'MathPerm' : '???' TODO + # 'MathProd' : '???' TODO + 'MathRemainder' : 'remainder', + 'MathTrunc' : 'trunc', + + # ----------------- Power and logarithmic functions ----------------------- + + 'MathExp' : 'exp', + 'MathExpm1' : 'expm1', + 'MathLog' : 'log', # take also an option arg [base] + 'MathLog1p' : 'log1p', + 'MathLog2' : 'log2', + 'MathLog10' : 'log10', + 'MathPow' : 'pow', + 'MathSqrt' : 'sqrt', + + # --------------------- Trigonometric functions --------------------------- + + 'MathAcos' : 'acos', + 'MathAsin' : 'asin', + 'MathAtan' : 'atan', + 'MathAtan2' : 'atan2', + 'MathCos' : 'cos', + # 'MathDist' : '???', TODO + 'MathHypot' : 'hypot', + 'MathSin' : 'sin', + 'MathTan' : 'tan', + + + # -------------------------- Hyperbolic functions ------------------------- + + 'MathAcosh' : 'acosh', + 'MathAsinh' : 'asinh', + 'MathAtanh' : 'atanh', + 'MathCosh' : 'cosh', + 'MathSinh' : 'sinh', + 'MathTanh' : 'tanh', + + # --------------------------- Special functions --------------------------- + + 'MathErf' : 'erf', + 'MathErfc' : 'erfc', + 'MathGamma' : 'tgamma', + 'MathLgamma' : 'lgamma', + + # --------------------------- internal functions -------------------------- + 'MathFactorial' : 'pyc_factorial', + 'MathGcd' : 'pyc_gcd', + 'MathDegrees' : 'pyc_degrees', + 'MathRadians' : 'pyc_radians', + 'MathLcm' : 'pyc_lcm', +} + +c_library_headers = ( + "complex", + "ctype", + "float", + "math", + "stdarg", + "stdbool", + "stddef", + "stdint", + "stdio", + "stdlib", + "string", + "tgmath", +) + +dtype_registry = {('float',8) : 'double', + ('float',4) : 'float', + ('complex',8) : 'double complex', + ('complex',4) : 'float complex', + ('int',4) : 'int32_t', + ('int',8) : 'int64_t', + ('int',2) : 'int16_t', + ('int',1) : 'int8_t', + ('bool',4) : 'bool'} + +ndarray_type_registry = { + ('float',8) : 'nd_double', + ('float',4) : 'nd_float', + ('complex',8) : 'nd_cdouble', + ('complex',4) : 'nd_cfloat', + ('int',8) : 'nd_int64', + ('int',4) : 'nd_int32', + ('int',2) : 'nd_int16', + ('int',1) : 'nd_int8', + ('bool',4) : 'nd_bool'} + +import_dict = {'omp_lib' : 'omp' } + +c_imports = {n : Import(n, Module(n, (), ())) for n in + ['stdlib', + 'math', + 'string', + 'ndarrays', + 'math', + 'complex', + 'stdint', + 'pyc_math_c', + 'stdio', + 'stdbool', + 'assert']} + +class CcudaCodePrinter(CCodePrinter): + """A printer to convert python expressions to strings of ccuda code""" + printmethod = "_ccudacode" + language = "ccuda" + + _default_settings = { + 'tabwidth': 4, + } + + def __init__(self, filename, prefix_module = None): + + errors.set_target(filename, 'file') + + super().__init__(filename) + self.prefix_module = prefix_module + self._additional_imports = {'stdlib':c_imports['stdlib']} + self._additional_code = '' + self._additional_args = [] + self._temporary_args = [] + self._current_module = None + self._in_header = False + # Dictionary linking optional variables to their + # temporary counterparts which provide allocated + # memory + # Key is optional variable + self._optional_partners = {} + + def function_signature(self, expr, print_arg_names = True): + """Extract from function definition all the information + (name, input, output) needed to create the signature + + Parameters + ---------- + expr : FunctionDef + the function defintion + + print_arg_names : Bool + default value True and False when we don't need to print + arguments names + + Return + ------ + String + Signature of the function + """ + + args = list(expr.arguments) + if len(expr.results) == 1: + ret_type = self.get_declare_type(expr.results[0]) + elif len(expr.results) > 1: + ret_type = self._print(datatype('int')) + ' ' + args += [FunctionDefArgument(a.clone(name = a.name, memory_handling ='alias')) for a in expr.results] + else: + ret_type = self._print(datatype('void')) + ' ' + name = expr.name + if not args: + arg_code = 'void' + else: + def get_var_arg(arg, var): + code = "const " * var.is_const + code += self.get_declare_type(var) + code += arg.name * print_arg_names + return code + + var_list = [a.var for a in args] + arg_code_list = [self.function_signature(var, False) if isinstance(var, FunctionAddress) else get_var_arg(arg, var) for arg, var in zip(args, var_list)] + arg_code = ', '.join(arg_code_list) + + cuda_deco = "__global__ " if 'kernel' in expr.decorators else '' + if isinstance(expr, FunctionAddress): + return '{}(*{})({})'.format(ret_type, name, arg_code) + else: + return '{0}{1}{2}({3})'.format(cuda_deco, ret_type, name, arg_code) + + def _print_KernelCall(self, expr): + func = expr.funcdef + if func.is_inline: + return self._handle_inline_func_call(expr) + # Ensure the correct syntax is used for pointers + args = [] + for a, f in zip(expr.args, func.arguments): + a = a.value if a else Nil() + f = f.var + if self.stored_in_c_pointer(f): + if isinstance(a, Variable): + args.append(ObjectAddress(a)) + elif not self.stored_in_c_pointer(a): + tmp_var = self.scope.get_temporary_variable(f.dtype) + assign = Assign(tmp_var, a) + self._additional_code += self._print(assign) + args.append(ObjectAddress(tmp_var)) + else: + args.append(a) + else : + args.append(a) + + args += self._temporary_args + self._temporary_args = [] + args = ', '.join(['{}'.format(self._print(a)) for a in args]) + # TODO: need to raise error in semantic if we have result , kernel can't return + if not func.results: + return '{}<<<{},{}>>>({});\n'.format(func.name, expr.numBlocks, expr.tpblock,args) + + +def ccudacode(expr, filename, assign_to=None, **settings): + """Converts an expr to a string of ccuda code + + expr : Expr + A pyccel expression to be converted. + filename : str + The name of the file being translated. Used in error printing + assign_to : optional + When given, the argument is used as the name of the variable to which + the expression is assigned. Can be a string, ``Symbol``, + ``MatrixSymbol``, or ``Indexed`` type. This is helpful in case of + line-wrapping, or for expressions that generate multi-line statements. + precision : integer, optional + The precision for numbers such as pi [default=15]. + user_functions : dict, optional + A dictionary where keys are ``FunctionClass`` instances and values are + their string representations. Alternatively, the dictionary value can + be a list of tuples i.e. [(argument_test, cfunction_string)]. See below + for examples. + dereference : iterable, optional + An iterable of symbols that should be dereferenced in the printed code + expression. These would be values passed by address to the function. + For example, if ``dereference=[a]``, the resulting code would print + ``(*a)`` instead of ``a``. + """ + return CCudaCodePrinter(filename, **settings).doprint(expr, assign_to) diff --git a/pyccel/naming/ccudanameclashchecker.py b/pyccel/naming/ccudanameclashchecker.py new file mode 100644 index 0000000000..5198b49c96 --- /dev/null +++ b/pyccel/naming/ccudanameclashchecker.py @@ -0,0 +1,61 @@ +# coding: utf-8 +#------------------------------------------------------------------------------------------# +# This file is part of Pyccel which is released under MIT License. See the LICENSE file or # +# go to https://github.com/pyccel/pyccel/blob/master/LICENSE for full license details. # +#------------------------------------------------------------------------------------------# +""" +Handles name clash problems in Ccuda +""" +from pyccel.utilities.metaclasses import Singleton +from pyccel.utilities.strings import create_incremented_string + +class CNameClashChecker(metaclass = Singleton): + """ Class containing functions to help avoid problematic names in Ccuda + """ + # Keywords as mentioned on https://en.cppreference.com/w/c/keyword + keywords = set(['auto', 'break', 'case', 'char', 'const', + 'continue', 'default', 'do', 'double', 'else', 'enum', + 'extern', 'float', 'for', 'goto', 'if', 'inline', 'int', + 'long', 'register', 'restrict', 'return', 'short', 'signed', + 'sizeof', 'static', 'struct', 'switch', 'typedef', 'union', + 'unsigned', 'void', 'volatile', 'whie', '_Alignas', + '_Alignof', '_Atomic', '_Bool', '_Complex', 'Decimal128', + '_Decimal32', '_Decimal64', '_Generic', '_Imaginary', + '_Noreturn', '_Static_assert', '_Thread_local', 't_ndarray', + 'array_create', 'new_slice', 'array_slicing', 'alias_assign', + 'transpose_alias_assign', 'array_fill', 't_slice', + 'GET_INDEX_EXP1', 'GET_INDEX_EXP2', 'GET_INDEX_EXP2', + 'GET_INDEX_EXP3', 'GET_INDEX_EXP4', 'GET_INDEX_EXP5', + 'GET_INDEX_EXP6', 'GET_INDEX_EXP7', 'GET_INDEX_EXP8', + 'GET_INDEX_EXP9', 'GET_INDEX_EXP10', 'GET_INDEX_EXP11', + 'GET_INDEX_EXP12', 'GET_INDEX_EXP13', 'GET_INDEX_EXP14', + 'GET_INDEX_EXP15', 'NUM_ARGS_H1', 'NUM_ARGS', + 'GET_INDEX_FUNC_H2', 'GET_INDEX_FUNC', 'GET_INDEX', + 'INDEX', 'GET_ELEMENT', 'free_array', 'free_pointer', + 'get_index', 'numpy_to_ndarray_strides', + 'numpy_to_ndarray_shape', 'get_size']) + + def has_clash(self, name, symbols): + """ Indicate whether the proposed name causes any clashes + """ + return any(name == k for k in self.keywords) or \ + any(name == s for s in symbols) + + def get_collisionless_name(self, name, symbols): + """ Get the name that will be used in the fortran code + """ + if len(name)>4 and all(name[i] == '_' for i in (0,1,-1,-2)): + # Ignore magic methods + return name + if name[0] == '_': + name = 'private'+name + prefix = name + coll_symbols = self.keywords.copy() + coll_symbols.update(s.lower() for s in symbols) + if prefix in coll_symbols: + counter = 1 + new_name, counter = create_incremented_string(coll_symbols, + prefix = prefix, counter = counter) + name = name+new_name[-5:] + return name + diff --git a/samples/test/test.py b/samples/test/test.py new file mode 100644 index 0000000000..3ded25b6f1 --- /dev/null +++ b/samples/test/test.py @@ -0,0 +1,21 @@ +# from numpy import array +from pyccel.decorators import kernel +from pyccel import cuda +from numpy import array as narray +# from pyccel.stdlib.internal import PyccelthreadIdx + + + +@kernel +@types('int[:]') +def func(a): + # i = cuda.threadIdx(0) + """ + test test + """ + print("Hello World!") + +if __name__ == "__main__": + b = narray([1, 2, 3], dtype='int', order='C') + a = cuda.array([1, 2, 3], dtype='int', order='C') + func[1, 2](a) \ No newline at end of file diff --git a/samples/test/tt.cu b/samples/test/tt.cu new file mode 100644 index 0000000000..d300c3ac6c --- /dev/null +++ b/samples/test/tt.cu @@ -0,0 +1,20 @@ +#include +#include + +__global__ void func(int *p) +{ + p[0] = 5; + printf("%s\n", "Hello World!"); +} + +int main() +{ + int *p1; + + cudaMallocManaged(&p1, 3*sizeof(int)); + p1[0] = 0; + func<<<1,2>>>(p1); + cudaDeviceSynchronize(); + printf("%d\n", p1[0]); + return 0; +} \ No newline at end of file From 42d695a7569f6bac726b9a2ca28958e7f164dc49 Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 17 Aug 2022 20:37:28 +0100 Subject: [PATCH 002/193] added kernel class and cudaext.py --- pyccel/ast/core.py | 54 ++++++++++++++++++ pyccel/ast/utilities.py | 5 +- pyccel/codegen/codegen.py | 8 ++- pyccel/codegen/pipeline.py | 3 + pyccel/commands/console.py | 2 +- pyccel/compilers/default_compilers.py | 14 ++++- pyccel/decorators.py | 4 ++ pyccel/naming/__init__.py | 1 + pyccel/parser/base.py | 1 + pyccel/parser/semantic.py | 79 +++++++++++++++++++++++++-- pyccel/parser/syntactic.py | 6 +- 11 files changed, 166 insertions(+), 11 deletions(-) diff --git a/pyccel/ast/core.py b/pyccel/ast/core.py index 38ab95eaa6..b35a0fb836 100644 --- a/pyccel/ast/core.py +++ b/pyccel/ast/core.py @@ -2095,6 +2095,60 @@ def prefix(self): """ return self._prefix +class KernelCall(FunctionCall): + """ + Represents a kernel call + (e.g. a[c, b]()) + + + + Parameters + ========== + func : FunctionDef + The definition of the function being called + args : tuple + The arguments being passed to the function + + numBlocks : NativeInteger + The number of blocks + tpblock : NativeInteger + The number of threads per block + """ + __slots__ = ('_numBlocks','_tpblock', '_func', '_args') + _attribute_nodes = (*FunctionCall._attribute_nodes, '_numBlocks', '_tpblock') + + def __init__(self, func, args, numBlocks, tpblock, current_function=None): + + self._func = func + self._args = args + self._numBlocks = numBlocks + self._tpblock = tpblock + super().__init__(func, args, current_function) + + @property + def func(self): + """ The number of blocks in which the kernel will run + """ + return self._func + + @property + def args(self): + """ The number of blocks in which the kernel will run + """ + return self._args + + @property + def numBlocks(self): + """ The number of blocks in which the kernel will run + """ + return self._numBlocks + + @property + def tpblock(self): + """ The number of threads in each block + """ + return self._tpblock + class Return(Basic): """Represents a function return in the code. diff --git a/pyccel/ast/utilities.py b/pyccel/ast/utilities.py index a750ff578f..fa83400efe 100644 --- a/pyccel/ast/utilities.py +++ b/pyccel/ast/utilities.py @@ -25,6 +25,7 @@ from .literals import LiteralInteger, Nil from .mathext import math_mod +from .cudaext import cuda_mod from .numpyext import (NumpyEmpty, NumpyArray, numpy_mod, NumpyTranspose, NumpyLinspace) from .operators import PyccelAdd, PyccelMul, PyccelIs, PyccelArithmeticOperator @@ -76,7 +77,9 @@ def builtin_function(expr, args=None): decorators_mod = Module('decorators',(), funcs = [PyccelFunctionDef(d, PyccelInternalFunction) for d in pyccel_decorators.__all__]) pyccel_mod = Module('pyccel',(),(), - imports = [Import('decorators', decorators_mod)]) + imports = [Import('decorators', decorators_mod), + Import('cuda', AsName(cuda_mod,'cuda')), + ]) # TODO add documentation builtin_import_registery = Module('__main__', diff --git a/pyccel/codegen/codegen.py b/pyccel/codegen/codegen.py index 1a5568bf96..eba821f777 100644 --- a/pyccel/codegen/codegen.py +++ b/pyccel/codegen/codegen.py @@ -8,17 +8,19 @@ from pyccel.codegen.printing.fcode import FCodePrinter from pyccel.codegen.printing.ccode import CCodePrinter +from pyccel.codegen.printing.ccudacode import CcudaCodePrinter from pyccel.codegen.printing.pycode import PythonCodePrinter from pyccel.ast.core import FunctionDef, Interface, ModuleHeader from pyccel.errors.errors import Errors from pyccel.utilities.stage import PyccelStage -_extension_registry = {'fortran': 'f90', 'c':'c', 'python':'py'} -_header_extension_registry = {'fortran': None, 'c':'h', 'python':None} +_extension_registry = {'fortran': 'f90', 'c':'c', 'python':'py', 'ccuda': 'cu'} +_header_extension_registry = {'fortran': None, 'c':'h', 'python':None, 'ccuda': 'h'} printer_registry = { 'fortran':FCodePrinter, 'c':CCodePrinter, + 'ccuda':CcudaCodePrinter, 'python':PythonCodePrinter } @@ -140,7 +142,7 @@ def set_printer(self, **settings): language = settings.pop('language', 'fortran') # Set language - if not language in ['fortran', 'c', 'python']: + if not language in ['fortran', 'c', 'python', 'ccuda']: raise ValueError('{} language is not available'.format(language)) self._language = language diff --git a/pyccel/codegen/pipeline.py b/pyccel/codegen/pipeline.py index 99208311f1..bf332dff5a 100644 --- a/pyccel/codegen/pipeline.py +++ b/pyccel/codegen/pipeline.py @@ -198,6 +198,9 @@ def handle_error(stage): if compiler is None: compiler = 'GNU' + # Choose cuda vendor + if language == 'ccuda': + compiler = 'nvidia' fflags = [] if fflags is None else fflags.split() wrapper_flags = [] if wrapper_flags is None else wrapper_flags.split() diff --git a/pyccel/commands/console.py b/pyccel/commands/console.py index 22e9f8afd9..e6a5841478 100644 --- a/pyccel/commands/console.py +++ b/pyccel/commands/console.py @@ -60,7 +60,7 @@ def pyccel(files=None, mpi=None, openmp=None, openacc=None, output_dir=None, com # ... backend compiler options group = parser.add_argument_group('Backend compiler options') - group.add_argument('--language', choices=('fortran', 'c', 'python'), help='Generated language') + group.add_argument('--language', choices=('fortran', 'c', 'python', 'ccuda'), help='Generated language') group.add_argument('--compiler', help='Compiler family or json file containing a compiler description {GNU,intel,PGI}') diff --git a/pyccel/compilers/default_compilers.py b/pyccel/compilers/default_compilers.py index 82b77f990a..fb850e60ab 100644 --- a/pyccel/compilers/default_compilers.py +++ b/pyccel/compilers/default_compilers.py @@ -170,6 +170,16 @@ 'family': 'nvidia', } +#------------------------------------------------------------ +nvcc_info = {'exec' : 'nvcc', + 'language': 'ccuda', + 'debug_flags': ("-g",), + 'release_flags': ("-O3",), + 'general_flags' : ('--compiler-options', '-fPIC',), + 'standard_flags' : ('-std=c99',), + 'family': 'nvidia', + } + #------------------------------------------------------------ def change_to_lib_flag(lib): """ @@ -240,6 +250,7 @@ def change_to_lib_flag(lib): pgfortran_info.update(python_info) nvc_info.update(python_info) nvfort_info.update(python_info) +nvcc_info.update(python_info) available_compilers = {('GNU', 'c') : gcc_info, ('GNU', 'fortran') : gfort_info, @@ -248,6 +259,7 @@ def change_to_lib_flag(lib): ('PGI', 'c') : pgcc_info, ('PGI', 'fortran') : pgfortran_info, ('nvidia', 'c') : nvc_info, - ('nvidia', 'fortran') : nvfort_info} + ('nvidia', 'fortran') : nvfort_info, + ('nvidia', 'ccuda') : nvcc_info} vendors = ('GNU','intel','PGI','nvidia') diff --git a/pyccel/decorators.py b/pyccel/decorators.py index 0bb87aca15..e85b2ebd75 100644 --- a/pyccel/decorators.py +++ b/pyccel/decorators.py @@ -21,6 +21,7 @@ 'sympy', 'template', 'types', + 'kernel', ) def lambdify(f): @@ -98,3 +99,6 @@ def allow_negative_index(f,*args): def identity(f): return f return identity + +def kernel(f): + return f \ No newline at end of file diff --git a/pyccel/naming/__init__.py b/pyccel/naming/__init__.py index a71d841c8e..24470d9f75 100644 --- a/pyccel/naming/__init__.py +++ b/pyccel/naming/__init__.py @@ -13,4 +13,5 @@ name_clash_checkers = {'fortran':FortranNameClashChecker(), 'c':CNameClashChecker(), + 'ccuda':CNameClashChecker(), 'python':PythonNameClashChecker()} diff --git a/pyccel/parser/base.py b/pyccel/parser/base.py index 63e7fc1330..936e2bd425 100644 --- a/pyccel/parser/base.py +++ b/pyccel/parser/base.py @@ -102,6 +102,7 @@ def get_filename_from_import(module,input_folder=''): filename_pyh = os.path.join(package_dir, filename_pyh) filename_py = os.path.join(package_dir, filename_py) + print("hemS", filename_py, filename_pyh) if os.path.isfile(filename_pyh): return filename_pyh elif os.path.isfile(filename_py): diff --git a/pyccel/parser/semantic.py b/pyccel/parser/semantic.py index 3d6c5518c2..8344d9b940 100644 --- a/pyccel/parser/semantic.py +++ b/pyccel/parser/semantic.py @@ -39,6 +39,7 @@ from pyccel.ast.core import ConstructorCall, InlineFunctionDef from pyccel.ast.core import FunctionDef, Interface, FunctionAddress, FunctionCall, FunctionCallArgument from pyccel.ast.core import DottedFunctionCall +from pyccel.ast.core import KernelCall from pyccel.ast.core import ClassDef from pyccel.ast.core import For from pyccel.ast.core import Module @@ -212,7 +213,6 @@ def __init__(self, inputs, **kwargs): # self._code = parser._code # ... - self.annotate() # ... @@ -419,7 +419,6 @@ def _infere_type(self, expr, **settings): d_var = {} # TODO improve => put settings as attribut of Parser - if expr in (PythonInt, PythonFloat, PythonComplex, PythonBool, NumpyBool, NumpyInt, NumpyInt8, NumpyInt16, NumpyInt32, NumpyInt64, NumpyComplex, NumpyComplex64, NumpyComplex128, NumpyFloat, NumpyFloat64, NumpyFloat32): @@ -795,6 +794,7 @@ def _handle_function(self, expr, func, args, **settings): ======= new_expr : FunctionCall or PyccelInternalFunction """ + print('handle func -- ', func, type(func)) if isinstance(func, PyccelFunctionDef): func = func.cls_name args, kwargs = split_positional_keyword_arguments(*args) @@ -827,6 +827,58 @@ def _handle_function(self, expr, func, args, **settings): self._check_argument_compatibility(new_expr.args, func.arguments, expr, func.is_elemental) return new_expr + + def _handle_kernel(self, expr, func, args, **settings): + """ + Create a FunctionCall or an instance of a PyccelInternalFunction + from the function information and arguments + + Parameters + ========== + expr : PyccelAstNode + The expression where this call is found (used for error output) + func : FunctionDef instance, Interface instance or PyccelInternalFunction type + The function being called + args : tuple + The arguments passed to the function + + Returns + ======= + new_expr : FunctionCall or PyccelInternalFunction + """ + if isinstance(func, PyccelFunctionDef): + func = func.cls_name + args, kwargs = split_positional_keyword_arguments(*args) + for a in args: + if getattr(a,'dtype',None) == 'tuple': + self._infere_type(a, **settings) + for a in kwargs.values(): + if getattr(a,'dtype',None) == 'tuple': + self._infere_type(a, **settings) + + try: + new_expr = func(*args, **kwargs) + except TypeError: + errors.report(UNRECOGNISED_FUNCTION_CALL, + symbol = expr, + severity = 'fatal') + + return new_expr + else: + if isinstance(func, FunctionDef) and len(args) > len(func.arguments): + errors.report("Too many arguments passed in function call", + symbol = expr, + severity='fatal') + # TODO : type check the NUMBER OF BLOCKS 'numBlocks' and threads per block 'tpblock' + new_expr = KernelCall(func, args, expr.numBlocks, expr.tpblock, self._current_function) + if None in new_expr.args: + errors.report("Too few arguments passed in function call", + symbol = expr, + severity='error') + elif isinstance(func, FunctionDef): + self._check_argument_compatibility(new_expr.args, func.arguments, + expr, func.is_elemental) + return new_expr def _create_variable(self, name, dtype, rhs, d_lhs): """ @@ -1789,7 +1841,7 @@ def _visit_PyccelSymbol(self, expr, **settings): def _visit_DottedName(self, expr, **settings): - + var = self.check_for_variable(_get_name(expr)) if var: return var @@ -1854,7 +1906,11 @@ def _visit_DottedName(self, expr, **settings): symbol=expr, bounding_box=(self._current_fst_node.lineno, self._current_fst_node.col_offset), severity='fatal') - + from pyccel.ast.cudaext import CudaThreadIdx, CudaBlockDim, CudaBlockIdx, CudaGridDim + # if first isinstance(): + if (first in (CudaThreadIdx, CudaBlockDim, CudaBlockIdx, CudaGridDim)): + dim = {'x':0, 'y':1, 'z':2} + return first(LiteralInteger(dim[rhs_name])) d_var = self._infere_type(first) if d_var.get('cls_base', None) is None: errors.report(f'Attribute {rhs_name} not found', @@ -1944,6 +2000,19 @@ def _visit_DottedName(self, expr, **settings): bounding_box=(self._current_fst_node.lineno, self._current_fst_node.col_offset), severity='fatal') + def _visit_KernelCall(self, expr, **settings): + name = expr.funcdef + try: + name = self.scope.get_expected_name(name) + except RuntimeError: + pass + func = self.scope.find(name, 'functions') + + args = self._handle_function_args(expr.args, **settings) + + return self._handle_kernel(expr, func, args, **settings) + # return KernelCall(func, expr.args, expr.numBlocks, expr.tpblock) + def _visit_PyccelOperator(self, expr, **settings): args = [self._visit(a, **settings) for a in expr.args] return self._create_PyccelOperator(expr, args) @@ -2153,6 +2222,7 @@ def _visit_FunctionCall(self, expr, **settings): # first we check if it is a macro, in this case, we will create # an appropriate FunctionCall + print('func call -- ', expr, type(expr)) macro = self.scope.find(name, 'macros') if macro is not None: func = macro.master.funcdef @@ -2160,6 +2230,7 @@ def _visit_FunctionCall(self, expr, **settings): args = macro.apply(args) else: func = self.scope.find(name, 'functions') + print('func -- ', func, type(func)) if func is None: return errors.report(UNDEFINED_FUNCTION, symbol=name, bounding_box=(self._current_fst_node.lineno, self._current_fst_node.col_offset), diff --git a/pyccel/parser/syntactic.py b/pyccel/parser/syntactic.py index 5f082cef08..ff89b073a6 100644 --- a/pyccel/parser/syntactic.py +++ b/pyccel/parser/syntactic.py @@ -19,7 +19,7 @@ from pyccel.ast.basic import Basic -from pyccel.ast.core import FunctionCall, FunctionCallArgument +from pyccel.ast.core import FunctionCall, FunctionCallArgument, KernelCall from pyccel.ast.core import Module from pyccel.ast.core import Assign from pyccel.ast.core import AugAssign @@ -899,6 +899,10 @@ def _visit_Call(self, stmt): elif isinstance(func, DottedName): func_attr = FunctionCall(func.name[-1], args) func = DottedName(*func.name[:-1], func_attr) + elif isinstance(func, IndexedElement): + if len(func.indices) != 2: + raise NotImplementedError + func = KernelCall(func.base, args, func.indices[0], func.indices[1]) else: raise NotImplementedError(' Unknown function type {}'.format(str(type(func)))) From fdddcc3c23b008d20589f491762240723e0f00fd Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 17 Aug 2022 21:06:18 +0100 Subject: [PATCH 003/193] remove prints --- pyccel/parser/base.py | 1 - pyccel/parser/semantic.py | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/pyccel/parser/base.py b/pyccel/parser/base.py index 936e2bd425..63e7fc1330 100644 --- a/pyccel/parser/base.py +++ b/pyccel/parser/base.py @@ -102,7 +102,6 @@ def get_filename_from_import(module,input_folder=''): filename_pyh = os.path.join(package_dir, filename_pyh) filename_py = os.path.join(package_dir, filename_py) - print("hemS", filename_py, filename_pyh) if os.path.isfile(filename_pyh): return filename_pyh elif os.path.isfile(filename_py): diff --git a/pyccel/parser/semantic.py b/pyccel/parser/semantic.py index 8344d9b940..b458c35fae 100644 --- a/pyccel/parser/semantic.py +++ b/pyccel/parser/semantic.py @@ -794,7 +794,7 @@ def _handle_function(self, expr, func, args, **settings): ======= new_expr : FunctionCall or PyccelInternalFunction """ - print('handle func -- ', func, type(func)) + if isinstance(func, PyccelFunctionDef): func = func.cls_name args, kwargs = split_positional_keyword_arguments(*args) @@ -2222,7 +2222,6 @@ def _visit_FunctionCall(self, expr, **settings): # first we check if it is a macro, in this case, we will create # an appropriate FunctionCall - print('func call -- ', expr, type(expr)) macro = self.scope.find(name, 'macros') if macro is not None: func = macro.master.funcdef @@ -2230,7 +2229,6 @@ def _visit_FunctionCall(self, expr, **settings): args = macro.apply(args) else: func = self.scope.find(name, 'functions') - print('func -- ', func, type(func)) if func is None: return errors.report(UNDEFINED_FUNCTION, symbol=name, bounding_box=(self._current_fst_node.lineno, self._current_fst_node.col_offset), From f07761b4cd862c139b36d020885753aca4f07ea3 Mon Sep 17 00:00:00 2001 From: bauom Date: Thu, 18 Aug 2022 14:36:43 +0000 Subject: [PATCH 004/193] added printers for arraycreation and thread indexing && added memory_location attr to variable --- pyccel/ast/class_defs.py | 27 +++++ pyccel/ast/cudaext.py | 60 ++++------ pyccel/ast/variable.py | 43 +++++++- pyccel/codegen/printing/ccudacode.py | 110 +++++++++++++++++++ pyccel/parser/semantic.py | 15 ++- pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu | 94 ++++++++++++++++ pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h | 11 ++ 7 files changed, 320 insertions(+), 40 deletions(-) create mode 100644 pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu create mode 100644 pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h diff --git a/pyccel/ast/class_defs.py b/pyccel/ast/class_defs.py index 1a08f2ab0a..48157a1bea 100644 --- a/pyccel/ast/class_defs.py +++ b/pyccel/ast/class_defs.py @@ -160,6 +160,33 @@ #======================================================================================= +CudaArrayClass = ClassDef('cuda.ndarray', + methods=[ + FunctionDef('shape',[],[],body=[], + decorators={'property':'property', 'numpy_wrapper':Shape})]) + # FunctionDef('size',[],[],body=[], + # decorators={'property':'property', 'numpy_wrapper':NumpyArraySize}), + # FunctionDef('T',[],[],body=[], + # decorators={'property':'property', 'numpy_wrapper':NumpyTranspose}), + # FunctionDef('transpose',[],[],body=[], + # decorators={'numpy_wrapper':NumpyTranspose}), + # FunctionDef('sum',[],[],body=[], + # decorators={'numpy_wrapper':NumpySum}), + # FunctionDef('min',[],[],body=[], + # decorators={'numpy_wrapper':NumpyAmin}), + # FunctionDef('max',[],[],body=[], + # decorators={'numpy_wrapper':NumpyAmax}), + # FunctionDef('imag',[],[],body=[], + # decorators={'property':'property', 'numpy_wrapper':NumpyImag}), + # FunctionDef('real',[],[],body=[], + # decorators={'property':'property', 'numpy_wrapper':NumpyReal}), + # FunctionDef('conj',[],[],body=[], + # decorators={'numpy_wrapper':NumpyConjugate}), + # FunctionDef('conjugate',[],[],body=[], + # decorators={'numpy_wrapper':NumpyConjugate})]) + +#======================================================================================= + literal_classes = { NativeBool() : BooleanClass, NativeInteger() : IntegerClass, diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index 68701f3667..e37052620f 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -30,19 +30,21 @@ from .operators import broadcast, PyccelMinus, PyccelDiv from .variable import (Variable, Constant, HomogeneousTupleVariable) -from .numpyext import process_dtype +from .numpyext import process_dtype, NumpyNewArray #============================================================================== __all__ = ( + 'CudaMemCopy', + 'CudaNewArray', 'CudaArray', - 'CudaMalloc' + 'CudaDeviceSynchronize', + 'CudaInternalVar', + 'CudaThreadIdx', + 'CudaBlockDim', + 'CudaBlockIdx', + 'CudaGridDim' ) - -#------------------------------------------------------------------------------ -# cuda_constants = { -# 'pi': Constant('float', 'pi', value=cuda.), -# } #============================================================================== class CudaMemCopy(): """Represents a call to cuda malloc for code generation. @@ -88,31 +90,6 @@ def _process_order(order): return order #============================================================================== -class CudaMalloc(CudaNewArray): - """Represents a call to cuda malloc for code generation. - - arg : str - """ - # _attribute_nodes = ('_alloct',) - def __init__(self, size, alloct, dtype): - # Verify dtype and get precision - dtype, prec = process_dtype(dtype) - self._shape = process_shape(size) - self._alloct = alloct - self._rank = len(size) - self._dtype = dtype - self._precision = prec - super().__init__() - - @property - def shape(self): - return self._shape - @property - def dtype(self): - return self._dtype - @property - def precision(self): - return self._precision #============================================================================== class CudaArray(CudaNewArray): @@ -200,13 +177,20 @@ class CudaGridDim(CudaInternalVar) : pass cuda_funcs = { - 'cudaMalloc' : PyccelFunctionDef('cudaMalloc' , CudaMalloc), # 'deviceSynchronize' : CudaDeviceSynchronize, - 'array' : PyccelFunctionDef('array' , CudaArray), - 'threadIdx' : PyccelFunctionDef('threadIdx' , CudaThreadIdx) - # 'blockDim' : CudaBlockDim, - # 'blockIdx' : CudaBlockIdx, - # 'gridDim' : CudaGridDim + 'array' : PyccelFunctionDef('array' , CudaArray), + 'deviceSynchronize' : PyccelFunctionDef('deviceSynchronize' , CudaDeviceSynchronize), + 'threadIdx' : PyccelFunctionDef('threadIdx' , CudaThreadIdx), + 'blockDim' : PyccelFunctionDef('blockDim' , CudaBlockDim), + 'blockIdx' : PyccelFunctionDef('blockIdx' , CudaBlockIdx), + 'gridDim' : PyccelFunctionDef('gridDim' , CudaGridDim) +} + +cuda_Internal_Var = { + 'CudaThreadIdx' : 'threadIdx', + 'CudaBlockDim' : 'blockDim', + 'CudaBlockIdx' : 'blockIdx', + 'CudaGridDim' : 'gridDim' } cuda_constants = { diff --git a/pyccel/ast/variable.py b/pyccel/ast/variable.py index 5b762cdd77..cede4b33ae 100644 --- a/pyccel/ast/variable.py +++ b/pyccel/ast/variable.py @@ -54,6 +54,12 @@ class Variable(PyccelAstNode): 'stack' if memory should be allocated on the stack, represents stack arrays and scalars 'alias' if object allows access to memory stored in another variable [Default value: 'stack'] + + memory_location: str + 'host' the variable can only be accessed by the CPU + 'device' the variable can only be accessed by the GPU + 'managed' the variable can be accessed by CPU and GPU and is being managed by the Cuda API (memory transfer is being done implicitly) + [Default value: 'host'] is_target: bool if object is pointed to by another variable [Default value: False] @@ -100,7 +106,7 @@ class base if variable is an object or an object member [Default value: None] >>> Variable('int', DottedName('matrix', 'n_rows')) matrix.n_rows """ - __slots__ = ('_name', '_alloc_shape', '_memory_handling', '_is_const', + __slots__ = ('_name', '_alloc_shape', '_memory_handling', '_memory_location', '_is_const', '_is_target', '_is_optional', '_allows_negative_indexes', '_cls_base', '_is_argument', '_is_kwonly', '_is_temp','_dtype','_precision', '_rank','_shape','_order','_is_private') @@ -113,6 +119,7 @@ def __init__( *, rank=0, memory_handling='stack', + memory_location='host', is_const=False, is_target=False, is_optional=False, @@ -148,6 +155,10 @@ def __init__( raise ValueError("memory_handling must be 'heap', 'stack' or 'alias'") self._memory_handling = memory_handling + if memory_location not in ('host', 'device', 'managed'): + raise ValueError("memory_location must be 'host', 'device' or 'managed'") + self._memory_location = memory_location + if not isinstance(is_const, bool): raise TypeError('is_const must be a boolean.') self._is_const = is_const @@ -293,6 +304,36 @@ def on_stack(self): """ return self.memory_handling == 'stack' + @property + def memory_location(self): + """ Indicates whether a Variable has a dynamic size + """ + return self._memory_location + + @memory_location.setter + def memory_location(self, memory_location): + if memory_location not in ('host', 'device', 'managed'): + raise ValueError("memory_location must be 'host', 'device' or 'managed'") + self._memory_location = memory_location + + @property + def on_host(self): + """ Indicates if memory is only accessible by the CPU + """ + return self.memory_location == 'host' + + @property + def on_device(self): + """ Indicates if memory is only accessible by the GPU + """ + return self.memory_location == 'device' + + @property + def is_managed(self): + """ Indicates if memory is being managed by CUDA API + """ + return self.memory_location == 'managed' + @property def is_stack_array(self): """ Indicates if the variable is located on stack and is an array diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index 0e806efcb1..d626e7bf96 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -40,6 +40,8 @@ from pyccel.ast.numpyext import NumpyFull, NumpyArray, NumpyArange from pyccel.ast.numpyext import NumpyReal, NumpyImag, NumpyFloat +from pyccel.ast.cudaext import cuda_Internal_Var, CudaArray + from pyccel.ast.utilities import expand_to_loops from pyccel.ast.variable import IndexedElement @@ -229,6 +231,7 @@ 'math', 'string', 'ndarrays', + 'cuda_ndarrays', 'math', 'complex', 'stdint', @@ -311,6 +314,32 @@ def get_var_arg(arg, var): else: return '{0}{1}{2}({3})'.format(cuda_deco, ret_type, name, arg_code) + def _print_Allocate(self, expr): + free_code = '' + #free the array if its already allocated and checking if its not null if the status is unknown + if (expr.status == 'unknown'): + free_code = 'if (%s.shape != NULL)\n' % self._print(expr.variable.name) + free_code += "{{\n{}}}\n".format(self._print(Deallocate(expr.variable))) + elif (expr.status == 'allocated'): + free_code += self._print(Deallocate(expr.variable)) + shape = ", ".join(self._print(i) for i in expr.shape) + dtype = self._print(expr.variable.dtype) + dtype = self.find_in_ndarray_type_registry(dtype, expr.variable.precision) + shape_dtype = self.find_in_dtype_registry('int', 8) + shape_Assign = "("+ shape_dtype +"[]){" + shape + "}" + is_view = 'false' if expr.variable.on_heap else 'true' + if expr.variable.is_managed or expr.variable.on_device: + self.add_import(c_imports['cuda_ndarrays']) + alloc_code = "{} = cuda_array_create({}, {}, {}, {});\n".format( + expr.variable, len(expr.shape), shape_Assign, dtype, + is_view) + else: + self.add_import(c_imports['ndarrays']) + alloc_code = "{} = array_create({}, {}, {}, {});\n".format( + expr.variable, len(expr.shape), shape_Assign, dtype, + is_view) + return '{}{}'.format(free_code, alloc_code) + def _print_KernelCall(self, expr): func = expr.funcdef if func.is_inline: @@ -340,6 +369,87 @@ def _print_KernelCall(self, expr): if not func.results: return '{}<<<{},{}>>>({});\n'.format(func.name, expr.numBlocks, expr.tpblock,args) + def _print_Assign(self, expr): + prefix_code = '' + lhs = expr.lhs + rhs = expr.rhs + if isinstance(lhs, Variable) and lhs.is_optional: + if lhs in self._optional_partners: + # Collect temporary variable which provides + # allocated memory space for this optional variable + tmp_var = self._optional_partners[lhs] + else: + # Create temporary variable to provide allocated + # memory space before assigning to the pointer value + # (may be NULL) + tmp_var = self.scope.get_temporary_variable(lhs, + is_optional = False) + self._optional_partners[lhs] = tmp_var + # Point optional variable at an allocated memory space + prefix_code = self._print(AliasAssign(lhs, tmp_var)) + if isinstance(rhs, FunctionCall) and isinstance(rhs.dtype, NativeTuple): + self._temporary_args = [ObjectAddress(a) for a in lhs] + return prefix_code+'{};\n'.format(self._print(rhs)) + # Inhomogenous tuples are unravelled and therefore do not exist in the c printer + + if isinstance(rhs, CudaArray): + return prefix_code+self.copy_CudaArray_Data(expr) + if isinstance(rhs, (NumpyArray, PythonTuple)): + return prefix_code+self.copy_NumpyArray_Data(expr) + if isinstance(rhs, (NumpyFull)): + return prefix_code+self.arrayFill(expr) + if isinstance(rhs, NumpyArange): + return prefix_code+self.fill_NumpyArange(rhs, lhs) + lhs = self._print(expr.lhs) + rhs = self._print(expr.rhs) + return prefix_code+'{} = {};\n'.format(lhs, rhs) + + def copy_CudaArray_Data(self, expr): + """ print the assignment of a Cuda NdArray + + parameters + ---------- + expr : PyccelAstNode + The Assign Node used to get the lhs and rhs + Return + ------ + String + Return a str that contains the declaration of a dummy data_buffer + and a call to an operator which copies it to a Cuda NdArray struct + if the ndarray is a stack_array the str will contain the initialization + """ + rhs = expr.rhs + lhs = expr.lhs + if rhs.rank == 0: + raise NotImplementedError(str(expr)) + dummy_array_name = self.scope.get_new_name('cuda_array_dummy') + declare_dtype = self.find_in_dtype_registry(self._print(rhs.dtype), rhs.precision) + dtype = self.find_in_ndarray_type_registry(self._print(rhs.dtype), rhs.precision) + arg = rhs.arg if isinstance(rhs, CudaArray) else rhs + if rhs.rank > 1: + # flattening the args to use them in C initialization. + arg = self._flatten_list(arg) + + self.add_import(c_imports['string']) + if isinstance(arg, Variable): + arg = self._print(arg) + cpy_data = "cudaMemcpy({0}.{2}, {1}.{2}, {0}.buffer_size, cudaMemcpyHostToDevice);".format(lhs, arg, dtype) + return '%s' % (cpy_data) + else : + arg = ', '.join(self._print(i) for i in arg) + dummy_array = "%s %s[] = {%s};\n" % (declare_dtype, dummy_array_name, arg) + cpy_data = "cudaMemcpy({0}.{2}, {1}, {0}.buffer_size, cudaMemcpyHostToDevice);".format(self._print(lhs), dummy_array_name, dtype) + return '%s%s' % (dummy_array, cpy_data) + + def _print_CudaDeviceSynchronize(self, expr): + return 'cudaDeviceSynchronize();' + + def _print_CudaInternalVar(self, expr): + var_name = type(expr).__name__ + var_name = cuda_Internal_Var[var_name] + dim_c = ('x', 'y', 'z')[expr.dim] + return '{}.{}'.format(var_name, dim_c) + def ccudacode(expr, filename, assign_to=None, **settings): """Converts an expr to a string of ccuda code diff --git a/pyccel/parser/semantic.py b/pyccel/parser/semantic.py index b458c35fae..a0962262ef 100644 --- a/pyccel/parser/semantic.py +++ b/pyccel/parser/semantic.py @@ -60,7 +60,7 @@ from pyccel.ast.core import PyccelFunctionDef from pyccel.ast.core import Assert -from pyccel.ast.class_defs import NumpyArrayClass, TupleClass, get_cls_base +from pyccel.ast.class_defs import NumpyArrayClass, TupleClass, get_cls_base, CudaArrayClass from pyccel.ast.datatypes import NativeRange, str_dtype from pyccel.ast.datatypes import NativeSymbol @@ -94,6 +94,8 @@ from pyccel.ast.numpyext import NumpyNewArray, NumpyNonZero from pyccel.ast.numpyext import DtypePrecisionToCastFunction +from pyccel.ast.cudaext import CudaNewArray + from pyccel.ast.omp import (OMP_For_Loop, OMP_Simd_Construct, OMP_Distribute_Construct, OMP_TaskLoop_Construct, OMP_Sections_Construct, Omp_End_Clause, OMP_Single_Construct) @@ -488,6 +490,17 @@ def _infere_type(self, expr, **settings): d_var['cls_base' ] = NumpyArrayClass return d_var + elif isinstance(expr, CudaNewArray): + d_var['datatype' ] = expr.dtype + d_var['memory_handling'] = 'heap' if expr.rank > 0 else 'stack' + d_var['memory_location'] = 'managed' + d_var['shape' ] = expr.shape + d_var['rank' ] = expr.rank + d_var['order' ] = expr.order + d_var['precision' ] = expr.precision + d_var['cls_base' ] = CudaArrayClass + return d_var + elif isinstance(expr, NumpyTranspose): var = expr.internal_var diff --git a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu new file mode 100644 index 0000000000..dd93ae7351 --- /dev/null +++ b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu @@ -0,0 +1,94 @@ +#include "cuda_ndarrays.h" + +__global__ +void cuda_array_arange(t_ndarray arr, int start) +{ + int index = blockIdx.x * blockDim.x + threadIdx.x; + int stride = gridDim.x * blockDim.x; + for(int i = index ; i < arr.length; i+=stride) + arr.nd_int64[i] = (i + start); +} + +__global__ +void cuda_array_fill_int(int64_t c, t_ndarray arr) +{ + int index = blockIdx.x * blockDim.x + threadIdx.x; + int stride = gridDim.x * blockDim.x; + for(int i = index ; i < arr.length; i+=stride) + arr.nd_int64[i] = c; +} + +t_ndarray cuda_array_create(int32_t nd, int64_t *shape, enum e_types type) +{ + t_ndarray arr; + + arr.nd = nd; + arr.type = type; + switch (type) + { + case nd_int8: + arr.type_size = sizeof(int8_t); + break; + case nd_int16: + arr.type_size = sizeof(int16_t); + break; + case nd_int32: + arr.type_size = sizeof(int32_t); + break; + case nd_int64: + arr.type_size = sizeof(int64_t); + break; + case nd_float: + arr.type_size = sizeof(float); + break; + case nd_double: + arr.type_size = sizeof(double); + break; + case nd_bool: + arr.type_size = sizeof(bool); + break; + } + arr.is_view = false; + arr.length = 1; + cudaMallocManaged(&(arr.shape), arr.nd * sizeof(int64_t)); + for (int32_t i = 0; i < arr.nd; i++) + { + arr.length *= shape[i]; + arr.shape[i] = shape[i]; + } + arr.buffer_size = arr.length * arr.type_size; + cudaMallocManaged(&(arr.strides), nd * sizeof(int64_t)); + for (int32_t i = 0; i < arr.nd; i++) + { + arr.strides[i] = 1; + for (int32_t j = i + 1; j < arr.nd; j++) + arr.strides[i] *= arr.shape[j]; + } + cudaMallocManaged(&(arr.raw_data), arr.buffer_size); + return (arr); +} + +int32_t cuda_free_array(t_ndarray arr) +{ + if (arr.shape == NULL) + return (0); + free(arr.raw_data); + arr.raw_data = NULL; + free(arr.shape); + arr.shape = NULL; + free(arr.strides); + arr.strides = NULL; + return (1); +} + + +int32_t cuda_free_pointer(t_ndarray arr) +{ + if (arr.is_view == false || arr.shape == NULL) + return (0); + free(arr.shape); + arr.shape = NULL; + free(arr.strides); + arr.strides = NULL; + return (1); +} diff --git a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h new file mode 100644 index 0000000000..f25a28d671 --- /dev/null +++ b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h @@ -0,0 +1,11 @@ +#ifndef CUDA_NDARRAYS_H +# define CUDA_NDARRAYS_H +# include "../ndarrays/ndarrays.h" +__global__ +void cuda_array_arange(t_ndarray arr, int start); +__global__ +void cuda_array_fill(int64_t c, t_ndarray arr); +t_ndarray cuda_array_create(int32_t nd, int64_t *shape, enum e_types type); +int32_t cuda_free_array(t_ndarray dump); +int32_t cuda_free_pointer(t_ndarray dump); +#endif From e5432f16e29b4a592073e047a6c296cee2181a72 Mon Sep 17 00:00:00 2001 From: bauom Date: Thu, 18 Aug 2022 20:30:39 +0100 Subject: [PATCH 005/193] fixed a cwarapper problem for cuda --- pyccel/ast/cudaext.py | 11 ++++- pyccel/codegen/printing/ccudacode.py | 34 ++++++++++---- pyccel/codegen/printing/cwrappercode.py | 5 +- pyccel/codegen/utilities.py | 3 +- pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu | 9 ++-- pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h | 48 +++++++++++++++++++- pyccel/stdlib/ndarrays/ndarrays.c | 6 +++ pyccel/stdlib/ndarrays/ndarrays.h | 13 ++++++ samples/test/test.py | 15 ++++-- 9 files changed, 121 insertions(+), 23 deletions(-) diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index e37052620f..90203eaa2b 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -146,8 +146,15 @@ def arg(self): class CudaDeviceSynchronize(PyccelAstNode): "Represents a call to Cuda.deviceSynchronize for code generation." - _attribute_nodes = () - pass + + def __init__(self): + #... + self._shape = () + self._rank = 0 + self._dtype = NativeInteger() + self._precision = 0 + self._order = None + super().__init__() class CudaInternalVar(PyccelAstNode): _attribute_nodes = ('_dim',) diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index d626e7bf96..d5a3e52188 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -309,10 +309,11 @@ def get_var_arg(arg, var): arg_code = ', '.join(arg_code_list) cuda_deco = "__global__ " if 'kernel' in expr.decorators else '' + # print(expr, ret_type, name, arg_code) if isinstance(expr, FunctionAddress): return '{}(*{})({})'.format(ret_type, name, arg_code) else: - return '{0}{1}{2}({3})'.format(cuda_deco, ret_type, name, arg_code) + return '{}{}{}({})'.format(cuda_deco, ret_type, name, arg_code) def _print_Allocate(self, expr): free_code = '' @@ -323,22 +324,35 @@ def _print_Allocate(self, expr): elif (expr.status == 'allocated'): free_code += self._print(Deallocate(expr.variable)) shape = ", ".join(self._print(i) for i in expr.shape) + shape_dtype = self.find_in_dtype_registry('int', 8) + tmp_shape = self.scope.get_new_name('tmp_shape') dtype = self._print(expr.variable.dtype) dtype = self.find_in_ndarray_type_registry(dtype, expr.variable.precision) - shape_dtype = self.find_in_dtype_registry('int', 8) - shape_Assign = "("+ shape_dtype +"[]){" + shape + "}" + + shape_Assign = "{} {}[] = {{{}}};".format(shape_dtype, tmp_shape, shape) is_view = 'false' if expr.variable.on_heap else 'true' if expr.variable.is_managed or expr.variable.on_device: self.add_import(c_imports['cuda_ndarrays']) - alloc_code = "{} = cuda_array_create({}, {}, {}, {});\n".format( - expr.variable, len(expr.shape), shape_Assign, dtype, + alloc_code = "{} = cuda_array_create({}, {}, {}, {});".format( + expr.variable, len(expr.shape), tmp_shape, dtype, is_view) else: self.add_import(c_imports['ndarrays']) - alloc_code = "{} = array_create({}, {}, {}, {});\n".format( - expr.variable, len(expr.shape), shape_Assign, dtype, + alloc_code = "{} = array_create({}, {}, {}, {});".format( + expr.variable, len(expr.shape), tmp_shape, dtype, is_view) - return '{}{}'.format(free_code, alloc_code) + return '{}\n{}\n{}\n'.format(free_code, shape_Assign, alloc_code) + + def _print_Deallocate(self, expr): + if isinstance(expr.variable, InhomogeneousTupleVariable): + return ''.join(self._print(Deallocate(v)) for v in expr.variable) + cuda = '' + if expr.variable.on_device or expr.variable.is_managed: + cuda = 'cuda_' + return '' + if expr.variable.is_alias: + return '{}free_pointer({});\n'.format(cuda, self._print(expr.variable)) + return '{}free_array({});\n'.format(cuda, self._print(expr.variable)) def _print_KernelCall(self, expr): func = expr.funcdef @@ -433,12 +447,12 @@ def copy_CudaArray_Data(self, expr): self.add_import(c_imports['string']) if isinstance(arg, Variable): arg = self._print(arg) - cpy_data = "cudaMemcpy({0}.{2}, {1}.{2}, {0}.buffer_size, cudaMemcpyHostToDevice);".format(lhs, arg, dtype) + cpy_data = "cudaMemcpy({0}.raw_data, {1}.{2}, {0}.buffer_size, cudaMemcpyHostToDevice);".format(lhs, arg, dtype) return '%s' % (cpy_data) else : arg = ', '.join(self._print(i) for i in arg) dummy_array = "%s %s[] = {%s};\n" % (declare_dtype, dummy_array_name, arg) - cpy_data = "cudaMemcpy({0}.{2}, {1}, {0}.buffer_size, cudaMemcpyHostToDevice);".format(self._print(lhs), dummy_array_name, dtype) + cpy_data = "cudaMemcpy({0}.raw_data, {1}, {0}.buffer_size, cudaMemcpyHostToDevice);".format(self._print(lhs), dummy_array_name, dtype) return '%s%s' % (dummy_array, cpy_data) def _print_CudaDeviceSynchronize(self, expr): diff --git a/pyccel/codegen/printing/cwrappercode.py b/pyccel/codegen/printing/cwrappercode.py index a436ba0526..52bfb2afa7 100644 --- a/pyccel/codegen/printing/cwrappercode.py +++ b/pyccel/codegen/printing/cwrappercode.py @@ -224,7 +224,7 @@ def static_function_signature(self, expr): Signature of the function """ #if target_language is C no need for the binding - if self._target_language == 'c': + if self._target_language in ('c', 'ccuda'): return self.function_signature(expr) args = [a.var for a in expr.arguments] @@ -236,6 +236,7 @@ def static_function_signature(self, expr): else: ret_type = self._print(datatype('void')) + ' ' name = expr.name + print(expr.arguments[0].var.is_ndarray) if not args: arg_code = 'void' else: @@ -293,10 +294,12 @@ def get_static_declare_type(self, variable): string """ + dtype = self._print(variable.dtype) prec = variable.precision dtype = self.find_in_dtype_registry(dtype, prec) + print(variable, variable.dtype, variable.is_ndarray, dtype) if self.stored_in_c_pointer(variable): return '{0}*'.format(dtype) diff --git a/pyccel/codegen/utilities.py b/pyccel/codegen/utilities.py index d2a0e88c89..2e69c3f56e 100644 --- a/pyccel/codegen/utilities.py +++ b/pyccel/codegen/utilities.py @@ -21,13 +21,14 @@ __all__ = ['copy_internal_library','recompile_object'] #============================================================================== -language_extension = {'fortran':'f90', 'c':'c', 'python':'py'} +language_extension = {'fortran':'f90', 'c':'c', 'python':'py', 'ccuda':'cu'} #============================================================================== # map internal libraries to their folders inside pyccel/stdlib and their compile objects # The compile object folder will be in the pyccel dirpath internal_libs = { "ndarrays" : ("ndarrays", CompileObj("ndarrays.c",folder="ndarrays")), + "cuda_ndarrays" : ("cuda_ndarrays", CompileObj("cuda_ndarrays.cu",folder="cuda_ndarrays")), "pyc_math_f90" : ("math", CompileObj("pyc_math_f90.f90",folder="math")), "pyc_math_c" : ("math", CompileObj("pyc_math_c.c",folder="math")), "cwrapper" : ("cwrapper", CompileObj("cwrapper.c",folder="cwrapper", accelerators=('python',))), diff --git a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu index dd93ae7351..ea8220619f 100644 --- a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu +++ b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu @@ -18,7 +18,8 @@ void cuda_array_fill_int(int64_t c, t_ndarray arr) arr.nd_int64[i] = c; } -t_ndarray cuda_array_create(int32_t nd, int64_t *shape, enum e_types type) +t_ndarray cuda_array_create(int32_t nd, int64_t *shape, + enum e_types type, bool is_view) { t_ndarray arr; @@ -48,7 +49,7 @@ t_ndarray cuda_array_create(int32_t nd, int64_t *shape, enum e_types type) arr.type_size = sizeof(bool); break; } - arr.is_view = false; + arr.is_view = is_view; arr.length = 1; cudaMallocManaged(&(arr.shape), arr.nd * sizeof(int64_t)); for (int32_t i = 0; i < arr.nd; i++) @@ -64,7 +65,9 @@ t_ndarray cuda_array_create(int32_t nd, int64_t *shape, enum e_types type) for (int32_t j = i + 1; j < arr.nd; j++) arr.strides[i] *= arr.shape[j]; } - cudaMallocManaged(&(arr.raw_data), arr.buffer_size); + printf("abbah"); + if (!is_view) + cudaMallocManaged(&(arr.raw_data), arr.buffer_size); return (arr); } diff --git a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h index f25a28d671..aad7a81a11 100644 --- a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h +++ b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h @@ -1,11 +1,55 @@ #ifndef CUDA_NDARRAYS_H # define CUDA_NDARRAYS_H -# include "../ndarrays/ndarrays.h" + +#include "../ndarrays/ndarrays.h" + +// enum e_cuda_types +// { +// nd_bool = 0, +// nd_int8 = 1, +// nd_int16 = 3, +// nd_int32 = 5, +// nd_int64 = 7, +// nd_float = 11, +// nd_double = 12 +// }; + +// typedef struct s_cuda_ndarray +// { +// /* raw data buffer*/ +// union { +// void *raw_data; +// int8_t *nd_int8; +// int16_t *nd_int16; +// int32_t *nd_int32; +// int64_t *nd_int64; +// float *nd_float; +// double *nd_double; +// bool *nd_bool; +// }; +// /* number of dimensions */ +// int32_t nd; +// /* shape 'size of each dimension' */ +// int64_t *shape; +// /* strides 'number of bytes to skip to get the next element' */ +// int64_t *strides; +// /* type of the array elements */ +// enum e_types type; +// /* type size of the array elements */ +// int32_t type_size; +// /* number of element in the array */ +// int32_t length; +// /* size of the array */ +// int32_t buffer_size; +// /* True if the array does not own the data */ +// bool is_view; +// } t_cuda_ndarray; + __global__ void cuda_array_arange(t_ndarray arr, int start); __global__ void cuda_array_fill(int64_t c, t_ndarray arr); -t_ndarray cuda_array_create(int32_t nd, int64_t *shape, enum e_types type); +t_ndarray cuda_array_create(int32_t nd, int64_t *shape, enum e_types type, bool is_view); int32_t cuda_free_array(t_ndarray dump); int32_t cuda_free_pointer(t_ndarray dump); #endif diff --git a/pyccel/stdlib/ndarrays/ndarrays.c b/pyccel/stdlib/ndarrays/ndarrays.c index 0f3ea4d16a..c02c43d524 100644 --- a/pyccel/stdlib/ndarrays/ndarrays.c +++ b/pyccel/stdlib/ndarrays/ndarrays.c @@ -42,12 +42,14 @@ t_ndarray array_create(int32_t nd, int64_t *shape, case nd_bool: arr.type_size = sizeof(bool); break; + #ifndef __NVCC__ case nd_cfloat: arr.type_size = sizeof(float complex); break; case nd_cdouble: arr.type_size = sizeof(double complex); break; + #endif } arr.is_view = is_view; arr.length = 1; @@ -95,12 +97,14 @@ void stack_array_init(t_ndarray *arr) case nd_bool: arr->type_size = sizeof(bool); break; + #ifndef __NVCC__ case nd_cfloat: arr->type_size = sizeof(float complex); break; case nd_cdouble: arr->type_size = sizeof(double complex); break; + #endif } arr->length = 1; for (int32_t i = 0; i < arr->nd; i++) @@ -177,6 +181,7 @@ void _array_fill_double(double c, t_ndarray arr) arr.nd_double[i] = c; } +#ifndef __NVCC__ void _array_fill_cfloat(float complex c, t_ndarray arr) { if (c == 0) @@ -195,6 +200,7 @@ void _array_fill_cdouble(double complex c, t_ndarray arr) for (int32_t i = 0; i < arr.length; i++) arr.nd_cdouble[i] = c; } +#endif /* ** deallocation diff --git a/pyccel/stdlib/ndarrays/ndarrays.h b/pyccel/stdlib/ndarrays/ndarrays.h index ca6592f4e9..ffb4c1e50d 100644 --- a/pyccel/stdlib/ndarrays/ndarrays.h +++ b/pyccel/stdlib/ndarrays/ndarrays.h @@ -10,6 +10,9 @@ # include # include +#ifdef __cplusplus +extern "C" { +#endif /* mapping the function array_fill to the correct type */ # define array_fill(c, arr) _Generic((c), int64_t : _array_fill_int64,\ int32_t : _array_fill_int32,\ @@ -66,8 +69,10 @@ enum e_types nd_int64 = 7, nd_float = 11, nd_double = 12, + #ifndef __NVCC__ nd_cfloat = 14, nd_cdouble = 15 + #endif }; typedef struct s_ndarray @@ -82,8 +87,10 @@ typedef struct s_ndarray float *nd_float; double *nd_double; bool *nd_bool; + #ifndef __NVCC__ double complex *nd_cdouble; float complex *nd_cfloat; + #endif }; /* number of dimensions */ int32_t nd; @@ -116,8 +123,10 @@ void _array_fill_int64(int64_t c, t_ndarray arr); void _array_fill_float(float c, t_ndarray arr); void _array_fill_double(double c, t_ndarray arr); void _array_fill_bool(bool c, t_ndarray arr); +#ifndef __NVCC__ void _array_fill_cfloat(float complex c, t_ndarray arr); void _array_fill_cdouble(double complex c, t_ndarray arr); +#endif /* slicing */ /* creating a Slice object */ @@ -140,4 +149,8 @@ int64_t get_index(t_ndarray arr, ...); int64_t *numpy_to_ndarray_strides(int64_t *np_strides, int type_size, int nd); int64_t *numpy_to_ndarray_shape(int64_t *np_shape, int nd); +#ifdef __cplusplus +} +#endif + #endif diff --git a/samples/test/test.py b/samples/test/test.py index 3ded25b6f1..b6e49d3320 100644 --- a/samples/test/test.py +++ b/samples/test/test.py @@ -5,17 +5,24 @@ # from pyccel.stdlib.internal import PyccelthreadIdx +@types('int[:]') +def abbah(z): + return z[0] + @kernel @types('int[:]') def func(a): - # i = cuda.threadIdx(0) + i = cuda.threadIdx(0) """ test test """ - print("Hello World!") + print("Hello World! ", a[i]) + a[i] += 1 if __name__ == "__main__": - b = narray([1, 2, 3], dtype='int', order='C') + # b = narray([1, 2, 3], dtype='int', order='C') a = cuda.array([1, 2, 3], dtype='int', order='C') - func[1, 2](a) \ No newline at end of file + func[1, 3](a) + cuda.deviceSynchronize() + print(a) \ No newline at end of file From c6116f5930b46f8dedea4db22ce27c8b2f5a3947 Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 23 Aug 2022 18:11:47 +0000 Subject: [PATCH 006/193] fixed cudadevicesynchronize call --- pyccel/ast/cudaext.py | 7 ++++--- pyccel/codegen/printing/ccudacode.py | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index 90203eaa2b..c532e63991 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -144,15 +144,16 @@ def __str__(self): def arg(self): return self._arg -class CudaDeviceSynchronize(PyccelAstNode): +class CudaDeviceSynchronize(PyccelInternalFunction): "Represents a call to Cuda.deviceSynchronize for code generation." - + # pass + _attribute_nodes = () def __init__(self): #... self._shape = () self._rank = 0 self._dtype = NativeInteger() - self._precision = 0 + self._precision = None self._order = None super().__init__() diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index d5a3e52188..811954f9e6 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -453,10 +453,10 @@ def copy_CudaArray_Data(self, expr): arg = ', '.join(self._print(i) for i in arg) dummy_array = "%s %s[] = {%s};\n" % (declare_dtype, dummy_array_name, arg) cpy_data = "cudaMemcpy({0}.raw_data, {1}, {0}.buffer_size, cudaMemcpyHostToDevice);".format(self._print(lhs), dummy_array_name, dtype) - return '%s%s' % (dummy_array, cpy_data) + return '%s%s\n' % (dummy_array, cpy_data) def _print_CudaDeviceSynchronize(self, expr): - return 'cudaDeviceSynchronize();' + return 'cudaDeviceSynchronize()' def _print_CudaInternalVar(self, expr): var_name = type(expr).__name__ From a9d61c90f853b85579cac929a38e50105f48d196 Mon Sep 17 00:00:00 2001 From: bauom Date: Thu, 25 Aug 2022 14:01:19 +0000 Subject: [PATCH 007/193] activate github action test --- .github/workflows/Cuda_pytest.yml | 110 ++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 .github/workflows/Cuda_pytest.yml diff --git a/.github/workflows/Cuda_pytest.yml b/.github/workflows/Cuda_pytest.yml new file mode 100644 index 0000000000..c703f0d60a --- /dev/null +++ b/.github/workflows/Cuda_pytest.yml @@ -0,0 +1,110 @@ +name: Pyccel CUDA tests + +on: + pull_request: + branches: [ cuda_main ] + +jobs: + Linux: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.7 + uses: actions/setup-python@v3 + with: + python-version: 3.7 + - name: Install dependencies + uses: ./.github/actions/linux_install + - name: Install python dependencies + uses: ./.github/actions/pip_installation + - name: Coverage install + uses: ./.github/actions/coverage_install + - name: Fortran/C tests with pytest + uses: ./.github/actions/pytest_run + - name: Python tests with pytest + uses: ./.github/actions/pytest_run_python + - name: Parallel tests with pytest + uses: ./.github/actions/pytest_parallel + - name: Test with valgrind for memory leaks + uses: ./.github/actions/valgrind_run + - name: Collect coverage information + continue-on-error: True + uses: ./.github/actions/coverage_collection + - name: Run codacy-coverage-reporter + uses: codacy/codacy-coverage-reporter-action@master + continue-on-error: True + with: + project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} + coverage-reports: cobertura.xml + + Windows: + + runs-on: windows-latest + + steps: + - uses: actions/checkout@v2 + - name: Setup Python 3.9 + uses: actions/setup-python@v3 + with: + # The second most recent version is used as + # setup-python installs the most recent patch + # which leads to linking problems as there are + # 2 versions of python3X.a and the wrong one + # is chosen + python-version: 3.9 + # Uncomment to examine DLL requirements with 'objdump -x FILE' + #- name: Install mingw tools + # uses: msys2/setup-msys2@v2 + - name: Install dependencies + uses: ./.github/actions/windows_install + - name: Install python dependencies + uses: ./.github/actions/pip_installation + - name: Fortran/C tests with pytest + uses: ./.github/actions/pytest_run + - name: Python tests with pytest + uses: ./.github/actions/pytest_run_python + - name: Parallel tests with pytest + uses: ./.github/actions/pytest_parallel + + MacOSX: + + runs-on: macos-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: '3.10' + - name: Install dependencies + uses: ./.github/actions/macos_install + - name: Install python dependencies + uses: ./.github/actions/pip_installation + - name: Fortran/C tests with pytest + uses: ./.github/actions/pytest_run + - name: Python tests with pytest + uses: ./.github/actions/pytest_run_python + - name: Parallel tests with pytest + uses: ./.github/actions/pytest_parallel + + Linter: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.7 + uses: actions/setup-python@v3 + with: + python-version: 3.7 + - name: Install python dependencies + run: | + python -m pip install --upgrade pip + python -m pip install pylint + shell: bash + - name: Pylint + run: | + python -m pylint --rcfile=.pylintrc pyccel/parser/semantic.py + shell: bash From 8290b6026fa049df95ae12cc9ee4de01e5e6e239 Mon Sep 17 00:00:00 2001 From: bauom Date: Thu, 25 Aug 2022 14:04:24 +0000 Subject: [PATCH 008/193] remove unnecessary lines --- pyccel/stdlib/ndarrays/ndarrays.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/pyccel/stdlib/ndarrays/ndarrays.h b/pyccel/stdlib/ndarrays/ndarrays.h index ffb4c1e50d..aed8a16e3a 100644 --- a/pyccel/stdlib/ndarrays/ndarrays.h +++ b/pyccel/stdlib/ndarrays/ndarrays.h @@ -10,9 +10,6 @@ # include # include -#ifdef __cplusplus -extern "C" { -#endif /* mapping the function array_fill to the correct type */ # define array_fill(c, arr) _Generic((c), int64_t : _array_fill_int64,\ int32_t : _array_fill_int32,\ @@ -149,8 +146,4 @@ int64_t get_index(t_ndarray arr, ...); int64_t *numpy_to_ndarray_strides(int64_t *np_strides, int type_size, int nd); int64_t *numpy_to_ndarray_shape(int64_t *np_shape, int nd); -#ifdef __cplusplus -} -#endif - #endif From 7a32fe53f564a3fbcfc9ec3e745120a9c5bfc563 Mon Sep 17 00:00:00 2001 From: bauom Date: Thu, 25 Aug 2022 14:29:44 +0000 Subject: [PATCH 009/193] Revert "remove unnecessary lines" This reverts commit 8290b6026fa049df95ae12cc9ee4de01e5e6e239. --- pyccel/stdlib/ndarrays/ndarrays.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pyccel/stdlib/ndarrays/ndarrays.h b/pyccel/stdlib/ndarrays/ndarrays.h index aed8a16e3a..ffb4c1e50d 100644 --- a/pyccel/stdlib/ndarrays/ndarrays.h +++ b/pyccel/stdlib/ndarrays/ndarrays.h @@ -10,6 +10,9 @@ # include # include +#ifdef __cplusplus +extern "C" { +#endif /* mapping the function array_fill to the correct type */ # define array_fill(c, arr) _Generic((c), int64_t : _array_fill_int64,\ int32_t : _array_fill_int32,\ @@ -146,4 +149,8 @@ int64_t get_index(t_ndarray arr, ...); int64_t *numpy_to_ndarray_strides(int64_t *np_strides, int type_size, int nd); int64_t *numpy_to_ndarray_shape(int64_t *np_shape, int nd); +#ifdef __cplusplus +} +#endif + #endif From 74b4ae5e559377b4cc4ed0c57dd7054ba25da302 Mon Sep 17 00:00:00 2001 From: bauom Date: Thu, 25 Aug 2022 14:49:49 +0000 Subject: [PATCH 010/193] fixed conflicts after merge with master --- pyccel/ast/cudaext.py | 62 +++++++++---------------- pyccel/codegen/printing/cwrappercode.py | 1 - 2 files changed, 22 insertions(+), 41 deletions(-) diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index c532e63991..2f57d5d367 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -13,7 +13,7 @@ PythonComplex, PythonReal, PythonImag, PythonList, PythonType, PythonConjugate) -from .core import process_shape, Module, Import, PyccelFunctionDef +from .core import Module, Import, PyccelFunctionDef from .datatypes import (dtype_and_precision_registry as dtype_registry, default_precision, datatype, NativeInteger, @@ -30,7 +30,7 @@ from .operators import broadcast, PyccelMinus, PyccelDiv from .variable import (Variable, Constant, HomogeneousTupleVariable) -from .numpyext import process_dtype, NumpyNewArray +from .numpyext import process_dtype, NumpyNewArray, process_shape #============================================================================== __all__ = ( @@ -45,32 +45,6 @@ 'CudaGridDim' ) -#============================================================================== -class CudaMemCopy(): - """Represents a call to cuda malloc for code generation. - - arg : list , tuple , PythonTuple, List, Variable - """ - def __init__(self, x, size): - self._shape = () - self._rank = 0 - self._dtype = x.dtype - self._precision = x.precision - - @property - def dest(self): - return self._args[0] - @property - def src(self): - return self._args[1] - @property - def size(self): - return self._args[2] - @property - def copy_mode(self): - return self._args[3] - - #============================================================================== class CudaNewArray(PyccelInternalFunction): """ Class from which all Cuda functions which imply a call to Allocate @@ -119,19 +93,27 @@ def __init__(self, arg, dtype=None, order='C'): if dtype is None: dtype = arg.dtype dtype, prec = process_dtype(dtype) - # ... Determine ordering - order = str(order).strip("\'") - if order not in ('K', 'A', 'C', 'F'): - raise ValueError("Cannot recognize '{:s}' order".format(order)) + shape = process_shape(False, arg.shape) + rank = len(shape) + + if rank < 2: + order = None + else: + # ... Determine ordering + order = str(order).strip("\'") + + if order not in ('K', 'A', 'C', 'F'): + raise ValueError(f"Cannot recognize '{order}' order") + + # TODO [YG, 18.02.2020]: set correct order based on input array + if order in ('K', 'A'): + order = 'C' + # ... - # TODO [YG, 18.02.2020]: set correct order based on input array - if order in ('K', 'A'): - order = 'C' - # ... self._arg = arg - self._shape = process_shape(arg.shape) - self._rank = len(self._shape) + self._shape = shape + self._rank = rank self._dtype = dtype self._order = order self._precision = prec @@ -150,7 +132,7 @@ class CudaDeviceSynchronize(PyccelInternalFunction): _attribute_nodes = () def __init__(self): #... - self._shape = () + self._shape = None self._rank = 0 self._dtype = NativeInteger() self._precision = None @@ -167,7 +149,7 @@ def __init__(self, dim=None): raise ValueError("dimension need to be 0, 1 or 2") #... self._dim = dim - self._shape = () + self._shape = None self._rank = 0 self._dtype = dim.dtype self._precision = dim.precision diff --git a/pyccel/codegen/printing/cwrappercode.py b/pyccel/codegen/printing/cwrappercode.py index b7b9d2970c..730dfefb1b 100644 --- a/pyccel/codegen/printing/cwrappercode.py +++ b/pyccel/codegen/printing/cwrappercode.py @@ -239,7 +239,6 @@ def static_function_signature(self, expr): else: ret_type = self._print(datatype('void')) + ' ' name = expr.name - print(expr.arguments[0].var.is_ndarray) if not args: arg_code = 'void' else: From 39d5652f0135cf469cb49fbdbfc6f2b36094807b Mon Sep 17 00:00:00 2001 From: bauom Date: Thu, 25 Aug 2022 14:54:28 +0000 Subject: [PATCH 011/193] fixed cudanameclashchecker --- pyccel/naming/__init__.py | 3 ++- pyccel/naming/ccudanameclashchecker.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pyccel/naming/__init__.py b/pyccel/naming/__init__.py index 24470d9f75..d425646a1a 100644 --- a/pyccel/naming/__init__.py +++ b/pyccel/naming/__init__.py @@ -10,8 +10,9 @@ from .fortrannameclashchecker import FortranNameClashChecker from .cnameclashchecker import CNameClashChecker from .pythonnameclashchecker import PythonNameClashChecker +from .ccudaNameClashChecker import CCudaNameClashChecker name_clash_checkers = {'fortran':FortranNameClashChecker(), 'c':CNameClashChecker(), - 'ccuda':CNameClashChecker(), + 'ccuda':CCudaNameClashChecker(), 'python':PythonNameClashChecker()} diff --git a/pyccel/naming/ccudanameclashchecker.py b/pyccel/naming/ccudanameclashchecker.py index 5198b49c96..c401345c82 100644 --- a/pyccel/naming/ccudanameclashchecker.py +++ b/pyccel/naming/ccudanameclashchecker.py @@ -9,7 +9,7 @@ from pyccel.utilities.metaclasses import Singleton from pyccel.utilities.strings import create_incremented_string -class CNameClashChecker(metaclass = Singleton): +class CCudaNameClashChecker(metaclass = Singleton): """ Class containing functions to help avoid problematic names in Ccuda """ # Keywords as mentioned on https://en.cppreference.com/w/c/keyword @@ -33,7 +33,8 @@ class CNameClashChecker(metaclass = Singleton): 'GET_INDEX_FUNC_H2', 'GET_INDEX_FUNC', 'GET_INDEX', 'INDEX', 'GET_ELEMENT', 'free_array', 'free_pointer', 'get_index', 'numpy_to_ndarray_strides', - 'numpy_to_ndarray_shape', 'get_size']) + 'numpy_to_ndarray_shape', 'get_size', + 'cuda_free_array', 'cuda_free_pointer', 'cuda_array_create', 'threadIdx', 'blockIdx']) def has_clash(self, name, symbols): """ Indicate whether the proposed name causes any clashes From cc94707aa6cb412f213f5a817c7c8ab6994164c0 Mon Sep 17 00:00:00 2001 From: bauom Date: Thu, 25 Aug 2022 14:58:30 +0000 Subject: [PATCH 012/193] fixed cudanameclasher typo --- pyccel/naming/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyccel/naming/__init__.py b/pyccel/naming/__init__.py index d425646a1a..b740f1e754 100644 --- a/pyccel/naming/__init__.py +++ b/pyccel/naming/__init__.py @@ -10,7 +10,7 @@ from .fortrannameclashchecker import FortranNameClashChecker from .cnameclashchecker import CNameClashChecker from .pythonnameclashchecker import PythonNameClashChecker -from .ccudaNameClashChecker import CCudaNameClashChecker +from .ccudanameclashchecker import CCudaNameClashChecker name_clash_checkers = {'fortran':FortranNameClashChecker(), 'c':CNameClashChecker(), From 9943622dec5bc723b1b739d354a4e89e87acc48a Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 6 Sep 2022 14:17:54 +0000 Subject: [PATCH 013/193] Cuda tests --- .github/workflows/Cuda_pytest.yml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/.github/workflows/Cuda_pytest.yml b/.github/workflows/Cuda_pytest.yml index c703f0d60a..80779cbe07 100644 --- a/.github/workflows/Cuda_pytest.yml +++ b/.github/workflows/Cuda_pytest.yml @@ -21,14 +21,8 @@ jobs: uses: ./.github/actions/pip_installation - name: Coverage install uses: ./.github/actions/coverage_install - - name: Fortran/C tests with pytest - uses: ./.github/actions/pytest_run - - name: Python tests with pytest - uses: ./.github/actions/pytest_run_python - - name: Parallel tests with pytest - uses: ./.github/actions/pytest_parallel - - name: Test with valgrind for memory leaks - uses: ./.github/actions/valgrind_run + - name: CCUDA Compilation test + run: nvcc --version # cuda install check - name: Collect coverage information continue-on-error: True uses: ./.github/actions/coverage_collection From bc737a4cfd34a2f5d796ae09ba314e73f0f777ac Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 6 Sep 2022 14:39:19 +0000 Subject: [PATCH 014/193] added cuda_install action --- .github/actions/cuda_install/action.yml | 18 ++++ .github/workflows/Cuda_pytest.yml | 128 ++++++++++++------------ 2 files changed, 83 insertions(+), 63 deletions(-) create mode 100644 .github/actions/cuda_install/action.yml diff --git a/.github/actions/cuda_install/action.yml b/.github/actions/cuda_install/action.yml new file mode 100644 index 0000000000..b5ab69606f --- /dev/null +++ b/.github/actions/cuda_install/action.yml @@ -0,0 +1,18 @@ +name: 'Cuda installation commands' + +runs: + using: "composite" + steps: + - name: add cuda package + run: + wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/cuda-keyring_1.0-1_all.deb + sudo dpkg -i cuda-keyring_1.0-1_all.deb + shell: bash + - name: update the package list + run: + sudo apt-get update + shell: bash + - name: install cuda + run: + sudo apt-get -y install cuda + shell: bash \ No newline at end of file diff --git a/.github/workflows/Cuda_pytest.yml b/.github/workflows/Cuda_pytest.yml index 80779cbe07..768f72c80a 100644 --- a/.github/workflows/Cuda_pytest.yml +++ b/.github/workflows/Cuda_pytest.yml @@ -17,12 +17,14 @@ jobs: python-version: 3.7 - name: Install dependencies uses: ./.github/actions/linux_install + - name: CUDA install + uses: ./.github/actions/cuda_install + - name: CCUDA Compilation test + run: nvcc --version # cuda install check - name: Install python dependencies uses: ./.github/actions/pip_installation - name: Coverage install uses: ./.github/actions/coverage_install - - name: CCUDA Compilation test - run: nvcc --version # cuda install check - name: Collect coverage information continue-on-error: True uses: ./.github/actions/coverage_collection @@ -33,72 +35,72 @@ jobs: project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} coverage-reports: cobertura.xml - Windows: + # Windows: - runs-on: windows-latest + # runs-on: windows-latest - steps: - - uses: actions/checkout@v2 - - name: Setup Python 3.9 - uses: actions/setup-python@v3 - with: - # The second most recent version is used as - # setup-python installs the most recent patch - # which leads to linking problems as there are - # 2 versions of python3X.a and the wrong one - # is chosen - python-version: 3.9 - # Uncomment to examine DLL requirements with 'objdump -x FILE' - #- name: Install mingw tools - # uses: msys2/setup-msys2@v2 - - name: Install dependencies - uses: ./.github/actions/windows_install - - name: Install python dependencies - uses: ./.github/actions/pip_installation - - name: Fortran/C tests with pytest - uses: ./.github/actions/pytest_run - - name: Python tests with pytest - uses: ./.github/actions/pytest_run_python - - name: Parallel tests with pytest - uses: ./.github/actions/pytest_parallel + # steps: + # - uses: actions/checkout@v2 + # - name: Setup Python 3.9 + # uses: actions/setup-python@v3 + # with: + # # The second most recent version is used as + # # setup-python installs the most recent patch + # # which leads to linking problems as there are + # # 2 versions of python3X.a and the wrong one + # # is chosen + # python-version: 3.9 + # # Uncomment to examine DLL requirements with 'objdump -x FILE' + # #- name: Install mingw tools + # # uses: msys2/setup-msys2@v2 + # - name: Install dependencies + # uses: ./.github/actions/windows_install + # - name: Install python dependencies + # uses: ./.github/actions/pip_installation + # - name: Fortran/C tests with pytest + # uses: ./.github/actions/pytest_run + # - name: Python tests with pytest + # uses: ./.github/actions/pytest_run_python + # - name: Parallel tests with pytest + # uses: ./.github/actions/pytest_parallel - MacOSX: + # MacOSX: - runs-on: macos-latest + # runs-on: macos-latest - steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.10 - uses: actions/setup-python@v3 - with: - python-version: '3.10' - - name: Install dependencies - uses: ./.github/actions/macos_install - - name: Install python dependencies - uses: ./.github/actions/pip_installation - - name: Fortran/C tests with pytest - uses: ./.github/actions/pytest_run - - name: Python tests with pytest - uses: ./.github/actions/pytest_run_python - - name: Parallel tests with pytest - uses: ./.github/actions/pytest_parallel + # steps: + # - uses: actions/checkout@v2 + # - name: Set up Python 3.10 + # uses: actions/setup-python@v3 + # with: + # python-version: '3.10' + # - name: Install dependencies + # uses: ./.github/actions/macos_install + # - name: Install python dependencies + # uses: ./.github/actions/pip_installation + # - name: Fortran/C tests with pytest + # uses: ./.github/actions/pytest_run + # - name: Python tests with pytest + # uses: ./.github/actions/pytest_run_python + # - name: Parallel tests with pytest + # uses: ./.github/actions/pytest_parallel - Linter: + # Linter: - runs-on: ubuntu-latest + # runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.7 - uses: actions/setup-python@v3 - with: - python-version: 3.7 - - name: Install python dependencies - run: | - python -m pip install --upgrade pip - python -m pip install pylint - shell: bash - - name: Pylint - run: | - python -m pylint --rcfile=.pylintrc pyccel/parser/semantic.py - shell: bash + # steps: + # - uses: actions/checkout@v2 + # - name: Set up Python 3.7 + # uses: actions/setup-python@v3 + # with: + # python-version: 3.7 + # - name: Install python dependencies + # run: | + # python -m pip install --upgrade pip + # python -m pip install pylint + # shell: bash + # - name: Pylint + # run: | + # python -m pylint --rcfile=.pylintrc pyccel/parser/semantic.py + # shell: bash From 80c683126b2b405c875fbce3aae1cd53fea18849 Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 6 Sep 2022 15:12:40 +0000 Subject: [PATCH 015/193] using local installer --- .github/actions/cuda_install/action.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/actions/cuda_install/action.yml b/.github/actions/cuda_install/action.yml index b5ab69606f..e5e9dfd8fb 100644 --- a/.github/actions/cuda_install/action.yml +++ b/.github/actions/cuda_install/action.yml @@ -5,8 +5,11 @@ runs: steps: - name: add cuda package run: - wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/cuda-keyring_1.0-1_all.deb - sudo dpkg -i cuda-keyring_1.0-1_all.deb + wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/cuda-ubuntu2004.pin + sudo mv cuda-ubuntu2004.pin /etc/apt/preferences.d/cuda-repository-pin-600 + wget https://developer.download.nvidia.com/compute/cuda/11.7.1/local_installers/cuda-repo-ubuntu2004-11-7-local_11.7.1-515.65.01-1_amd64.deb + sudo dpkg -i cuda-repo-ubuntu2004-11-7-local_11.7.1-515.65.01-1_amd64.deb + sudo cp /var/cuda-repo-ubuntu2004-11-7-local/cuda-*-keyring.gpg /usr/share/keyrings/ shell: bash - name: update the package list run: From 5797580ddf2a036ee76d11eccda25d062f8b0ca5 Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 6 Sep 2022 15:57:02 +0000 Subject: [PATCH 016/193] use docker container --- .github/workflows/Cuda_pytest.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/Cuda_pytest.yml b/.github/workflows/Cuda_pytest.yml index 768f72c80a..ea39e7a067 100644 --- a/.github/workflows/Cuda_pytest.yml +++ b/.github/workflows/Cuda_pytest.yml @@ -8,7 +8,7 @@ jobs: Linux: runs-on: ubuntu-latest - + container: nvidia/cuda:11.7.1-base-ubuntu20.04 steps: - uses: actions/checkout@v2 - name: Set up Python 3.7 @@ -17,9 +17,7 @@ jobs: python-version: 3.7 - name: Install dependencies uses: ./.github/actions/linux_install - - name: CUDA install - uses: ./.github/actions/cuda_install - - name: CCUDA Compilation test + - name: CUDA Version run: nvcc --version # cuda install check - name: Install python dependencies uses: ./.github/actions/pip_installation From ea2657a44095684210428325b93b42dfa1846f8a Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 6 Sep 2022 15:58:54 +0000 Subject: [PATCH 017/193] use docker container --- .github/workflows/Cuda_pytest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Cuda_pytest.yml b/.github/workflows/Cuda_pytest.yml index ea39e7a067..4770a2e2cf 100644 --- a/.github/workflows/Cuda_pytest.yml +++ b/.github/workflows/Cuda_pytest.yml @@ -15,8 +15,8 @@ jobs: uses: actions/setup-python@v3 with: python-version: 3.7 - - name: Install dependencies - uses: ./.github/actions/linux_install + # - name: Install dependencies + # uses: ./.github/actions/linux_install - name: CUDA Version run: nvcc --version # cuda install check - name: Install python dependencies From 4cd6e860baf84fce3f1f1d22638dc8a98fc83251 Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 6 Sep 2022 16:03:29 +0000 Subject: [PATCH 018/193] use docker container devel --- .github/workflows/Cuda_pytest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Cuda_pytest.yml b/.github/workflows/Cuda_pytest.yml index 4770a2e2cf..46f47b7400 100644 --- a/.github/workflows/Cuda_pytest.yml +++ b/.github/workflows/Cuda_pytest.yml @@ -8,7 +8,7 @@ jobs: Linux: runs-on: ubuntu-latest - container: nvidia/cuda:11.7.1-base-ubuntu20.04 + container: nvidia/cuda:11.7.1-devel-ubuntu20.04 steps: - uses: actions/checkout@v2 - name: Set up Python 3.7 From b37135815c3ab066f7b4cb0538c324b34c95b3d5 Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 6 Sep 2022 16:04:00 +0000 Subject: [PATCH 019/193] use docker container devel --- .github/workflows/Cuda_pytest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Cuda_pytest.yml b/.github/workflows/Cuda_pytest.yml index 46f47b7400..1d506d12e2 100644 --- a/.github/workflows/Cuda_pytest.yml +++ b/.github/workflows/Cuda_pytest.yml @@ -11,14 +11,14 @@ jobs: container: nvidia/cuda:11.7.1-devel-ubuntu20.04 steps: - uses: actions/checkout@v2 + - name: CUDA Version + run: nvcc --version # cuda install check - name: Set up Python 3.7 uses: actions/setup-python@v3 with: python-version: 3.7 # - name: Install dependencies # uses: ./.github/actions/linux_install - - name: CUDA Version - run: nvcc --version # cuda install check - name: Install python dependencies uses: ./.github/actions/pip_installation - name: Coverage install From 6376c3801be55524bfe1c02a416ccd12ca73c476 Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 6 Sep 2022 18:54:59 +0000 Subject: [PATCH 020/193] removed sudo from linux_install --- .github/actions/linux_install/action.yml | 12 ++++++------ .github/workflows/Cuda_pytest.yml | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/actions/linux_install/action.yml b/.github/actions/linux_install/action.yml index 8fb5cd8505..831b6ca7da 100644 --- a/.github/actions/linux_install/action.yml +++ b/.github/actions/linux_install/action.yml @@ -5,26 +5,26 @@ runs: steps: - name: update the package list run: - sudo apt-get update + apt-get update shell: bash - name: Install fortran run: - sudo apt-get install gfortran + apt-get install gfortran shell: bash - name: Install LaPack run: - sudo apt-get install libblas-dev liblapack-dev + apt-get install libblas-dev liblapack-dev shell: bash - name: Install MPI run: | - sudo apt-get install libopenmpi-dev openmpi-bin + apt-get install libopenmpi-dev openmpi-bin echo "MPI_OPTS=--oversubscribe" >> $GITHUB_ENV shell: bash - name: Install OpenMP run: - sudo apt-get install libomp-dev libomp5 + apt-get install libomp-dev libomp5 shell: bash - name: Install Valgrind run: - sudo apt-get install valgrind + apt-get install valgrind shell: bash diff --git a/.github/workflows/Cuda_pytest.yml b/.github/workflows/Cuda_pytest.yml index 1d506d12e2..31b32836e1 100644 --- a/.github/workflows/Cuda_pytest.yml +++ b/.github/workflows/Cuda_pytest.yml @@ -17,8 +17,8 @@ jobs: uses: actions/setup-python@v3 with: python-version: 3.7 - # - name: Install dependencies - # uses: ./.github/actions/linux_install + - name: Install dependencies + uses: ./.github/actions/linux_install - name: Install python dependencies uses: ./.github/actions/pip_installation - name: Coverage install From a26546d87a18a9d70945ed9c43301ece4874ab62 Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 6 Sep 2022 18:59:07 +0000 Subject: [PATCH 021/193] added y option to apt-get install cmd --- .github/actions/linux_install/action.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/actions/linux_install/action.yml b/.github/actions/linux_install/action.yml index 831b6ca7da..0ac22148e8 100644 --- a/.github/actions/linux_install/action.yml +++ b/.github/actions/linux_install/action.yml @@ -9,22 +9,22 @@ runs: shell: bash - name: Install fortran run: - apt-get install gfortran + apt-get -y install gfortran shell: bash - name: Install LaPack run: - apt-get install libblas-dev liblapack-dev + apt-get -y install libblas-dev liblapack-dev shell: bash - name: Install MPI run: | - apt-get install libopenmpi-dev openmpi-bin + apt-get -y install libopenmpi-dev openmpi-bin echo "MPI_OPTS=--oversubscribe" >> $GITHUB_ENV shell: bash - name: Install OpenMP run: - apt-get install libomp-dev libomp5 + apt-get -y install libomp-dev libomp5 shell: bash - name: Install Valgrind run: - apt-get install valgrind + apt-get -y install valgrind shell: bash From 1830de94b93891e109feb8869ec43c4e0342371a Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 6 Sep 2022 19:34:54 +0000 Subject: [PATCH 022/193] tzdata waiting for input --- .github/actions/linux_install/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/linux_install/action.yml b/.github/actions/linux_install/action.yml index 0ac22148e8..dd94e69095 100644 --- a/.github/actions/linux_install/action.yml +++ b/.github/actions/linux_install/action.yml @@ -17,6 +17,7 @@ runs: shell: bash - name: Install MPI run: | + DEBIAN_FRONTEND=noninteractive apt-get -y install libopenmpi-dev openmpi-bin echo "MPI_OPTS=--oversubscribe" >> $GITHUB_ENV shell: bash From 902cb36e305af1319f4f02a8733c337b74727607 Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 6 Sep 2022 19:42:20 +0000 Subject: [PATCH 023/193] added TIMEZONE --- .github/actions/linux_install/action.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/actions/linux_install/action.yml b/.github/actions/linux_install/action.yml index dd94e69095..f429de92a0 100644 --- a/.github/actions/linux_install/action.yml +++ b/.github/actions/linux_install/action.yml @@ -17,8 +17,7 @@ runs: shell: bash - name: Install MPI run: | - DEBIAN_FRONTEND=noninteractive - apt-get -y install libopenmpi-dev openmpi-bin + DEBIAN_FRONTEND=noninteractive TZ=Africa/Morocco apt-get -y install libopenmpi-dev openmpi-bin echo "MPI_OPTS=--oversubscribe" >> $GITHUB_ENV shell: bash - name: Install OpenMP From 6ee698485e6bc685e42348cf50c268efccca95ad Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 6 Sep 2022 19:52:43 +0000 Subject: [PATCH 024/193] installing python3 in container --- .github/actions/linux_install/action.yml | 4 ++++ .github/workflows/Cuda_pytest.yml | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/actions/linux_install/action.yml b/.github/actions/linux_install/action.yml index f429de92a0..2653766fff 100644 --- a/.github/actions/linux_install/action.yml +++ b/.github/actions/linux_install/action.yml @@ -11,6 +11,10 @@ runs: run: apt-get -y install gfortran shell: bash + - name: Install python + run: + apt-get -y install python3-dev + shell: bash - name: Install LaPack run: apt-get -y install libblas-dev liblapack-dev diff --git a/.github/workflows/Cuda_pytest.yml b/.github/workflows/Cuda_pytest.yml index 31b32836e1..773f587549 100644 --- a/.github/workflows/Cuda_pytest.yml +++ b/.github/workflows/Cuda_pytest.yml @@ -13,10 +13,6 @@ jobs: - uses: actions/checkout@v2 - name: CUDA Version run: nvcc --version # cuda install check - - name: Set up Python 3.7 - uses: actions/setup-python@v3 - with: - python-version: 3.7 - name: Install dependencies uses: ./.github/actions/linux_install - name: Install python dependencies From e43a920ac55c0fcc1182f3b36578d2988f485a6d Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 6 Sep 2022 19:56:17 +0000 Subject: [PATCH 025/193] python-is-python3 --- .github/actions/linux_install/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/linux_install/action.yml b/.github/actions/linux_install/action.yml index 2653766fff..1564a104b7 100644 --- a/.github/actions/linux_install/action.yml +++ b/.github/actions/linux_install/action.yml @@ -14,6 +14,7 @@ runs: - name: Install python run: apt-get -y install python3-dev + apt install -y python-is-python3 shell: bash - name: Install LaPack run: From 59211fc5937eec3d12ae96fd7d92f85f427b0918 Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 6 Sep 2022 20:25:43 +0000 Subject: [PATCH 026/193] python-is-python3 --- .github/actions/linux_install/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/linux_install/action.yml b/.github/actions/linux_install/action.yml index 1564a104b7..6de36a7af7 100644 --- a/.github/actions/linux_install/action.yml +++ b/.github/actions/linux_install/action.yml @@ -14,7 +14,7 @@ runs: - name: Install python run: apt-get -y install python3-dev - apt install -y python-is-python3 + apt-get -y install python-is-python3 shell: bash - name: Install LaPack run: From 0cac77c2ab6204b6897d9214dc46b109b1ca3699 Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 6 Sep 2022 20:38:14 +0000 Subject: [PATCH 027/193] python-is-python3 --- .github/actions/linux_install/action.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/actions/linux_install/action.yml b/.github/actions/linux_install/action.yml index 6de36a7af7..13d7146fb7 100644 --- a/.github/actions/linux_install/action.yml +++ b/.github/actions/linux_install/action.yml @@ -14,6 +14,9 @@ runs: - name: Install python run: apt-get -y install python3-dev + shell: bash + - name: python as python3 + run: apt-get -y install python-is-python3 shell: bash - name: Install LaPack From 2446b45a208f8b490d1ad2512f7dd90a1920b6a0 Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 6 Sep 2022 20:46:19 +0000 Subject: [PATCH 028/193] python-is-python3 --- .github/actions/linux_install/action.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/actions/linux_install/action.yml b/.github/actions/linux_install/action.yml index 13d7146fb7..a6bd55e54b 100644 --- a/.github/actions/linux_install/action.yml +++ b/.github/actions/linux_install/action.yml @@ -19,6 +19,10 @@ runs: run: apt-get -y install python-is-python3 shell: bash + - name: Install Pip + run: + apt-get install python3-pip + shell: bash - name: Install LaPack run: apt-get -y install libblas-dev liblapack-dev From 24dd528834313c5c3c519b588a16de59c3e58942 Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 6 Sep 2022 20:50:27 +0000 Subject: [PATCH 029/193] python-is-python3 --- .github/actions/linux_install/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/linux_install/action.yml b/.github/actions/linux_install/action.yml index a6bd55e54b..81b7e95616 100644 --- a/.github/actions/linux_install/action.yml +++ b/.github/actions/linux_install/action.yml @@ -21,7 +21,7 @@ runs: shell: bash - name: Install Pip run: - apt-get install python3-pip + apt-get -y install python3-pip shell: bash - name: Install LaPack run: From 975732d2916a69425d43e93299b386c85f9b62b8 Mon Sep 17 00:00:00 2001 From: bauom Date: Sun, 11 Sep 2022 12:03:23 +0100 Subject: [PATCH 030/193] added cupy support --- pyccel/ast/class_defs.py | 24 +- pyccel/ast/cudaext.py | 7 +- pyccel/ast/cupyext.py | 470 +++++++++++++++++++ pyccel/ast/utilities.py | 2 + pyccel/codegen/printing/ccudacode.py | 81 +++- pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu | 55 ++- pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h | 19 +- pyccel/stdlib/ndarrays/ndarrays.c | 8 +- pyccel/stdlib/ndarrays/ndarrays.h | 18 +- 9 files changed, 633 insertions(+), 51 deletions(-) create mode 100644 pyccel/ast/cupyext.py diff --git a/pyccel/ast/class_defs.py b/pyccel/ast/class_defs.py index 48157a1bea..b04600b330 100644 --- a/pyccel/ast/class_defs.py +++ b/pyccel/ast/class_defs.py @@ -164,26 +164,10 @@ methods=[ FunctionDef('shape',[],[],body=[], decorators={'property':'property', 'numpy_wrapper':Shape})]) - # FunctionDef('size',[],[],body=[], - # decorators={'property':'property', 'numpy_wrapper':NumpyArraySize}), - # FunctionDef('T',[],[],body=[], - # decorators={'property':'property', 'numpy_wrapper':NumpyTranspose}), - # FunctionDef('transpose',[],[],body=[], - # decorators={'numpy_wrapper':NumpyTranspose}), - # FunctionDef('sum',[],[],body=[], - # decorators={'numpy_wrapper':NumpySum}), - # FunctionDef('min',[],[],body=[], - # decorators={'numpy_wrapper':NumpyAmin}), - # FunctionDef('max',[],[],body=[], - # decorators={'numpy_wrapper':NumpyAmax}), - # FunctionDef('imag',[],[],body=[], - # decorators={'property':'property', 'numpy_wrapper':NumpyImag}), - # FunctionDef('real',[],[],body=[], - # decorators={'property':'property', 'numpy_wrapper':NumpyReal}), - # FunctionDef('conj',[],[],body=[], - # decorators={'numpy_wrapper':NumpyConjugate}), - # FunctionDef('conjugate',[],[],body=[], - # decorators={'numpy_wrapper':NumpyConjugate})]) +# CupyArrayClass = ClassDef('cupy.ndarray', +# methods=[ +# FunctionDef('shape',[],[],body=[], +# decorators={'property':'property', 'numpy_wrapper':Shape})]) #======================================================================================= diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index 2f57d5d367..58fd66ce74 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -30,7 +30,7 @@ from .operators import broadcast, PyccelMinus, PyccelDiv from .variable import (Variable, Constant, HomogeneousTupleVariable) -from .numpyext import process_dtype, NumpyNewArray, process_shape +from .numpyext import process_dtype, process_shape #============================================================================== __all__ = ( @@ -50,12 +50,11 @@ class CudaNewArray(PyccelInternalFunction): """ Class from which all Cuda functions which imply a call to Allocate inherit """ - #-------------------------------------------------------------------------- @staticmethod - def _process_order(order): + def _process_order(rank, order): - if not order: + if rank < 2: return None order = str(order).strip('\'"') diff --git a/pyccel/ast/cupyext.py b/pyccel/ast/cupyext.py new file mode 100644 index 0000000000..08650db3eb --- /dev/null +++ b/pyccel/ast/cupyext.py @@ -0,0 +1,470 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +#------------------------------------------------------------------------------------------# +# This file is part of Pyccel which is released under MIT License. See the LICENSE file or # +# go to https://github.com/pyccel/pyccel/blob/master/LICENSE for full license details. # +#------------------------------------------------------------------------------------------# +""" Module containing objects from the cupy module understood by pyccel +""" + +from functools import reduce +import operator + +from pyccel.errors.errors import Errors +from pyccel.errors.messages import WRONG_LINSPACE_ENDPOINT, NON_LITERAL_KEEP_DIMS, NON_LITERAL_AXIS + +from pyccel.utilities.stage import PyccelStage + +from .basic import PyccelAstNode +from .builtins import (PythonInt, PythonBool, PythonFloat, PythonTuple, + PythonComplex, PythonReal, PythonImag, PythonList, + PythonType, PythonConjugate) + +from .core import Module, Import, PyccelFunctionDef, FunctionCall + +from .datatypes import (dtype_and_precision_registry as dtype_registry, + default_precision, datatype, NativeInteger, + NativeFloat, NativeComplex, NativeBool, str_dtype, + NativeNumeric) + +from .internals import PyccelInternalFunction, Slice, max_precision, get_final_precision +from .internals import PyccelArraySize + +from .literals import LiteralInteger, LiteralFloat, LiteralComplex, LiteralString, convert_to_literal +from .literals import LiteralTrue, LiteralFalse +from .literals import Nil +from .mathext import MathCeil +from .operators import broadcast, PyccelMinus, PyccelDiv +from .variable import (Variable, Constant, HomogeneousTupleVariable) +from .cudaext import CudaNewArray, CudaArray +from .numpyext import process_dtype, process_shape, DtypePrecisionToCastFunction + +errors = Errors() +pyccel_stage = PyccelStage() + +__all__ = ( + 'CupyArray', + 'CupyEmpty', + 'CupyEmptyLike', + 'CupyFull', + 'CupyFullLike', + 'CupyArange', + 'CupyArraySize', + 'CupyOnes', + 'CupyOnesLike', + 'Shape', + 'CupyZeros', + 'CupyZerosLike', +) + +#============================================================================== +class CupyArray(CudaNewArray): + """ + Represents a call to cupy.array for code generation. + + arg : list, tuple, PythonList + + """ + __slots__ = ('_arg','_dtype','_precision','_shape','_rank','_order') + _attribute_nodes = ('_arg',) + name = 'array' + + def __init__(self, arg, dtype=None, order='C'): + + if not isinstance(arg, (PythonTuple, PythonList, Variable)): + raise TypeError('Unknown type of %s.' % type(arg)) + + is_homogeneous_tuple = isinstance(arg, (PythonTuple, PythonList, HomogeneousTupleVariable)) and arg.is_homogeneous + is_array = isinstance(arg, Variable) and arg.is_ndarray + + # TODO: treat inhomogenous lists and tuples when they have mixed ordering + if not (is_homogeneous_tuple or is_array): + raise TypeError('we only accept homogeneous arguments') + + # Verify dtype and get precision + if dtype is None: + dtype = arg.dtype + prec = get_final_precision(arg) + else: + dtype, prec = process_dtype(dtype) + # ... Determine ordering + order = str(order).strip("\'") + + shape = process_shape(False, arg.shape) + rank = len(shape) + + if rank < 2: + order = None + else: + # ... Determine ordering + order = str(order).strip("\'") + + if order not in ('K', 'A', 'C', 'F'): + raise ValueError(f"Cannot recognize '{order}' order") + + # TODO [YG, 18.02.2020]: set correct order based on input array + if order in ('K', 'A'): + order = 'C' + # ... + + self._arg = arg + self._shape = shape + self._rank = rank + self._dtype = dtype + self._order = order + self._precision = prec + super().__init__() + + def __str__(self): + return str(self.arg) + + @property + def arg(self): + return self._arg + +#============================================================================== +class CupyArange(CudaNewArray): + """ + Represents a call to cupy.arange for code generation. + + Parameters + ---------- + start : Numeric + Start of interval, default value 0 + + stop : Numeric + End of interval + + step : Numeric + Spacing between values, default value 1 + + dtype : Datatype + The type of the output array, if dtype is not given, + infer the data type from the other input arguments. + """ + __slots__ = ('_start','_step','_stop','_dtype','_precision','_shape') + _attribute_nodes = ('_start','_step','_stop') + _rank = 1 + _order = None + name = 'arange' + + def __init__(self, start, stop = None, step = None, dtype = None): + + if stop is None: + self._start = LiteralInteger(0) + self._stop = start + else: + self._start = start + self._stop = stop + self._step = step if step is not None else LiteralInteger(1) + + if dtype is None: + self._dtype = max([i.dtype for i in self.arg], key = NativeNumeric.index) + self._precision = max_precision(self.arg, allow_native=False) + else: + self._dtype, self._precision = process_dtype(dtype) + + self._shape = (MathCeil(PyccelDiv(PyccelMinus(self._stop, self._start), self._step))) + self._shape = process_shape(False, self._shape) + super().__init__() + + @property + def arg(self): + return (self._start, self._stop, self._step) + + @property + def start(self): + return self._start + + @property + def stop(self): + return self._stop + + @property + def step(self): + return self._step + +#============================================================================== + +class Shape(PyccelInternalFunction): + """ Represents a call to cupy.shape for code generation + """ + __slots__ = () + name = 'shape' + def __new__(cls, arg): + if isinstance(arg.shape, PythonTuple): + return arg.shape + else: + return PythonTuple(*arg.shape) + +#============================================================================== +class CupyFull(CudaNewArray): + """ + Represents a call to cupy.full for code generation. + + Parameters + ---------- + shape : int or sequence of ints + Shape of the new array, e.g., ``(2, 3)`` or ``2``. + + fill_value : scalar + Fill value. + + dtype: str, DataType + datatype for the constructed array + The default, `None`, means `np.array(fill_value).dtype`. + + order : {'C', 'F'}, optional + Whether to store multidimensional data in C- or Fortran-contiguous + (row- or column-wise) order in memory. + + """ + __slots__ = ('_fill_value','_dtype','_precision','_shape','_rank','_order') + name = 'full' + + def __init__(self, shape, fill_value, dtype=None, order='C'): + + # Convert shape to PythonTuple + shape = process_shape(False, shape) + # If there is no dtype, extract it from fill_value + # TODO: must get dtype from an annotated node + if dtype is None: + dtype = fill_value.dtype + precision = get_final_precision(fill_value) + else: + dtype, precision = process_dtype(dtype) + + # Cast fill_value to correct type + if fill_value: + if fill_value.dtype != dtype or get_final_precision(fill_value) != precision: + cast_func = DtypePrecisionToCastFunction[dtype.name][precision] + fill_value = cast_func(fill_value) + self._shape = shape + self._rank = len(self._shape) + self._dtype = dtype + self._order = CudaNewArray._process_order(self._rank, order) + self._precision = precision + + super().__init__(fill_value) + + #-------------------------------------------------------------------------- + @property + def fill_value(self): + return self._args[0] + +#============================================================================== +class CupyAutoFill(CupyFull): + """ Abstract class for all classes which inherit from CupyFull but + the fill_value is implicitly specified + """ + __slots__ = () + def __init__(self, shape, dtype='float', order='C'): + if not dtype: + raise TypeError("Data type must be provided") + super().__init__(shape, Nil(), dtype, order) + +#============================================================================== +class CupyEmpty(CupyAutoFill): + """ Represents a call to cupy.empty for code generation. + """ + __slots__ = () + name = 'empty' + + def __init__(self, shape, dtype='float', order='C'): + if dtype in NativeNumeric: + precision = default_precision[str_dtype(dtype)] + dtype = DtypePrecisionToCastFunction[dtype.name][precision] + super().__init__(shape, dtype, order) + @property + def fill_value(self): + return None + + +#============================================================================== +class CupyZeros(CupyAutoFill): + """ Represents a call to cupy.zeros for code generation. + """ + __slots__ = () + name = 'zeros' + @property + def fill_value(self): + dtype = self.dtype + if isinstance(dtype, NativeInteger): + value = LiteralInteger(0, precision = self.precision) + elif isinstance(dtype, NativeFloat): + value = LiteralFloat(0, precision = self.precision) + elif isinstance(dtype, NativeComplex): + value = LiteralComplex(0., 0., precision = self.precision) + elif isinstance(dtype, NativeBool): + value = LiteralFalse(precision = self.precision) + else: + raise TypeError('Unknown type') + return value + +#============================================================================== +class CupyOnes(CupyAutoFill): + """ Represents a call to cupy.ones for code generation. + """ + __slots__ = () + name = 'ones' + @property + def fill_value(self): + dtype = self.dtype + if isinstance(dtype, NativeInteger): + value = LiteralInteger(1, precision = self.precision) + elif isinstance(dtype, NativeFloat): + value = LiteralFloat(1., precision = self.precision) + elif isinstance(dtype, NativeComplex): + value = LiteralComplex(1., 0., precision = self.precision) + elif isinstance(dtype, NativeBool): + value = LiteralTrue(precision = self.precision) + else: + raise TypeError('Unknown type') + return value + +#======================================================================================= +class CupyFullLike(PyccelInternalFunction): + """ Represents a call to cupy.full_like for code generation. + """ + __slots__ = () + name = 'full_like' + def __new__(cls, a, fill_value, dtype=None, order='K', subok=True, shape=None): + + # NOTE: we ignore 'subok' argument + if dtype is None: + dtype = DtypePrecisionToCastFunction[a.dtype.name][a.precision] + order = a.order if str(order).strip('\'"') in ('K', 'A') else order + shape = Shape(a) if shape is None else shape + return CupyFull(shape, fill_value, dtype, order) + +#======================================================================================= +class CupyEmptyLike(PyccelInternalFunction): + """ Represents a call to cupy.empty_like for code generation. + """ + __slots__ = () + name = 'empty_like' + def __new__(cls, a, dtype=None, order='K', subok=True, shape=None): + + # NOTE: we ignore 'subok' argument + if dtype is None: + dtype = DtypePrecisionToCastFunction[a.dtype.name][a.precision] + order = a.order if str(order).strip('\'"') in ('K', 'A') else order + shape = Shape(a) if shape is None else shape + + return CupyEmpty(shape, dtype, order) + +#======================================================================================= +class CupyOnesLike(PyccelInternalFunction): + """ Represents a call to cupy.ones_like for code generation. + """ + __slots__ = () + name = 'ones_like' + def __new__(cls, a, dtype=None, order='K', subok=True, shape=None): + + # NOTE: we ignore 'subok' argument + if dtype is None: + dtype = DtypePrecisionToCastFunction[a.dtype.name][a.precision] + order = a.order if str(order).strip('\'"') in ('K', 'A') else order + shape = Shape(a) if shape is None else shape + + return CupyOnes(shape, dtype, order) + +#======================================================================================= +class CupyZerosLike(PyccelInternalFunction): + """ Represents a call to cupy.zeros_like for code generation. + """ + __slots__ = () + name = 'zeros_like' + def __new__(cls, a, dtype=None, order='K', subok=True, shape=None): + + # NOTE: we ignore 'subok' argument + if dtype is None: + dtype = DtypePrecisionToCastFunction[a.dtype.name][a.precision] + order = a.order if str(order).strip('\'"') in ('K', 'A') else order + shape = Shape(a) if shape is None else shape + + return CupyZeros(shape, dtype, order) + +#======================================================================================= + +class CupyArraySize(PyccelInternalFunction): + """ + Class representing a call to the cupy size function which + returns the shape of an object in a given dimension + + Parameters + ========== + arg : PyccelAstNode + A PyccelAstNode of unknown shape + axis : int + The dimension along which the size is + requested + """ + __slots__ = ('_arg',) + _attribute_nodes = ('_arg',) + name = 'size' + _dtype = NativeInteger() + _precision = -1 + _rank = 0 + _shape = None + _order = None + + def __new__(cls, a, axis = None): + if axis is not None: + return PyccelArraySize(a, axis) + elif not isinstance(a, (list, + tuple, + PyccelAstNode)): + raise TypeError('Unknown type of %s.' % type(a)) + elif all(isinstance(s, LiteralInteger) for s in a.shape): + return LiteralInteger(reduce(operator.mul, [s.python_value for s in a.shape])) + else: + return super().__new__(cls) + + def __init__(self, a, axis = None): + self._arg = a + super().__init__(a) + + @property + def arg(self): + """ Object whose size is investigated + """ + return self._arg + + def __str__(self): + return 'Size({})'.format(str(self.arg)) + +#============================================================================== + +cupy_funcs = { + # ... array creation routines + 'full' : PyccelFunctionDef('full' , CupyFull), + 'empty' : PyccelFunctionDef('empty' , CupyEmpty), + 'zeros' : PyccelFunctionDef('zeros' , CupyZeros), + 'ones' : PyccelFunctionDef('ones' , CupyOnes), + 'full_like' : PyccelFunctionDef('full_like' , CupyFullLike), + 'empty_like': PyccelFunctionDef('empty_like', CupyEmptyLike), + 'zeros_like': PyccelFunctionDef('zeros_like', CupyZerosLike), + 'ones_like' : PyccelFunctionDef('ones_like' , CupyOnesLike), + 'array' : PyccelFunctionDef('array' , CupyArray), + 'arange' : PyccelFunctionDef('arange' , CupyArange), + # ... + 'shape' : PyccelFunctionDef('shape' , Shape), + 'size' : PyccelFunctionDef('size' , CupyArraySize), +} + +cuda_constants = { +} + +cupy_mod = Module('cupy', + variables = cuda_constants.values(), + funcs = cupy_funcs.values()) + +#============================================================================== + +cupy_target_swap = { + cupy_funcs['full_like'] : cupy_funcs['full'], + cupy_funcs['empty_like'] : cupy_funcs['empty'], + cupy_funcs['zeros_like'] : cupy_funcs['zeros'], + cupy_funcs['ones_like'] : cupy_funcs['ones'] + } diff --git a/pyccel/ast/utilities.py b/pyccel/ast/utilities.py index d6ee1e6a7c..1abd6d4ddd 100644 --- a/pyccel/ast/utilities.py +++ b/pyccel/ast/utilities.py @@ -26,6 +26,7 @@ from .mathext import math_mod from .cudaext import cuda_mod +from .cupyext import cupy_mod from .numpyext import (NumpyEmpty, NumpyArray, numpy_mod, NumpyTranspose, NumpyLinspace) from .operators import PyccelAdd, PyccelMul, PyccelIs, PyccelArithmeticOperator @@ -85,6 +86,7 @@ def builtin_function(expr, args=None): builtin_import_registery = Module('__main__', (),(), imports = [ + Import('cupy', AsName(cupy_mod,'cupy')), Import('numpy', AsName(numpy_mod,'numpy')), Import('scipy', AsName(scipy_mod,'scipy')), Import('itertools', AsName(itertools_mod,'itertools')), diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index 811954f9e6..5cb8636c35 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -40,6 +40,8 @@ from pyccel.ast.numpyext import NumpyFull, NumpyArray, NumpyArange from pyccel.ast.numpyext import NumpyReal, NumpyImag, NumpyFloat +from pyccel.ast.cupyext import CupyFull, CupyArray, CupyArange + from pyccel.ast.cudaext import cuda_Internal_Var, CudaArray from pyccel.ast.utilities import expand_to_loops @@ -309,7 +311,6 @@ def get_var_arg(arg, var): arg_code = ', '.join(arg_code_list) cuda_deco = "__global__ " if 'kernel' in expr.decorators else '' - # print(expr, ret_type, name, arg_code) if isinstance(expr, FunctionAddress): return '{}(*{})({})'.format(ret_type, name, arg_code) else: @@ -406,7 +407,11 @@ def _print_Assign(self, expr): return prefix_code+'{};\n'.format(self._print(rhs)) # Inhomogenous tuples are unravelled and therefore do not exist in the c printer - if isinstance(rhs, CudaArray): + if isinstance(rhs, (CupyFull)): + return prefix_code+self.cuda_arrayFill(expr) + if isinstance(rhs, CupyArange): + return prefix_code+self.cuda_Arange(expr) + if isinstance(rhs, (CudaArray, CupyArray)): return prefix_code+self.copy_CudaArray_Data(expr) if isinstance(rhs, (NumpyArray, PythonTuple)): return prefix_code+self.copy_NumpyArray_Data(expr) @@ -418,6 +423,74 @@ def _print_Assign(self, expr): rhs = self._print(expr.rhs) return prefix_code+'{} = {};\n'.format(lhs, rhs) + def arrayFill(self, expr): + """ print the assignment of a NdArray + + parameters + ---------- + expr : PyccelAstNode + The Assign Node used to get the lhs and rhs + Return + ------ + String + Return a str that contains a call to the C function array_fill using Cuda api, + """ + rhs = expr.rhs + lhs = expr.lhs + code_init = '' + declare_dtype = self.find_in_dtype_registry(self._print(rhs.dtype), rhs.precision) + + if rhs.fill_value is not None: + if isinstance(rhs.fill_value, Literal): + code_init += '_array_fill_{0}(({0}){1}, {2});\n'.format(declare_dtype, self._print(rhs.fill_value), self._print(lhs)) + else: + code_init += '_array_fill_{0}({1}, {2});\n'.format(declare_dtype, self._print(rhs.fill_value), self._print(lhs)) + return code_init + + def cuda_Arange(self, expr): + """ print the assignment of a NdArray + + parameters + ---------- + expr : PyccelAstNode + The Assign Node used to get the lhs and rhs + Return + ------ + String + Return a str that contains a call to the C function array_arange using Cuda api, + """ + rhs = expr.rhs + lhs = expr.lhs + code_init = '' + declare_dtype = self.find_in_dtype_registry(self._print(rhs.dtype), rhs.precision) + + code_init += 'cuda_array_arange_{0}<<<1,1>>>({1}, {2});\n'.format(declare_dtype, self._print(lhs), self._print(rhs.start)) + return code_init + + def cuda_arrayFill(self, expr): + """ print the assignment of a NdArray + + parameters + ---------- + expr : PyccelAstNode + The Assign Node used to get the lhs and rhs + Return + ------ + String + Return a str that contains a call to the C function array_fill using Cuda api, + """ + rhs = expr.rhs + lhs = expr.lhs + code_init = '' + declare_dtype = self.find_in_dtype_registry(self._print(rhs.dtype), rhs.precision) + + if rhs.fill_value is not None: + if isinstance(rhs.fill_value, Literal): + code_init += '_cuda_array_fill_{0}<<<1,1>>>(({0}){1}, {2});\n'.format(declare_dtype, self._print(rhs.fill_value), self._print(lhs)) + else: + code_init += '_cuda_array_fill_{0}<<<1,1>>>({1}, {2});\n'.format(declare_dtype, self._print(rhs.fill_value), self._print(lhs)) + return code_init + def copy_CudaArray_Data(self, expr): """ print the assignment of a Cuda NdArray @@ -439,7 +512,7 @@ def copy_CudaArray_Data(self, expr): dummy_array_name = self.scope.get_new_name('cuda_array_dummy') declare_dtype = self.find_in_dtype_registry(self._print(rhs.dtype), rhs.precision) dtype = self.find_in_ndarray_type_registry(self._print(rhs.dtype), rhs.precision) - arg = rhs.arg if isinstance(rhs, CudaArray) else rhs + arg = rhs.arg if isinstance(rhs, (CudaArray, CupyArray)) else rhs if rhs.rank > 1: # flattening the args to use them in C initialization. arg = self._flatten_list(arg) @@ -448,7 +521,7 @@ def copy_CudaArray_Data(self, expr): if isinstance(arg, Variable): arg = self._print(arg) cpy_data = "cudaMemcpy({0}.raw_data, {1}.{2}, {0}.buffer_size, cudaMemcpyHostToDevice);".format(lhs, arg, dtype) - return '%s' % (cpy_data) + return '%s\n' % (cpy_data) else : arg = ', '.join(self._print(i) for i in arg) dummy_array = "%s %s[] = {%s};\n" % (declare_dtype, dummy_array_name, arg) diff --git a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu index ea8220619f..654bbd5170 100644 --- a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu +++ b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu @@ -1,7 +1,31 @@ #include "cuda_ndarrays.h" __global__ -void cuda_array_arange(t_ndarray arr, int start) +void cuda_array_arange_int8_t(t_ndarray arr, int start) +{ + int index = blockIdx.x * blockDim.x + threadIdx.x; + int stride = gridDim.x * blockDim.x; + for(int i = index ; i < arr.length; i+=stride) + arr.nd_int8[i] = (i + start); +} +__global__ +void cuda_array_arange_int32_t(t_ndarray arr, int start) +{ + int index = blockIdx.x * blockDim.x + threadIdx.x; + int stride = gridDim.x * blockDim.x; + for(int i = index ; i < arr.length; i+=stride) + arr.nd_int32[i] = (i + start); +} +__global__ +void cuda_array_arange_int64_t(t_ndarray arr, int start) +{ + int index = blockIdx.x * blockDim.x + threadIdx.x; + int stride = gridDim.x * blockDim.x; + for(int i = index ; i < arr.length; i+=stride) + arr.nd_int64[i] = (i + start); +} +__global__ +void cuda_array_arange_double(t_ndarray arr, int start) { int index = blockIdx.x * blockDim.x + threadIdx.x; int stride = gridDim.x * blockDim.x; @@ -10,13 +34,39 @@ void cuda_array_arange(t_ndarray arr, int start) } __global__ -void cuda_array_fill_int(int64_t c, t_ndarray arr) +void _cuda_array_fill_int8_t(int8_t c, t_ndarray arr) +{ + int index = blockIdx.x * blockDim.x + threadIdx.x; + int stride = gridDim.x * blockDim.x; + for(int i = index ; i < arr.length; i+=stride) + arr.nd_int8[i] = c; +} + +__global__ +void _cuda_array_fill_int32_t(int32_t c, t_ndarray arr) +{ + int index = blockIdx.x * blockDim.x + threadIdx.x; + int stride = gridDim.x * blockDim.x; + for(int i = index ; i < arr.length; i+=stride) + arr.nd_int32[i] = c; +} + +__global__ +void _cuda_array_fill_int64_t(int64_t c, t_ndarray arr) { int index = blockIdx.x * blockDim.x + threadIdx.x; int stride = gridDim.x * blockDim.x; for(int i = index ; i < arr.length; i+=stride) arr.nd_int64[i] = c; } +__global__ +void _cuda_array_fill_double(double c, t_ndarray arr) +{ + int index = blockIdx.x * blockDim.x + threadIdx.x; + int stride = gridDim.x * blockDim.x; + for(int i = index ; i < arr.length; i+=stride) + arr.nd_double[i] = c; +} t_ndarray cuda_array_create(int32_t nd, int64_t *shape, enum e_types type, bool is_view) @@ -65,7 +115,6 @@ t_ndarray cuda_array_create(int32_t nd, int64_t *shape, for (int32_t j = i + 1; j < arr.nd; j++) arr.strides[i] *= arr.shape[j]; } - printf("abbah"); if (!is_view) cudaMallocManaged(&(arr.raw_data), arr.buffer_size); return (arr); diff --git a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h index aad7a81a11..12dc8bbedd 100644 --- a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h +++ b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h @@ -3,6 +3,7 @@ #include "../ndarrays/ndarrays.h" +/* mapping the function array_fill to the correct type */ // enum e_cuda_types // { // nd_bool = 0, @@ -46,9 +47,23 @@ // } t_cuda_ndarray; __global__ -void cuda_array_arange(t_ndarray arr, int start); +void cuda_array_arange_int8_t(t_ndarray arr, int start); __global__ -void cuda_array_fill(int64_t c, t_ndarray arr); +void cuda_array_arange_int32_t(t_ndarray arr, int start); +__global__ +void cuda_array_arange_int64_t(t_ndarray arr, int start); +__global__ +void cuda_array_arange_double(t_ndarray arr, int start); + +__global__ +void _cuda_array_fill_int8_t(int8_t c, t_ndarray arr); +__global__ +void _cuda_array_fill_int32_t(int32_t c, t_ndarray arr); +__global__ +void _cuda_array_fill_int64_t(int64_t c, t_ndarray arr); +__global__ +void _cuda_array_fill_double(double c, t_ndarray arr); + t_ndarray cuda_array_create(int32_t nd, int64_t *shape, enum e_types type, bool is_view); int32_t cuda_free_array(t_ndarray dump); int32_t cuda_free_pointer(t_ndarray dump); diff --git a/pyccel/stdlib/ndarrays/ndarrays.c b/pyccel/stdlib/ndarrays/ndarrays.c index c02c43d524..b83f8ad38b 100644 --- a/pyccel/stdlib/ndarrays/ndarrays.c +++ b/pyccel/stdlib/ndarrays/ndarrays.c @@ -118,7 +118,7 @@ void stack_array_init(t_ndarray *arr) } } -void _array_fill_int8(int8_t c, t_ndarray arr) +void _array_fill_int8_t(int8_t c, t_ndarray arr) { if (c == 0) memset(arr.raw_data, 0, arr.buffer_size); @@ -127,7 +127,7 @@ void _array_fill_int8(int8_t c, t_ndarray arr) arr.nd_int8[i] = c; } -void _array_fill_int16(int16_t c, t_ndarray arr) +void _array_fill_int16_t(int16_t c, t_ndarray arr) { if (c == 0) memset(arr.raw_data, 0, arr.buffer_size); @@ -136,7 +136,7 @@ void _array_fill_int16(int16_t c, t_ndarray arr) arr.nd_int16[i] = c; } -void _array_fill_int32(int32_t c, t_ndarray arr) +void _array_fill_int32_t(int32_t c, t_ndarray arr) { if (c == 0) memset(arr.raw_data, 0, arr.buffer_size); @@ -145,7 +145,7 @@ void _array_fill_int32(int32_t c, t_ndarray arr) arr.nd_int32[i] = c; } -void _array_fill_int64(int64_t c, t_ndarray arr) +void _array_fill_int64_t(int64_t c, t_ndarray arr) { if (c == 0) memset(arr.raw_data, 0, arr.buffer_size); diff --git a/pyccel/stdlib/ndarrays/ndarrays.h b/pyccel/stdlib/ndarrays/ndarrays.h index ffb4c1e50d..28bdd146a0 100644 --- a/pyccel/stdlib/ndarrays/ndarrays.h +++ b/pyccel/stdlib/ndarrays/ndarrays.h @@ -13,16 +13,6 @@ #ifdef __cplusplus extern "C" { #endif -/* mapping the function array_fill to the correct type */ -# define array_fill(c, arr) _Generic((c), int64_t : _array_fill_int64,\ - int32_t : _array_fill_int32,\ - int16_t : _array_fill_int16,\ - int8_t : _array_fill_int8,\ - float : _array_fill_float,\ - double : _array_fill_double,\ - bool : _array_fill_bool,\ - float complex : _array_fill_cfloat,\ - double complex : _array_fill_cdouble)(c, arr) typedef struct s_slice { @@ -116,10 +106,10 @@ typedef struct s_ndarray void stack_array_init(t_ndarray *arr); t_ndarray array_create(int32_t nd, int64_t *shape, enum e_types type, bool is_view); -void _array_fill_int8(int8_t c, t_ndarray arr); -void _array_fill_int16(int16_t c, t_ndarray arr); -void _array_fill_int32(int32_t c, t_ndarray arr); -void _array_fill_int64(int64_t c, t_ndarray arr); +void _array_fill_int8_t(int8_t c, t_ndarray arr); +void _array_fill_int16_t(int16_t c, t_ndarray arr); +void _array_fill_int32_t(int32_t c, t_ndarray arr); +void _array_fill_int64_t(int64_t c, t_ndarray arr); void _array_fill_float(float c, t_ndarray arr); void _array_fill_double(double c, t_ndarray arr); void _array_fill_bool(bool c, t_ndarray arr); From 75d0e3607bb9c7af6bef94f455ad8428456369b8 Mon Sep 17 00:00:00 2001 From: bauom Date: Fri, 16 Sep 2022 20:24:05 +0100 Subject: [PATCH 031/193] fixed type of cuda_ndaarays funcs --- pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu index 654bbd5170..87890c2ecb 100644 --- a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu +++ b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu @@ -4,33 +4,29 @@ __global__ void cuda_array_arange_int8_t(t_ndarray arr, int start) { int index = blockIdx.x * blockDim.x + threadIdx.x; - int stride = gridDim.x * blockDim.x; - for(int i = index ; i < arr.length; i+=stride) + for(int i = index ; i < arr.length; i+=1) arr.nd_int8[i] = (i + start); } __global__ void cuda_array_arange_int32_t(t_ndarray arr, int start) { int index = blockIdx.x * blockDim.x + threadIdx.x; - int stride = gridDim.x * blockDim.x; - for(int i = index ; i < arr.length; i+=stride) + for(int i = index ; i < arr.length; i+=1) arr.nd_int32[i] = (i + start); } __global__ void cuda_array_arange_int64_t(t_ndarray arr, int start) { int index = blockIdx.x * blockDim.x + threadIdx.x; - int stride = gridDim.x * blockDim.x; - for(int i = index ; i < arr.length; i+=stride) + for(int i = index ; i < arr.length; i+=1) arr.nd_int64[i] = (i + start); } __global__ void cuda_array_arange_double(t_ndarray arr, int start) { int index = blockIdx.x * blockDim.x + threadIdx.x; - int stride = gridDim.x * blockDim.x; - for(int i = index ; i < arr.length; i+=stride) - arr.nd_int64[i] = (i + start); + for(int i = index ; i < arr.length; i+=1) + arr.nd_double[i] = (i + start); } __global__ From f6b8d602eb03234e38429d96a1b31c430d1f7427 Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 20 Sep 2022 14:22:11 +0100 Subject: [PATCH 032/193] temp branch waiting for cuda_main to be merged --- tests/internal/scripts/ccuda/kernel.py | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/internal/scripts/ccuda/kernel.py diff --git a/tests/internal/scripts/ccuda/kernel.py b/tests/internal/scripts/ccuda/kernel.py new file mode 100644 index 0000000000..86d77418c6 --- /dev/null +++ b/tests/internal/scripts/ccuda/kernel.py @@ -0,0 +1,8 @@ +from pyccel.decorators import kernel, types +from pyccel import cuda + +@kernel +@types('int[:]') +def func(a): + i = cuda.threadIdx(0) + cuda.blockIdx(0) * cuda.blockDim(0) + print("Hello World! ", a[i]) From 52f4bdc5cc46372f9a374dc4417f6b3b7643cc84 Mon Sep 17 00:00:00 2001 From: Fatima-zahra RAMDANI Date: Tue, 20 Sep 2022 13:51:44 +0100 Subject: [PATCH 033/193] extern "C" to function_signature added --- pyccel/codegen/printing/ccudacode.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index 5cb8636c35..7292ec0361 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -289,6 +289,7 @@ def function_signature(self, expr, print_arg_names = True): """ args = list(expr.arguments) + extern_word = 'extern "C" ' if len(expr.results) == 1: ret_type = self.get_declare_type(expr.results[0]) elif len(expr.results) > 1: @@ -312,9 +313,9 @@ def get_var_arg(arg, var): cuda_deco = "__global__ " if 'kernel' in expr.decorators else '' if isinstance(expr, FunctionAddress): - return '{}(*{})({})'.format(ret_type, name, arg_code) + return '{}{}(*{})({})'.format(extern_word, ret_type, name, arg_code) else: - return '{}{}{}({})'.format(cuda_deco, ret_type, name, arg_code) + return '{}{}{}{}({})'.format(extern_word, cuda_deco, ret_type, name, arg_code) def _print_Allocate(self, expr): free_code = '' From 17f4d36b4ed497e33ccd2f83d62a5cce397328b7 Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 20 Sep 2022 16:33:27 +0100 Subject: [PATCH 034/193] fixed types in array_fill funcs --- pyccel/stdlib/ndarrays/ndarrays.c | 18 +++++++++--------- pyccel/stdlib/ndarrays/ndarrays.h | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/pyccel/stdlib/ndarrays/ndarrays.c b/pyccel/stdlib/ndarrays/ndarrays.c index b83f8ad38b..75eefc7b48 100644 --- a/pyccel/stdlib/ndarrays/ndarrays.c +++ b/pyccel/stdlib/ndarrays/ndarrays.c @@ -118,7 +118,7 @@ void stack_array_init(t_ndarray *arr) } } -void _array_fill_int8_t(int8_t c, t_ndarray arr) +void array_fill_int8(int8_t c, t_ndarray arr) { if (c == 0) memset(arr.raw_data, 0, arr.buffer_size); @@ -127,7 +127,7 @@ void _array_fill_int8_t(int8_t c, t_ndarray arr) arr.nd_int8[i] = c; } -void _array_fill_int16_t(int16_t c, t_ndarray arr) +void array_fill_int16(int16_t c, t_ndarray arr) { if (c == 0) memset(arr.raw_data, 0, arr.buffer_size); @@ -136,7 +136,7 @@ void _array_fill_int16_t(int16_t c, t_ndarray arr) arr.nd_int16[i] = c; } -void _array_fill_int32_t(int32_t c, t_ndarray arr) +void array_fill_int32(int32_t c, t_ndarray arr) { if (c == 0) memset(arr.raw_data, 0, arr.buffer_size); @@ -145,7 +145,7 @@ void _array_fill_int32_t(int32_t c, t_ndarray arr) arr.nd_int32[i] = c; } -void _array_fill_int64_t(int64_t c, t_ndarray arr) +void array_fill_int64(int64_t c, t_ndarray arr) { if (c == 0) memset(arr.raw_data, 0, arr.buffer_size); @@ -154,7 +154,7 @@ void _array_fill_int64_t(int64_t c, t_ndarray arr) arr.nd_int64[i] = c; } -void _array_fill_bool(bool c, t_ndarray arr) +void array_fill_bool(bool c, t_ndarray arr) { if (c == 0) memset(arr.raw_data, 0, arr.buffer_size); @@ -163,7 +163,7 @@ void _array_fill_bool(bool c, t_ndarray arr) arr.nd_bool[i] = c; } -void _array_fill_float(float c, t_ndarray arr) +void array_fill_float(float c, t_ndarray arr) { if (c == 0) memset(arr.raw_data, 0, arr.buffer_size); @@ -172,7 +172,7 @@ void _array_fill_float(float c, t_ndarray arr) arr.nd_float[i] = c; } -void _array_fill_double(double c, t_ndarray arr) +void array_fill_double(double c, t_ndarray arr) { if (c == 0) memset(arr.raw_data, 0, arr.buffer_size); @@ -182,7 +182,7 @@ void _array_fill_double(double c, t_ndarray arr) } #ifndef __NVCC__ -void _array_fill_cfloat(float complex c, t_ndarray arr) +void array_fill_cfloat(float complex c, t_ndarray arr) { if (c == 0) memset(arr.raw_data, 0, arr.buffer_size); @@ -192,7 +192,7 @@ void _array_fill_cfloat(float complex c, t_ndarray arr) } -void _array_fill_cdouble(double complex c, t_ndarray arr) +void array_fill_cdouble(double complex c, t_ndarray arr) { if (c == 0) memset(arr.raw_data, 0, arr.buffer_size); diff --git a/pyccel/stdlib/ndarrays/ndarrays.h b/pyccel/stdlib/ndarrays/ndarrays.h index 28bdd146a0..5cdd9c8cbe 100644 --- a/pyccel/stdlib/ndarrays/ndarrays.h +++ b/pyccel/stdlib/ndarrays/ndarrays.h @@ -106,16 +106,16 @@ typedef struct s_ndarray void stack_array_init(t_ndarray *arr); t_ndarray array_create(int32_t nd, int64_t *shape, enum e_types type, bool is_view); -void _array_fill_int8_t(int8_t c, t_ndarray arr); -void _array_fill_int16_t(int16_t c, t_ndarray arr); -void _array_fill_int32_t(int32_t c, t_ndarray arr); -void _array_fill_int64_t(int64_t c, t_ndarray arr); -void _array_fill_float(float c, t_ndarray arr); -void _array_fill_double(double c, t_ndarray arr); -void _array_fill_bool(bool c, t_ndarray arr); +void array_fill_int8(int8_t c, t_ndarray arr); +void array_fill_int16(int16_t c, t_ndarray arr); +void array_fill_int32(int32_t c, t_ndarray arr); +void array_fill_int64(int64_t c, t_ndarray arr); +void array_fill_float(float c, t_ndarray arr); +void array_fill_double(double c, t_ndarray arr); +void array_fill_bool(bool c, t_ndarray arr); #ifndef __NVCC__ -void _array_fill_cfloat(float complex c, t_ndarray arr); -void _array_fill_cdouble(double complex c, t_ndarray arr); +void array_fill_cfloat(float complex c, t_ndarray arr); +void array_fill_cdouble(double complex c, t_ndarray arr); #endif /* slicing */ From 3363001ad20c7175ca4a84ad4aa200f3d192fe02 Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 20 Sep 2022 17:37:16 +0100 Subject: [PATCH 035/193] updated CudaArray process shape --- pyccel/ast/cudaext.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index 58fd66ce74..7846c9ba60 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -50,6 +50,7 @@ class CudaNewArray(PyccelInternalFunction): """ Class from which all Cuda functions which imply a call to Allocate inherit """ + __slots__ = () #-------------------------------------------------------------------------- @staticmethod def _process_order(rank, order): @@ -91,7 +92,11 @@ def __init__(self, arg, dtype=None, order='C'): # Verify dtype and get precision if dtype is None: dtype = arg.dtype - dtype, prec = process_dtype(dtype) + prec = get_final_precision(arg) + else: + dtype, prec = process_dtype(dtype) + # ... Determine ordering + order = str(order).strip("\'") shape = process_shape(False, arg.shape) rank = len(shape) From 37d4d8f358e8d35fd1a51258723e4d01f857b2fc Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 20 Sep 2022 17:38:56 +0100 Subject: [PATCH 036/193] removed linker options as they don't work with nvcc --- pyccel/codegen/compiling/compilers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyccel/codegen/compiling/compilers.py b/pyccel/codegen/compiling/compilers.py index 9e4edf023a..cfb5a031e3 100644 --- a/pyccel/codegen/compiling/compilers.py +++ b/pyccel/codegen/compiling/compilers.py @@ -379,7 +379,7 @@ def compile_shared_library(self, compile_obj, output_folder, verbose = False, sh # Collect compile information exec_cmd, includes, libs_flags, libdirs_flags, m_code = \ self._get_compile_components(compile_obj, accelerators) - linker_libdirs_flags = ['-Wl,-rpath' if l == '-L' else l for l in libdirs_flags] + linker_libdirs_flags = ['-Wl,-rpath' if l == '-L' and self._info['exec'] != 'nvcc' else l for l in libdirs_flags] flags.insert(0,"-shared") From c757181e5c25868f73fdd68248530b7d2bf76fe5 Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 20 Sep 2022 17:41:05 +0100 Subject: [PATCH 037/193] fixed typo --- pyccel/codegen/pipeline.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pyccel/codegen/pipeline.py b/pyccel/codegen/pipeline.py index bf332dff5a..3f61edbb66 100644 --- a/pyccel/codegen/pipeline.py +++ b/pyccel/codegen/pipeline.py @@ -198,9 +198,10 @@ def handle_error(stage): if compiler is None: compiler = 'GNU' - # Choose cuda vendor - if language == 'ccuda': + # Choose cuda compiler + if language is 'ccuda': compiler = 'nvidia' + fflags = [] if fflags is None else fflags.split() wrapper_flags = [] if wrapper_flags is None else wrapper_flags.split() From eedc616c309a90ed70c20ba5a1374fe294d76534 Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 20 Sep 2022 17:42:28 +0100 Subject: [PATCH 038/193] updated test for array_fill --- tests/ndarrays/test_ndarrays.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/ndarrays/test_ndarrays.c b/tests/ndarrays/test_ndarrays.c index f145d17d2f..bf1aa7191b 100644 --- a/tests/ndarrays/test_ndarrays.c +++ b/tests/ndarrays/test_ndarrays.c @@ -609,7 +609,7 @@ int32_t test_array_fill_int64(void) int64_t c_value; x = array_create(2, m_1_shape, nd_int64, false); - array_fill((int64_t)32, x); + array_fill_int64((int64_t)32, x); // testing the index [3, 1] index = 3 * x.strides[0] + 1 * x.strides[1]; c_index = 7; @@ -633,7 +633,7 @@ int32_t test_array_fill_int32(void) int32_t c_value; x = array_create(2, m_1_shape, nd_int32, false); - array_fill((int32_t)32, x); + array_fill_int32((int32_t)32, x); // testing the index [3, 1] index = 3 * x.strides[0] + 1 * x.strides[1]; c_index = 7; @@ -657,7 +657,7 @@ int32_t test_array_fill_int16(void) int16_t c_value; x = array_create(2, m_1_shape, nd_int16, false); - array_fill((int16_t)32, x); + array_fill_int16((int16_t)32, x); // testing the index [3, 1] index = 3 * x.strides[0] + 1 * x.strides[1]; c_index = 7; @@ -681,7 +681,7 @@ int32_t test_array_fill_int8(void) int8_t c_value; x = array_create(2, m_1_shape, nd_int8, false); - array_fill((int8_t)32, x); + array_fill_int8((int8_t)32, x); // testing the index [3, 1] index = 3 * x.strides[0] + 1 * x.strides[1]; c_index = 7; @@ -705,7 +705,7 @@ int32_t test_array_fill_double(void) double c_value; x = array_create(2, m_1_shape, nd_double, false); - array_fill(2., x); + array_fill_double(2., x); // testing the index [3, 1] index = 3 * x.strides[0] + 1 * x.strides[1]; c_index = 7; @@ -729,7 +729,7 @@ int32_t test_array_fill_cdouble(void) double complex c_value; x = array_create(2, m_1_shape, nd_cdouble, false); - array_fill(0.3+0.54*I, x); + array_fill_cdouble(0.3+0.54*I, x); // testing the index [3, 1] index = 3 * x.strides[0] + 1 * x.strides[1]; c_index = 7; @@ -755,7 +755,7 @@ int32_t test_array_zeros_double(void) double c_value; x = array_create(2, m_1_shape, nd_double, false); - array_fill(0, x); + array_fill_double(0, x); // testing the index [3, 1] index = 3 * x.strides[0] + 1 * x.strides[1]; c_index = 7; @@ -779,7 +779,7 @@ int32_t test_array_zeros_int32(void) int32_t c_value; x = array_create(2, m_1_shape, nd_int32, false); - array_fill(0, x); + array_fill_int32(0, x); // testing the index [3, 1] index = 3 * x.strides[0] + 1 * x.strides[1]; c_index = 7; @@ -803,7 +803,7 @@ int32_t test_array_zeros_cdouble(void) double complex c_value; x = array_create(2, m_1_shape, nd_cdouble, false); - array_fill(0, x); + array_fill_cdouble(0, x); // testing the index [3, 1] index = 3 * x.strides[0] + 1 * x.strides[1]; c_index = 7; From dff7a9f7661ec474b32d21a4966e1bf96f6b302c Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 20 Sep 2022 18:02:29 +0100 Subject: [PATCH 039/193] added cuda tests --- .github/actions/pytest_run_cuda/action.yml | 16 ++++++++++++++++ .github/workflows/Cuda_pytest.yml | 19 ++++++++----------- pytest.ini | 1 + tests/internal/scripts/ccuda/cupy_arange.py | 17 +++++++++++++++++ tests/internal/scripts/ccuda/cupy_array.py | 17 +++++++++++++++++ tests/internal/scripts/ccuda/kernel_launch.py | 16 ++++++++++++++++ tests/internal/test_internal.py | 18 ++++++++++++++++++ 7 files changed, 93 insertions(+), 11 deletions(-) create mode 100644 .github/actions/pytest_run_cuda/action.yml create mode 100644 tests/internal/scripts/ccuda/cupy_arange.py create mode 100644 tests/internal/scripts/ccuda/cupy_array.py create mode 100644 tests/internal/scripts/ccuda/kernel_launch.py diff --git a/.github/actions/pytest_run_cuda/action.yml b/.github/actions/pytest_run_cuda/action.yml new file mode 100644 index 0000000000..8f82dda10f --- /dev/null +++ b/.github/actions/pytest_run_cuda/action.yml @@ -0,0 +1,16 @@ +name: 'Pyccel pytest commands generating Ccuda' +inputs: + shell_cmd: + description: 'Specifies the shell command (different for anaconda)' + required: false + default: "bash" + +runs: + using: "composite" + steps: + - name: Ccuda tests with pytest + run: | + python -m pytest -n auto -rx -m "not (parallel or xdist_incompatible) and ccuda" --ignore=symbolic --ignore=ndarrays + pyccel-clean + shell: ${{ inputs.shell_cmd }} + working-directory: ./tests diff --git a/.github/workflows/Cuda_pytest.yml b/.github/workflows/Cuda_pytest.yml index 773f587549..7e4fd5b9a8 100644 --- a/.github/workflows/Cuda_pytest.yml +++ b/.github/workflows/Cuda_pytest.yml @@ -17,17 +17,14 @@ jobs: uses: ./.github/actions/linux_install - name: Install python dependencies uses: ./.github/actions/pip_installation - - name: Coverage install - uses: ./.github/actions/coverage_install - - name: Collect coverage information - continue-on-error: True - uses: ./.github/actions/coverage_collection - - name: Run codacy-coverage-reporter - uses: codacy/codacy-coverage-reporter-action@master - continue-on-error: True - with: - project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} - coverage-reports: cobertura.xml + - name: Fortran/C tests with pytest + uses: ./.github/actions/pytest_run + - name: cuda tests with pytest + uses: ./.github/actions/pytest_run_cuda + - name: Python tests with pytest + uses: ./.github/actions/pytest_run_python + - name: Parallel tests with pytest + uses: ./.github/actions/pytest_parallel # Windows: diff --git a/pytest.ini b/pytest.ini index 42eb0d72ba..41cd7cdace 100644 --- a/pytest.ini +++ b/pytest.ini @@ -6,6 +6,7 @@ markers = parallel: test to be run using 'mpiexec' fortran: test to generate fortran code c: test to generate c code + ccuda: test to generate Ccuda code python: test to generate python code xdist_incompatible: test which compiles a file also compiled by another test external: test using an external dll (problematic with conda on Windows) diff --git a/tests/internal/scripts/ccuda/cupy_arange.py b/tests/internal/scripts/ccuda/cupy_arange.py new file mode 100644 index 0000000000..c3b84588c6 --- /dev/null +++ b/tests/internal/scripts/ccuda/cupy_arange.py @@ -0,0 +1,17 @@ +from pyccel.decorators import kernel, types +from pyccel import cuda +import cupy as cp + +@kernel +@types('int[:]') +def func(a): + i = cuda.threadIdx(0) + cuda.blockIdx(0) * cuda.blockDim(0) + print("Hello World! ", a[i]) + +if __name__ == '__main__': + threads_per_block = 32 + n_blocks = 1 + arr = cp.arange(32) + cuda.deviceSynchronize() + func[n_blocks, threads_per_block](arr) + cuda.deviceSynchronize() \ No newline at end of file diff --git a/tests/internal/scripts/ccuda/cupy_array.py b/tests/internal/scripts/ccuda/cupy_array.py new file mode 100644 index 0000000000..bf739ae7d5 --- /dev/null +++ b/tests/internal/scripts/ccuda/cupy_array.py @@ -0,0 +1,17 @@ +from pyccel.decorators import kernel, types +from pyccel import cuda +import cupy as cp + +@kernel +@types('int[:]') +def func(a): + i = cuda.threadIdx(0) + cuda.blockIdx(0) * cuda.blockDim(0) + print("Hello World! ", a[i]) + +if __name__ == '__main__': + threads_per_block = 5 + n_blocks = 1 + arr = cp.array([0, 1, 2, 3, 4]) + cuda.deviceSynchronize() + func[n_blocks, threads_per_block](arr) + cuda.deviceSynchronize() \ No newline at end of file diff --git a/tests/internal/scripts/ccuda/kernel_launch.py b/tests/internal/scripts/ccuda/kernel_launch.py new file mode 100644 index 0000000000..16cf40fbdc --- /dev/null +++ b/tests/internal/scripts/ccuda/kernel_launch.py @@ -0,0 +1,16 @@ +from pyccel.decorators import kernel, types +from pyccel import cuda + +@kernel +@types('int[:]') +def func(a): + i = cuda.threadIdx(0) + cuda.blockIdx(0) * cuda.blockDim(0) + print("Hello World! ", a[i]) + +if __name__ == '__main__': + threads_per_block = 5 + n_blocks = 1 + arr = cuda.array([0, 1, 2, 3, 4]) + cuda.deviceSynchronize() + func[n_blocks, threads_per_block](arr) + cuda.deviceSynchronize() \ No newline at end of file diff --git a/tests/internal/test_internal.py b/tests/internal/test_internal.py index 308a8251d0..dde67e5578 100644 --- a/tests/internal/test_internal.py +++ b/tests/internal/test_internal.py @@ -41,6 +41,14 @@ def test_mpi(f): def test_openmp(f, language): execute_pyccel(f, accelerators=['openmp'], language=language) +@pytest.mark.parametrize("f", get_files_from_folder('ccuda')) +@pytest.mark.parametrize( 'language', + (pytest.param("ccuda", marks = pytest.mark.ccuda),) +) +@pytest.mark.external +def test_ccuda(f, language): + execute_pyccel(f, language=language) + #@pytest.mark.parametrize("f", get_files_from_folder('openacc')) #@pytest.mark.external #def test_openacc(): @@ -98,3 +106,13 @@ def test_openmp(f, language): # print('> testing {0}'.format(str(os.path.basename(f)))) # test_openacc(f) # print('\n') + + print('*********************************') + print('*** ***') + print('*** TESTING INTERNAL/Cuda ***') + print('*** ***') + print('*********************************') + for f in get_files_from_folder('ccuda'): + print('> testing {0}'.format(str(os.path.basename(f)))) + test_ccuda(f) + print('\n') From 6dc0be44ea921ca53c38f9d47c75c40ba0e55392 Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 20 Sep 2022 18:06:33 +0100 Subject: [PATCH 040/193] eq in place of is --- pyccel/codegen/pipeline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyccel/codegen/pipeline.py b/pyccel/codegen/pipeline.py index 3f61edbb66..912a78cb9e 100644 --- a/pyccel/codegen/pipeline.py +++ b/pyccel/codegen/pipeline.py @@ -199,7 +199,7 @@ def handle_error(stage): compiler = 'GNU' # Choose cuda compiler - if language is 'ccuda': + if language == 'ccuda': compiler = 'nvidia' fflags = [] if fflags is None else fflags.split() From f668acdde776408b01e8c1e139b84bc5dfdc8f62 Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 20 Sep 2022 18:23:33 +0100 Subject: [PATCH 041/193] capitalized first char of ccuda --- .github/workflows/Cuda_pytest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Cuda_pytest.yml b/.github/workflows/Cuda_pytest.yml index 7e4fd5b9a8..2ee1cc56d2 100644 --- a/.github/workflows/Cuda_pytest.yml +++ b/.github/workflows/Cuda_pytest.yml @@ -19,7 +19,7 @@ jobs: uses: ./.github/actions/pip_installation - name: Fortran/C tests with pytest uses: ./.github/actions/pytest_run - - name: cuda tests with pytest + - name: Ccuda tests with pytest uses: ./.github/actions/pytest_run_cuda - name: Python tests with pytest uses: ./.github/actions/pytest_run_python From 7ce11992e664afff49ed5d2164192c1adfde6afe Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 20 Sep 2022 18:27:08 +0100 Subject: [PATCH 042/193] fixed trailing type for array_fill functions --- pyccel/codegen/printing/ccode.py | 5 +++-- pyccel/codegen/printing/ccudacode.py | 14 +++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/pyccel/codegen/printing/ccode.py b/pyccel/codegen/printing/ccode.py index 747c37a68a..8109eef4cd 100644 --- a/pyccel/codegen/printing/ccode.py +++ b/pyccel/codegen/printing/ccode.py @@ -342,11 +342,12 @@ def arrayFill(self, expr): code_init = '' declare_dtype = self.find_in_dtype_registry(self._print(rhs.dtype), rhs.precision) + ftype = declare_dtype if self._print(rhs.dtype) != 'int' else declare_dtype[:-2] if rhs.fill_value is not None: if isinstance(rhs.fill_value, Literal): - code_init += 'array_fill(({0}){1}, {2});\n'.format(declare_dtype, self._print(rhs.fill_value), self._print(lhs)) + code_init += 'array_fill_{0}(({1}){2}, {3});\n'.format(ftype, declare_dtype, self._print(rhs.fill_value), self._print(lhs)) else: - code_init += 'array_fill({0}, {1});\n'.format(self._print(rhs.fill_value), self._print(lhs)) + code_init += 'array_fill_{0}({1}, {2});\n'.format(ftype, self._print(rhs.fill_value), self._print(lhs)) return code_init def _init_stack_array(self, expr): diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index 7292ec0361..8261d64c30 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -440,12 +440,13 @@ def arrayFill(self, expr): lhs = expr.lhs code_init = '' declare_dtype = self.find_in_dtype_registry(self._print(rhs.dtype), rhs.precision) + ftype = declare_dtype if self._print(rhs.dtype) != 'int' else declare_dtype[:-2] if rhs.fill_value is not None: if isinstance(rhs.fill_value, Literal): - code_init += '_array_fill_{0}(({0}){1}, {2});\n'.format(declare_dtype, self._print(rhs.fill_value), self._print(lhs)) + code_init += 'cuda_array_fill_{0}(({1}){2}, {3});\n'.format(ftype, declare_dtype, self._print(rhs.fill_value), self._print(lhs)) else: - code_init += '_array_fill_{0}({1}, {2});\n'.format(declare_dtype, self._print(rhs.fill_value), self._print(lhs)) + code_init += 'cuda_array_fill_{0}({1}, {2});\n'.format(ftype, self._print(rhs.fill_value), self._print(lhs)) return code_init def cuda_Arange(self, expr): @@ -464,8 +465,10 @@ def cuda_Arange(self, expr): lhs = expr.lhs code_init = '' declare_dtype = self.find_in_dtype_registry(self._print(rhs.dtype), rhs.precision) + ftype = declare_dtype if self._print(rhs.dtype) != 'int' else declare_dtype[:-2] - code_init += 'cuda_array_arange_{0}<<<1,1>>>({1}, {2});\n'.format(declare_dtype, self._print(lhs), self._print(rhs.start)) + #TODO: calculate best thread number to run the kernel + code_init += 'cuda_array_arange_{0}<<<1,32>>>({1}, {2});\n'.format(ftype, self._print(lhs), self._print(rhs.start)) return code_init def cuda_arrayFill(self, expr): @@ -484,12 +487,13 @@ def cuda_arrayFill(self, expr): lhs = expr.lhs code_init = '' declare_dtype = self.find_in_dtype_registry(self._print(rhs.dtype), rhs.precision) + ftype = declare_dtype if self._print(rhs.dtype) != 'int' else declare_dtype[:-2] if rhs.fill_value is not None: if isinstance(rhs.fill_value, Literal): - code_init += '_cuda_array_fill_{0}<<<1,1>>>(({0}){1}, {2});\n'.format(declare_dtype, self._print(rhs.fill_value), self._print(lhs)) + code_init += 'cuda_array_fill_{0}<<<1,1>>>(({1}){2}, {3});\n'.format(ftype, declare_dtype, self._print(rhs.fill_value), self._print(lhs)) else: - code_init += '_cuda_array_fill_{0}<<<1,1>>>({1}, {2});\n'.format(declare_dtype, self._print(rhs.fill_value), self._print(lhs)) + code_init += 'cuda_array_fill_{0}<<<1,1>>>({1}, {2});\n'.format(ftype, self._print(rhs.fill_value), self._print(lhs)) return code_init def copy_CudaArray_Data(self, expr): From bf9d7d54734b75bb3d752df63d035ce1330125e4 Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 20 Sep 2022 18:53:06 +0100 Subject: [PATCH 043/193] fixed trailing types --- pyccel/codegen/printing/ccode.py | 9 +++++---- pyccel/codegen/printing/ccudacode.py | 19 +++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/pyccel/codegen/printing/ccode.py b/pyccel/codegen/printing/ccode.py index 8109eef4cd..6300ffb982 100644 --- a/pyccel/codegen/printing/ccode.py +++ b/pyccel/codegen/printing/ccode.py @@ -341,13 +341,14 @@ def arrayFill(self, expr): lhs = expr.lhs code_init = '' declare_dtype = self.find_in_dtype_registry(self._print(rhs.dtype), rhs.precision) - - ftype = declare_dtype if self._print(rhs.dtype) != 'int' else declare_dtype[:-2] + dtype = self.find_in_ndarray_type_registry(self._print(rhs.dtype), rhs.precision) + dtype = dtype[3:] + if rhs.fill_value is not None: if isinstance(rhs.fill_value, Literal): - code_init += 'array_fill_{0}(({1}){2}, {3});\n'.format(ftype, declare_dtype, self._print(rhs.fill_value), self._print(lhs)) + code_init += 'array_fill_{0}(({1}){2}, {3});\n'.format(dtype, declare_dtype, self._print(rhs.fill_value), self._print(lhs)) else: - code_init += 'array_fill_{0}({1}, {2});\n'.format(ftype, self._print(rhs.fill_value), self._print(lhs)) + code_init += 'array_fill_{0}({1}, {2});\n'.format(dtype, self._print(rhs.fill_value), self._print(lhs)) return code_init def _init_stack_array(self, expr): diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index 8261d64c30..fcf7d62eb2 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -440,13 +440,14 @@ def arrayFill(self, expr): lhs = expr.lhs code_init = '' declare_dtype = self.find_in_dtype_registry(self._print(rhs.dtype), rhs.precision) - ftype = declare_dtype if self._print(rhs.dtype) != 'int' else declare_dtype[:-2] + dtype = self.find_in_ndarray_type_registry(self._print(rhs.dtype), rhs.precision) + dtype = dtype[3:] if rhs.fill_value is not None: if isinstance(rhs.fill_value, Literal): - code_init += 'cuda_array_fill_{0}(({1}){2}, {3});\n'.format(ftype, declare_dtype, self._print(rhs.fill_value), self._print(lhs)) + code_init += 'cuda_array_fill_{0}(({1}){2}, {3});\n'.format(dtype, declare_dtype, self._print(rhs.fill_value), self._print(lhs)) else: - code_init += 'cuda_array_fill_{0}({1}, {2});\n'.format(ftype, self._print(rhs.fill_value), self._print(lhs)) + code_init += 'cuda_array_fill_{0}({1}, {2});\n'.format(dtype, self._print(rhs.fill_value), self._print(lhs)) return code_init def cuda_Arange(self, expr): @@ -465,10 +466,11 @@ def cuda_Arange(self, expr): lhs = expr.lhs code_init = '' declare_dtype = self.find_in_dtype_registry(self._print(rhs.dtype), rhs.precision) - ftype = declare_dtype if self._print(rhs.dtype) != 'int' else declare_dtype[:-2] + dtype = self.find_in_ndarray_type_registry(self._print(rhs.dtype), rhs.precision) + dtype = dtype[3:] #TODO: calculate best thread number to run the kernel - code_init += 'cuda_array_arange_{0}<<<1,32>>>({1}, {2});\n'.format(ftype, self._print(lhs), self._print(rhs.start)) + code_init += 'cuda_array_arange_{0}<<<1,32>>>({1}, {2});\n'.format(dtype, self._print(lhs), self._print(rhs.start)) return code_init def cuda_arrayFill(self, expr): @@ -487,13 +489,14 @@ def cuda_arrayFill(self, expr): lhs = expr.lhs code_init = '' declare_dtype = self.find_in_dtype_registry(self._print(rhs.dtype), rhs.precision) - ftype = declare_dtype if self._print(rhs.dtype) != 'int' else declare_dtype[:-2] + dtype = self.find_in_ndarray_type_registry(self._print(rhs.dtype), rhs.precision) + dtype = dtype[3:] if rhs.fill_value is not None: if isinstance(rhs.fill_value, Literal): - code_init += 'cuda_array_fill_{0}<<<1,1>>>(({1}){2}, {3});\n'.format(ftype, declare_dtype, self._print(rhs.fill_value), self._print(lhs)) + code_init += 'cuda_array_fill_{0}<<<1,1>>>(({1}){2}, {3});\n'.format(dtype, declare_dtype, self._print(rhs.fill_value), self._print(lhs)) else: - code_init += 'cuda_array_fill_{0}<<<1,1>>>({1}, {2});\n'.format(ftype, self._print(rhs.fill_value), self._print(lhs)) + code_init += 'cuda_array_fill_{0}<<<1,1>>>({1}, {2});\n'.format(dtype, self._print(rhs.fill_value), self._print(lhs)) return code_init def copy_CudaArray_Data(self, expr): From 740ff35505b80204f5e37df028f5d4474334e5fe Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 20 Sep 2022 22:24:51 +0100 Subject: [PATCH 044/193] specify only fortran test instead of not c or python --- .github/actions/pytest_run/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/pytest_run/action.yml b/.github/actions/pytest_run/action.yml index 6bec971da6..ddafb0f782 100644 --- a/.github/actions/pytest_run/action.yml +++ b/.github/actions/pytest_run/action.yml @@ -15,8 +15,8 @@ runs: python -m pytest -n auto -rX -m "not (parallel or xdist_incompatible) and c" --ignore=symbolic --ignore=ndarrays python -m pytest -rX -m "xdist_incompatible and not parallel and c" --ignore=symbolic --ignore=ndarrays pyccel-clean - python -m pytest -n auto -rX -m "not (parallel or xdist_incompatible) and not (c or python)" --ignore=symbolic --ignore=ndarrays - python -m pytest -rX -m "xdist_incompatible and not parallel and not (c or python)" --ignore=symbolic --ignore=ndarrays + python -m pytest -n auto -rX -m "not (parallel or xdist_incompatible) and fortran" --ignore=symbolic --ignore=ndarrays + python -m pytest -rX -m "xdist_incompatible and not parallel and fortran" --ignore=symbolic --ignore=ndarrays pyccel-clean python -m pytest ndarrays/ -rX pyccel-clean From b2a719c63808e2c807ebc22c719700a18916ddb2 Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 20 Sep 2022 23:05:14 +0100 Subject: [PATCH 045/193] fixed trailing type for functions in header file --- pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu | 14 ++--- pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h | 55 +++----------------- 2 files changed, 13 insertions(+), 56 deletions(-) diff --git a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu index 87890c2ecb..f7fa9f333b 100644 --- a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu +++ b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu @@ -1,21 +1,21 @@ #include "cuda_ndarrays.h" __global__ -void cuda_array_arange_int8_t(t_ndarray arr, int start) +void cuda_array_arange_int8(t_ndarray arr, int start) { int index = blockIdx.x * blockDim.x + threadIdx.x; for(int i = index ; i < arr.length; i+=1) arr.nd_int8[i] = (i + start); } __global__ -void cuda_array_arange_int32_t(t_ndarray arr, int start) +void cuda_array_arange_int32(t_ndarray arr, int start) { int index = blockIdx.x * blockDim.x + threadIdx.x; for(int i = index ; i < arr.length; i+=1) arr.nd_int32[i] = (i + start); } __global__ -void cuda_array_arange_int64_t(t_ndarray arr, int start) +void cuda_array_arange_int64(t_ndarray arr, int start) { int index = blockIdx.x * blockDim.x + threadIdx.x; for(int i = index ; i < arr.length; i+=1) @@ -30,7 +30,7 @@ void cuda_array_arange_double(t_ndarray arr, int start) } __global__ -void _cuda_array_fill_int8_t(int8_t c, t_ndarray arr) +void cuda_array_fill_int8(int8_t c, t_ndarray arr) { int index = blockIdx.x * blockDim.x + threadIdx.x; int stride = gridDim.x * blockDim.x; @@ -39,7 +39,7 @@ void _cuda_array_fill_int8_t(int8_t c, t_ndarray arr) } __global__ -void _cuda_array_fill_int32_t(int32_t c, t_ndarray arr) +void cuda_array_fill_int32(int32_t c, t_ndarray arr) { int index = blockIdx.x * blockDim.x + threadIdx.x; int stride = gridDim.x * blockDim.x; @@ -48,7 +48,7 @@ void _cuda_array_fill_int32_t(int32_t c, t_ndarray arr) } __global__ -void _cuda_array_fill_int64_t(int64_t c, t_ndarray arr) +void cuda_array_fill_int64(int64_t c, t_ndarray arr) { int index = blockIdx.x * blockDim.x + threadIdx.x; int stride = gridDim.x * blockDim.x; @@ -56,7 +56,7 @@ void _cuda_array_fill_int64_t(int64_t c, t_ndarray arr) arr.nd_int64[i] = c; } __global__ -void _cuda_array_fill_double(double c, t_ndarray arr) +void cuda_array_fill_double(double c, t_ndarray arr) { int index = blockIdx.x * blockDim.x + threadIdx.x; int stride = gridDim.x * blockDim.x; diff --git a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h index 12dc8bbedd..2875ee76e3 100644 --- a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h +++ b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h @@ -3,64 +3,21 @@ #include "../ndarrays/ndarrays.h" -/* mapping the function array_fill to the correct type */ -// enum e_cuda_types -// { -// nd_bool = 0, -// nd_int8 = 1, -// nd_int16 = 3, -// nd_int32 = 5, -// nd_int64 = 7, -// nd_float = 11, -// nd_double = 12 -// }; - -// typedef struct s_cuda_ndarray -// { -// /* raw data buffer*/ -// union { -// void *raw_data; -// int8_t *nd_int8; -// int16_t *nd_int16; -// int32_t *nd_int32; -// int64_t *nd_int64; -// float *nd_float; -// double *nd_double; -// bool *nd_bool; -// }; -// /* number of dimensions */ -// int32_t nd; -// /* shape 'size of each dimension' */ -// int64_t *shape; -// /* strides 'number of bytes to skip to get the next element' */ -// int64_t *strides; -// /* type of the array elements */ -// enum e_types type; -// /* type size of the array elements */ -// int32_t type_size; -// /* number of element in the array */ -// int32_t length; -// /* size of the array */ -// int32_t buffer_size; -// /* True if the array does not own the data */ -// bool is_view; -// } t_cuda_ndarray; - __global__ -void cuda_array_arange_int8_t(t_ndarray arr, int start); +void cuda_array_arange_int8(t_ndarray arr, int start); __global__ -void cuda_array_arange_int32_t(t_ndarray arr, int start); +void cuda_array_arange_int32(t_ndarray arr, int start); __global__ -void cuda_array_arange_int64_t(t_ndarray arr, int start); +void cuda_array_arange_int64(t_ndarray arr, int start); __global__ void cuda_array_arange_double(t_ndarray arr, int start); __global__ -void _cuda_array_fill_int8_t(int8_t c, t_ndarray arr); +void _cuda_array_fill_int8(int8_t c, t_ndarray arr); __global__ -void _cuda_array_fill_int32_t(int32_t c, t_ndarray arr); +void _cuda_array_fill_int32(int32_t c, t_ndarray arr); __global__ -void _cuda_array_fill_int64_t(int64_t c, t_ndarray arr); +void _cuda_array_fill_int64(int64_t c, t_ndarray arr); __global__ void _cuda_array_fill_double(double c, t_ndarray arr); From 61a3d7143439a9bb618fdf2b2fd088f0e703231c Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 21 Sep 2022 09:06:41 +0000 Subject: [PATCH 046/193] testing Cuda tests alone --- .github/actions/pytest_run_cuda/action.yml | 1 - .github/workflows/Cuda_pytest.yml | 12 ++++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/actions/pytest_run_cuda/action.yml b/.github/actions/pytest_run_cuda/action.yml index 8f82dda10f..52dd6c2f93 100644 --- a/.github/actions/pytest_run_cuda/action.yml +++ b/.github/actions/pytest_run_cuda/action.yml @@ -11,6 +11,5 @@ runs: - name: Ccuda tests with pytest run: | python -m pytest -n auto -rx -m "not (parallel or xdist_incompatible) and ccuda" --ignore=symbolic --ignore=ndarrays - pyccel-clean shell: ${{ inputs.shell_cmd }} working-directory: ./tests diff --git a/.github/workflows/Cuda_pytest.yml b/.github/workflows/Cuda_pytest.yml index 2ee1cc56d2..daaaf701f6 100644 --- a/.github/workflows/Cuda_pytest.yml +++ b/.github/workflows/Cuda_pytest.yml @@ -17,14 +17,14 @@ jobs: uses: ./.github/actions/linux_install - name: Install python dependencies uses: ./.github/actions/pip_installation - - name: Fortran/C tests with pytest - uses: ./.github/actions/pytest_run + # - name: Fortran/C tests with pytest + # uses: ./.github/actions/pytest_run - name: Ccuda tests with pytest uses: ./.github/actions/pytest_run_cuda - - name: Python tests with pytest - uses: ./.github/actions/pytest_run_python - - name: Parallel tests with pytest - uses: ./.github/actions/pytest_parallel + # - name: Python tests with pytest + # uses: ./.github/actions/pytest_run_python + # - name: Parallel tests with pytest + # uses: ./.github/actions/pytest_parallel # Windows: From 842171a79e41f8fd8bf55566816242de39c3bc99 Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 21 Sep 2022 10:34:53 +0100 Subject: [PATCH 047/193] fixed trailing types --- .github/actions/pytest_run_cuda/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/pytest_run_cuda/action.yml b/.github/actions/pytest_run_cuda/action.yml index 52dd6c2f93..d95b747aa0 100644 --- a/.github/actions/pytest_run_cuda/action.yml +++ b/.github/actions/pytest_run_cuda/action.yml @@ -10,6 +10,7 @@ runs: steps: - name: Ccuda tests with pytest run: | + ls -R . python -m pytest -n auto -rx -m "not (parallel or xdist_incompatible) and ccuda" --ignore=symbolic --ignore=ndarrays shell: ${{ inputs.shell_cmd }} working-directory: ./tests From bc0862cfd8f177a494bc4459f5e3d522286375fc Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 21 Sep 2022 11:04:24 +0100 Subject: [PATCH 048/193] test github action working dir --- .github/actions/pytest_run_cuda/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/pytest_run_cuda/action.yml b/.github/actions/pytest_run_cuda/action.yml index d95b747aa0..59ffd5eabe 100644 --- a/.github/actions/pytest_run_cuda/action.yml +++ b/.github/actions/pytest_run_cuda/action.yml @@ -13,4 +13,4 @@ runs: ls -R . python -m pytest -n auto -rx -m "not (parallel or xdist_incompatible) and ccuda" --ignore=symbolic --ignore=ndarrays shell: ${{ inputs.shell_cmd }} - working-directory: ./tests + working-directory: . From 3a70b555bd1aa587b1d2084b19679725bef73a89 Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 21 Sep 2022 11:16:32 +0100 Subject: [PATCH 049/193] test github action working dir --- .github/actions/pytest_run_cuda/action.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/actions/pytest_run_cuda/action.yml b/.github/actions/pytest_run_cuda/action.yml index 59ffd5eabe..ece889c28e 100644 --- a/.github/actions/pytest_run_cuda/action.yml +++ b/.github/actions/pytest_run_cuda/action.yml @@ -10,7 +10,9 @@ runs: steps: - name: Ccuda tests with pytest run: | - ls -R . python -m pytest -n auto -rx -m "not (parallel or xdist_incompatible) and ccuda" --ignore=symbolic --ignore=ndarrays + ls . + pyccel-clean + ls . shell: ${{ inputs.shell_cmd }} working-directory: . From d9aba150a6d1991a38dedc6e23d4481bf5ffcafa Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 21 Sep 2022 11:33:23 +0100 Subject: [PATCH 050/193] removed debug and changed working dir to tests --- .github/actions/pytest_run_cuda/action.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/actions/pytest_run_cuda/action.yml b/.github/actions/pytest_run_cuda/action.yml index ece889c28e..8f82dda10f 100644 --- a/.github/actions/pytest_run_cuda/action.yml +++ b/.github/actions/pytest_run_cuda/action.yml @@ -11,8 +11,6 @@ runs: - name: Ccuda tests with pytest run: | python -m pytest -n auto -rx -m "not (parallel or xdist_incompatible) and ccuda" --ignore=symbolic --ignore=ndarrays - ls . pyccel-clean - ls . shell: ${{ inputs.shell_cmd }} - working-directory: . + working-directory: ./tests From e2c9155f679e5dcbe7c8547a0e4aee3ce7154f47 Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 21 Sep 2022 12:11:37 +0100 Subject: [PATCH 051/193] verbose True --- tests/internal/test_internal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/internal/test_internal.py b/tests/internal/test_internal.py index dde67e5578..904e5c4f5a 100644 --- a/tests/internal/test_internal.py +++ b/tests/internal/test_internal.py @@ -47,7 +47,7 @@ def test_openmp(f, language): ) @pytest.mark.external def test_ccuda(f, language): - execute_pyccel(f, language=language) + execute_pyccel(f, verbose=True, language=language) #@pytest.mark.parametrize("f", get_files_from_folder('openacc')) #@pytest.mark.external From 452d2e39333a78e150e491ea14ca063f84fc70b1 Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 21 Sep 2022 12:27:42 +0100 Subject: [PATCH 052/193] changing working dir to root --- .github/actions/pytest_run_cuda/action.yml | 4 ++-- tests/internal/test_internal.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/actions/pytest_run_cuda/action.yml b/.github/actions/pytest_run_cuda/action.yml index 8f82dda10f..7ab1a8eabc 100644 --- a/.github/actions/pytest_run_cuda/action.yml +++ b/.github/actions/pytest_run_cuda/action.yml @@ -10,7 +10,7 @@ runs: steps: - name: Ccuda tests with pytest run: | - python -m pytest -n auto -rx -m "not (parallel or xdist_incompatible) and ccuda" --ignore=symbolic --ignore=ndarrays + python -m pytest -n auto -rx -m "not (parallel or xdist_incompatible) and ccuda" --ignore=tests/symbolic --ignore=tests/ndarrays pyccel-clean shell: ${{ inputs.shell_cmd }} - working-directory: ./tests + working-directory: ./ diff --git a/tests/internal/test_internal.py b/tests/internal/test_internal.py index 904e5c4f5a..dde67e5578 100644 --- a/tests/internal/test_internal.py +++ b/tests/internal/test_internal.py @@ -47,7 +47,7 @@ def test_openmp(f, language): ) @pytest.mark.external def test_ccuda(f, language): - execute_pyccel(f, verbose=True, language=language) + execute_pyccel(f, language=language) #@pytest.mark.parametrize("f", get_files_from_folder('openacc')) #@pytest.mark.external From 2aca8e7bc28c74d871890be78a364425a1ebaadd Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 21 Sep 2022 18:07:15 +0100 Subject: [PATCH 053/193] removed outdated code --- pyccel/parser/semantic.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pyccel/parser/semantic.py b/pyccel/parser/semantic.py index c51596ea7a..fc893de7da 100644 --- a/pyccel/parser/semantic.py +++ b/pyccel/parser/semantic.py @@ -1956,11 +1956,7 @@ def _visit_DottedName(self, expr, **settings): symbol=expr, bounding_box=(self._current_fst_node.lineno, self._current_fst_node.col_offset), severity='fatal') - from pyccel.ast.cudaext import CudaThreadIdx, CudaBlockDim, CudaBlockIdx, CudaGridDim - # if first isinstance(): - if (first in (CudaThreadIdx, CudaBlockDim, CudaBlockIdx, CudaGridDim)): - dim = {'x':0, 'y':1, 'z':2} - return first(LiteralInteger(dim[rhs_name])) + d_var = self._infere_type(first) if d_var.get('cls_base', None) is None: errors.report(f'Attribute {rhs_name} not found', From d76a61b7a47a41e84b1e21ce9447749ab2b141c5 Mon Sep 17 00:00:00 2001 From: Aymane Benaissa <47903494+Pinkyboi@users.noreply.github.com> Date: Tue, 4 Oct 2022 13:28:26 +0100 Subject: [PATCH 054/193] Cuda_internal_var can only be used in kernels and device funcs --- pyccel/parser/semantic.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pyccel/parser/semantic.py b/pyccel/parser/semantic.py index fc893de7da..8e08af95f2 100644 --- a/pyccel/parser/semantic.py +++ b/pyccel/parser/semantic.py @@ -94,7 +94,7 @@ from pyccel.ast.numpyext import NumpyNewArray, NumpyNonZero from pyccel.ast.numpyext import DtypePrecisionToCastFunction -from pyccel.ast.cudaext import CudaNewArray +from pyccel.ast.cudaext import CudaNewArray, CudaThreadIdx, CudaBlockDim, CudaBlockIdx, CudaGridDim from pyccel.ast.omp import (OMP_For_Loop, OMP_Simd_Construct, OMP_Distribute_Construct, OMP_TaskLoop_Construct, OMP_Sections_Construct, Omp_End_Clause, @@ -810,6 +810,12 @@ def _handle_function(self, expr, func, args, **settings): if isinstance(func, PyccelFunctionDef): func = func.cls_name + if func in (CudaThreadIdx, CudaBlockDim, CudaBlockIdx, CudaGridDim): + if 'kernel' not in self.scope.decorators: + errors.report("Cuda internal variables should only be used in Kernel or Device functions", + symbol = expr, + severity = 'fatal') + args, kwargs = split_positional_keyword_arguments(*args) for a in args: if getattr(a,'dtype',None) == 'tuple': From 4eec542aea882d94ce7cbbe9271c6229caa62474 Mon Sep 17 00:00:00 2001 From: bauom <40796259+bauom@users.noreply.github.com> Date: Tue, 4 Oct 2022 13:44:14 +0100 Subject: [PATCH 055/193] reactivate normal pyccel tests --- .github/workflows/Cuda_pytest.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/Cuda_pytest.yml b/.github/workflows/Cuda_pytest.yml index daaaf701f6..2ee1cc56d2 100644 --- a/.github/workflows/Cuda_pytest.yml +++ b/.github/workflows/Cuda_pytest.yml @@ -17,14 +17,14 @@ jobs: uses: ./.github/actions/linux_install - name: Install python dependencies uses: ./.github/actions/pip_installation - # - name: Fortran/C tests with pytest - # uses: ./.github/actions/pytest_run + - name: Fortran/C tests with pytest + uses: ./.github/actions/pytest_run - name: Ccuda tests with pytest uses: ./.github/actions/pytest_run_cuda - # - name: Python tests with pytest - # uses: ./.github/actions/pytest_run_python - # - name: Parallel tests with pytest - # uses: ./.github/actions/pytest_parallel + - name: Python tests with pytest + uses: ./.github/actions/pytest_run_python + - name: Parallel tests with pytest + uses: ./.github/actions/pytest_parallel # Windows: From 02384327db8e5423fd84a2fb0b4a5cd5633ed104 Mon Sep 17 00:00:00 2001 From: Aymane Benaissa <47903494+Pinkyboi@users.noreply.github.com> Date: Tue, 4 Oct 2022 17:04:20 +0100 Subject: [PATCH 056/193] Check type of passed parameter to Kernel Block number and Thread per Block number --- pyccel/parser/semantic.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pyccel/parser/semantic.py b/pyccel/parser/semantic.py index 8e08af95f2..9b45a2ae7d 100644 --- a/pyccel/parser/semantic.py +++ b/pyccel/parser/semantic.py @@ -901,6 +901,12 @@ def _handle_kernel(self, expr, func, args, **settings): symbol = expr, severity='fatal') # TODO : type check the NUMBER OF BLOCKS 'numBlocks' and threads per block 'tpblock' + if not all(isinstance(param, (LiteralInteger, PythonTuple, PyccelSymbol))\ + for param in [expr.numBlocks, expr.tpblock]): + errors.report("Invalid parameter for Kernel Block number or Thread per Block", + symbol = expr, + severity='error') + new_expr = KernelCall(func, args, expr.numBlocks, expr.tpblock, self._current_function) if None in new_expr.args: errors.report("Too few arguments passed in function call", From 9804d3b15cf488beac967ea5ab694129fb9e5856 Mon Sep 17 00:00:00 2001 From: Aymane Benaissa <47903494+Pinkyboi@users.noreply.github.com> Date: Tue, 4 Oct 2022 17:10:37 +0100 Subject: [PATCH 057/193] Check for stackarray variables in kernel call --- pyccel/parser/semantic.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/pyccel/parser/semantic.py b/pyccel/parser/semantic.py index 9b45a2ae7d..1e0f57c123 100644 --- a/pyccel/parser/semantic.py +++ b/pyccel/parser/semantic.py @@ -908,11 +908,18 @@ def _handle_kernel(self, expr, func, args, **settings): severity='error') new_expr = KernelCall(func, args, expr.numBlocks, expr.tpblock, self._current_function) - if None in new_expr.args: - errors.report("Too few arguments passed in function call", + + for a in new_expr.args: + if a is None: + errors.report("Too few arguments passed in function call", symbol = expr, severity='error') - elif isinstance(func, FunctionDef): + elif isinstance(a.value, Variable) and a.value.on_stack: + errors.report("Variable allocated on the stack passed to a Kernel", + symbol = expr, + severity='error') + + if isinstance(func, FunctionDef): self._check_argument_compatibility(new_expr.args, func.arguments, expr, func.is_elemental) return new_expr From bad4f7ae1be454669e4460dc290a3fd1b7f1251c Mon Sep 17 00:00:00 2001 From: Fatima-zahra RAMDANI Date: Fri, 7 Oct 2022 15:16:25 +0100 Subject: [PATCH 058/193] update clash class name for cuda --- pyccel/naming/ccudanameclashchecker.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/pyccel/naming/ccudanameclashchecker.py b/pyccel/naming/ccudanameclashchecker.py index c401345c82..570a72060a 100644 --- a/pyccel/naming/ccudanameclashchecker.py +++ b/pyccel/naming/ccudanameclashchecker.py @@ -13,14 +13,15 @@ class CCudaNameClashChecker(metaclass = Singleton): """ Class containing functions to help avoid problematic names in Ccuda """ # Keywords as mentioned on https://en.cppreference.com/w/c/keyword - keywords = set(['auto', 'break', 'case', 'char', 'const', + keywords = set(['auto', 'break', 'case', 'char', 'const', 'bool', 'continue', 'default', 'do', 'double', 'else', 'enum', - 'extern', 'float', 'for', 'goto', 'if', 'inline', 'int', + 'extern', 'float', 'for', 'goto', 'if', 'inline', + 'int', 'int8_t', 'int16_t', 'int32_t', 'int64_t', 'long', 'register', 'restrict', 'return', 'short', 'signed', 'sizeof', 'static', 'struct', 'switch', 'typedef', 'union', - 'unsigned', 'void', 'volatile', 'whie', '_Alignas', + 'unsigned', 'void', 'volatile', 'while', '_Alignas', '_Alignof', '_Atomic', '_Bool', '_Complex', 'Decimal128', - '_Decimal32', '_Decimal64', '_Generic', '_Imaginary', + '_Decimal32', '_Decimal64', '_Generic', '_Imaginary', '__global__', '_Noreturn', '_Static_assert', '_Thread_local', 't_ndarray', 'array_create', 'new_slice', 'array_slicing', 'alias_assign', 'transpose_alias_assign', 'array_fill', 't_slice', @@ -34,7 +35,13 @@ class CCudaNameClashChecker(metaclass = Singleton): 'INDEX', 'GET_ELEMENT', 'free_array', 'free_pointer', 'get_index', 'numpy_to_ndarray_strides', 'numpy_to_ndarray_shape', 'get_size', - 'cuda_free_array', 'cuda_free_pointer', 'cuda_array_create', 'threadIdx', 'blockIdx']) + 'cuda_free_array', 'cuda_free_pointer', 'cuda_array_create', + 'threadIdx', 'blockIdx', 'blockDim', 'gridDim', + 'cuda_array_fill_double', 'cuda_array_fill_int64', + 'cuda_array_fill_int32', 'cuda_array_fill_int8', + 'cuda_array_arange_double', 'cuda_array_arange_int64', + 'cuda_array_arange_int32', 'cuda_array_arange_int8', + 'cudaMallocManaged', 'cudaDeviceSynchronize']) def has_clash(self, name, symbols): """ Indicate whether the proposed name causes any clashes From df8fb177a72a279533a99f929d364ab480177a29 Mon Sep 17 00:00:00 2001 From: Fatima-zahra RAMDANI Date: Fri, 7 Oct 2022 20:04:59 +0100 Subject: [PATCH 059/193] activate tests --- .github/workflows/Github_pytest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index a4a2f986c8..15fa883b21 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -2,7 +2,7 @@ name: Pyccel tests on: pull_request: - branches: [ master ] + branches: [ cuda_main_temp ] jobs: Linux: From 455ed0f5ca3663801d9d7a9aa23fd6e7fd77c7a1 Mon Sep 17 00:00:00 2001 From: Fatima-zahra RAMDANI Date: Fri, 7 Oct 2022 20:37:09 +0100 Subject: [PATCH 060/193] fix linter --- pyccel/parser/semantic.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyccel/parser/semantic.py b/pyccel/parser/semantic.py index fc893de7da..330d0054c0 100644 --- a/pyccel/parser/semantic.py +++ b/pyccel/parser/semantic.py @@ -852,7 +852,7 @@ def _handle_function(self, expr, func, args, **settings): self._check_argument_compatibility(new_expr.args, func.arguments, expr, func.is_elemental) return new_expr - + def _handle_kernel(self, expr, func, args, **settings): """ Create a FunctionCall or an instance of a PyccelInternalFunction @@ -1891,7 +1891,6 @@ def _visit_PyccelSymbol(self, expr, **settings): def _visit_DottedName(self, expr, **settings): - var = self.check_for_variable(_get_name(expr)) if var: return var From c81420c2cf1d1e9ed719404cd8754aef781a4171 Mon Sep 17 00:00:00 2001 From: Fatima-zahra RAMDANI Date: Sat, 8 Oct 2022 03:57:24 +0100 Subject: [PATCH 061/193] define the memory location for cuda array --- .github/workflows/Github_pytest.yml | 2 +- pyccel/ast/cudaext.py | 9 ++++++--- pyccel/parser/semantic.py | 7 +++---- tests/internal/scripts/ccuda/kernel_launch.py | 2 +- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index a4a2f986c8..15fa883b21 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -2,7 +2,7 @@ name: Pyccel tests on: pull_request: - branches: [ master ] + branches: [ cuda_main_temp ] jobs: Linux: diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index 7846c9ba60..7e3d063785 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -73,11 +73,11 @@ class CudaArray(CudaNewArray): arg : list, tuple, PythonList """ - __slots__ = ('_arg','_dtype','_precision','_shape','_rank','_order') + __slots__ = ('_arg','_dtype','_precision','_shape','_rank','_order','memory_location') _attribute_nodes = ('_arg',) name = 'array' - def __init__(self, arg, dtype=None, order='C'): + def __init__(self, arg, dtype=None, order='C', memory_location='managed'): if not isinstance(arg, (PythonTuple, PythonList, Variable)): raise TypeError('Unknown type of %s.' % type(arg)) @@ -114,13 +114,16 @@ def __init__(self, arg, dtype=None, order='C'): if order in ('K', 'A'): order = 'C' # ... - + #Verify memory location + if memory_location not in ('host', 'device', 'managed'): + raise ValueError("memory_location must be 'host', 'device' or 'managed'") self._arg = arg self._shape = shape self._rank = rank self._dtype = dtype self._order = order self._precision = prec + self.memory_location = memory_location super().__init__() def __str__(self): diff --git a/pyccel/parser/semantic.py b/pyccel/parser/semantic.py index fc893de7da..3430da9e3f 100644 --- a/pyccel/parser/semantic.py +++ b/pyccel/parser/semantic.py @@ -493,7 +493,7 @@ def _infere_type(self, expr, **settings): elif isinstance(expr, CudaNewArray): d_var['datatype' ] = expr.dtype d_var['memory_handling'] = 'heap' if expr.rank > 0 else 'stack' - d_var['memory_location'] = 'managed' + d_var['memory_location'] = expr.memory_location d_var['shape' ] = expr.shape d_var['rank' ] = expr.rank d_var['order' ] = expr.order @@ -852,7 +852,7 @@ def _handle_function(self, expr, func, args, **settings): self._check_argument_compatibility(new_expr.args, func.arguments, expr, func.is_elemental) return new_expr - + def _handle_kernel(self, expr, func, args, **settings): """ Create a FunctionCall or an instance of a PyccelInternalFunction @@ -1890,8 +1890,7 @@ def _visit_PyccelSymbol(self, expr, **settings): return var - def _visit_DottedName(self, expr, **settings): - + def _visit_DottedName(self, expr, **settings): var = self.check_for_variable(_get_name(expr)) if var: return var diff --git a/tests/internal/scripts/ccuda/kernel_launch.py b/tests/internal/scripts/ccuda/kernel_launch.py index 16cf40fbdc..7f8ed4b6b6 100644 --- a/tests/internal/scripts/ccuda/kernel_launch.py +++ b/tests/internal/scripts/ccuda/kernel_launch.py @@ -10,7 +10,7 @@ def func(a): if __name__ == '__main__': threads_per_block = 5 n_blocks = 1 - arr = cuda.array([0, 1, 2, 3, 4]) + arr = cuda.array([0, 1, 2, 3, 4], memory_location='device') cuda.deviceSynchronize() func[n_blocks, threads_per_block](arr) cuda.deviceSynchronize() \ No newline at end of file From 400a662731ef906f11f95776896c8a3823be16eb Mon Sep 17 00:00:00 2001 From: Fatima-zahra RAMDANI Date: Sat, 8 Oct 2022 04:29:16 +0100 Subject: [PATCH 062/193] remove trailing whitespace --- pyccel/ast/cudaext.py | 7 +++++-- pyccel/parser/semantic.py | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index 7e3d063785..aed2e206ed 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -73,7 +73,7 @@ class CudaArray(CudaNewArray): arg : list, tuple, PythonList """ - __slots__ = ('_arg','_dtype','_precision','_shape','_rank','_order','memory_location') + __slots__ = ('_arg','_dtype','_precision','_shape','_rank','_order','_memory_location') _attribute_nodes = ('_arg',) name = 'array' @@ -123,7 +123,7 @@ def __init__(self, arg, dtype=None, order='C', memory_location='managed'): self._dtype = dtype self._order = order self._precision = prec - self.memory_location = memory_location + self._memory_location = memory_location super().__init__() def __str__(self): @@ -132,6 +132,9 @@ def __str__(self): @property def arg(self): return self._arg + @property + def memory_location(self): + return self._memory_location class CudaDeviceSynchronize(PyccelInternalFunction): "Represents a call to Cuda.deviceSynchronize for code generation." diff --git a/pyccel/parser/semantic.py b/pyccel/parser/semantic.py index 3430da9e3f..cdd21655c4 100644 --- a/pyccel/parser/semantic.py +++ b/pyccel/parser/semantic.py @@ -1890,7 +1890,7 @@ def _visit_PyccelSymbol(self, expr, **settings): return var - def _visit_DottedName(self, expr, **settings): + def _visit_DottedName(self, expr, **settings): var = self.check_for_variable(_get_name(expr)) if var: return var From 249de6daf3f76d83243bc4d9017a7cbe507b97eb Mon Sep 17 00:00:00 2001 From: Fatima-zahra RAMDANI Date: Mon, 10 Oct 2022 14:23:19 +0100 Subject: [PATCH 063/193] fix linux failing test --- .github/actions/linux_install/action.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/actions/linux_install/action.yml b/.github/actions/linux_install/action.yml index 81b7e95616..8222eb0091 100644 --- a/.github/actions/linux_install/action.yml +++ b/.github/actions/linux_install/action.yml @@ -5,38 +5,38 @@ runs: steps: - name: update the package list run: - apt-get update + sudo apt-get update shell: bash - name: Install fortran run: - apt-get -y install gfortran + sudo apt-get -y install gfortran shell: bash - name: Install python run: - apt-get -y install python3-dev + sudo apt-get -y install python3-dev shell: bash - name: python as python3 run: - apt-get -y install python-is-python3 + sudo apt-get -y install python-is-python3 shell: bash - name: Install Pip run: - apt-get -y install python3-pip + sudo apt-get -y install python3-pip shell: bash - name: Install LaPack run: - apt-get -y install libblas-dev liblapack-dev + sudo apt-get -y install libblas-dev liblapack-dev shell: bash - name: Install MPI run: | - DEBIAN_FRONTEND=noninteractive TZ=Africa/Morocco apt-get -y install libopenmpi-dev openmpi-bin + DEBIAN_FRONTEND=noninteractive TZ=Africa/Morocco sudo apt-get -y install libopenmpi-dev openmpi-bin echo "MPI_OPTS=--oversubscribe" >> $GITHUB_ENV shell: bash - name: Install OpenMP run: - apt-get -y install libomp-dev libomp5 + sudo apt-get -y install libomp-dev libomp5 shell: bash - name: Install Valgrind run: - apt-get -y install valgrind + sudo apt-get -y install valgrind shell: bash From cab397d6a2ed988239e2104986e0496279a8e693 Mon Sep 17 00:00:00 2001 From: Fatima-zahra RAMDANI Date: Wed, 12 Oct 2022 02:25:48 +0100 Subject: [PATCH 064/193] call the appropriate memory management function --- pyccel/codegen/printing/ccudacode.py | 20 +++++++-------- pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu | 27 +++++++++++++++++--- pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h | 2 +- pyccel/stdlib/ndarrays/ndarrays.h | 7 +++++ 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index fcf7d62eb2..1dbaa8c0eb 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -330,19 +330,17 @@ def _print_Allocate(self, expr): tmp_shape = self.scope.get_new_name('tmp_shape') dtype = self._print(expr.variable.dtype) dtype = self.find_in_ndarray_type_registry(dtype, expr.variable.precision) - shape_Assign = "{} {}[] = {{{}}};".format(shape_dtype, tmp_shape, shape) is_view = 'false' if expr.variable.on_heap else 'true' - if expr.variable.is_managed or expr.variable.on_device: - self.add_import(c_imports['cuda_ndarrays']) - alloc_code = "{} = cuda_array_create({}, {}, {}, {});".format( - expr.variable, len(expr.shape), tmp_shape, dtype, - is_view) - else: - self.add_import(c_imports['ndarrays']) - alloc_code = "{} = array_create({}, {}, {}, {});".format( - expr.variable, len(expr.shape), tmp_shape, dtype, - is_view) + # if expr.variable.is_managed or expr.variable.on_device: + self.add_import(c_imports['cuda_ndarrays']) + alloc_code = "{} = cuda_array_create({}, {}, {}, {}, {});".format( + expr.variable, len(expr.shape), tmp_shape, dtype, is_view, expr.variable.memory_location) + # else: + # self.add_import(c_imports['ndarrays']) + # alloc_code = "{} = array_create({}, {}, {}, {});".format( + # expr.variable, len(expr.shape), tmp_shape, dtype, + # is_view) return '{}\n{}\n{}\n'.format(free_code, shape_Assign, alloc_code) def _print_Deallocate(self, expr): diff --git a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu index f7fa9f333b..cd37fa20c0 100644 --- a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu +++ b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu @@ -64,10 +64,26 @@ void cuda_array_fill_double(double c, t_ndarray arr) arr.nd_double[i] = c; } +void device_location(void* devPtr, size_t size) +{ + cudaMalloc(&devPtr, size); +} + +void managed_location(void* devPtr, size_t size) +{ + cudaMallocManaged(&devPtr, size); +} + +void host_location(void* devPtr, size_t size) +{ + cudaMallocHost(&devPtr, size); +} + t_ndarray cuda_array_create(int32_t nd, int64_t *shape, - enum e_types type, bool is_view) + enum e_types type, bool is_view, enum e_memory_locations location) { t_ndarray arr; + void (*fun_ptr_arr[])(void*, size_t) = {managed_location, host_location, device_location}; arr.nd = nd; arr.type = type; @@ -97,14 +113,16 @@ t_ndarray cuda_array_create(int32_t nd, int64_t *shape, } arr.is_view = is_view; arr.length = 1; - cudaMallocManaged(&(arr.shape), arr.nd * sizeof(int64_t)); + (*fun_ptr_arr[location])(&(arr.shape), arr.nd * sizeof(int64_t)); + //cudaMallocManaged(&(arr.shape), arr.nd * sizeof(int64_t)); for (int32_t i = 0; i < arr.nd; i++) { arr.length *= shape[i]; arr.shape[i] = shape[i]; } arr.buffer_size = arr.length * arr.type_size; - cudaMallocManaged(&(arr.strides), nd * sizeof(int64_t)); + (*fun_ptr_arr[location])(&(arr.strides), nd * sizeof(int64_t)); + //cudaMallocManaged(&(arr.strides), nd * sizeof(int64_t)); for (int32_t i = 0; i < arr.nd; i++) { arr.strides[i] = 1; @@ -112,7 +130,8 @@ t_ndarray cuda_array_create(int32_t nd, int64_t *shape, arr.strides[i] *= arr.shape[j]; } if (!is_view) - cudaMallocManaged(&(arr.raw_data), arr.buffer_size); + (*fun_ptr_arr[location])(&(arr.raw_data), arr.buffer_size); + //cudaMallocManaged(&(arr.raw_data), arr.buffer_size); return (arr); } diff --git a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h index 2875ee76e3..d0cce3b5f0 100644 --- a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h +++ b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h @@ -21,7 +21,7 @@ void _cuda_array_fill_int64(int64_t c, t_ndarray arr); __global__ void _cuda_array_fill_double(double c, t_ndarray arr); -t_ndarray cuda_array_create(int32_t nd, int64_t *shape, enum e_types type, bool is_view); +t_ndarray cuda_array_create(int32_t nd, int64_t *shape, enum e_types type, bool is_view, enum e_memory_locations location); int32_t cuda_free_array(t_ndarray dump); int32_t cuda_free_pointer(t_ndarray dump); #endif diff --git a/pyccel/stdlib/ndarrays/ndarrays.h b/pyccel/stdlib/ndarrays/ndarrays.h index 5cdd9c8cbe..ba0c1391f6 100644 --- a/pyccel/stdlib/ndarrays/ndarrays.h +++ b/pyccel/stdlib/ndarrays/ndarrays.h @@ -65,6 +65,13 @@ enum e_types #endif }; +enum e_memory_locations +{ + managed = 0, + host = 1, + device = 2 +} + typedef struct s_ndarray { /* raw data buffer*/ From 307e6133ec9ed95fd0aca71cd77a7ad731850620 Mon Sep 17 00:00:00 2001 From: Fatima-zahra RAMDANI Date: Wed, 12 Oct 2022 02:39:08 +0100 Subject: [PATCH 065/193] add missing ; --- pyccel/stdlib/ndarrays/ndarrays.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyccel/stdlib/ndarrays/ndarrays.h b/pyccel/stdlib/ndarrays/ndarrays.h index ba0c1391f6..888a514a31 100644 --- a/pyccel/stdlib/ndarrays/ndarrays.h +++ b/pyccel/stdlib/ndarrays/ndarrays.h @@ -70,7 +70,7 @@ enum e_memory_locations managed = 0, host = 1, device = 2 -} +}; typedef struct s_ndarray { From f76f788f925d0650e916a118ff7355dd57c74a06 Mon Sep 17 00:00:00 2001 From: Fatima-zahra RAMDANI Date: Mon, 17 Oct 2022 09:05:01 +0100 Subject: [PATCH 066/193] update memory management functions name and memory_locations enum --- pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu | 8 ++++---- pyccel/stdlib/ndarrays/ndarrays.h | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu index cd37fa20c0..b17bef131e 100644 --- a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu +++ b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu @@ -64,17 +64,17 @@ void cuda_array_fill_double(double c, t_ndarray arr) arr.nd_double[i] = c; } -void device_location(void* devPtr, size_t size) +void device_memory(void* devPtr, size_t size) { cudaMalloc(&devPtr, size); } -void managed_location(void* devPtr, size_t size) +void managed_memory(void* devPtr, size_t size) { cudaMallocManaged(&devPtr, size); } -void host_location(void* devPtr, size_t size) +void host_memory(void* devPtr, size_t size) { cudaMallocHost(&devPtr, size); } @@ -83,7 +83,7 @@ t_ndarray cuda_array_create(int32_t nd, int64_t *shape, enum e_types type, bool is_view, enum e_memory_locations location) { t_ndarray arr; - void (*fun_ptr_arr[])(void*, size_t) = {managed_location, host_location, device_location}; + void (*fun_ptr_arr[])(void*, size_t) = {managed_memory, host_memory, device_memory}; arr.nd = nd; arr.type = type; diff --git a/pyccel/stdlib/ndarrays/ndarrays.h b/pyccel/stdlib/ndarrays/ndarrays.h index 888a514a31..dd911b797f 100644 --- a/pyccel/stdlib/ndarrays/ndarrays.h +++ b/pyccel/stdlib/ndarrays/ndarrays.h @@ -67,9 +67,9 @@ enum e_types enum e_memory_locations { - managed = 0, - host = 1, - device = 2 + managed, + host, + device }; typedef struct s_ndarray From e9c9ff9218d9daad2c22cb41babc88bc40693c8c Mon Sep 17 00:00:00 2001 From: Aymane Benaissa <47903494+Pinkyboi@users.noreply.github.com> Date: Mon, 17 Oct 2022 13:40:33 +0100 Subject: [PATCH 067/193] Add device to the check for Cuda_internal_var --- pyccel/parser/semantic.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyccel/parser/semantic.py b/pyccel/parser/semantic.py index 1e0f57c123..6d79d68e84 100644 --- a/pyccel/parser/semantic.py +++ b/pyccel/parser/semantic.py @@ -811,7 +811,8 @@ def _handle_function(self, expr, func, args, **settings): if isinstance(func, PyccelFunctionDef): func = func.cls_name if func in (CudaThreadIdx, CudaBlockDim, CudaBlockIdx, CudaGridDim): - if 'kernel' not in self.scope.decorators: + if 'kernel' not in self.scope.decorators\ + or 'device' not in self.scope.decorators: errors.report("Cuda internal variables should only be used in Kernel or Device functions", symbol = expr, severity = 'fatal') From 688a2bca36021b405347b3d977168c2c30a27597 Mon Sep 17 00:00:00 2001 From: Aymane Benaissa <47903494+Pinkyboi@users.noreply.github.com> Date: Mon, 17 Oct 2022 13:45:48 +0100 Subject: [PATCH 068/193] Check for number of passed argument to kernel call --- pyccel/parser/semantic.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyccel/parser/semantic.py b/pyccel/parser/semantic.py index 6d79d68e84..a226a994ff 100644 --- a/pyccel/parser/semantic.py +++ b/pyccel/parser/semantic.py @@ -910,6 +910,10 @@ def _handle_kernel(self, expr, func, args, **settings): new_expr = KernelCall(func, args, expr.numBlocks, expr.tpblock, self._current_function) + if len(new_expr.args) is not len(func.arguments): + errors.report("The number of passed arguments to the function call does not correspond to the function definition", + symbol = expr, + severity='error') for a in new_expr.args: if a is None: errors.report("Too few arguments passed in function call", From 7ce252c391cdac2f36d51bb8f37aba2a21716393 Mon Sep 17 00:00:00 2001 From: Aymane Benaissa <47903494+Pinkyboi@users.noreply.github.com> Date: Mon, 17 Oct 2022 13:47:17 +0100 Subject: [PATCH 069/193] Remove check for none argument variables --- pyccel/parser/semantic.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/pyccel/parser/semantic.py b/pyccel/parser/semantic.py index a226a994ff..8e47acb31c 100644 --- a/pyccel/parser/semantic.py +++ b/pyccel/parser/semantic.py @@ -915,12 +915,8 @@ def _handle_kernel(self, expr, func, args, **settings): symbol = expr, severity='error') for a in new_expr.args: - if a is None: - errors.report("Too few arguments passed in function call", - symbol = expr, - severity='error') - elif isinstance(a.value, Variable) and a.value.on_stack: - errors.report("Variable allocated on the stack passed to a Kernel", + if isinstance(a.value, Variable) and a.value.on_stack: + errors.report("A variable allocated on the stack can't be passed to a Kernel function", symbol = expr, severity='error') From b3a265739d8f36c3a7209562980f5740791afbce Mon Sep 17 00:00:00 2001 From: Aymane Benaissa <47903494+Pinkyboi@users.noreply.github.com> Date: Mon, 17 Oct 2022 14:20:19 +0100 Subject: [PATCH 070/193] Revert back to previous check on passed argument --- pyccel/parser/semantic.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pyccel/parser/semantic.py b/pyccel/parser/semantic.py index 8e47acb31c..dcea530336 100644 --- a/pyccel/parser/semantic.py +++ b/pyccel/parser/semantic.py @@ -910,12 +910,12 @@ def _handle_kernel(self, expr, func, args, **settings): new_expr = KernelCall(func, args, expr.numBlocks, expr.tpblock, self._current_function) - if len(new_expr.args) is not len(func.arguments): - errors.report("The number of passed arguments to the function call does not correspond to the function definition", - symbol = expr, - severity='error') for a in new_expr.args: - if isinstance(a.value, Variable) and a.value.on_stack: + if a is None: + errors.report("Too few arguments passed in function call", + symbol = expr, + severity='error') + elif isinstance(a.value, Variable) and a.value.on_stack: errors.report("A variable allocated on the stack can't be passed to a Kernel function", symbol = expr, severity='error') From 6182501274b2053cb82ab087b291760971df3a12 Mon Sep 17 00:00:00 2001 From: Aymane Benaissa <47903494+Pinkyboi@users.noreply.github.com> Date: Mon, 17 Oct 2022 14:22:01 +0100 Subject: [PATCH 071/193] Remove support for tuple and varibale number of block and number of threads per block --- pyccel/parser/semantic.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyccel/parser/semantic.py b/pyccel/parser/semantic.py index dcea530336..e88a94cb78 100644 --- a/pyccel/parser/semantic.py +++ b/pyccel/parser/semantic.py @@ -902,7 +902,7 @@ def _handle_kernel(self, expr, func, args, **settings): symbol = expr, severity='fatal') # TODO : type check the NUMBER OF BLOCKS 'numBlocks' and threads per block 'tpblock' - if not all(isinstance(param, (LiteralInteger, PythonTuple, PyccelSymbol))\ + if not all(isinstance(param, LiteralInteger)\ for param in [expr.numBlocks, expr.tpblock]): errors.report("Invalid parameter for Kernel Block number or Thread per Block", symbol = expr, @@ -919,7 +919,6 @@ def _handle_kernel(self, expr, func, args, **settings): errors.report("A variable allocated on the stack can't be passed to a Kernel function", symbol = expr, severity='error') - if isinstance(func, FunctionDef): self._check_argument_compatibility(new_expr.args, func.arguments, expr, func.is_elemental) From 960d8a762ad9eef6acb365f7744f42577914fc34 Mon Sep 17 00:00:00 2001 From: Aymane Benaissa <47903494+Pinkyboi@users.noreply.github.com> Date: Mon, 17 Oct 2022 18:03:25 +0100 Subject: [PATCH 072/193] Split check for thread per block and block number --- pyccel/parser/semantic.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pyccel/parser/semantic.py b/pyccel/parser/semantic.py index e88a94cb78..7e009e1f90 100644 --- a/pyccel/parser/semantic.py +++ b/pyccel/parser/semantic.py @@ -902,9 +902,12 @@ def _handle_kernel(self, expr, func, args, **settings): symbol = expr, severity='fatal') # TODO : type check the NUMBER OF BLOCKS 'numBlocks' and threads per block 'tpblock' - if not all(isinstance(param, LiteralInteger)\ - for param in [expr.numBlocks, expr.tpblock]): - errors.report("Invalid parameter for Kernel Block number or Thread per Block", + if not isinstance(expr.numBlocks, LiteralInteger): + errors.report("Invalid Block number parameter for Kernel call", + symbol = expr, + severity='error') + if not isinstance(expr.tpblock, LiteralInteger): + errors.report("Invalid Thread per Block parameter for Kernel call", symbol = expr, severity='error') From 7b76f590bb1adda82b02be27f5f3fd06fc55c9cd Mon Sep 17 00:00:00 2001 From: Aymane Benaissa <47903494+Pinkyboi@users.noreply.github.com> Date: Mon, 17 Oct 2022 18:04:40 +0100 Subject: [PATCH 073/193] add ccuda to test languages and add the new error messages --- pyccel/errors/messages.py | 4 ++++ tests/conftest.py | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/pyccel/errors/messages.py b/pyccel/errors/messages.py index cfd457b4d8..b4f28fc91b 100644 --- a/pyccel/errors/messages.py +++ b/pyccel/errors/messages.py @@ -160,3 +160,7 @@ WRONG_LINSPACE_ENDPOINT = 'endpoint argument must be boolean' NON_LITERAL_KEEP_DIMS = 'keep_dims argument must be a literal, otherwise rank is unknown' NON_LITERAL_AXIS = 'axis argument must be a literal, otherwise pyccel cannot determine which dimension to operate on' +KERNEL_STACK_ARRAY_ARG = "A variable allocated on the stack can't be passed to a Kernel function" +NON_KERNEL_FUNCTION_CUDA_VAR = 'Cuda internal variables should only be used in Kernel or Device functions' +UNVALID_KERNEL_CALL_BLOCK_NUM = 'Invalid Block number parameter for Kernel call' +UNVALID_KERNEL_CALL_TP_BLOCK = 'Invalid Thread per Block parameter for Kernel call' \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index 5a0789701b..f8378599b3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -12,7 +12,8 @@ @pytest.fixture( params=[ pytest.param("fortran", marks = pytest.mark.fortran), pytest.param("c", marks = pytest.mark.c), - pytest.param("python", marks = pytest.mark.python) + pytest.param("python", marks = pytest.mark.python), + pytest.param("ccuda", marks = pytest.mark.ccuda) ], scope = "session" ) From 13b08b8ac50639de4aaf8abc7870e8ffd6cbcf4b Mon Sep 17 00:00:00 2001 From: Aymane Benaissa <47903494+Pinkyboi@users.noreply.github.com> Date: Mon, 17 Oct 2022 18:05:13 +0100 Subject: [PATCH 074/193] Add tests --- tests/cuda_test/test_kernel_semantic.py | 108 ++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 tests/cuda_test/test_kernel_semantic.py diff --git a/tests/cuda_test/test_kernel_semantic.py b/tests/cuda_test/test_kernel_semantic.py new file mode 100644 index 0000000000..801bf81951 --- /dev/null +++ b/tests/cuda_test/test_kernel_semantic.py @@ -0,0 +1,108 @@ + +# pylint: disable=missing-function-docstring, missing-module-docstring/ +import pytest + +import numpy as np +from pyccel.epyccel import epyccel +from pyccel.decorators import stack_array, types, kernel +from pyccel.errors.errors import Errors, PyccelSemanticError +from pyccel.errors.messages import (KERNEL_STACK_ARRAY_ARG, + NON_KERNEL_FUNCTION_CUDA_VAR, + UNVALID_KERNEL_CALL_BLOCK_NUM, + UNVALID_KERNEL_CALL_TP_BLOCK, + ) + +def test_stack_array_kernel(language): + @stack_array('arr') + def kernel_caller(): + from numpy import ones + @kernel + @types('int[:]') + def stack_array_kernel(arr): + return arr[0] + arr = ones(1, dtype=int) + return stack_array_kernel[1,1](arr) + + # Initialize singleton that stores Pyccel errors + errors = Errors() + + # epyccel should raise an Exception + with pytest.raises(PyccelSemanticError): + epyccel(kernel_caller, language=language) + + # Check that we got exactly 1 Pyccel error + assert errors.has_errors() + assert errors.num_messages() == 1 + + # Check that the error is correct + error_info = [*errors.error_info_map.values()][0][0] + assert error_info.symbol.func == 'stack_array_kernel' + assert KERNEL_STACK_ARRAY_ARG == error_info.message + +def test_cuda_intern_var_non_kernel(language): + def non_kernel_function(): + from pyccel import cuda + i = cuda.threadIdx(0) + cuda.blockIdx(0) * cuda.blockDim(0) + + # Initialize singleton that stores Pyccel errors + errors = Errors() + + # epyccel should raise an Exception + with pytest.raises(PyccelSemanticError): + epyccel(non_kernel_function, language=language) + + # Check that we got exactly 1 Pyccel error + assert errors.has_errors() + assert errors.num_messages() == 1 + + # Check that the error is correct + error_info = [*errors.error_info_map.values()][0][0] + assert error_info.symbol.name[0] == 'cuda' + assert error_info.symbol.name[1].func_name == 'threadIdx' + assert NON_KERNEL_FUNCTION_CUDA_VAR == error_info.message + +def test_unvalid_block_number(language): + def unvalid_block_number(): + @kernel + def kernel_call(): + pass + kernel_call[1.2,1]() + + # Initialize singleton that stores Pyccel errors + errors = Errors() + + # epyccel should raise an Exception + with pytest.raises(PyccelSemanticError): + epyccel(unvalid_block_number, language=language) + + # Check that we got exactly 1 Pyccel error + assert errors.has_errors() + assert errors.num_messages() == 1 + + # Check that the error is correct + error_info = [*errors.error_info_map.values()][0][0] + assert error_info.symbol.func == 'kernel_call' + assert UNVALID_KERNEL_CALL_BLOCK_NUM == error_info.message + +def test_unvalid_thread_per_block(language): + def unvalid_thread_per_block(): + @kernel + def kernel_call(): + pass + kernel_call[1,1.2]() + + # Initialize singleton that stores Pyccel errors + errors = Errors() + + # epyccel should raise an Exception + with pytest.raises(PyccelSemanticError): + epyccel(unvalid_thread_per_block, language=language) + + # Check that we got exactly 1 Pyccel error + assert errors.has_errors() + assert errors.num_messages() == 1 + + # Check that the error is correct + error_info = [*errors.error_info_map.values()][0][0] + assert error_info.symbol.func == 'kernel_call' + assert UNVALID_KERNEL_CALL_TP_BLOCK == error_info.message From 214a2f68aaa5e0b697650386d20ca9e64ca589b2 Mon Sep 17 00:00:00 2001 From: Aymane Benaissa <47903494+Pinkyboi@users.noreply.github.com> Date: Mon, 17 Oct 2022 18:16:24 +0100 Subject: [PATCH 075/193] Specify test parameter inside the test file instead --- tests/conftest.py | 3 +-- tests/cuda_test/test_kernel_semantic.py | 30 ++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index f8378599b3..5a0789701b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -12,8 +12,7 @@ @pytest.fixture( params=[ pytest.param("fortran", marks = pytest.mark.fortran), pytest.param("c", marks = pytest.mark.c), - pytest.param("python", marks = pytest.mark.python), - pytest.param("ccuda", marks = pytest.mark.ccuda) + pytest.param("python", marks = pytest.mark.python) ], scope = "session" ) diff --git a/tests/cuda_test/test_kernel_semantic.py b/tests/cuda_test/test_kernel_semantic.py index 801bf81951..6a341ad24f 100644 --- a/tests/cuda_test/test_kernel_semantic.py +++ b/tests/cuda_test/test_kernel_semantic.py @@ -12,6 +12,13 @@ UNVALID_KERNEL_CALL_TP_BLOCK, ) +@pytest.mark.parametrize( 'language', [ + pytest.param("fortran", marks = pytest.mark.fortran), + pytest.param("c", marks = pytest.mark.fortran), + pytest.param("ccuda", marks = pytest.mark.ccuda), + pytest.param("python", marks = pytest.mark.python) + ] +) def test_stack_array_kernel(language): @stack_array('arr') def kernel_caller(): @@ -38,7 +45,14 @@ def stack_array_kernel(arr): error_info = [*errors.error_info_map.values()][0][0] assert error_info.symbol.func == 'stack_array_kernel' assert KERNEL_STACK_ARRAY_ARG == error_info.message - + +@pytest.mark.parametrize( 'language', [ + pytest.param("fortran", marks = pytest.mark.fortran), + pytest.param("c", marks = pytest.mark.fortran), + pytest.param("ccuda", marks = pytest.mark.ccuda), + pytest.param("python", marks = pytest.mark.python) + ] +) def test_cuda_intern_var_non_kernel(language): def non_kernel_function(): from pyccel import cuda @@ -61,6 +75,13 @@ def non_kernel_function(): assert error_info.symbol.name[1].func_name == 'threadIdx' assert NON_KERNEL_FUNCTION_CUDA_VAR == error_info.message +@pytest.mark.parametrize( 'language', [ + pytest.param("fortran", marks = pytest.mark.fortran), + pytest.param("c", marks = pytest.mark.fortran), + pytest.param("ccuda", marks = pytest.mark.ccuda), + pytest.param("python", marks = pytest.mark.python) + ] +) def test_unvalid_block_number(language): def unvalid_block_number(): @kernel @@ -84,6 +105,13 @@ def kernel_call(): assert error_info.symbol.func == 'kernel_call' assert UNVALID_KERNEL_CALL_BLOCK_NUM == error_info.message +@pytest.mark.parametrize( 'language', [ + pytest.param("fortran", marks = pytest.mark.fortran), + pytest.param("c", marks = pytest.mark.fortran), + pytest.param("ccuda", marks = pytest.mark.ccuda), + pytest.param("python", marks = pytest.mark.python) + ] +) def test_unvalid_thread_per_block(language): def unvalid_thread_per_block(): @kernel From aede95b23b5439fd1ca68de1f1a8b06b52111c66 Mon Sep 17 00:00:00 2001 From: Aymane Benaissa <47903494+Pinkyboi@users.noreply.github.com> Date: Tue, 18 Oct 2022 14:17:12 +0100 Subject: [PATCH 076/193] Make tests run only with ccuda --- tests/cuda_test/test_kernel_semantic.py | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/tests/cuda_test/test_kernel_semantic.py b/tests/cuda_test/test_kernel_semantic.py index 6a341ad24f..d2cfd5e143 100644 --- a/tests/cuda_test/test_kernel_semantic.py +++ b/tests/cuda_test/test_kernel_semantic.py @@ -13,10 +13,7 @@ ) @pytest.mark.parametrize( 'language', [ - pytest.param("fortran", marks = pytest.mark.fortran), - pytest.param("c", marks = pytest.mark.fortran), - pytest.param("ccuda", marks = pytest.mark.ccuda), - pytest.param("python", marks = pytest.mark.python) + pytest.param("ccuda", marks = pytest.mark.ccuda) ] ) def test_stack_array_kernel(language): @@ -47,10 +44,7 @@ def stack_array_kernel(arr): assert KERNEL_STACK_ARRAY_ARG == error_info.message @pytest.mark.parametrize( 'language', [ - pytest.param("fortran", marks = pytest.mark.fortran), - pytest.param("c", marks = pytest.mark.fortran), - pytest.param("ccuda", marks = pytest.mark.ccuda), - pytest.param("python", marks = pytest.mark.python) + pytest.param("ccuda", marks = pytest.mark.ccuda) ] ) def test_cuda_intern_var_non_kernel(language): @@ -76,10 +70,7 @@ def non_kernel_function(): assert NON_KERNEL_FUNCTION_CUDA_VAR == error_info.message @pytest.mark.parametrize( 'language', [ - pytest.param("fortran", marks = pytest.mark.fortran), - pytest.param("c", marks = pytest.mark.fortran), - pytest.param("ccuda", marks = pytest.mark.ccuda), - pytest.param("python", marks = pytest.mark.python) + pytest.param("ccuda", marks = pytest.mark.ccuda) ] ) def test_unvalid_block_number(language): @@ -106,10 +97,7 @@ def kernel_call(): assert UNVALID_KERNEL_CALL_BLOCK_NUM == error_info.message @pytest.mark.parametrize( 'language', [ - pytest.param("fortran", marks = pytest.mark.fortran), - pytest.param("c", marks = pytest.mark.fortran), - pytest.param("ccuda", marks = pytest.mark.ccuda), - pytest.param("python", marks = pytest.mark.python) + pytest.param("ccuda", marks = pytest.mark.ccuda) ] ) def test_unvalid_thread_per_block(language): From dbff2d7464b06b5a6f037536c7879db20702990b Mon Sep 17 00:00:00 2001 From: Fatima-zahra RAMDANI Date: Wed, 19 Oct 2022 12:31:32 +0100 Subject: [PATCH 077/193] update create_cuda_array() and add tests --- pyccel/codegen/printing/ccudacode.py | 7 ++++++- pyccel/stdlib/ndarrays/ndarrays.h | 6 +++--- .../scripts/ccuda/create_array_device.py | 16 ++++++++++++++++ .../internal/scripts/ccuda/create_array_host.py | 16 ++++++++++++++++ tests/internal/scripts/ccuda/kernel_launch.py | 2 +- 5 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 tests/internal/scripts/ccuda/create_array_device.py create mode 100644 tests/internal/scripts/ccuda/create_array_host.py diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index 1dbaa8c0eb..a124f1baa8 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -334,8 +334,13 @@ def _print_Allocate(self, expr): is_view = 'false' if expr.variable.on_heap else 'true' # if expr.variable.is_managed or expr.variable.on_device: self.add_import(c_imports['cuda_ndarrays']) + memory_location = expr.variable.memory_location + if memory_location in ('device', 'host'): + memory_location = 'allocateMemoryOn' + str(memory_location).capitalize() + else: + memory_location = 'managedMemory' alloc_code = "{} = cuda_array_create({}, {}, {}, {}, {});".format( - expr.variable, len(expr.shape), tmp_shape, dtype, is_view, expr.variable.memory_location) + expr.variable, len(expr.shape), tmp_shape, dtype, is_view, memory_location) # else: # self.add_import(c_imports['ndarrays']) # alloc_code = "{} = array_create({}, {}, {}, {});".format( diff --git a/pyccel/stdlib/ndarrays/ndarrays.h b/pyccel/stdlib/ndarrays/ndarrays.h index dd911b797f..135b4635ce 100644 --- a/pyccel/stdlib/ndarrays/ndarrays.h +++ b/pyccel/stdlib/ndarrays/ndarrays.h @@ -67,9 +67,9 @@ enum e_types enum e_memory_locations { - managed, - host, - device + managedMemory, + allocateMemoryOnHost, + allocateMemoryOnDevice }; typedef struct s_ndarray diff --git a/tests/internal/scripts/ccuda/create_array_device.py b/tests/internal/scripts/ccuda/create_array_device.py new file mode 100644 index 0000000000..7f8ed4b6b6 --- /dev/null +++ b/tests/internal/scripts/ccuda/create_array_device.py @@ -0,0 +1,16 @@ +from pyccel.decorators import kernel, types +from pyccel import cuda + +@kernel +@types('int[:]') +def func(a): + i = cuda.threadIdx(0) + cuda.blockIdx(0) * cuda.blockDim(0) + print("Hello World! ", a[i]) + +if __name__ == '__main__': + threads_per_block = 5 + n_blocks = 1 + arr = cuda.array([0, 1, 2, 3, 4], memory_location='device') + cuda.deviceSynchronize() + func[n_blocks, threads_per_block](arr) + cuda.deviceSynchronize() \ No newline at end of file diff --git a/tests/internal/scripts/ccuda/create_array_host.py b/tests/internal/scripts/ccuda/create_array_host.py new file mode 100644 index 0000000000..fd3abd632c --- /dev/null +++ b/tests/internal/scripts/ccuda/create_array_host.py @@ -0,0 +1,16 @@ +from pyccel.decorators import kernel, types +from pyccel import cuda + +@kernel +@types('int[:]') +def func(a): + i = cuda.threadIdx(0) + cuda.blockIdx(0) * cuda.blockDim(0) + print("Hello World! ", a[i]) + +if __name__ == '__main__': + threads_per_block = 5 + n_blocks = 1 + arr = cuda.array([0, 1, 2, 3, 4], memory_location='host') + cuda.deviceSynchronize() + func[n_blocks, threads_per_block](arr) + cuda.deviceSynchronize() \ No newline at end of file diff --git a/tests/internal/scripts/ccuda/kernel_launch.py b/tests/internal/scripts/ccuda/kernel_launch.py index 7f8ed4b6b6..fd3abd632c 100644 --- a/tests/internal/scripts/ccuda/kernel_launch.py +++ b/tests/internal/scripts/ccuda/kernel_launch.py @@ -10,7 +10,7 @@ def func(a): if __name__ == '__main__': threads_per_block = 5 n_blocks = 1 - arr = cuda.array([0, 1, 2, 3, 4], memory_location='device') + arr = cuda.array([0, 1, 2, 3, 4], memory_location='host') cuda.deviceSynchronize() func[n_blocks, threads_per_block](arr) cuda.deviceSynchronize() \ No newline at end of file From fd0bf9a566ec5a029861a695d1f3ea1d4a3bd47b Mon Sep 17 00:00:00 2001 From: Fatima-zahra RAMDANI Date: Thu, 20 Oct 2022 12:50:09 +0100 Subject: [PATCH 078/193] Add tests for each memory_location --- .../internal/scripts/ccuda/create_array_host.py | 16 ---------------- .../internal/scripts/ccuda/cuda_array_device.py | 17 +++++++++++++++++ tests/internal/scripts/ccuda/cuda_array_host.py | 12 ++++++++++++ ...te_array_device.py => cuda_array_managed.py} | 10 +++++----- tests/internal/scripts/ccuda/kernel_launch.py | 2 +- 5 files changed, 35 insertions(+), 22 deletions(-) delete mode 100644 tests/internal/scripts/ccuda/create_array_host.py create mode 100644 tests/internal/scripts/ccuda/cuda_array_device.py create mode 100644 tests/internal/scripts/ccuda/cuda_array_host.py rename tests/internal/scripts/ccuda/{create_array_device.py => cuda_array_managed.py} (50%) diff --git a/tests/internal/scripts/ccuda/create_array_host.py b/tests/internal/scripts/ccuda/create_array_host.py deleted file mode 100644 index fd3abd632c..0000000000 --- a/tests/internal/scripts/ccuda/create_array_host.py +++ /dev/null @@ -1,16 +0,0 @@ -from pyccel.decorators import kernel, types -from pyccel import cuda - -@kernel -@types('int[:]') -def func(a): - i = cuda.threadIdx(0) + cuda.blockIdx(0) * cuda.blockDim(0) - print("Hello World! ", a[i]) - -if __name__ == '__main__': - threads_per_block = 5 - n_blocks = 1 - arr = cuda.array([0, 1, 2, 3, 4], memory_location='host') - cuda.deviceSynchronize() - func[n_blocks, threads_per_block](arr) - cuda.deviceSynchronize() \ No newline at end of file diff --git a/tests/internal/scripts/ccuda/cuda_array_device.py b/tests/internal/scripts/ccuda/cuda_array_device.py new file mode 100644 index 0000000000..e7fb520633 --- /dev/null +++ b/tests/internal/scripts/ccuda/cuda_array_device.py @@ -0,0 +1,17 @@ +from pyccel.decorators import kernel, types +from pyccel import cuda + +@kernel +@types('int[:]') +def square(a): + index = cuda.blockIdx(0) * cuda.blockDim(0) + cuda.threadIdx(0) + a[index] = a[index] * a[index] + +if __name__ == '__main__': + threads_per_block = 5 + n_blocks = 1 + arr = cuda.array([0,1,2,3,4], memory_location = 'device') + #b = cuda.array([0,1,2,3,4], memory_location = 'device') + cuda.deviceSynchronize() + square[n_blocks, threads_per_block](arr) + cuda.deviceSynchronize() diff --git a/tests/internal/scripts/ccuda/cuda_array_host.py b/tests/internal/scripts/ccuda/cuda_array_host.py new file mode 100644 index 0000000000..5697c32963 --- /dev/null +++ b/tests/internal/scripts/ccuda/cuda_array_host.py @@ -0,0 +1,12 @@ +from pyccel.decorators import kernel, types +from pyccel import cuda + +@types('int[:]') +def multiplyPrintElements(a): + for i in a: + i = i * 2 + print(i) + +if __name__ == '__main__': + arr = cuda.array([0,1,2,3,4], memory_location='host') + multiplyPrintElements(arr) diff --git a/tests/internal/scripts/ccuda/create_array_device.py b/tests/internal/scripts/ccuda/cuda_array_managed.py similarity index 50% rename from tests/internal/scripts/ccuda/create_array_device.py rename to tests/internal/scripts/ccuda/cuda_array_managed.py index 7f8ed4b6b6..83817a2451 100644 --- a/tests/internal/scripts/ccuda/create_array_device.py +++ b/tests/internal/scripts/ccuda/cuda_array_managed.py @@ -3,14 +3,14 @@ @kernel @types('int[:]') -def func(a): - i = cuda.threadIdx(0) + cuda.blockIdx(0) * cuda.blockDim(0) - print("Hello World! ", a[i]) +def square(a): + index = cuda.blockIdx(0) * cuda.blockDim(0) + cuda.threadIdx(0) + a[index] = a[index] * a[index] if __name__ == '__main__': threads_per_block = 5 n_blocks = 1 - arr = cuda.array([0, 1, 2, 3, 4], memory_location='device') + a = cuda.array([0,1,2,3,4], memory_location = 'managed') cuda.deviceSynchronize() - func[n_blocks, threads_per_block](arr) + square[n_blocks, threads_per_block](a) cuda.deviceSynchronize() \ No newline at end of file diff --git a/tests/internal/scripts/ccuda/kernel_launch.py b/tests/internal/scripts/ccuda/kernel_launch.py index fd3abd632c..16cf40fbdc 100644 --- a/tests/internal/scripts/ccuda/kernel_launch.py +++ b/tests/internal/scripts/ccuda/kernel_launch.py @@ -10,7 +10,7 @@ def func(a): if __name__ == '__main__': threads_per_block = 5 n_blocks = 1 - arr = cuda.array([0, 1, 2, 3, 4], memory_location='host') + arr = cuda.array([0, 1, 2, 3, 4]) cuda.deviceSynchronize() func[n_blocks, threads_per_block](arr) cuda.deviceSynchronize() \ No newline at end of file From a011e4470408a703d2a197b4c0a2733927037d2a Mon Sep 17 00:00:00 2001 From: Fatima-zahra RAMDANI Date: Thu, 20 Oct 2022 22:45:36 +0100 Subject: [PATCH 079/193] update test for managedMemory --- tests/internal/scripts/ccuda/cuda_array_managed.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/internal/scripts/ccuda/cuda_array_managed.py b/tests/internal/scripts/ccuda/cuda_array_managed.py index 83817a2451..5d55151e65 100644 --- a/tests/internal/scripts/ccuda/cuda_array_managed.py +++ b/tests/internal/scripts/ccuda/cuda_array_managed.py @@ -13,4 +13,5 @@ def square(a): a = cuda.array([0,1,2,3,4], memory_location = 'managed') cuda.deviceSynchronize() square[n_blocks, threads_per_block](a) - cuda.deviceSynchronize() \ No newline at end of file + cuda.deviceSynchronize() + print(a) \ No newline at end of file From cd4b53e4c30c995fbab13cc861c329cd551a0b6d Mon Sep 17 00:00:00 2001 From: Aymane Benaissa <47903494+Pinkyboi@users.noreply.github.com> Date: Sat, 22 Oct 2022 19:15:54 +0100 Subject: [PATCH 080/193] Initial issue fix --- pyccel/ast/cudaext.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index 7846c9ba60..80890bfc91 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -27,7 +27,7 @@ from .literals import LiteralTrue, LiteralFalse from .literals import Nil from .mathext import MathCeil -from .operators import broadcast, PyccelMinus, PyccelDiv +from .operators import PyccelAdd, PyccelLe, PyccelMul, broadcast, PyccelMinus, PyccelDiv from .variable import (Variable, Constant, HomogeneousTupleVariable) from .numpyext import process_dtype, process_shape @@ -42,7 +42,8 @@ 'CudaThreadIdx', 'CudaBlockDim', 'CudaBlockIdx', - 'CudaGridDim' + 'CudaGridDim', + 'CudaGrid' ) #============================================================================== @@ -147,6 +148,9 @@ class CudaInternalVar(PyccelAstNode): _attribute_nodes = ('_dim',) def __init__(self, dim=None): + + if isinstance(dim, int): + dim = LiteralInteger(dim) if not isinstance(dim, LiteralInteger): raise TypeError("dimension need to be an integer") if dim not in (0, 1, 2): @@ -164,10 +168,19 @@ def __init__(self, dim=None): def dim(self): return self._dim -class CudaThreadIdx(CudaInternalVar) : pass -class CudaBlockDim(CudaInternalVar) : pass -class CudaBlockIdx(CudaInternalVar) : pass -class CudaGridDim(CudaInternalVar) : pass +class CudaThreadIdx(CudaInternalVar) : pass +class CudaBlockDim(CudaInternalVar) : pass +class CudaBlockIdx(CudaInternalVar) : pass +class CudaGridDim(CudaInternalVar) : pass +class CudaGrid(CudaInternalVar) : + def __new__(cls, dim=0): + if not isinstance(dim, LiteralInteger): + raise TypeError("dimension need to be an integer") + if dim not in (0, 1, 2): + raise ValueError("dimension need to be 0, 1 or 2") + expr = [PyccelAdd(PyccelMul(CudaBlockIdx(d), CudaBlockDim(d)), CudaThreadIdx(d))\ + for d in range(dim.python_value + 1)] + return PythonTuple(*expr) cuda_funcs = { @@ -177,7 +190,8 @@ class CudaGridDim(CudaInternalVar) : pass 'threadIdx' : PyccelFunctionDef('threadIdx' , CudaThreadIdx), 'blockDim' : PyccelFunctionDef('blockDim' , CudaBlockDim), 'blockIdx' : PyccelFunctionDef('blockIdx' , CudaBlockIdx), - 'gridDim' : PyccelFunctionDef('gridDim' , CudaGridDim) + 'gridDim' : PyccelFunctionDef('gridDim' , CudaGridDim), + 'grid' : PyccelFunctionDef('grid' , CudaGrid) } cuda_Internal_Var = { From 8bd840ef4f56d945e399c1bf21f32b760d46bf52 Mon Sep 17 00:00:00 2001 From: Aymane Benaissa <47903494+Pinkyboi@users.noreply.github.com> Date: Sat, 22 Oct 2022 23:18:02 +0100 Subject: [PATCH 081/193] Handle case of one dimensional call --- pyccel/ast/cudaext.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index 80890bfc91..c96d85a7fe 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -172,7 +172,7 @@ class CudaThreadIdx(CudaInternalVar) : pass class CudaBlockDim(CudaInternalVar) : pass class CudaBlockIdx(CudaInternalVar) : pass class CudaGridDim(CudaInternalVar) : pass -class CudaGrid(CudaInternalVar) : +class CudaGrid(PyccelAstNode) : def __new__(cls, dim=0): if not isinstance(dim, LiteralInteger): raise TypeError("dimension need to be an integer") @@ -180,6 +180,8 @@ def __new__(cls, dim=0): raise ValueError("dimension need to be 0, 1 or 2") expr = [PyccelAdd(PyccelMul(CudaBlockIdx(d), CudaBlockDim(d)), CudaThreadIdx(d))\ for d in range(dim.python_value + 1)] + if dim == 0: + return expr[0] return PythonTuple(*expr) From abdac0a399f7e1171427726e8a9d69600496092c Mon Sep 17 00:00:00 2001 From: Fatima-zahra RAMDANI Date: Mon, 24 Oct 2022 15:08:20 +0100 Subject: [PATCH 082/193] remove unnecessary comments --- pyccel/codegen/printing/ccudacode.py | 7 +------ pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu | 3 --- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index a124f1baa8..7bbf6f0d67 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -332,8 +332,8 @@ def _print_Allocate(self, expr): dtype = self.find_in_ndarray_type_registry(dtype, expr.variable.precision) shape_Assign = "{} {}[] = {{{}}};".format(shape_dtype, tmp_shape, shape) is_view = 'false' if expr.variable.on_heap else 'true' - # if expr.variable.is_managed or expr.variable.on_device: self.add_import(c_imports['cuda_ndarrays']) + # define the memory location for the created cuda array memory_location = expr.variable.memory_location if memory_location in ('device', 'host'): memory_location = 'allocateMemoryOn' + str(memory_location).capitalize() @@ -341,11 +341,6 @@ def _print_Allocate(self, expr): memory_location = 'managedMemory' alloc_code = "{} = cuda_array_create({}, {}, {}, {}, {});".format( expr.variable, len(expr.shape), tmp_shape, dtype, is_view, memory_location) - # else: - # self.add_import(c_imports['ndarrays']) - # alloc_code = "{} = array_create({}, {}, {}, {});".format( - # expr.variable, len(expr.shape), tmp_shape, dtype, - # is_view) return '{}\n{}\n{}\n'.format(free_code, shape_Assign, alloc_code) def _print_Deallocate(self, expr): diff --git a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu index b17bef131e..2c0d517e19 100644 --- a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu +++ b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu @@ -114,7 +114,6 @@ t_ndarray cuda_array_create(int32_t nd, int64_t *shape, arr.is_view = is_view; arr.length = 1; (*fun_ptr_arr[location])(&(arr.shape), arr.nd * sizeof(int64_t)); - //cudaMallocManaged(&(arr.shape), arr.nd * sizeof(int64_t)); for (int32_t i = 0; i < arr.nd; i++) { arr.length *= shape[i]; @@ -122,7 +121,6 @@ t_ndarray cuda_array_create(int32_t nd, int64_t *shape, } arr.buffer_size = arr.length * arr.type_size; (*fun_ptr_arr[location])(&(arr.strides), nd * sizeof(int64_t)); - //cudaMallocManaged(&(arr.strides), nd * sizeof(int64_t)); for (int32_t i = 0; i < arr.nd; i++) { arr.strides[i] = 1; @@ -131,7 +129,6 @@ t_ndarray cuda_array_create(int32_t nd, int64_t *shape, } if (!is_view) (*fun_ptr_arr[location])(&(arr.raw_data), arr.buffer_size); - //cudaMallocManaged(&(arr.raw_data), arr.buffer_size); return (arr); } From 32959a6c108f560d8863f1b9220b6af15e7f2bed Mon Sep 17 00:00:00 2001 From: Aymane Benaissa <47903494+Pinkyboi@users.noreply.github.com> Date: Tue, 25 Oct 2022 01:16:27 +0100 Subject: [PATCH 083/193] Add test for cuda_grid --- tests/internal/scripts/ccuda/cuda_grid.py | 30 +++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/internal/scripts/ccuda/cuda_grid.py diff --git a/tests/internal/scripts/ccuda/cuda_grid.py b/tests/internal/scripts/ccuda/cuda_grid.py new file mode 100644 index 0000000000..a17b004ea2 --- /dev/null +++ b/tests/internal/scripts/ccuda/cuda_grid.py @@ -0,0 +1,30 @@ +from pyccel.decorators import kernel, types +from pyccel import cuda + +@kernel +@types('int[:]') +def func_1d(a): + i = cuda.grid(0) + print("1 dim :", a[i]) + +@kernel +@types('int[:]') +def func_2d(a): + i, j = cuda.grid(1) + print("2 dim :", a[i], a[j]) + +@kernel +@types('int[:]') +def func_3d(a): + i, j, k = cuda.grid(2) + print("3 dim :", a[i], a[j], a[k]) + +if __name__ == '__main__': + threads_per_block = 5 + n_blocks = 1 + arr = cuda.array([0, 1, 2, 3, 4]) + cuda.deviceSynchronize() + # Sice we dont support multi-dim n_block / threads_per_block + # func_2d and func_3d won't compile + func_1d[n_blocks, threads_per_block](arr) + cuda.deviceSynchronize() \ No newline at end of file From a0dcf124521ae9b00a3c5fff3e130b264ad07679 Mon Sep 17 00:00:00 2001 From: Aymane Benaissa <47903494+Pinkyboi@users.noreply.github.com> Date: Wed, 26 Oct 2022 14:15:46 +0100 Subject: [PATCH 084/193] Call other kernels in test --- tests/internal/scripts/ccuda/cuda_grid.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/internal/scripts/ccuda/cuda_grid.py b/tests/internal/scripts/ccuda/cuda_grid.py index a17b004ea2..c9442c16ee 100644 --- a/tests/internal/scripts/ccuda/cuda_grid.py +++ b/tests/internal/scripts/ccuda/cuda_grid.py @@ -24,7 +24,7 @@ def func_3d(a): n_blocks = 1 arr = cuda.array([0, 1, 2, 3, 4]) cuda.deviceSynchronize() - # Sice we dont support multi-dim n_block / threads_per_block - # func_2d and func_3d won't compile func_1d[n_blocks, threads_per_block](arr) + func_2d[n_blocks, threads_per_block](arr) + func_3d[n_blocks, threads_per_block](arr) cuda.deviceSynchronize() \ No newline at end of file From 3c603a4eec8eac38bf5e78e39bc1cd14f4a43996 Mon Sep 17 00:00:00 2001 From: Fatima-zahra RAMDANI Date: Wed, 26 Oct 2022 14:49:48 +0100 Subject: [PATCH 085/193] remove unnecessary commented lines --- tests/internal/scripts/ccuda/cuda_array_device.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/internal/scripts/ccuda/cuda_array_device.py b/tests/internal/scripts/ccuda/cuda_array_device.py index e7fb520633..fa2e0f0c8c 100644 --- a/tests/internal/scripts/ccuda/cuda_array_device.py +++ b/tests/internal/scripts/ccuda/cuda_array_device.py @@ -11,7 +11,6 @@ def square(a): threads_per_block = 5 n_blocks = 1 arr = cuda.array([0,1,2,3,4], memory_location = 'device') - #b = cuda.array([0,1,2,3,4], memory_location = 'device') cuda.deviceSynchronize() square[n_blocks, threads_per_block](arr) cuda.deviceSynchronize() From bb7cb0c183dfbda7d87cb087dac569f03b8c65f0 Mon Sep 17 00:00:00 2001 From: Fatima-zahra RAMDANI Date: Fri, 28 Oct 2022 00:13:03 +0100 Subject: [PATCH 086/193] add class CudaCopy --- pyccel/ast/cudaext.py | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index aed2e206ed..3439afba6b 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -170,6 +170,44 @@ def __init__(self, dim=None): def dim(self): return self._dim +class CudaCopy(CudaNewArray): + """ + Represents a call to cuda.copy for code generation. + + arg : list, tuple, PythonList + + memory_location : str + + """ + __slots__ = ('_arg','_dtype','_precision','_shape','_rank','_order','_memory_location') + + def __init__(self, arg, memory_location): + + if not isinstance(arg, (PythonTuple, PythonList, Variable)): + raise TypeError('unknown type of %s.' % type(arg)) + + # Verify the direction of the copy + if (arg._memory_location or arg._memory_location) not in ('device', 'host'): + raise ValueError("The direction of the copy should be between host and device") + + self._arg = arg + self._shape = arg._shape + self._rank = arg._rank + self._dtype = arg._dtype + self._order = arg._order + self._precision = arg._precision + self._memory_location = memory_location + super().__init__() + + @property + def arg(self): + return self._arg + + @property + def memory_location(self): + return self._memory_location + + class CudaThreadIdx(CudaInternalVar) : pass class CudaBlockDim(CudaInternalVar) : pass class CudaBlockIdx(CudaInternalVar) : pass @@ -183,7 +221,8 @@ class CudaGridDim(CudaInternalVar) : pass 'threadIdx' : PyccelFunctionDef('threadIdx' , CudaThreadIdx), 'blockDim' : PyccelFunctionDef('blockDim' , CudaBlockDim), 'blockIdx' : PyccelFunctionDef('blockIdx' , CudaBlockIdx), - 'gridDim' : PyccelFunctionDef('gridDim' , CudaGridDim) + 'gridDim' : PyccelFunctionDef('gridDim' , CudaGridDim), + 'copy' : PyccelFunctionDef('copy' , CudaCopy) } cuda_Internal_Var = { From 40dbf9c8c37abd48247c9163e763c10fff7de12c Mon Sep 17 00:00:00 2001 From: Fatima-zahra RAMDANI Date: Fri, 28 Oct 2022 01:23:05 +0100 Subject: [PATCH 087/193] add printer and tests for cuda.copy --- pyccel/ast/cudaext.py | 10 +++++++--- pyccel/codegen/printing/ccudacode.py | 12 ++++++++++-- tests/internal/scripts/ccuda/cuda_copy.py | 6 ++++++ 3 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 tests/internal/scripts/ccuda/cuda_copy.py diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index 3439afba6b..c8d34fdaa1 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -186,9 +186,13 @@ def __init__(self, arg, memory_location): if not isinstance(arg, (PythonTuple, PythonList, Variable)): raise TypeError('unknown type of %s.' % type(arg)) - # Verify the direction of the copy - if (arg._memory_location or arg._memory_location) not in ('device', 'host'): - raise ValueError("The direction of the copy should be between host and device") + # Verify the memory_location of src + if arg._memory_location not in ('device', 'host'): + raise ValueError("The direction of the copy should be from 'host' or 'device'") + + # Verify the memory_location of dst + if memory_location not in ('device', 'host'): + raise ValueError("The direction of the copy should be to 'host' or 'device'") self._arg = arg self._shape = arg._shape diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index 7bbf6f0d67..badacbbf09 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -42,7 +42,7 @@ from pyccel.ast.cupyext import CupyFull, CupyArray, CupyArange -from pyccel.ast.cudaext import cuda_Internal_Var, CudaArray +from pyccel.ast.cudaext import CudaCopy, cuda_Internal_Var, CudaArray from pyccel.ast.utilities import expand_to_loops @@ -418,6 +418,8 @@ def _print_Assign(self, expr): return prefix_code+self.arrayFill(expr) if isinstance(rhs, NumpyArange): return prefix_code+self.fill_NumpyArange(rhs, lhs) + if isinstance(rhs, CudaCopy): + return prefix_code+self.cudaCopy(lhs, rhs) lhs = self._print(expr.lhs) rhs = self._print(expr.rhs) return prefix_code+'{} = {};\n'.format(lhs, rhs) @@ -542,7 +544,13 @@ def _print_CudaInternalVar(self, expr): var_name = cuda_Internal_Var[var_name] dim_c = ('x', 'y', 'z')[expr.dim] return '{}.{}'.format(var_name, dim_c) - + + def cudaCopy(self, lhs, rhs): + from_location = str(rhs._arg._memory_location).capitalize() + to_location = str(rhs._memory_location).capitalize() + transfer_type = 'cudaMemcpy{0}To{1}'.format(from_location, to_location) + code = "cudaMemcpy({0}.raw_data, {1}.raw_data, {0}.buffer_size, {2});".format(lhs, rhs._arg, transfer_type) + return '%s\n' % (code) def ccudacode(expr, filename, assign_to=None, **settings): """Converts an expr to a string of ccuda code diff --git a/tests/internal/scripts/ccuda/cuda_copy.py b/tests/internal/scripts/ccuda/cuda_copy.py new file mode 100644 index 0000000000..36885a4217 --- /dev/null +++ b/tests/internal/scripts/ccuda/cuda_copy.py @@ -0,0 +1,6 @@ +from pyccel.decorators import kernel, types +from pyccel import cuda + +if __name__ == '__main__': + arr = cuda.array([1,2,3,4], memory_location='device') + cpy = cuda.copy(arr, 'host') \ No newline at end of file From 4ec6b7fcc625cb297c82bddc513b1a107f1bcba2 Mon Sep 17 00:00:00 2001 From: Aymane Benaissa <47903494+Pinkyboi@users.noreply.github.com> Date: Fri, 28 Oct 2022 14:03:40 +0100 Subject: [PATCH 088/193] Comment calls to func_2d func_3d --- tests/internal/scripts/ccuda/cuda_grid.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/internal/scripts/ccuda/cuda_grid.py b/tests/internal/scripts/ccuda/cuda_grid.py index c9442c16ee..92b8c74a83 100644 --- a/tests/internal/scripts/ccuda/cuda_grid.py +++ b/tests/internal/scripts/ccuda/cuda_grid.py @@ -25,6 +25,8 @@ def func_3d(a): arr = cuda.array([0, 1, 2, 3, 4]) cuda.deviceSynchronize() func_1d[n_blocks, threads_per_block](arr) - func_2d[n_blocks, threads_per_block](arr) - func_3d[n_blocks, threads_per_block](arr) + # Since we dont support multi-dim n_block / threads_per_block + # func_2d and func_3d won't compile + # func_2d[n_blocks, threads_per_block](arr) + # func_3d[n_blocks, threads_per_block](arr) cuda.deviceSynchronize() \ No newline at end of file From 592f909b81a63c352981fff87ad65456358a4120 Mon Sep 17 00:00:00 2001 From: Fatima-zahra RAMDANI Date: Mon, 31 Oct 2022 13:20:36 +0100 Subject: [PATCH 089/193] add support for asynchronous copy --- pyccel/ast/cudaext.py | 18 +++++++++++++++--- pyccel/codegen/printing/ccudacode.py | 7 +++++-- tests/internal/scripts/ccuda/cuda_copy.py | 2 +- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index c8d34fdaa1..5a82b12deb 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -174,14 +174,21 @@ class CudaCopy(CudaNewArray): """ Represents a call to cuda.copy for code generation. + Parameters + ---------- arg : list, tuple, PythonList memory_location : str + 'host' the newly created array is allocated on host. + 'device' the newly created array is allocated on device. + + is_async: bool + Indicates whether the copy is asynchronous or not [Default value: False] """ - __slots__ = ('_arg','_dtype','_precision','_shape','_rank','_order','_memory_location') + __slots__ = ('_arg','_dtype','_precision','_shape','_rank','_order','_memory_location', '_is_async') - def __init__(self, arg, memory_location): + def __init__(self, arg, memory_location, is_async=False): if not isinstance(arg, (PythonTuple, PythonList, Variable)): raise TypeError('unknown type of %s.' % type(arg)) @@ -193,7 +200,11 @@ def __init__(self, arg, memory_location): # Verify the memory_location of dst if memory_location not in ('device', 'host'): raise ValueError("The direction of the copy should be to 'host' or 'device'") - + + # verify the type of is_async + if not isinstance(is_async, (LiteralTrue, LiteralFalse, bool)): + raise TypeError('is_async must be boolean') + self._arg = arg self._shape = arg._shape self._rank = arg._rank @@ -201,6 +212,7 @@ def __init__(self, arg, memory_location): self._order = arg._order self._precision = arg._precision self._memory_location = memory_location + self._is_async = is_async super().__init__() @property diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index badacbbf09..3d1eba3df5 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -549,8 +549,11 @@ def cudaCopy(self, lhs, rhs): from_location = str(rhs._arg._memory_location).capitalize() to_location = str(rhs._memory_location).capitalize() transfer_type = 'cudaMemcpy{0}To{1}'.format(from_location, to_location) - code = "cudaMemcpy({0}.raw_data, {1}.raw_data, {0}.buffer_size, {2});".format(lhs, rhs._arg, transfer_type) - return '%s\n' % (code) + if isinstance(rhs._is_async, LiteralTrue): + cpy_data = "cudaMemcpyAsync({0}.raw_data, {1}.raw_data, {0}.buffer_size, {2}, 0);".format(lhs, rhs._arg, transfer_type) + else: + cpy_data = "cudaMemcpy({0}.raw_data, {1}.raw_data, {0}.buffer_size, {2});".format(lhs, rhs._arg, transfer_type) + return '%s\n' % (cpy_data) def ccudacode(expr, filename, assign_to=None, **settings): """Converts an expr to a string of ccuda code diff --git a/tests/internal/scripts/ccuda/cuda_copy.py b/tests/internal/scripts/ccuda/cuda_copy.py index 36885a4217..462f36684f 100644 --- a/tests/internal/scripts/ccuda/cuda_copy.py +++ b/tests/internal/scripts/ccuda/cuda_copy.py @@ -3,4 +3,4 @@ if __name__ == '__main__': arr = cuda.array([1,2,3,4], memory_location='device') - cpy = cuda.copy(arr, 'host') \ No newline at end of file + cpy = cuda.copy(arr, 'host', is_async=True) \ No newline at end of file From e543938a3da8ab15ac1ebcdfd75c58267f2201fd Mon Sep 17 00:00:00 2001 From: Aymane Benaissa <47903494+Pinkyboi@users.noreply.github.com> Date: Tue, 8 Nov 2022 11:32:00 +0100 Subject: [PATCH 090/193] Initial addition of memcopy_kind --- pyccel/ast/cudaext.py | 9 +++++++-- pyccel/ast/cupyext.py | 12 +++++++++++- pyccel/ast/variable.py | 21 +++++++++++++++++++-- pyccel/codegen/printing/ccudacode.py | 6 ++++-- pyccel/parser/semantic.py | 7 ++++--- 5 files changed, 45 insertions(+), 10 deletions(-) diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index 79f42bda74..4aadaa525c 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -74,11 +74,11 @@ class CudaArray(CudaNewArray): arg : list, tuple, PythonList """ - __slots__ = ('_arg','_dtype','_precision','_shape','_rank','_order','_memory_location') + __slots__ = ('_arg','_dtype','_precision','_shape','_rank','_order','_memory_location', '_current_location') _attribute_nodes = ('_arg',) name = 'array' - def __init__(self, arg, dtype=None, order='C', memory_location='managed'): + def __init__(self, arg, dtype=None, order='C', memory_location='managed', current_location='host'): if not isinstance(arg, (PythonTuple, PythonList, Variable)): raise TypeError('Unknown type of %s.' % type(arg)) @@ -125,6 +125,7 @@ def __init__(self, arg, dtype=None, order='C', memory_location='managed'): self._order = order self._precision = prec self._memory_location = memory_location + self._current_location = current_location super().__init__() def __str__(self): @@ -137,6 +138,10 @@ def arg(self): def memory_location(self): return self._memory_location + @property + def current_location(self): + return self._current_location + class CudaDeviceSynchronize(PyccelInternalFunction): "Represents a call to Cuda.deviceSynchronize for code generation." # pass diff --git a/pyccel/ast/cupyext.py b/pyccel/ast/cupyext.py index 08650db3eb..0b9191b041 100644 --- a/pyccel/ast/cupyext.py +++ b/pyccel/ast/cupyext.py @@ -69,7 +69,7 @@ class CupyArray(CudaNewArray): _attribute_nodes = ('_arg',) name = 'array' - def __init__(self, arg, dtype=None, order='C'): + def __init__(self, arg, dtype=None, order='C', memory_location='managed', current_location='host'): if not isinstance(arg, (PythonTuple, PythonList, Variable)): raise TypeError('Unknown type of %s.' % type(arg)) @@ -113,6 +113,8 @@ def __init__(self, arg, dtype=None, order='C'): self._dtype = dtype self._order = order self._precision = prec + self._memory_location = memory_location + self._current_location = current_location super().__init__() def __str__(self): @@ -122,6 +124,14 @@ def __str__(self): def arg(self): return self._arg + @property + def memory_location(self): + return self._memory_location + + @property + def current_location(self): + return self._current_location + #============================================================================== class CupyArange(CudaNewArray): """ diff --git a/pyccel/ast/variable.py b/pyccel/ast/variable.py index 36a92ac858..7421cddf42 100644 --- a/pyccel/ast/variable.py +++ b/pyccel/ast/variable.py @@ -61,6 +61,11 @@ class Variable(PyccelAstNode): 'managed' the variable can be accessed by CPU and GPU and is being managed by the Cuda API (memory transfer is being done implicitly) [Default value: 'host'] + current_location: str + 'host' The current context is in a host function + 'device' The current context is in a device function + [Default value: 'host'] + is_target: bool if object is pointed to by another variable [Default value: False] @@ -106,8 +111,8 @@ class base if variable is an object or an object member [Default value: None] >>> Variable('int', DottedName('matrix', 'n_rows')) matrix.n_rows """ - __slots__ = ('_name', '_alloc_shape', '_memory_handling', '_memory_location', '_is_const', - '_is_target', '_is_optional', '_allows_negative_indexes', + __slots__ = ('_name', '_alloc_shape', '_memory_handling', '_memory_location','_current_location', + '_is_const', '_is_target', '_is_optional', '_allows_negative_indexes', '_cls_base', '_is_argument', '_is_kwonly', '_is_temp','_dtype','_precision', '_rank','_shape','_order','_is_private') _attribute_nodes = () @@ -120,6 +125,7 @@ def __init__( rank=0, memory_handling='stack', memory_location='host', + current_location='host', is_const=False, is_target=False, is_optional=False, @@ -159,6 +165,11 @@ def __init__( raise ValueError("memory_location must be 'host', 'device' or 'managed'") self._memory_location = memory_location + if current_location not in ('host', 'device'): + raise ValueError("current_location must be 'host' or 'device'") + + self._current_location = current_location + if not isinstance(is_const, bool): raise TypeError('is_const must be a boolean.') self._is_const = is_const @@ -326,6 +337,12 @@ def memory_location(self, memory_location): raise ValueError("memory_location must be 'host', 'device' or 'managed'") self._memory_location = memory_location + @property + def current_location(self): + """ Gives the current context of the function + """ + return self._current_location + @property def on_host(self): """ Indicates if memory is only accessible by the CPU diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index 7bbf6f0d67..2097f8c08d 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -519,6 +519,8 @@ def copy_CudaArray_Data(self, expr): declare_dtype = self.find_in_dtype_registry(self._print(rhs.dtype), rhs.precision) dtype = self.find_in_ndarray_type_registry(self._print(rhs.dtype), rhs.precision) arg = rhs.arg if isinstance(rhs, (CudaArray, CupyArray)) else rhs + memory_location = 'Device' if rhs.memory_location == 'managed' else str(rhs.memory_location).capitalize() + memcpy_kind = "{}To{}".format(str(rhs.current_location).capitalize(), memory_location) if rhs.rank > 1: # flattening the args to use them in C initialization. arg = self._flatten_list(arg) @@ -526,12 +528,12 @@ def copy_CudaArray_Data(self, expr): self.add_import(c_imports['string']) if isinstance(arg, Variable): arg = self._print(arg) - cpy_data = "cudaMemcpy({0}.raw_data, {1}.{2}, {0}.buffer_size, cudaMemcpyHostToDevice);".format(lhs, arg, dtype) + cpy_data = "cudaMemcpy({0}.raw_data, {1}.{2}, {0}.buffer_size, {3});".format(lhs, arg, dtype, memcpy_kind) return '%s\n' % (cpy_data) else : arg = ', '.join(self._print(i) for i in arg) dummy_array = "%s %s[] = {%s};\n" % (declare_dtype, dummy_array_name, arg) - cpy_data = "cudaMemcpy({0}.raw_data, {1}, {0}.buffer_size, cudaMemcpyHostToDevice);".format(self._print(lhs), dummy_array_name, dtype) + cpy_data = "cudaMemcpy({0}.raw_data, {1}, {0}.buffer_size, {3});".format(self._print(lhs), dummy_array_name, dtype, memcpy_kind) return '%s%s\n' % (dummy_array, cpy_data) def _print_CudaDeviceSynchronize(self, expr): diff --git a/pyccel/parser/semantic.py b/pyccel/parser/semantic.py index 53c022993f..9e885f037f 100644 --- a/pyccel/parser/semantic.py +++ b/pyccel/parser/semantic.py @@ -494,6 +494,7 @@ def _infere_type(self, expr, **settings): d_var['datatype' ] = expr.dtype d_var['memory_handling'] = 'heap' if expr.rank > 0 else 'stack' d_var['memory_location'] = expr.memory_location + d_var['current_location'] = 'device' if 'device' in self.scope.decorators else 'host' d_var['shape' ] = expr.shape d_var['rank' ] = expr.rank d_var['order' ] = expr.order @@ -812,7 +813,7 @@ def _handle_function(self, expr, func, args, **settings): func = func.cls_name if func in (CudaThreadIdx, CudaBlockDim, CudaBlockIdx, CudaGridDim): if 'kernel' not in self.scope.decorators\ - or 'device' not in self.scope.decorators: + and 'device' not in self.scope.decorators: errors.report("Cuda internal variables should only be used in Kernel or Device functions", symbol = expr, severity = 'fatal') @@ -902,11 +903,11 @@ def _handle_kernel(self, expr, func, args, **settings): symbol = expr, severity='fatal') # TODO : type check the NUMBER OF BLOCKS 'numBlocks' and threads per block 'tpblock' - if not isinstance(expr.numBlocks, LiteralInteger): + if not isinstance(expr.numBlocks, (LiteralInteger, PyccelSymbol)): errors.report("Invalid Block number parameter for Kernel call", symbol = expr, severity='error') - if not isinstance(expr.tpblock, LiteralInteger): + if not isinstance(expr.tpblock, (LiteralInteger, PyccelSymbol)): errors.report("Invalid Thread per Block parameter for Kernel call", symbol = expr, severity='error') From 2823e74733d2b727832a7ab3e0a561608d641b11 Mon Sep 17 00:00:00 2001 From: Aymane Benaissa <47903494+Pinkyboi@users.noreply.github.com> Date: Fri, 11 Nov 2022 16:59:34 +0100 Subject: [PATCH 091/193] Raise error when allocating to host from device function --- pyccel/codegen/printing/ccudacode.py | 4 +++- pyccel/parser/semantic.py | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index 2097f8c08d..1ab67e589c 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -519,7 +519,9 @@ def copy_CudaArray_Data(self, expr): declare_dtype = self.find_in_dtype_registry(self._print(rhs.dtype), rhs.precision) dtype = self.find_in_ndarray_type_registry(self._print(rhs.dtype), rhs.precision) arg = rhs.arg if isinstance(rhs, (CudaArray, CupyArray)) else rhs - memory_location = 'Device' if rhs.memory_location == 'managed' else str(rhs.memory_location).capitalize() + if rhs.current_location == 'device' and rhs.memory_location == 'host': + errors.report("Cannot copy data from device to host", symbol=expr, severity='error') + memory_location = 'Host' if rhs.memory_location == 'host' else 'Device' memcpy_kind = "{}To{}".format(str(rhs.current_location).capitalize(), memory_location) if rhs.rank > 1: # flattening the args to use them in C initialization. diff --git a/pyccel/parser/semantic.py b/pyccel/parser/semantic.py index 9e885f037f..b6123f40e2 100644 --- a/pyccel/parser/semantic.py +++ b/pyccel/parser/semantic.py @@ -494,7 +494,11 @@ def _infere_type(self, expr, **settings): d_var['datatype' ] = expr.dtype d_var['memory_handling'] = 'heap' if expr.rank > 0 else 'stack' d_var['memory_location'] = expr.memory_location - d_var['current_location'] = 'device' if 'device' in self.scope.decorators else 'host' + if 'device' in self.scope.decorators or\ + 'kernel' in self.scope.decorators: + d_var['current_location'] = 'device' + else: + d_var['current_location'] = 'host' d_var['shape' ] = expr.shape d_var['rank' ] = expr.rank d_var['order' ] = expr.order From aa5e773c542e0ecc6f193ac775b43dfde1d35b16 Mon Sep 17 00:00:00 2001 From: Aymane Benaissa <47903494+Pinkyboi@users.noreply.github.com> Date: Wed, 16 Nov 2022 13:35:45 +0100 Subject: [PATCH 092/193] Change error message for creating host array form device --- pyccel/codegen/printing/ccudacode.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index 1ab67e589c..58885e88a7 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -520,9 +520,10 @@ def copy_CudaArray_Data(self, expr): dtype = self.find_in_ndarray_type_registry(self._print(rhs.dtype), rhs.precision) arg = rhs.arg if isinstance(rhs, (CudaArray, CupyArray)) else rhs if rhs.current_location == 'device' and rhs.memory_location == 'host': - errors.report("Cannot copy data from device to host", symbol=expr, severity='error') - memory_location = 'Host' if rhs.memory_location == 'host' else 'Device' - memcpy_kind = "{}To{}".format(str(rhs.current_location).capitalize(), memory_location) + errors.report("You can't create a host array from a device function", + symbol=expr, severity='error') + end_memory_location = 'Host' if rhs.memory_location == 'host' else 'Device' + memcpy_kind = "{}To{}".format(str(rhs.current_location).capitalize(), end_memory_location) if rhs.rank > 1: # flattening the args to use them in C initialization. arg = self._flatten_list(arg) From e3e13f1053e123c5288d81de2b2523ad13135a73 Mon Sep 17 00:00:00 2001 From: Aymane Benaissa <47903494+Pinkyboi@users.noreply.github.com> Date: Wed, 16 Nov 2022 13:39:32 +0100 Subject: [PATCH 093/193] Change memcpy_kind variable name for readability --- pyccel/codegen/printing/ccudacode.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index 58885e88a7..d9c83f6458 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -522,8 +522,9 @@ def copy_CudaArray_Data(self, expr): if rhs.current_location == 'device' and rhs.memory_location == 'host': errors.report("You can't create a host array from a device function", symbol=expr, severity='error') - end_memory_location = 'Host' if rhs.memory_location == 'host' else 'Device' - memcpy_kind = "{}To{}".format(str(rhs.current_location).capitalize(), end_memory_location) + memcpy_kind_src = str(rhs.current_location).capitalize() + memcpy_kind_dest = 'Host' if rhs.memory_location == 'host' else 'Device' + memcpy_kind = "{}To{}".format(memcpy_kind_src, memcpy_kind_dest) if rhs.rank > 1: # flattening the args to use them in C initialization. arg = self._flatten_list(arg) From 7a623010e252f447e0942e7d542ae33849f9e10e Mon Sep 17 00:00:00 2001 From: bauom <40796259+bauom@users.noreply.github.com> Date: Thu, 17 Nov 2022 07:21:15 +0100 Subject: [PATCH 094/193] Revert "Addition of valid cudaMemcpyKind to cudaMemcpy" --- pyccel/ast/cudaext.py | 9 ++------- pyccel/ast/cupyext.py | 12 +----------- pyccel/ast/variable.py | 21 ++------------------- pyccel/codegen/printing/ccudacode.py | 10 ++-------- pyccel/parser/semantic.py | 11 +++-------- 5 files changed, 10 insertions(+), 53 deletions(-) diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index 4aadaa525c..79f42bda74 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -74,11 +74,11 @@ class CudaArray(CudaNewArray): arg : list, tuple, PythonList """ - __slots__ = ('_arg','_dtype','_precision','_shape','_rank','_order','_memory_location', '_current_location') + __slots__ = ('_arg','_dtype','_precision','_shape','_rank','_order','_memory_location') _attribute_nodes = ('_arg',) name = 'array' - def __init__(self, arg, dtype=None, order='C', memory_location='managed', current_location='host'): + def __init__(self, arg, dtype=None, order='C', memory_location='managed'): if not isinstance(arg, (PythonTuple, PythonList, Variable)): raise TypeError('Unknown type of %s.' % type(arg)) @@ -125,7 +125,6 @@ def __init__(self, arg, dtype=None, order='C', memory_location='managed', curren self._order = order self._precision = prec self._memory_location = memory_location - self._current_location = current_location super().__init__() def __str__(self): @@ -138,10 +137,6 @@ def arg(self): def memory_location(self): return self._memory_location - @property - def current_location(self): - return self._current_location - class CudaDeviceSynchronize(PyccelInternalFunction): "Represents a call to Cuda.deviceSynchronize for code generation." # pass diff --git a/pyccel/ast/cupyext.py b/pyccel/ast/cupyext.py index 0b9191b041..08650db3eb 100644 --- a/pyccel/ast/cupyext.py +++ b/pyccel/ast/cupyext.py @@ -69,7 +69,7 @@ class CupyArray(CudaNewArray): _attribute_nodes = ('_arg',) name = 'array' - def __init__(self, arg, dtype=None, order='C', memory_location='managed', current_location='host'): + def __init__(self, arg, dtype=None, order='C'): if not isinstance(arg, (PythonTuple, PythonList, Variable)): raise TypeError('Unknown type of %s.' % type(arg)) @@ -113,8 +113,6 @@ def __init__(self, arg, dtype=None, order='C', memory_location='managed', curren self._dtype = dtype self._order = order self._precision = prec - self._memory_location = memory_location - self._current_location = current_location super().__init__() def __str__(self): @@ -124,14 +122,6 @@ def __str__(self): def arg(self): return self._arg - @property - def memory_location(self): - return self._memory_location - - @property - def current_location(self): - return self._current_location - #============================================================================== class CupyArange(CudaNewArray): """ diff --git a/pyccel/ast/variable.py b/pyccel/ast/variable.py index 7421cddf42..36a92ac858 100644 --- a/pyccel/ast/variable.py +++ b/pyccel/ast/variable.py @@ -61,11 +61,6 @@ class Variable(PyccelAstNode): 'managed' the variable can be accessed by CPU and GPU and is being managed by the Cuda API (memory transfer is being done implicitly) [Default value: 'host'] - current_location: str - 'host' The current context is in a host function - 'device' The current context is in a device function - [Default value: 'host'] - is_target: bool if object is pointed to by another variable [Default value: False] @@ -111,8 +106,8 @@ class base if variable is an object or an object member [Default value: None] >>> Variable('int', DottedName('matrix', 'n_rows')) matrix.n_rows """ - __slots__ = ('_name', '_alloc_shape', '_memory_handling', '_memory_location','_current_location', - '_is_const', '_is_target', '_is_optional', '_allows_negative_indexes', + __slots__ = ('_name', '_alloc_shape', '_memory_handling', '_memory_location', '_is_const', + '_is_target', '_is_optional', '_allows_negative_indexes', '_cls_base', '_is_argument', '_is_kwonly', '_is_temp','_dtype','_precision', '_rank','_shape','_order','_is_private') _attribute_nodes = () @@ -125,7 +120,6 @@ def __init__( rank=0, memory_handling='stack', memory_location='host', - current_location='host', is_const=False, is_target=False, is_optional=False, @@ -165,11 +159,6 @@ def __init__( raise ValueError("memory_location must be 'host', 'device' or 'managed'") self._memory_location = memory_location - if current_location not in ('host', 'device'): - raise ValueError("current_location must be 'host' or 'device'") - - self._current_location = current_location - if not isinstance(is_const, bool): raise TypeError('is_const must be a boolean.') self._is_const = is_const @@ -337,12 +326,6 @@ def memory_location(self, memory_location): raise ValueError("memory_location must be 'host', 'device' or 'managed'") self._memory_location = memory_location - @property - def current_location(self): - """ Gives the current context of the function - """ - return self._current_location - @property def on_host(self): """ Indicates if memory is only accessible by the CPU diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index d9c83f6458..7bbf6f0d67 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -519,12 +519,6 @@ def copy_CudaArray_Data(self, expr): declare_dtype = self.find_in_dtype_registry(self._print(rhs.dtype), rhs.precision) dtype = self.find_in_ndarray_type_registry(self._print(rhs.dtype), rhs.precision) arg = rhs.arg if isinstance(rhs, (CudaArray, CupyArray)) else rhs - if rhs.current_location == 'device' and rhs.memory_location == 'host': - errors.report("You can't create a host array from a device function", - symbol=expr, severity='error') - memcpy_kind_src = str(rhs.current_location).capitalize() - memcpy_kind_dest = 'Host' if rhs.memory_location == 'host' else 'Device' - memcpy_kind = "{}To{}".format(memcpy_kind_src, memcpy_kind_dest) if rhs.rank > 1: # flattening the args to use them in C initialization. arg = self._flatten_list(arg) @@ -532,12 +526,12 @@ def copy_CudaArray_Data(self, expr): self.add_import(c_imports['string']) if isinstance(arg, Variable): arg = self._print(arg) - cpy_data = "cudaMemcpy({0}.raw_data, {1}.{2}, {0}.buffer_size, {3});".format(lhs, arg, dtype, memcpy_kind) + cpy_data = "cudaMemcpy({0}.raw_data, {1}.{2}, {0}.buffer_size, cudaMemcpyHostToDevice);".format(lhs, arg, dtype) return '%s\n' % (cpy_data) else : arg = ', '.join(self._print(i) for i in arg) dummy_array = "%s %s[] = {%s};\n" % (declare_dtype, dummy_array_name, arg) - cpy_data = "cudaMemcpy({0}.raw_data, {1}, {0}.buffer_size, {3});".format(self._print(lhs), dummy_array_name, dtype, memcpy_kind) + cpy_data = "cudaMemcpy({0}.raw_data, {1}, {0}.buffer_size, cudaMemcpyHostToDevice);".format(self._print(lhs), dummy_array_name, dtype) return '%s%s\n' % (dummy_array, cpy_data) def _print_CudaDeviceSynchronize(self, expr): diff --git a/pyccel/parser/semantic.py b/pyccel/parser/semantic.py index b6123f40e2..53c022993f 100644 --- a/pyccel/parser/semantic.py +++ b/pyccel/parser/semantic.py @@ -494,11 +494,6 @@ def _infere_type(self, expr, **settings): d_var['datatype' ] = expr.dtype d_var['memory_handling'] = 'heap' if expr.rank > 0 else 'stack' d_var['memory_location'] = expr.memory_location - if 'device' in self.scope.decorators or\ - 'kernel' in self.scope.decorators: - d_var['current_location'] = 'device' - else: - d_var['current_location'] = 'host' d_var['shape' ] = expr.shape d_var['rank' ] = expr.rank d_var['order' ] = expr.order @@ -817,7 +812,7 @@ def _handle_function(self, expr, func, args, **settings): func = func.cls_name if func in (CudaThreadIdx, CudaBlockDim, CudaBlockIdx, CudaGridDim): if 'kernel' not in self.scope.decorators\ - and 'device' not in self.scope.decorators: + or 'device' not in self.scope.decorators: errors.report("Cuda internal variables should only be used in Kernel or Device functions", symbol = expr, severity = 'fatal') @@ -907,11 +902,11 @@ def _handle_kernel(self, expr, func, args, **settings): symbol = expr, severity='fatal') # TODO : type check the NUMBER OF BLOCKS 'numBlocks' and threads per block 'tpblock' - if not isinstance(expr.numBlocks, (LiteralInteger, PyccelSymbol)): + if not isinstance(expr.numBlocks, LiteralInteger): errors.report("Invalid Block number parameter for Kernel call", symbol = expr, severity='error') - if not isinstance(expr.tpblock, (LiteralInteger, PyccelSymbol)): + if not isinstance(expr.tpblock, LiteralInteger): errors.report("Invalid Thread per Block parameter for Kernel call", symbol = expr, severity='error') From 28f3ecf8916671459508cdbd9b75fcca28a692f4 Mon Sep 17 00:00:00 2001 From: Fatima-zahra RAMDANI Date: Sun, 20 Nov 2022 22:58:38 +0100 Subject: [PATCH 095/193] support copy only from cuda arrays --- pyccel/ast/cudaext.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index af14f8a0c4..ed3aa7c826 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -181,7 +181,7 @@ class CudaCopy(CudaNewArray): Parameters ---------- - arg : list, tuple, PythonList + arg : Variable memory_location : str 'host' the newly created array is allocated on host. @@ -194,8 +194,8 @@ class CudaCopy(CudaNewArray): __slots__ = ('_arg','_dtype','_precision','_shape','_rank','_order','_memory_location', '_is_async') def __init__(self, arg, memory_location, is_async=False): - - if not isinstance(arg, (PythonTuple, PythonList, Variable)): + + if not isinstance(arg, Variable): raise TypeError('unknown type of %s.' % type(arg)) # Verify the memory_location of src From 55ac74160903acf9799f1a795cbab1eb035dcf0c Mon Sep 17 00:00:00 2001 From: Fatima-zahra RAMDANI Date: Mon, 21 Nov 2022 21:28:46 +0100 Subject: [PATCH 096/193] access attributes by getters --- pyccel/ast/cudaext.py | 16 +++++++++------- pyccel/codegen/printing/ccudacode.py | 10 +++++----- tests/internal/scripts/ccuda/cuda_copy.py | 4 ++-- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index ed3aa7c826..ce77936ca0 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -199,7 +199,7 @@ def __init__(self, arg, memory_location, is_async=False): raise TypeError('unknown type of %s.' % type(arg)) # Verify the memory_location of src - if arg._memory_location not in ('device', 'host'): + if arg.memory_location not in ('device', 'host'): raise ValueError("The direction of the copy should be from 'host' or 'device'") # Verify the memory_location of dst @@ -211,11 +211,11 @@ def __init__(self, arg, memory_location, is_async=False): raise TypeError('is_async must be boolean') self._arg = arg - self._shape = arg._shape - self._rank = arg._rank - self._dtype = arg._dtype - self._order = arg._order - self._precision = arg._precision + self._shape = arg.shape + self._rank = arg.rank + self._dtype = arg.dtype + self._order = arg.order + self._precision = arg.precision self._memory_location = memory_location self._is_async = is_async super().__init__() @@ -228,7 +228,9 @@ def arg(self): def memory_location(self): return self._memory_location - + @property + def is_async(self): + return self._is_async class CudaThreadIdx(CudaInternalVar) : pass class CudaBlockDim(CudaInternalVar) : pass diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index 3d1eba3df5..654cdb82f2 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -546,13 +546,13 @@ def _print_CudaInternalVar(self, expr): return '{}.{}'.format(var_name, dim_c) def cudaCopy(self, lhs, rhs): - from_location = str(rhs._arg._memory_location).capitalize() - to_location = str(rhs._memory_location).capitalize() + from_location = str(rhs.arg.memory_location).capitalize() + to_location = str(rhs.memory_location).capitalize() transfer_type = 'cudaMemcpy{0}To{1}'.format(from_location, to_location) - if isinstance(rhs._is_async, LiteralTrue): - cpy_data = "cudaMemcpyAsync({0}.raw_data, {1}.raw_data, {0}.buffer_size, {2}, 0);".format(lhs, rhs._arg, transfer_type) + if isinstance(rhs.is_async, LiteralTrue): + cpy_data = "cudaMemcpyAsync({0}.raw_data, {1}.raw_data, {0}.buffer_size, {2}, 0);".format(lhs, rhs.arg, transfer_type) else: - cpy_data = "cudaMemcpy({0}.raw_data, {1}.raw_data, {0}.buffer_size, {2});".format(lhs, rhs._arg, transfer_type) + cpy_data = "cudaMemcpy({0}.raw_data, {1}.raw_data, {0}.buffer_size, {2});".format(lhs, rhs.arg, transfer_type) return '%s\n' % (cpy_data) def ccudacode(expr, filename, assign_to=None, **settings): diff --git a/tests/internal/scripts/ccuda/cuda_copy.py b/tests/internal/scripts/ccuda/cuda_copy.py index 462f36684f..74cc2f313c 100644 --- a/tests/internal/scripts/ccuda/cuda_copy.py +++ b/tests/internal/scripts/ccuda/cuda_copy.py @@ -2,5 +2,5 @@ from pyccel import cuda if __name__ == '__main__': - arr = cuda.array([1,2,3,4], memory_location='device') - cpy = cuda.copy(arr, 'host', is_async=True) \ No newline at end of file + arr = cuda.array([1,2,3,4], memory_location='host') + cpy = cuda.copy(arr, 'device', is_async=True) \ No newline at end of file From 3b6c82f950a072652c41b27ce080802b2b5d6ac9 Mon Sep 17 00:00:00 2001 From: Fatima-zahra RAMDANI Date: Mon, 28 Nov 2022 16:56:38 +0100 Subject: [PATCH 097/193] add 'managed' as valid memory location arg for cuda.copy() --- pyccel/ast/cudaext.py | 4 ++-- pyccel/codegen/printing/ccudacode.py | 8 ++++++-- tests/internal/scripts/ccuda/cuda_copy.py | 4 ++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index ce77936ca0..3c6541c2d6 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -199,11 +199,11 @@ def __init__(self, arg, memory_location, is_async=False): raise TypeError('unknown type of %s.' % type(arg)) # Verify the memory_location of src - if arg.memory_location not in ('device', 'host'): + if arg.memory_location not in ('device', 'host', 'managed'): raise ValueError("The direction of the copy should be from 'host' or 'device'") # Verify the memory_location of dst - if memory_location not in ('device', 'host'): + if memory_location not in ('device', 'host', 'managed'): raise ValueError("The direction of the copy should be to 'host' or 'device'") # verify the type of is_async diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index 654cdb82f2..e4b78eae87 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -546,8 +546,12 @@ def _print_CudaInternalVar(self, expr): return '{}.{}'.format(var_name, dim_c) def cudaCopy(self, lhs, rhs): - from_location = str(rhs.arg.memory_location).capitalize() - to_location = str(rhs.memory_location).capitalize() + from_location = 'Host' + to_location = 'Host' + if rhs.arg.memory_location in ('device', 'managed'): + from_location = 'Device' + if rhs.memory_location in ('device', 'managed'): + to_location = 'Device' transfer_type = 'cudaMemcpy{0}To{1}'.format(from_location, to_location) if isinstance(rhs.is_async, LiteralTrue): cpy_data = "cudaMemcpyAsync({0}.raw_data, {1}.raw_data, {0}.buffer_size, {2}, 0);".format(lhs, rhs.arg, transfer_type) diff --git a/tests/internal/scripts/ccuda/cuda_copy.py b/tests/internal/scripts/ccuda/cuda_copy.py index 74cc2f313c..a3a3e93e4d 100644 --- a/tests/internal/scripts/ccuda/cuda_copy.py +++ b/tests/internal/scripts/ccuda/cuda_copy.py @@ -2,5 +2,5 @@ from pyccel import cuda if __name__ == '__main__': - arr = cuda.array([1,2,3,4], memory_location='host') - cpy = cuda.copy(arr, 'device', is_async=True) \ No newline at end of file + arr = cuda.array([1,2,3,4], memory_location='device') + out = cuda.copy(arr, 'host', is_async=True) \ No newline at end of file From d1a8d5bb378e5050bbb8a9ec9c03ade4aa19e7ea Mon Sep 17 00:00:00 2001 From: Aymane Benaissa <47903494+Pinkyboi@users.noreply.github.com> Date: Wed, 21 Dec 2022 19:43:13 +0100 Subject: [PATCH 098/193] Change call to cudaDeviceSynchronize to cuda.synchronize() (#1274) --- pyccel/ast/cudaext.py | 8 +++----- pyccel/codegen/printing/ccudacode.py | 2 +- pyccel/naming/ccudanameclashchecker.py | 2 +- pyccel/parser/semantic.py | 2 +- samples/test/test.py | 2 +- tests/internal/scripts/ccuda/cuda_array_device.py | 4 ++-- tests/internal/scripts/ccuda/cuda_array_managed.py | 4 ++-- tests/internal/scripts/ccuda/cuda_grid.py | 4 ++-- tests/internal/scripts/ccuda/cupy_array.py | 4 ++-- tests/internal/scripts/ccuda/kernel_launch.py | 4 ++-- 10 files changed, 17 insertions(+), 19 deletions(-) diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index 3c6541c2d6..d1ed2728bc 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -37,7 +37,7 @@ 'CudaMemCopy', 'CudaNewArray', 'CudaArray', - 'CudaDeviceSynchronize', + 'CudaSynchronize', 'CudaInternalVar', 'CudaThreadIdx', 'CudaBlockDim', @@ -137,7 +137,7 @@ def arg(self): def memory_location(self): return self._memory_location -class CudaDeviceSynchronize(PyccelInternalFunction): +class CudaSynchronize(PyccelInternalFunction): "Represents a call to Cuda.deviceSynchronize for code generation." # pass _attribute_nodes = () @@ -251,16 +251,14 @@ def __new__(cls, dim=0): cuda_funcs = { - # 'deviceSynchronize' : CudaDeviceSynchronize, 'array' : PyccelFunctionDef('array' , CudaArray), 'copy' : PyccelFunctionDef('copy' , CudaCopy), - 'deviceSynchronize' : PyccelFunctionDef('deviceSynchronize' , CudaDeviceSynchronize), + 'synchronize' : PyccelFunctionDef('synchronize' , CudaSynchronize), 'threadIdx' : PyccelFunctionDef('threadIdx' , CudaThreadIdx), 'blockDim' : PyccelFunctionDef('blockDim' , CudaBlockDim), 'blockIdx' : PyccelFunctionDef('blockIdx' , CudaBlockIdx), 'gridDim' : PyccelFunctionDef('gridDim' , CudaGridDim), 'grid' : PyccelFunctionDef('grid' , CudaGrid) - } cuda_Internal_Var = { diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index e4b78eae87..2d6ac5ad43 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -536,7 +536,7 @@ def copy_CudaArray_Data(self, expr): cpy_data = "cudaMemcpy({0}.raw_data, {1}, {0}.buffer_size, cudaMemcpyHostToDevice);".format(self._print(lhs), dummy_array_name, dtype) return '%s%s\n' % (dummy_array, cpy_data) - def _print_CudaDeviceSynchronize(self, expr): + def _print_CudaSynchronize(self, expr): return 'cudaDeviceSynchronize()' def _print_CudaInternalVar(self, expr): diff --git a/pyccel/naming/ccudanameclashchecker.py b/pyccel/naming/ccudanameclashchecker.py index 570a72060a..e5221139e6 100644 --- a/pyccel/naming/ccudanameclashchecker.py +++ b/pyccel/naming/ccudanameclashchecker.py @@ -41,7 +41,7 @@ class CCudaNameClashChecker(metaclass = Singleton): 'cuda_array_fill_int32', 'cuda_array_fill_int8', 'cuda_array_arange_double', 'cuda_array_arange_int64', 'cuda_array_arange_int32', 'cuda_array_arange_int8', - 'cudaMallocManaged', 'cudaDeviceSynchronize']) + 'cudaMallocManaged', 'cudaSynchronize']) def has_clash(self, name, symbols): """ Indicate whether the proposed name causes any clashes diff --git a/pyccel/parser/semantic.py b/pyccel/parser/semantic.py index 53c022993f..61cf7fcb19 100644 --- a/pyccel/parser/semantic.py +++ b/pyccel/parser/semantic.py @@ -812,7 +812,7 @@ def _handle_function(self, expr, func, args, **settings): func = func.cls_name if func in (CudaThreadIdx, CudaBlockDim, CudaBlockIdx, CudaGridDim): if 'kernel' not in self.scope.decorators\ - or 'device' not in self.scope.decorators: + and 'device' not in self.scope.decorators: errors.report("Cuda internal variables should only be used in Kernel or Device functions", symbol = expr, severity = 'fatal') diff --git a/samples/test/test.py b/samples/test/test.py index b6e49d3320..027e3945e9 100644 --- a/samples/test/test.py +++ b/samples/test/test.py @@ -24,5 +24,5 @@ def func(a): # b = narray([1, 2, 3], dtype='int', order='C') a = cuda.array([1, 2, 3], dtype='int', order='C') func[1, 3](a) - cuda.deviceSynchronize() + cuda.synchronize() print(a) \ No newline at end of file diff --git a/tests/internal/scripts/ccuda/cuda_array_device.py b/tests/internal/scripts/ccuda/cuda_array_device.py index fa2e0f0c8c..ff6bd392ae 100644 --- a/tests/internal/scripts/ccuda/cuda_array_device.py +++ b/tests/internal/scripts/ccuda/cuda_array_device.py @@ -11,6 +11,6 @@ def square(a): threads_per_block = 5 n_blocks = 1 arr = cuda.array([0,1,2,3,4], memory_location = 'device') - cuda.deviceSynchronize() + cuda.synchronize() square[n_blocks, threads_per_block](arr) - cuda.deviceSynchronize() + cuda.synchronize() diff --git a/tests/internal/scripts/ccuda/cuda_array_managed.py b/tests/internal/scripts/ccuda/cuda_array_managed.py index 5d55151e65..96898ff506 100644 --- a/tests/internal/scripts/ccuda/cuda_array_managed.py +++ b/tests/internal/scripts/ccuda/cuda_array_managed.py @@ -11,7 +11,7 @@ def square(a): threads_per_block = 5 n_blocks = 1 a = cuda.array([0,1,2,3,4], memory_location = 'managed') - cuda.deviceSynchronize() + cuda.synchronize() square[n_blocks, threads_per_block](a) - cuda.deviceSynchronize() + cuda.synchronize() print(a) \ No newline at end of file diff --git a/tests/internal/scripts/ccuda/cuda_grid.py b/tests/internal/scripts/ccuda/cuda_grid.py index 92b8c74a83..835f0902bb 100644 --- a/tests/internal/scripts/ccuda/cuda_grid.py +++ b/tests/internal/scripts/ccuda/cuda_grid.py @@ -23,10 +23,10 @@ def func_3d(a): threads_per_block = 5 n_blocks = 1 arr = cuda.array([0, 1, 2, 3, 4]) - cuda.deviceSynchronize() + cuda.synchronize() func_1d[n_blocks, threads_per_block](arr) # Since we dont support multi-dim n_block / threads_per_block # func_2d and func_3d won't compile # func_2d[n_blocks, threads_per_block](arr) # func_3d[n_blocks, threads_per_block](arr) - cuda.deviceSynchronize() \ No newline at end of file + cuda.synchronize() \ No newline at end of file diff --git a/tests/internal/scripts/ccuda/cupy_array.py b/tests/internal/scripts/ccuda/cupy_array.py index bf739ae7d5..108cab1ebb 100644 --- a/tests/internal/scripts/ccuda/cupy_array.py +++ b/tests/internal/scripts/ccuda/cupy_array.py @@ -12,6 +12,6 @@ def func(a): threads_per_block = 5 n_blocks = 1 arr = cp.array([0, 1, 2, 3, 4]) - cuda.deviceSynchronize() + cuda.synchronize() func[n_blocks, threads_per_block](arr) - cuda.deviceSynchronize() \ No newline at end of file + cuda.synchronize() \ No newline at end of file diff --git a/tests/internal/scripts/ccuda/kernel_launch.py b/tests/internal/scripts/ccuda/kernel_launch.py index 16cf40fbdc..4335a6067c 100644 --- a/tests/internal/scripts/ccuda/kernel_launch.py +++ b/tests/internal/scripts/ccuda/kernel_launch.py @@ -11,6 +11,6 @@ def func(a): threads_per_block = 5 n_blocks = 1 arr = cuda.array([0, 1, 2, 3, 4]) - cuda.deviceSynchronize() + cuda.synchronize() func[n_blocks, threads_per_block](arr) - cuda.deviceSynchronize() \ No newline at end of file + cuda.synchronize() \ No newline at end of file From c2dd0b7985d9a57d9e7b5f7536752e0cd26d5759 Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 4 Jan 2023 13:50:42 +0100 Subject: [PATCH 099/193] Temporarily deactivate most recent numpy --- pyproject.toml | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9f6ed0ea68..6a90904947 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [build-system] requires = [ "setuptools >= 37, < 61", - "numpy >= 1.16", + "numpy >= 1.16, < 1.24", "sympy>=1.2", "termcolor >= 1.0.0", "textx>=2.2", diff --git a/setup.cfg b/setup.cfg index 1fbf13e34c..c3ff2e5572 100644 --- a/setup.cfg +++ b/setup.cfg @@ -13,7 +13,7 @@ long_description_content_type = text/markdown [options] packages = find: install_requires = - numpy >= 1.16 + numpy >= 1.16, < 1.24 sympy >= 1.2 termcolor >= 1.0.0 textx >= 2.2 From 4335a52904f5639d2288826590ebb5b2d1c68958 Mon Sep 17 00:00:00 2001 From: EmilyBoune Date: Fri, 6 Jan 2023 17:33:29 +0100 Subject: [PATCH 100/193] Put tests back on branch --- .github/workflows/Cuda_pytest.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/Cuda_pytest.yml b/.github/workflows/Cuda_pytest.yml index 2ee1cc56d2..a6cae06f53 100644 --- a/.github/workflows/Cuda_pytest.yml +++ b/.github/workflows/Cuda_pytest.yml @@ -2,7 +2,7 @@ name: Pyccel CUDA tests on: pull_request: - branches: [ cuda_main ] + branches: [ cuda_main, cuda_main_temp ] jobs: Linux: @@ -11,6 +11,13 @@ jobs: container: nvidia/cuda:11.7.1-devel-ubuntu20.04 steps: - uses: actions/checkout@v2 + - name: Prepare docker + run: | + apt update && apt install sudo + TZ=Europe/France + ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata + shell: bash - name: CUDA Version run: nvcc --version # cuda install check - name: Install dependencies From aa4786c9c597cd7ce8452ecc969438131993a099 Mon Sep 17 00:00:00 2001 From: EmilyBoune Date: Fri, 6 Jan 2023 17:35:24 +0100 Subject: [PATCH 101/193] Fix CI tools --- ci_tools/check_new_coverage.py | 2 +- ci_tools/coverage_analysis_tools.py | 6 +++--- ci_tools/git_evaluation_tools.py | 17 +++-------------- 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/ci_tools/check_new_coverage.py b/ci_tools/check_new_coverage.py index 4c4dd343e7..53573fe5e3 100644 --- a/ci_tools/check_new_coverage.py +++ b/ci_tools/check_new_coverage.py @@ -25,7 +25,7 @@ with open(args.gitEvent, encoding="utf-8") as pr_data_file: pr_data = json.load(pr_data_file) -cov.print_markdown_summary(new_untested, file_contents, pr_data["pull_request"]["base"]["sha"], args.output) +cov.print_markdown_summary(new_untested, file_contents, pr_data["pull_request"]["head"]["sha"], args.output) cov.show_results(new_untested) diff --git a/ci_tools/coverage_analysis_tools.py b/ci_tools/coverage_analysis_tools.py index 236987300a..346e78fe27 100644 --- a/ci_tools/coverage_analysis_tools.py +++ b/ci_tools/coverage_analysis_tools.py @@ -104,8 +104,8 @@ def allow_untested_error_calls(untested): for f,line_nums in untested.items(): with open(f, encoding="utf-8") as filename: f_lines = filename.readlines() - untested_lines = [f_lines[i-1].strip() for i in line_nums] - lines = [l for l in untested_lines if not (l.startswith('raise ') or l.startswith('errors.report(') or l.startswith('return errors.report('))] + untested_lines = [(i, f_lines[i-1].strip()) for i in line_nums] + lines = [i for i,l in untested_lines if not (l.startswith('raise ') or l.startswith('errors.report(') or l.startswith('return errors.report('))] if len(lines): reduced_untested[f] = lines @@ -151,7 +151,7 @@ def print_markdown_summary(untested, content_lines, commit, output): end_line = line_indices[j]-1 else: end_line = line_indices[j] - md_string += "https://github.com/pyccel/pyccel/blob/"+commit+"/"+f+f"#L{start_line}-L{end_line}" + md_string += "https://github.com/pyccel/pyccel/blob/"+commit+"/"+f+f"#L{start_line}-L{end_line}\n" with open(output, "a", encoding="utf-8") as out_file: print(md_string, file=out_file) diff --git a/ci_tools/git_evaluation_tools.py b/ci_tools/git_evaluation_tools.py index 8f594f00af..4290ab4e9d 100644 --- a/ci_tools/git_evaluation_tools.py +++ b/ci_tools/git_evaluation_tools.py @@ -43,20 +43,7 @@ def get_diff_as_json(filename): changes[current_file_name]['deletion'] = current_file_deletions current_file_additions = [] current_file_deletions = [] - while not lines[i].startswith('---'): - i+=1 - current_file_name_del = lines[i][3:].strip() - i+=1 - current_file_name_add = lines[i][3:].strip() - if current_file_name_del == '/dev/null': - current_file_name = current_file_name_add[2:] - assert current_file_name != '/dev/null' - elif current_file_name_add == '/dev/null': - current_file_name = current_file_name_del[2:] - assert current_file_name != '/dev/null' - else: - current_file_name = current_file_name_del[2:] - assert current_file_name == current_file_name_add[2:] + current_file_name = l.split(' ')[3][2:] i+=1 elif l.startswith('@@'): line_info = l.split('@@')[1].split() @@ -81,6 +68,8 @@ def get_diff_as_json(filename): j+=1 i+=1 assert n_delete == j + while i Date: Fri, 6 Jan 2023 11:53:42 +0100 Subject: [PATCH 102/193] Add coverage to cuda tests --- .../actions/coverage_collection/action.yml | 1 - .github/workflows/Cuda_pytest.yml | 104 ------------------ .github/workflows/Github_pytest.yml | 72 ++++++++++-- 3 files changed, 63 insertions(+), 114 deletions(-) delete mode 100644 .github/workflows/Cuda_pytest.yml diff --git a/.github/actions/coverage_collection/action.yml b/.github/actions/coverage_collection/action.yml index 03d1b2cdfd..e32b82b317 100644 --- a/.github/actions/coverage_collection/action.yml +++ b/.github/actions/coverage_collection/action.yml @@ -6,7 +6,6 @@ runs: - name: Coverage collection run: | coverage combine - coverage xml rm ${SITE_DIR}/pyccel_cov.pth shell: bash diff --git a/.github/workflows/Cuda_pytest.yml b/.github/workflows/Cuda_pytest.yml deleted file mode 100644 index a6cae06f53..0000000000 --- a/.github/workflows/Cuda_pytest.yml +++ /dev/null @@ -1,104 +0,0 @@ -name: Pyccel CUDA tests - -on: - pull_request: - branches: [ cuda_main, cuda_main_temp ] - -jobs: - Linux: - - runs-on: ubuntu-latest - container: nvidia/cuda:11.7.1-devel-ubuntu20.04 - steps: - - uses: actions/checkout@v2 - - name: Prepare docker - run: | - apt update && apt install sudo - TZ=Europe/France - ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone - DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata - shell: bash - - name: CUDA Version - run: nvcc --version # cuda install check - - name: Install dependencies - uses: ./.github/actions/linux_install - - name: Install python dependencies - uses: ./.github/actions/pip_installation - - name: Fortran/C tests with pytest - uses: ./.github/actions/pytest_run - - name: Ccuda tests with pytest - uses: ./.github/actions/pytest_run_cuda - - name: Python tests with pytest - uses: ./.github/actions/pytest_run_python - - name: Parallel tests with pytest - uses: ./.github/actions/pytest_parallel - - # Windows: - - # runs-on: windows-latest - - # steps: - # - uses: actions/checkout@v2 - # - name: Setup Python 3.9 - # uses: actions/setup-python@v3 - # with: - # # The second most recent version is used as - # # setup-python installs the most recent patch - # # which leads to linking problems as there are - # # 2 versions of python3X.a and the wrong one - # # is chosen - # python-version: 3.9 - # # Uncomment to examine DLL requirements with 'objdump -x FILE' - # #- name: Install mingw tools - # # uses: msys2/setup-msys2@v2 - # - name: Install dependencies - # uses: ./.github/actions/windows_install - # - name: Install python dependencies - # uses: ./.github/actions/pip_installation - # - name: Fortran/C tests with pytest - # uses: ./.github/actions/pytest_run - # - name: Python tests with pytest - # uses: ./.github/actions/pytest_run_python - # - name: Parallel tests with pytest - # uses: ./.github/actions/pytest_parallel - - # MacOSX: - - # runs-on: macos-latest - - # steps: - # - uses: actions/checkout@v2 - # - name: Set up Python 3.10 - # uses: actions/setup-python@v3 - # with: - # python-version: '3.10' - # - name: Install dependencies - # uses: ./.github/actions/macos_install - # - name: Install python dependencies - # uses: ./.github/actions/pip_installation - # - name: Fortran/C tests with pytest - # uses: ./.github/actions/pytest_run - # - name: Python tests with pytest - # uses: ./.github/actions/pytest_run_python - # - name: Parallel tests with pytest - # uses: ./.github/actions/pytest_parallel - - # Linter: - - # runs-on: ubuntu-latest - - # steps: - # - uses: actions/checkout@v2 - # - name: Set up Python 3.7 - # uses: actions/setup-python@v3 - # with: - # python-version: 3.7 - # - name: Install python dependencies - # run: | - # python -m pip install --upgrade pip - # python -m pip install pylint - # shell: bash - # - name: Pylint - # run: | - # python -m pylint --rcfile=.pylintrc pyccel/parser/semantic.py - # shell: bash diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index 4e71a5391a..f5c79403b8 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -2,7 +2,7 @@ name: Pyccel tests on: pull_request: - branches: [ cuda_main_temp ] + branches: [ master, cuda_main_temp ] jobs: Linux: @@ -32,17 +32,11 @@ jobs: - name: Collect coverage information continue-on-error: True uses: ./.github/actions/coverage_collection - - name: Run codacy-coverage-reporter - uses: codacy/codacy-coverage-reporter-action@master - continue-on-error: True - with: - project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} - coverage-reports: cobertura.xml - name: Save code coverage report uses: actions/upload-artifact@v3 with: name: coverage-artifact - path: cobertura.xml + path: .coverage retention-days: 1 Windows: @@ -95,6 +89,33 @@ jobs: - name: Parallel tests with pytest uses: ./.github/actions/pytest_parallel + Cuda: + + runs-on: ubuntu-latest + container: nvidia/cuda:11.7.1-devel-ubuntu20.04 + if: github.event.pull_request.base.ref == 'cuda_main_temp' + steps: + - uses: actions/checkout@v2 + - name: CUDA Version + run: nvcc --version # cuda install check + - name: Install dependencies + uses: ./.github/actions/linux_install + - name: Install python dependencies + uses: ./.github/actions/pip_installation + - name: Coverage install + uses: ./.github/actions/coverage_install + - name: Ccuda tests with pytest + uses: ./.github/actions/pytest_run_cuda + - name: Collect coverage information + continue-on-error: True + uses: ./.github/actions/coverage_collection + - name: Save code coverage report + uses: actions/upload-artifact@v3 + with: + name: cuda-coverage-artifact + path: .coverage + retention-days: 1 + Linter: runs-on: ubuntu-latest @@ -115,10 +136,43 @@ jobs: python -m pylint --rcfile=.pylintrc pyccel/parser/semantic.py shell: bash + CoverageCollection: + + runs-on: ubuntu-latest + needs: [Linux, Cuda] + + steps: + - name: Collect coverage information + uses: actions/download-artifact@v3 + with: + name: coverage-artifact + - name: Rename coverage file + run: mv .coverage .coverage-linux + - name: Collect coverage information + uses: actions/download-artifact@v3 + if: + with: + name: cuda-coverage-artifact + - name: Rename coverage file + if: ${{ success() }} + run: mv .coverage .coverage-cuda + - name: Generate coverage report + run: | + ls -a + coverage combine + ls -a + coverage xml + - name: Run codacy-coverage-reporter + uses: codacy/codacy-coverage-reporter-action@master + continue-on-error: True + with: + project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} + coverage-reports: cobertura.xml + CoverageChecker: runs-on: ubuntu-latest - needs: Linux + needs: [CoverageCollection] steps: - uses: actions/checkout@v3 From 61dc630bcffcf2c15ad0a3fc3f289a174dcd942b Mon Sep 17 00:00:00 2001 From: EmilyBoune Date: Fri, 6 Jan 2023 12:15:44 +0100 Subject: [PATCH 103/193] Trigger --- .github/workflows/Github_pytest.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index f5c79403b8..9244a6b9f0 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -140,6 +140,7 @@ jobs: runs-on: ubuntu-latest needs: [Linux, Cuda] + if: ${{ always() }} steps: - name: Collect coverage information From 7ce2df6f087f4853889d5803ab0b977838671695 Mon Sep 17 00:00:00 2001 From: EmilyBoune Date: Fri, 6 Jan 2023 13:10:43 +0100 Subject: [PATCH 104/193] Add missing condition --- .github/workflows/Github_pytest.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index 9244a6b9f0..6349f69121 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -158,6 +158,7 @@ jobs: if: ${{ success() }} run: mv .coverage .coverage-cuda - name: Generate coverage report + if: ${{ always() }} run: | ls -a coverage combine From 789389369453625590916019b15964cda99698a9 Mon Sep 17 00:00:00 2001 From: EmilyBoune Date: Fri, 6 Jan 2023 13:35:14 +0100 Subject: [PATCH 105/193] Add missing setup --- .github/workflows/Github_pytest.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index 6349f69121..dab14e2933 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -143,6 +143,13 @@ jobs: if: ${{ always() }} steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.7 + uses: actions/setup-python@v4 + with: + python-version: 3.7 + - name: Coverage install + run: python -m pip install coverage - name: Collect coverage information uses: actions/download-artifact@v3 with: From 47382ce378dc0294a1020a4de5e9a1280810d071 Mon Sep 17 00:00:00 2001 From: EmilyBoune Date: Fri, 6 Jan 2023 14:02:21 +0100 Subject: [PATCH 106/193] Correct name --- .github/workflows/Github_pytest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index dab14e2933..67783de757 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -155,7 +155,7 @@ jobs: with: name: coverage-artifact - name: Rename coverage file - run: mv .coverage .coverage-linux + run: mv .coverage .coverage.linux - name: Collect coverage information uses: actions/download-artifact@v3 if: @@ -163,7 +163,7 @@ jobs: name: cuda-coverage-artifact - name: Rename coverage file if: ${{ success() }} - run: mv .coverage .coverage-cuda + run: mv .coverage .coverage.cuda - name: Generate coverage report if: ${{ always() }} run: | From 665baff4921615f81a93f0337e36501b39bef815 Mon Sep 17 00:00:00 2001 From: EmilyBoune Date: Fri, 6 Jan 2023 14:24:47 +0100 Subject: [PATCH 107/193] Install source --- .github/workflows/Github_pytest.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index 67783de757..b7e4a69cf7 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -148,6 +148,10 @@ jobs: uses: actions/setup-python@v4 with: python-version: 3.7 + - name: Install dependencies + uses: ./.github/actions/linux_install + - name: Install python dependencies + uses: ./.github/actions/pip_installation - name: Coverage install run: python -m pip install coverage - name: Collect coverage information @@ -167,9 +171,7 @@ jobs: - name: Generate coverage report if: ${{ always() }} run: | - ls -a coverage combine - ls -a coverage xml - name: Run codacy-coverage-reporter uses: codacy/codacy-coverage-reporter-action@master From ef8f762dccc19a9c65de2c28ce4cc0982e70dc93 Mon Sep 17 00:00:00 2001 From: EmilyBoune Date: Fri, 6 Jan 2023 15:02:05 +0100 Subject: [PATCH 108/193] Add sensible condition --- .github/workflows/Github_pytest.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index b7e4a69cf7..de3331e89c 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -162,14 +162,13 @@ jobs: run: mv .coverage .coverage.linux - name: Collect coverage information uses: actions/download-artifact@v3 - if: + if: needs.Cuda.result == 'success' with: name: cuda-coverage-artifact - name: Rename coverage file - if: ${{ success() }} + if: needs.Cuda.result == 'success' run: mv .coverage .coverage.cuda - name: Generate coverage report - if: ${{ always() }} run: | coverage combine coverage xml From c7f354f146b9b2d922fc6707e3638ac601af6bee Mon Sep 17 00:00:00 2001 From: EmilyBoune Date: Fri, 6 Jan 2023 15:17:36 +0100 Subject: [PATCH 109/193] Install sudo --- .github/workflows/Github_pytest.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index de3331e89c..d66950cdda 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -96,6 +96,9 @@ jobs: if: github.event.pull_request.base.ref == 'cuda_main_temp' steps: - uses: actions/checkout@v2 + - name: Install sudo + run: apt update && apt install sudo + shell: bash - name: CUDA Version run: nvcc --version # cuda install check - name: Install dependencies From 34858796b82952c79cddc831b267375beff44394 Mon Sep 17 00:00:00 2001 From: EmilyBoune Date: Fri, 6 Jan 2023 15:19:39 +0100 Subject: [PATCH 110/193] Trigger for cuda_main and cuda_main_temp --- .github/workflows/Github_pytest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index d66950cdda..28fb4b07cb 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -2,7 +2,7 @@ name: Pyccel tests on: pull_request: - branches: [ master, cuda_main_temp ] + branches: [ master, cuda_main_temp, cuda_main ] jobs: Linux: @@ -93,7 +93,7 @@ jobs: runs-on: ubuntu-latest container: nvidia/cuda:11.7.1-devel-ubuntu20.04 - if: github.event.pull_request.base.ref == 'cuda_main_temp' + if: github.event.pull_request.base.ref != 'master' steps: - uses: actions/checkout@v2 - name: Install sudo From f519036f09ae00021ab67448f6b903223cd7cddd Mon Sep 17 00:00:00 2001 From: EmilyBoune Date: Fri, 6 Jan 2023 15:26:40 +0100 Subject: [PATCH 111/193] Don't run after failure --- .github/workflows/Github_pytest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index 28fb4b07cb..7164f74336 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -143,7 +143,7 @@ jobs: runs-on: ubuntu-latest needs: [Linux, Cuda] - if: ${{ always() }} + if: ${{ always() && needs.Linux.result != 'failed' }} steps: - uses: actions/checkout@v3 From 01fe982785b75a6e54dbb4161b507143e0a7495c Mon Sep 17 00:00:00 2001 From: EmilyBoune Date: Fri, 6 Jan 2023 15:29:52 +0100 Subject: [PATCH 112/193] Configure tzdata --- .github/workflows/Github_pytest.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index 7164f74336..d983a3571c 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -96,8 +96,11 @@ jobs: if: github.event.pull_request.base.ref != 'master' steps: - uses: actions/checkout@v2 - - name: Install sudo - run: apt update && apt install sudo + - name: Prepare docker + run: | + apt update && apt install sudo + ENV TZ=Europe/France + ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone shell: bash - name: CUDA Version run: nvcc --version # cuda install check From 213e970bda53f18e7d6d5bc940bbf71de685475b Mon Sep 17 00:00:00 2001 From: EmilyBoune Date: Fri, 6 Jan 2023 15:32:32 +0100 Subject: [PATCH 113/193] Remove ENV --- .github/workflows/Github_pytest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index d983a3571c..17c09d9319 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -99,7 +99,7 @@ jobs: - name: Prepare docker run: | apt update && apt install sudo - ENV TZ=Europe/France + TZ=Europe/France ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone shell: bash - name: CUDA Version From 053858bd66e61df8b81c17a9cf6f08ec2207e98f Mon Sep 17 00:00:00 2001 From: EmilyBoune Date: Fri, 6 Jan 2023 15:33:48 +0100 Subject: [PATCH 114/193] Require success --- .github/workflows/Github_pytest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index 17c09d9319..83ce4ce3a5 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -146,7 +146,7 @@ jobs: runs-on: ubuntu-latest needs: [Linux, Cuda] - if: ${{ always() && needs.Linux.result != 'failed' }} + if: ${{ always() && needs.Linux.result == 'success' }} steps: - uses: actions/checkout@v3 From f6ded8523c5c36982afd745064adc703a891c842 Mon Sep 17 00:00:00 2001 From: EmilyBoune Date: Fri, 6 Jan 2023 15:39:42 +0100 Subject: [PATCH 115/193] install tzdata --- .github/workflows/Github_pytest.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index 83ce4ce3a5..d7fabdc2b3 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -101,6 +101,7 @@ jobs: apt update && apt install sudo TZ=Europe/France ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata shell: bash - name: CUDA Version run: nvcc --version # cuda install check From 8f51b28dd733ad1b53a3771f14dbc3f5b3d1cbcc Mon Sep 17 00:00:00 2001 From: EmilyBoune Date: Fri, 6 Jan 2023 15:59:35 +0100 Subject: [PATCH 116/193] Try alternative definition --- .github/actions/coverage_install/action.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/actions/coverage_install/action.yml b/.github/actions/coverage_install/action.yml index 44ef760dcb..40d80c7cb4 100644 --- a/.github/actions/coverage_install/action.yml +++ b/.github/actions/coverage_install/action.yml @@ -11,6 +11,10 @@ runs: run: | INSTALL_DIR=$(cd tests; python -c "import pyccel; print(pyccel.__path__[0])") SITE_DIR=$(python -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])') + echo "INSTALL_DIR = ${INSTALL_DIR}" + echo "SITE_DIR = ${SITE_DIR}" + SITE_DIR=$(dirname ${INSTALL_DIR}) + echo "SITE_DIR = ${SITE_DIR}" echo -e "import coverage; coverage.process_startup()" > ${SITE_DIR}/pyccel_cov.pth echo -e "[run]\nparallel = True\nsource = ${INSTALL_DIR}\ndata_file = $(pwd)/.coverage\n[report]\ninclude = ${INSTALL_DIR}/*\n[xml]\noutput = cobertura.xml" > .coveragerc echo "SITE_DIR=${SITE_DIR}" >> $GITHUB_ENV From 8beef7c2637d4f2310fcbfcc8138a2003aded006 Mon Sep 17 00:00:00 2001 From: EmilyBoune Date: Fri, 6 Jan 2023 16:39:10 +0100 Subject: [PATCH 117/193] Add check --- .github/workflows/Github_pytest.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index d7fabdc2b3..31f84efd29 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -190,6 +190,7 @@ jobs: runs-on: ubuntu-latest needs: [CoverageCollection] + if: ${{ always() && needs.CoverageCollection.result == 'success' }} steps: - uses: actions/checkout@v3 From 0c24cf5cda75979d5ea3893bacb5a01289b15721 Mon Sep 17 00:00:00 2001 From: EmilyBoune Date: Fri, 6 Jan 2023 16:39:39 +0100 Subject: [PATCH 118/193] Remove debug info --- .github/actions/coverage_install/action.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/actions/coverage_install/action.yml b/.github/actions/coverage_install/action.yml index 40d80c7cb4..b8ca501936 100644 --- a/.github/actions/coverage_install/action.yml +++ b/.github/actions/coverage_install/action.yml @@ -10,9 +10,6 @@ runs: - name: Directory Creation run: | INSTALL_DIR=$(cd tests; python -c "import pyccel; print(pyccel.__path__[0])") - SITE_DIR=$(python -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])') - echo "INSTALL_DIR = ${INSTALL_DIR}" - echo "SITE_DIR = ${SITE_DIR}" SITE_DIR=$(dirname ${INSTALL_DIR}) echo "SITE_DIR = ${SITE_DIR}" echo -e "import coverage; coverage.process_startup()" > ${SITE_DIR}/pyccel_cov.pth From a6082c35667e3db2b2234e4f934c052c06f783ce Mon Sep 17 00:00:00 2001 From: EmilyBoune Date: Fri, 6 Jan 2023 18:15:58 +0100 Subject: [PATCH 119/193] Save xml file --- .github/workflows/Github_pytest.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index 31f84efd29..ca302aff97 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -185,6 +185,12 @@ jobs: with: project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} coverage-reports: cobertura.xml + - name: Save code coverage xml report + uses: actions/upload-artifact@v3 + with: + name: coverage-artifact-xml + path: cobertura.xml + retention-days: 1 CoverageChecker: @@ -206,7 +212,7 @@ jobs: - name: Collect coverage information uses: actions/download-artifact@v3 with: - name: coverage-artifact + name: coverage-artifact-xml - name: Collect diff information run: | BASE_BRANCH=$GITHUB_BASE_REF From 5656ed20cbadde7638936b7ab690f234dc70bd1a Mon Sep 17 00:00:00 2001 From: EmilyBoune Date: Fri, 6 Jan 2023 18:54:15 +0100 Subject: [PATCH 120/193] Save to correct xml file --- .github/workflows/Github_pytest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index ca302aff97..add83f662f 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -160,7 +160,7 @@ jobs: - name: Install python dependencies uses: ./.github/actions/pip_installation - name: Coverage install - run: python -m pip install coverage + uses: ./.github/actions/coverage_install - name: Collect coverage information uses: actions/download-artifact@v3 with: From 0430ec0b817b910de23ce7df9dc41fe9d4f036c3 Mon Sep 17 00:00:00 2001 From: EmilyBoune Date: Fri, 6 Jan 2023 19:19:57 +0100 Subject: [PATCH 121/193] Don't run if failure --- .github/workflows/Github_pytest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index add83f662f..13f3fd30e5 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -147,7 +147,7 @@ jobs: runs-on: ubuntu-latest needs: [Linux, Cuda] - if: ${{ always() && needs.Linux.result == 'success' }} + if: ${{ always() && needs.Linux.result == 'success' && needs.Cuda.result != 'failure' }} steps: - uses: actions/checkout@v3 @@ -196,7 +196,7 @@ jobs: runs-on: ubuntu-latest needs: [CoverageCollection] - if: ${{ always() && needs.CoverageCollection.result == 'success' }} + if: ${{ always() && needs.CoverageCollection.result == 'success' && needs.Cuda.result != 'failure' }} steps: - uses: actions/checkout@v3 From 80197fa5dd090a8f85e31d239f54abf1d3144c27 Mon Sep 17 00:00:00 2001 From: EmilyBoune Date: Mon, 9 Jan 2023 15:22:29 +0100 Subject: [PATCH 122/193] Remove restriction --- pyproject.toml | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6a90904947..9f6ed0ea68 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [build-system] requires = [ "setuptools >= 37, < 61", - "numpy >= 1.16, < 1.24", + "numpy >= 1.16", "sympy>=1.2", "termcolor >= 1.0.0", "textx>=2.2", diff --git a/setup.cfg b/setup.cfg index c3ff2e5572..1fbf13e34c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -13,7 +13,7 @@ long_description_content_type = text/markdown [options] packages = find: install_requires = - numpy >= 1.16, < 1.24 + numpy >= 1.16 sympy >= 1.2 termcolor >= 1.0.0 textx >= 2.2 From 631fd9b3a58b5956ebfcee04cff01313e927facf Mon Sep 17 00:00:00 2001 From: Aymane Benaissa <47903494+Pinkyboi@users.noreply.github.com> Date: Wed, 11 Jan 2023 10:29:50 +0100 Subject: [PATCH 123/193] Fix type inference for Cupy array functions (#1300) * Inital issue fix * Put memory_location assignation in CupyNewArray * Fix codacy errors * Remove memory_location from cupy parameter / add memory_location property * Fix the slots * Add memory_location to infere_type for CupyNewArray * Clean up cupy_arrange and cupy_array tests * Make memory location a class attribute for CupyNewArray --- pyccel/ast/cupyext.py | 24 +++++++++++++++++---- pyccel/parser/semantic.py | 13 +++++++++++ tests/internal/scripts/ccuda/cupy_arange.py | 12 ----------- tests/internal/scripts/ccuda/cupy_array.py | 12 ----------- 4 files changed, 33 insertions(+), 28 deletions(-) diff --git a/pyccel/ast/cupyext.py b/pyccel/ast/cupyext.py index 08650db3eb..d64d96cc2d 100644 --- a/pyccel/ast/cupyext.py +++ b/pyccel/ast/cupyext.py @@ -43,6 +43,7 @@ pyccel_stage = PyccelStage() __all__ = ( + 'CupyNewArray', 'CupyArray', 'CupyEmpty', 'CupyEmptyLike', @@ -58,7 +59,22 @@ ) #============================================================================== -class CupyArray(CudaNewArray): +class CupyNewArray(CudaNewArray): + """ Class from which all Cupy functions which imply a call to Allocate + inherit + """ + _memory_location = 'device' + def __init__(self): + super().__init__() + + @property + def memory_location(self): + """ Indicate if the array is allocated on the host, device or has a managed memory + """ + return self._memory_location + +#============================================================================== +class CupyArray(CupyNewArray): """ Represents a call to cupy.array for code generation. @@ -123,7 +139,7 @@ def arg(self): return self._arg #============================================================================== -class CupyArange(CudaNewArray): +class CupyArange(CupyNewArray): """ Represents a call to cupy.arange for code generation. @@ -198,7 +214,7 @@ def __new__(cls, arg): return PythonTuple(*arg.shape) #============================================================================== -class CupyFull(CudaNewArray): +class CupyFull(CupyNewArray): """ Represents a call to cupy.full for code generation. @@ -242,7 +258,7 @@ def __init__(self, shape, fill_value, dtype=None, order='C'): self._shape = shape self._rank = len(self._shape) self._dtype = dtype - self._order = CudaNewArray._process_order(self._rank, order) + self._order = CupyNewArray._process_order(self._rank, order) self._precision = precision super().__init__(fill_value) diff --git a/pyccel/parser/semantic.py b/pyccel/parser/semantic.py index f5bb312efc..5cda8cf19d 100644 --- a/pyccel/parser/semantic.py +++ b/pyccel/parser/semantic.py @@ -95,6 +95,7 @@ from pyccel.ast.numpyext import NumpyNewArray, NumpyNonZero from pyccel.ast.numpyext import DtypePrecisionToCastFunction +from pyccel.ast.cupyext import CupyNewArray from pyccel.ast.cudaext import CudaNewArray, CudaThreadIdx, CudaBlockDim, CudaBlockIdx, CudaGridDim from pyccel.ast.omp import (OMP_For_Loop, OMP_Simd_Construct, OMP_Distribute_Construct, @@ -434,6 +435,7 @@ def _infere_type(self, expr, **settings): elif isinstance(expr, Variable): d_var['datatype' ] = expr.dtype d_var['memory_handling'] = expr.memory_handling + d_var['memory_location'] = expr.memory_location d_var['shape' ] = expr.shape d_var['rank' ] = expr.rank d_var['cls_base' ] = expr.cls_base @@ -491,6 +493,17 @@ def _infere_type(self, expr, **settings): d_var['cls_base' ] = NumpyArrayClass return d_var + elif isinstance(expr, CupyNewArray): + d_var['datatype' ] = expr.dtype + d_var['memory_handling'] = 'heap' if expr.rank > 0 else 'stack' + d_var['memory_location'] = expr.memory_location + d_var['shape' ] = expr.shape + d_var['rank' ] = expr.rank + d_var['order' ] = expr.order + d_var['precision' ] = expr.precision + d_var['cls_base' ] = CudaArrayClass + return d_var + elif isinstance(expr, CudaNewArray): d_var['datatype' ] = expr.dtype d_var['memory_handling'] = 'heap' if expr.rank > 0 else 'stack' diff --git a/tests/internal/scripts/ccuda/cupy_arange.py b/tests/internal/scripts/ccuda/cupy_arange.py index c3b84588c6..19f4d5ecc7 100644 --- a/tests/internal/scripts/ccuda/cupy_arange.py +++ b/tests/internal/scripts/ccuda/cupy_arange.py @@ -1,17 +1,5 @@ from pyccel.decorators import kernel, types -from pyccel import cuda import cupy as cp -@kernel -@types('int[:]') -def func(a): - i = cuda.threadIdx(0) + cuda.blockIdx(0) * cuda.blockDim(0) - print("Hello World! ", a[i]) - if __name__ == '__main__': - threads_per_block = 32 - n_blocks = 1 arr = cp.arange(32) - cuda.deviceSynchronize() - func[n_blocks, threads_per_block](arr) - cuda.deviceSynchronize() \ No newline at end of file diff --git a/tests/internal/scripts/ccuda/cupy_array.py b/tests/internal/scripts/ccuda/cupy_array.py index 108cab1ebb..150aa2de14 100644 --- a/tests/internal/scripts/ccuda/cupy_array.py +++ b/tests/internal/scripts/ccuda/cupy_array.py @@ -1,17 +1,5 @@ from pyccel.decorators import kernel, types -from pyccel import cuda import cupy as cp -@kernel -@types('int[:]') -def func(a): - i = cuda.threadIdx(0) + cuda.blockIdx(0) * cuda.blockDim(0) - print("Hello World! ", a[i]) - if __name__ == '__main__': - threads_per_block = 5 - n_blocks = 1 arr = cp.array([0, 1, 2, 3, 4]) - cuda.synchronize() - func[n_blocks, threads_per_block](arr) - cuda.synchronize() \ No newline at end of file From 81bd653ab8c60176d226c2470e88f3539a0ae742 Mon Sep 17 00:00:00 2001 From: EmilyBoune Date: Wed, 11 Jan 2023 12:10:37 +0100 Subject: [PATCH 124/193] Turn on tests for cuda --- .github/workflows/lint.yml | 2 +- .github/workflows/pyccel_lint.yml | 2 +- .github/workflows/spelling.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 21e5676b93..f2809af248 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,7 +1,7 @@ name: Python Linting on: pull_request: - branches: [ master ] + branches: [ master, cuda_main_temp, cuda_main ] jobs: Linter: diff --git a/.github/workflows/pyccel_lint.yml b/.github/workflows/pyccel_lint.yml index 55e5f6fced..6ccaba4b3e 100644 --- a/.github/workflows/pyccel_lint.yml +++ b/.github/workflows/pyccel_lint.yml @@ -1,7 +1,7 @@ name: Pyccel Linting on: pull_request: - branches: [ master ] + branches: [ master, cuda_main_temp, cuda_main ] jobs: Pyccel-Linter: diff --git a/.github/workflows/spelling.yml b/.github/workflows/spelling.yml index 9b3e891074..dd3a5b3f8b 100644 --- a/.github/workflows/spelling.yml +++ b/.github/workflows/spelling.yml @@ -1,7 +1,7 @@ name: Spellcheck Action on: pull_request: - branches: [ master ] + branches: [ master, cuda_main_temp, cuda_main ] jobs: Spelling: From ef45c7458047638a44c721eeb42f5d916f7e8527 Mon Sep 17 00:00:00 2001 From: EmilyBoune Date: Wed, 11 Jan 2023 12:10:37 +0100 Subject: [PATCH 125/193] Turn on tests for cuda --- .github/workflows/lint.yml | 2 +- .github/workflows/pyccel_lint.yml | 2 +- .github/workflows/spelling.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 21e5676b93..f2809af248 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,7 +1,7 @@ name: Python Linting on: pull_request: - branches: [ master ] + branches: [ master, cuda_main_temp, cuda_main ] jobs: Linter: diff --git a/.github/workflows/pyccel_lint.yml b/.github/workflows/pyccel_lint.yml index 55e5f6fced..6ccaba4b3e 100644 --- a/.github/workflows/pyccel_lint.yml +++ b/.github/workflows/pyccel_lint.yml @@ -1,7 +1,7 @@ name: Pyccel Linting on: pull_request: - branches: [ master ] + branches: [ master, cuda_main_temp, cuda_main ] jobs: Pyccel-Linter: diff --git a/.github/workflows/spelling.yml b/.github/workflows/spelling.yml index 9b3e891074..dd3a5b3f8b 100644 --- a/.github/workflows/spelling.yml +++ b/.github/workflows/spelling.yml @@ -1,7 +1,7 @@ name: Spellcheck Action on: pull_request: - branches: [ master ] + branches: [ master, cuda_main_temp, cuda_main ] jobs: Spelling: From 30750a3ccc2d49383e8c2665cfc9789090853ec1 Mon Sep 17 00:00:00 2001 From: Fatima-zahra RAMDANI <52450718+framdani@users.noreply.github.com> Date: Wed, 11 Jan 2023 12:20:07 +0100 Subject: [PATCH 126/193] Fix kernel launch configuration errors (#1279) * fix kernel launch configuration errors * fix linter * Avoid checking for attribute 'decorators' for Interface objects * update error messages * add tests * Fix new line missing * add missing newline * Combining error tests in a single file * remove Trailing whitespace * add test for invalid kernel configuration * fix codacy issue * trailing whitespace removed * deactivate numpy's recent version temp * fix typo * Handle 'No new line at end of file' * Fix line definition * Add line break between errors * Correct url * add tests * Activate cuda tests * Fix cuda setup * Update pyccel/parser/semantic.py Co-authored-by: EmilyBourne * Update variable search method * Display correct error message * raise error for calling non-kernel functions with parameters * add test for invalid function call error * codacy issues * remove unecessary error check * Update kernel decorator definition * fix typo * Fix new line missing * Fix new line Co-authored-by: EmilyBoune Co-authored-by: bauom --- pyccel/decorators.py | 4 +- pyccel/errors/messages.py | 7 +- pyccel/parser/semantic.py | 30 +++++-- tests/cuda_test/test_kernel_semantic.py | 104 ++++++++++++++++++++++-- 4 files changed, 132 insertions(+), 13 deletions(-) diff --git a/pyccel/decorators.py b/pyccel/decorators.py index e85b2ebd75..0e3f04c4e3 100644 --- a/pyccel/decorators.py +++ b/pyccel/decorators.py @@ -6,6 +6,7 @@ """ This module contains all the provided decorator methods. """ +import numpy #TODO use pycode and call exec after that in lambdify @@ -101,4 +102,5 @@ def identity(f): return identity def kernel(f): - return f \ No newline at end of file + return numpy.array([[f]]) + diff --git a/pyccel/errors/messages.py b/pyccel/errors/messages.py index b4f28fc91b..a339154ee2 100644 --- a/pyccel/errors/messages.py +++ b/pyccel/errors/messages.py @@ -162,5 +162,8 @@ NON_LITERAL_AXIS = 'axis argument must be a literal, otherwise pyccel cannot determine which dimension to operate on' KERNEL_STACK_ARRAY_ARG = "A variable allocated on the stack can't be passed to a Kernel function" NON_KERNEL_FUNCTION_CUDA_VAR = 'Cuda internal variables should only be used in Kernel or Device functions' -UNVALID_KERNEL_CALL_BLOCK_NUM = 'Invalid Block number parameter for Kernel call' -UNVALID_KERNEL_CALL_TP_BLOCK = 'Invalid Thread per Block parameter for Kernel call' \ No newline at end of file +INVALID_KERNEL_CALL_BP_GRID = 'Invalid Block per grid parameter for Kernel call' +INVALID_KERNEL_CALL_TP_BLOCK = 'Invalid Thread per Block parameter for Kernel call' +MISSING_KERNEL_CONFIGURATION = 'Kernel launch configuration not specified' +INVALID_FUNCTION_CALL = 'Invalid call for a non-kernel function' + diff --git a/pyccel/parser/semantic.py b/pyccel/parser/semantic.py index fa31ccbd5e..d69a880955 100644 --- a/pyccel/parser/semantic.py +++ b/pyccel/parser/semantic.py @@ -123,7 +123,7 @@ from pyccel.errors.errors import Errors from pyccel.errors.errors import PyccelSemanticError -from pyccel.errors.messages import (PYCCEL_RESTRICTION_TODO, UNDERSCORE_NOT_A_THROWAWAY, +from pyccel.errors.messages import (INVALID_FUNCTION_CALL, INVALID_KERNEL_CALL_BP_GRID, INVALID_KERNEL_CALL_TP_BLOCK, MISSING_KERNEL_CONFIGURATION,PYCCEL_RESTRICTION_TODO, UNDERSCORE_NOT_A_THROWAWAY, UNDEFINED_VARIABLE, IMPORTING_EXISTING_IDENTIFIED, INDEXED_TUPLE, LIST_OF_TUPLES, INVALID_INDICES, INCOMPATIBLE_ARGUMENT, INCOMPATIBLE_ORDERING, UNRECOGNISED_FUNCTION_CALL, STACK_ARRAY_SHAPE_UNPURE_FUNC, STACK_ARRAY_UNKNOWN_SHAPE, @@ -825,7 +825,8 @@ def _handle_function(self, expr, func, args, **settings): ======= new_expr : FunctionCall or PyccelInternalFunction """ - + if isinstance(func, FunctionDef) and 'kernel' in func.decorators: + errors.report(MISSING_KERNEL_CONFIGURATION, symbol = expr, severity = 'fatal') if isinstance(func, PyccelFunctionDef): func = func.cls_name if func in (CudaThreadIdx, CudaBlockDim, CudaBlockIdx, CudaGridDim): @@ -913,6 +914,10 @@ def _handle_kernel(self, expr, func, args, **settings): ======= new_expr : FunctionCall or PyccelInternalFunction """ + if 'kernel' not in func.decorators: + errors.report(INVALID_FUNCTION_CALL, + symbol = expr, + severity = 'fatal') if isinstance(func, PyccelFunctionDef): func = func.cls_name args, kwargs = split_positional_keyword_arguments(*args) @@ -938,14 +943,29 @@ def _handle_kernel(self, expr, func, args, **settings): severity='fatal') # TODO : type check the NUMBER OF BLOCKS 'numBlocks' and threads per block 'tpblock' if not isinstance(expr.numBlocks, LiteralInteger): - errors.report("Invalid Block number parameter for Kernel call", + # expr.numBlocks could be invalid type, or PyccelSymbol + if isinstance(expr.numBlocks, PyccelSymbol): + numBlocks = self.get_variable(expr.numBlocks) + if not isinstance(numBlocks.dtype, NativeInteger): + errors.report(INVALID_KERNEL_CALL_BP_GRID, + symbol = expr, + severity='error') + else: + errors.report(INVALID_KERNEL_CALL_BP_GRID, symbol = expr, severity='error') if not isinstance(expr.tpblock, LiteralInteger): - errors.report("Invalid Thread per Block parameter for Kernel call", + # expr.tpblock could be invalid type, or PyccelSymbol + if isinstance(expr.tpblock, PyccelSymbol): + tpblock = self.get_variable(expr.tpblock) + if not isinstance(tpblock.dtype, NativeInteger): + errors.report(INVALID_KERNEL_CALL_TP_BLOCK, + symbol = expr, + severity='error') + else: + errors.report(INVALID_KERNEL_CALL_TP_BLOCK, symbol = expr, severity='error') - new_expr = KernelCall(func, args, expr.numBlocks, expr.tpblock, self._current_function) for a in new_expr.args: diff --git a/tests/cuda_test/test_kernel_semantic.py b/tests/cuda_test/test_kernel_semantic.py index d2cfd5e143..2783c5c9f4 100644 --- a/tests/cuda_test/test_kernel_semantic.py +++ b/tests/cuda_test/test_kernel_semantic.py @@ -6,10 +6,10 @@ from pyccel.epyccel import epyccel from pyccel.decorators import stack_array, types, kernel from pyccel.errors.errors import Errors, PyccelSemanticError -from pyccel.errors.messages import (KERNEL_STACK_ARRAY_ARG, +from pyccel.errors.messages import (INVALID_FUNCTION_CALL, KERNEL_STACK_ARRAY_ARG, MISSING_KERNEL_CONFIGURATION, NON_KERNEL_FUNCTION_CUDA_VAR, - UNVALID_KERNEL_CALL_BLOCK_NUM, - UNVALID_KERNEL_CALL_TP_BLOCK, + INVALID_KERNEL_CALL_BP_GRID, + INVALID_KERNEL_CALL_TP_BLOCK ) @pytest.mark.parametrize( 'language', [ @@ -94,7 +94,7 @@ def kernel_call(): # Check that the error is correct error_info = [*errors.error_info_map.values()][0][0] assert error_info.symbol.func == 'kernel_call' - assert UNVALID_KERNEL_CALL_BLOCK_NUM == error_info.message + assert INVALID_KERNEL_CALL_BP_GRID == error_info.message @pytest.mark.parametrize( 'language', [ pytest.param("ccuda", marks = pytest.mark.ccuda) @@ -121,4 +121,98 @@ def kernel_call(): # Check that the error is correct error_info = [*errors.error_info_map.values()][0][0] assert error_info.symbol.func == 'kernel_call' - assert UNVALID_KERNEL_CALL_TP_BLOCK == error_info.message + assert INVALID_KERNEL_CALL_TP_BLOCK == error_info.message + +@pytest.mark.parametrize( 'language', [ + pytest.param("ccuda", marks = pytest.mark.ccuda) + ] +) +def test_missing_kernel_config(language): + def missing_kernel_config(): + @kernel + def kernel_call(): + pass + kernel_call() + + errors = Errors() + + with pytest.raises(PyccelSemanticError): + epyccel(missing_kernel_config, language=language) + + assert errors.has_errors() + assert errors.num_messages() == 1 + + error_info = [*errors.error_info_map.values()][0][0] + assert error_info.symbol.func_name == 'kernel_call' + assert MISSING_KERNEL_CONFIGURATION == error_info.message + +@pytest.mark.parametrize( 'language', [ + pytest.param("ccuda", marks = pytest.mark.ccuda) + ] +) +def test_invalid_block_number(language): + def invalid_block_number(): + @kernel + def kernel_call(): + pass + blocks_per_grid = 5.0 + kernel_call[blocks_per_grid, 1]() + + errors = Errors() + + with pytest.raises(PyccelSemanticError): + epyccel(invalid_block_number, language=language) + + assert errors.has_errors() + assert errors.num_messages() == 1 + + error_info = [*errors.error_info_map.values()][0][0] + assert error_info.symbol.func == 'kernel_call' + assert INVALID_KERNEL_CALL_BP_GRID == error_info.message + +@pytest.mark.parametrize( 'language', [ + pytest.param("ccuda", marks = pytest.mark.ccuda) + ] +) +def test_invalid_thread_per_block(language): + def invalid_thread_per_block(): + @kernel + def kernel_call(): + pass + threads_per_block = 5.0 + kernel_call[1, threads_per_block]() + + errors = Errors() + + with pytest.raises(PyccelSemanticError): + epyccel(invalid_thread_per_block, language=language) + + assert errors.has_errors() + assert errors.num_messages() == 1 + + error_info = [*errors.error_info_map.values()][0][0] + assert error_info.symbol.func == 'kernel_call' + assert INVALID_KERNEL_CALL_TP_BLOCK == error_info.message + + +@pytest.mark.parametrize( 'language', [ + pytest.param("ccuda", marks = pytest.mark.ccuda) + ] +) +def test_invalid_function_call(language): + def invalid_function_call(): + def non_kernel_func(): + pass + non_kernel_func[1, 2]() # pylint: disable=E1136 + + errors = Errors() + + with pytest.raises(PyccelSemanticError): + epyccel(invalid_function_call, language=language) + + assert errors.has_errors() + assert errors.num_messages() == 1 + + error_info = [*errors.error_info_map.values()][0][0] + assert error_info.symbol.func == 'non_kernel_func' + assert INVALID_FUNCTION_CALL == error_info.message From 68a3b117092d2d25d406c29c298faa8947b8bd3b Mon Sep 17 00:00:00 2001 From: bauom <40796259+bauom@users.noreply.github.com> Date: Wed, 11 Jan 2023 14:10:41 +0100 Subject: [PATCH 127/193] cleaning compile stage Cuda tests. (#1301) * cleaned cuda tests to be more specific * fixed kernel_launch function * added kernel_launch_config tests * fixing pyccel linter --- pyccel/ast/core.py | 7 ++- pyccel/ast/cudaext.py | 55 +++++++++++++++---- pyccel/ast/cupyext.py | 10 ++-- .../scripts/ccuda/cuda_array_device.py | 12 ---- .../internal/scripts/ccuda/cuda_array_host.py | 8 --- .../scripts/ccuda/cuda_array_managed.py | 13 ----- tests/internal/scripts/ccuda/cuda_copy.py | 2 +- tests/internal/scripts/ccuda/cuda_grid.py | 12 ---- tests/internal/scripts/ccuda/cupy_arange.py | 1 - tests/internal/scripts/ccuda/cupy_array.py | 1 - tests/internal/scripts/ccuda/kernel_launch.py | 16 ------ .../ccuda/kernel_launch_config_literal.py | 11 ++++ .../ccuda/kernel_launch_config_variable.py | 13 +++++ 13 files changed, 79 insertions(+), 82 deletions(-) delete mode 100644 tests/internal/scripts/ccuda/kernel_launch.py create mode 100644 tests/internal/scripts/ccuda/kernel_launch_config_literal.py create mode 100644 tests/internal/scripts/ccuda/kernel_launch_config_variable.py diff --git a/pyccel/ast/core.py b/pyccel/ast/core.py index 0c9c45bf7f..f9fa5fe1b8 100644 --- a/pyccel/ast/core.py +++ b/pyccel/ast/core.py @@ -57,9 +57,9 @@ 'Declare', 'Decorator', 'Del', + 'DoConcurrent', 'DottedFunctionCall', 'Duplicate', - 'DoConcurrent', 'EmptyNode', 'ErrorExit', 'Exit', @@ -74,10 +74,11 @@ 'If', 'IfSection', 'Import', - 'InlineFunctionDef', 'InProgram', + 'InlineFunctionDef', 'Interface', 'Iterable', + 'KernelCall', 'Module', 'ModuleHeader', 'Pass', @@ -92,8 +93,8 @@ 'SympyFunction', 'While', 'With', - 'create_variable', 'create_incremented_string', + 'create_variable', 'get_iterable_ranges', 'inline', 'subs' diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index d1ed2728bc..63b837be9d 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -34,16 +34,17 @@ #============================================================================== __all__ = ( - 'CudaMemCopy', - 'CudaNewArray', 'CudaArray', - 'CudaSynchronize', - 'CudaInternalVar', - 'CudaThreadIdx', 'CudaBlockDim', 'CudaBlockIdx', + 'CudaCopy', + 'CudaGrid', 'CudaGridDim', - 'CudaGrid' + 'CudaInternalVar', + 'CudaMemCopy', + 'CudaNewArray', + 'CudaSynchronize', + 'CudaThreadIdx' ) #============================================================================== @@ -139,7 +140,8 @@ def memory_location(self): class CudaSynchronize(PyccelInternalFunction): "Represents a call to Cuda.deviceSynchronize for code generation." - # pass + + __slots__ = ('_dtype','_precision','_shape','_rank','_order') _attribute_nodes = () def __init__(self): #... @@ -151,6 +153,16 @@ def __init__(self): super().__init__() class CudaInternalVar(PyccelAstNode): + """ + Represents a General Class For Cuda internal Variables Used To locate Thread In the GPU architecture" + + Parameters + ---------- + dim : NativeInteger + Represent the dimension where we want to locate our thread. + + """ + __slots__ = ('_dim','_dtype','_precision','_shape','_rank','_order') _attribute_nodes = ('_dim',) def __init__(self, dim=None): @@ -232,11 +244,32 @@ def memory_location(self): def is_async(self): return self._is_async -class CudaThreadIdx(CudaInternalVar) : pass -class CudaBlockDim(CudaInternalVar) : pass -class CudaBlockIdx(CudaInternalVar) : pass -class CudaGridDim(CudaInternalVar) : pass +class CudaThreadIdx(CudaInternalVar): + __slots__ = () + pass +class CudaBlockDim(CudaInternalVar): + __slots__ = () + pass +class CudaBlockIdx(CudaInternalVar): + __slots__ = () + pass +class CudaGridDim(CudaInternalVar): + __slots__ = () + pass + class CudaGrid(PyccelAstNode) : + """ + CudaGrid locate Thread In the GPU architecture Using CudaThreadIdx, CudaBlockDim, CudaBlockIdx + To calculate the exact index of the thread automatically. + + Parameters + ---------- + dim : NativeInteger + Represent the dimension where we want to locate our thread. + + """ + __slots__ = () + _attribute_nodes = () def __new__(cls, dim=0): if not isinstance(dim, LiteralInteger): raise TypeError("dimension need to be an integer") diff --git a/pyccel/ast/cupyext.py b/pyccel/ast/cupyext.py index d64d96cc2d..68a5a07391 100644 --- a/pyccel/ast/cupyext.py +++ b/pyccel/ast/cupyext.py @@ -43,19 +43,20 @@ pyccel_stage = PyccelStage() __all__ = ( - 'CupyNewArray', + 'CupyArange', 'CupyArray', + 'CupyArraySize', + 'CupyAutoFill', 'CupyEmpty', 'CupyEmptyLike', 'CupyFull', 'CupyFullLike', - 'CupyArange', - 'CupyArraySize', + 'CupyNewArray', 'CupyOnes', 'CupyOnesLike', - 'Shape', 'CupyZeros', 'CupyZerosLike', + 'Shape' ) #============================================================================== @@ -63,6 +64,7 @@ class CupyNewArray(CudaNewArray): """ Class from which all Cupy functions which imply a call to Allocate inherit """ + __slots__ = () _memory_location = 'device' def __init__(self): super().__init__() diff --git a/tests/internal/scripts/ccuda/cuda_array_device.py b/tests/internal/scripts/ccuda/cuda_array_device.py index ff6bd392ae..17c50a2824 100644 --- a/tests/internal/scripts/ccuda/cuda_array_device.py +++ b/tests/internal/scripts/ccuda/cuda_array_device.py @@ -1,16 +1,4 @@ -from pyccel.decorators import kernel, types from pyccel import cuda -@kernel -@types('int[:]') -def square(a): - index = cuda.blockIdx(0) * cuda.blockDim(0) + cuda.threadIdx(0) - a[index] = a[index] * a[index] - if __name__ == '__main__': - threads_per_block = 5 - n_blocks = 1 arr = cuda.array([0,1,2,3,4], memory_location = 'device') - cuda.synchronize() - square[n_blocks, threads_per_block](arr) - cuda.synchronize() diff --git a/tests/internal/scripts/ccuda/cuda_array_host.py b/tests/internal/scripts/ccuda/cuda_array_host.py index 5697c32963..8702031bba 100644 --- a/tests/internal/scripts/ccuda/cuda_array_host.py +++ b/tests/internal/scripts/ccuda/cuda_array_host.py @@ -1,12 +1,4 @@ -from pyccel.decorators import kernel, types from pyccel import cuda -@types('int[:]') -def multiplyPrintElements(a): - for i in a: - i = i * 2 - print(i) - if __name__ == '__main__': arr = cuda.array([0,1,2,3,4], memory_location='host') - multiplyPrintElements(arr) diff --git a/tests/internal/scripts/ccuda/cuda_array_managed.py b/tests/internal/scripts/ccuda/cuda_array_managed.py index 96898ff506..58070d84ae 100644 --- a/tests/internal/scripts/ccuda/cuda_array_managed.py +++ b/tests/internal/scripts/ccuda/cuda_array_managed.py @@ -1,17 +1,4 @@ -from pyccel.decorators import kernel, types from pyccel import cuda -@kernel -@types('int[:]') -def square(a): - index = cuda.blockIdx(0) * cuda.blockDim(0) + cuda.threadIdx(0) - a[index] = a[index] * a[index] - if __name__ == '__main__': - threads_per_block = 5 - n_blocks = 1 a = cuda.array([0,1,2,3,4], memory_location = 'managed') - cuda.synchronize() - square[n_blocks, threads_per_block](a) - cuda.synchronize() - print(a) \ No newline at end of file diff --git a/tests/internal/scripts/ccuda/cuda_copy.py b/tests/internal/scripts/ccuda/cuda_copy.py index a3a3e93e4d..11a59c7fa0 100644 --- a/tests/internal/scripts/ccuda/cuda_copy.py +++ b/tests/internal/scripts/ccuda/cuda_copy.py @@ -3,4 +3,4 @@ if __name__ == '__main__': arr = cuda.array([1,2,3,4], memory_location='device') - out = cuda.copy(arr, 'host', is_async=True) \ No newline at end of file + out = cuda.copy(arr, 'host', is_async=True) diff --git a/tests/internal/scripts/ccuda/cuda_grid.py b/tests/internal/scripts/ccuda/cuda_grid.py index 835f0902bb..56c2d14325 100644 --- a/tests/internal/scripts/ccuda/cuda_grid.py +++ b/tests/internal/scripts/ccuda/cuda_grid.py @@ -18,15 +18,3 @@ def func_2d(a): def func_3d(a): i, j, k = cuda.grid(2) print("3 dim :", a[i], a[j], a[k]) - -if __name__ == '__main__': - threads_per_block = 5 - n_blocks = 1 - arr = cuda.array([0, 1, 2, 3, 4]) - cuda.synchronize() - func_1d[n_blocks, threads_per_block](arr) - # Since we dont support multi-dim n_block / threads_per_block - # func_2d and func_3d won't compile - # func_2d[n_blocks, threads_per_block](arr) - # func_3d[n_blocks, threads_per_block](arr) - cuda.synchronize() \ No newline at end of file diff --git a/tests/internal/scripts/ccuda/cupy_arange.py b/tests/internal/scripts/ccuda/cupy_arange.py index 19f4d5ecc7..91664bf956 100644 --- a/tests/internal/scripts/ccuda/cupy_arange.py +++ b/tests/internal/scripts/ccuda/cupy_arange.py @@ -1,4 +1,3 @@ -from pyccel.decorators import kernel, types import cupy as cp if __name__ == '__main__': diff --git a/tests/internal/scripts/ccuda/cupy_array.py b/tests/internal/scripts/ccuda/cupy_array.py index 150aa2de14..c168c156e5 100644 --- a/tests/internal/scripts/ccuda/cupy_array.py +++ b/tests/internal/scripts/ccuda/cupy_array.py @@ -1,4 +1,3 @@ -from pyccel.decorators import kernel, types import cupy as cp if __name__ == '__main__': diff --git a/tests/internal/scripts/ccuda/kernel_launch.py b/tests/internal/scripts/ccuda/kernel_launch.py deleted file mode 100644 index 4335a6067c..0000000000 --- a/tests/internal/scripts/ccuda/kernel_launch.py +++ /dev/null @@ -1,16 +0,0 @@ -from pyccel.decorators import kernel, types -from pyccel import cuda - -@kernel -@types('int[:]') -def func(a): - i = cuda.threadIdx(0) + cuda.blockIdx(0) * cuda.blockDim(0) - print("Hello World! ", a[i]) - -if __name__ == '__main__': - threads_per_block = 5 - n_blocks = 1 - arr = cuda.array([0, 1, 2, 3, 4]) - cuda.synchronize() - func[n_blocks, threads_per_block](arr) - cuda.synchronize() \ No newline at end of file diff --git a/tests/internal/scripts/ccuda/kernel_launch_config_literal.py b/tests/internal/scripts/ccuda/kernel_launch_config_literal.py new file mode 100644 index 0000000000..25a6d3f317 --- /dev/null +++ b/tests/internal/scripts/ccuda/kernel_launch_config_literal.py @@ -0,0 +1,11 @@ +from pyccel.decorators import kernel +from pyccel import cuda + + +@kernel +def func(): + i = cuda.threadIdx(0) + cuda.blockIdx(0) * cuda.blockDim(0) + print("Hello World! ") + +if __name__ == '__main__': + func[1, 5]() diff --git a/tests/internal/scripts/ccuda/kernel_launch_config_variable.py b/tests/internal/scripts/ccuda/kernel_launch_config_variable.py new file mode 100644 index 0000000000..199e9ee6e5 --- /dev/null +++ b/tests/internal/scripts/ccuda/kernel_launch_config_variable.py @@ -0,0 +1,13 @@ +from pyccel.decorators import kernel +from pyccel import cuda + + +@kernel +def func(): + i = cuda.threadIdx(0) + cuda.blockIdx(0) * cuda.blockDim(0) + print("Hello World! ") + +if __name__ == '__main__': + nb = 1 + tpb = 5 + func[nb, tpb]() From f6ee2bb7ebac86453a7e279770fca17501cac945 Mon Sep 17 00:00:00 2001 From: EmilyBourne Date: Thu, 12 Jan 2023 13:37:00 +0100 Subject: [PATCH 128/193] Ensure master tests are triggered on cuda_main (#1305) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update branch with commits from master. `cuda_main` and `cuda_devel` branches should have the same tests as the master branch. These tests are activated here. The setup for cuda tests is also added Co-authored-by: Abdechahid Ihya <50792865+aihya@users.noreply.github.com> Co-authored-by: Aymane Benaissa <47903494+Pinkyboi@users.noreply.github.com> Co-authored-by: Youness Farini Co-authored-by: Yassine Alaoui <48657685+yassine-alaoui@users.noreply.github.com> Co-authored-by: Youness Farini Co-authored-by: Yaman Güçlü Co-authored-by: Aaron Holmes Co-authored-by: Ibrahim El Mountasser <33915853+ceciver@users.noreply.github.com> Co-authored-by: OTHMANE HACHIM <35775290+ohachim@users.noreply.github.com> --- .dict_custom.txt | 77 +++ .github/CONTRIBUTING.md | 51 ++ .github/ISSUE_TEMPLATE/bug-report.md | 11 +- .github/ISSUE_TEMPLATE/documentation.md | 10 + .github/ISSUE_TEMPLATE/feature-request.md | 14 +- .../ISSUE_TEMPLATE/installation-problem.md | 24 + .../actions/coverage_collection/action.yml | 1 - .github/actions/coverage_install/action.yml | 2 +- .github/actions/linux_install/action.yml | 10 +- .github/actions/macos_install/action.yml | 3 +- .github/actions/pytest_parallel/action.yml | 4 +- .github/actions/pytest_run/action.yml | 16 +- .github/actions/pytest_run_cuda/action.yml | 17 + .github/actions/pytest_run_python/action.yml | 4 +- .github/actions/python_install/action.yml | 17 + .github/workflows/Github_pytest.yml | 134 ++++- .github/workflows/bench.yml | 38 +- .github/workflows/lint.yml | 24 + .github/workflows/master.yml | 28 +- .github/workflows/pyccel_lint.yml | 23 + .github/workflows/spelling.yml | 29 + .pylintrc | 3 +- .pyspelling.yml | 26 + README.md | 61 +-- ci_tools/check_new_coverage.py | 31 ++ ci_tools/check_slots.py | 109 ++++ ci_tools/coverage_analysis_tools.py | 175 ++++++ ci_tools/git_evaluation_tools.py | 88 +++ ci_tools/summarise_pyspelling.py | 62 +++ developer_docs/how_to_solve_an_issue.md | 2 +- developer_docs/order_docs.md | 468 ++++++++++++++++ developer_docs/overview.md | 17 +- developer_docs/review_process.md | 6 +- developer_docs/scope.md | 4 +- pyccel/ast/basic.py | 2 +- pyccel/ast/bind_c.py | 2 +- pyccel/ast/bitwise_operators.py | 10 +- pyccel/ast/builtin_imports.py | 2 + pyccel/ast/builtins.py | 34 +- pyccel/ast/c_concepts.py | 179 +++++- pyccel/ast/core.py | 13 + pyccel/ast/cwrapper.py | 1 + pyccel/ast/datatypes.py | 3 +- pyccel/ast/headers.py | 7 +- pyccel/ast/internals.py | 9 +- pyccel/ast/itertoolsext.py | 2 +- pyccel/ast/literals.py | 10 +- pyccel/ast/numpyext.py | 135 +++-- pyccel/ast/omp.py | 18 + pyccel/ast/operators.py | 13 +- pyccel/ast/scipyext.py | 2 + pyccel/ast/sympy_helper.py | 3 + pyccel/ast/sysext.py | 61 +++ pyccel/ast/utilities.py | 31 +- pyccel/ast/variable.py | 3 + pyccel/codegen/printing/ccode.py | 213 +++++--- pyccel/codegen/printing/fcode.py | 113 +++- pyccel/codegen/utilities.py | 10 +- pyccel/compilers/default_compilers.py | 2 + pyccel/naming/cnameclashchecker.py | 2 +- pyccel/naming/fortrannameclashchecker.py | 2 +- pyccel/parser/scope.py | 1 + pyccel/parser/semantic.py | 66 ++- pyccel/parser/syntactic.py | 4 +- .../cwrapper_ndarrays/cwrapper_ndarrays.c | 5 +- pyccel/stdlib/numpy/numpy_c.c | 24 + pyccel/stdlib/numpy/numpy_c.h | 20 + pyccel/stdlib/numpy/numpy_f90.f90 | 156 ++++++ pyccel/version.py | 2 +- pyproject.toml | 6 +- setup.cfg | 19 +- tests/epyccel/modules/arrays.py | 41 +- tests/epyccel/modules/augassign.py | 155 ++++++ .../modules/call_user_defined_funcs.py | 9 + tests/epyccel/modules/numpy_sign.py | 295 ++++++++++ .../recognised_functions/test_numpy_funcs.py | 514 ++++++++++++++++-- .../recognised_functions/test_numpy_types.py | 188 ++++++- tests/epyccel/test_arrays.py | 180 +++++- tests/epyccel/test_builtins.py | 79 +-- .../test_default_precision_template.py | 31 ++ tests/epyccel/test_epyccel_augassign.py | 212 ++++++++ tests/epyccel/test_epyccel_complex_func.py | 19 +- tests/epyccel/test_epyccel_functions.py | 13 +- tests/epyccel/test_epyccel_generators.py | 2 +- tests/epyccel/test_epyccel_modules.py | 13 +- tests/epyccel/test_epyccel_optional_args.py | 17 +- tests/epyccel/test_epyccel_return_arrays.py | 481 ++++++++++++++++ tests/epyccel/test_epyccel_sign.py | 310 +++++++++++ tests/epyccel/test_generic_functions.py | 3 +- tests/epyccel/test_return.py | 21 + .../pyccel/scripts/array_binary_operation.py | 92 ++++ tests/pyccel/scripts/exits/empty_exit.py | 6 + tests/pyccel/scripts/exits/negative_exit1.py | 6 + tests/pyccel/scripts/exits/negative_exit2.py | 6 + tests/pyccel/scripts/exits/positive_exit1.py | 6 + tests/pyccel/scripts/exits/positive_exit2.py | 6 + tests/pyccel/scripts/exits/positive_exit3.py | 7 + tests/pyccel/scripts/exits/zero_exit.py | 6 + tests/pyccel/scripts/numpy/numpy_sign.py | 84 +++ tests/pyccel/scripts/print_integers.py | 37 ++ tests/pyccel/scripts/print_tuples.py | 12 + tests/pyccel/scripts/runtest_type_print.py | 4 +- tests/pyccel/test_pyccel.py | 94 ++-- tutorial/builtin-functions.md | 138 ++--- tutorial/compiler.md | 48 +- tutorial/const_keyword.md | 14 +- tutorial/decorators.md | 8 +- tutorial/function-pointers-as-arguments.md | 12 +- tutorial/header-files.md | 12 +- tutorial/ndarrays.md | 12 +- tutorial/numpy-functions.md | 78 +-- tutorial/openmp.md | 140 ++--- tutorial/quickstart.md | 34 +- tutorial/templates.md | 10 +- 114 files changed, 5479 insertions(+), 750 deletions(-) create mode 100644 .dict_custom.txt create mode 100644 .github/CONTRIBUTING.md create mode 100644 .github/ISSUE_TEMPLATE/documentation.md create mode 100644 .github/ISSUE_TEMPLATE/installation-problem.md create mode 100644 .github/actions/pytest_run_cuda/action.yml create mode 100644 .github/actions/python_install/action.yml create mode 100644 .github/workflows/lint.yml create mode 100644 .github/workflows/pyccel_lint.yml create mode 100644 .github/workflows/spelling.yml create mode 100644 .pyspelling.yml create mode 100644 ci_tools/check_new_coverage.py create mode 100644 ci_tools/check_slots.py create mode 100644 ci_tools/coverage_analysis_tools.py create mode 100644 ci_tools/git_evaluation_tools.py create mode 100644 ci_tools/summarise_pyspelling.py create mode 100644 developer_docs/order_docs.md create mode 100644 pyccel/ast/sysext.py create mode 100644 pyccel/stdlib/numpy/numpy_c.c create mode 100644 pyccel/stdlib/numpy/numpy_c.h create mode 100644 pyccel/stdlib/numpy/numpy_f90.f90 create mode 100644 tests/epyccel/modules/augassign.py create mode 100644 tests/epyccel/modules/numpy_sign.py create mode 100644 tests/epyccel/test_default_precision_template.py create mode 100644 tests/epyccel/test_epyccel_augassign.py create mode 100644 tests/epyccel/test_epyccel_sign.py create mode 100644 tests/pyccel/scripts/array_binary_operation.py create mode 100644 tests/pyccel/scripts/exits/empty_exit.py create mode 100644 tests/pyccel/scripts/exits/negative_exit1.py create mode 100644 tests/pyccel/scripts/exits/negative_exit2.py create mode 100644 tests/pyccel/scripts/exits/positive_exit1.py create mode 100644 tests/pyccel/scripts/exits/positive_exit2.py create mode 100644 tests/pyccel/scripts/exits/positive_exit3.py create mode 100644 tests/pyccel/scripts/exits/zero_exit.py create mode 100644 tests/pyccel/scripts/numpy/numpy_sign.py create mode 100644 tests/pyccel/scripts/print_integers.py create mode 100644 tests/pyccel/scripts/print_tuples.py diff --git a/.dict_custom.txt b/.dict_custom.txt new file mode 100644 index 0000000000..c8a236346b --- /dev/null +++ b/.dict_custom.txt @@ -0,0 +1,77 @@ +Pyccel +Pythran +numba +NumPy +NumPy's +BLAS +LAPACK +MPI +OpenMP +Fortran +pyccelise +pyccelised +pyccelising +allocatable +deallocate +conda +PyPI +CentOS +RHEL +executables +linux +macOS +DLL +DLLs +MPICH +openSUSE +Xcode +SIMD +runtime +pragma +pragmas +API +APIs +Pyccel's +PGI +GFortran +GCC +DNF +Homebrew +CLT +SDK +GitHub +OpenMPI +JSON +CPython +IPython +HPC +podman +vectorisation +precompiled +elementwise +SELinux +distro +PACKage +ndarray +ndarrays +metavariable +broadcastable +parallelisation +Quickstart +FST +AST +RedBaron +intel +nvidia +boolean +quicksort +iterm +textx +tracebacks +pytest +docstring +docstrings +Codacy +codebase +backend +iterable diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000000..ce4c3456df --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,51 @@ +# Contributing to pyccel + +Firstly, thanks for your interest in contributing code to pyccel! + +We welcome any and all contributions. + +There are many ways to help with the pyccel project which are more or less involved. +The following is a summary of how you can do this. + +## Asking questions + +For simple questions (e.g. installation problems), we encourage you to use the [discussions tab](https://github.com/pyccel/pyccel/discussions/categories/q-a). + +## Answering questions + +Questions are sometimes asked in the [discussions tab](https://github.com/pyccel/pyccel/discussions/categories/q-a) or in issues. +Any help answering these questions is appreciated as it helps our users find solutions as fast as possible. + +## Reporting issues + +When reporting issues please include as much detail as possible about your +operating system, and python version. Whenever possible, please +also include a brief, self-contained code example that demonstrates the problem. + +## Requesting features + +To request a new feature, please use the [discussions tab](https://github.com/pyccel/pyccel/discussions/categories/ideas). +This allows us to discuss the best implementation in the target languages before creating an issue to work on. + +## Documentation + +Reviewing the existing documentation is a great way to help out! +We need to ensure that it is up-to-date and relevant, and that the examples are clear, relevant, and work correctly. + +Missing documentation can also be added to improve the existing docs. + +Please open a pull request to add or change anything in the docs. + +## Reviewing a pull request + +There are many pull requests and reviewing them is a great way to help things get to the master branch faster. +This is also a really good way to get to grips with the code base as you will see examples of many different areas of the code. +It's incredibly helpful to have pull requests go through as many reviews as possible, to make sure the code change makes sense, is documented, and is efficient and clear. +As the clarity is subjective, more eyes can only improve the code base. +The review process is described in the [developer docs](https://github.com/pyccel/pyccel/blob/master/developer_docs/review_process.md), keep an eye out for PRs tagged `needs_initial_review`. + +## Contributing code + +Before contributing we strongly recommend you check out the [developer docs](https://github.com/pyccel/pyccel/tree/master/developer_docs). +We try to flag `good-first-issue`s. +These are either issues which can be fixed by following the example provided by a similar solution which is already implemented, or issues which only concern one of the pyccel [stages](https://github.com/pyccel/pyccel/blob/master/developer_docs/overview.md). diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index f2c83bb246..cff2bddd9d 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -7,25 +7,26 @@ assignees: '' --- -**Describe the bug** +### Describe the bug A clear and concise description of what the bug is. -**To Reproduce** +### To Reproduce Provide code to reproduce the behavior: ```python Code here ``` +### Error details Provide the generated code, or the error message: ```bash/fortran/c Translated code here ``` -**Expected behavior** +### Expected behavior A clear and concise description of what you expected to happen. -**Language** +### Language Please specify which language the python code is translated to (Fortran by default) -**Additional context** +### Additional context Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md new file mode 100644 index 0000000000..c549bc5621 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.md @@ -0,0 +1,10 @@ +--- +name: Documentation issue +about: Request additional documentation or report documentation errors +title: '' +labels: documentation +assignees: '' + +--- + +A description of the requested documentation changes. diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md index 1fd5ce674a..bd9afcb48b 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.md +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -7,14 +7,20 @@ assignees: '' --- -**Describe the bug** +### Relevant Discussion + +Non-trivial features require consideration to find the best implementation in the target languages. +This conversation should take place in the [discussion tab](https://github.com/pyccel/pyccel/discussions/categories/q-a). +Please link to the discussion where the proposed feature was investigated. + +### Describe the feature A clear and concise description of what you would like to be implemented. -**Test Code** +### Test Code Provide code which does not currently work but which should do when this issue is fixed: ```python Code here ``` -**Language** -If appropriate, please specify which language the python code is translated to (Fortran by default) +### Proposed Solution +Briefly summarise how you expect this feature to be implemented diff --git a/.github/ISSUE_TEMPLATE/installation-problem.md b/.github/ISSUE_TEMPLATE/installation-problem.md new file mode 100644 index 0000000000..6f6342c4c2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/installation-problem.md @@ -0,0 +1,24 @@ +--- +name: Installation problem +about: Report an installation problem +title: '' +labels: bug +assignees: '' + +--- + +### Relevant Discussion + +Most installation problems are not directly caused by pyccel, but are due to dependencies. Please link to the discussion in the [discussion tab](https://github.com/pyccel/pyccel/discussions/categories/q-a) where the problem was investigated. + +### Description + +A clear and concise description of the problem encountered. + +### Environment information + +Python version : + +OS version : + +Versions for any relevant dependencies: diff --git a/.github/actions/coverage_collection/action.yml b/.github/actions/coverage_collection/action.yml index 03d1b2cdfd..e32b82b317 100644 --- a/.github/actions/coverage_collection/action.yml +++ b/.github/actions/coverage_collection/action.yml @@ -6,7 +6,6 @@ runs: - name: Coverage collection run: | coverage combine - coverage xml rm ${SITE_DIR}/pyccel_cov.pth shell: bash diff --git a/.github/actions/coverage_install/action.yml b/.github/actions/coverage_install/action.yml index 44ef760dcb..fb55f29c45 100644 --- a/.github/actions/coverage_install/action.yml +++ b/.github/actions/coverage_install/action.yml @@ -10,7 +10,7 @@ runs: - name: Directory Creation run: | INSTALL_DIR=$(cd tests; python -c "import pyccel; print(pyccel.__path__[0])") - SITE_DIR=$(python -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])') + SITE_DIR=$(dirname ${INSTALL_DIR}) echo -e "import coverage; coverage.process_startup()" > ${SITE_DIR}/pyccel_cov.pth echo -e "[run]\nparallel = True\nsource = ${INSTALL_DIR}\ndata_file = $(pwd)/.coverage\n[report]\ninclude = ${INSTALL_DIR}/*\n[xml]\noutput = cobertura.xml" > .coveragerc echo "SITE_DIR=${SITE_DIR}" >> $GITHUB_ENV diff --git a/.github/actions/linux_install/action.yml b/.github/actions/linux_install/action.yml index 8fb5cd8505..0ef9a69b8e 100644 --- a/.github/actions/linux_install/action.yml +++ b/.github/actions/linux_install/action.yml @@ -9,22 +9,22 @@ runs: shell: bash - name: Install fortran run: - sudo apt-get install gfortran + sudo apt-get install -y gfortran shell: bash - name: Install LaPack run: - sudo apt-get install libblas-dev liblapack-dev + sudo apt-get install -y libblas-dev liblapack-dev shell: bash - name: Install MPI run: | - sudo apt-get install libopenmpi-dev openmpi-bin + sudo apt-get install -y libopenmpi-dev openmpi-bin echo "MPI_OPTS=--oversubscribe" >> $GITHUB_ENV shell: bash - name: Install OpenMP run: - sudo apt-get install libomp-dev libomp5 + sudo apt-get install -y libomp-dev libomp5 shell: bash - name: Install Valgrind run: - sudo apt-get install valgrind + sudo apt-get install -y valgrind shell: bash diff --git a/.github/actions/macos_install/action.yml b/.github/actions/macos_install/action.yml index 941d6920d6..212c79d24c 100644 --- a/.github/actions/macos_install/action.yml +++ b/.github/actions/macos_install/action.yml @@ -8,7 +8,8 @@ runs: brew install open-mpi brew install libomp if [[ ! -f "/usr/local/bin/gfortran" ]]; then - ln -s /usr/local/bin/gfortran-10 /usr/local/bin/gfortran + gfort=$(ls /usr/local/bin/gfortran-* | tail -n 1) + ln -s ${gfort} /usr/local/bin/gfortran fi echo "MPI_OPTS=--oversubscribe" >> $GITHUB_ENV shell: bash diff --git a/.github/actions/pytest_parallel/action.yml b/.github/actions/pytest_parallel/action.yml index 74a9652afe..c7c77d99c7 100644 --- a/.github/actions/pytest_parallel/action.yml +++ b/.github/actions/pytest_parallel/action.yml @@ -10,8 +10,8 @@ runs: steps: - name: Test with pytest run: | - mpiexec -n 4 ${MPI_OPTS} python -m pytest epyccel/test_parallel_epyccel.py -v -m parallel -rx - #mpiexec -n 4 ${MPI_OPTS} python -m pytest epyccel -v -m parallel -rx + mpiexec -n 4 ${MPI_OPTS} python -m pytest epyccel/test_parallel_epyccel.py -v -m parallel -rXx + #mpiexec -n 4 ${MPI_OPTS} python -m pytest epyccel -v -m parallel -rXx shell: ${{ inputs.shell_cmd }} working-directory: ./tests diff --git a/.github/actions/pytest_run/action.yml b/.github/actions/pytest_run/action.yml index 6bec971da6..73a3e53080 100644 --- a/.github/actions/pytest_run/action.yml +++ b/.github/actions/pytest_run/action.yml @@ -12,13 +12,19 @@ runs: - name: Test with pytest run: | which python - python -m pytest -n auto -rX -m "not (parallel or xdist_incompatible) and c" --ignore=symbolic --ignore=ndarrays - python -m pytest -rX -m "xdist_incompatible and not parallel and c" --ignore=symbolic --ignore=ndarrays + python -m pytest -n auto -rXx -v -m "not (parallel or xdist_incompatible) and c" --ignore=symbolic --ignore=ndarrays + if [ -n "${SITE_DIR}" ]; then + echo "Touching" + # Test ndarray folder update (requires parallel tests to avoid clean) + touch ${SITE_DIR}/pyccel/stdlib/cwrapper/cwrapper.h + python -m pytest -n auto -rXx -v -m c -k test_array_int32_1d_scalar epyccel/test_arrays.py + fi + python -m pytest -rXx -m "xdist_incompatible and not parallel and c" --ignore=symbolic --ignore=ndarrays pyccel-clean - python -m pytest -n auto -rX -m "not (parallel or xdist_incompatible) and not (c or python)" --ignore=symbolic --ignore=ndarrays - python -m pytest -rX -m "xdist_incompatible and not parallel and not (c or python)" --ignore=symbolic --ignore=ndarrays + python -m pytest -n auto -rXx -m "not (parallel or xdist_incompatible) and not (c or python or ccuda)" --ignore=symbolic --ignore=ndarrays + python -m pytest -rXx -m "xdist_incompatible and not parallel and not (c or python or ccuda)" --ignore=symbolic --ignore=ndarrays pyccel-clean - python -m pytest ndarrays/ -rX + python -m pytest ndarrays/ -rXx pyccel-clean shell: ${{ inputs.shell_cmd }} working-directory: ./tests diff --git a/.github/actions/pytest_run_cuda/action.yml b/.github/actions/pytest_run_cuda/action.yml new file mode 100644 index 0000000000..59c8b5b916 --- /dev/null +++ b/.github/actions/pytest_run_cuda/action.yml @@ -0,0 +1,17 @@ +name: 'Pyccel pytest commands generating Ccuda' +inputs: + shell_cmd: + description: 'Specifies the shell command (different for anaconda)' + required: false + default: "bash" + +runs: + using: "composite" + steps: + - name: Ccuda tests with pytest + run: | + # Catch exit 5 (no tests found) + sh -c 'python -m pytest -n auto -rx -m "not (parallel or xdist_incompatible) and ccuda" --ignore=tests/symbolic --ignore=tests/ndarrays; ret=$?; [ $ret = 5 ] && exit 0 || exit $ret' + pyccel-clean + shell: ${{ inputs.shell_cmd }} + working-directory: ./ diff --git a/.github/actions/pytest_run_python/action.yml b/.github/actions/pytest_run_python/action.yml index f57507773e..842fd2eaf6 100644 --- a/.github/actions/pytest_run_python/action.yml +++ b/.github/actions/pytest_run_python/action.yml @@ -10,8 +10,8 @@ runs: steps: - name: Python tests with pytest run: | - python -m pytest -n auto -rx -m "not (parallel or xdist_incompatible) and python" --ignore=symbolic --ignore=ndarrays - python -m pytest -rx -m "xdist_incompatible and not parallel and python" --ignore=symbolic --ignore=ndarrays + python -m pytest -n auto -rXx -m "not (parallel or xdist_incompatible) and python" --ignore=symbolic --ignore=ndarrays + python -m pytest -rXx -m "xdist_incompatible and not parallel and python" --ignore=symbolic --ignore=ndarrays pyccel-clean shell: ${{ inputs.shell_cmd }} working-directory: ./tests diff --git a/.github/actions/python_install/action.yml b/.github/actions/python_install/action.yml new file mode 100644 index 0000000000..f9b720e3e1 --- /dev/null +++ b/.github/actions/python_install/action.yml @@ -0,0 +1,17 @@ +name: 'Python installation commands' + +runs: + using: "composite" + steps: + - name: Install python + run: + sudo apt-get -y install python3-dev + shell: bash + - name: python as python3 + run: + sudo apt-get -y install python-is-python3 + shell: bash + - name: Install Pip + run: + sudo apt-get -y install python3-pip + shell: bash diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index a4a2f986c8..fb1c717218 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -2,7 +2,7 @@ name: Pyccel tests on: pull_request: - branches: [ master ] + branches: [ master, cuda_main, cuda_devel ] jobs: Linux: @@ -10,9 +10,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python 3.7 - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: 3.7 - name: Install dependencies @@ -32,21 +32,21 @@ jobs: - name: Collect coverage information continue-on-error: True uses: ./.github/actions/coverage_collection - - name: Run codacy-coverage-reporter - uses: codacy/codacy-coverage-reporter-action@master - continue-on-error: True + - name: Save code coverage report + uses: actions/upload-artifact@v3 with: - project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} - coverage-reports: cobertura.xml + name: coverage-artifact + path: .coverage + retention-days: 1 Windows: runs-on: windows-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup Python 3.9 - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: # The second most recent version is used as # setup-python installs the most recent patch @@ -73,9 +73,9 @@ jobs: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python 3.10 - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: '3.10' - name: Install dependencies @@ -89,22 +89,120 @@ jobs: - name: Parallel tests with pytest uses: ./.github/actions/pytest_parallel - Linter: + Cuda: + + runs-on: ubuntu-20.04 + container: nvidia/cuda:11.7.1-devel-ubuntu20.04 + if: github.event.pull_request.base.ref != 'master' + steps: + - uses: actions/checkout@v2 + - name: Prepare docker + run: | + apt update && apt install sudo + TZ=Europe/France + ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata + shell: bash + - name: CUDA Version + run: nvcc --version # cuda install check + - name: Install dependencies + uses: ./.github/actions/linux_install + - name: Install python (setup-python action doesn't work with containers) + uses: ./.github/actions/python_install + - name: Install python dependencies + uses: ./.github/actions/pip_installation + - name: Coverage install + uses: ./.github/actions/coverage_install + - name: Ccuda tests with pytest + uses: ./.github/actions/pytest_run_cuda + - name: Collect coverage information + continue-on-error: True + uses: ./.github/actions/coverage_collection + - name: Save code coverage report + uses: actions/upload-artifact@v3 + with: + name: cuda-coverage-artifact + path: .coverage + retention-days: 1 + + CoverageCollection: runs-on: ubuntu-latest + needs: [Linux, Cuda] + if: ${{ always() && needs.Linux.result == 'success' && needs.Cuda.result != 'failure' }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python 3.7 - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 + with: + python-version: 3.7 + - name: Install dependencies + uses: ./.github/actions/linux_install + - name: Install python dependencies + uses: ./.github/actions/pip_installation + - name: Coverage install + uses: ./.github/actions/coverage_install + - name: Collect coverage information + uses: actions/download-artifact@v3 + with: + name: coverage-artifact + - name: Rename coverage file + run: mv .coverage .coverage.linux + - name: Collect coverage information + uses: actions/download-artifact@v3 + if: needs.Cuda.result == 'success' + with: + name: cuda-coverage-artifact + - name: Rename coverage file + if: needs.Cuda.result == 'success' + run: mv .coverage .coverage.cuda + - name: Generate coverage report + run: | + coverage combine + coverage xml + - name: Run codacy-coverage-reporter + uses: codacy/codacy-coverage-reporter-action@master + continue-on-error: True + with: + project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} + coverage-reports: cobertura.xml + - name: Save code coverage xml report + uses: actions/upload-artifact@v3 + with: + name: coverage-artifact-xml + path: cobertura.xml + retention-days: 1 + + CoverageChecker: + + runs-on: ubuntu-latest + needs: [CoverageCollection] + if: ${{ always() && needs.CoverageCollection.result == 'success' && needs.Cuda.result != 'failure' }} + + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.7 + uses: actions/setup-python@v4 with: python-version: 3.7 - name: Install python dependencies run: | python -m pip install --upgrade pip - python -m pip install pylint + python -m pip install defusedxml + shell: bash + - name: Collect coverage information + uses: actions/download-artifact@v3 + with: + name: coverage-artifact-xml + - name: Collect diff information + run: | + BASE_BRANCH=$GITHUB_BASE_REF + git fetch + git diff origin/${BASE_BRANCH}..HEAD --no-indent-heuristic --unified=0 --output=pull_diff.txt --no-color + ls shell: bash - - name: Pylint + - name: Check coverage run: | - python -m pylint --rcfile=.pylintrc pyccel/parser/semantic.py + python ci_tools/check_new_coverage.py pull_diff.txt cobertura.xml $GITHUB_EVENT_PATH $GITHUB_STEP_SUMMARY shell: bash diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 789cbab4ec..72b927ff70 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -11,30 +11,20 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.7 - uses: actions/setup-python@v3 + - name: Set up ssh + uses: webfactory/ssh-agent@v0.7.0 with: - python-version: 3.7 - - name: Install dependencies - uses: ./.github/actions/linux_install - - name: Install python dependencies - uses: ./.github/actions/pip_installation - - name: Install python benchmark dependencies + ssh-private-key: ${{ secrets.SSH_DEPLOY_KEY }} + - name: Clone pyccel-benchmarks repository + working-directory: ../ run: | - python -m pip install pythran - python -m pip install numba - python -m pip install pyperf - - name: Benchmark + git clone git@github.com:pyccel/pyccel-benchmarks.git + - name: Push results to pyccel-benchmarks + working-directory: ../pyccel-benchmarks run: | - python benchmarks/run_benchmark.py --pyperf --verbose - echo "# Performance Comparison (as of $(date))" > performance.md - cat bench.out >> performance.md - shell: bash - working-directory: ./. - - name: Add & Commit - uses: EndBug/add-and-commit@v9.0.0 - with: - message: 'Update performance comparison' - add: 'performance.md' - default_author: github_actions + export GIT_AUTHOR_NAME="Pyccel/pyccel" + export GIT_AUTHOR_EMAIL="41898282+github-actions[bot]@users.noreply.github.com" + git config user.email ${GIT_AUTHOR_EMAIL} + git config user.name ${GIT_AUTHOR_NAME} + git commit --allow-empty -m "Benchmark of pyccel/pyccel@${GITHUB_SHA}" + git push diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000000..827b8eece6 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,24 @@ +name: Python Linting +on: + pull_request: + branches: [ master, cuda_main, cuda_devel ] + +jobs: + Linter: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.7 + uses: actions/setup-python@v4 + with: + python-version: 3.7 + - name: Install python dependencies + run: | + python -m pip install --upgrade pip + python -m pip install pylint + shell: bash + - name: Pylint + run: | + python -m pylint --rcfile=.pylintrc pyccel/parser/semantic.py > $GITHUB_STEP_SUMMARY + shell: bash diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index afb51c6f3d..dd89a52af8 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -15,9 +15,9 @@ jobs: python-version: [3.7, 3.8, 3.9, '3.10'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -47,9 +47,9 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup Python 3.7 - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: # The second most recent version is used as # setup-python installs the most recent patch @@ -73,9 +73,9 @@ jobs: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python 3.9 - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.9 - name: Install dependencies @@ -94,9 +94,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python 3.7 - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.7 - name: Install dependencies @@ -113,9 +113,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python 3.7 - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.7 - name: Install dependencies @@ -132,9 +132,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python 3.7 - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.7 - name: Install dependencies @@ -152,7 +152,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install dependencies uses: ./.github/actions/linux_install - uses: conda-incubator/setup-miniconda@v2 @@ -180,7 +180,7 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: conda-incubator/setup-miniconda@v2 with: auto-update-conda: true diff --git a/.github/workflows/pyccel_lint.yml b/.github/workflows/pyccel_lint.yml new file mode 100644 index 0000000000..fab5dda618 --- /dev/null +++ b/.github/workflows/pyccel_lint.yml @@ -0,0 +1,23 @@ +name: Pyccel Linting +on: + pull_request: + branches: [ master, cuda_main, cuda_devel ] + +jobs: + Pyccel-Linter: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.7 + uses: actions/setup-python@v4 + with: + python-version: 3.7 + - name: Install dependencies + uses: ./.github/actions/linux_install + - name: Install python dependencies + uses: ./.github/actions/pip_installation + - name: Lint + run: | + python ci_tools/check_slots.py $GITHUB_STEP_SUMMARY + shell: bash diff --git a/.github/workflows/spelling.yml b/.github/workflows/spelling.yml new file mode 100644 index 0000000000..b6c754efdc --- /dev/null +++ b/.github/workflows/spelling.yml @@ -0,0 +1,29 @@ +name: Spellcheck Action +on: + pull_request: + branches: [ master, cuda_main, cuda_devel ] + +jobs: + Spelling: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.7 + uses: actions/setup-python@v4 + with: + python-version: 3.7 + - name: Install dependencies + run: | + python -m pip install --upgrade pip setuptools + python -m pip install pyspelling + python -m pip install pymdown-extensions + # Install any additional libraries required: additional plugins, documentation building libraries, etc. + - name: Install Aspell + run: | + sudo apt-get install aspell aspell-en + - name: Spell check + run: | + python -m pyspelling > misspellings.txt || true + cat misspellings.txt + python ci_tools/summarise_pyspelling.py misspellings.txt $GITHUB_STEP_SUMMARY + diff --git a/.pylintrc b/.pylintrc index 6e9e563490..2e79ec9299 100644 --- a/.pylintrc +++ b/.pylintrc @@ -99,7 +99,8 @@ disable=invalid-name, redundant-keyword-arg, duplicate-code, unused-wildcard-import, # Raised by everything not used in pyccel.errors.messages when using 'from pyccel.errors.messages import *' - isinstance-second-argument-not-valid-type # prevents doing isinstance(a, acceptable_iterable_types) + isinstance-second-argument-not-valid-type, # prevents doing isinstance(a, acceptable_iterable_types) + django-not-configured # Enable the message, report, category or checker with the given id(s). You can diff --git a/.pyspelling.yml b/.pyspelling.yml new file mode 100644 index 0000000000..50acd593cf --- /dev/null +++ b/.pyspelling.yml @@ -0,0 +1,26 @@ +matrix: +- name: markdown + sources: + - README.md + - tutorial/*.md + - developer_docs/*.md + aspell: + lang: en + d: en_GB + dictionary: + wordlists: + - .dict_custom.txt + encoding: utf-8 + output: build/dictionary/python.dic + pipeline: + - pyspelling.filters.markdown: + markdown_extensions: + - pymdownx.superfences: + - pymdownx.magiclink: + - pyspelling.filters.html: + comments: false + ignores: + - code + - pre + - a + default_encoding: utf-8 diff --git a/README.md b/README.md index db3f160020..01c34305d8 100644 --- a/README.md +++ b/README.md @@ -16,12 +16,14 @@ The aim of **Pyccel** is to provide a simple way to generate automatically, para Pyccel comes with a selection of **extensions** allowing you to convert calls to some specific Python packages to Fortran/C. The following packages will be covered (partially): -- numpy -- scipy -- mpi4py (not available yet) -- h5py (not available yet) +- `numpy` +- `scipy` +- `mpi4py` (not available yet) +- `h5py` (not available yet) -Pyccel's acceleration capabilities lead to much faster code. A small speed comparison of Python vs Pyccel or other tools can be found in the [performance](./performance.md) file. +Pyccel's acceleration capabilities lead to much faster code. Comparisons of Python vs Pyccel or other tools can be found in the [benchmarks](https://github.com/pyccel/pyccel-benchmarks) repository. +The results for the master branch currently show the following performance on python 3.10: +![Pyccel execution times for master branch](https://github.com/pyccel/pyccel-benchmarks/blob/main/version_specific_results/devel_performance_310_execution.svg) If you are eager to try Pyccel out, we recommend reading our [quick-start guide](./tutorial/quickstart.md) @@ -40,7 +42,7 @@ If you are eager to try Pyccel out, we recommend reading our [quick-start guide] - [Windows](#Windows) - [Installation](#Installation) - - [From PyPi](#From-PyPi) + - [From PyPI](#From-PyPI) - [From sources](#From-sources) - [On a read-only system](#On-a-read-only-system) @@ -70,7 +72,7 @@ If you are eager to try Pyccel out, we recommend reading our [quick-start guide] - Supported libraries/APIs - [OpenMP](./tutorial/openmp.md) - - [Numpy](./tutorial/numpy-functions.md) + - [NumPy](./tutorial/numpy-functions.md) ## Pyccel Installation Methods @@ -80,8 +82,8 @@ Some advanced features of Pyccel require additional non-Python libraries to be i Alternatively, Pyccel can be deployed through a **Linux Docker image** that contains all dependencies, and which can be setup with any version of Pyccel. For more information, please read the section on [Pyccel container images](#Pyccel-Container-Images). -It is possible to use pyccel with anaconda but this is generally not advised as anaconda modifies paths used for finding executables, shared libraries and other objects. -Support is provided for anaconda on linux/macos. +It is possible to use Pyccel with anaconda but this is generally not advised as anaconda modifies paths used for finding executables, shared libraries and other objects. +Support is provided for anaconda on linux/macOS. On Windows support is limited to examples which do not use external libraries. This is because we do not know of a way to reliably avoid [DLL hell](https://en.wikipedia.org/wiki/DLL_Hell). @@ -116,7 +118,7 @@ Finally, Pyccel supports distributed-memory parallel programming through the Mes We recommend using GFortran/GCC and Open-MPI. -Pyccel also depends on several Python3 packages, which are automatically downloaded by pip, the Python Package Installer, during the installation process. In addition to these, unit tests require the _scipy_, _mpi4py_, _pytest_ and _coverage_ packages, while building the documentation requires [Sphinx](http://www.sphinx-doc.org/). +Pyccel also depends on several Python3 packages, which are automatically downloaded by pip, the Python Package Installer, during the installation process. In addition to these, unit tests require additional packages which are installed as optional dependencies with pip, while building the documentation requires [Sphinx](http://www.sphinx-doc.org/). ### Linux Debian-Ubuntu-Mint @@ -206,7 +208,7 @@ msiexec //i msmpisdk.msi At this point, close and reopen your terminal to refresh all environment variables! -In Administrator git-bash, generate mpi.mod for gfortran according to : +In Administrator git-bash, generate `mpi.mod` for GFortran according to : ```sh cd "$MSMPI_INC" @@ -216,7 +218,7 @@ gfortran -c -D_WIN64 -D INT_PTR_KIND\(\)=8 -fno-range-check mpi.f90 cd - ``` -Generate static libmsmpi.a from msmpi.dll: +Generate static `libmsmpi.a` from `msmpi.dll`: ```sh cd "$MSMPI_LIB64" @@ -252,7 +254,7 @@ echo "import os; os.add_dll_directory('C://ProgramData/chocolatey/lib/mingw/tool On Windows and/or Anaconda Python, use `pip` instead of `pip3` for the Installation of Pyccel below. -### From PyPi +### From PyPI Simply run, for a user-specific installation: @@ -283,10 +285,10 @@ for a system-wide installation. ```sh git clone git@github.com:pyccel/pyccel.git cd pyccel - pip3 install --user -e . + pip3 install --user -e .[test] ``` -this will install a _python_ library **pyccel** and a _binary_ called **pyccel**. +this will install a _python_ library **Pyccel** and a _binary_ called **`pyccel`**. Any required Python packages will be installed automatically from PyPI. ### On a read-only system @@ -298,42 +300,33 @@ sudo pyccel-init This step is necessary in order to [pickle header files](./tutorial/header-files.md#Pickling-header-files). If this command is not run then Pyccel will still run correctly but may be slower when using [OpenMP](./tutorial/openmp.md) or other supported external packages. -A warning, reminding the user to execute this command, will be printed to the screen when pyccelizing files which rely on these packages if the pickling step has not been executed. +A warning, reminding the user to execute this command, will be printed to the screen when pyccelising files which rely on these packages if the pickling step has not been executed. ## Additional packages In order to run the unit tests and to get a coverage report, a few additional Python packages should be installed: ```sh -pip3 install --user scipy -pip3 install --user mpi4py -pip3 install --user tblib -pip3 install --user pytest -pip3 install --user astunparse -pip3 install --user coverage +pip install --user -e .[test] ``` -Most of the unit tests can also be run in parallel. This can be done by installing one additional package: - -```sh -pip3 install --user pytest-xdist -``` +Most of the unit tests can also be run in parallel. ## Testing -To test your Pyccel installation please run the script _tests/run\_tests\_py3.sh_ (Unix), or _tests/run\_tests.bat_ (Windows). +To test your Pyccel installation please run the script `tests/run\_tests\_py3.sh` (Unix), or `tests/run\_tests.bat` (Windows). -Continuous testing runs on github actions: +Continuous testing runs on GitHub actions: ## Pyccel Container Images -Pyccel container images are available through both Docker Hub (docker.io) and the GitHub Container Registry (ghcr.io). +Pyccel container images are available through both Docker Hub () and the GitHub Container Registry (). The images: -- are based on ubuntu:latest -- use distro packaged python3, gcc, gfortran, blas and openmpi -- support all pyccel releases except the legacy "0.1" +- are based on `ubuntu:latest` +- use distro packaged python3, GCC, GFortran, BLAS and OpenMPI +- support all Pyccel releases except the legacy "0.1" Image tags match Pyccel releases. @@ -347,7 +340,7 @@ docker run -it -v $PWD:/data:rw pyccel/pyccel:v1.0.0 bash ``` If you are using SELinux, you will need to set the right context for your host based volume. -Alternatively you may have docker or podman set the context using -v $PWD:/data:rwz instead of -v $PWD:/data:rw . +Alternatively you may have docker or podman set the context using `-v $PWD:/data:rwz` instead of `-v $PWD:/data:rw` . ## Developer Documentation diff --git a/ci_tools/check_new_coverage.py b/ci_tools/check_new_coverage.py new file mode 100644 index 0000000000..53573fe5e3 --- /dev/null +++ b/ci_tools/check_new_coverage.py @@ -0,0 +1,31 @@ +""" Script to check that all new lines in the python files in the pyccel/ code folder are used in the tests +""" +import json +import argparse +from git_evaluation_tools import get_diff_as_json +import coverage_analysis_tools as cov + +parser = argparse.ArgumentParser(description='Check that all new lines in the python files in the pyccel/ code folder are used in the tests') +parser.add_argument('diffFile', metavar='diffFile', type=str, + help='File containing the git diff output') +parser.add_argument('coverageFile', metavar='coverageFile', type=str, + help='File containing the coverage xml output') +parser.add_argument('gitEvent', metavar='gitEvent', type=str, + help='File containing the json description of the triggering event') +parser.add_argument('output', metavar='output', type=str, + help='File where the markdown output will be printed') + +args = parser.parse_args() + +diff = get_diff_as_json(args.diffFile) +untested, file_contents = cov.get_untested_lines(args.coverageFile) + +new_untested = cov.allow_untested_error_calls(cov.compare_coverage_to_diff(untested, diff)) + +with open(args.gitEvent, encoding="utf-8") as pr_data_file: + pr_data = json.load(pr_data_file) + +cov.print_markdown_summary(new_untested, file_contents, pr_data["pull_request"]["head"]["sha"], args.output) + +cov.show_results(new_untested) + diff --git a/ci_tools/check_slots.py b/ci_tools/check_slots.py new file mode 100644 index 0000000000..b4b7287943 --- /dev/null +++ b/ci_tools/check_slots.py @@ -0,0 +1,109 @@ +""" Script to check that Pyccel coding conventions are correctly followed in the AST +""" +import argparse +import importlib +import inspect +import os +import sys +from pyccel import ast +from pyccel.ast.basic import Basic, PyccelAstNode, ScopedNode + +parser = argparse.ArgumentParser(description='Check that all new lines in the python files in the pyccel/ code folder are used in the tests') +parser.add_argument('output', metavar='output', type=str, + help='File where the markdown output will be printed') + +args = parser.parse_args() + +# Get ast modules +ast_folder = os.path.dirname(ast.__file__) +ast_modules = [mod[:-3] for mod in os.listdir(ast_folder) if mod != '__init__.py' and mod.endswith('.py')] + +# Prepare error collection +missing_all = [] +non_alphabetical_all = [] +missing_slots = [] +overridden_slots = [] +missing_attribute_nodes = [] +missing_from_all = [] + +for mod_name in ast_modules: + mod = importlib.import_module('pyccel.ast.'+mod_name) + all_attr = getattr(mod, '__all__', None) + if all_attr: + sorted_all = list(all_attr) + sorted_all.sort() + if sorted_all != list(all_attr): + non_alphabetical_all.append(mod_name) + else: + missing_all.append(mod_name) + + classes = inspect.getmembers(mod, inspect.isclass) + for cls_name, cls_obj in classes: + if inspect.getmodule(cls_obj) is not mod: + continue + super_classes = cls_obj.mro()[1:] + if '__slots__' not in cls_obj.__dict__: + missing_slots.append(f"{mod_name}.{cls_name}") + else: + slots = cls_obj.__slots__ + for c in super_classes: + if '__slots__' not in c.__dict__: + continue + elif any(s in slots for s in c.__slots__): + overridden_slots.append(f'Slot values are overwritten between `{mod_name}.{cls_name}` and `{c.__name__}`') + + if Basic in super_classes: + if cls_obj not in (PyccelAstNode, ScopedNode) and not isinstance(cls_obj._attribute_nodes, tuple): #pylint: disable=W0212 + missing_attribute_nodes.append(f"{mod_name}.{cls_name}") + + if all_attr and cls_name not in all_attr: + missing_from_all.append(f"{mod_name}.{cls_name}") + +print("Missing __all__") +print(missing_all) +print("__all__ non-alphabetical") +print(non_alphabetical_all) +print("Missing __slots__") +print(missing_slots) +print("Missing _attribute_nodes") +print(missing_attribute_nodes) +print("Not in __all__") +print(missing_from_all) +print("Misused slots") +print(overridden_slots) + +with open(args.output, "w", encoding="utf-8") as out: + # Report error + if missing_all: + print("## Missing `__all__`", file=out) + for f in missing_all: + print(f"- `pyccel.ast.{f}`", file=out) + if non_alphabetical_all: + print("## Non-alphabetical `__all__`", file=out) + for f in non_alphabetical_all: + print(f"- `pyccel.ast.{f}`", file=out) + if missing_from_all: + print("## Classes missing from `__all__`", file=out) + for f in missing_from_all: + print(f"- `pyccel.ast.{f}`", file=out) + if missing_slots: + print("## Classes with no `__slots__`", file=out) + for f in missing_slots: + print(f"- `pyccel.ast.{f}`", file=out) + if missing_attribute_nodes: + print("## Classes with no `_attribute_nodes`", file=out) + for f in missing_attribute_nodes: + print(f"- `pyccel.ast.{f}`", file=out) + if overridden_slots: + print("## Misused slots", file=out) + for o in overridden_slots: + print("- ", o, file=out) + +failure = (bool(missing_all) or # bool(non_alphabetical_all) or + bool(missing_slots) or bool(missing_attribute_nodes) or + bool(overridden_slots)) + +if failure: + sys.exit(1) +else: + sys.exit(0) diff --git a/ci_tools/coverage_analysis_tools.py b/ci_tools/coverage_analysis_tools.py new file mode 100644 index 0000000000..346e78fe27 --- /dev/null +++ b/ci_tools/coverage_analysis_tools.py @@ -0,0 +1,175 @@ +""" Functions for comparing coverage output and git diff output +""" +import os +import sys +import defusedxml.ElementTree as ET + +def get_untested_lines(coverage_filename): + """ + Parse a coverage xml file and return a dictionary containing the files and lines + which are untested + + Parameters + ---------- + coverage_filename : str + The name of the xml file containing the coverage information + + Returns + ------- + no_coverage : dict + A dictionary whose keys are the files in pyccel + and whose values are lists containing the line numbers + where coverage is lacking in that file + content_lines : dict + A dictionary whose keys are the files in pyccel + and whose values are lists containing the line numbers + where a python command starts (this excludes comments, + empty lines, and lines which are continuations of + previous lines) + """ + tree = ET.parse(coverage_filename) + root = tree.getroot() + + content_lines = {} + no_coverage = {} + + for f in root.findall('.//class'): + filename = f.attrib['filename'] + lines = f.findall('lines')[0].findall('line') + all_lines = [int(l.attrib['number']) for l in lines] + untested_lines = [int(l.attrib['number']) for l in lines if l.attrib['hits'] == "0"] + no_coverage[os.path.join('pyccel',filename)] = untested_lines + content_lines[os.path.join('pyccel',filename)] = all_lines + + return no_coverage, content_lines + +def compare_coverage_to_diff(coverage, diff): + """ + Compare dictionaries containing coverage information and git + diff information to find untested lines which have been added + to the code base + + Parameters + ---------- + coverage : dict + A dictionary whose keys are the files in pyccel + and whose values are lists containing the line numbers + where coverage is lacking in that file + diff : dict + A dictionary whose keys are files which have been + changed in this branch and whose values are a dictionary. + The dictionary must contain a key 'addition' whose value + is a list containing the line numbers of lines which have + been changed/added + + Returns + ------- + untested : dict + A dictionary whose keys are the files in pyccel with + untested lines which have been added in this branch + and whose values are lists containing the line numbers + where coverage is lacking in that file + """ + untested = {} + for f,line_info in diff.items(): + if f not in coverage: + # Ignore non-python files or files in other directories + continue + new_lines = line_info['addition'] + untested_lines = coverage[f] + if any(n in untested_lines for n in new_lines): + untested[f] = [n for n in new_lines if n in untested_lines] + return untested + +def allow_untested_error_calls(untested): + """ + Takes a dictionary describing untested lines and returns an + equivalent dictionary without lines designed to raise errors + + Parameter + --------- + untested : dict + A dictionary whose keys are the files in pyccel with + untested lines which have been added in this branch + and whose values are lists containing the line numbers + where coverage is lacking in that file + + Returns + ------- + reduced_untested : dict + A dictionary which is a copy of the input dictionary + without the lines which express raise statements + """ + reduced_untested = {} + for f,line_nums in untested.items(): + with open(f, encoding="utf-8") as filename: + f_lines = filename.readlines() + untested_lines = [(i, f_lines[i-1].strip()) for i in line_nums] + lines = [i for i,l in untested_lines if not (l.startswith('raise ') or l.startswith('errors.report(') or l.startswith('return errors.report('))] + if len(lines): + reduced_untested[f] = lines + + return reduced_untested + +def print_markdown_summary(untested, content_lines, commit, output): + """ + Print the results neatly in markdown in a provided file + + Parameters + ---------- + untested : dict + Dictionary whose keys are the files in pyccel with untested + lines which have been added in this branch and whose values + are lists containing the line numbers where coverage is + lacking in that file + content_lines : dict + Dictionary whose keys are the files in pyccel and whose + values are lists containing the line numbers where python + commands begin + commit : str + The commit being tested + output : str + The file where the markdown summary should be printed + """ + if len(untested) == 0: + md_string = "## Congratulations! All new python code in the pyccel package is fully tested! :tada:" + else: + md_string = "## Warning! The new code is not run\n" + for f, lines in untested.items(): + md_string += f"### {f}\n" + line_indices = content_lines[f] + n_code_lines = len(line_indices) + n_untested = len(lines) + i = 0 + while i < n_untested: + start_line = lines[i] + j = line_indices.index(start_line) + while j < n_code_lines and i < n_untested and lines[i] == line_indices[j]: + i+=1 + j+=1 + if j < n_code_lines-1: + end_line = line_indices[j]-1 + else: + end_line = line_indices[j] + md_string += "https://github.com/pyccel/pyccel/blob/"+commit+"/"+f+f"#L{start_line}-L{end_line}\n" + + with open(output, "a", encoding="utf-8") as out_file: + print(md_string, file=out_file) + +def show_results(untested): + """ + Print the results and fail if coverage is lacking + + Parameters + ---------- + untested : dict + Dictionary whose keys are the files in pyccel with untested + lines which have been added in this branch and whose values + are lists containing the line numbers where coverage is + lacking in that file + """ + for f, lines in untested.items(): + print(f"In file {f} the following lines are untested : {lines}") + + if len(untested) != 0: + sys.exit(1) diff --git a/ci_tools/git_evaluation_tools.py b/ci_tools/git_evaluation_tools.py new file mode 100644 index 0000000000..4290ab4e9d --- /dev/null +++ b/ci_tools/git_evaluation_tools.py @@ -0,0 +1,88 @@ +""" Tools to help examine git information +""" + +def get_diff_as_json(filename): + """ + A function which converts the output of a reduced git diff call + to a dictionary that can be exported using json. + The diff call should use the argument `--unified=0` + + Parameters + ---------- + filename : str + The file where the diff was printed + + Returns + ------- + changes : dict + A dictionary whose keys are files which have been + changed in this branch and whose values are a dictionary. + The dictionary is itself a dictionary with the keys 'addition' + and 'deletion' whose values are lists containing the line + numbers of lines which have been changed/added (addition) or + changed/deleted (deletion) + """ + with open(filename, encoding="utf-8") as f: + lines = f.readlines() + + lines = [l.strip() for l in lines] + changes ={} + i = 0 + n = len(lines) + + current_file_name = None + current_file_additions = [] + current_file_deletions = [] + + while i < n: + l = lines[i] + if l.startswith("diff "): + if current_file_name: + changes[current_file_name] = {} + changes[current_file_name]['addition'] = current_file_additions + changes[current_file_name]['deletion'] = current_file_deletions + current_file_additions = [] + current_file_deletions = [] + current_file_name = l.split(' ')[3][2:] + i+=1 + elif l.startswith('@@'): + line_info = l.split('@@')[1].split() + for info in line_info: + key = info[0] + info = info[1:] + if ',' in info: + line_num, n_lines = [int(li) for li in info.split(',')] + else: + n_lines = 1 + line_num = int(info) + if key == '+': + insert_index = line_num + n_append = n_lines + elif key == '-': + delete_index = line_num + n_delete = n_lines + i+=1 + j=0 + while j'): + words.add(lines[i]) + i+=1 + + if filename in errors: + errors[filename].update(words) + else: + errors[filename] = words + +if errors: + all_words = set() + + with open(os.path.join(os.path.dirname(__file__),'..','.dict_custom.txt'), encoding="utf-8") as d: + internal_dict = [w.strip() for w in d.readlines()] + + with open(args.output, 'w', encoding="utf-8") as f: + print("There are misspelled words", file=f) + for name, words in errors.items(): + print("## `", name, "`", file=f) + for w in words: + suggestions = difflib.get_close_matches(w, internal_dict) + if suggestions: + print("- ", w, f" : Did you mean {w} -> {suggestions}", file=f) + else: + print("- ", w, file=f) + print(file=f) + all_words.update(words) + + print("These errors may be due to typos, capitalisation errors, or lack of quotes around code. If this is a false positive please add your word to `.dict_custom.txt`", file=f) + + sys.exit(1) +else: + sys.exit(0) diff --git a/developer_docs/how_to_solve_an_issue.md b/developer_docs/how_to_solve_an_issue.md index 3b68e9f166..0a6a12ebb4 100644 --- a/developer_docs/how_to_solve_an_issue.md +++ b/developer_docs/how_to_solve_an_issue.md @@ -10,7 +10,7 @@ To add a new function: - Add a class to represent the function. The class should go in the appropriate file in the [ast](../pyccel/ast) folder. This function will probably inherit from [PyccelInternalFunction](../pyccel/ast/internals.py) - Ensure the function is recognised in the semantic stage by adding it to the appropriate dictionary (see the function `builtin_function` and the dictionary `builtin_import_registery` in [ast/utilities.py](../pyccel/ast/utilities.py) - Add the print functions for the 3 languages -- Add tests in the folder tests/epyccel +- Add tests in the folder `tests/epyccel` ## Language Specific Bug Fixes diff --git a/developer_docs/order_docs.md b/developer_docs/order_docs.md new file mode 100644 index 0000000000..1d6a837d99 --- /dev/null +++ b/developer_docs/order_docs.md @@ -0,0 +1,468 @@ +# ndarrays memory layout (order) + +## Order in NumPy + +`order` is the parameter given to the `numpy.array` function in order to choose how a multi-dimensional array is stored in memory. +For both of the orders discussed here (`C` and `F`) the arrays are stored **contiguously** in memory, but they differ in how their entries are arranged. + +### Order C + +`order='C'` tells NumPy to store the array row by row (row-major). For example: + +```python +import numpy as np + +if __name__ == "__main__": + a = np.array([[1, 2, 3], + [4, 5, 6], + [7, 8, 9]], order='C') # order='C' is the default in numpy.array + print(a.ravel('K')) + ``` + +`array.ravel('k')` shows us how the array is stored in memory. +This Python script will output `[1 2 3 4 5 6 7 8 9]`; notice that the rows are stored one after the other. +This is the default behaviour in Python. + +### Order F + +`order='F'` on the other hand tells NumPy to store the array column by column (column-major). For example: + +```python +import numpy as np + +if __name__ == "__main__": + a = np.array([[1, 2, 3], + [4, 5, 6], + [7, 8, 9]], order='F') + print(a.ravel('K')) +``` + +This Python script will output `[1 4 7 2 5 8 3 6 9]`; notice that the columns are stored one after the other. + +### Printing and indexing in NumPy + +The `order` of a NumPy array does not affect the indexing or the printing: unlike the `transpose` operation, the `shape` of the array remains the same, and only the `strides` change. For example: + +```python +import numpy as np + +if __name__ == "__main__": + a = np.array([[1, 2], + [4, 5], + [7, 8]], order='F') + b = np.array([[1, 2], + [4, 5], + [7, 8]], order='C') + print(a.shape, a.strides) # output: (3, 2) (8, 24) + print(b.shape, b.strides) # output: (3, 2) (16, 8) + print(a) + # output:[[1 2] + # [4 5] + # [7 8]] + print(b) + # output:[[1 2] + # [4 5] + # [7 8]] + + print(a[2][1], a[0][0], a[1]) # output: 8 1 [4 5] + print(b[2][1], b[0][0], b[1]) # output: 8 1 [4 5] +``` + +`arr.strides` is the variable that helps us navigate the array (indexing/printing) by telling us how many bytes we have to skip in memory to move to the next position along a certain axis (dimension). For example for `memory_layout_of_a = [1 4 7 2 5 8]` and `strides_of_a = (8, 24)`, we have to skip 8 bytes (1 element for `int64`) to move to the next row, but 24 bytes (3 elements for `int64`) to get to the same position in the next column of `a`. +`a[2][1]` would give us `'8'`, using the `strides`: `2 * 8 + 1 * 24 = 40`, which means that in the flattened array, we would have to skip `40` bytes to get the value of `a[2][1]`, each element is 8 bytes, so we would have to skip `40 / 8 = 5` elements to get to `'8'` + +The order does however change how the user writes code. +With `order='C'` (as in C), the last dimension contains contiguous elements, whereas with `order='F'` (as in Fortran) the first dimension contains contiguous elements. +Fast code should index efficiently. +By this, we mean that the elements should be visited in the order in which they appear in memory. +For example here is the efficient indexing for 2D arrays: +```python +import numpy as np +if __name__ == "__main__": + a = np.array([[1, 2], + [4, 5], + [7, 8]], order='F') + b = np.array([[1, 2], + [4, 5], + [7, 8]], order='C') + for row in range(3): + for col in range(2): + b[row, col] = ... + for col in range(2): + for row in range(3): + b[row, col] = ... +``` + +## Pyccel's C code + +In Pyccel's C code, we aim to replicate NumPy's indexing/printing and memory layout conventions. + +### Ordering in C code + +Multidimensional arrays in `C` code are flattened into a one dimensional array, `strides` and `shape` are used to navigate this array (unlike NumPy, Pyccel's strides use 'number of elements' instead of 'number of bytes' as a unit) +While the `order_c ndarrays` only require a simple copy to be populated, `order_f` array creation requires slightly different steps. + +Example: + To translate the following: + + ```python + a = np.array([[1, 2, 3], [4, 5, 6]], order=?) + ``` + `order_c` creation +     1. allocate/create `order_c ndarray` +     2. copy values to `ndarray` + + `order_f` creation +     1. allocate/create temporary `order_c ndarray` +     2. copy values to temporary `ndarray` +     3. allocate/create `order_f ndarray` +     4. copy temporary `ndarray` elements to final `order_f ndarray` using `strides` and `shape`, this will create a column-major version of the temporary `order_c ndarray` + +One dimensional arrays require no order, since order would not change how they behave. + +### Indexing in C code + +For indexing the function `GET_ELEMENT(arr, type, ...)` is used, indexing does not change with `order` so that we can mirror NumPy's conventions. + +If we take the following 2D array as an example: +| | | | +|---|---|---| +| 1 | 2 | 3 | +| 4 | 5 | 6 | + +with `array.rows = 2` and `array.columns = 3`, `GET_ELEMENT(arr, int32, 0, 1)` which is equivalent to `arr[0][1]` would return `2` no matter the `order`. + +To loop efficiently in an `order_c ndarray`, we would do this: +```c +for (int row = 0; row < array.rows; ++i) +{ + for (int column = 0; column < array.columns; ++j) + { + GET_ELEMENT(array, int32, row, column) = ...; + } +} +``` + +For an `order_f ndarray` we would do this: + +```c +for (int column = 0; column < array.columns; ++i) +{ + for (int row = 0; row < array.rows; ++j) + { + GET_ELEMENT(array, int32, row, column) = ...; + } +} +``` + +### `order_c` array creation example + +To create an `order_c ndarray`, we simply copy the flattened data to our `ndarray`'s data placeholder that changes depending on the type. + +If the data is composed of scalars only (ex: `np.array([1, 2, 3])`), an `array_dummy` is created, before copying it to our destination `ndarray`. + +Example: + +```python +if __name__ == "__main__": + import numpy as np + a = np.array([[1, 2, 3], [4, 5, 6]]) +``` + +Would translate to: + +```c +int main() +{ + t_ndarray a = {.shape = NULL}; + a = array_create(2, (int64_t[]){INT64_C(2), INT64_C(3)}, nd_int64, false, order_c); + int64_t array_dummy[] = {INT64_C(1), INT64_C(2), INT64_C(3), INT64_C(4), INT64_C(5), INT64_C(6)}; // Creation of an array_dummy containing the scalars, notice the data is flattened + memcpy(a.nd_int64, array_dummy, 6 * a.type_size); // Copying from array_dummy to our ndarray 'a' + free_array(a); + return 0; +} +``` + +If the data is composed of at least one variable array (like `c` in the example below), we would use a series of copy operations to our `ndarray`. + +Example: + +```python +if __name__ == "__main__": + import numpy as np + a = np.array([1, 2, 3]) + b = np.array([4, 5, 6]) + c = np.array([a, [7, 8, 9], b]) +``` + +Would translate to this: + +```c +int main() +{ + t_ndarray a = {.shape = NULL}; + t_ndarray b = {.shape = NULL}; + t_ndarray c = {.shape = NULL}; + a = array_create(1, (int64_t[]){INT64_C(3)}, nd_int64, false, order_c); + int64_t array_dummy[] = {INT64_C(1), INT64_C(2), INT64_C(3)}; + memcpy(a.nd_int64, array_dummy, 3 * a.type_size); + b = array_create(1, (int64_t[]){INT64_C(3)}, nd_int64, false, order_c); + int64_t array_dummy_0001[] = {INT64_C(4), INT64_C(5), INT64_C(6)}; + memcpy(b.nd_int64, array_dummy_0001, 3 * b.type_size); + + // 'c' ndarray creation starts here, 'c' is [a, [7, 8, 9], b] + + c = array_create(2, (int64_t[]){INT64_C(3), INT64_C(3)}, nd_int64, false, order_c); // Allocating 'c' ndarray + uint32_t offset = 0; // Initializing offset, used later to avoid overwritting data when executing multiple copy operations + array_copy_data(&c, a, offset); // Copying the first element of 'c', 'offset' is 0 since it's our first copy operation + offset += a.length; // Incrementing offset for upcoming copy operation + int64_t array_dummy_0002[] = {INT64_C(7), INT64_C(8), INT64_C(9)}; // Creating an array_dummy with 'c''s second element's scalars ([7, 8, 9]) + memcpy(c.nd_int64 + offset, array_dummy_0002, 3 * c.type_size); // 'offset' is also with 'memcpy' + offset += 3; // incrementing 'offset', preparing for final copy + array_copy_data(&c, b, offset); // Copying the third element to 'c' ndarray + free_array(a); + free_array(b); + free_array(c); + return 0; +} +``` + +### `order_f` array creation example + +For `order_f`, the process is similar to `order_c`, but instead of copying our data straight to the destination `ndarray`, we first create an (`order_c`) `temp_ndarray`, copy the data to the `temp_ndarray`, then create an `order_f ndarray`, and copy from the `temp_ndarray` to the destination `order_f ndarray` -- using `strides` and `shape` -- to get the correct column-major memory layout. + +Example: + +```python +if __name__ == "__main__": + import numpy as np + a = np.array([[1, 2, 3], [4, 5, 6]], order="F") + print(a[0][0]) # output ==> 1 +``` + +Would be translated to this: + +```c +int main() +{ + t_ndarray a = {.shape = NULL}; + a = array_create(2, (int64_t[]){INT64_C(2), INT64_C(3)}, nd_int64, false, order_f); // Allocating the required ndarray + t_ndarray temp_array = {.shape = NULL}; + temp_array = array_create(2, (int64_t[]){INT64_C(2), INT64_C(3)}, nd_int64, false, order_c); // Allocating an order_c temp_array + int64_t array_dummy[] = {INT64_C(1), INT64_C(2), INT64_C(3), INT64_C(4), INT64_C(5), INT64_C(6)}; // array_dummy with our flattened data + memcpy(temp_array.nd_int64, array_dummy, 6 * temp_array.type_size); // Copying our array_dummy to our temp ndarray + array_copy_data(&a, temp_array, 0); // Copying into a column-major memory layout + free_array(temp_array); // Freeing the temp_array right after we were done with it + printf("%ld\n", GET_ELEMENT(a, nd_int64, (int64_t)0, (int64_t)0)); // output ==> 1 + free_array(a); + return 0; +} +``` + +If the data is composed of at least one variable array, the process would still be somewhat the same as an `order_c ndarray` creation: + The `order_f ndarray` is not populated from the get go, instead, we create an `order_c temp_array` (following `order_c ndarray` creation steps) containing all the data, then we do a 'copy into a column-major memory layout' operation to our `order_f ndarray`. + +Example: + +```python +if __name__ == "__main__": + import numpy as np + a = np.array([1, 2, 3]) + b = np.array([4, 5, 6]) + f = np.array([a, [7, 8, 9], b], order="F") +``` + +Would be translated to (focus on `f` `ndarray` creation): + +```c +int main() +{ + t_ndarray a = {.shape = NULL}; + t_ndarray b = {.shape = NULL}; + t_ndarray c = {.shape = NULL}; + a = array_create(1, (int64_t[]){3}, nd_int64, false, order_c); + int64_t array_dummy[] = {INT64_C(1), INT64_C(2), INT64_C(3)}; + memcpy(a.nd_int64, array_dummy, 3 * a.type_size); + b = array_create(1, (int64_t[]){INT64_C(3)}, nd_int64, false, order_c); + int64_t array_dummy_0001[] = {INT64_C(4), INT64_C(5), INT64_C(6)}; + memcpy(b.nd_int64, array_dummy_0001, 3 * b.type_size); + + // 'f' ndarray creation + + f = array_create(2, (int64_t[]){INT64_C(3), INT64_C(3)}, nd_int64, false, order_f); // Allocating the required ndarray (order_f) + t_ndarray temp_array = {.shape = NULL}; + temp_array = array_create(2, (int64_t[]){INT64_C(3), INT64_C(3)}, nd_int64, false, order_c); // Allocating a temp_array (order_c) + uint32_t offset = 0; + array_copy_data(&temp_array, a, offset); // Copying the first element to temp_array + offset += a.length; + int64_t array_dummy_0002[] = {INT64_C(7), INT64_C(8), INT64_C(9)}; + memcpy(temp_array.nd_int64 + offset, array_dummy_0002, 3 * temp_array.type_size); // Copying the second element to temp_array + offset += 3; + array_copy_data(&temp_array, b, offset); // Copying the third element to temp_array + array_copy_data(&f, temp_array, 0); // Copying our temp_array into a column-major memory layout (order_f) + free_array(temp_array); // freeing the temp_array + free_array(a); + free_array(b); + free_array(c); + return 0; +} +``` + +## Pyccel's Fortran code + +As Fortran has arrays in the language there is no need to add special handling for arrays. Fortran ordered arrays (`order_f`) are already compatible with the Fortran language. They can therefore be passed to the function as they are. + +In order to pass C ordered arrays (`order_c`) and retain the shape and correct element placing to be compatible with Fortran, a transpose would be needed. +In Pyccel we prefer to avoid unnecessary copies, so instead we pass the contiguous block of memory to Fortran and change how we index the array to ensure that we access the expected element. + +### Ordering in Fortran code + +Fortran indexing does not occur in the same order as in C. +If we take the following 2D array as an example: + +| | | | +|---|---|---| +| 1 | 2 | 3 | +| 4 | 5 | 6 | + +In C the element `A[1,0]=4` is the fourth element in memory, however in Fortran the element `A(1,0)=4` is the second element in memory. +Thus to iterate over this array in the most efficient way in C we would do: +```C +# A.shape = (2,3) +for (int row = 0; row < 2; ++row) { + for (int column = 0; column < 3; ++column) { + A[row,column] = .... + } +} +``` + +while in Fortran we would do: +```Fortran +# A.shape = (2,3) +do column = 0, 3 + do row = 0, 2 + A(row,column) = .... + end do +end do +``` + +As you can see in the Fortran-ordered array the indices are passed to the array in the same order, however the index does not point to the same location in memory. +In C code the index `i_1, i_2, i_3` points to the element `i_1 * (n_2 * n_3) + i_2 * n_2 + i_3` in memory. +In Fortran code the index `i_1, i_2, i_3` points to the element `i_1 + i_2 * n_1 + i_3 * (n_2 * n_3)` in memory. + +### Order F +Pyccel's translation of code with `order='F'` should look very similar to the original Python code. + +NumPy's storage of the strides ensures that the first dimension is the contiguous dimension as in Fortran, so the code is equivalent for all element-wise operations. + +There are some exceptions to this rule, for example printing. Python always prints arrays in a row-major format so Pyccel must take care to respect this rule in the output. + +### Order C + +As mentioned above, printing a C-ordered array in Fortran is more complicated. +Consider the following 2D C-ordered array: + +| | | | +|---|---|---| +| 1 | 2 | 3 | +| 4 | 5 | 6 | + +where the numbers indicate the position of the elements in memory. If this data block (`[1, 2, 3, 4, 5, 6]`) were passed to Fortran indicating a size (2,3), we would obtain the following array: + +| | | | +|---|---|---| +| 1 | 3 | 5 | +| 2 | 4 | 6 | + +As a result we cannot pass the data block without either rearranging the elements (transposing), or changing the index. In Pyccel we prefer avoiding unnecessary copies. As a result we pass a data block (`[1, 2, 3, 4, 5, 6]`), but we indicate a size (3,2). This gives us the following array: + +| | | +|---|---| +| 1 | 4 | +| 2 | 5 | +| 3 | 6 | + +This is equivalent to the transpose of the original array. As a result we can obtain expected results by simply inverting the index order. + +Therefore the following Python code +```python +for i in range(2): + for j in range(3): + a[i,j] = i*3+j +``` + +is translated to the following efficient indexing: +```fortran +do i = 0_i64, 1_i64, 1_i64 + do j = 0_i64, 2_i64, 1_i64 + a(j, i) = i * 3_i64 + j + end do +end do +``` + +As we are effectively operating on the transpose of the array, this must be taken into account when printing anything related to arrays with `order='C'`. + +For example, consider the code: +```python +def f(c_array : 'float[:,:](order=C)', f_array : 'float[:,:](order=F)'): + print(c_array.shape) + print(f_array.shape) + + for row in range(c_array.shape[0]): + for col in range(c_array.shape[1]): + c_array[row, col] = ... + + for col in range(f_array.shape[1]): + for row in range(f_array.shape[0]): + f_array[row, col] = ... +``` + +This will be translated to: + +```Fortran + subroutine f(c_array, f_array) + + implicit none + + real(f64), intent(inout) :: c_array(0:,0:) + real(f64), intent(inout) :: f_array(0:,0:) + integer(i64) :: row + integer(i64) :: col + + write(stdout, '(A I0 A I0 A)', advance="no") '(' , size(c_array, & + 2_i64, i64) , ', ' , size(c_array, 1_i64, i64) , ')' + write(stdout, '()', advance="yes") + write(stdout, '(A I0 A I0 A)', advance="no") '(' , size(f_array, & + 1_i64, i64) , ', ' , size(f_array, 2_i64, i64) , ')' + write(stdout, '()', advance="yes") + do row = 0_i64, size(c_array, 2_i64, i64) - 1_i64, 1_i64 + do col = 0_i64, size(c_array, 1_i64, i64) - 1_i64, 1_i64 + c_array(col, row) = ... + end do + end do + do col = 0_i64, size(f_array, 2_i64, i64) - 1_i64, 1_i64 + do row = 0_i64, size(f_array, 1_i64, i64) - 1_i64, 1_i64 + f_array(row, col) = ... + end do + end do + + end subroutine f +``` + +Note the changes to the shape and the indexing, which make this code closer to the following intermediate representation: + +```python +def f_intermediate(c_array_T : 'float[:,:](order=F)', f_array : 'float[:,:](order=F)'): + print(c_array_T.shape[::-1]) + print(f_array.shape) + + for row in range(c_array_T.shape[1]): + for col in range(c_array_T.shape[0]): + c_array_T[col, row] = ... + + for col in range(f_array.shape[1]): + for row in range(f_array.shape[0]): + f_array[row, col] = ... +``` + +Note that `f(c_array, f_array) == f_intermediate(c_array.T, f_array)`. diff --git a/developer_docs/overview.md b/developer_docs/overview.md index 1b8a7f171e..519377fe69 100644 --- a/developer_docs/overview.md +++ b/developer_docs/overview.md @@ -1,6 +1,6 @@ ## Developer Setup -Before beginning any development in pyccel, it is important to ensure pyccel is correctly installed **from source in development mode** as described [here](../README.md#from-sources). If this step is not followed then any changes made to source will not be used when `pyccel` or `epyccel` are used. +Before beginning any development in Pyccel, it is important to ensure Pyccel is correctly installed **from source in development mode** as described [here](../README.md#from-sources). If this step is not followed then any changes made to source will not be used when `pyccel` or `epyccel` are used. ## Overview @@ -8,7 +8,7 @@ Pyccel's development is split into 4 main stages: ### Syntactic Stage -Pyccel uses Python's [ast module](https://docs.python.org/3/library/ast.html) to read the input file(s). The ast does not store information in the same way as the rest of Pyccel so this stage exists to **convert Python's ast to Pyccel's ast**. The related code can be found in [parser/syntactic.py](../pyccel/parser/syntactic.py). +Pyccel uses Python's [`ast` module](https://docs.python.org/3/library/ast.html) to read the input file(s). The abstract syntax tree (AST) of Python's `ast` module does not store information in the same way as the rest of Pyccel so this stage exists to **convert Python's AST to Pyccel's AST**. The related code can be found in [parser/syntactic.py](../pyccel/parser/syntactic.py). The syntactic stage also handles parsing header comments. This is managed using [textx](http://textx.github.io/textX/stable/). The files describing the _textx_ grammar are found in the folder [parser/grammar](../pyccel/parser/grammar). From these files _textx_ generates instances of the classes found in the folder [parser/syntax](../pyccel/parser/syntax). @@ -18,7 +18,7 @@ The role of this stage has decreased significantly since we moved from [redbaron ### Semantic Stage -This is the most important stage in pyccel. It is here that all the information about types is calculated. This stage strives to be **language-agnostic**; this means for example, that additional variables required to handle problems appearing in one specific language should not be created here. +This is the most important stage in Pyccel. It is here that all the information about types is calculated. This stage strives to be **language-agnostic**; this means for example, that additional variables required to handle problems appearing in one specific language should not be created here. When adding functions to this stage the aim is often to create a `PyccelAstNode` (see [ast/basic.py](../pyccel/ast/basic.py)) and correctly define all of its parameters. This information is sometimes readily available (e.g. the type of a `PyccelAdd` can be derived from the type of the variables passed to it), but sometimes the information must be collected from elsewhere (e.g. when creating a `Variable` from a `PyccelSymbol` (roughly equivalent to a string). In this case information is needed from a `Scope` instance which is stored in the `scope`. @@ -34,11 +34,11 @@ As in the Semantic stage, the Code Generation stage also stores the current Scop ### Compilation Stage -Finally the generated code is compiled. This is handled in the [pipeline](../pyccel/codegen/pipeline.py). The compilers commands are found in [codegen/compiling/compilers.py](../pyccel/codegen/compiling/compilers.py). Different compilers have different flags and need different libraries. Once pyccel has been executed once on your machine the flags and libraries can be found in json files in the [compilers](../pyccel/compilers) folder +Finally the generated code is compiled. This is handled in the [pipeline](../pyccel/codegen/pipeline.py). The compilers commands are found in [codegen/compiling/compilers.py](../pyccel/codegen/compiling/compilers.py). Different compilers have different flags and need different libraries. Once Pyccel has been executed once on your machine the flags and libraries can be found in JSON files in the [compilers](../pyccel/compilers) folder ### Function Naming Conventions/File Navigation -In the syntactic, semantic, and code generation stages a similar strategy is used for traversing the Python objects. This strategy is based on function names. The majority of functions have names of the form: `_prefix_ClassName` (in the syntactic and semantic stages the prefix is `visit`, in the code generation stages it is `print`). These functions are never called directly, but instead are called via a high level function `_prefix` (e.g. `_visit` for the semantic stage). This strategy avoids large if/elif blocks to handle all possible types. +In the syntactic, semantic, and code generation stages a similar strategy is used for traversing the Python objects. This strategy is based on function names. The majority of functions have names of the form: `_prefix_ClassName` (in the syntactic and semantic stages the prefix is `visit`, in the code generation stages it is `print`). These functions are never called directly, but instead are called via a high level function `_prefix` (e.g. `_visit` for the semantic stage). This strategy avoids large `if`/`elif` blocks to handle all possible types. #### Example Suppose we want to generate the code for an object of the class `NumpyTanh`, first we collect the inheritance tree of `NumpyTanh`. This gives us: @@ -60,7 +60,10 @@ In the case of `NumpyTanh` the function which will be selected is `_print_NumpyU ### AST -The objects as understood by pyccel are each described by classes which inherit from [pyccel.ast.basic.Basic](../pyccel/ast/basic.py). These classes are found in the [ast](../pyccel/ast) folder. The ast is split into several files. There is one file for each supported extension module and files to group concepts, e.g. literals/operators/built-in functions +The objects as understood by Pyccel are each described by classes which inherit from [pyccel.ast.basic.Basic](../pyccel/ast/basic.py). +These classes are found in the [ast](../pyccel/ast) folder. +The objects in the Abstract Syntax Tree (AST) are described in several files. +There is one file for each supported extension module and files to group concepts, e.g. literals/operators/built-in functions ## Error System @@ -73,4 +76,4 @@ If the error prevents further translation (e.g. the type of an object is now unk ## Getting Help -While discussions within the associated Github issue are often sufficient, should you require more help do not hesitate to ask one of the other developers to add you to our slack: pyccel.slack.com +While discussions within the associated GitHub issue are often sufficient, should you require more help do not hesitate to ask one of the other developers to add you to our slack: diff --git a/developer_docs/review_process.md b/developer_docs/review_process.md index ca6b767583..a1e71cd85c 100644 --- a/developer_docs/review_process.md +++ b/developer_docs/review_process.md @@ -7,13 +7,13 @@ When you believe your branch is ready to merge you should create a pull request. Once the pull request is opened 4 tests should be triggered they are: - **Linux** : Runs the suite of tests on a linux machine -- **MacOS** : Runs the suite of tests on a mac os machine +- **MacOS** : Runs the suite of tests on a macOS machine - **Windows** : Runs the suite of tests on a windows machine - **Codacy** : Runs a static compiler via the [codacy](https://app.codacy.com/gh/pyccel/pyccel/dashboard) platform -Once the pull request is open the tests will be triggered every time you push new changes to the branch. Please be mindful of this and try to avoid pushing multiple times in a row to save compute resources. If you do find you need to push repeatedly, don't hesitate to cancel concurrent jobs using the github "Actions" tab. +Once the pull request is open the tests will be triggered every time you push new changes to the branch. Please be mindful of this and try to avoid pushing multiple times in a row to save compute resources. If you do find you need to push repeatedly, don't hesitate to cancel concurrent jobs using the GitHub "Actions" tab. -When the pull request is ready for review (ie. you are happy with it, and it is passing all tests) it can be marked as such and the review process can begin. This process is split into 3 stages which each have an associated label. The labels are described in the next sections. When a reviewer marks a PR as accepted, they should change the label to indicate the next stage of the review process. If they request changes they should remove the label so the pull request owner can react. +When the pull request is ready for review (i.e. you are happy with it, and it is passing all tests) it can be marked as such and the review process can begin. This process is split into 3 stages which each have an associated label. The labels are described in the next sections. When a reviewer marks a PR as accepted, they should change the label to indicate the next stage of the review process. If they request changes they should remove the label so the pull request owner can react. Once your pull request has been reviewed please react to the open conversations. If you disagree you can say this, if not please leave a reference to the commit which fixes the mentioned issue. This makes the review process faster and more streamlined. Please only resolve conversations that you opened. You may think you fixed the problem, but the reviewer may disagree and leaving the discussion open makes it easier for them to verify that they agree with you. If you are reviewing then please close all conversations that you open once the problem is resolved. If you don't this can block the merge. diff --git a/developer_docs/scope.md b/developer_docs/scope.md index 23d2031ab8..134dc1bbdd 100644 --- a/developer_docs/scope.md +++ b/developer_docs/scope.md @@ -6,7 +6,7 @@ In Pyccel, a `Scope` is an object defined in [parser/scope.py](../pyccel/parser/ Each of these objects must be inserted into the scope using and insert function. -## ScopedNode +## `ScopedNode` Each scope is associated with a class, e.g. `FunctionDef`, `For`, `Module`. These classes inherit from the `ScopedNode` class. The scope associated with the class instance, is saved within the class. This makes the scope available when the class instance is available. This is important so as to correctly set the `scope` variable in the `SemanticParser` and the different `CodePrinter`s. @@ -16,7 +16,7 @@ The `Scope` object keeps track of all names used in the described scope. This me 1. In the syntactic stage all symbols in the code must be added to the correct scope. This is done via the function `insert_symbol` -2. All names of variables created by pyccel must be created using one of the following functions defined in the Scope class: +2. All names of variables created by Pyccel must be created using one of the following functions defined in the Scope class: - `get_expected_name` : Collect the name which will be used to create the Variable referenced in the argument. In most cases this operation will be the identity operation, but it ensures that name collisions are handled and that the Symbol has been correctly inserted into the Scope - `get_new_name` : Get a new name with no collisions. A name can be requested and will be used if available - `get_new_incremented_symbol` : Get a new name with no collisions following a pattern. This function keeps track of the index appended to the incremented string so it is most useful when creating multiple names with the same prefix diff --git a/pyccel/ast/basic.py b/pyccel/ast/basic.py index a703d665da..b1fd29ec54 100644 --- a/pyccel/ast/basic.py +++ b/pyccel/ast/basic.py @@ -11,7 +11,7 @@ """ import ast -__all__ = ('Basic', 'PyccelAstNode') +__all__ = ('Basic', 'Immutable', 'PyccelAstNode', 'ScopedNode') dict_keys = type({}.keys()) dict_values = type({}.values()) diff --git a/pyccel/ast/bind_c.py b/pyccel/ast/bind_c.py index ff8cdcd65f..d857a9afa6 100644 --- a/pyccel/ast/bind_c.py +++ b/pyccel/ast/bind_c.py @@ -20,9 +20,9 @@ 'BindCFunctionDef', 'BindCPointer', 'CLocFunc', - 'as_static_module', 'as_static_function', 'as_static_function_call', + 'as_static_module', 'sanitize_arguments', 'wrap_array', 'wrap_module_array_var', diff --git a/pyccel/ast/bitwise_operators.py b/pyccel/ast/bitwise_operators.py index 6e69bdf50b..fc59dc6d73 100644 --- a/pyccel/ast/bitwise_operators.py +++ b/pyccel/ast/bitwise_operators.py @@ -15,12 +15,14 @@ from .operators import PyccelUnaryOperator, PyccelOperator __all__ = ( - 'PyccelRShift', - 'PyccelLShift', - 'PyccelBitXor', - 'PyccelBitOr', + 'PyccelBitComparisonOperator', + 'PyccelBitOperator', 'PyccelBitAnd', + 'PyccelBitOr', + 'PyccelBitXor', 'PyccelInvert', + 'PyccelLShift', + 'PyccelRShift', ) #============================================================================== diff --git a/pyccel/ast/builtin_imports.py b/pyccel/ast/builtin_imports.py index 885a1659b4..afddd7a97d 100644 --- a/pyccel/ast/builtin_imports.py +++ b/pyccel/ast/builtin_imports.py @@ -2,6 +2,8 @@ File containing a set of all python standard libraries from python 3.6 - 3.9 (from python 3.10 there is a function in sys for this purpose) """ +__all__ = ('python_builtin_libs',) + python_builtin_libs = { "_dummy_thread", "_thread", diff --git a/pyccel/ast/builtins.py b/pyccel/ast/builtins.py index 9e078b7bdc..09d9f60f34 100644 --- a/pyccel/ast/builtins.py +++ b/pyccel/ast/builtins.py @@ -29,6 +29,9 @@ pyccel_stage = PyccelStage() __all__ = ( + 'Lambda', + 'PythonAbs', + 'PythonComplexProperty', 'PythonReal', 'PythonImag', 'PythonConjugate', @@ -43,6 +46,7 @@ 'PythonMap', 'PythonPrint', 'PythonRange', + 'PythonSum', 'PythonType', 'PythonZip', 'PythonMax', @@ -61,6 +65,7 @@ class PythonComplexProperty(PyccelInternalFunction): arg : Variable, Literal """ + __slots__ = () _dtype = NativeFloat() _precision = -1 _rank = 0 @@ -132,6 +137,7 @@ class PythonConjugate(PyccelInternalFunction): arg : Variable, Literal """ + __slots__ = () _dtype = NativeComplex() _precision = -1 _rank = 0 @@ -588,7 +594,8 @@ class PythonPrint(Basic): expr : PyccelAstNode The expression to print - + file: String (Optional) + Select 'stdout' (default) or 'stderr' to print to Examples >>> from pyccel.ast.internals import symbols @@ -597,18 +604,27 @@ class PythonPrint(Basic): >>> Print(('results', n,m)) Print((results, n, m)) """ - __slots__ = ('_expr') + __slots__ = ('_expr', '_file') _attribute_nodes = ('_expr',) name = 'print' - def __init__(self, expr): + def __init__(self, expr, file="stdout"): + if file not in ('stdout', 'stderr'): + raise ValueError('output_unit can be `stdout` or `stderr`') self._expr = expr + self._file = file super().__init__() @property def expr(self): return self._expr + @property + def file(self): + """ returns the output unit (`stdout` or `stderr`) + """ + return self._file + #============================================================================== class PythonRange(Basic): @@ -673,8 +689,7 @@ class PythonZip(PyccelInternalFunction): Represents a zip stmt. """ - __slots__ = ('_length','_args') - _attribute_nodes = ('_args',) + __slots__ = ('_length',) name = 'zip' def __init__(self, *args): @@ -886,9 +901,12 @@ def print_string(self): can be used in a print statement """ prec = self.precision - return LiteralString("".format( - dtype = str(self.dtype), - precision = '' if prec in (None, -1) else (prec * (16 if self.dtype is NativeComplex() else 8)))) + dtype = str(self.dtype) + if prec in (None, -1): + return LiteralString(f"") + else: + precision = prec * (16 if self.dtype is NativeComplex() else 8) + return LiteralString(f"") #============================================================================== python_builtin_datatypes_dict = { diff --git a/pyccel/ast/c_concepts.py b/pyccel/ast/c_concepts.py index b00c110117..0e5f807f42 100644 --- a/pyccel/ast/c_concepts.py +++ b/pyccel/ast/c_concepts.py @@ -7,7 +7,12 @@ Module representing object address. """ -from .basic import PyccelAstNode +from .basic import PyccelAstNode, Basic +from .literals import LiteralString + +__all__ = ('CMacro', + 'CStringExpression', + 'ObjectAddress') class ObjectAddress(PyccelAstNode): """Represents the address of an object. @@ -34,3 +39,175 @@ def obj(self): """The object whose address is of interest """ return self._obj + +#------------------------------------------------------------------------------ +class CStringExpression(Basic): + """ + Internal class used to hold a C string that has LiteralStrings and C macros. + + Parameters + ---------- + *args : str / LiteralString / CMacro / CStringExpression + any number of arguments to be added to the expression + note: they will get added in the order provided + + Example + ------ + >>> expr = CStringExpression( + ... CMacro("m"), + ... CStringExpression( + ... LiteralString("the macro is: "), + ... CMacro("mc") + ... ), + ... LiteralString("."), + ... ) + """ + __slots__ = ('_expression',) + _attribute_nodes = ('_expression',) + + def __init__(self, *args): + self._expression = [] + super().__init__() + for arg in args: + self.append(arg) + + def __repr__(self): + return ''.join(repr(e) for e in self._expression) + + def __str__(self): + return ''.join(str(e) for e in self._expression) + + def __add__(self, o): + """ + return new CStringExpression that has `o` at the end + + Parameter + ---------- + o : str / LiteralString / CMacro / CStringExpression + the expression to add + """ + if isinstance(o, str): + o = LiteralString(o) + if not isinstance(o, (LiteralString, CMacro, CStringExpression)): + raise TypeError(f"unsupported operand type(s) for +: '{self.__class__}' and '{type(o)}'") + return CStringExpression(*self._expression, o) + + def __radd__(self, o): + if isinstance(o, LiteralString): + return CStringExpression(o, self) + return NotImplemented + + def __iadd__(self, o): + self.append(o) + return self + + def append(self, o): + """ + append the argument `o` to the end of the list _expression + + Parameter + --------- + o : str / LiteralString / CMacro / CStringExpression + the expression to append + """ + if isinstance(o, str): + o = LiteralString(o) + if not isinstance(o, (LiteralString, CMacro, CStringExpression)): + raise TypeError(f"unsupported operand type(s) for append: '{self.__class__}' and '{type(o)}'") + self._expression += (o,) + o.set_current_user_node(self) + + def join(self, lst): + """ + insert self between each element of the list `lst` + + Parameter + --------- + lst : list + the list to insert self between its elements + + Example + ------- + >>> a = [ + ... CMacro("m"), + ... CStringExpression(LiteralString("the macro is: ")), + ... LiteralString("."), + ... ] + >>> b = CStringExpression("?").join(a) + ... + ... # is the same as: + ... + >>> b = CStringExpression( + ... CMacro("m"), + ... CStringExpression("?"), + ... CStringExpression(LiteralString("the macro is: ")), + CStringExpression("?"), + ... LiteralString("."), + ... ) + """ + result = CStringExpression() + if not lst: + return result + result += lst[0] + for elm in lst[1:]: + result += self + result += elm + return result + + def get_flat_expression_list(self): + """ + returns a list of LiteralStrings and CMacros after merging every + consecutive LiteralString + """ + tmp_res = [] + for e in self.expression: + if isinstance(e, CStringExpression): + tmp_res.extend(e.get_flat_expression_list()) + else: + tmp_res.append(e) + if not tmp_res: + return [] + result = [tmp_res[0]] + for e in tmp_res[1:]: + if isinstance(e, LiteralString) and isinstance(result[-1], LiteralString): + result[-1] += e + else: + result.append(e) + return result + + @property + def expression(self): + """ The list containing the literal strings and c macros + """ + return self._expression + +#------------------------------------------------------------------------------ +class CMacro(Basic): + """Represents a c macro""" + __slots__ = ('_macro',) + _attribute_nodes = () + + def __init__(self, arg): + super().__init__() + if not isinstance(arg, str): + raise TypeError('arg must be of type str') + self._macro = arg + + def __repr__(self): + return str(self._macro) + + def __add__(self, o): + if isinstance(o, (LiteralString, CStringExpression)): + return CStringExpression(self, o) + return NotImplemented + + def __radd__(self, o): + if isinstance(o, LiteralString): + return CStringExpression(o, self) + return NotImplemented + + @property + def macro(self): + """ The string containing macro name + """ + return self._macro diff --git a/pyccel/ast/core.py b/pyccel/ast/core.py index 92ccc80c1f..a297d5aea2 100644 --- a/pyccel/ast/core.py +++ b/pyccel/ast/core.py @@ -50,29 +50,40 @@ 'CodeBlock', 'Comment', 'CommentBlock', + 'Concatenate', 'ConstructorCall', 'Continue', 'Deallocate', 'Declare', + 'Decorator', 'Del', + 'DottedFunctionCall', 'Duplicate', 'DoConcurrent', 'EmptyNode', + 'ErrorExit', + 'Exit', 'For', 'ForIterator', + 'FuncAddressDeclare', + 'FunctionAddress', 'FunctionCall', 'FunctionCallArgument', 'FunctionDef', 'FunctionDefArgument', 'If', + 'IfSection', 'Import', 'InlineFunctionDef', 'InProgram', 'Interface', + 'Iterable', 'Module', 'ModuleHeader', 'Pass', 'Program', + 'PyccelFunctionDef', + 'Raise', 'Return', 'SeparatorComment', 'StarredArguments', @@ -2735,6 +2746,7 @@ class PyccelFunctionDef(FunctionDef): The class which should be instantiated upon a FunctionCall to this FunctionDef object """ + __slots__ = () def __init__(self, name, func_class): assert isinstance(func_class, type) and \ issubclass(func_class, (PyccelInternalFunction, PyccelAstNode)) @@ -3987,6 +3999,7 @@ class Decorator(Basic): The name of the decorator """ __slots__ = ('_name',) + _attribute_nodes = () def __init__(self, name): self._name = name diff --git a/pyccel/ast/cwrapper.py b/pyccel/ast/cwrapper.py index b00d5f72fd..d71845897b 100644 --- a/pyccel/ast/cwrapper.py +++ b/pyccel/ast/cwrapper.py @@ -38,6 +38,7 @@ 'PyArgKeywords', 'PyArg_ParseTupleNode', 'PyBuildValueNode', + 'PyModule_AddObject', #--------- CONSTANTS ---------- 'Py_True', 'Py_False', diff --git a/pyccel/ast/datatypes.py b/pyccel/ast/datatypes.py index 581f60cf56..6d17610a8a 100644 --- a/pyccel/ast/datatypes.py +++ b/pyccel/ast/datatypes.py @@ -27,7 +27,7 @@ 'NativeGeneric', 'NativeInteger', 'NativeTuple', -# 'NativeNil', + 'NativeNil', 'NativeRange', 'NativeFloat', 'NativeString', @@ -193,6 +193,7 @@ def __init__(self, name='__UNDEFINED__'): self._name = name class NativeGeneric(DataType): + __slots__ = () _name = 'Generic' diff --git a/pyccel/ast/headers.py b/pyccel/ast/headers.py index 09bddba117..61051e099f 100644 --- a/pyccel/ast/headers.py +++ b/pyccel/ast/headers.py @@ -10,7 +10,7 @@ from .basic import Basic, iterable from .core import Assign, FunctionCallArgument from .core import FunctionDef, FunctionCall, FunctionAddress -from .datatypes import datatype, DataTypeFactory, UnionType +from .datatypes import datatype, DataTypeFactory, UnionType, default_precision from .internals import PyccelSymbol, Slice from .macros import Macro, MacroShape, construct_macro from .variable import DottedName, DottedVariable @@ -25,6 +25,7 @@ 'MacroVariable', 'MetaVariable', 'MethodHeader', + 'Template', 'VariableHeader', ) @@ -284,6 +285,10 @@ def build_argument(var_name, dc): order = None shape = None + + if rank and precision == -1: + precision = default_precision[dtype] + if rank >1: order = dc['order'] diff --git a/pyccel/ast/internals.py b/pyccel/ast/internals.py index caac5ef444..b7e6df0587 100644 --- a/pyccel/ast/internals.py +++ b/pyccel/ast/internals.py @@ -8,6 +8,7 @@ To avoid circular imports this file should only import from basic, datatypes, and literals """ +from operator import attrgetter from pyccel.utilities.stage import PyccelStage from .basic import Basic, PyccelAstNode, Immutable @@ -22,8 +23,8 @@ 'PyccelInternalFunction', 'PyccelSymbol', 'Slice', + 'get_final_precision', 'max_precision', - 'get_final_precision' ) @@ -292,8 +293,10 @@ def max_precision(objs : list, dtype = None, allow_native = True): return max(def_prec if o.precision == -1 \ else o.precision for o in objs if o.dtype is dtype) else: - return max(default_precision[str(o.dtype)] if o.precision == -1 \ - else o.precision for o in objs) + ndarray_list = [o for o in objs if getattr(o, 'is_ndarray', False)] + if ndarray_list: + return get_final_precision(max(ndarray_list, key=attrgetter('precision'))) + return max(get_final_precision(o) for o in objs) def get_final_precision(obj): """ diff --git a/pyccel/ast/itertoolsext.py b/pyccel/ast/itertoolsext.py index faea896c45..24c6914890 100644 --- a/pyccel/ast/itertoolsext.py +++ b/pyccel/ast/itertoolsext.py @@ -10,8 +10,8 @@ from .internals import PyccelInternalFunction __all__ = ( - 'itertools_mod', 'Product', + 'itertools_mod', ) class Product(PyccelInternalFunction): diff --git a/pyccel/ast/literals.py b/pyccel/ast/literals.py index 39f01be2a6..0a3a6ad5d1 100644 --- a/pyccel/ast/literals.py +++ b/pyccel/ast/literals.py @@ -11,6 +11,7 @@ NativeComplex, NativeString) __all__ = ( + 'Literal', 'LiteralTrue', 'LiteralFalse', 'LiteralInteger', @@ -149,8 +150,8 @@ def __new__(cls, real, imag, precision = -1): def __init__(self, real, imag, precision = -1): super().__init__(precision) - self._real_part = LiteralFloat(self._collect_python_val(real)) - self._imag_part = LiteralFloat(self._collect_python_val(imag)) + self._real_part = LiteralFloat(self._collect_python_val(real), precision = precision) + self._imag_part = LiteralFloat(self._collect_python_val(imag), precision = precision) @staticmethod def _collect_python_val(arg): @@ -213,6 +214,11 @@ def __repr__(self): def __str__(self): return str(self.python_value) + def __add__(self, o): + if isinstance(o, LiteralString): + return LiteralString(self._string + o._string) + return NotImplemented + @property def python_value(self): return self.arg diff --git a/pyccel/ast/numpyext.py b/pyccel/ast/numpyext.py index e79dba32d7..70f97ba63f 100644 --- a/pyccel/ast/numpyext.py +++ b/pyccel/ast/numpyext.py @@ -32,11 +32,11 @@ from .internals import PyccelInternalFunction, Slice, max_precision, get_final_precision from .internals import PyccelArraySize -from .literals import LiteralInteger, LiteralFloat, LiteralComplex, convert_to_literal +from .literals import LiteralInteger, LiteralFloat, LiteralComplex, LiteralString, convert_to_literal from .literals import LiteralTrue, LiteralFalse from .literals import Nil from .mathext import MathCeil -from .operators import broadcast, PyccelMinus, PyccelDiv +from .operators import broadcast, PyccelMinus, PyccelDiv, PyccelMul, PyccelAdd from .variable import (Variable, Constant, HomogeneousTupleVariable) errors = Errors() @@ -45,8 +45,14 @@ __all__ = ( 'process_shape', # --- + 'NumpyAutoFill', + 'NumpyUfuncBase', + 'NumpyUfuncBinary', + 'NumpyUfuncUnary', + # --- 'NumpyAbs', 'NumpyFloor', + 'NumpySign', # --- 'NumpySqrt', 'NumpySin', @@ -65,18 +71,27 @@ 'NumpyArccosh', 'NumpyArctanh', # --- - 'NumpyEmpty', - 'NumpyEmptyLike', - 'NumpyFloat', + 'NumpyAmax', + 'NumpyAmin', + 'NumpyArange', + 'NumpyArray', + 'NumpyArraySize', + 'NumpyBool', + 'NumpyCountNonZero', 'NumpyComplex', 'NumpyComplex64', 'NumpyComplex128', + 'NumpyConjugate', + 'NumpyEmpty', + 'NumpyEmptyLike', + 'NumpyFabs', + 'NumpyFloat', 'NumpyFloat32', 'NumpyFloat64', 'NumpyFull', 'NumpyFullLike', 'NumpyImag', - 'NumpyBool', + 'NumpyHypot', 'NumpyInt', 'NumpyInt8', 'NumpyInt16', @@ -84,11 +99,7 @@ 'NumpyInt64', 'NumpyLinspace', 'NumpyMatmul', - 'NumpyAmax', - 'NumpyAmin', - 'NumpyArange', - 'NumpyArraySize', - 'NumpyCountNonZero', + 'NumpyNewArray', 'NumpyMod', 'NumpyNonZero', 'NumpyNonZeroElement', @@ -100,10 +111,11 @@ 'NumpyRand', 'NumpyRandint', 'NumpyReal', - 'Shape', + 'NumpyTranspose', 'NumpyWhere', 'NumpyZeros', 'NumpyZerosLike', + 'Shape', ) #======================================================================================= @@ -188,12 +200,14 @@ def __init__(self, arg=None, base=10): class NumpyInt8(NumpyInt): """ Represents a call to numpy.int8() function. """ + __slots__ = () _precision = dtype_registry['int8'][1] name = 'int8' class NumpyInt16(NumpyInt): """ Represents a call to numpy.int16() function. """ + __slots__ = () _precision = dtype_registry['int16'][1] name = 'int16' @@ -253,10 +267,12 @@ class NumpyImag(PythonImag): __slots__ = ('_precision','_rank','_shape','_order') name = 'imag' def __new__(cls, arg): + if not isinstance(arg.dtype, NativeComplex): - dtype=NativeInteger() if isinstance(arg.dtype, NativeBool) else arg.dtype + dtype = NativeInteger() if isinstance(arg.dtype, NativeBool) else arg.dtype if arg.rank == 0: return convert_to_literal(0, dtype, arg.precision) + dtype = DtypePrecisionToCastFunction[dtype.name][arg.precision] return NumpyZeros(arg.shape, dtype=dtype) return super().__new__(cls, arg) @@ -318,8 +334,7 @@ class NumpyComplex128(NumpyComplex): 'Complex' : { -1 : PythonComplex, 4 : NumpyComplex64, - 8 : NumpyComplex, - 16 : NumpyComplex128,}, + 8 : NumpyComplex128,}, 'Bool': { -1 : PythonBool, 4 : NumpyBool} @@ -328,24 +343,54 @@ class NumpyComplex128(NumpyComplex): #============================================================================== def process_dtype(dtype): + """ + This function takes a dtype passed to a numpy array creation function, + processes it in different ways depending on its type, and finally extracts + the corresponding type and precision from the `dtype_registry` dictionary. + + This function could be useful when working with numpy creation function + having a dtype argument, like numpy.array, numpy.arrange, numpy.linspace... + + Parameters + ---------- + dtype: PythonType | PyccelFunctionDef | String + The actual dtype passed to the numpy function + + Raises + ------ + TypeError: In the case of unrecognized argument type. + TypeError: In the case of passed string argument not recognized as valid dtype. + + Returns: + ---------- + dtype: Datatype + The Datatype corresponding to the passed dtype. + precision: int + The precision corresponding to the passed dtype. + """ + if isinstance(dtype, PythonType): return dtype.dtype, get_final_precision(dtype) if isinstance(dtype, PyccelFunctionDef): dtype = dtype.cls_name - if dtype in (PythonInt, PythonFloat, PythonComplex, PythonBool): + if dtype in (PythonInt, PythonFloat, PythonComplex, PythonBool): # remove python prefix from dtype.name len("python") = 6 dtype = dtype.__name__.lower()[6:] - elif dtype in (NumpyInt, NumpyInt8, NumpyInt16, NumpyInt32, NumpyInt64, NumpyComplex, NumpyFloat, + elif dtype in (NumpyInt, NumpyInt8, NumpyInt16, NumpyInt32, NumpyInt64, NumpyComplex, NumpyFloat, NumpyComplex128, NumpyComplex64, NumpyFloat64, NumpyFloat32): # remove numpy prefix from dtype.name len("numpy") = 5 dtype = dtype.__name__.lower()[5:] + elif isinstance(dtype, (LiteralString, str)): + dtype = str(dtype).replace('\'', '').lower() + if dtype not in dtype_registry: + raise TypeError(f'Unknown type of {dtype}.') else: - dtype = str(dtype).replace('\'', '').lower() + raise TypeError(f'Unknown type of {dtype}.') dtype, precision = dtype_registry[dtype] if precision == -1: - precision = default_precision[dtype] - dtype = datatype(dtype) + precision = default_precision[dtype] + dtype = datatype(dtype) return dtype, precision @@ -398,7 +443,11 @@ def __init__(self, arg, dtype=None, order='C'): # Verify dtype and get precision if dtype is None: dtype = arg.dtype - dtype, prec = process_dtype(dtype) + prec = get_final_precision(arg) + else: + dtype, prec = process_dtype(dtype) + # ... Determine ordering + order = str(order).strip("\'") shape = process_shape(False, arg.shape) rank = len(shape) @@ -494,6 +543,9 @@ def stop(self): def step(self): return self._step + def __getitem__(self, index): + step = PyccelMul(index, self.step, simplify=True) + return PyccelAdd(self.start, step, simplify=True) #============================================================================== class NumpySum(PyccelInternalFunction): @@ -931,11 +983,11 @@ def __init__(self, shape, fill_value, dtype=None, order='C'): # If there is no dtype, extract it from fill_value # TODO: must get dtype from an annotated node - if not dtype: + if dtype is None: dtype = fill_value.dtype - - # Verify dtype and get precision - dtype, precision = process_dtype(dtype) + precision = get_final_precision(fill_value) + else: + dtype, precision = process_dtype(dtype) # Cast fill_value to correct type if fill_value: @@ -964,7 +1016,6 @@ class NumpyAutoFill(NumpyFull): def __init__(self, shape, dtype='float', order='C'): if not dtype: raise TypeError("Data type must be provided") - super().__init__(shape, Nil(), dtype, order) #============================================================================== @@ -973,6 +1024,12 @@ class NumpyEmpty(NumpyAutoFill): """ __slots__ = () name = 'empty' + + def __init__(self, shape, dtype='float', order='C'): + if dtype in NativeNumeric: + precision = default_precision[str_dtype(dtype)] + dtype = DtypePrecisionToCastFunction[dtype.name][precision] + super().__init__(shape, dtype, order) @property def fill_value(self): return None @@ -1029,7 +1086,8 @@ class NumpyFullLike(PyccelInternalFunction): def __new__(cls, a, fill_value, dtype=None, order='K', subok=True, shape=None): # NOTE: we ignore 'subok' argument - dtype = dtype or a.dtype + if dtype is None: + dtype = DtypePrecisionToCastFunction[a.dtype.name][a.precision] order = a.order if str(order).strip('\'"') in ('K', 'A') else order shape = Shape(a) if shape is None else shape return NumpyFull(shape, fill_value, dtype, order) @@ -1043,7 +1101,8 @@ class NumpyEmptyLike(PyccelInternalFunction): def __new__(cls, a, dtype=None, order='K', subok=True, shape=None): # NOTE: we ignore 'subok' argument - dtype = dtype or a.dtype + if dtype is None: + dtype = DtypePrecisionToCastFunction[a.dtype.name][a.precision] order = a.order if str(order).strip('\'"') in ('K', 'A') else order shape = Shape(a) if shape is None else shape @@ -1058,7 +1117,8 @@ class NumpyOnesLike(PyccelInternalFunction): def __new__(cls, a, dtype=None, order='K', subok=True, shape=None): # NOTE: we ignore 'subok' argument - dtype = dtype or a.dtype + if dtype is None: + dtype = DtypePrecisionToCastFunction[a.dtype.name][a.precision] order = a.order if str(order).strip('\'"') in ('K', 'A') else order shape = Shape(a) if shape is None else shape @@ -1073,7 +1133,8 @@ class NumpyZerosLike(PyccelInternalFunction): def __new__(cls, a, dtype=None, order='K', subok=True, shape=None): # NOTE: we ignore 'subok' argument - dtype = dtype or a.dtype + if dtype is None: + dtype = DtypePrecisionToCastFunction[a.dtype.name][a.precision] order = a.order if str(order).strip('\'"') in ('K', 'A') else order shape = Shape(a) if shape is None else shape @@ -1277,13 +1338,24 @@ class NumpyArctanh(NumpyUfuncUnary): #======================================================================================= + +class NumpySign(NumpyUfuncUnary): + """Represent a call to the sign function in the Numpy library""" + __slots__ = () + name = 'sign' + def _set_dtype_precision(self, x): + if not isinstance(x.dtype, (NativeInteger, NativeFloat, NativeComplex)): + raise TypeError(f'{x.dtype} not supported') + self._dtype = x.dtype + self._precision = get_final_precision(x) + class NumpyAbs(NumpyUfuncUnary): """Represent a call to the abs function in the Numpy library""" __slots__ = () name = 'abs' def _set_dtype_precision(self, x): self._dtype = NativeInteger() if x.dtype is NativeInteger() else NativeFloat() - self._precision = default_precision[str_dtype(self._dtype)] + self._precision = get_final_precision(x) class NumpyFloor(NumpyUfuncUnary): """Represent a call to the floor function in the Numpy library""" @@ -1707,6 +1779,7 @@ def __str__(self): 'linspace' : PyccelFunctionDef('linspace' , NumpyLinspace), 'where' : PyccelFunctionDef('where' , NumpyWhere), # --- + 'sign' : PyccelFunctionDef('sign' , NumpySign), 'abs' : PyccelFunctionDef('abs' , NumpyAbs), 'floor' : PyccelFunctionDef('floor' , NumpyFloor), 'absolute' : PyccelFunctionDef('absolute' , NumpyAbs), diff --git a/pyccel/ast/omp.py b/pyccel/ast/omp.py index 9c7df0ce02..0c61d9e721 100644 --- a/pyccel/ast/omp.py +++ b/pyccel/ast/omp.py @@ -12,6 +12,24 @@ from .basic import Basic +__all__ = ('OmpAnnotatedComment', + 'OMP_For_Loop', + 'OMP_Simd_Construct', + 'OMP_TaskLoop_Construct', + 'OMP_Distribute_Construct', + 'OMP_Parallel_Construct', + 'OMP_Task_Construct', + 'OMP_Single_Construct', + 'OMP_Critical_Construct', + 'OMP_Master_Construct', + 'OMP_Masked_Construct', + 'OMP_Cancel_Construct', + 'OMP_Target_Construct', + 'OMP_Teams_Construct', + 'OMP_Sections_Construct', + 'OMP_Section_Construct', + 'Omp_End_Clause') + class OmpAnnotatedComment(Basic): """Represents an OpenMP Annotated Comment in the code. diff --git a/pyccel/ast/operators.py b/pyccel/ast/operators.py index 6b6d654880..d863a85d00 100644 --- a/pyccel/ast/operators.py +++ b/pyccel/ast/operators.py @@ -30,6 +30,11 @@ __all__ = ( 'PyccelOperator', + 'PyccelArithmeticOperator', + 'PyccelBinaryOperator', + 'PyccelBooleanOperator', + 'PyccelComparisonOperator', + 'PyccelUnaryOperator', 'PyccelPow', 'PyccelAdd', 'PyccelMinus', @@ -390,14 +395,14 @@ def _calculate_dtype(cls, *args): strs = [a for a in args if a.dtype is NativeString()] if strs: - return cls._handle_str_type(strs) assert len(integers + floats + complexes) == 0 + return cls._handle_str_type(strs) elif complexes: - return cls._handle_complex_type(complexes) + return cls._handle_complex_type(args) elif floats: - return cls._handle_float_type(floats) + return cls._handle_float_type(args) elif integers: - return cls._handle_integer_type(integers) + return cls._handle_integer_type(args) else: raise TypeError('cannot determine the type of {}'.format(args)) diff --git a/pyccel/ast/scipyext.py b/pyccel/ast/scipyext.py index 115ced880c..8bbe7adf89 100644 --- a/pyccel/ast/scipyext.py +++ b/pyccel/ast/scipyext.py @@ -10,6 +10,8 @@ from .core import Module, Import from .variable import Constant +__all__ = ('scipy_mod', 'scipy_pi_const') + scipy_pi_const = Constant('float', 'pi', value=pi) scipy_mod = Module('scipy', diff --git a/pyccel/ast/sympy_helper.py b/pyccel/ast/sympy_helper.py index dd84de94e2..8e3ea31992 100644 --- a/pyccel/ast/sympy_helper.py +++ b/pyccel/ast/sympy_helper.py @@ -27,6 +27,9 @@ from .variable import Variable, PyccelArraySize +__all__ = ('sympy_to_pyccel', + 'pyccel_to_sympy') + #============================================================================== def sympy_to_pyccel(expr, symbol_map): """ diff --git a/pyccel/ast/sysext.py b/pyccel/ast/sysext.py new file mode 100644 index 0000000000..355ce16152 --- /dev/null +++ b/pyccel/ast/sysext.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +#------------------------------------------------------------------------------------------# +# This file is part of Pyccel which is released under MIT License. See the LICENSE file or # +# go to https://github.com/pyccel/pyccel/blob/master/LICENSE for full license details. # +#------------------------------------------------------------------------------------------# +""" Module containing objects from the sys module understood by pyccel +""" +from .core import PyccelFunctionDef, Module +from .internals import PyccelInternalFunction +from .datatypes import NativeVoid +from .internals import LiteralInteger + +__all__ = ( + 'SysExit', + 'sys_constants', + 'sys_funcs', + 'sys_mod', +) + +class SysExit(PyccelInternalFunction): + """Represents a call to sys.exit + + Parameters + ---------- + + arg : PyccelAstNode (optional) + if arg.dtype is NativeInteger it will be used as the exit_code + else the arg will be printed to the stderror + """ + __slots__ = () + name = 'exit' + _dtype = NativeVoid() + _precision = -1 + _rank = 0 + _shape = None + _order = None + + def __init__(self, status=LiteralInteger(0)): + super().__init__(status) + + @property + def status(self): + """return the arg of exit""" + return self._args[0] + + def __str__(self): + return f'exit({str(self.status)})' + +sys_constants = { + +} + +sys_funcs = { + 'exit' : PyccelFunctionDef('exit', SysExit), +} + +sys_mod = Module('sys', + variables = (sys_constants.values()), + funcs = (sys_funcs.values()), + imports = [ + ]) diff --git a/pyccel/ast/utilities.py b/pyccel/ast/utilities.py index dca7d425c4..9e15e910a7 100644 --- a/pyccel/ast/utilities.py +++ b/pyccel/ast/utilities.py @@ -24,6 +24,7 @@ from .itertoolsext import itertools_mod from .literals import LiteralInteger, Nil from .mathext import math_mod +from .sysext import sys_mod from .numpyext import (NumpyEmpty, NumpyArray, numpy_mod, NumpyTranspose, NumpyLinspace) @@ -37,6 +38,7 @@ errors = Errors() __all__ = ( + 'LoopCollection', 'builtin_function', 'builtin_import', 'builtin_import_registery', @@ -86,7 +88,8 @@ def builtin_function(expr, args=None): Import('scipy', AsName(scipy_mod,'scipy')), Import('itertools', AsName(itertools_mod,'itertools')), Import('math', AsName(math_mod,'math')), - Import('pyccel', AsName(pyccel_mod,'pyccel')) + Import('pyccel', AsName(pyccel_mod,'pyccel')), + Import('sys', AsName(sys_mod,'sys')), ]) if sys.version_info < (3, 10): from .builtin_imports import python_builtin_libs @@ -309,6 +312,9 @@ def insert_index(expr, pos, index_var): return type(expr)(insert_index(expr.args[0], pos, index_var), insert_index(expr.args[1], pos, index_var)) + elif hasattr(expr, '__getitem__'): + return expr[index_var] + else: raise NotImplementedError("Expansion not implemented for type : {}".format(type(expr))) @@ -356,8 +362,8 @@ def collect_loops(block, indices, new_index, language_has_vectors = False, resul current_level = 0 array_creator_types = (Allocate, PythonList, PythonTuple, Concatenate, Duplicate) is_function_call = lambda f: ((isinstance(f, FunctionCall) and not f.funcdef.is_elemental) - or (isinstance(f, PyccelInternalFunction) and not f.is_elemental - and not isinstance(f, NumpyTranspose))) + or (isinstance(f, PyccelInternalFunction) and not f.is_elemental and not hasattr(f, '__getitem__') + and not isinstance(f, (NumpyTranspose)))) for line in block: if (isinstance(line, Assign) and @@ -400,6 +406,7 @@ def collect_loops(block, indices, new_index, language_has_vectors = False, resul transposed_vars = [v for v in notable_nodes if isinstance(v, NumpyTranspose)] \ + [v for f in elemental_func_calls \ for v in f.get_attribute_nodes(NumpyTranspose)] + indexed_funcs = [v for v in notable_nodes if isinstance(v, PyccelInternalFunction) and hasattr(v, '__getitem__')] is_checks = [n for n in notable_nodes if isinstance(n, PyccelIs)] @@ -416,7 +423,7 @@ def collect_loops(block, indices, new_index, language_has_vectors = False, resul funcs = [f for f in notable_nodes+transposed_vars if (isinstance(f, FunctionCall) \ and not f.funcdef.is_elemental)] internal_funcs = [f for f in notable_nodes+transposed_vars if (isinstance(f, PyccelInternalFunction) \ - and not f.is_elemental) \ + and not f.is_elemental and not hasattr(f, '__getitem__')) \ and not isinstance(f, NumpyTranspose)] # Collect all variables for which values other than the value indexed in the loop are important @@ -439,7 +446,7 @@ def collect_loops(block, indices, new_index, language_has_vectors = False, resul symbol=line, severity='fatal') func_results = [f.funcdef.results[0] for f in funcs] - func_vars2 = [new_index(r.dtype, r) for r in func_results] + func_vars2 = [new_index(r.dtype, r.name) for r in func_results] assigns += [Assign(v, f) for v,f in zip(func_vars2, funcs)] if assigns: @@ -457,7 +464,7 @@ def collect_loops(block, indices, new_index, language_has_vectors = False, resul rank = line.lhs.rank shape = line.lhs.shape new_vars = variables - new_vars_t = transposed_vars + handled_funcs = transposed_vars + indexed_funcs # Loop over indexes, inserting until the expression can be evaluated # in the desired language new_level = 0 @@ -468,8 +475,8 @@ def collect_loops(block, indices, new_index, language_has_vectors = False, resul indices.append(new_index('int','i')) index_var = indices[rank+index] new_vars = [insert_index(v, index, index_var) for v in new_vars] - new_vars_t = [insert_index(v, index, index_var) for v in new_vars_t] - if compatible_operation(*new_vars, *new_vars_t, language_has_vectors = language_has_vectors): + handled_funcs = [insert_index(v, index, index_var) for v in handled_funcs] + if compatible_operation(*new_vars, *handled_funcs, language_has_vectors = language_has_vectors): break # TODO [NH]: get all indices when adding axis argument to linspace function @@ -477,10 +484,12 @@ def collect_loops(block, indices, new_index, language_has_vectors = False, resul line.rhs.ind = indices[0] # Replace variable expressions with Indexed versions - line.substitute(variables, new_vars, excluded_nodes = (FunctionCall, PyccelInternalFunction)) - line.substitute(transposed_vars, new_vars_t, excluded_nodes = (FunctionCall)) + line.substitute(variables, new_vars, + excluded_nodes = (FunctionCall, PyccelInternalFunction)) + line.substitute(transposed_vars + indexed_funcs, handled_funcs, + excluded_nodes = (FunctionCall)) _ = [f.substitute(variables, new_vars) for f in elemental_func_calls] - _ = [f.substitute(transposed_vars, new_vars_t) for f in elemental_func_calls] + _ = [f.substitute(transposed_vars + indexed_funcs, handled_funcs) for f in elemental_func_calls] # Recurse through result tree to save line with lines which need # the same set of for loops diff --git a/pyccel/ast/variable.py b/pyccel/ast/variable.py index 611c34d924..502bf3fd1f 100644 --- a/pyccel/ast/variable.py +++ b/pyccel/ast/variable.py @@ -25,9 +25,12 @@ pyccel_stage = PyccelStage() __all__ = ( + 'Constant', 'DottedName', 'DottedVariable', + 'HomogeneousTupleVariable', 'IndexedElement', + 'InhomogeneousTupleVariable', 'TupleVariable', 'Variable' ) diff --git a/pyccel/codegen/printing/ccode.py b/pyccel/codegen/printing/ccode.py index dc6758656f..c299980981 100644 --- a/pyccel/codegen/printing/ccode.py +++ b/pyccel/codegen/printing/ccode.py @@ -47,7 +47,7 @@ from pyccel.ast.variable import DottedName from pyccel.ast.variable import InhomogeneousTupleVariable, HomogeneousTupleVariable -from pyccel.ast.c_concepts import ObjectAddress +from pyccel.ast.c_concepts import ObjectAddress, CMacro, CStringExpression from pyccel.codegen.printing.codeprinter import CodePrinter @@ -66,7 +66,7 @@ # Used in CCodePrinter._print_NumpyUfuncBase(self, expr) numpy_ufunc_to_c_float = { 'NumpyAbs' : 'fabs', - 'NumpyFabs' : 'fabs', + 'NumpyFabs' : 'fabs', 'NumpyMin' : 'minval', 'NumpyMax' : 'maxval', 'NumpyFloor': 'floor', # TODO: might require special treatment with casting @@ -199,6 +199,7 @@ "stdlib", "string", "tgmath", + "inttypes", ) dtype_registry = {('float',8) : 'double', @@ -222,6 +223,17 @@ ('int',1) : 'nd_int8', ('bool',4) : 'nd_bool'} +type_to_format = {('float',8) : '%.12lf', + ('float',4) : '%.12f', + ('complex',8) : '(%.12lf + %.12lfj)', + ('complex',4) : '(%.12f + %.12fj)', + ('int',4) : '%d', + ('int',8) : LiteralString("%") + CMacro('PRId64'), + ('int',2) : LiteralString("%") + CMacro('PRId16'), + ('int',1) : LiteralString("%") + CMacro('PRId8'), + ('bool',4) : '%s', + ('string', 0) : '%s'} + import_dict = {'omp_lib' : 'omp' } c_imports = {n : Import(n, Module(n, (), ())) for n in @@ -234,8 +246,10 @@ 'stdint', 'pyc_math_c', 'stdio', + "inttypes", 'stdbool', - 'assert']} + 'assert', + 'numpy_c']} class CCodePrinter(CodePrinter): """A printer to convert python expressions to strings of c code""" @@ -310,9 +324,6 @@ def copy_NumpyArray_Data(self, expr): declare_dtype = self.find_in_dtype_registry(self._print(rhs.dtype), rhs.precision) dtype = self.find_in_ndarray_type_registry(self._print(rhs.dtype), rhs.precision) arg = rhs.arg if isinstance(rhs, NumpyArray) else rhs - if rhs.rank > 1: - # flattening the args to use them in C initialization. - arg = self._flatten_list(arg) self.add_import(c_imports['string']) if isinstance(arg, Variable): @@ -320,6 +331,9 @@ def copy_NumpyArray_Data(self, expr): cpy_data = "memcpy({0}.{2}, {1}.{2}, {0}.buffer_size);\n".format(lhs, arg, dtype) return '%s' % (cpy_data) else : + if arg.rank > 1: + # flattening the args to use them in C initialization. + arg = self._flatten_list(arg) arg = ', '.join(self._print(i) for i in arg) dummy_array = "%s %s[] = {%s};\n" % (declare_dtype, dummy_array_name, arg) cpy_data = "memcpy({0}.{2}, {1}, {0}.buffer_size);\n".format(self._print(lhs), dummy_array_name, dtype) @@ -387,43 +401,6 @@ def _init_stack_array(self, expr): self.add_import(c_imports['ndarrays']) return buffer_array, array_init - def fill_NumpyArange(self, expr, lhs): - """ print the assignment of a NumpyArange - parameters - ---------- - expr : NumpyArange - The node holding NumpyArange - lhs : Variable - The left hand of Assign - Return - ------ - String - Return string that contains the Assign code and the For loop - responsible for filling the array values - """ - start = self._print(expr.start) - stop = self._print(expr.stop) - step = self._print(expr.step) - dtype = self.find_in_ndarray_type_registry(self._print(expr.dtype), expr.precision) - - target = self.scope.get_temporary_variable(expr.dtype) - index = self.scope.get_temporary_variable(NativeInteger()) - - self._additional_code += self._print(Assign(index, LiteralInteger(0))) - - code = 'for({target} = {start}; {target} {op} {stop}; {target} += {step})' - code += '\n{{\n{lhs}.{dtype}[{index}] = {target};\n' - code += self._print(AugAssign(index, '+', LiteralInteger(1))) + '\n}}' - code = code.format(target = self._print(target), - start = start, - stop = stop, - op = '<' if not isinstance(expr.step, PyccelUnarySub) else '>', - step = step, - index = self._print(index), - lhs = lhs, - dtype = dtype) - return code - def _handle_inline_func_call(self, expr): """ Print a function call to an inline function """ @@ -521,8 +498,16 @@ def _print_PythonMin(self, expr): self.add_import(c_imports['math']) return "fmin({}, {})".format(self._print(arg[0]), self._print(arg[1])) + elif arg.dtype is NativeInteger() and len(arg) == 2: + arg1 = self.scope.get_temporary_variable(NativeInteger()) + arg2 = self.scope.get_temporary_variable(NativeInteger()) + assign1 = Assign(arg1, arg[0]) + assign2 = Assign(arg2, arg[1]) + self._additional_code += self._print(assign1) + self._additional_code += self._print(assign2) + return f"({arg1} < {arg2} ? {arg1} : {arg2})" else: - return errors.report("min in C is only supported for 2 float arguments", symbol=expr, + return errors.report("min in C is only supported for 2 scalar arguments", symbol=expr, severity='fatal') def _print_PythonMax(self, expr): @@ -531,10 +516,28 @@ def _print_PythonMax(self, expr): self.add_import(c_imports['math']) return "fmax({}, {})".format(self._print(arg[0]), self._print(arg[1])) + elif arg.dtype is NativeInteger() and len(arg) == 2: + arg1 = self.scope.get_temporary_variable(NativeInteger()) + arg2 = self.scope.get_temporary_variable(NativeInteger()) + assign1 = Assign(arg1, arg[0]) + assign2 = Assign(arg2, arg[1]) + self._additional_code += self._print(assign1) + self._additional_code += self._print(assign2) + return f"({arg1} > {arg2} ? {arg1} : {arg2})" else: - return errors.report("max in C is only supported for 2 float arguments", symbol=expr, + return errors.report("max in C is only supported for 2 scalar arguments", symbol=expr, severity='fatal') + def _print_SysExit(self, expr): + code = "" + if expr.status.dtype is not NativeInteger() or expr.status.rank > 0: + print_arg = FunctionCallArgument(expr.status) + code = self._print(PythonPrint((print_arg, ), file="stderr")) + arg = "1" + else: + arg = self._print(expr.status) + return f"{code}exit({arg});\n" + def _print_PythonFloat(self, expr): value = self._print(expr.arg) type_name = self.find_in_dtype_registry('float', expr.precision) @@ -553,6 +556,17 @@ def _print_PythonBool(self, expr): def _print_Literal(self, expr): return repr(expr.python_value) + def _print_LiteralInteger(self, expr): + if isinstance(expr, LiteralInteger) and get_final_precision(expr) == 8: + self.add_import(c_imports['stdint']) + return f"INT64_C({repr(expr.python_value)})" + return repr(expr.python_value) + + def _print_LiteralFloat(self, expr): + if isinstance(expr, LiteralFloat) and get_final_precision(expr) == 4: + return f"{repr(expr.python_value)}f" + return repr(expr.python_value) + def _print_LiteralComplex(self, expr): if expr.real == LiteralFloat(0): return self._print(PyccelAssociativeParenthesis(PyccelMul(expr.imag, LiteralImaginaryUnit()))) @@ -793,16 +807,6 @@ def _print_LiteralString(self, expr): return '"{}"'.format(format_str) def get_print_format_and_arg(self, var): - type_to_format = {('float',8) : '%.12lf', - ('float',4) : '%.12f', - ('complex',8) : '(%.12lf + %.12lfj)', - ('complex',4) : '(%.12f + %.12fj)', - ('int',4) : '%d', - ('int',8) : '%ld', - ('int',2) : '%hd', - ('int',1) : '%c', - ('bool',4) : '%s', - ('string', 0) : '%s'} try: arg_format = type_to_format[(self._print(var.dtype), get_final_precision(var))] except KeyError: @@ -815,17 +819,25 @@ def get_print_format_and_arg(self, var): arg = self._print(var) return arg_format, arg + def _print_CStringExpression(self, expr): + return "".join(self._print(e) for e in expr.get_flat_expression_list()) + + def _print_CMacro(self, expr): + return str(expr.macro) + def extract_function_call_results(self, expr): tmp_list = [self.scope.get_temporary_variable(a.dtype) for a in expr.funcdef.results] return tmp_list def _print_PythonPrint(self, expr): self.add_import(c_imports['stdio']) + self.add_import(c_imports['inttypes']) end = '\n' sep = ' ' code = '' empty_end = FunctionCallArgument(LiteralString(''), 'end') space_end = FunctionCallArgument(LiteralString(' '), 'end') + empty_sep = FunctionCallArgument(LiteralString(''), 'sep') kwargs = [f for f in expr.expr if f.has_keyword] for f in kwargs: if f.keyword == 'sep' : sep = str(f.value) @@ -836,17 +848,38 @@ def _print_PythonPrint(self, expr): orig_args = [f for f in expr.expr if not f.has_keyword] def formatted_args_to_printf(args_format, args, end): - args_format = sep.join(args_format) + args_format = CStringExpression(sep).join(args_format) args_format += end - args_format = self._print(LiteralString(args_format)) + args_format = self._print(args_format) args_code = ', '.join([args_format, *args]) - return "printf({});\n".format(args_code) + if expr.file == 'stderr': + return f"fprintf(stderr, {args_code});\n" + return f"printf({args_code});\n" if len(orig_args) == 0: return formatted_args_to_printf(args_format, args, end) + tuple_start = FunctionCallArgument(LiteralString('(')) + tuple_sep = LiteralString(', ') + tuple_end = FunctionCallArgument(LiteralString(')')) + for i, f in enumerate(orig_args): f = f.value + if isinstance(f, (InhomogeneousTupleVariable, PythonTuple)): + if args_format: + code += formatted_args_to_printf(args_format, args, sep) + args_format = [] + args = [] + args = [FunctionCallArgument(print_arg) for tuple_elem in f for print_arg in (tuple_elem, tuple_sep)][:-1] + if len(f) == 1: + args.append(FunctionCallArgument(LiteralString(','))) + if i + 1 == len(orig_args): + end_of_tuple = FunctionCallArgument(LiteralString(end), 'end') + else: + end_of_tuple = FunctionCallArgument(LiteralString(sep), 'end') + code += self._print(PythonPrint([tuple_start, *args, tuple_end, empty_sep, end_of_tuple])) + args = [] + continue if isinstance(f, PythonType): f = f.print_string @@ -857,7 +890,8 @@ def formatted_args_to_printf(args_format, args, end): arg_format, arg = self.get_print_format_and_arg(a) tmp_arg_format_list.append(arg_format) args.append(arg) - args_format.append('({})'.format(', '.join(tmp_arg_format_list))) + tmp_arg_format_list = CStringExpression(', ').join(tmp_arg_format_list) + args_format.append(CStringExpression('(', tmp_arg_format_list, ')')) assign = Assign(tmp_list, f) self._additional_code += self._print(assign) elif f.rank > 0: @@ -872,14 +906,16 @@ def formatted_args_to_printf(args_format, args, end): if f.rank == 1: print_body.append(space_end) - for_body = [PythonPrint(print_body)] + for_body = [PythonPrint(print_body, file=expr.file)] for_scope = self.scope.create_new_loop_scope() for_loop = For(for_index, for_range, for_body, scope=for_scope) for_end = FunctionCallArgument(LiteralString(']'+end if i == len(orig_args)-1 else ']'), keyword='end') - body = CodeBlock([PythonPrint([ FunctionCallArgument(LiteralString('[')), empty_end]), + body = CodeBlock([PythonPrint([ FunctionCallArgument(LiteralString('[')), empty_end], + file=expr.file), for_loop, - PythonPrint([ FunctionCallArgument(f[max_index]), for_end])], + PythonPrint([ FunctionCallArgument(f[max_index]), for_end], + file=expr.file)], unravelled = True) code += self._print(body) else: @@ -1254,6 +1290,40 @@ def _print_NumpyUfuncBase(self, expr): code_args = ', '.join(args) return '{0}({1})'.format(func_name, code_args) + def _print_NumpySign(self, expr): + """ Print the corresponding C function for a call to Numpy.sign + + Parameters + ---------- + expr : Pyccel ast node + Python expression with Numpy.sign call + + Returns + ------- + string + Equivalent internal function in C + + Example + ------- + import numpy + + numpy.sign(x) => isign(x) (x is integer) + numpy.sign(x) => fsign(x) (x if float) + numpy.sign(x) => csign(x) (x is complex) + + """ + self.add_import(c_imports['numpy_c']) + dtype = expr.dtype + func = '' + if isinstance(dtype, NativeInteger): + func = 'isign' + elif isinstance(dtype, NativeFloat): + func = 'fsign' + elif isinstance(dtype, NativeComplex): + func = 'csign' + + return f'{func}({self._print(expr.args[0])})' + def _print_MathFunctionBase(self, expr): """ Convert a Python expression with a math function call to C function call @@ -1546,13 +1616,13 @@ def _print_Return(self, expr): # make sure that stmt contains one assign node. last_assign = last_assign[-1] variables = last_assign.rhs.get_attribute_nodes(Variable) - unneeded_var = not any(b in vars_in_deallocate_nodes for b in variables) + unneeded_var = not any(b in vars_in_deallocate_nodes or b.is_ndarray for b in variables) if unneeded_var: code = ''.join(self._print(a) for a in expr.stmt.body if a is not last_assign) return code + 'return {};\n'.format(self._print(last_assign.rhs)) else: - code = ''+self._print(expr.stmt) last_assign.lhs.is_temp = False + code = self._print(expr.stmt) return code + 'return {0};\n'.format(self._print(args[0])) @@ -1632,10 +1702,17 @@ def _print_PyccelUnarySub(self, expr): return '-{}'.format(self._print(expr.args[0])) def _print_AugAssign(self, expr): - lhs_code = self._print(expr.lhs) op = expr.op - rhs_code = self._print(expr.rhs) - return "{0} {1}= {2};\n".format(lhs_code, op, rhs_code) + lhs = expr.lhs + rhs = expr.rhs + + if op == '%' and isinstance(lhs.dtype, NativeFloat): + _expr = expr.to_basic_assign() + return self._print(_expr) + + lhs_code = self._print(lhs) + rhs_code = self._print(rhs) + return f'{lhs_code} {op}= {rhs_code};\n' def _print_Assign(self, expr): prefix_code = '' @@ -1663,8 +1740,6 @@ def _print_Assign(self, expr): return prefix_code+self.copy_NumpyArray_Data(expr) if isinstance(rhs, (NumpyFull)): return prefix_code+self.arrayFill(expr) - if isinstance(rhs, NumpyArange): - return prefix_code+self.fill_NumpyArange(rhs, lhs) lhs = self._print(expr.lhs) rhs = self._print(expr.rhs) return prefix_code+'{} = {};\n'.format(lhs, rhs) diff --git a/pyccel/codegen/printing/fcode.py b/pyccel/codegen/printing/fcode.py index 93a4320268..c059f1944a 100644 --- a/pyccel/codegen/printing/fcode.py +++ b/pyccel/codegen/printing/fcode.py @@ -33,15 +33,15 @@ If, IfSection, For, Deallocate) from pyccel.ast.variable import (Variable, - IndexedElement, HomogeneousTupleVariable, + IndexedElement, InhomogeneousTupleVariable, DottedName, PyccelArraySize) -from pyccel.ast.operators import PyccelAdd, PyccelMul, PyccelMinus, PyccelNot +from pyccel.ast.operators import PyccelAdd, PyccelMul, PyccelMinus from pyccel.ast.operators import PyccelMod from pyccel.ast.operators import PyccelUnarySub, PyccelLt, PyccelGt, IfTernaryOperator -from pyccel.ast.core import FunctionCall, DottedFunctionCall +from pyccel.ast.core import FunctionCall, DottedFunctionCall, PyccelFunctionDef from pyccel.ast.builtins import (PythonInt, PythonType, PythonPrint, PythonRange, @@ -64,12 +64,13 @@ from pyccel.ast.mathext import math_constants -from pyccel.ast.numpyext import NumpyEmpty +from pyccel.ast.numpyext import NumpyEmpty, NumpyInt32 from pyccel.ast.numpyext import NumpyFloat, NumpyBool from pyccel.ast.numpyext import NumpyReal, NumpyImag from pyccel.ast.numpyext import NumpyRand from pyccel.ast.numpyext import NumpyNewArray from pyccel.ast.numpyext import NumpyNonZero +from pyccel.ast.numpyext import NumpySign from pyccel.ast.numpyext import Shape from pyccel.ast.numpyext import DtypePrecisionToCastFunction @@ -210,7 +211,7 @@ def __init__(self, filename, prefix_module = None): errors.set_target(filename, 'file') super().__init__() - self._constantImports = set() + self._constantImports = {} self._current_class = None self._additional_code = None @@ -220,12 +221,16 @@ def __init__(self, filename, prefix_module = None): def print_constant_imports(self): """Prints the use line for the constant imports used""" - macro = "use, intrinsic :: ISO_C_Binding, only : " - rename = [c if isinstance(c, str) else c[0] + ' => ' + c[1] for c in self._constantImports] - if len(rename) == 0: - return '' - macro += " , ".join(rename) - return macro + macros = [] + for (name, imports) in self._constantImports.items(): + + macro = f"use, intrinsic :: {name}, only : " + rename = [c if isinstance(c, str) else c[0] + ' => ' + c[1] for c in imports] + if len(rename) == 0: + continue + macro += " , ".join(rename) + macros.append(macro) + return "\n".join(macros) def get_additional_imports(self): """return the additional modules collected for importing in printing stage""" @@ -280,10 +285,12 @@ def print_kind(self, expr): constant_name = iso_c_binding[self._print(expr.dtype)][precision] constant_shortcut = iso_c_binding_shortcut_mapping[constant_name] if constant_shortcut not in self.scope.all_used_symbols and constant_name != constant_shortcut: - self._constantImports.add((constant_shortcut, constant_name)) + self._constantImports.setdefault('ISO_C_Binding', set())\ + .add((constant_shortcut, constant_name)) constant_name = constant_shortcut else: - self._constantImports.add(constant_name) + self._constantImports.setdefault('ISO_C_Binding', set())\ + .add(constant_name) return constant_name def _handle_inline_func_call(self, expr, provided_args, assign_lhs = None): @@ -573,6 +580,7 @@ def _print_PythonPrint(self, expr): code = '' empty_end = FunctionCallArgument(LiteralString(''), 'end') space_end = FunctionCallArgument(LiteralString(' '), 'end') + empty_sep = FunctionCallArgument(LiteralString(''), 'sep') for f in expr.expr: if f.has_keyword: if f.keyword == 'sep': @@ -591,25 +599,31 @@ def _print_PythonPrint(self, expr): tuple_sep = LiteralString(', ') tuple_end = FunctionCallArgument(LiteralString(')')) - for f in orig_args: + for i, f in enumerate(orig_args): if f.keyword: continue else: f = f.value if isinstance(f, (InhomogeneousTupleVariable, PythonTuple, str)): if args_format: - code += self._formatted_args_to_print(args_format, args, sep, separator) + code += self._formatted_args_to_print(args_format, args, sep, separator, expr) args_format = [] args = [] + if i + 1 == len(orig_args): + end_of_tuple = empty_end + else: + end_of_tuple = FunctionCallArgument(sep, 'end') args = [FunctionCallArgument(print_arg) for tuple_elem in f for print_arg in (tuple_elem, tuple_sep)][:-1] - code += self._print(PythonPrint([tuple_start, *args, tuple_end])) + if len(f) == 1: + args.append(FunctionCallArgument(LiteralString(','))) + code += self._print(PythonPrint([tuple_start, *args, tuple_end, empty_sep, end_of_tuple], file=expr.file)) args = [] elif isinstance(f, PythonType): args_format.append('A') args.append(self._print(f.print_string)) elif isinstance(f.rank, int) and f.rank > 0: if args_format: - code += self._formatted_args_to_print(args_format, args, sep, separator) + code += self._formatted_args_to_print(args_format, args, sep, separator, expr) args_format = [] args = [] loop_scope = self.scope.create_new_loop_scope() @@ -620,25 +634,27 @@ def _print_PythonPrint(self, expr): if f.rank == 1: print_body.append(space_end) - for_body = [PythonPrint(print_body)] + for_body = [PythonPrint(print_body, file=expr.file)] for_loop = For(for_index, for_range, for_body, scope=loop_scope) for_end_char = LiteralString(']') for_end = FunctionCallArgument(for_end_char, keyword='end') - body = CodeBlock([PythonPrint([FunctionCallArgument(LiteralString('[')), empty_end]), + body = CodeBlock([PythonPrint([FunctionCallArgument(LiteralString('[')), empty_end], + file=expr.file), for_loop, - PythonPrint([FunctionCallArgument(f[max_index]), for_end])], + PythonPrint([FunctionCallArgument(f[max_index]), for_end], + file=expr.file)], unravelled=True) code += self._print(body) else: arg_format, arg = self._get_print_format_and_arg(f) args_format.append(arg_format) args.append(arg) - code += self._formatted_args_to_print(args_format, args, end, separator) + code += self._formatted_args_to_print(args_format, args, end, separator, expr) return code - def _formatted_args_to_print(self, fargs_format, fargs, fend, fsep): + def _formatted_args_to_print(self, fargs_format, fargs, fend, fsep, expr): """ Produce a write statement from a list of formats, args and an end statement @@ -650,6 +666,8 @@ def _formatted_args_to_print(self, fargs_format, fargs, fend, fsep): The args to be printed fend : PyccelAstNode The character describing the end of the line + expr : PyccelAstNode + The PythonPrint currently printed """ if fargs_format == ['*']: # To print the result of a FunctionCall @@ -670,8 +688,13 @@ def _formatted_args_to_print(self, fargs_format, fargs, fend, fsep): args_code = ' , '.join(args_list) args_formatting = ' '.join(fargs_format) - return "write(*, '({})', advance=\"{}\") {}\n"\ - .format(args_formatting, advance, args_code) + if expr.file == "stderr": + self._constantImports.setdefault('ISO_FORTRAN_ENV', set())\ + .add(("stderr", "error_unit")) + return f"write(stderr, '({args_formatting})', advance=\"{advance}\") {args_code}\n" + self._constantImports.setdefault('ISO_FORTRAN_ENV', set())\ + .add(("stdout", "output_unit")) + return f"write(stdout, '({args_formatting})', advance=\"{advance}\") {args_code}\n" def _get_print_format_and_arg(self,var): """ Get the format string and the printable argument for an object. @@ -1356,7 +1379,7 @@ def _print_Declare(self, expr): #TODO improve ,this is the case of character as argument elif isinstance(expr_dtype, BindCPointer): dtype = 'type(c_ptr)' - self._constantImports.add('c_ptr') + self._constantImports.setdefault('ISO_C_Binding', set()).add('c_ptr') else: dtype += '({0})'.format(self.print_kind(expr.variable)) @@ -2645,6 +2668,19 @@ def _print_ConstructorCall(self, expr): code = '{0}({1})'.format(name, code_args) return self._get_statement(code) + def _print_SysExit(self, expr): + code = "" + if expr.status.dtype is not NativeInteger() or expr.status.rank > 0: + print_arg = FunctionCallArgument(expr.status) + code = self._print(PythonPrint((print_arg, ), file="stderr")) + arg = "1" + else: + arg = expr.status + if arg.precision != 4: + arg = NumpyInt32(arg) + arg = self._print(arg) + return f'{code}stop {arg}\n' + def _print_NumpyUfuncBase(self, expr): type_name = type(expr).__name__ try: @@ -2657,6 +2693,31 @@ def _print_NumpyUfuncBase(self, expr): code = '{0}({1})'.format(func_name, code_args) return self._get_statement(code) + def _print_NumpySign(self, expr): + """ Print the corresponding Fortran function for a call to Numpy.sign + + Parameters + ---------- + expr : Pyccel ast node + Python expression with Numpy.sign call + + Returns + ------- + string + Equivalent internal function in Fortran + + Example + ------- + import numpy + + numpy.sign(x) => numpy_sign(x) + numpy_sign is an interface which calls the proper function depending on the data type of x + + """ + func = PyccelFunctionDef('numpy_sign', NumpySign) + self._additional_imports.add(Import('numpy_f90', AsName(func, 'numpy_sign'))) + return f'numpy_sign({self._print(expr.args[0])})' + def _print_NumpyTranspose(self, expr): var = expr.internal_var arg = self._print(var) @@ -3018,7 +3079,7 @@ def _print_PrecomputedCode(self, expr): def _print_CLocFunc(self, expr): lhs = self._print(expr.result) rhs = self._print(expr.arg) - self._constantImports.add('c_loc') + self._constantImports.setdefault('ISO_C_Binding', set()).add('c_loc') return f'{lhs} = c_loc({rhs})\n' #======================================================================================= diff --git a/pyccel/codegen/utilities.py b/pyccel/codegen/utilities.py index d2a0e88c89..62ac36bd19 100644 --- a/pyccel/codegen/utilities.py +++ b/pyccel/codegen/utilities.py @@ -31,6 +31,8 @@ "pyc_math_f90" : ("math", CompileObj("pyc_math_f90.f90",folder="math")), "pyc_math_c" : ("math", CompileObj("pyc_math_c.c",folder="math")), "cwrapper" : ("cwrapper", CompileObj("cwrapper.c",folder="cwrapper", accelerators=('python',))), + "numpy_f90" : ("numpy", CompileObj("numpy_f90.f90",folder="numpy")), + "numpy_c" : ("numpy", CompileObj("numpy_c.c",folder="numpy")), } internal_libs["cwrapper_ndarrays"] = ("cwrapper_ndarrays", CompileObj("cwrapper_ndarrays.c",folder="cwrapper_ndarrays", accelerators = ('python',), @@ -89,7 +91,7 @@ def copy_internal_library(lib_folder, pyccel_dirpath, extra_files = None): to_create = False # If folder exists check if it needs updating src_files = os.listdir(lib_path) - dst_files = os.listdir(lib_dest_path) + dst_files = [f for f in os.listdir(lib_dest_path) if not f.endswith('.lock')] # Check if all files are present in destination to_update = any(s not in dst_files for s in src_files) @@ -116,7 +118,8 @@ def copy_internal_library(lib_folder, pyccel_dirpath, extra_files = None): l.acquire() # Remove all files in destination directory for d in dst_files: - os.remove(os.path.join(lib_dest_path, d)) + d_file = os.path.join(lib_dest_path, d) + os.remove(d_file) # Copy all files from the source to the destination for s in src_files: shutil.copyfile(os.path.join(lib_path, s), @@ -124,7 +127,8 @@ def copy_internal_library(lib_folder, pyccel_dirpath, extra_files = None): # Create any requested extra files if extra_files: for filename, contents in extra_files.items(): - with open(os.path.join(lib_dest_path, filename), 'w') as f: + extra_file = os.path.join(lib_dest_path, filename) + with open(extra_file, 'w', encoding="utf-8") as f: f.writelines(contents) # Release the locks for l in locks: diff --git a/pyccel/compilers/default_compilers.py b/pyccel/compilers/default_compilers.py index 82b77f990a..1710764188 100644 --- a/pyccel/compilers/default_compilers.py +++ b/pyccel/compilers/default_compilers.py @@ -112,6 +112,8 @@ if sys.platform == "darwin": gcc_info['openmp']['flags'] = ("-Xpreprocessor",'-fopenmp') gcc_info['openmp']['libs'] = ('omp',) + gcc_info['openmp']['libdirs'] = ('/usr/local/opt/libomp/lib',) + gcc_info['openmp']['includes'] = ('/usr/local/opt/libomp/include',) elif sys.platform == "win32": gcc_info['mpi_exec'] = 'gcc' gcc_info['mpi']['flags'] = ('-D','USE_MPI_MODULE') diff --git a/pyccel/naming/cnameclashchecker.py b/pyccel/naming/cnameclashchecker.py index dd5dad709c..7bc31965bb 100644 --- a/pyccel/naming/cnameclashchecker.py +++ b/pyccel/naming/cnameclashchecker.py @@ -13,7 +13,7 @@ class CNameClashChecker(metaclass = Singleton): """ Class containing functions to help avoid problematic names in C """ # Keywords as mentioned on https://en.cppreference.com/w/c/keyword - keywords = set(['auto', 'break', 'case', 'char', 'const', + keywords = set(['isign', 'fsign', 'csign', 'auto', 'break', 'case', 'char', 'const', 'continue', 'default', 'do', 'double', 'else', 'enum', 'extern', 'float', 'for', 'goto', 'if', 'inline', 'int', 'long', 'register', 'restrict', 'return', 'short', 'signed', diff --git a/pyccel/naming/fortrannameclashchecker.py b/pyccel/naming/fortrannameclashchecker.py index e4514d3433..f6c041cd06 100644 --- a/pyccel/naming/fortrannameclashchecker.py +++ b/pyccel/naming/fortrannameclashchecker.py @@ -40,7 +40,7 @@ class FortranNameClashChecker(metaclass = Singleton): 'unlock', 'test', 'abs', 'sqrt', 'sin', 'cos', 'tan', 'asin', 'acos', 'atan', 'exp', 'log', 'int', 'nint', 'floor', 'fraction', 'real', 'max', 'mod', 'count', - 'pack']) + 'pack', 'numpy_sign']) def has_clash(self, name, symbols): """ Indicate whether the proposed name causes any clashes diff --git a/pyccel/parser/scope.py b/pyccel/parser/scope.py index 054c38cb50..669ce59b96 100644 --- a/pyccel/parser/scope.py +++ b/pyccel/parser/scope.py @@ -477,6 +477,7 @@ def get_temporary_variable(self, dtype_or_var, name = None, **kwargs): kwargs : dict See Variable keyword arguments """ + assert isinstance(name, (str, type(None))) name = self.get_new_name(name) if isinstance(dtype_or_var, Variable): var = dtype_or_var.clone(name, **kwargs, is_temp = True) diff --git a/pyccel/parser/semantic.py b/pyccel/parser/semantic.py index 02dfb31342..57b12b78e6 100644 --- a/pyccel/parser/semantic.py +++ b/pyccel/parser/semantic.py @@ -66,14 +66,15 @@ from pyccel.ast.datatypes import default_precision from pyccel.ast.datatypes import (NativeInteger, NativeBool, NativeFloat, NativeString, - NativeGeneric, NativeComplex) + NativeGeneric, NativeComplex, + NativeVoid) from pyccel.ast.functionalexpr import FunctionalSum, FunctionalMax, FunctionalMin, GeneratorComprehension, FunctionalFor from pyccel.ast.headers import FunctionHeader, MethodHeader, Header from pyccel.ast.headers import MacroFunction, MacroVariable -from pyccel.ast.internals import Slice, PyccelSymbol, get_final_precision +from pyccel.ast.internals import PyccelInternalFunction, Slice, PyccelSymbol, get_final_precision from pyccel.ast.itertoolsext import Product from pyccel.ast.literals import LiteralTrue, LiteralFalse @@ -97,7 +98,7 @@ OMP_TaskLoop_Construct, OMP_Sections_Construct, Omp_End_Clause, OMP_Single_Construct) -from pyccel.ast.operators import PyccelIs, PyccelIsNot, IfTernaryOperator, PyccelUnarySub +from pyccel.ast.operators import PyccelArithmeticOperator, PyccelIs, PyccelIsNot, IfTernaryOperator, PyccelUnarySub from pyccel.ast.operators import PyccelNot, PyccelEq, PyccelAdd, PyccelMul, PyccelPow from pyccel.ast.operators import PyccelAssociativeParenthesis, PyccelDiv @@ -670,8 +671,6 @@ def _create_PyccelOperator(self, expr, visited_args): errors.report(msg, symbol=expr, bounding_box=(self._current_fst_node.lineno, self._current_fst_node.col_offset), severity='fatal') - #if stmts: - # expr_new = CodeBlock(stmts + [expr_new]) return expr_new def _create_Duplicate(self, val, length): @@ -707,6 +706,11 @@ def _handle_function_args(self, arguments, **settings): if isinstance(a.value, StarredArguments): args.extend([FunctionCallArgument(av) for av in a.value.args_var]) else: + if isinstance(a.value, PyccelArithmeticOperator) and a.value.rank: + tmp_var = PyccelSymbol(self.scope.get_new_name(), is_temp=True) + assign = self._visit(Assign(tmp_var, arg.value, fst= arg.value.fst)) + self._additional_exprs[-1].append(assign) + a = FunctionCallArgument(self._visit(tmp_var)) args.append(a) return args @@ -724,7 +728,7 @@ def get_type_description(self, var, include_rank = True): """ dtype = var.dtype prec = get_final_precision(var) - descr = f'{dtype}(kind={prec})' + descr = f'{dtype}{(prec * 2 if isinstance(dtype, NativeComplex) else prec) * 8 if prec else ""}' if include_rank and var.rank>0: dims = ','.join(':'*var.rank) descr += f'[{dims}]' @@ -755,6 +759,7 @@ def incompatible(i_arg, f_arg): get_final_precision(i_arg) != get_final_precision(f_arg) or i_arg.rank != f_arg.rank) + # Compare each set of arguments for idx, (i_arg, f_arg) in enumerate(zip(input_args, func_args)): i_arg = i_arg.value f_arg = f_arg.var @@ -830,14 +835,31 @@ def _handle_function(self, expr, func, args, **settings): errors.report("Too many arguments passed in function call", symbol = expr, severity='fatal') + + func_args = func.arguments if isinstance(func, FunctionDef) else func.functions[0].arguments + # Sort arguments to match the order in the function definition + input_args = [a for a in args if a.keyword is None] + nargs = len(input_args) + for ka in func_args[nargs:]: + key = ka.name + relevant_args = [a for a in args[nargs:] if a.keyword == key] + n_relevant_args = len(relevant_args) + assert n_relevant_args <= 1 + if n_relevant_args == 0 and ka.has_default: + input_args.append(ka.default_call_arg) + elif n_relevant_args == 1: + input_args.append(relevant_args[0]) + + args = input_args + new_expr = FunctionCall(func, args, self._current_function) if None in new_expr.args: errors.report("Too few arguments passed in function call", symbol = expr, severity='error') elif isinstance(func, FunctionDef): - self._check_argument_compatibility(new_expr.args, func.arguments, - expr, func.is_elemental) + self._check_argument_compatibility(args, func_args, + expr, func.is_elemental) return new_expr def _create_variable(self, name, dtype, rhs, d_lhs, arr_in_multirets=False): @@ -1188,7 +1210,7 @@ def _ensure_infered_type_matches_existing(self, dtype, d_var, var, is_augassign, self._current_fst_node.col_offset), severity='error', symbol=var.name) - elif var.is_ndarray and var.is_alias: + elif var.is_ndarray and var.is_alias and not is_augassign: # we allow pointers to be reassigned multiple times # pointers reassigning need to call free_pointer func # to remove memory leaks @@ -1386,7 +1408,7 @@ def _assign_GeneratorComprehension(self, lhs_name, expr, **settings): # Iterate over the loops # This provides the definitions of iterators as well # as the central expression - loops = [self._visit(expr.loops, **settings)] + loops = [self._visit(expr.loops, **settings)] # If necessary add additional expressions corresponding # to nested GeneratorComprehensions @@ -2151,6 +2173,14 @@ def _visit_FunctionCall(self, expr, **settings): return getattr(self, annotation_method)(expr, **settings) args = self._handle_function_args(expr.args, **settings) + # Correct keyword names if scope is available + # The scope is only available if the function body has been parsed + # (i.e. not for headers or builtin functions) + if isinstance(func, FunctionDef) and func.scope: + args = [a if a.keyword is None else \ + FunctionCallArgument(a.value, func.scope.get_expected_name(a.keyword)) \ + for a in args] + if name == 'lambdify': args = self.scope.find(str(expr.args[0]), 'symbolic_functions') @@ -2440,6 +2470,11 @@ def _visit_Assign(self, expr, **settings): d_var = self._infere_type(rhs, **settings) if d_var['memory_handling'] == 'alias' and not isinstance(lhs, IndexedElement): rhs = rhs.internal_var + elif isinstance(rhs, PyccelInternalFunction) and isinstance(rhs.dtype, NativeVoid): + if expr.lhs.is_temp: + return rhs + else: + raise NotImplementedError("Cannot assign result of a function without a return") else: d_var = self._infere_type(rhs, **settings) @@ -2596,11 +2631,10 @@ def _visit_Assign(self, expr, **settings): new_expr = Assign(l, r) - if is_pointer_i: - new_expr = AliasAssign(l, r) - - elif isinstance(expr, AugAssign): + if isinstance(expr, AugAssign): new_expr = AugAssign(l, expr.op, r) + elif is_pointer_i: + new_expr = AliasAssign(l, r) elif new_expr.is_symbolic_alias: @@ -3031,9 +3065,7 @@ def _visit_Return(self, expr, **settings): assigns = [] for v,r in zip(return_vars, results): if not (isinstance(r, PyccelSymbol) and r == (v.name if isinstance(v, Variable) else v)): - a = Assign(v, r) - a.set_fst(expr.fst) - a = self._visit_Assign(a) + a = self._visit(Assign(v, r, fst=expr.fst)) assigns.append(a) results = [self._visit(i, **settings) for i in return_vars] diff --git a/pyccel/parser/syntactic.py b/pyccel/parser/syntactic.py index 5f082cef08..0fed30cbf8 100644 --- a/pyccel/parser/syntactic.py +++ b/pyccel/parser/syntactic.py @@ -786,12 +786,14 @@ def fill_types(ls): results = [] result_counter = 1 + local_symbols = self.scope.local_used_symbols + for r in zip(*returns): r0 = r[0] pyccel_symbol = isinstance(r0, PyccelSymbol) same_results = all(r0 == ri for ri in r) - name_available = all(r0 != a.name for a in arguments) + name_available = all(r0 != a.name for a in arguments) and r0 in local_symbols if pyccel_symbol and same_results and name_available: result_name = r0 diff --git a/pyccel/stdlib/cwrapper_ndarrays/cwrapper_ndarrays.c b/pyccel/stdlib/cwrapper_ndarrays/cwrapper_ndarrays.c index ee3afd869b..4057470d70 100644 --- a/pyccel/stdlib/cwrapper_ndarrays/cwrapper_ndarrays.c +++ b/pyccel/stdlib/cwrapper_ndarrays/cwrapper_ndarrays.c @@ -321,7 +321,10 @@ PyObject* ndarray_to_pyarray(t_ndarray *o) PyObject* c_ndarray_to_pyarray(t_ndarray *o) { int FLAGS = NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_WRITEABLE; - return PyArray_NewFromDescr(&PyArray_Type, PyArray_DescrFromType(o->type), + + enum NPY_TYPES npy_type = get_numpy_type(o); + + return PyArray_NewFromDescr(&PyArray_Type, PyArray_DescrFromType(npy_type), o->nd, _ndarray_to_numpy_shape(o->shape, o->nd), _ndarray_to_numpy_strides(o->strides, o->type_size, o->nd), o->raw_data, FLAGS, NULL); diff --git a/pyccel/stdlib/numpy/numpy_c.c b/pyccel/stdlib/numpy/numpy_c.c new file mode 100644 index 0000000000..36e4a205ec --- /dev/null +++ b/pyccel/stdlib/numpy/numpy_c.c @@ -0,0 +1,24 @@ +/* --------------------------------------------------------------------------------------- */ +/* This file is part of Pyccel which is released under MIT License. See the LICENSE file */ +/* or go to https://github.com/pyccel/pyccel/blob/master/LICENSE for full license details. */ +/* --------------------------------------------------------------------------------------- */ + +#include "numpy_c.h" + +/* numpy.sign for float, double and integers */ +long long int isign(long long int x) +{ + return SIGN(x); +} + +/* numpy.sign for float, double and integers */ +double fsign(double x) +{ + return SIGN(x); +} + +/* numpy.sign for complex */ +double complex csign(double complex x) +{ + return x ? ((!creal(x) && cimag(x) < 0) || (creal(x) < 0) ? -1 : 1) : 0; +} diff --git a/pyccel/stdlib/numpy/numpy_c.h b/pyccel/stdlib/numpy/numpy_c.h new file mode 100644 index 0000000000..4133e9dbe9 --- /dev/null +++ b/pyccel/stdlib/numpy/numpy_c.h @@ -0,0 +1,20 @@ +/* --------------------------------------------------------------------------------------- */ +/* This file is part of Pyccel which is released under MIT License. See the LICENSE file */ +/* or go to https://github.com/pyccel/pyccel/blob/master/LICENSE for full license details. */ +/* --------------------------------------------------------------------------------------- */ + +#ifndef NUMPY_H +# define NUMPY_H + +# include +# include +# include +# include + +#define SIGN(x) (x ? (x < 0 ? -1 : 1) : 0) + +long long int isign(long long int x); +double fsign(double x); +double complex csign(double complex x); + +#endif diff --git a/pyccel/stdlib/numpy/numpy_f90.f90 b/pyccel/stdlib/numpy/numpy_f90.f90 new file mode 100644 index 0000000000..7e057225d1 --- /dev/null +++ b/pyccel/stdlib/numpy/numpy_f90.f90 @@ -0,0 +1,156 @@ +! ! --------------------------------------------------------------------------------------- ! +! ! This file is part of Pyccel which is released under MIT License. See the LICENSE file ! +! ! or go to https://github.com/pyccel/pyccel/blob/master/LICENSE for full license details. ! +! ! --------------------------------------------------------------------------------------- ! + +module numpy_f90 + + + use, intrinsic :: ISO_C_Binding, only : i64 => C_INT64_T , f32 => & + C_FLOAT , i32 => C_INT32_T , f64 => C_DOUBLE , i8 => C_INT8_T , & + c64 => C_DOUBLE_COMPLEX , i16 => C_INT16_T , c32 => & + C_FLOAT_COMPLEX + implicit none + + private + + public :: numpy_sign + + interface numpy_sign + module procedure numpy_sign_i8 + module procedure numpy_sign_i16 + module procedure numpy_sign_i32 + module procedure numpy_sign_i64 + module procedure numpy_sign_f32 + module procedure numpy_sign_f64 + module procedure numpy_sign_c32 + module procedure numpy_sign_c64 + end interface + + contains + + !........................................ + elemental function numpy_sign_i8(x) result(Out_0001) + + implicit none + + integer(i8) :: Out_0001 + integer(i8), value :: x + + Out_0001 = merge(0_i8, (merge(1_i8, -1_i8, x > 0_i8)), x == 0_i8) + return + + end function numpy_sign_i8 + !........................................ + + !........................................ + elemental function numpy_sign_i16(x) result(Out_0001) + + implicit none + + integer(i16) :: Out_0001 + integer(i16), value :: x + + Out_0001 = merge(0_i16, (merge(1_i16, -1_i16, x > 0_i16)), x == 0_i16) + return + + end function numpy_sign_i16 + !........................................ + + !........................................ + elemental function numpy_sign_i32(x) result(Out_0001) + + implicit none + + integer(i32) :: Out_0001 + integer(i32), value :: x + + Out_0001 = merge(0_i32, (merge(1_i32, -1_i32, x > 0_i32)), x == 0_i32) + return + + end function numpy_sign_i32 + !........................................ + + !........................................ + elemental function numpy_sign_i64(x) result(Out_0001) + + implicit none + + integer(i64) :: Out_0001 + integer(i64), value :: x + + Out_0001 = merge(0_i64, (merge(1_i64, -1_i64, x > 0_i64)), x == 0_i64) + return + + end function numpy_sign_i64 + !........................................ + + !........................................ + elemental function numpy_sign_f32(x) result(Out_0001) + + implicit none + + real(f32) :: Out_0001 + real(f32), value :: x + + Out_0001 = merge(0_f32, (merge(1_f32, -1_f32, x > 0_f32)), x == 0_f32) + return + + end function numpy_sign_f32 + !........................................ + + !........................................ + elemental function numpy_sign_f64(x) result(Out_0001) + + implicit none + + real(f64) :: Out_0001 + real(f64), value :: x + + Out_0001 = merge(0_f64, (merge(1_f64, -1_f64, x > 0_f64)), x == 0_f64) + return + + end function numpy_sign_f64 + !........................................ + + !........................................ + elemental function numpy_sign_c32(x) result(Out_0001) + + implicit none + + complex(c32) :: Out_0001 + complex(c32), value :: x + logical :: x_ne_zero ! Condition for x different than 0 + logical :: x_lt_zero ! Condition for x less than 0 + + x_ne_zero = (REALPART(x) .ne. 0_f32) .or. (IMAGPART(x) .ne. 0_f32) + x_lt_zero = ((REALPART(x) .eq. 0_f32) .and. IMAGPART(x) .lt. 0_f32) & + .or. (REALPART(x) .lt. 0_f32) + + Out_0001 = merge(merge(-1_c32, 1_c32, x_lt_zero), 0_c32, x_ne_zero) + return + + end function numpy_sign_c32 + !........................................ + + !........................................ + elemental function numpy_sign_c64(x) result(Out_0001) + + implicit none + + complex(c64) :: Out_0001 + complex(c64), value :: x + logical :: x_ne_zero ! Condition for x different than 0 + logical :: x_lt_zero ! Condition for x less than 0 + + x_ne_zero = (REALPART(x) .ne. 0_f64) .or. (IMAGPART(x) .ne. 0_f64) + x_lt_zero = ((REALPART(x) .eq. 0_f64) .and. IMAGPART(x) .lt. 0_f64) & + .or. (REALPART(x) .lt. 0_f64) + + Out_0001 = merge(merge(-1_c64, 1_c64, x_lt_zero), 0_c64, x_ne_zero) + return + + end function numpy_sign_c64 + !........................................ + + end module numpy_f90 diff --git a/pyccel/version.py b/pyccel/version.py index 050c34f9dc..fea6104e13 100644 --- a/pyccel/version.py +++ b/pyccel/version.py @@ -1,4 +1,4 @@ """ Module specifying the current version string for pyccel """ -__version__ = "1.6.0" +__version__ = "1.7.0" diff --git a/pyproject.toml b/pyproject.toml index c3118a9127..9f6ed0ea68 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,10 @@ [build-system] requires = [ "setuptools >= 37, < 61", - "numpy", + "numpy >= 1.16", "sympy>=1.2", - "termcolor", + "termcolor >= 1.0.0", "textx>=2.2", - "filelock", + "filelock >= 3.4.0", ] build-backend = "setuptools.build_meta" diff --git a/setup.cfg b/setup.cfg index 1f4fdcb0e4..1fbf13e34c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -13,15 +13,24 @@ long_description_content_type = text/markdown [options] packages = find: install_requires = - numpy - sympy>=1.2 - termcolor - textx>=2.2 - filelock + numpy >= 1.16 + sympy >= 1.2 + termcolor >= 1.0.0 + textx >= 2.2 + filelock >= 3.4.0 python_requires = >= 3.7 zip_safe = False include_package_data = True +[options.extras_require] +test = + pytest >= 2.7 + scipy >= 1.5.0 + mpi4py >= 3.0.0 + coverage >= 3.1 + astunparse >= 1.6.0 + pytest-xdist >= 1.16 + [options.entry_points] console_scripts = pyccel = pyccel.commands.console:pyccel diff --git a/tests/epyccel/modules/arrays.py b/tests/epyccel/modules/arrays.py index 01ae040a42..1315866fed 100644 --- a/tests/epyccel/modules/arrays.py +++ b/tests/epyccel/modules/arrays.py @@ -1,12 +1,21 @@ # pylint: disable=missing-function-docstring, missing-module-docstring/ import numpy as np -from pyccel.decorators import types, stack_array, allow_negative_index +from pyccel.decorators import types, template, stack_array, allow_negative_index a_1d = np.array([1 << i for i in range(21)], dtype=int) a_2d_f = np.array([[1 << j for j in range(21)] for i in range(21)], dtype=int, order='F') a_2d_c = np.array([[1 << j for j in range(21)] for i in range(21)], dtype=int) + +@types('T', 'T') +@template(name='T' , types=['int', 'int8', 'int16', 'int32', 'int64', 'float', + 'float32', 'float64', 'complex64', 'complex128']) +def array_return_first_element(a, b): + from numpy import array + x = array([a,b]) + return x[0] + #============================================================================== # 1D ARRAYS OF INT-32 #============================================================================== @@ -287,6 +296,10 @@ def array_real_1d_scalar_mul( x, a ): def array_real_1d_scalar_div( x, a ): x[:] /= a +@types( 'real[:]', 'real') +def array_real_1d_scalar_mod( x, a ): + x[:] %= a + @types( 'real[:]', 'real' ) def array_real_1d_scalar_idiv( x, a ): x[:] = x // a @@ -307,6 +320,10 @@ def array_real_1d_mul( x, y ): def array_real_1d_div( x, y ): x[:] /= y +@types( 'real[:]', 'real[:]') +def array_real_1d_mod( x, y ): + x[:] %= y + @types( 'real[:]', 'real[:]' ) def array_real_1d_idiv( x, y ): x[:] = x // y @@ -331,6 +348,10 @@ def array_real_2d_C_scalar_mul( x, a ): def array_real_2d_C_scalar_div( x, a ): x[:,:] /= a +@types( 'real[:,:]', 'real' ) +def array_real_2d_C_scalar_mod( x, a ): + x[:,:] %= a + @types( 'real[:,:]', 'real[:,:]' ) def array_real_2d_C_add( x, y ): x[:,:] += y @@ -347,6 +368,10 @@ def array_real_2d_C_mul( x, y ): def array_real_2d_C_div( x, y ): x[:,:] /= y +@types( 'real[:,:]', 'real[:,:]' ) +def array_real_2d_C_mod( x, y ): + x[:,:] %= y + @types('real[:,:]') def array_real_2d_C_array_initialization(a): from numpy import array @@ -393,6 +418,10 @@ def array_real_2d_F_scalar_mul( x, a ): def array_real_2d_F_scalar_div( x, a ): x[:,:] /= a +@types( 'real[:,:](order=F)', 'real' ) +def array_real_2d_F_scalar_mod( x, a ): + x[:,:] %= a + @types( 'real[:,:](order=F)', 'real[:,:](order=F)' ) def array_real_2d_F_add( x, y ): x[:,:] += y @@ -409,6 +438,10 @@ def array_real_2d_F_mul( x, y ): def array_real_2d_F_div( x, y ): x[:,:] /= y +@types( 'real[:,:](order=F)', 'real[:,:](order=F)' ) +def array_real_2d_F_mod( x, y ): + x[:,:] %= y + @types('real[:,:](order=F)') def array_real_2d_F_array_initialization(a): from numpy import array @@ -1780,6 +1813,12 @@ def arr_arange_6(): a = np.arange(20, 1, -1.1) return np.shape(a)[0], a[0], a[-1] +def arr_arange_7(arr : 'int[:,:]'): + import numpy as np + n, m = arr.shape + for i in range(n): + arr[i] = np.arange(i, i+m) + def iterate_slice(i : int): import numpy as np a = np.arange(15) diff --git a/tests/epyccel/modules/augassign.py b/tests/epyccel/modules/augassign.py new file mode 100644 index 0000000000..bf3fbaeec4 --- /dev/null +++ b/tests/epyccel/modules/augassign.py @@ -0,0 +1,155 @@ +# pylint: disable=missing-function-docstring, missing-module-docstring/ + +from pyccel.decorators import types + +# += + +@types('int[:]') +def augassign_add_1d_int(a): + b = a + b += 42 + return b[0] + +@types('float[:]') +def augassign_add_1d_float(a): + b = a + b += 4.2 + return b[0] + +@types('complex[:]') +def augassign_add_1d_complex(a): + b = a + b += (4.0 + 2.0j) + return b[0] + +@types('int[:,:]') +def augassign_add_2d_int(a): + b = a + b += 42 + return b[0][0] + +@types('float[:,:]') +def augassign_add_2d_float(a): + b = a + b += 4.2 + return b[0][0] + +@types('complex[:,:]') +def augassign_add_2d_complex(a): + b = a + b += (4.0 + 2.0j) + return b[0][0] + +# -= + +@types('int[:]') +def augassign_sub_1d_int(a): + b = a + b -= 42 + return b[0] + +@types('float[:]') +def augassign_sub_1d_float(a): + b = a + b -= 4.2 + return b[0] + +@types('complex[:]') +def augassign_sub_1d_complex(a): + b = a + b -= (4.0 + 2.0j) + return b[0] + +@types('int[:,:]') +def augassign_sub_2d_int(a): + b = a + b -= 42 + return b[0][0] + +@types('float[:,:]') +def augassign_sub_2d_float(a): + b = a + b -= 4.2 + return b[0][0] + +@types('complex[:,:]') +def augassign_sub_2d_complex(a): + b = a + b -= (4.0 + 2.0j) + return b[0][0] + +# *= + +@types('int[:]') +def augassign_mul_1d_int(a): + b = a + b *= 42 + return b[0] + +@types('float[:]') +def augassign_mul_1d_float(a): + b = a + b *= 4.2 + return b[0] + +@types('complex[:]') +def augassign_mul_1d_complex(a): + b = a + b *= (4.0 + 2.0j) + return b[0] + +@types('int[:,:]') +def augassign_mul_2d_int(a): + b = a + b *= 42 + return b[0][0] + +@types('float[:,:]') +def augassign_mul_2d_float(a): + b = a + b *= 4.2 + return b[0][0] + +@types('complex[:,:]') +def augassign_mul_2d_complex(a): + b = a + b *= (4.0 + 2.0j) + return b[0][0] + +# /= + +@types('int[:]') +def augassign_div_1d_int(a): + b = a + b /= 42 + return b[0] + +@types('float[:]') +def augassign_div_1d_float(a): + b = a + b /= 4.2 + return b[0] + +@types('complex[:]') +def augassign_div_1d_complex(a): + b = a + b /= (4.0 + 2.0j) + return b[0] + +@types('int[:,:]') +def augassign_div_2d_int(a): + b = a + b /= 42 + return b[0][0] + +@types('float[:,:]') +def augassign_div_2d_float(a): + b = a + b /= 4.2 + return b[0][0] + +@types('complex[:,:]') +def augassign_div_2d_complex(a): + b = a + b /= (4.0 + 2.0j) + return b[0][0] diff --git a/tests/epyccel/modules/call_user_defined_funcs.py b/tests/epyccel/modules/call_user_defined_funcs.py index 9e32e6cf3a..3c3c614bee 100644 --- a/tests/epyccel/modules/call_user_defined_funcs.py +++ b/tests/epyccel/modules/call_user_defined_funcs.py @@ -33,3 +33,12 @@ def circle_volume(radius): volume = my_mult(my_mult(my_div(3. , 4.), my_pi()), my_cub(radius)) not_change(volume) return volume + +def arr_mult_scalar(T: 'int[:]', t: int = 13): + x = T * t + return x + +def alias(T: 'int[:]', t: int): + x = arr_mult_scalar(T, t=t) + y = arr_mult_scalar(t=t, T=T) + return x, y diff --git a/tests/epyccel/modules/numpy_sign.py b/tests/epyccel/modules/numpy_sign.py new file mode 100644 index 0000000000..db5403a90b --- /dev/null +++ b/tests/epyccel/modules/numpy_sign.py @@ -0,0 +1,295 @@ +# pylint: disable=missing-function-docstring, missing-module-docstring\ + +from pyccel.decorators import types + +def complex_nul(): + import numpy as np + b = np.sign(complex(0+0j)) + return b + +def complex_pos(): + import numpy as np + b = np.sign(complex(1+2j)) + return b + +def complex_neg(): + import numpy as np + b = np.sign(complex(-1-2j)) + return b + +def complex64_nul(): + import numpy as np + b = np.sign(np.complex64(0+0j)) + return b + +def complex64_pos(): + import numpy as np + b = np.sign(np.complex64(64+64j)) + return b + +def complex64_neg(): + import numpy as np + b = np.sign(np.complex64(-64-64j)) + return b + +def complex128_nul(): + import numpy as np + b = np.sign(np.complex128(0+0j)) + return b + +def complex128_pos(): + import numpy as np + b = np.sign(np.complex128(128+128j)) + return b + +def complex128_neg(): + import numpy as np + b = np.sign(np.complex128(-128-128j)) + return b + +def complex_pos_neg(): + import numpy as np + b = np.sign(complex(1-2j)) + return b + +def complex_neg_pos(): + import numpy as np + b = np.sign(complex(-1+2j)) + return b + +def complex64_pos_neg(): + import numpy as np + b = np.sign(np.complex64(64-64j)) + return b + +def complex64_neg_pos(): + import numpy as np + b = np.sign(np.complex64(-64+64j)) + return b + +def complex128_pos_neg(): + import numpy as np + b = np.sign(np.complex128(128-128j)) + return b + +def complex128_neg_pos(): + import numpy as np + b = np.sign(np.complex128(-128+128j)) + return b + +def int16_pos(): + import numpy as np + b = np.sign(np.int16(16)) + return b + +def int16_neg(): + import numpy as np + b = np.sign(np.int16(-16)) + return b + +def int32_pos(): + import numpy as np + b = np.sign(np.int32(32)) + return b + +def int32_neg(): + import numpy as np + b = np.sign(np.int32(-32)) + return b + +def int64_pos(): + import numpy as np + b = np.sign(np.int64(64)) + return b + +def int64_neg(): + import numpy as np + b = np.sign(np.int64(-64)) + return b + +def float_pos(): + import numpy as np + b = np.sign(float(32.32)) + return b + +def float_neg(): + import numpy as np + b = np.sign(float(-32.32)) + return b + +def float_nul(): + import numpy as np + b = np.sign(float(0.0)) + return b + +def float64_pos(): + import numpy as np + b = np.sign(np.float64(64.64)) + return b + +def float64_neg(): + import numpy as np + b = np.sign(np.float64(-64.64)) + return b + +def literal_complex_pos(): + import numpy as np + b = np.sign(1+2j) + return b + +def literal_complex_neg(): + import numpy as np + b = np.sign(-1-2j) + return b + +def literal_complex_nul_imag(): + import numpy as np + b = np.sign(0-42j) + return b + +def literal_complex_real_nul(): + import numpy as np + b = np.sign(-42+0j) + return b + +def literal_complex_nul_nul(): + import numpy as np + b = np.sign(-0-0j) + return b + +def literal_int_pos(): + import numpy as np + b = np.sign(42) + return b + +def literal_int_neg(): + import numpy as np + b = np.sign(-42) + return b + +def literal_int_nul(): + import numpy as np + b = np.sign(0) + return b + +def literal_float_pos(): + import numpy as np + b = np.sign(42.42) + return b + +def literal_float_neg(): + import numpy as np + b = np.sign(-42.42) + return b + +def literal_float_nul(): + import numpy as np + b = np.sign(0.0) + return b + +################### +# Arrays tests +################### + +# Intergers + +@types('int8[:]') +def array_1d_int8(x): + import numpy as np + y = np.sign(x) + return y + +@types('int16[:]') +def array_1d_int16(x): + import numpy as np + y = np.sign(x) + return y + +@types('int32[:]') +def array_1d_int32(x): + import numpy as np + y = np.sign(x) + return y + +@types('int64[:]') +def array_1d_int64(x): + import numpy as np + y = np.sign(x) + return y + +@types('int8[:,:]') +def array_2d_int8(x): + import numpy as np + y = np.sign(x) + return y + +@types('int16[:,:]') +def array_2d_int16(x): + import numpy as np + y = np.sign(x) + return y + +@types('int32[:,:]') +def array_2d_int32(x): + import numpy as np + y = np.sign(x) + return y + +@types('int64[:,:]') +def array_2d_int64(x): + import numpy as np + y = np.sign(x) + return y + +# Floats + +@types('float32[:]') +def array_1d_float32(x): + import numpy as np + y = np.sign(x) + return y + +@types('float64[:]') +def array_1d_float64(x): + import numpy as np + y = np.sign(x) + return y + +@types('float32[:,:]') +def array_2d_float32(x): + import numpy as np + y = np.sign(x) + return y + +@types('float64[:,:]') +def array_2d_float64(x): + import numpy as np + y = np.sign(x) + return y + +# Complexs + +@types('complex64[:]') +def array_1d_complex64(x): + import numpy as np + y = np.sign(x) + return y + +@types('complex128[:]') +def array_1d_complex128(x): + import numpy as np + y = np.sign(x) + return y + +@types('complex64[:,:]') +def array_2d_complex64(x): + import numpy as np + y = np.sign(x) + return y + + +@types('complex128[:,:]') +def array_2d_complex128(x): + import numpy as np + y = np.sign(x) + return y diff --git a/tests/epyccel/recognised_functions/test_numpy_funcs.py b/tests/epyccel/recognised_functions/test_numpy_funcs.py index cefe285639..4be09eeb20 100644 --- a/tests/epyccel/recognised_functions/test_numpy_funcs.py +++ b/tests/epyccel/recognised_functions/test_numpy_funcs.py @@ -5,7 +5,7 @@ from numpy import isclose, iinfo, finfo import numpy as np -from pyccel.decorators import types +from pyccel.decorators import types, template from pyccel.epyccel import epyccel min_int8 = iinfo('int8').min @@ -154,8 +154,8 @@ def absolute_call_r(x): f1 = epyccel(absolute_call_r, language = language) x = uniform(high=1e6) - assert(isclose(f1(x), absolute_call_r(x), rtol=RTOL, atol=ATOL)) - assert(isclose(f1(-x), absolute_call_r(-x), rtol=RTOL, atol=ATOL)) + assert f1(x) == absolute_call_r(x) + assert f1(-x) == absolute_call_r(-x) assert matching_types(f1(x), absolute_call_r(x)) def test_absolute_call_i(language): @@ -166,10 +166,31 @@ def absolute_call_i(x): f1 = epyccel(absolute_call_i, language = language) x = randint(1e6) - assert(isclose(f1(x), absolute_call_i(x), rtol=RTOL, atol=ATOL)) - assert(isclose(f1(-x), absolute_call_i(-x), rtol=RTOL, atol=ATOL)) + assert f1(x) == absolute_call_i(x) + assert f1(-x) == absolute_call_i(-x) assert matching_types(f1(x), absolute_call_i(x)) +def test_absolute_call_c(language): + @template(name='T', types=['complex','complex64','complex128']) + @types('T') + def absolute_call_c(x): + from numpy import absolute + return absolute(x) + + f1 = epyccel(absolute_call_c, language = language) + x = uniform(high=1e6)+1j*uniform(high=1e6) + assert(isclose(f1(x), absolute_call_c(x), rtol=RTOL, atol=ATOL)) + assert(isclose(f1(-x), absolute_call_c(-x), rtol=RTOL, atol=ATOL)) + assert matching_types(f1(x), absolute_call_c(x)) + + x = np.complex64(uniform(high=1e6)-1j*uniform(high=1e6)) + assert(isclose(f1(x), absolute_call_c(x), rtol=RTOL32, atol=ATOL32)) + assert matching_types(f1(x), absolute_call_c(x)) + + x = np.complex128(uniform(high=1e6)-1j*uniform(high=1e6)) + assert(isclose(f1(x), absolute_call_c(x), rtol=RTOL, atol=ATOL)) + assert matching_types(f1(x), absolute_call_c(x)) + def test_absolute_phrase_r_r(language): @types('real','real') def absolute_phrase_r_r(x,y): @@ -1413,6 +1434,69 @@ def create_full_val_real_complex128(val): assert(isclose( f_real_complex128(val_float) , create_full_val_real_complex128(val_float), rtol=RTOL, atol=ATOL)) assert matching_types(f_real_complex128(val_float), create_full_val_real_complex128(val_float)) +@pytest.mark.parametrize( 'language', ( + pytest.param("fortran", marks = pytest.mark.fortran), + pytest.param("c", marks = pytest.mark.c), + pytest.param("python", marks = [ + pytest.mark.skip("full handles types in __new__ so it " + "cannot be used in a translated interface in python"), + pytest.mark.python] + ), + ) +) + +def test_full_dtype_auto(language): + @types('T') + @template(name='T', types=['int','float', 'complex', 'int32', + 'float32', 'float64', 'complex64', 'complex128']) + def create_full_val_auto(val): + from numpy import full + a = full(3,val) + return a[0] + + integer = randint(low = min_int, high = max_int, dtype=int) + integer32 = randint(low = min_int32, high = max_int32, dtype=np.int32) + + fl = float(integer) + fl32 = np.float32(fl) + fl64 = np.float64(fl) + + cmplx = complex(integer) + cmplx64 = np.complex64(fl32) + cmplx128 = np.complex128(fl64) + + f_int = epyccel(create_full_val_auto, language = language) + assert(f_int(integer) == create_full_val_auto(integer)) + assert matching_types(f_int(integer), create_full_val_auto(integer)) + + f_float = epyccel(create_full_val_auto, language = language) + assert(isclose(f_float(fl), create_full_val_auto(fl), rtol=RTOL, atol=ATOL)) + assert matching_types(f_float(fl), create_full_val_auto(fl)) + + f_complex = epyccel(create_full_val_auto, language = language) + assert(isclose(f_complex(cmplx), create_full_val_auto(cmplx), rtol=RTOL, atol=ATOL)) + assert matching_types(f_complex(cmplx), create_full_val_auto(cmplx)) + + f_int32 = epyccel(create_full_val_auto, language = language) + assert(f_int32(integer32) == create_full_val_auto(integer32)) + assert matching_types(f_int32(integer32), create_full_val_auto(integer32)) + + f_float32 = epyccel(create_full_val_auto, language = language) + assert(isclose(f_float32(fl32) , create_full_val_auto(fl32), rtol=RTOL, atol=ATOL)) + assert matching_types(f_float32(fl32), create_full_val_auto(fl32)) + + f_float64 = epyccel(create_full_val_auto, language = language) + assert(isclose(f_float64(fl64) , create_full_val_auto(fl64), rtol=RTOL, atol=ATOL)) + assert matching_types(f_float64(fl64), create_full_val_auto(fl64)) + + f_complex64 = epyccel(create_full_val_auto, language = language) + assert(isclose(f_complex64(cmplx64) , create_full_val_auto(cmplx64), rtol=RTOL, atol=ATOL)) + assert matching_types(f_complex64(cmplx64), create_full_val_auto(cmplx64)) + + f_complex128 = epyccel(create_full_val_auto, language = language) + assert(isclose(f_complex128(cmplx128) , create_full_val_auto(cmplx128), rtol=RTOL, atol=ATOL)) + assert matching_types(f_complex128(cmplx128), create_full_val_auto(cmplx128)) + def test_full_combined_args(language): def create_full_1_shape(): from numpy import full, shape @@ -2568,47 +2652,102 @@ def create_full_like_shape_F(n): ) def test_full_like_dtype(language): @types('int') + def create_full_like_val_int_int_auto(val): + from numpy import full_like, array + arr = array([5, 1, 8, 0, 9], int) + a = full_like(arr,val) + return a[0] + @types('int') def create_full_like_val_int_int(val): from numpy import full_like, array arr = array([5, 1, 8, 0, 9]) a = full_like(arr,val,int) return a[0] + + @types('int') + def create_full_like_val_int_float_auto(val): + from numpy import full_like, array + arr = array([5, 1, 8, 0, 9], float) + a = full_like(arr,val) + return a[0] @types('int') def create_full_like_val_int_float(val): from numpy import full_like, array arr = array([5, 1, 8, 0, 9]) a = full_like(arr,val,float) return a[0] + + @types('int') + def create_full_like_val_int_complex_auto(val): + from numpy import full_like, array + arr = array([5, 1, 8, 0, 9], complex) + a = full_like(arr,val) + return a[0] @types('int') def create_full_like_val_int_complex(val): from numpy import full_like, array arr = array([5, 1, 8, 0, 9]) a = full_like(arr,val,complex) return a[0] + + @types('real') + def create_full_like_val_real_int32_auto(val): + from numpy import full_like, int32, array + arr = array([5, 1, 8, 0, 9], int32) + a = full_like(arr,val) + return a[0] @types('real') def create_full_like_val_real_int32(val): from numpy import full_like, int32, array arr = array([5, 1, 8, 0, 9]) a = full_like(arr,val,int32) return a[0] + + @types('real') + def create_full_like_val_real_float32_auto(val): + from numpy import full_like, float32, array + arr = array([5, 1, 8, 0, 9], float32) + a = full_like(arr,val) + return a[0] @types('real') def create_full_like_val_real_float32(val): from numpy import full_like, float32, array arr = array([5, 1, 8, 0, 9]) a = full_like(arr,val,float32) return a[0] + + @types('real') + def create_full_like_val_real_float64_auto(val): + from numpy import full_like, float64, array + arr = array([5, 1, 8, 0, 9], float64) + a = full_like(arr,val) + return a[0] @types('real') def create_full_like_val_real_float64(val): from numpy import full_like, float64, array arr = array([5, 1, 8, 0, 9]) a = full_like(arr,val,float64) return a[0] + + @types('real') + def create_full_like_val_real_complex64_auto(val): + from numpy import full_like, complex64, array + arr = array([5, 1, 8, 0, 9], complex64) + a = full_like(arr,val) + return a[0] @types('real') def create_full_like_val_real_complex64(val): from numpy import full_like, complex64, array arr = array([5, 1, 8, 0, 9]) a = full_like(arr,val,complex64) return a[0] + + @types('real') + def create_full_like_val_real_complex128_auto(val): + from numpy import full_like, complex128, array + arr = array([5, 1, 8, 0, 9], complex128) + a = full_like(arr,val) + return a[0] @types('real') def create_full_like_val_real_complex128(val): from numpy import full_like, complex128, array @@ -2651,6 +2790,38 @@ def create_full_like_val_real_complex128(val): assert(isclose( f_real_complex128(val_float) , create_full_like_val_real_complex128(val_float), rtol=RTOL, atol=ATOL)) assert matching_types(f_real_complex128(val_float), create_full_like_val_real_complex128(val_float)) + f_int_int_auto = epyccel(create_full_like_val_int_int_auto, language = language) + assert( f_int_int_auto(val_int) == create_full_like_val_int_int_auto(val_int)) + assert matching_types(f_int_int(val_int), create_full_like_val_int_int_auto(val_int)) + + f_int_float_auto = epyccel(create_full_like_val_int_float_auto, language = language) + assert(isclose( f_int_float_auto(val_int) , create_full_like_val_int_float_auto(val_int), rtol=RTOL, atol=ATOL)) + assert matching_types(f_int_float_auto(val_int), create_full_like_val_int_float_auto(val_int)) + + f_int_complex_auto = epyccel(create_full_like_val_int_complex_auto, language = language) + assert(isclose( f_int_complex_auto(val_int) , create_full_like_val_int_complex_auto(val_int), rtol=RTOL, atol=ATOL)) + assert matching_types(f_int_complex_auto(val_int), create_full_like_val_int_complex_auto(val_int)) + + f_real_int32_auto = epyccel(create_full_like_val_real_int32_auto, language = language) + assert( f_real_int32_auto(val_float) == create_full_like_val_real_int32_auto(val_float)) + assert matching_types(f_real_int32_auto(val_float), create_full_like_val_real_int32_auto(val_float)) + + f_real_float32_auto = epyccel(create_full_like_val_real_float32_auto, language = language) + assert(isclose( f_real_float32_auto(val_float) , create_full_like_val_real_float32_auto(val_float), rtol=RTOL, atol=ATOL)) + assert matching_types(f_real_float32_auto(val_float), create_full_like_val_real_float32_auto(val_float)) + + f_real_float64_auto = epyccel(create_full_like_val_real_float64_auto, language = language) + assert(isclose( f_real_float64_auto(val_float) , create_full_like_val_real_float64_auto(val_float), rtol=RTOL, atol=ATOL)) + assert matching_types(f_real_float64_auto(val_float), create_full_like_val_real_float64_auto(val_float)) + + f_real_complex64_auto = epyccel(create_full_like_val_real_complex64_auto, language = language) + assert(isclose( f_real_complex64_auto(val_float) , create_full_like_val_real_complex64_auto(val_float), rtol=RTOL, atol=ATOL)) + assert matching_types(f_real_complex64_auto(val_float), create_full_like_val_real_complex64_auto(val_float)) + + f_real_complex128_auto = epyccel(create_full_like_val_real_complex128_auto, language = language) + assert(isclose( f_real_complex128_auto(val_float) , create_full_like_val_real_complex128_auto(val_float), rtol=RTOL, atol=ATOL)) + assert matching_types(f_real_complex128_auto(val_float), create_full_like_val_real_complex128_auto(val_float)) + def test_full_like_combined_args(language): def create_full_like_1_shape(): from numpy import full_like, shape, array @@ -2756,48 +2927,96 @@ def create_empty_like_shape_F(n,m): def test_empty_like_dtype(language): + def create_empty_like_val_int_auto(): + from numpy import empty_like, array + arr = array([5, 1, 8, 0, 9], dtype=int) + a = empty_like(arr) + return a[0] + def create_empty_like_val_int(): from numpy import empty_like, array arr = array([5, 1, 8, 0, 9]) a = empty_like(arr, int) return a[0] + def create_empty_like_val_float_auto(): + from numpy import empty_like, array + arr = array([5, 1, 8, 0, 9], dtype=float) + a = empty_like(arr) + return a[0] + def create_empty_like_val_float(): from numpy import empty_like, array arr = array([5, 1, 8, 0, 9]) a = empty_like(arr, dtype=float) return a[0] + def create_empty_like_val_complex_auto(): + from numpy import empty_like, array + arr = array([5, 1, 8, 0, 9], dtype=complex) + a = empty_like(arr) + return a[0] + def create_empty_like_val_complex(): from numpy import empty_like, array arr = array([5, 1, 8, 0, 9]) a = empty_like(arr, dtype=complex) return a[0] + def create_empty_like_val_int32_auto(): + from numpy import empty_like, array, int32 + arr = array([5, 1, 8, 0, 9], dtype=int32) + a = empty_like(arr) + return a[0] + def create_empty_like_val_int32(): from numpy import empty_like, int32, array arr = array([5, 1, 8, 0, 9]) a = empty_like(arr, dtype=int32) return a[0] + def create_empty_like_val_float32_auto(): + from numpy import empty_like, array, float32 + arr = array([5, 1, 8, 0, 9], dtype='float32') + a = empty_like(arr) + return a[0] + def create_empty_like_val_float32(): from numpy import empty_like, float32, array arr = array([5, 1, 8, 0, 9]) a = empty_like(arr, dtype=float32) return a[0] + def create_empty_like_val_float64_auto(): + from numpy import empty_like, array, float64 + arr = array([5, 1, 8, 0, 9], dtype=float64) + a = empty_like(arr) + return a[0] + def create_empty_like_val_float64(): from numpy import empty_like, float64, array arr = array([5, 1, 8, 0, 9]) a = empty_like(arr,dtype=float64) return a[0] + def create_empty_like_val_complex64_auto(): + from numpy import empty_like, array, complex64 + arr = array([5, 1, 8, 0, 9], dtype=complex64) + a = empty_like(arr) + return a[0] + def create_empty_like_val_complex64(): from numpy import empty_like, complex64, array arr = array([5, 1, 8, 0, 9]) a = empty_like(arr,dtype=complex64) return a[0] + def create_empty_like_val_complex128_auto(): + from numpy import empty_like, array, complex128 + arr = array([5, 1, 8, 0, 9], dtype=complex128) + a = empty_like(arr) + return a[0] + def create_empty_like_val_complex128(): from numpy import empty_like, complex128, array arr = array([5, 1, 8, 0, 9]) @@ -2805,27 +3024,52 @@ def create_empty_like_val_complex128(): return a[0] + f_int_auto = epyccel(create_empty_like_val_int_auto, language = language) + assert matching_types(f_int_auto(), create_empty_like_val_int_auto()) + f_int_int = epyccel(create_empty_like_val_int, language = language) assert matching_types(f_int_int(), create_empty_like_val_int()) + f_float_auto = epyccel(create_empty_like_val_float_auto, language = language) + assert matching_types(f_float_auto(), create_empty_like_val_float_auto()) + f_int_float = epyccel(create_empty_like_val_float, language = language) assert matching_types(f_int_float(), create_empty_like_val_float()) + f_complex_auto = epyccel(create_empty_like_val_complex_auto, language = language) + assert matching_types(f_complex_auto(), create_empty_like_val_complex_auto()) + f_int_complex = epyccel(create_empty_like_val_complex, language = language) assert matching_types(f_int_complex(), create_empty_like_val_complex()) + f_int32_auto = epyccel(create_empty_like_val_int32_auto, language = language) + assert matching_types(f_int32_auto(), create_empty_like_val_int32_auto()) + f_real_int32 = epyccel(create_empty_like_val_int32, language = language) assert matching_types(f_real_int32(), create_empty_like_val_int32()) + f_float32_auto = epyccel(create_empty_like_val_float32_auto, language = language) + assert matching_types(f_float32_auto(), create_empty_like_val_float32_auto()) + f_real_float32 = epyccel(create_empty_like_val_float32, language = language) assert matching_types(f_real_float32(), create_empty_like_val_float32()) + f_float64_auto = epyccel(create_empty_like_val_float64_auto, language = language) + assert matching_types(f_float64_auto(), create_empty_like_val_float64_auto()) + f_real_float64 = epyccel(create_empty_like_val_float64, language = language) assert matching_types(f_real_float64(), create_empty_like_val_float64()) + f_complex64_auto = epyccel(create_empty_like_val_complex64_auto, language = language) + + assert matching_types(f_complex64_auto(), create_empty_like_val_complex64_auto()) + f_real_complex64 = epyccel(create_empty_like_val_complex64, language = language) assert matching_types(f_real_complex64(), create_empty_like_val_complex64()) + f_complex128_auto = epyccel(create_empty_like_val_complex128_auto, language = language) + assert matching_types(f_complex128_auto(), create_empty_like_val_complex128_auto()) + f_real_complex128 = epyccel(create_empty_like_val_complex128, language = language) assert matching_types(f_real_complex128(), create_empty_like_val_complex128()) @@ -2936,48 +3180,96 @@ def create_ones_like_shape_F(n,m): def test_ones_like_dtype(language): + def create_ones_like_val_int_auto(): + from numpy import ones_like, array + arr = array([5, 1, 8, 0, 9], int) + a = ones_like(arr) + return a[0] + def create_ones_like_val_int(): from numpy import ones_like, array arr = array([5, 1, 8, 0, 9]) a = ones_like(arr, int) return a[0] + def create_ones_like_val_float_auto(): + from numpy import ones_like, array + arr = array([5, 1, 8, 0, 9], float) + a = ones_like(arr) + return a[0] + def create_ones_like_val_float(): from numpy import ones_like, array arr = array([5, 1, 8, 0, 9]) a = ones_like(arr,float) return a[0] + def create_ones_like_val_complex_auto(): + from numpy import ones_like, array + arr = array([5, 1, 8, 0, 9], complex) + a = ones_like(arr) + return a[0] + def create_ones_like_val_complex(): from numpy import ones_like, array arr = array([5, 1, 8, 0, 9]) a = ones_like(arr, complex) return a[0] + def create_ones_like_val_int32_auto(): + from numpy import ones_like, int32, array + arr = array([5, 1, 8, 0, 9], int32) + a = ones_like(arr) + return a[0] + def create_ones_like_val_int32(): from numpy import ones_like, int32, array arr = array([5, 1, 8, 0, 9]) a = ones_like(arr,int32) return a[0] + def create_ones_like_val_float32_auto(): + from numpy import ones_like, float32, array + arr = array([5, 1, 8, 0, 9], float32) + a = ones_like(arr) + return a[0] + def create_ones_like_val_float32(): from numpy import ones_like, float32, array arr = array([5, 1, 8, 0, 9]) a = ones_like(arr, float32) return a[0] + def create_ones_like_val_float64_auto(): + from numpy import ones_like, float64, array + arr = array([5, 1, 8, 0, 9], float64) + a = ones_like(arr) + return a[0] + def create_ones_like_val_float64(): from numpy import ones_like, float64, array arr = array([5, 1, 8, 0, 9]) a = ones_like(arr, float64) return a[0] + def create_ones_like_val_complex64_auto(): + from numpy import ones_like, complex64, array + arr = array([5, 1, 8, 0, 9], complex64) + a = ones_like(arr) + return a[0] + def create_ones_like_val_complex64(): from numpy import ones_like, complex64, array arr = array([5, 1, 8, 0, 9]) a = ones_like(arr, complex64) return a[0] + def create_ones_like_val_complex128_auto(): + from numpy import ones_like, complex128, array + arr = array([5, 1, 8, 0, 9], complex128) + a = ones_like(arr) + return a[0] + def create_ones_like_val_complex128(): from numpy import ones_like, complex128, array arr = array([5, 1, 8, 0, 9]) @@ -3017,6 +3309,38 @@ def create_ones_like_val_complex128(): assert(isclose( f_real_complex128() , create_ones_like_val_complex128(), rtol=RTOL, atol=ATOL)) assert matching_types(f_real_complex128(), create_ones_like_val_complex128()) + f_int_int_auto = epyccel(create_ones_like_val_int_auto, language = language) + assert( f_int_int_auto() == create_ones_like_val_int_auto()) + assert matching_types(f_int_int_auto(), create_ones_like_val_int_auto()) + + f_int_float_auto = epyccel(create_ones_like_val_float_auto, language = language) + assert(isclose( f_int_float_auto() , create_ones_like_val_float_auto(), rtol=RTOL, atol=ATOL)) + assert matching_types(f_int_float_auto(), create_ones_like_val_float_auto()) + + f_int_complex_auto = epyccel(create_ones_like_val_complex_auto, language = language) + assert(isclose( f_int_complex_auto() , create_ones_like_val_complex_auto(), rtol=RTOL, atol=ATOL)) + assert matching_types(f_int_complex_auto(), create_ones_like_val_complex_auto()) + + f_real_int32_auto = epyccel(create_ones_like_val_int32_auto, language = language) + assert( f_real_int32_auto() == create_ones_like_val_int32_auto()) + assert matching_types(f_real_int32_auto(), create_ones_like_val_int32_auto()) + + f_real_float32_auto = epyccel(create_ones_like_val_float32_auto, language = language) + assert(isclose( f_real_float32_auto() , create_ones_like_val_float32_auto(), rtol=RTOL, atol=ATOL)) + assert matching_types(f_real_float32_auto(), create_ones_like_val_float32_auto()) + + f_real_float64_auto = epyccel(create_ones_like_val_float64_auto, language = language) + assert(isclose( f_real_float64_auto() , create_ones_like_val_float64_auto(), rtol=RTOL, atol=ATOL)) + assert matching_types(f_real_float64_auto(), create_ones_like_val_float64_auto()) + + f_real_complex64_auto = epyccel(create_ones_like_val_complex64_auto, language = language) + assert(isclose( f_real_complex64_auto() , create_ones_like_val_complex64_auto(), rtol=RTOL, atol=ATOL)) + assert matching_types(f_real_complex64_auto(), create_ones_like_val_complex64_auto()) + + f_real_complex128_auto = epyccel(create_ones_like_val_complex128_auto, language = language) + assert(isclose( f_real_complex128_auto() , create_ones_like_val_complex128_auto(), rtol=RTOL, atol=ATOL)) + assert matching_types(f_real_complex128_auto(), create_ones_like_val_complex128_auto()) + def test_ones_like_combined_args(language): def create_ones_like_1_shape(): @@ -3207,6 +3531,81 @@ def create_zeros_like_val_complex128(): assert(isclose( f_real_complex128() , create_zeros_like_val_complex128(), rtol=RTOL, atol=ATOL)) assert matching_types(f_real_complex128(), create_zeros_like_val_complex128()) +def test_zeros_like_dtype_auto(language): + + def create_zeros_like_val_int_auto(): + from numpy import zeros_like, array + arr = array([5, 1, 8, 0, 9], dtype=int) + a = zeros_like(arr) + return a[0] + + def create_zeros_like_val_float_auto(): + from numpy import zeros_like, array + arr = array([5, 1, 8, 0, 9], dtype=float) + a = zeros_like(arr) + return a[0] + + def create_zeros_like_val_complex_auto(): + from numpy import zeros_like, array + arr = array([5, 1, 8, 0, 9], dtype=complex) + a = zeros_like(arr) + return a[0] + + def create_zeros_like_val_int32_auto(): + from numpy import zeros_like, array, int32 + arr = array([5, 1, 8, 0, 9], dtype=int32) + a = zeros_like(arr) + return a[0] + + def create_zeros_like_val_float32_auto(): + from numpy import zeros_like, array, float32 + arr = array([5, 1, 8, 0, 9], dtype='float32') + a = zeros_like(arr) + return a[0] + + def create_zeros_like_val_float64_auto(): + from numpy import zeros_like, array, float64 + arr = array([5, 1, 8, 0, 9], dtype=float64) + a = zeros_like(arr) + return a[0] + + def create_zeros_like_val_complex64_auto(): + from numpy import zeros_like, array, complex64 + arr = array([5, 1, 8, 0, 9], dtype=complex64) + a = zeros_like(arr) + return a[0] + + def create_zeros_like_val_complex128_auto(): + from numpy import zeros_like, array, complex128 + arr = array([5, 1, 8, 0, 9], dtype=complex128) + a = zeros_like(arr) + return a[0] + + f_int_auto = epyccel(create_zeros_like_val_int_auto, language = language) + assert matching_types(f_int_auto(), create_zeros_like_val_int_auto()) + + f_float_auto = epyccel(create_zeros_like_val_float_auto, language = language) + assert matching_types(f_float_auto(), create_zeros_like_val_float_auto()) + + f_complex_auto = epyccel(create_zeros_like_val_complex_auto, language = language) + assert matching_types(f_complex_auto(), create_zeros_like_val_complex_auto()) + + f_int32_auto = epyccel(create_zeros_like_val_int32_auto, language = language) + assert matching_types(f_int32_auto(), create_zeros_like_val_int32_auto()) + + f_float32_auto = epyccel(create_zeros_like_val_float32_auto, language = language) + assert matching_types(f_float32_auto(), create_zeros_like_val_float32_auto()) + + f_float64_auto = epyccel(create_zeros_like_val_float64_auto, language = language) + assert matching_types(f_float64_auto(), create_zeros_like_val_float64_auto()) + + f_complex64_auto = epyccel(create_zeros_like_val_complex64_auto, language = language) + assert matching_types(f_complex64_auto(), create_zeros_like_val_complex64_auto()) + + f_complex128_auto = epyccel(create_zeros_like_val_complex128_auto, language = language) + assert matching_types(f_complex128_auto(), create_zeros_like_val_complex128_auto()) + + def test_zeros_like_combined_args(language): def create_zeros_like_1_shape(): @@ -4124,25 +4523,33 @@ def get_prod(arr): bl = randint(0, 2, size = size, dtype= bool) - integer8 = randint(min_int8, max_int8, size = size, dtype=np.int8) - integer16 = randint(min_int16, max_int16, size = size, dtype=np.int16) - integer = randint(min_int, max_int, size = size, dtype=int) - integer32 = randint(min_int32, max_int32, size = size, dtype=np.int32) - integer64 = randint(min_int64, max_int64, size = size, dtype=np.int64) + max_ok_int = int(max_int64 ** (1/5)) - fl = uniform(min_float / 2, max_float / 2, size = size) - fl32 = uniform(min_float32 / 2, max_float32 / 2, size = size) - fl32 = np.float32(fl32) - fl64 = uniform(min_float64 / 2, max_float64 / 2, size=size) + integer8 = randint(max(min_int8, -max_ok_int), min(max_ok_int, max_int8), size = size, dtype=np.int8) + integer16 = randint(max(min_int16, -max_ok_int), min(max_ok_int, max_int16), size = size, dtype=np.int16) + integer = randint(max(min_int, -max_ok_int), min(max_ok_int, max_int), size = size, dtype=int) + integer32 = randint(max(min_int32, -max_ok_int), min(max_ok_int, max_int32), size = size, dtype=np.int32) + integer64 = randint(-max_ok_int, max_ok_int, size = size, dtype=np.int64) + + fl = uniform(-((-min_float) ** (1/5)), max_float ** (1/5), size = size) + + min_ok_float32 = -((-min_float32) ** (1/5)) + min_ok_float64 = -((-min_float64) ** (1/5)) + max_ok_float32 = max_float32 ** (1/5) + max_ok_float64 = max_float64 ** (1/5) - cmplx128_from_float32 = uniform(low=-((-min_float32) ** (1/5)), - high=(max_float32 ** (1/5)), size = size) + \ - uniform(low=-((-min_float32) ** (1/5)), - high=(max_float32 ** (1/5)), size = size) * 1j - cmplx128_from_float64 = uniform(low=-((-min_float64) ** (1/5)), - high=(max_float64 ** (1/5)), size = size) + \ - uniform(low=-((-min_float64) ** (1/5)), - high=(max_float64 ** (1/5)), size = size) * 1j + fl32 = uniform(min_ok_float32, max_ok_float32, size = size) + fl32 = np.float32(fl32) + fl64 = uniform(min_ok_float64, max_ok_float64, size=size) + + cmplx128_from_float32 = uniform(low=min_ok_float32/2, + high=max_ok_float32/2, size = size) + \ + uniform(low=min_ok_float32/2, + high=max_ok_float32/2, size = size) * 1j + cmplx128_from_float64 = uniform(low=min_ok_float64/2, + high=max_ok_float64/2, size = size) + \ + uniform(low=min_ok_float64/2, + high=max_ok_float64/2, size = size) * 1j # the result of the last operation is a Python complex type which has 8 bytes in the alignment, # that's why we need to convert it to a numpy.complex64 the needed type. cmplx64 = np.complex64(cmplx128_from_float32) @@ -4161,6 +4568,17 @@ def get_prod(arr): assert np.isclose(epyccel_func(fl64), get_prod(fl64), rtol=RTOL, atol=ATOL) assert np.isclose(epyccel_func(cmplx64), get_prod(cmplx64), rtol=RTOL32, atol=ATOL32) assert np.isclose(epyccel_func(cmplx128), get_prod(cmplx128), rtol=RTOL, atol=ATOL) + assert matching_types(epyccel_func(bl), get_prod(bl)) + assert matching_types(epyccel_func(integer8), get_prod(integer8)) + assert matching_types(epyccel_func(integer16), get_prod(integer16)) + assert matching_types(epyccel_func(integer), get_prod(integer)) + assert matching_types(epyccel_func(integer32), get_prod(integer32)) + assert matching_types(epyccel_func(integer64), get_prod(integer64)) + assert matching_types(epyccel_func(fl), get_prod(fl)) + assert matching_types(epyccel_func(fl32), get_prod(fl32)) + assert matching_types(epyccel_func(fl64), get_prod(fl64)) + assert matching_types(epyccel_func(cmplx64), get_prod(cmplx64)) + assert matching_types(epyccel_func(cmplx128), get_prod(cmplx128)) @pytest.mark.parametrize( 'language', ( pytest.param("fortran", marks = [pytest.mark.fortran]), @@ -4198,25 +4616,33 @@ def get_prod(arr): bl = randint(0, 2, size = size, dtype= bool) - integer8 = randint(min_int8, max_int8, size = size, dtype=np.int8) - integer16 = randint(min_int16, max_int16, size = size, dtype=np.int16) - integer = randint(min_int, max_int, size = size, dtype=int) - integer32 = randint(min_int32, max_int32, size = size, dtype=np.int32) - integer64 = randint(min_int64, max_int64, size = size, dtype=np.int64) + max_ok_int = int(max_int64 ** (1/10)) - fl = uniform(min_float / 10, max_float / 10, size = size) - fl32 = uniform(min_float32 / 10, max_float32 / 10, size = size) + integer8 = randint(max(min_int8, -max_ok_int), min(max_ok_int, max_int8), size = size, dtype=np.int8) + integer16 = randint(max(min_int16, -max_ok_int), min(max_ok_int, max_int16), size = size, dtype=np.int16) + integer = randint(max(min_int, -max_ok_int), min(max_ok_int, max_int), size = size, dtype=int) + integer32 = randint(max(min_int32, -max_ok_int), min(max_ok_int, max_int32), size = size, dtype=np.int32) + integer64 = randint(-max_ok_int, max_ok_int, size = size, dtype=np.int64) + + fl = uniform(-((-min_float) ** (1/10)), max_float ** (1/10), size = size) + + min_ok_float32 = -((-min_float32) ** (1/10)) + min_ok_float64 = -((-min_float64) ** (1/10)) + max_ok_float32 = max_float32 ** (1/10) + max_ok_float64 = max_float64 ** (1/10) + + fl32 = uniform(min_ok_float32, max_ok_float32, size = size) fl32 = np.float32(fl32) - fl64 = uniform(min_float64 / 10, max_float64 / 10, size=size) - - cmplx128_from_float32 = uniform(low=-((-min_float32) ** (1/10)), - high=(max_float32 ** (1/10)), size = size) + \ - uniform(low=-((-min_float32) ** (1/10)), - high=(max_float32 ** (1/10)), size = size) * 1j - cmplx128_from_float64 = uniform(low=-((-min_float64) ** (1/10)), - high=(max_float64 ** (1/10)), size = size) + \ - uniform(low=-((-min_float64) ** (1/10)), - high=(max_float64 ** (1/10)), size = size) * 1j + fl64 = uniform(min_ok_float64, max_ok_float64, size=size) + + cmplx128_from_float32 = uniform(low=min_ok_float32/2, + high=max_ok_float32/2, size = size) + \ + uniform(low=min_ok_float32/2, + high=max_ok_float32/2, size = size) * 1j + cmplx128_from_float64 = uniform(low=min_ok_float64/2, + high=max_ok_float64/2, size = size) + \ + uniform(low=min_ok_float64/2, + high=max_ok_float64/2, size = size) * 1j # the result of the last operation is a Python complex type which has 8 bytes in the alignment, # that's why we need to convert it to a numpy.complex64 the needed type. cmplx64 = np.complex64(cmplx128_from_float32) @@ -4895,7 +5321,7 @@ def test_numpy_where_array_like_1d_with_condition(language): @types('float64[:]') def get_chosen_elements(arr): from numpy import where, shape - a = where(arr > 5, arr, arr*2) + a = where(arr > 5, arr, arr * 2) s = shape(a) return len(s), s[0], a[1], a[0] @@ -4988,7 +5414,7 @@ def test_numpy_where_array_like_2d_with_condition(language): @types('float64[:,:]') def get_chosen_elements(arr): from numpy import where, shape - a = where(arr < 0, arr, arr+1) + a = where(arr < 0, arr, arr + 1) s = shape(a) return len(s), s[0], a[0,0], a[0,1], a[1,0], a[1,1] @@ -5139,7 +5565,7 @@ def test_linspace_type2(start, end, result): for i in range(len(x)): result[i] = x[i] - integer8 = randint(min_int8, max_int8, dtype=np.int8) + integer8 = randint(min_int8, max_int8 // 2, dtype=np.int8) integer16 = randint(min_int16, max_int16, dtype=np.int16) integer = randint(min_int, max_int, dtype=int) integer32 = randint(min_int32, max_int32, dtype=np.int32) @@ -5163,8 +5589,8 @@ def test_linspace_type2(start, end, result): epyccel_func_type2(0, 10, out) assert (np.allclose(x, out)) arr = np.zeros - x = randint(100, 200) - assert np.isclose(epyccel_func(integer8, x, 100), get_linspace(integer8, x, 100), rtol=RTOL, atol=ATOL) + x = randint(1, 60) + assert np.isclose(epyccel_func(integer8, x, 30), get_linspace(integer8, x, 30), rtol=RTOL, atol=ATOL) assert matching_types(epyccel_func(integer8, x, 100), get_linspace(integer8, x, 100)) x = randint(100, 200) assert np.isclose(epyccel_func(integer, x, 30), get_linspace(integer, x, 30), rtol=RTOL, atol=ATOL) diff --git a/tests/epyccel/recognised_functions/test_numpy_types.py b/tests/epyccel/recognised_functions/test_numpy_types.py index dd22e7e678..24c7e2500c 100644 --- a/tests/epyccel/recognised_functions/test_numpy_types.py +++ b/tests/epyccel/recognised_functions/test_numpy_types.py @@ -9,9 +9,130 @@ from test_numpy_funcs import max_float, min_float, max_float32, min_float32,max_float64, min_float64 from test_numpy_funcs import matching_types -from pyccel.decorators import types +from pyccel.decorators import types, template from pyccel.epyccel import epyccel +numpy_basic_types_deprecated = tuple(int(v) for v in np.version.version.split('.'))>=(1,24,0) + +def test_mult_numpy_python_type(language): + + def mult_on_array_int8(): + from numpy import ones, int8 + a = ones(5, dtype=int8) + b = a * 2 + return b[0] + + def mult_on_array_int16(): + from numpy import ones, int16 + a = ones(5, dtype=int16) + b = a * 2 + return b[0] + + def mult_on_array_int32(): + from numpy import ones, int32 + a = ones(5, dtype=int32) + b = a * 2 + return b[0] + + def mult_on_array_int64(): + from numpy import ones, int64 + a = ones(5, dtype=int64) + b = a * 2 + return b[0] + + def mult_on_array_float32(): + from numpy import ones, float32 + a = ones(5, dtype=float32) + b = a * 2 + return b[0] + + def mult_on_array_float64(): + from numpy import ones, float64 + a = ones(5, dtype=float64) + b = a * 2 + return b[0] + + epyccel_func = epyccel(mult_on_array_int8, language=language) + python_result = mult_on_array_int8() + pyccel_result = epyccel_func() + assert python_result == pyccel_result + assert matching_types(pyccel_result, python_result) + + epyccel_func = epyccel(mult_on_array_int16, language=language) + python_result = mult_on_array_int16() + pyccel_result = epyccel_func() + assert python_result == pyccel_result + assert matching_types(pyccel_result, python_result) + + epyccel_func = epyccel(mult_on_array_int32, language=language) + python_result = mult_on_array_int32() + pyccel_result = epyccel_func() + assert python_result == pyccel_result + assert matching_types(pyccel_result, python_result) + + epyccel_func = epyccel(mult_on_array_int64, language=language) + python_result = mult_on_array_int64() + pyccel_result = epyccel_func() + assert python_result == pyccel_result + assert matching_types(pyccel_result, python_result) + + epyccel_func = epyccel(mult_on_array_float32, language=language) + python_result = mult_on_array_float32() + pyccel_result = epyccel_func() + assert python_result == pyccel_result + assert matching_types(pyccel_result, python_result) + + epyccel_func = epyccel(mult_on_array_float64, language=language) + python_result = mult_on_array_float64() + pyccel_result = epyccel_func() + assert python_result == pyccel_result + assert matching_types(pyccel_result, python_result) + +def test_numpy_scalar_promotion(language): + + @types('T', 'D') + @template(name='T', types=['int32', 'int64', 'float32', 'float64', 'complex64', 'complex128']) + @template(name='D', types=['int32', 'int64', 'float32', 'float64', 'complex64', 'complex128']) + def add_numpy_to_numpy_type(np_s_l, np_s_r): + rs = np_s_l + np_s_r + return rs + + integer32 = randint(min_int32 // 2, max_int32 // 2, dtype=np.int32) + integer64 = randint(min_int64 // 2, max_int64 // 2, dtype=np.int64) + fl32 = np.float32(uniform(min_float32 / 2, max_float32 / 2)) + fl64 = np.float64(uniform(min_float64 / 2, max_float64 / 2)) + complex64 = np.complex64(uniform(min_float32 / 2, max_float32 / 2)) + complex128 = np.complex64(uniform(min_float32 / 2, max_float32 / 2)) + + epyccel_func = epyccel(add_numpy_to_numpy_type, language=language) + + pyccel_result = epyccel_func(integer32, integer64) + python_result = add_numpy_to_numpy_type(integer32, integer64) + + assert pyccel_result == python_result + assert isinstance(pyccel_result, type(python_result)) + + pyccel_result = epyccel_func(integer64, fl32) + python_result = add_numpy_to_numpy_type(integer64, fl32) + assert pyccel_result == python_result + assert isinstance(pyccel_result, type(python_result)) + + pyccel_result = epyccel_func(integer64, fl64) + python_result = add_numpy_to_numpy_type(integer64, fl64) + assert pyccel_result == python_result + assert isinstance(pyccel_result, type(python_result)) + + pyccel_result = epyccel_func(fl64, complex64) + python_result = add_numpy_to_numpy_type(fl64, complex64) + assert pyccel_result == python_result + assert isinstance(pyccel_result, type(python_result)) + + pyccel_result = epyccel_func(complex128, fl64) + python_result = add_numpy_to_numpy_type(complex128, fl64) + assert pyccel_result == python_result + assert isinstance(pyccel_result, type(python_result)) + +@pytest.mark.skipif(numpy_basic_types_deprecated, reason="Can't import bool from numpy") def test_numpy_bool_scalar(language): @types('bool') @@ -174,15 +295,21 @@ def get_int8(a): b = int8(a) return b -@pytest.mark.parametrize( 'function_boundaries', [(get_int, min_int, max_int), (get_int64, min_int64, max_int64), (get_int32, min_int32, max_int32),\ - (get_int16, min_int16, max_int16), (get_int8, min_int8, max_int8)]) +if numpy_basic_types_deprecated: + int_functions_and_boundaries = [(get_int64, min_int64, max_int64), (get_int32, min_int32, max_int32),\ + (get_int16, min_int16, max_int16), (get_int8, min_int8, max_int8)] +else: + int_functions_and_boundaries = [(get_int, min_int, max_int), (get_int64, min_int64, max_int64), (get_int32, min_int32, max_int32),\ + (get_int16, min_int16, max_int16), (get_int8, min_int8, max_int8)] + +@pytest.mark.parametrize( 'function_boundaries', int_functions_and_boundaries) def test_numpy_int_scalar(language, function_boundaries): integer8 = randint(min_int8, max_int8, dtype=np.int8) - integer16 = randint(min_int16, max_int16, dtype=np.int16) - integer = randint(min_int, max_int, dtype=int) - integer32 = randint(min_int32, max_int32, dtype=np.int32) - integer64 = randint(min_int64, max_int64, dtype=np.int64) + integer16 = randint(min_int8, max_int8, dtype=np.int16) + integer = randint(min_int8, max_int8, dtype=int) + integer32 = randint(min_int8, max_int8, dtype=np.int32) + integer64 = randint(min_int8, max_int8, dtype=np.int64) get_int = function_boundaries[0] # Modifying a global variable in a scop will change it to a local variable, so it needs to be initialized. @@ -525,7 +652,12 @@ def get_float32(a): b = float32(a) return b -@pytest.mark.parametrize( 'get_float', [get_float64, get_float32, get_float]) +if numpy_basic_types_deprecated: + float_functions = [get_float64, get_float32] +else: + float_functions = [get_float64, get_float32, get_float] + +@pytest.mark.parametrize( 'get_float', float_functions) def test_numpy_float_scalar(language, get_float): integer8 = randint(min_int8, max_int8, dtype=np.int8) @@ -534,10 +666,10 @@ def test_numpy_float_scalar(language, get_float): integer32 = randint(min_int32, max_int32, dtype=np.int32) integer64 = randint(min_int64, max_int64, dtype=np.int64) - fl = uniform(min_float / 2, max_float / 2) + fl = uniform(min_float32 / 2, max_float32 / 2) fl32 = uniform(min_float32 / 2, max_float32 / 2) fl32 = np.float32(fl32) - fl64 = uniform(min_float64 / 2, max_float64 / 2) + fl64 = uniform(min_float32 / 2, max_float32 / 2) epyccel_func = epyccel(get_float, language=language) @@ -660,10 +792,10 @@ def test_numpy_float_array_like_1d(language, get_float): integer32 = randint(min_int32, max_int32, size=size, dtype=np.int32) integer64 = randint(min_int64, max_int64, size=size, dtype=np.int64) - fl = uniform(min_float / 2, max_float / 2, size = size) + fl = uniform(min_float32 / 2, max_float32 / 2, size = size) fl32 = uniform(min_float32 / 2, max_float32 / 2, size = size) fl32 = np.float32(fl32) - fl64 = uniform(min_float64 / 2, max_float64 / 2, size = size) + fl64 = uniform(min_float32 / 2, max_float32 / 2, size = size) epyccel_func = epyccel(get_float, language=language) @@ -734,10 +866,10 @@ def test_numpy_float_array_like_2d(language, get_float): integer32 = randint(min_int32, max_int32, size=size, dtype=np.int32) integer64 = randint(min_int64, max_int64, size=size, dtype=np.int64) - fl = uniform(min_float / 2, max_float / 2, size = size) + fl = uniform(min_float32 / 2, max_float32 / 2, size = size) fl32 = uniform(min_float32 / 2, max_float32 / 2, size = size) fl32 = np.float32(fl32) - fl64 = uniform(min_float64 / 2, max_float64 / 2, size = size) + fl64 = uniform(min_float32 / 2, max_float32 / 2, size = size) epyccel_func = epyccel(get_float, language=language) @@ -1232,3 +1364,31 @@ def test_numpy_complex_array_like_2d(language, get_complex): assert epyccel_func(fl) == get_complex(fl) assert epyccel_func(fl64) == get_complex(fl64) assert epyccel_func(fl32) == get_complex(fl32) + +def test_literal_complex64(language): + def get_complex64(): + from numpy import complex64 + compl = complex64(3+4j) + return compl, compl.real, compl.imag + + epyccel_func = epyccel(get_complex64, language=language) + + pyth_res = get_complex64() + pycc_res = epyccel_func() + for pyth, pycc in zip(pyth_res, pycc_res): + assert pyth == pycc + assert isinstance(pycc, type(pyth)) + +def test_literal_complex128(language): + def get_complex128(): + from numpy import complex128 + compl = complex128(3+4j) + return compl, compl.real, compl.imag + + epyccel_func = epyccel(get_complex128, language=language) + + pyth_res = get_complex128() + pycc_res = epyccel_func() + for pyth, pycc in zip(pyth_res, pycc_res): + assert pyth == pycc + assert isinstance(pycc, type(pyth)) diff --git a/tests/epyccel/test_arrays.py b/tests/epyccel/test_arrays.py index 3879892626..d9c80f7c33 100644 --- a/tests/epyccel/test_arrays.py +++ b/tests/epyccel/test_arrays.py @@ -1,11 +1,72 @@ # pylint: disable=missing-function-docstring, missing-module-docstring/ import pytest import numpy as np +from numpy import iinfo from numpy.random import randint from pyccel.epyccel import epyccel from modules import arrays +#============================================================================== +# TEST: VERIFY ARRAY'S DTYPE CORRESPONDENCE TO THE PASSED ELEMENTS +#============================================================================== + +def test_array_assigned_dtype(language): + integer = randint(low = iinfo('int').min, high = iinfo('int').max, dtype=int) + integer8 = randint(low = iinfo('int8').min, high = iinfo('int8').max, dtype=np.int8) + integer16 = randint(low = iinfo('int16').min, high = iinfo('int16').max, dtype=np.int16) + integer32 = randint(low = iinfo('int32').min, high = iinfo('int32').max, dtype=np.int32) + integer64 = randint(low = iinfo('int64').min, high = iinfo('int64').max, dtype=np.int64) + + fl = float(integer) + fl32 = np.float32(fl) + fl64 = np.float64(fl) + + cmplx64 = np.complex64(fl32) + cmplx128 = np.complex128(fl64) + + epyccel_func = epyccel(arrays.array_return_first_element, language=language) + + f_integer_output = epyccel_func(integer, integer) + test_int_output = arrays.array_return_first_element(integer, integer) + assert isinstance(f_integer_output, type(test_int_output)) + + f_integer8_output = epyccel_func(integer8, integer8) + test_int8_output = arrays.array_return_first_element(integer8, integer8) + assert isinstance(f_integer8_output, type(test_int8_output)) + + f_integer16_output = epyccel_func(integer16, integer16) + test_int16_output = arrays.array_return_first_element(integer16, integer16) + assert isinstance(f_integer16_output, type(test_int16_output)) + + f_integer32_output = epyccel_func(integer32, integer32) + test_int32_output = arrays.array_return_first_element(integer32, integer32) + assert isinstance(f_integer32_output, type(test_int32_output)) + + f_integer64_output = epyccel_func(integer64, integer64) + test_int64_output = arrays.array_return_first_element(integer64, integer64) + assert isinstance(f_integer64_output, type(test_int64_output)) + + f_fl_output = epyccel_func(fl, fl) + test_float_output = arrays.array_return_first_element(fl, fl) + assert isinstance(f_fl_output, type(test_float_output)) + + f_fl32_output = epyccel_func(fl32, fl32) + test_float32_output = arrays.array_return_first_element(fl32, fl32) + assert isinstance(f_fl32_output, type(test_float32_output)) + + f_fl64_output = epyccel_func(fl64, fl64) + test_float64_output = arrays.array_return_first_element(fl64, fl64) + assert isinstance(f_fl64_output, type(test_float64_output)) + + f_cmplx64_output = epyccel_func(cmplx64, cmplx64) + test_cmplx64_output = arrays.array_return_first_element(cmplx64, cmplx64) + assert isinstance(f_cmplx64_output, type(test_cmplx64_output)) + + f_cmplx128_output = epyccel_func(cmplx128, cmplx128) + test_cmplx128_output = arrays.array_return_first_element(cmplx128, cmplx128) + assert isinstance(f_cmplx128_output, type(test_cmplx128_output)) + #============================================================================== # TEST: 1D ARRAYS OF INT-32 #============================================================================== @@ -873,6 +934,19 @@ def test_array_real_1d_scalar_div(language): assert np.array_equal( x1, x2 ) +def test_array_real_1d_scalar_mod(language): + f1 = arrays.array_real_1d_scalar_mod + f2 = epyccel( f1 , language = language) + + x1 = np.array( [1.,2.,3.] ) + x2 = np.copy(x1) + a = 5. + + f1(x1, a) + f2(x2, a) + + assert np.array_equal( x1, x2 ) + def test_array_real_1d_scalar_idiv(language): f1 = arrays.array_real_1d_scalar_idiv @@ -943,6 +1017,20 @@ def test_array_real_1d_div(language): assert np.array_equal( x1, x2 ) +def test_array_real_1d_mod(language): + + f1 = arrays.array_real_1d_mod + f2 = epyccel( f1 , language = language) + + x1 = np.array( [1.,2.,3.] ) + x2 = np.copy(x1) + a = np.array( [1.,2.,3.] ) + + f1(x1, a) + f2(x2, a) + + assert np.array_equal( x1, x2) + def test_array_real_1d_idiv(language): f1 = arrays.array_real_1d_idiv @@ -1017,6 +1105,20 @@ def test_array_real_2d_C_scalar_div(language): assert np.array_equal( x1, x2 ) +def test_array_real_2d_C_scalar_mod(language): + + f1 = arrays.array_real_2d_C_scalar_mod + f2 = epyccel( f1 , language = language) + + x1 = np.array( [[1.,2.,3.], [4.,5.,6.]] ) + x2 = np.copy(x1) + a = 5. + + f1(x1, a) + f2(x2, a) + + assert np.array_equal( x1, x2 ) + def test_array_real_2d_C_add(language): f1 = arrays.array_real_2d_C_add @@ -1073,6 +1175,20 @@ def test_array_real_2d_C_div(language): assert np.array_equal( x1, x2 ) +def test_array_real_2d_C_mod(language): + + f1 = arrays.array_real_2d_C_mod + f2 = epyccel( f1 , language = language) + + x1 = np.array( [[1.,2.,3.], [4.,5.,6.]] ) + x2 = np.copy(x1) + a = np.array( [[-1.,-2.,-3.], [-4.,-5.,-6.]] ) + + f1(x1, a) + f2(x2, a) + + assert np.array_equal( x1, x2 ) + def test_array_real_2d_C_array_initialization(language): f1 = arrays.array_real_2d_C_array_initialization @@ -1206,6 +1322,20 @@ def test_array_real_2d_F_scalar_div(language): assert np.array_equal( x1, x2 ) +def test_array_real_2d_F_scalar_mod(language): + + f1 = arrays.array_real_2d_F_scalar_mod + f2 = epyccel( f1 , language = language) + + x1 = np.array( [[1.,2.,3.], [4.,5.,6.]], order='F' ) + x2 = np.copy(x1) + a = 5. + + f1(x1, a) + f2(x2, a) + + assert np.array_equal( x1, x2 ) + def test_array_real_2d_F_add(language): f1 = arrays.array_real_2d_F_add @@ -1262,6 +1392,20 @@ def test_array_real_2d_F_div(language): assert np.array_equal( x1, x2 ) +def test_array_real_2d_F_mod(language): + + f1 = arrays.array_real_2d_F_mod + f2 = epyccel( f1 , language = language) + + x1 = np.array( [[1.,2.,3.], [4.,5.,6.]], order='F' ) + x2 = np.copy(x1) + a = np.array( [[-1.,-2.,-3.], [-4.,-5.,-6.]], order='F' ) + + f1(x1, a) + f2(x2, a) + + assert np.array_equal( x1, x2 ) + def test_array_real_2d_F_array_initialization(language): f1 = arrays.array_real_2d_F_array_initialization @@ -3823,6 +3967,8 @@ def test_arrs_2d_negative_index(language): #============================================================================== # TEST : NUMPY ARANGE #============================================================================== +RTOL = 1e-12 +ATOL = 1e-16 def test_numpy_arange_one_arg(language): f1 = arrays.arr_arange_1 @@ -3837,7 +3983,12 @@ def test_numpy_arange_two_arg(language): def test_numpy_arange_full_arg(language): f1 = arrays.arr_arange_3 f2 = epyccel(f1, language = language) - np.testing.assert_array_almost_equal(f1(), f2(), decimal=9) + + r_f1 = f1() + r_f2 = f2() + + assert (type(r_f1[1]) is type(r_f2[1])) + np.testing.assert_allclose(f1(), f2(), rtol=RTOL, atol=ATOL) def test_numpy_arange_with_dtype(language): f1 = arrays.arr_arange_4 @@ -3847,17 +3998,38 @@ def test_numpy_arange_with_dtype(language): def test_numpy_arange_negative_step(language): f1 = arrays.arr_arange_5 f2 = epyccel(f1, language = language) - np.testing.assert_array_almost_equal(f1(), f2(), decimal = 9) + + r_f1 = f1() + r_f2 = f2() + + assert (type(r_f1[1]) is type(r_f2[1])) + np.testing.assert_allclose(f1(), f2(), rtol=RTOL, atol=ATOL) def test_numpy_arange_negative_step_2(language): f1 = arrays.arr_arange_6 f2 = epyccel(f1, language = language) - np.testing.assert_array_almost_equal(f1(), f2(), decimal = 9) + + r_f1 = f1() + r_f2 = f2() + + assert (type(r_f1[1]) is type(r_f2[1])) + np.testing.assert_allclose(f1(), f2(), rtol=RTOL, atol=ATOL) + +def test_numpy_arange_into_slice(language): + f1 = arrays.arr_arange_7 + f2 = epyccel(f1, language = language) + n = randint(2, 10) + m = randint(2, 10) + x = np.array(100 * np.random.random((n, m)), dtype=int) + x_expected = x.copy() + f1(x_expected) + f2(x) + np.testing.assert_allclose(x, x_expected, rtol=RTOL, atol=ATOL) def test_iterate_slice(language): f1 = arrays.iterate_slice f2 = epyccel(f1, language = language) - i = randint(2,10) + i = randint(2, 10) assert f1(i) == f2(i) ##============================================================================== diff --git a/tests/epyccel/test_builtins.py b/tests/epyccel/test_builtins.py index cdc4419e2c..0b5790f1ab 100644 --- a/tests/epyccel/test_builtins.py +++ b/tests/epyccel/test_builtins.py @@ -7,6 +7,10 @@ from pyccel.epyccel import epyccel from pyccel.decorators import types, template +ATOL = 1e-15 +RTOL = 2e-14 + + min_int = iinfo('int').min max_int = iinfo('int').max @@ -23,9 +27,9 @@ def f1(x): negative_test = randint(min_int, 0) positive_test = randint(0, max_int) - assert f1(0) == f2(0) - assert f1(negative_test) == f2(negative_test) - assert f1(positive_test) == f2(positive_test) + assert np.isclose(f1(0), f2(0), rtol=RTOL, atol=ATOL) + assert np.isclose(f1(negative_test), f2(negative_test), rtol=RTOL, atol=ATOL) + assert np.isclose(f1(positive_test), f2(positive_test), rtol=RTOL, atol=ATOL) def test_abs_r(language): @types('real') @@ -37,9 +41,9 @@ def f1(x): negative_test = uniform(min_float, 0.0) positive_test = uniform(0.0, max_float) - assert f1(0.00000) == f2(0.00000) - assert f1(negative_test) == f2(negative_test) - assert f1(positive_test) == f2(positive_test) + assert np.isclose(f1(0.00000), f2(0.00000), rtol=RTOL, atol=ATOL) + assert np.isclose(f1(negative_test), f2(negative_test), rtol=RTOL, atol=ATOL) + assert np.isclose(f1(positive_test), f2(positive_test), rtol=RTOL, atol=ATOL) @@ -60,23 +64,14 @@ def f1(x): zero_rand = 1j*uniform(min_compl_abs, max_compl_abs) rand_zero = uniform(min_compl_abs, max_compl_abs) + 0j - assert f1(pos_pos) == f2(pos_pos) - assert f1(pos_neg) == f2(pos_neg) - assert f1(neg_pos) == f2(neg_pos) - assert f1(neg_neg) == f2(neg_neg) - assert f1(zero_rand) == f2(zero_rand) - assert f1(rand_zero) == f2(rand_zero) - assert f1(0j + 0) == f2(0j + 0) + assert np.isclose(f1(pos_pos), f2(pos_pos), rtol=RTOL, atol=ATOL) + assert np.isclose(f1(pos_neg), f2(pos_neg), rtol=RTOL, atol=ATOL) + assert np.isclose(f1(neg_pos), f2(neg_pos), rtol=RTOL, atol=ATOL) + assert np.isclose(f1(neg_neg), f2(neg_neg), rtol=RTOL, atol=ATOL) + assert np.isclose(f1(zero_rand), f2(zero_rand), rtol=RTOL, atol=ATOL) + assert np.isclose(f1(rand_zero), f2(rand_zero), rtol=RTOL, atol=ATOL) + assert np.isclose(f1(0j + 0), f2(0j + 0), rtol=RTOL, atol=ATOL) -@pytest.mark.parametrize( 'language', ( - pytest.param("fortran", marks = pytest.mark.fortran), - pytest.param("c", marks = [ - pytest.mark.skip(reason="min not implemented in C for integers"), - pytest.mark.c] - ), - pytest.param("python", marks = pytest.mark.python) - ) -) def test_min_2_args_i(language): @types('int','int') def f(x, y): @@ -88,15 +83,6 @@ def f(x, y): assert epyc_f(*int_args) == f(*int_args) -@pytest.mark.parametrize( 'language', ( - pytest.param("fortran", marks = pytest.mark.fortran), - pytest.param("c", marks = [ - pytest.mark.skip(reason="min not implemented in C for integers"), - pytest.mark.c] - ), - pytest.param("python", marks = pytest.mark.python) - ) -) def test_min_2_args_i_adhoc(language): def f(x:int): return min(x, 0) @@ -115,7 +101,7 @@ def f(x:float): float_arg = uniform(min_float /2, max_float/2) - assert epyc_f(float_arg) == f(float_arg) + assert np.isclose(epyc_f(float_arg), f(float_arg), rtol=RTOL, atol=ATOL) def test_min_2_args_f(language): @types('float','float') @@ -126,7 +112,7 @@ def f(x, y): float_args = [uniform(min_float/2, max_float/2) for _ in range(2)] - assert epyc_f(*float_args) == f(*float_args) + assert np.isclose(epyc_f(*float_args), f(*float_args), rtol=RTOL, atol=ATOL) @pytest.mark.parametrize( 'language', ( pytest.param("fortran", marks = pytest.mark.fortran), @@ -149,7 +135,7 @@ def f(x, y, z): float_args = [uniform(min_float/2, max_float/2) for _ in range(3)] assert epyc_f(*int_args) == f(*int_args) - assert epyc_f(*float_args) == f(*float_args) + assert np.isclose(epyc_f(*float_args), f(*float_args), rtol=RTOL, atol=ATOL) @pytest.mark.parametrize( 'language', ( pytest.param("fortran", marks = pytest.mark.fortran), @@ -172,7 +158,7 @@ def f(x, y, z): float_args = [uniform(min_float/2, max_float/2) for _ in range(3)] assert epyc_f(*int_args) == f(*int_args) - assert epyc_f(*float_args) == f(*float_args) + assert np.isclose(epyc_f(*float_args), f(*float_args), rtol=RTOL, atol=ATOL) @pytest.mark.parametrize( 'language', ( pytest.param("fortran", marks = pytest.mark.fortran), @@ -195,17 +181,8 @@ def f(x, y, z): float_args = [uniform(min_float/2, max_float/2) for _ in range(3)] assert epyc_f(*int_args) == f(*int_args) - assert epyc_f(*float_args) == f(*float_args) + assert np.isclose(epyc_f(*float_args), f(*float_args), rtol=RTOL, atol=ATOL) -@pytest.mark.parametrize( 'language', ( - pytest.param("fortran", marks = pytest.mark.fortran), - pytest.param("c", marks = [ - pytest.mark.skip(reason="max not implemented in C for integers"), - pytest.mark.c] - ), - pytest.param("python", marks = pytest.mark.python) - ) -) def test_max_2_args_i(language): @types('int','int') def f(x, y): @@ -226,7 +203,7 @@ def f(x, y): float_args = [uniform(min_float/2, max_float/2) for _ in range(2)] - assert epyc_f(*float_args) == f(*float_args) + assert np.isclose(epyc_f(*float_args), f(*float_args), rtol=RTOL, atol=ATOL) @pytest.mark.parametrize( 'language', ( pytest.param("fortran", marks = pytest.mark.fortran), @@ -249,7 +226,7 @@ def f(x, y, z): float_args = [uniform(min_float/2, max_float/2) for _ in range(3)] assert epyc_f(*int_args) == f(*int_args) - assert epyc_f(*float_args) == f(*float_args) + assert np.isclose(epyc_f(*float_args), f(*float_args), rtol=RTOL, atol=ATOL) @pytest.mark.parametrize( 'language', ( pytest.param("fortran", marks = pytest.mark.fortran), @@ -272,7 +249,7 @@ def f(x, y, z): float_args = [uniform(min_float/2, max_float/2) for _ in range(3)] assert epyc_f(*int_args) == f(*int_args) - assert epyc_f(*float_args) == f(*float_args) + assert np.isclose(epyc_f(*float_args), f(*float_args), rtol=RTOL, atol=ATOL) @pytest.mark.parametrize( 'language', ( pytest.param("fortran", marks = pytest.mark.fortran), @@ -295,7 +272,7 @@ def f(x, y, z): float_args = [uniform(min_float/2, max_float/2) for _ in range(3)] assert epyc_f(*int_args) == f(*int_args) - assert epyc_f(*float_args) == f(*float_args) + assert np.isclose(epyc_f(*float_args), f(*float_args), rtol=RTOL, atol=ATOL) @pytest.mark.parametrize( 'language', ( pytest.param("fortran", marks = pytest.mark.fortran), @@ -320,5 +297,5 @@ def f(x, y): for _ in range(2)] assert epyc_f(*int_args) == f(*int_args) - assert epyc_f(*float_args) == f(*float_args) - assert epyc_f(*complex_args) == f(*complex_args) + assert np.isclose(epyc_f(*float_args), f(*float_args), rtol=RTOL, atol=ATOL) + assert np.isclose(epyc_f(*complex_args), f(*complex_args), rtol=RTOL, atol=ATOL) diff --git a/tests/epyccel/test_default_precision_template.py b/tests/epyccel/test_default_precision_template.py new file mode 100644 index 0000000000..8e8139f6ef --- /dev/null +++ b/tests/epyccel/test_default_precision_template.py @@ -0,0 +1,31 @@ +# pylint: disable=missing-function-docstring, missing-module-docstring + +from numpy.random import randint +from numpy import isclose +import numpy as np + +from pyccel.decorators import types +from pyccel.epyccel import epyccel + +RTOL = 1e-12 +ATOL = 1e-16 + +def test_default_precision_template(language): + + @types('int[:]') + @types('float[:]') + @types('complex[:]') + def return_array_element(array): + return array[0] + + test_types = ['int', 'float', 'complex'] + f1 = return_array_element + f2 = epyccel(f1, language=language) + for t in test_types: + d1 = randint(1, 15) + arr = np.ones(d1, dtype=t) + python_result = f1(arr) + pyccel_result = f2(arr) + + assert isinstance(pyccel_result, type(python_result)) + assert isclose(pyccel_result, python_result, rtol=RTOL, atol=ATOL) diff --git a/tests/epyccel/test_epyccel_augassign.py b/tests/epyccel/test_epyccel_augassign.py new file mode 100644 index 0000000000..c654e5971c --- /dev/null +++ b/tests/epyccel/test_epyccel_augassign.py @@ -0,0 +1,212 @@ +# pylint: disable=missing-function-docstring, missing-module-docstring/ + +import numpy as np +import modules.augassign as mod + +from pyccel.epyccel import epyccel + + +# += tests + +def test_augassign_add_1d(language): + f_int = mod.augassign_add_1d_int + f_float = mod.augassign_add_1d_float + f_complex = mod.augassign_add_1d_complex + f_int_epyc = epyccel(f_int, language = language) + f_float_epyc = epyccel(f_float, language = language) + f_complex_epyc = epyccel(f_complex, language = language) + + x1_int = np.zeros(5, dtype=int) + x1_float = np.zeros(5, dtype=float) + x1_complex = np.zeros(5, dtype=complex) + x2_int = np.zeros(5, dtype=int) + x2_float = np.zeros(5, dtype=float) + x2_complex = np.zeros(5, dtype=complex) + + y1_int = f_int(x1_int) + y1_float = f_float(x1_float) + y1_complex = f_complex(x1_complex) + y2_int = f_int_epyc(x2_int) + y2_float = f_float_epyc(x2_float) + y2_complex = f_complex_epyc(x2_complex) + + assert y1_int == y2_int and np.array_equal(x1_int, x2_int) + assert y1_float == y2_float and np.array_equal(x1_float, x2_float) + assert y1_complex == y2_complex and np.array_equal(x1_complex, x2_complex) + +def test_augassign_add_2d(language): + f_int = mod.augassign_add_2d_int + f_float = mod.augassign_add_2d_float + f_complex = mod.augassign_add_2d_complex + f_int_epyc = epyccel(f_int, language = language) + f_float_epyc = epyccel(f_float, language = language) + f_complex_epyc = epyccel(f_complex, language = language) + + x1_int = np.zeros((5, 5), dtype=int) + x1_float = np.zeros((5, 5), dtype=float) + x1_complex = np.zeros((5, 5), dtype=complex) + x2_int = np.zeros((5, 5), dtype=int) + x2_float = np.zeros((5, 5), dtype=float) + x2_complex = np.zeros((5, 5), dtype=complex) + + y1_int = f_int(x1_int) + y1_float = f_float(x1_float) + y1_complex = f_complex(x1_complex) + y2_int = f_int_epyc(x2_int) + y2_float = f_float_epyc(x2_float) + y2_complex = f_complex_epyc(x2_complex) + + assert y1_int == y2_int and np.array_equal(x1_int, x2_int) + assert y1_float == y2_float and np.array_equal(x1_float, x2_float) + assert y1_complex == y2_complex and np.array_equal(x1_complex, x2_complex) + + +# -= tests + +def test_augassign_sub_1d(language): + f_int = mod.augassign_sub_1d_int + f_float = mod.augassign_sub_1d_float + f_complex = mod.augassign_sub_1d_complex + f_int_epyc = epyccel(f_int, language = language) + f_float_epyc = epyccel(f_float, language = language) + f_complex_epyc = epyccel(f_complex, language = language) + + x1_int = np.zeros(5, dtype=int) + x1_float = np.zeros(5, dtype=float) + x1_complex = np.zeros(5, dtype=complex) + x2_int = np.zeros(5, dtype=int) + x2_float = np.zeros(5, dtype=float) + x2_complex = np.zeros(5, dtype=complex) + + y1_int = f_int(x1_int) + y1_float = f_float(x1_float) + y1_complex = f_complex(x1_complex) + y2_int = f_int_epyc(x2_int) + y2_float = f_float_epyc(x2_float) + y2_complex = f_complex_epyc(x2_complex) + + assert y1_int == y2_int and np.array_equal(x1_int, x2_int) + assert y1_float == y2_float and np.array_equal(x1_float, x2_float) + assert y1_complex == y2_complex and np.array_equal(x1_complex, x2_complex) + +def test_augassign_sub_2d(language): + f_int = mod.augassign_sub_2d_int + f_float = mod.augassign_sub_2d_float + f_complex = mod.augassign_sub_2d_complex + f_int_epyc = epyccel(f_int, language = language) + f_float_epyc = epyccel(f_float, language = language) + f_complex_epyc = epyccel(f_complex, language = language) + + x1_int = np.zeros((5, 5), dtype=int) + x1_float = np.zeros((5, 5), dtype=float) + x1_complex = np.zeros((5, 5), dtype=complex) + x2_int = np.zeros((5, 5), dtype=int) + x2_float = np.zeros((5, 5), dtype=float) + x2_complex = np.zeros((5, 5), dtype=complex) + + y1_int = f_int(x1_int) + y1_float = f_float(x1_float) + y1_complex = f_complex(x1_complex) + y2_int = f_int_epyc(x2_int) + y2_float = f_float_epyc(x2_float) + y2_complex = f_complex_epyc(x2_complex) + + assert y1_int == y2_int and np.array_equal(x1_int, x2_int) + assert y1_float == y2_float and np.array_equal(x1_float, x2_float) + assert y1_complex == y2_complex and np.array_equal(x1_complex, x2_complex) + + +# *= tests + +def test_augassign_mul_1d(language): + f_int = mod.augassign_mul_1d_int + f_float = mod.augassign_mul_1d_float + f_complex = mod.augassign_mul_1d_complex + f_int_epyc = epyccel(f_int, language = language) + f_float_epyc = epyccel(f_float, language = language) + f_complex_epyc = epyccel(f_complex, language = language) + + x1_int = np.zeros(5, dtype=int) + x1_float = np.zeros(5, dtype=float) + x1_complex = np.zeros(5, dtype=complex) + x2_int = np.zeros(5, dtype=int) + x2_float = np.zeros(5, dtype=float) + x2_complex = np.zeros(5, dtype=complex) + + y1_int = f_int(x1_int) + y1_float = f_float(x1_float) + y1_complex = f_complex(x1_complex) + y2_int = f_int_epyc(x2_int) + y2_float = f_float_epyc(x2_float) + y2_complex = f_complex_epyc(x2_complex) + + assert y1_int == y2_int and np.array_equal(x1_int, x2_int) + assert y1_float == y2_float and np.array_equal(x1_float, x2_float) + assert y1_complex == y2_complex and np.array_equal(x1_complex, x2_complex) + +def test_augassign_mul_2d(language): + f_int = mod.augassign_mul_2d_int + f_float = mod.augassign_mul_2d_float + f_complex = mod.augassign_mul_2d_complex + f_int_epyc = epyccel(f_int, language = language) + f_float_epyc = epyccel(f_float, language = language) + f_complex_epyc = epyccel(f_complex, language = language) + + x1_int = np.zeros((5, 5), dtype=int) + x1_float = np.zeros((5, 5), dtype=float) + x1_complex = np.zeros((5, 5), dtype=complex) + x2_int = np.zeros((5, 5), dtype=int) + x2_float = np.zeros((5, 5), dtype=float) + x2_complex = np.zeros((5, 5), dtype=complex) + + y1_int = f_int(x1_int) + y1_float = f_float(x1_float) + y1_complex = f_complex(x1_complex) + y2_int = f_int_epyc(x2_int) + y2_float = f_float_epyc(x2_float) + y2_complex = f_complex_epyc(x2_complex) + + assert y1_int == y2_int and np.array_equal(x1_int, x2_int) + assert y1_float == y2_float and np.array_equal(x1_float, x2_float) + assert y1_complex == y2_complex and np.array_equal(x1_complex, x2_complex) + + +# /= tests + +def test_augassign_div_1d(language): + f_float = mod.augassign_div_1d_float + f_complex = mod.augassign_div_1d_complex + f_float_epyc = epyccel(f_float, language = language) + f_complex_epyc = epyccel(f_complex, language = language) + + x1_float = np.zeros(5, dtype=float) + x1_complex = np.zeros(5, dtype=complex) + x2_float = np.zeros(5, dtype=float) + x2_complex = np.zeros(5, dtype=complex) + + y1_float = f_float(x1_float) + y1_complex = f_complex(x1_complex) + y2_float = f_float_epyc(x2_float) + y2_complex = f_complex_epyc(x2_complex) + + assert y1_float == y2_float and np.array_equal(x1_float, x2_float) + assert y1_complex == y2_complex and np.array_equal(x1_complex, x2_complex) + +def test_augassign_div_2d(language): + f_float = mod.augassign_div_2d_float + f_complex = mod.augassign_div_2d_complex + f_float_epyc = epyccel(f_float, language = language) + f_complex_epyc = epyccel(f_complex, language = language) + + x1_float = np.zeros((5, 5), dtype=float) + x1_complex = np.zeros((5, 5), dtype=complex) + x2_float = np.zeros((5, 5), dtype=float) + x2_complex = np.zeros((5, 5), dtype=complex) + + y1_float = f_float(x1_float) + y1_complex = f_complex(x1_complex) + y2_float = f_float_epyc(x2_float) + y2_complex = f_complex_epyc(x2_complex) + + assert y1_float == y2_float and np.array_equal(x1_float, x2_float) + assert y1_complex == y2_complex and np.array_equal(x1_complex, x2_complex) diff --git a/tests/epyccel/test_epyccel_complex_func.py b/tests/epyccel/test_epyccel_complex_func.py index 8ad32c038b..67ea1df4c5 100644 --- a/tests/epyccel/test_epyccel_complex_func.py +++ b/tests/epyccel/test_epyccel_complex_func.py @@ -7,6 +7,9 @@ import modules.complex_func as mod from pyccel.epyccel import epyccel +ATOL = 1e-15 +RTOL = 2e-14 + @pytest.mark.parametrize("f", [ mod.create_complex_literal__int_int, mod.create_complex_literal__int_float, mod.create_complex_literal__int_complex, @@ -43,7 +46,7 @@ def test_create_complex_var__complex_float(language): a = complex(randint(100), randint(100)) b = rand()*100 - assert f_epyc(a,b) == f(a,b) + assert np.allclose(f_epyc(a,b), f(a,b), rtol=RTOL, atol=ATOL) def test_create_complex_var__complex_complex(language): f = mod.create_complex_var__complex_complex @@ -51,7 +54,7 @@ def test_create_complex_var__complex_complex(language): a = complex(randint(100), randint(100)) b = complex(randint(100), randint(100)) - assert f_epyc(a,b) == f(a,b) + assert np.allclose(f_epyc(a,b), f(a,b), rtol=RTOL, atol=ATOL) def test_create_complex__int_int(language): f = mod.create_complex__int_int @@ -72,35 +75,35 @@ def test_create_complex__float_float(language): f_epyc = epyccel(f, language = language) a = rand()*100 - assert f_epyc(a) == f(a) + assert np.allclose(f_epyc(a), f(a), rtol=RTOL, atol=ATOL) def test_create_complex_0__float_float(language): f = mod.create_complex_0__float_float f_epyc = epyccel(f, language = language) a = rand()*100 - assert f_epyc(a) == f(a) + assert np.allclose(f_epyc(a), f(a), rtol=RTOL, atol=ATOL) def test_create_complex__complex_complex(language): f = mod.create_complex__complex_complex f_epyc = epyccel(f, language = language) a = complex(randint(100), randint(100)) - assert f_epyc(a) == f(a) + assert np.allclose(f_epyc(a), f(a), rtol=RTOL, atol=ATOL) def test_cast_complex_1(language): f = mod.cast_complex_1 f_epyc = epyccel(f, language = language) a = np.complex64(complex(randint(100), randint(100))) - assert np.isclose(f_epyc(a), f(a), rtol = 1e-7, atol = 1e-8) + assert np.allclose(f_epyc(a), f(a), rtol = 1e-7, atol = 1e-8) def test_cast_complex_2(language): f = mod.cast_complex_2 f_epyc = epyccel(f, language = language) a = np.complex128(complex(randint(100), randint(100))) - assert f_epyc(a) == f(a) + assert np.allclose(f_epyc(a), f(a), rtol=RTOL, atol=ATOL) def test_cast_float_complex(language): f = mod.cast_float_complex @@ -108,4 +111,4 @@ def test_cast_float_complex(language): a = rand()*100 b = complex(randint(100), randint(100)) - assert f_epyc(a,b) == f(a,b) + assert np.allclose(f_epyc(a,b), f(a,b), rtol=RTOL, atol=ATOL) diff --git a/tests/epyccel/test_epyccel_functions.py b/tests/epyccel/test_epyccel_functions.py index 122ebddd5f..52758cd814 100644 --- a/tests/epyccel/test_epyccel_functions.py +++ b/tests/epyccel/test_epyccel_functions.py @@ -68,14 +68,23 @@ def p_func(): with pytest.raises(TypeError): c_func(unexpected_arg) -def test_func_no_args_f1(): +def test_func_no_args_f1(language): def f1(): from numpy import pi value = (2*pi)**(3/2) return value f = epyccel(f1) - assert abs(f()-f1()) < 1e-13 + assert np.isclose(f(), f1(), rtol=RTOL, atol=ATOL) + +def test_func_return_constant(language): + def f1(): + from numpy import pi + return pi + + f = epyccel(f1) + assert np.isclose(f(), f1(), rtol=RTOL, atol=ATOL) + #------------------------------------------------------------------------------ def test_decorator_f1(language): @types('int') diff --git a/tests/epyccel/test_epyccel_generators.py b/tests/epyccel/test_epyccel_generators.py index caf1bd4d7b..c81bee0a24 100644 --- a/tests/epyccel/test_epyccel_generators.py +++ b/tests/epyccel/test_epyccel_generators.py @@ -173,4 +173,4 @@ def f(a : 'float[:,:,:,:]'): f_epyc = epyccel(f, language = language) - assert f(x) == f_epyc(x) + assert f(x) == f_epyc(x) \ No newline at end of file diff --git a/tests/epyccel/test_epyccel_modules.py b/tests/epyccel/test_epyccel_modules.py index 061e6c7576..6981333e9a 100644 --- a/tests/epyccel/test_epyccel_modules.py +++ b/tests/epyccel/test_epyccel_modules.py @@ -70,7 +70,18 @@ def test_module_3(language): r = 4.5 x_expected = mod.circle_volume(r) x = modnew.circle_volume(r) - assert np.isclose( x, x_expected, rtol=1e-14, atol=1e-14 ) + assert np.isclose( x, x_expected, rtol=RTOL, atol=ATOL ) + + i = np.random.randint(4,20) + n = np.random.randint(2,8) + arr = np.array(100*np.random.random_sample(n), dtype=int) + x_expected, y_expected = mod.alias(arr, i) + x, y = modnew.alias(arr, i) + + assert np.allclose( x, x_expected, rtol=RTOL, atol=ATOL ) + assert np.allclose( y, y_expected, rtol=RTOL, atol=ATOL ) + assert x.dtype is x_expected.dtype + assert y.dtype is y_expected.dtype def test_module_4(language): import modules.Module_6 as mod diff --git a/tests/epyccel/test_epyccel_optional_args.py b/tests/epyccel/test_epyccel_optional_args.py index ab355cf3de..0b49a70d0f 100644 --- a/tests/epyccel/test_epyccel_optional_args.py +++ b/tests/epyccel/test_epyccel_optional_args.py @@ -6,6 +6,9 @@ from pyccel.epyccel import epyccel from pyccel.decorators import types +RTOL = 2e-14 +ATOL = 1e-15 + @pytest.fixture(scope="module") def Module_5(language): import modules.Module_5 as mod @@ -40,10 +43,10 @@ def f2(x = None): f = epyccel(f2, language = language) # ... - assert f(2.0) == f2(2.0) - assert f() == f2() - assert f(None) == f2(None) - assert f(0.0) == f2(0.0) + assert np.isclose(f(2.0), f2(2.0), rtol=RTOL, atol=ATOL) + assert np.isclose(f(), f2(), rtol=RTOL, atol=ATOL) + assert np.isclose(f(None), f2(None), rtol=RTOL, atol=ATOL) + assert np.isclose(f(0.0), f2(0.0), rtol=RTOL, atol=ATOL) # ... #------------------------------------------------------------------------------ def test_f3(language): @@ -56,9 +59,9 @@ def f3(x = None): f = epyccel(f3, language = language) # ... - assert f(complex(1, 2.2)) == f3(complex(1, 2.2)) - assert f() == f3() - assert f(None) == f3(None) + assert np.isclose(f(complex(1, 2.2)), f3(complex(1, 2.2)), rtol=RTOL, atol=ATOL) + assert np.isclose(f(), f3(), rtol=RTOL, atol=ATOL) + assert np.isclose(f(None), f3(None), rtol=RTOL, atol=ATOL) # ... #------------------------------------------------------------------------------ def test_f4(language): diff --git a/tests/epyccel/test_epyccel_return_arrays.py b/tests/epyccel/test_epyccel_return_arrays.py index 3b108b936d..ffeed20fa9 100644 --- a/tests/epyccel/test_epyccel_return_arrays.py +++ b/tests/epyccel/test_epyccel_return_arrays.py @@ -228,6 +228,487 @@ def return_array(a, b): assert np.array_equal(f_cmplx128_output, test_cmplx128_output) assert f_cmplx128_output[0].dtype == test_cmplx128_output[0].dtype +def test_return_array_array_op(language): + + @types('int', 'int') + @types('int8', 'int8') + @types('int16', 'int16') + @types('int32', 'int32') + @types('int64', 'int64') + @types('float', 'float') + @types('float32', 'float32') + @types('float64', 'float64') + @types('complex64', 'complex64') + @types('complex128', 'complex128') + def return_array(a, b): + from numpy import array + x = array([a,b], dtype=type(a)) + y = array([a,b], dtype=type(a)) + return x + y + + integer8 = randint(min_int8, max_int8, dtype=np.int8) + integer16 = randint(min_int16, max_int16, dtype=np.int16) + integer = randint(min_int, max_int, dtype=int) + integer32 = randint(min_int32, max_int32, dtype=np.int32) + integer64 = randint(min_int64, max_int64, dtype=np.int64) + + fl = uniform(min_float / 2, max_float / 2) + fl32 = uniform(min_float32 / 2, max_float32 / 2) + fl32 = np.float32(fl32) + fl64 = uniform(min_float64 / 2, max_float64 / 2) + + cmplx128_from_float32 = uniform(low=min_float32 / 2, high=max_float32 / 2) + uniform(low=min_float32 / 2, high=max_float32 / 2) * 1j + # the result of the last operation is a Python complex type which has 8 bytes in the alignment, + # that's why we need to convert it to a numpy.complex64 the needed type. + cmplx64 = np.complex64(cmplx128_from_float32) + cmplx128 = np.complex128(uniform(low=min_float64 / 2, high=max_float64 / 2) + uniform(low=min_float64 / 2, high=max_float64 / 2) * 1j) + + epyccel_func = epyccel(return_array, language=language) + + f_integer_output = epyccel_func(integer, integer) + test_int_output = return_array(integer, integer) + + assert np.array_equal(f_integer_output, test_int_output) + assert f_integer_output[0].dtype == test_int_output[0].dtype + + f_integer8_output = epyccel_func(integer8, integer8) + test_int8_output = return_array(integer8, integer8) + + assert np.array_equal(f_integer8_output, test_int8_output) + assert f_integer8_output[0].dtype == test_int8_output[0].dtype + + f_integer16_output = epyccel_func(integer16, integer16) + test_int16_output = return_array(integer16, integer16) + + assert np.array_equal(f_integer16_output, test_int16_output) + assert f_integer16_output[0].dtype == test_int16_output[0].dtype + + f_integer32_output = epyccel_func(integer32, integer32) + test_int32_output = return_array(integer32, integer32) + + assert np.array_equal(f_integer32_output, test_int32_output) + assert f_integer32_output[0].dtype == test_int32_output[0].dtype + + f_integer64_output = epyccel_func(integer64, integer64) + test_int64_output = return_array(integer64, integer64) + + assert np.array_equal(f_integer64_output, test_int64_output) + assert f_integer64_output[0].dtype == test_int64_output[0].dtype + + f_fl_output = epyccel_func(fl, fl) + test_float_output = return_array(fl, fl) + + assert np.array_equal(f_fl_output, test_float_output) + assert f_fl_output[0].dtype == test_float_output[0].dtype + + f_fl32_output = epyccel_func(fl32, fl32) + test_float32_output = return_array(fl32, fl32) + + assert np.array_equal(f_fl32_output, test_float32_output) + assert f_fl32_output[0].dtype == test_float32_output[0].dtype + + f_fl64_output = epyccel_func(fl64, fl64) + test_float64_output = return_array(fl64, fl64) + + assert np.array_equal(f_fl64_output, test_float64_output) + assert f_fl64_output[0].dtype == test_float64_output[0].dtype + + f_cmplx64_output = epyccel_func(cmplx64, cmplx64) + test_cmplx64_output = return_array(cmplx64, cmplx64) + + assert np.array_equal(f_cmplx64_output, test_cmplx64_output) + assert f_cmplx64_output[0].dtype == test_cmplx64_output[0].dtype + + f_cmplx128_output = epyccel_func(cmplx128, cmplx128) + test_cmplx128_output = return_array(cmplx128, cmplx128) + + assert np.array_equal(f_cmplx128_output, test_cmplx128_output) + assert f_cmplx128_output[0].dtype == test_cmplx128_output[0].dtype + +def test_return_multi_array_array_op(language): + + @types('int', 'int') + @types('int8', 'int8') + @types('int16', 'int16') + @types('int32', 'int32') + @types('int64', 'int64') + @types('float', 'float') + @types('float32', 'float32') + @types('float64', 'float64') + @types('complex64', 'complex64') + @types('complex128', 'complex128') + def return_array(a, b): + from numpy import array + x = array([a,b], dtype=type(a)) + y = array([a,b], dtype=type(a)) + return x + y, x - y + + integer8 = randint(min_int8, max_int8, dtype=np.int8) + integer16 = randint(min_int16, max_int16, dtype=np.int16) + integer = randint(min_int, max_int, dtype=int) + integer32 = randint(min_int32, max_int32, dtype=np.int32) + integer64 = randint(min_int64, max_int64, dtype=np.int64) + + fl = uniform(min_float / 2, max_float / 2) + fl32 = uniform(min_float32 / 2, max_float32 / 2) + fl32 = np.float32(fl32) + fl64 = uniform(min_float64 / 2, max_float64 / 2) + + cmplx128_from_float32 = uniform(low=min_float32 / 2, high=max_float32 / 2) + uniform(low=min_float32 / 2, high=max_float32 / 2) * 1j + # the result of the last operation is a Python complex type which has 8 bytes in the alignment, + # that's why we need to convert it to a numpy.complex64 the needed type. + cmplx64 = np.complex64(cmplx128_from_float32) + cmplx128 = np.complex128(uniform(low=min_float64 / 2, high=max_float64 / 2) + uniform(low=min_float64 / 2, high=max_float64 / 2) * 1j) + + epyccel_func = epyccel(return_array, language=language) + + f_integer_output = epyccel_func(integer, integer) + test_int_output = return_array(integer, integer) + + assert np.array_equal(f_integer_output, test_int_output) + assert f_integer_output[0].dtype == test_int_output[0].dtype + + f_integer8_output = epyccel_func(integer8, integer8) + test_int8_output = return_array(integer8, integer8) + + assert np.array_equal(f_integer8_output, test_int8_output) + assert f_integer8_output[0].dtype == test_int8_output[0].dtype + + f_integer16_output = epyccel_func(integer16, integer16) + test_int16_output = return_array(integer16, integer16) + + assert np.array_equal(f_integer16_output, test_int16_output) + assert f_integer16_output[0].dtype == test_int16_output[0].dtype + + f_integer32_output = epyccel_func(integer32, integer32) + test_int32_output = return_array(integer32, integer32) + + assert np.array_equal(f_integer32_output, test_int32_output) + assert f_integer32_output[0].dtype == test_int32_output[0].dtype + + f_integer64_output = epyccel_func(integer64, integer64) + test_int64_output = return_array(integer64, integer64) + + assert np.array_equal(f_integer64_output, test_int64_output) + assert f_integer64_output[0].dtype == test_int64_output[0].dtype + + f_fl_output = epyccel_func(fl, fl) + test_float_output = return_array(fl, fl) + + assert np.array_equal(f_fl_output, test_float_output) + assert f_fl_output[0].dtype == test_float_output[0].dtype + + f_fl32_output = epyccel_func(fl32, fl32) + test_float32_output = return_array(fl32, fl32) + + assert np.array_equal(f_fl32_output, test_float32_output) + assert f_fl32_output[0].dtype == test_float32_output[0].dtype + + f_fl64_output = epyccel_func(fl64, fl64) + test_float64_output = return_array(fl64, fl64) + + assert np.array_equal(f_fl64_output, test_float64_output) + assert f_fl64_output[0].dtype == test_float64_output[0].dtype + + f_cmplx64_output = epyccel_func(cmplx64, cmplx64) + test_cmplx64_output = return_array(cmplx64, cmplx64) + + assert np.array_equal(f_cmplx64_output, test_cmplx64_output) + assert f_cmplx64_output[0].dtype == test_cmplx64_output[0].dtype + + f_cmplx128_output = epyccel_func(cmplx128, cmplx128) + test_cmplx128_output = return_array(cmplx128, cmplx128) + + assert np.array_equal(f_cmplx128_output, test_cmplx128_output) + assert f_cmplx128_output[0].dtype == test_cmplx128_output[0].dtype + +def test_return_array_scalar_op(language): + + @types('int8') + @types('int16') + @types('int32') + @types('int64') + @types('int') + @types('float32') + @types('float64') + @types('float') + @types('complex64') + @types('complex128') + def return_array_scalar_op(a): + from numpy import ones, int8, int16, int32, int64, float32, float64, complex64, complex128 # pylint: disable=unused-import + x = ones(5, dtype=type(a)) + return x * a + + integer8 = randint(min_int8, max_int8, dtype=np.int8) + integer16 = randint(min_int16, max_int16, dtype=np.int16) + integer = randint(min_int, max_int, dtype=int) + integer32 = randint(min_int32, max_int32, dtype=np.int32) + integer64 = randint(min_int64, max_int64, dtype=np.int64) + + fl = uniform(min_float / 2, max_float / 2) + fl32 = uniform(min_float32 / 2, max_float32 / 2) + fl32 = np.float32(fl32) + fl64 = uniform(min_float64 / 2, max_float64 / 2) + + cmplx128_from_float32 = uniform(low=min_float32 / 2, high=max_float32 / 2) + uniform(low=min_float32 / 2, high=max_float32 / 2) * 1j + # the result of the last operation is a Python complex type which has 8 bytes in the alignment, + # that's why we need to convert it to a numpy.complex64 the needed type. + cmplx64 = np.complex64(cmplx128_from_float32) + cmplx128 = np.complex128(uniform(low=min_float64 / 2, high=max_float64 / 2) + uniform(low=min_float64 / 2, high=max_float64 / 2) * 1j) + + epyccel_func = epyccel(return_array_scalar_op, language=language) + + f_integer_output = epyccel_func(integer) + test_int_output = return_array_scalar_op(integer) + + assert np.array_equal(f_integer_output, test_int_output) + assert f_integer_output[0].dtype == test_int_output[0].dtype + + f_integer8_output = epyccel_func(integer8) + test_int8_output = return_array_scalar_op(integer8) + + assert np.array_equal(f_integer8_output, test_int8_output) + assert f_integer8_output[0].dtype == test_int8_output[0].dtype + + f_integer16_output = epyccel_func(integer16) + test_int16_output = return_array_scalar_op(integer16) + + assert np.array_equal(f_integer16_output, test_int16_output) + assert f_integer16_output[0].dtype == test_int16_output[0].dtype + + f_integer32_output = epyccel_func(integer32) + test_int32_output = return_array_scalar_op(integer32) + + assert np.array_equal(f_integer32_output, test_int32_output) + assert f_integer32_output[0].dtype == test_int32_output[0].dtype + + f_integer64_output = epyccel_func(integer64) + test_int64_output = return_array_scalar_op(integer64) + + assert np.array_equal(f_integer64_output, test_int64_output) + assert f_integer64_output[0].dtype == test_int64_output[0].dtype + + f_fl_output = epyccel_func(fl) + test_float_output = return_array_scalar_op(fl) + + assert np.array_equal(f_fl_output, test_float_output) + assert f_fl_output[0].dtype == test_float_output[0].dtype + + f_fl32_output = epyccel_func(fl32) + test_float32_output = return_array_scalar_op(fl32) + + assert np.array_equal(f_fl32_output, test_float32_output) + assert f_fl32_output[0].dtype == test_float32_output[0].dtype + + f_fl64_output = epyccel_func(fl64) + test_float64_output = return_array_scalar_op(fl64) + + assert np.array_equal(f_fl64_output, test_float64_output) + assert f_fl64_output[0].dtype == test_float64_output[0].dtype + + f_cmplx64_output = epyccel_func(cmplx64) + test_cmplx64_output = return_array_scalar_op(cmplx64) + + assert np.array_equal(f_cmplx64_output, test_cmplx64_output) + assert f_cmplx64_output[0].dtype == test_cmplx64_output[0].dtype + + f_cmplx128_output = epyccel_func(cmplx128) + test_cmplx128_output = return_array_scalar_op(cmplx128) + + assert np.array_equal(f_cmplx128_output, test_cmplx128_output) + assert f_cmplx128_output[0].dtype == test_cmplx128_output[0].dtype + +def test_multi_return_array_scalar_op(language): + + @types('int8') + @types('int16') + @types('int32') + @types('int64') + @types('int') + @types('float32') + @types('float64') + @types('float') + @types('complex64') + @types('complex128') + def return_multi_array_scalar_op(a): + from numpy import ones, int8, int16, int32, int64, float32, float64, complex64, complex128 #pylint: disable=unused-import + x = ones(5, dtype=type(a)) + y = ones(5, dtype=type(a)) + return x * a, y * a + + integer8 = randint(min_int8, max_int8, dtype=np.int8) + integer16 = randint(min_int16, max_int16, dtype=np.int16) + integer = randint(min_int, max_int, dtype=int) + integer32 = randint(min_int32, max_int32, dtype=np.int32) + integer64 = randint(min_int64, max_int64, dtype=np.int64) + + fl = uniform(min_float / 2, max_float / 2) + fl32 = uniform(min_float32 / 2, max_float32 / 2) + fl32 = np.float32(fl32) + fl64 = uniform(min_float64 / 2, max_float64 / 2) + + cmplx128_from_float32 = uniform(low=min_float32 / 2, high=max_float32 / 2) + uniform(low=min_float32 / 2, high=max_float32 / 2) * 1j + # the result of the last operation is a Python complex type which has 8 bytes in the alignment, + # that's why we need to convert it to a numpy.complex64 the needed type. + cmplx64 = np.complex64(cmplx128_from_float32) + cmplx128 = np.complex128(uniform(low=min_float64 / 2, high=max_float64 / 2) + uniform(low=min_float64 / 2, high=max_float64 / 2) * 1j) + + epyccel_func = epyccel(return_multi_array_scalar_op, language=language) + + f_integer_output = epyccel_func(integer) + test_int_output = return_multi_array_scalar_op(integer) + + assert np.array_equal(f_integer_output, test_int_output) + assert f_integer_output[0].dtype == test_int_output[0].dtype + + f_integer8_output = epyccel_func(integer8) + test_int8_output = return_multi_array_scalar_op(integer8) + + assert np.array_equal(f_integer8_output, test_int8_output) + assert f_integer8_output[0].dtype == test_int8_output[0].dtype + + f_integer16_output = epyccel_func(integer16) + test_int16_output = return_multi_array_scalar_op(integer16) + + assert np.array_equal(f_integer16_output, test_int16_output) + assert f_integer16_output[0].dtype == test_int16_output[0].dtype + + f_integer32_output = epyccel_func(integer32) + test_int32_output = return_multi_array_scalar_op(integer32) + + assert np.array_equal(f_integer32_output, test_int32_output) + assert f_integer32_output[0].dtype == test_int32_output[0].dtype + + f_integer64_output = epyccel_func(integer64) + test_int64_output = return_multi_array_scalar_op(integer64) + + assert np.array_equal(f_integer64_output, test_int64_output) + assert f_integer64_output[0].dtype == test_int64_output[0].dtype + + f_fl_output = epyccel_func(fl) + test_float_output = return_multi_array_scalar_op(fl) + + assert np.array_equal(f_fl_output, test_float_output) + assert f_fl_output[0].dtype == test_float_output[0].dtype + + f_fl32_output = epyccel_func(fl32) + test_float32_output = return_multi_array_scalar_op(fl32) + + assert np.array_equal(f_fl32_output, test_float32_output) + assert f_fl32_output[0].dtype == test_float32_output[0].dtype + + f_fl64_output = epyccel_func(fl64) + test_float64_output = return_multi_array_scalar_op(fl64) + + assert np.array_equal(f_fl64_output, test_float64_output) + assert f_fl64_output[0].dtype == test_float64_output[0].dtype + + f_cmplx64_output = epyccel_func(cmplx64) + test_cmplx64_output = return_multi_array_scalar_op(cmplx64) + + assert np.array_equal(f_cmplx64_output, test_cmplx64_output) + assert f_cmplx64_output[0].dtype == test_cmplx64_output[0].dtype + + f_cmplx128_output = epyccel_func(cmplx128) + test_cmplx128_output = return_multi_array_scalar_op(cmplx128) + + assert np.array_equal(f_cmplx128_output, test_cmplx128_output) + assert f_cmplx128_output[0].dtype == test_cmplx128_output[0].dtype + +def test_multi_return_array_array_op(language): + + @types('int8[:]') + @types('int16[:]') + @types('int32[:]') + @types('int64[:]') + @types('int[:]') + @types('float32[:]') + @types('float64[:]') + @types('float[:]') + @types('complex64[:]') + @types('complex128[:]') + def return_array_arg_array_op(a): + from numpy import ones + x = ones(7) + return x * a + + arr_integer8 = np.ones(7, dtype=np.int8) + arr_integer16 = np.ones(7, dtype=np.int16) + arr_integer = np.ones(7, dtype=int) + arr_integer32 = np.ones(7, dtype=np.int32) + arr_integer64 = np.ones(7, dtype=np.int64) + + arr_fl = np.ones(7, dtype=float) + arr_fl32 = np.ones(7, dtype=np.float32) + arr_fl64 = np.ones(7, dtype=np.float64) + + # the result of the last operation is a Python complex type which has 8 bytes in the alignment, + # that's why we need to convert it to a numpy.complex64 the needed type. + cmplx64 = np.ones(7, dtype=np.float32) + np.ones(7, dtype=np.float32) * 1j + cmplx128 = np.ones(7, dtype=np.float64) + np.ones(7, dtype=np.float64) * 1j + + epyccel_func = epyccel(return_array_arg_array_op, language=language) + + f_integer_output = epyccel_func(arr_integer) + test_int_output = return_array_arg_array_op(arr_integer) + + assert np.array_equal(f_integer_output, test_int_output) + assert f_integer_output[0].dtype == test_int_output[0].dtype + + f_integer8_output = epyccel_func(arr_integer8) + test_int8_output = return_array_arg_array_op(arr_integer8) + + assert np.array_equal(f_integer8_output, test_int8_output) + assert f_integer8_output[0].dtype == test_int8_output[0].dtype + + f_integer16_output = epyccel_func(arr_integer16) + test_int16_output = return_array_arg_array_op(arr_integer16) + + assert np.array_equal(f_integer16_output, test_int16_output) + assert f_integer16_output[0].dtype == test_int16_output[0].dtype + + f_integer32_output = epyccel_func(arr_integer32) + test_int32_output = return_array_arg_array_op(arr_integer32) + + assert np.array_equal(f_integer32_output, test_int32_output) + assert f_integer32_output[0].dtype == test_int32_output[0].dtype + + f_integer64_output = epyccel_func(arr_integer64) + test_int64_output = return_array_arg_array_op(arr_integer64) + + assert np.array_equal(f_integer64_output, test_int64_output) + assert f_integer64_output[0].dtype == test_int64_output[0].dtype + + f_fl_output = epyccel_func(arr_fl) + test_float_output = return_array_arg_array_op(arr_fl) + + assert np.array_equal(f_fl_output, test_float_output) + assert f_fl_output[0].dtype == test_float_output[0].dtype + + f_fl32_output = epyccel_func(arr_fl32) + test_float32_output = return_array_arg_array_op(arr_fl32) + + assert np.array_equal(f_fl32_output, test_float32_output) + assert f_fl32_output[0].dtype == test_float32_output[0].dtype + + f_fl64_output = epyccel_func(arr_fl64) + test_float64_output = return_array_arg_array_op(arr_fl64) + + assert np.array_equal(f_fl64_output, test_float64_output) + assert f_fl64_output[0].dtype == test_float64_output[0].dtype + + f_cmplx64_output = epyccel_func(cmplx64) + test_cmplx64_output = return_array_arg_array_op(cmplx64) + + assert np.array_equal(f_cmplx64_output, test_cmplx64_output) + assert f_cmplx64_output[0].dtype == test_cmplx64_output[0].dtype + + f_cmplx128_output = epyccel_func(cmplx128) + test_cmplx128_output = return_array_arg_array_op(cmplx128) + + assert np.array_equal(f_cmplx128_output, test_cmplx128_output) + assert f_cmplx128_output[0].dtype == test_cmplx128_output[0].dtype + @pytest.mark.parametrize( 'language', ( pytest.param("fortran", marks = pytest.mark.fortran), pytest.param("c", marks = [ diff --git a/tests/epyccel/test_epyccel_sign.py b/tests/epyccel/test_epyccel_sign.py new file mode 100644 index 0000000000..5655e8554c --- /dev/null +++ b/tests/epyccel/test_epyccel_sign.py @@ -0,0 +1,310 @@ +# pylint: disable=missing-function-docstring, missing-module-docstring/ + +import numpy as np +import modules.numpy_sign as mod + +from pyccel.epyccel import epyccel + +def test_sign_complex(language): + f_nul = mod.complex_nul + f_pos = mod.complex_pos + f_neg = mod.complex_neg + f_pos_neg = mod.complex_pos_neg + f_neg_pos = mod.complex_neg_pos + f_nul_epyc = epyccel(f_nul, language = language) + f_pos_epyc = epyccel(f_pos, language = language) + f_neg_epyc = epyccel(f_neg, language = language) + f_pos_neg_epyc = epyccel(f_pos_neg, language = language) + f_neg_pos_epyc = epyccel(f_neg_pos, language = language) + + x1_nul, x2_nul = f_nul(), f_nul_epyc() + x1_pos, x2_pos = f_pos(), f_pos_epyc() + x1_neg, x2_neg = f_neg(), f_neg_epyc() + x1_pos_neg, x2_pos_neg = f_pos_neg(), f_pos_neg_epyc() + x1_neg_pos, x2_neg_pos = f_neg_pos(), f_neg_pos_epyc() + + assert x1_nul == x2_nul and x1_nul.dtype == x2_nul.dtype + assert x1_pos == x2_pos and x1_pos.dtype == x2_pos.dtype + assert x1_neg == x2_neg and x1_neg.dtype == x2_neg.dtype + assert x1_pos_neg == x2_pos_neg and x1_pos_neg.dtype == x2_pos_neg.dtype + assert x1_neg_pos == x2_neg_pos and x1_neg_pos.dtype == x2_neg_pos.dtype + +def test_sign_complex64(language): + f_nul = mod.complex64_nul + f_pos = mod.complex64_pos + f_neg = mod.complex64_neg + f_pos_neg = mod.complex64_pos_neg + f_neg_pos = mod.complex64_neg_pos + f_nul_epyc = epyccel(f_nul, language = language) + f_pos_epyc = epyccel(f_pos, language = language) + f_neg_epyc = epyccel(f_neg, language = language) + f_pos_neg_epyc = epyccel(f_pos_neg, language = language) + f_neg_pos_epyc = epyccel(f_neg_pos, language = language) + + x1_nul, x2_nul = f_nul(), f_nul_epyc() + x1_pos, x2_pos = f_pos(), f_pos_epyc() + x1_neg, x2_neg = f_neg(), f_neg_epyc() + x1_pos_neg, x2_pos_neg = f_pos_neg(), f_pos_neg_epyc() + x1_neg_pos, x2_neg_pos = f_neg_pos(), f_neg_pos_epyc() + + assert x1_nul == x2_nul and x1_nul.dtype == x2_nul.dtype + assert x1_pos == x2_pos and x1_pos.dtype == x2_pos.dtype + assert x1_neg == x2_neg and x1_neg.dtype == x2_neg.dtype + assert x1_pos_neg == x2_pos_neg and x1_pos_neg.dtype == x2_pos_neg.dtype + assert x1_neg_pos == x2_neg_pos and x1_neg_pos.dtype == x2_neg_pos.dtype + +def test_sign_complex128(language): + f_nul = mod.complex128_nul + f_pos = mod.complex128_pos + f_neg = mod.complex128_neg + f_pos_neg = mod.complex128_pos_neg + f_neg_pos = mod.complex128_neg_pos + f_nul_epyc = epyccel(f_nul, language = language) + f_pos_epyc = epyccel(f_pos, language = language) + f_neg_epyc = epyccel(f_neg, language = language) + f_pos_neg_epyc = epyccel(f_pos_neg, language = language) + f_neg_pos_epyc = epyccel(f_neg_pos, language = language) + + x1_nul, x2_nul = f_nul(), f_nul_epyc() + x1_pos, x2_pos = f_pos(), f_pos_epyc() + x1_neg, x2_neg = f_neg(), f_neg_epyc() + x1_pos_neg, x2_pos_neg = f_pos_neg(), f_pos_neg_epyc() + x1_neg_pos, x2_neg_pos = f_neg_pos(), f_neg_pos_epyc() + + assert x1_nul == x2_nul and x1_nul.dtype == x2_nul.dtype + assert x1_pos == x2_pos and x1_pos.dtype == x2_pos.dtype + assert x1_neg == x2_neg and x1_neg.dtype == x2_neg.dtype + assert x1_pos_neg == x2_pos_neg and x1_pos_neg.dtype == x2_pos_neg.dtype + assert x1_neg_pos == x2_neg_pos and x1_neg_pos.dtype == x2_neg_pos.dtype + +def test_sign_int16(language): + f_pos = mod.int16_pos + f_neg = mod.int16_neg + f_pos_epyc = epyccel(f_pos, language = language) + f_neg_epyc = epyccel(f_neg, language = language) + + x1_pos, x2_pos = f_pos(), f_pos_epyc() + x1_neg, x2_neg = f_neg(), f_neg_epyc() + + assert x1_pos == x2_pos and x1_pos.dtype == x2_pos.dtype + assert x1_neg == x2_neg and x1_neg.dtype == x2_neg.dtype + +def test_sign_int32(language): + f_pos = mod.int32_pos + f_neg = mod.int32_neg + f_pos_epyc = epyccel(f_pos, language = language) + f_neg_epyc = epyccel(f_neg, language = language) + + x1_pos, x2_pos = f_pos(), f_pos_epyc() + x1_neg, x2_neg = f_neg(), f_neg_epyc() + + assert x1_pos == x2_pos and x1_pos.dtype == x2_pos.dtype + assert x1_neg == x2_neg and x1_neg.dtype == x2_neg.dtype + +def test_sign_int64(language): + f_pos = mod.int64_pos + f_neg = mod.int64_neg + f_pos_epyc = epyccel(f_pos, language = language) + f_neg_epyc = epyccel(f_neg, language = language) + + x1_pos, x2_pos = f_pos(), f_pos_epyc() + x1_neg, x2_neg = f_neg(), f_neg_epyc() + + assert x1_pos == x2_pos and x1_pos.dtype == x2_pos.dtype + assert x1_neg == x2_neg and x1_neg.dtype == x2_neg.dtype + +def test_sign_float(language): + f_pos = mod.float_pos + f_neg = mod.float_neg + f_nul = mod.float_nul + f_pos_epyc = epyccel(f_pos, language = language) + f_neg_epyc = epyccel(f_neg, language = language) + f_nul_epyc = epyccel(f_nul, language = language) + + x1_pos, x2_pos = f_pos(), f_pos_epyc() + x1_neg, x2_neg = f_neg(), f_neg_epyc() + x1_nul, x2_nul = f_nul(), f_nul_epyc() + + assert x1_pos == x2_pos and x1_pos.dtype == x2_pos.dtype + assert x1_neg == x2_neg and x1_neg.dtype == x2_neg.dtype + assert x1_nul == x2_nul and x1_nul.dtype == x2_nul.dtype + +def test_sign_float64(language): + f_pos = mod.float64_pos + f_neg = mod.float64_neg + f_pos_epyc = epyccel(f_pos, language = language) + f_neg_epyc = epyccel(f_neg, language = language) + + x1_pos, x2_pos = f_pos(), f_pos_epyc() + x1_neg, x2_neg = f_neg(), f_neg_epyc() + + assert x1_pos == x2_pos and x1_pos.dtype == x2_pos.dtype + assert x1_neg == x2_neg and x1_neg.dtype == x2_neg.dtype + +def test_sign_literal_complex(language): + f_pos = mod.literal_complex_pos + f_neg = mod.literal_complex_neg + f_nul = mod.literal_complex_nul_nul + f_nul_imag = mod.literal_complex_nul_imag + f_real_nul = mod.literal_complex_real_nul + f_pos_epyc = epyccel(f_pos, language = language) + f_neg_epyc = epyccel(f_neg, language = language) + f_nul_epyc = epyccel(f_nul, language = language) + f_nul_imag_epyc = epyccel(f_nul_imag, language = language) + f_real_nul_epyc = epyccel(f_real_nul, language = language) + + x1_pos, x2_pos = f_pos(), f_pos_epyc() + x1_neg, x2_neg = f_neg(), f_neg_epyc() + x1_nul, x2_nul = f_nul(), f_nul_epyc() + x1_nul_imag, x2_nul_imag = f_nul_imag(), f_nul_imag_epyc() + x1_real_nul, x2_real_nul = f_real_nul(), f_real_nul_epyc() + + assert x1_pos == x2_pos and x1_pos.dtype == x2_pos.dtype + assert x1_neg == x2_neg and x1_neg.dtype == x2_neg.dtype + assert x1_nul == x2_nul and x1_nul.dtype == x2_nul.dtype + assert x1_nul_imag == x2_nul_imag and x1_nul_imag.dtype == x2_nul_imag.dtype + assert x1_real_nul == x2_real_nul and x1_real_nul.dtype == x2_real_nul.dtype + +def test_sign_literal_int(language): + f_pos = mod.literal_int_pos + f_neg = mod.literal_int_neg + f_nul = mod.literal_int_nul + f_pos_epyc = epyccel(f_pos, language = language) + f_neg_epyc = epyccel(f_neg, language = language) + f_nul_epyc = epyccel(f_nul, language = language) + + x1_pos, x2_pos = f_pos(), f_pos_epyc() + x1_neg, x2_neg = f_neg(), f_neg_epyc() + x1_nul, x2_nul = f_nul(), f_nul_epyc() + + assert x1_pos == x2_pos and x1_pos.dtype == x2_pos.dtype + assert x1_neg == x2_neg and x1_neg.dtype == x2_neg.dtype + assert x1_nul == x2_nul and x1_nul.dtype == x2_nul.dtype + +def test_sign_literal_float(language): + f_pos = mod.literal_float_pos + f_neg = mod.literal_float_neg + f_nul = mod.literal_float_nul + f_pos_epyc = epyccel(f_pos, language = language) + f_neg_epyc = epyccel(f_neg, language = language) + f_nul_epyc = epyccel(f_nul, language = language) + + x1_pos, x2_pos = f_pos(), f_pos_epyc() + x1_neg, x2_neg = f_neg(), f_neg_epyc() + x1_nul, x2_nul = f_nul(), f_nul_epyc() + + assert x1_pos == x2_pos and x1_pos.dtype == x2_pos.dtype + assert x1_neg == x2_neg and x1_neg.dtype == x2_neg.dtype + assert x1_nul == x2_nul and x1_nul.dtype == x2_nul.dtype + +# Tests on arrays + +def test_sign_array_1d_int(language): + f_int8 = mod.array_1d_int8 + f_int16 = mod.array_1d_int16 + f_int32 = mod.array_1d_int32 + f_int64 = mod.array_1d_int64 + f_int8_epyc = epyccel(f_int8, language = language) + f_int16_epyc = epyccel(f_int16, language = language) + f_int32_epyc = epyccel(f_int32, language = language) + f_int64_epyc = epyccel(f_int64, language = language) + + arr8 = np.array([2, 0, -0, -13, 37, 42], dtype=np.int8) + arr16 = np.array([2, 0, -0, -13, 37, 42], dtype=np.int16) + arr32 = np.array([2, 0, -0, -13, 37, 42], dtype=np.int32) + arr64 = np.array([2, 0, -0, -13, 37, 42], dtype=np.int64) + + x_int8, y_int8 = f_int8(arr8), f_int8_epyc(arr8) + x_int16, y_int16 = f_int16(arr16), f_int16_epyc(arr16) + x_int32, y_int32 = f_int32(arr32), f_int32_epyc(arr32) + x_int64, y_int64 = f_int64(arr64), f_int64_epyc(arr64) + + assert np.array_equal(x_int8, y_int8) and x_int8.dtype == y_int8.dtype + assert np.array_equal(x_int16, y_int16) and x_int16.dtype == y_int16.dtype + assert np.array_equal(x_int32, y_int32) and x_int32.dtype == y_int32.dtype + assert np.array_equal(x_int64, y_int64) and x_int64.dtype == y_int64.dtype + +def test_sign_array_2d_int(language): + f_int8 = mod.array_2d_int8 + f_int16 = mod.array_2d_int16 + f_int32 = mod.array_2d_int32 + f_int64 = mod.array_2d_int64 + f_int8_epyc = epyccel(f_int8, language = language) + f_int16_epyc = epyccel(f_int16, language = language) + f_int32_epyc = epyccel(f_int32, language = language) + f_int64_epyc = epyccel(f_int64, language = language) + + arr8 = np.array([[2, 0], [-0, -13], [37, 42]], dtype=np.int8) + arr16 = np.array([[2, 0], [-0, -13], [37, 42]], dtype=np.int16) + arr32 = np.array([[2, 0], [-0, -13], [37, 42]], dtype=np.int32) + arr64 = np.array([[2, 0], [-0, -13], [37, 42]], dtype=np.int64) + + x_int8, y_int8 = f_int8(arr8), f_int8_epyc(arr8) + x_int16, y_int16 = f_int16(arr16), f_int16_epyc(arr16) + x_int32, y_int32 = f_int32(arr32), f_int32_epyc(arr32) + x_int64, y_int64 = f_int64(arr64), f_int64_epyc(arr64) + + assert np.array_equal(x_int8, y_int8) and x_int8.dtype == y_int8.dtype + assert np.array_equal(x_int16, y_int16) and x_int16.dtype == y_int16.dtype + assert np.array_equal(x_int32, y_int32) and x_int32.dtype == y_int32.dtype + assert np.array_equal(x_int64, y_int64) and x_int64.dtype == y_int64.dtype + +def test_sign_array_1d_float(language): + f_float32 = mod.array_1d_float32 + f_float64 = mod.array_1d_float64 + f_float32_epyc = epyccel(f_float32, language = language) + f_float64_epyc = epyccel(f_float64, language = language) + + arr32 = np.array([2., 0., -0., -1.3, 3.7, .42], dtype=np.float32) + arr64 = np.array([2., 0., -0., -1.3, 3.7, .42], dtype=np.float64) + + x_float32, y_float32 = f_float32(arr32), f_float32_epyc(arr32) + x_float64, y_float64 = f_float64(arr64), f_float64_epyc(arr64) + + assert np.array_equal(x_float32, y_float32) and x_float32.dtype == y_float32.dtype + assert np.array_equal(x_float64, y_float64) and x_float64.dtype == y_float64.dtype + +def test_sign_array_2d_float(language): + f_float32 = mod.array_2d_float32 + f_float64 = mod.array_2d_float64 + f_float32_epyc = epyccel(f_float32, language = language) + f_float64_epyc = epyccel(f_float64, language = language) + + arr32 = np.array([[2., 0.], [-0., -1.3], [3.7, .42]], dtype=np.float32) + arr64 = np.array([[2., 0.], [-0., -1.3], [3.7, .42]], dtype=np.float64) + + x_float32, y_float32 = f_float32(arr32), f_float32_epyc(arr32) + x_float64, y_float64 = f_float64(arr64), f_float64_epyc(arr64) + + assert np.array_equal(x_float32, y_float32) and x_float32.dtype == y_float32.dtype + assert np.array_equal(x_float64, y_float64) and x_float64.dtype == y_float64.dtype + +def test_sign_array_1d_complex(language): + f_complex64 = mod.array_1d_complex64 + f_complex128 = mod.array_1d_complex128 + f_complex64_epyc = epyccel(f_complex64, language = language) + f_complex128_epyc = epyccel(f_complex128, language = language) + + arr64 = np.array([0.+0j, 0.j, 1.+2.j, -1.+2.j, 1.-2.j, -1.-2.j, 2.j, -2.j], dtype=np.complex64) + arr128 = np.array([0.+0j, 0.j, 1.+2.j, -1.+2.j, 1.-2.j, -1.-2.j, 2.j, -2.j], dtype=np.complex128) + + x_complex64, y_complex64 = f_complex64(arr64), f_complex64_epyc(arr64) + x_complex128, y_complex128 = f_complex128(arr128), f_complex128_epyc(arr128) + + assert np.array_equal(x_complex64, y_complex64) and x_complex64.dtype == y_complex64.dtype + assert np.array_equal(x_complex128, y_complex128) and x_complex128.dtype == y_complex128.dtype + +def test_sign_array_2d_complex(language): + f_complex64 = mod.array_2d_complex64 + f_complex128 = mod.array_2d_complex128 + f_complex64_epyc = epyccel(f_complex64, language = language) + f_complex128_epyc = epyccel(f_complex128, language = language) + + arr64 = np.array([[0.+0j, 0.j], [1.+2.j, -1.+2.j], [1.-2.j, -1.-2.j], [2.j, -2.j]], dtype=np.complex64) + arr128 = np.array([[0.+0j, 0.j], [1.+2.j, -1.+2.j], [1.-2.j, -1.-2.j], [2.j, -2.j]], dtype=np.complex128) + + x_complex64, y_complex64 = f_complex64(arr64), f_complex64_epyc(arr64) + x_complex128, y_complex128 = f_complex128(arr128), f_complex128_epyc(arr128) + + assert np.array_equal(x_complex64, y_complex64) and x_complex64.dtype == y_complex64.dtype + assert np.array_equal(x_complex128, y_complex128) and x_complex128.dtype == y_complex128.dtype diff --git a/tests/epyccel/test_generic_functions.py b/tests/epyccel/test_generic_functions.py index 5f1bcf802b..62dba5d8e7 100644 --- a/tests/epyccel/test_generic_functions.py +++ b/tests/epyccel/test_generic_functions.py @@ -317,7 +317,7 @@ def test_mix_int_array(language): f2(x2, a) assert np.array_equal( x1, x2 ) - x1 = np.array([166,20,-5], dtype=np.int8) + x1 = np.array([126,20,-5], dtype=np.int8) x2 = np.copy(x1) f1(x1, a) f2(x2, a) @@ -418,4 +418,3 @@ def test_zeros_types(language): assert fl_1 == fl_2 assert isinstance(fl_1, type(fl_2)) - diff --git a/tests/epyccel/test_return.py b/tests/epyccel/test_return.py index 07cce2a0f3..e3482b9ffd 100644 --- a/tests/epyccel/test_return.py +++ b/tests/epyccel/test_return.py @@ -174,3 +174,24 @@ def divide_by(a : 'float[:]', b : 'float'): # pylint: disable=inconsistent-retur divide_by(x_copy,b) assert np.allclose(x, x_copy, rtol=1e-13, atol=1e-14) +def test_arg_arr_element_op(language): + def return_mult_arr_arg_element(i: 'int', arg:'float[:]'): + import numpy as np + a = np.ones(i) + return a[0] * arg[0] + def return_add_arr_arg_element(i: 'int', arg:'float[:]'): + import numpy as np + a = np.ones(i) + return a[0] + arg[0] + def return_op_arr_arg_element(i: 'int', arg:'float[:]'): + import numpy as np + a = np.ones(i) + return ((a[2] + arg[0]) * arg[2] - 2) / 4 * 2 + + arr = np.array([1,2,3,4], dtype=float) + epyc_return_mult_arr_arg_element = epyccel(return_mult_arr_arg_element, language=language, fflags="-Werror -Wunused-variable") + assert (epyc_return_mult_arr_arg_element(7, arr) == return_mult_arr_arg_element(7, arr)) + epyc_return_add_arr_arg_element = epyccel(return_add_arr_arg_element, language=language, fflags="-Werror -Wunused-variable") + assert (epyc_return_add_arr_arg_element(7, arr) == return_add_arr_arg_element(7, arr)) + epyc_return_op_arr_arg_element = epyccel(return_op_arr_arg_element, language=language, fflags="-Werror -Wunused-variable") + assert (epyc_return_op_arr_arg_element(7, arr) == return_op_arr_arg_element(7, arr)) diff --git a/tests/pyccel/scripts/array_binary_operation.py b/tests/pyccel/scripts/array_binary_operation.py new file mode 100644 index 0000000000..e52c028aa9 --- /dev/null +++ b/tests/pyccel/scripts/array_binary_operation.py @@ -0,0 +1,92 @@ +# pylint: disable=missing-function-docstring, missing-module-docstring/ +import numpy as np +from pyccel.decorators import types + +@types('int', 'int') +def my_pow(n, m): + return n ** m + +def array_func_mult(): + arr = np.array([1,2,3,4]) + arr1 = arr * my_pow(2, 3) + shape = np.shape(arr1) + return arr[0], arr1[0], len(shape), shape[0] + +def array_func_div(): + arr = np.array([1,2,3,4]) + arr1 = arr / my_pow(2, 3) + shape = np.shape(arr1) + return arr[0], arr1[0], len(shape), shape[0] + +def array_arithmetic_op_func_call_1(): + arr = np.array([1,2,3,4]) + arr1 = np.array(arr * 2) + shape = np.shape(arr1) + return arr[0], arr1[0], len(shape), shape[0] + +def array_arithmetic_op_func_call_2(): + arr = np.array([1,2,3,4]) + arr1 = np.array(arr / 2) + shape = np.shape(arr1) + return arr[0], arr1[0], len(shape), shape[0] + +def array_arithmetic_op_func_call_3(): + arr = np.array([1,2,3,4]) + arr1 = np.array(arr * my_pow(2, 2)) + shape = np.shape(arr1) + return arr[0], arr1[0], len(shape), shape[0] + +def array_arithmetic_op_func_call_4(): + arr = np.array([1,2,3,4]) + arr1 = np.array(arr / my_pow(2, 2) + arr * 2) + shape = np.shape(arr1) + return arr[0], arr1[0], len(shape), shape[0] + +def array_arithmetic_op_func_call_5(): + arr = np.array([1,2,3,4]) + arr1 = np.where(arr > 5, arr, (arr * 2) + arr) + shape = np.shape(arr1) + return arr[0], arr1[0], len(shape), shape[0] + +def array_arithmetic_op_func_call_6(): + arr = np.array([1,2,3,4]) + arr1 = np.where(arr < 5, arr / 2, arr * 2) + shape = np.shape(arr1) + return arr[0], arr1[0], len(shape), shape[0] + +def array_arithmetic_op_func_call_7(): + arr = np.array([1,2,3,4]) + arr1 = np.array([4,3,2,1]) + arr2 = np.array(arr + arr1) + shape = np.shape(arr2) + return arr[0], arr2[0], len(shape), shape[0] + +def array_arithmetic_op_func_call_8(): + arr = np.array([1,2,3,4]) + arr1 = np.array([4,3,2,1]) + arr2 = np.array(arr - arr1) + shape = np.shape(arr2) + return arr[0], arr2[0], len(shape), shape[0] + + +if __name__ == "__main__": + a_0, a1_0, ls_0, s_0 = array_func_mult() + print(a_0, a1_0, ls_0, s_0) + a_1, a1_1, ls_1, s_1 = array_func_div() + print(a_1, a1_1, ls_1, s_1) + a_2, a1_2, ls_2, s_2 = array_arithmetic_op_func_call_1() + print(a_2, a1_2, ls_2, s_2) + a_3, a1_3, ls_3, s_3 = array_arithmetic_op_func_call_2() + print(a_3, a1_3, ls_3, s_3) + a_4, a1_4, ls_4, s_4 = array_arithmetic_op_func_call_3() + print(a_4, a1_4, ls_4, s_4) + a_5, a1_5, ls_5, s_5 = array_arithmetic_op_func_call_4() + print(a_5, a1_5, ls_5, s_5) + a_6, a1_6, ls_6, s_6 = array_arithmetic_op_func_call_5() + print(a_6, a1_6, ls_6, s_6) + a_7, a1_7, ls_7, s_7 = array_arithmetic_op_func_call_6() + print(a_7, a1_7, ls_7, s_7) + a_8, a1_8, ls_8, s_8 = array_arithmetic_op_func_call_7() + print(a_8, a1_8, ls_8, s_8) + a_9, a1_9, ls_9, s_9 = array_arithmetic_op_func_call_8() + print(a_9, a1_9, ls_9, s_9) diff --git a/tests/pyccel/scripts/exits/empty_exit.py b/tests/pyccel/scripts/exits/empty_exit.py new file mode 100644 index 0000000000..e1a274c1e4 --- /dev/null +++ b/tests/pyccel/scripts/exits/empty_exit.py @@ -0,0 +1,6 @@ +# pylint: disable=missing-function-docstring, missing-module-docstring/ + +import sys + +if __name__ == "__main__": + sys.exit() diff --git a/tests/pyccel/scripts/exits/negative_exit1.py b/tests/pyccel/scripts/exits/negative_exit1.py new file mode 100644 index 0000000000..e848e36738 --- /dev/null +++ b/tests/pyccel/scripts/exits/negative_exit1.py @@ -0,0 +1,6 @@ +# pylint: disable=missing-function-docstring, missing-module-docstring/ + +from sys import exit as sys_exit + +if __name__ == "__main__": + sys_exit(-1) diff --git a/tests/pyccel/scripts/exits/negative_exit2.py b/tests/pyccel/scripts/exits/negative_exit2.py new file mode 100644 index 0000000000..096997ed50 --- /dev/null +++ b/tests/pyccel/scripts/exits/negative_exit2.py @@ -0,0 +1,6 @@ +# pylint: disable=missing-function-docstring, missing-module-docstring/ + +import sys + +if __name__ == "__main__": + sys.exit(-345) diff --git a/tests/pyccel/scripts/exits/positive_exit1.py b/tests/pyccel/scripts/exits/positive_exit1.py new file mode 100644 index 0000000000..8b13dfc8d4 --- /dev/null +++ b/tests/pyccel/scripts/exits/positive_exit1.py @@ -0,0 +1,6 @@ +# pylint: disable=missing-function-docstring, missing-module-docstring/ + +import sys + +if __name__ == "__main__": + sys.exit(1) diff --git a/tests/pyccel/scripts/exits/positive_exit2.py b/tests/pyccel/scripts/exits/positive_exit2.py new file mode 100644 index 0000000000..3d0bfff6dc --- /dev/null +++ b/tests/pyccel/scripts/exits/positive_exit2.py @@ -0,0 +1,6 @@ +# pylint: disable=missing-function-docstring, missing-module-docstring/ + +import sys + +if __name__ == "__main__": + sys.exit(1024) diff --git a/tests/pyccel/scripts/exits/positive_exit3.py b/tests/pyccel/scripts/exits/positive_exit3.py new file mode 100644 index 0000000000..b4cf7f9b27 --- /dev/null +++ b/tests/pyccel/scripts/exits/positive_exit3.py @@ -0,0 +1,7 @@ +# pylint: disable=missing-function-docstring, missing-module-docstring/ + +from sys import exit as sys_exit + +if __name__ == "__main__": + exit_code = 2147483647 + sys_exit(exit_code) diff --git a/tests/pyccel/scripts/exits/zero_exit.py b/tests/pyccel/scripts/exits/zero_exit.py new file mode 100644 index 0000000000..d8984534c2 --- /dev/null +++ b/tests/pyccel/scripts/exits/zero_exit.py @@ -0,0 +1,6 @@ +# pylint: disable=missing-function-docstring, missing-module-docstring/ + +import sys + +if __name__ == "__main__": + sys.exit(0) diff --git a/tests/pyccel/scripts/numpy/numpy_sign.py b/tests/pyccel/scripts/numpy/numpy_sign.py new file mode 100644 index 0000000000..c17c806602 --- /dev/null +++ b/tests/pyccel/scripts/numpy/numpy_sign.py @@ -0,0 +1,84 @@ +# pylint: disable=missing-function-docstring, missing-module-docstring/ +import numpy as np + +if __name__ == "__main__": + print(np.sign(0)) + print(np.sign(-0)) + print(np.sign(np.int8(0))) + print(np.sign(np.int8(-0))) + print(np.sign(np.int16(0))) + print(np.sign(np.int16(-0))) + print(np.sign(np.int32(0))) + print(np.sign(np.int32(-0))) + print(np.sign(np.int64(0))) + print(np.sign(np.int64(-0))) + print(np.sign(42)) + print(np.sign(-42)) + print(np.sign(np.int8(42))) + print(np.sign(np.int8(-42))) + print(np.sign(np.int16(42))) + print(np.sign(np.int16(-42))) + print(np.sign(np.int32(42))) + print(np.sign(np.int32(-42))) + print(np.sign(np.int64(42))) + print(np.sign(np.int64(-42))) + print(np.sign(0.0)) + print(np.sign(-0.0)) + print(np.sign(np.float32(0.0))) + print(np.sign(np.float32(-0.0))) + print(np.sign(np.float64(0.0))) + print(np.sign(np.float64(-0.0))) + print(np.sign(4.2)) + print(np.sign(-4.2)) + print(np.sign(np.float32(4.2))) + print(np.sign(np.float32(-4.2))) + print(np.sign(np.float64(4.2))) + print(np.sign(np.float64(-4.2))) + print(np.sign(0-0j)) + print(np.sign(-0-0j)) + print(np.sign(np.complex64(0-0j))) + print(np.sign(np.complex64(-0-0j))) + print(np.sign(np.complex128(0-0j))) + print(np.sign(np.complex128(-0-0j))) + print(np.sign(0+0j)) + print(np.sign(-0+0j)) + print(np.sign(np.complex64(0+0j))) + print(np.sign(np.complex64(-0+0j))) + print(np.sign(np.complex128(0+0j))) + print(np.sign(np.complex128(-0+0j))) + print(np.sign(4+2j)) + print(np.sign(-4+2j)) + print(np.sign(np.complex64(4+2j))) + print(np.sign(np.complex64(-4+2j))) + print(np.sign(np.complex128(4+2j))) + print(np.sign(np.complex128(-4+2j))) + print(np.sign(0+2j)) + print(np.sign(-0+2j)) + print(np.sign(np.complex64(0+2j))) + print(np.sign(np.complex64(-0+2j))) + print(np.sign(np.complex128(0+2j))) + print(np.sign(np.complex128(-0+2j))) + print(np.sign(4+0j)) + print(np.sign(-4+0j)) + print(np.sign(np.complex64(4+0j))) + print(np.sign(np.complex64(-4+0j))) + print(np.sign(np.complex128(4+0j))) + print(np.sign(np.complex128(-4+0j))) + print(np.sign(4-2j)) + print(np.sign(-4-2j)) + print(np.sign(np.complex64(4-2j))) + print(np.sign(np.complex64(-4-2j))) + print(np.sign(np.complex128(4-2j))) + print(np.sign(np.complex128(-4-2j))) + print(np.sign(0-2j)) + print(np.sign(-0-2j)) + print(np.sign(np.complex64(0-2j))) + print(np.sign(np.complex64(-0-2j))) + print(np.sign(np.complex128(0-2j))) + print(np.sign(np.complex128(-0-2j))) + print(np.sign(4-0j)) + print(np.sign(-4-0j)) + print(np.sign(np.complex64(4-0j))) + print(np.sign(np.complex64(-4-0j))) + print(np.sign(np.complex128(4-0j))) + print(np.sign(np.complex128(-4-0j))) diff --git a/tests/pyccel/scripts/print_integers.py b/tests/pyccel/scripts/print_integers.py new file mode 100644 index 0000000000..bad1f54557 --- /dev/null +++ b/tests/pyccel/scripts/print_integers.py @@ -0,0 +1,37 @@ +# pylint: disable=missing-function-docstring, missing-module-docstring/ +# ------------------------------- Strings ------------------------------------ + +from numpy import int32, int64, int16, int8 +if __name__ == '__main__': + print(0) + print(00) + print(1) + print(-1) + print(-0) + print(10000) + print(-10000) + print(2147483647) + + print(int64(2147483648)) + print(int64(9223372036854775807)) + + print(int32(0)) + print(int32(00)) + print(int32(1)) + print(int32(-1)) + print(int32(-0)) + print(int32(10000)) + print(int32(-10000)) + print(int32(2147483647)) + + print(int16(0)) + print(int16(-10)) + print(int16(1)) + print(int16(32767)) + print(int16(-32768)) + + print(int8(0)) + print(int8(-10)) + print(int8(1)) + print(int8(127)) + print(int8(-128)) diff --git a/tests/pyccel/scripts/print_tuples.py b/tests/pyccel/scripts/print_tuples.py new file mode 100644 index 0000000000..31cb6a453d --- /dev/null +++ b/tests/pyccel/scripts/print_tuples.py @@ -0,0 +1,12 @@ +# pylint: disable=missing-function-docstring, missing-module-docstring/ +# ------------------------------- Strings ------------------------------------ +if __name__ == '__main__': + print(()) + print((1,)) + print((1,2,3)) + print(((1,2),3)) + print(((1,2),(3,))) + print((((1,),2),(3,))) + print((1, True)) + print((1, True), sep=",") + print((1, True), end="!\n") diff --git a/tests/pyccel/scripts/runtest_type_print.py b/tests/pyccel/scripts/runtest_type_print.py index 121b3aa1fb..e9ef5fe840 100644 --- a/tests/pyccel/scripts/runtest_type_print.py +++ b/tests/pyccel/scripts/runtest_type_print.py @@ -2,11 +2,13 @@ import numpy as np if __name__ == '__main__': + print(type(int(3))) print(type(np.int16(3))) print(type(np.int32(3))) print(type(np.int64(3))) + print(type(float(3))) print(type(np.float32(3))) print(type(np.float64(3))) - print(type(np.complex(3))) + print(type(complex(3))) print(type(np.complex64(3))) print(type(np.complex128(3))) diff --git a/tests/pyccel/test_pyccel.py b/tests/pyccel/test_pyccel.py index a5134b8f6d..1d09d47a6b 100644 --- a/tests/pyccel/test_pyccel.py +++ b/tests/pyccel/test_pyccel.py @@ -634,6 +634,16 @@ def test_print_strings(language): types = str pyccel_test("scripts/print_strings.py", language=language, output_dtype=types) +#------------------------------------------------------------------------------ +def test_print_integers(language): + types = str + pyccel_test("scripts/print_integers.py", language=language, output_dtype=types) + +#------------------------------------------------------------------------------ +def test_print_tuples(language): + types = str + pyccel_test("scripts/print_tuples.py", language=language, output_dtype=types) + #------------------------------------------------------------------------------ def test_print_sp_and_end(language): types = str @@ -664,6 +674,19 @@ def test_return_numpy_arrays(language): types += [int]*4 # 4 ints for k pyccel_test("scripts/return_numpy_arrays.py", language=language, output_dtype=types) +#------------------------------------------------------------------------------ +def test_array_binary_op(language): + types = [int] * 4 + types += [int, float, int, int] + types += [int] * 4 + types += [int, float, int, int] + types += [int] * 4 + types += [int, float, int, int] + types += [int] * 4 + types += [int, float, int, int] + types += [int] * 8 + pyccel_test("scripts/array_binary_operation.py", language = language, output_dtype=types) + #------------------------------------------------------------------------------ @pytest.mark.parametrize( 'language', ( pytest.param("c", marks = pytest.mark.c), @@ -779,46 +802,8 @@ def test_lapack( test_file ): #------------------------------------------------------------------------------ def test_type_print( language ): - test_file = 'scripts/runtest_type_print.py' - - test_file = os.path.normpath(test_file) - - cwd = os.path.dirname(test_file) - cwd = get_abs_path(cwd) - - test_file = get_abs_path(test_file) - - pyccel_commands = "--language="+language - - if language=="python": - output_dir = get_abs_path('__pyccel__') - pyccel_commands += " --output=" + output_dir - output_test_file = os.path.join(output_dir, os.path.basename(test_file)) - else: - output_test_file = test_file - - compile_pyccel(cwd, test_file, pyccel_commands) - - lang_out = get_lang_output(output_test_file, language) - lang_out = lang_out.split('\n') - assert len(lang_out)>=5 - - if language=="python": - assert 'int16' in lang_out[0] - if sys.platform == "win32": - assert 'int' in lang_out[1] - assert 'int64' in lang_out[2] - else: - assert 'int32' in lang_out[1] - assert 'int' in lang_out[2] - assert 'float32' in lang_out[3] - assert 'float' in lang_out[4] - else: - assert 'int16' in lang_out[0] - assert 'int32' in lang_out[1] - assert 'int64' in lang_out[2] - assert 'float32' in lang_out[3] - assert 'float64' in lang_out[4] + pyccel_test("scripts/runtest_type_print.py", + language = language, output_dtype=str) @pytest.mark.parametrize( 'language', ( pytest.param("fortran", marks = pytest.mark.fortran), @@ -898,6 +883,35 @@ def test_assert(language, test_file): pyth_out = get_lang_exit_value(test_file, "python") assert (not lang_out and not pyth_out) or (lang_out and pyth_out) +#------------------------------------------------------------------------------ +@pytest.mark.parametrize( "test_file", ["scripts/exits/empty_exit.py", + "scripts/exits/negative_exit1.py", + "scripts/exits/negative_exit2.py", + "scripts/exits/positive_exit1.py", + "scripts/exits/positive_exit2.py", + "scripts/exits/positive_exit3.py", + "scripts/exits/zero_exit.py", + ] ) + +def test_exit(language, test_file): + test_dir = os.path.dirname(test_file) + test_file = get_abs_path(os.path.normpath(test_file)) + + output_dir = os.path.join(get_abs_path(test_dir),'__pyccel__') + output_test_file = os.path.join(output_dir, os.path.basename(test_file)) + + cwd = get_abs_path(test_dir) + + if not language: + language = "fortran" + pyccel_commands = " --language="+language + pyccel_commands += " --output="+ output_dir + + compile_pyccel(cwd, test_file, pyccel_commands) + lang_out = get_lang_exit_value(output_test_file, language) + pyth_out = get_lang_exit_value(test_file, "python") + assert lang_out == pyth_out + #------------------------------------------------------------------------------ @pytest.mark.parametrize( 'language', ( pytest.param("fortran", marks = pytest.mark.fortran), diff --git a/tutorial/builtin-functions.md b/tutorial/builtin-functions.md index 1f31e74a8f..ee0bb46f64 100644 --- a/tutorial/builtin-functions.md +++ b/tutorial/builtin-functions.md @@ -4,72 +4,72 @@ Python contains a limited number of builtin functions defined [here](https://doc | Function | Supported | |----------|-----------| -| **abs** | **Yes** | -| all | No | -| any | No | -| ascii | No | -| bin | No | -| **bool** | **Yes** | -| breakpoint | No | -| bytearray | No | -| bytes | No | -| callable | No | -| chr | No | -| classmethod | No | -| compile | No | -| **complex** | **Yes** | -| delattr | No | -| dict | No | -| dir | No | -| divmod | No | -| **enumerate** | **Yes** | -| eval | No | -| exec | No | -| filter | No | -| **float** | **Yes** | -| format | No | -| frozenset | No | -| getattr | No | -| globals | No | -| hasattr | No | -| hash | No | -| help | No | -| hex | No | -| id | No | -| input | No | -| **int** | **Yes** | -| isinstance | No | -| issubclass | No | -| iter | No | -| **len** | **Yes** | -| *list* | implemented as a tuple | -| locals | No | -| **map** | **Yes** | -| **max** | Fortran-only | -| memoryview | No | -| **min** | Fortran-only | -| next | No | -| object | No | -| oct | No | -| open | No | -| ord | No | -| pow | No | -| **print** | **Yes** | -| property | No | -| **range** | **Yes** | -| repr | No | -| reversed | No | -| round | No | -| set | No | -| setattr | No | -| slice | No | -| sorted | No | -| staticmethod | No | -| str | No | -| **sum** | Fortran-only | -| super | No | -| **tuple** | **Yes** | -| **type** | **Yes** | -| vars | No | -| **zip** | **Yes** | -| \_\_import\_\_ | No +| **`abs`** | **Yes** | +| `all` | No | +| `any` | No | +| `ascii` | No | +| `bin` | No | +| **`bool`** | **Yes** | +| `breakpoint` | No | +| `bytearray` | No | +| `bytes` | No | +| `callable` | No | +| `chr` | No | +| `classmethod` | No | +| `compile` | No | +| **`complex`** | **Yes** | +| `delattr` | No | +| `dict` | No | +| `dir` | No | +| `divmod` | No | +| **`enumerate`** | **Yes** | +| `eval` | No | +| `exec` | No | +| `filter` | No | +| **`float`** | **Yes** | +| `format` | No | +| `frozenset` | No | +| `getattr` | No | +| `globals` | No | +| `hasattr` | No | +| `hash` | No | +| `help` | No | +| `hex` | No | +| `id` | No | +| `input` | No | +| **`int`** | **Yes** | +| `isinstance` | No | +| `issubclass` | No | +| `iter` | No | +| **`len`** | **Yes** | +| *`list`* | implemented as a tuple | +| `locals` | No | +| **`map`** | **Yes** | +| **`max`** | Fortran-only | +| `memoryview` | No | +| **`min`** | Fortran-only | +| `next` | No | +| `object` | No | +| `oct` | No | +| `open` | No | +| `ord` | No | +| `pow` | No | +| **`print`** | **Yes** | +| `property` | No | +| **`range`** | **Yes** | +| `repr` | No | +| `reversed` | No | +| `round` | No | +| `set` | No | +| `setattr` | No | +| `slice` | No | +| `sorted` | No | +| `staticmethod` | No | +| `str` | No | +| **`sum`** | Fortran-only | +| `super` | No | +| **`tuple`** | **Yes** | +| **`type`** | **Yes** | +| `vars` | No | +| **`zip`** | **Yes** | +| \_\_`import`\_\_ | No diff --git a/tutorial/compiler.md b/tutorial/compiler.md index e61fb506fc..b12fa4ecfe 100644 --- a/tutorial/compiler.md +++ b/tutorial/compiler.md @@ -2,16 +2,16 @@ ## Compilers supported by Pyccel Pyccel provides default compiler settings for 4 different compiler families: -- **GNU** : gcc / gfortran -- **intel** : icc / ifort -- **PGI** : pgcc / pgfortran -- **nvidia** : nvc / nvfort +- **GNU** : `gcc` / `gfortran` +- **intel** : `icc` / `ifort` +- **PGI** : `pgcc` / `pgfortran` +- **nvidia** : `nvc` / `nvfort` **Warning** : The **GNU** compiler is currently the only compiler which is tested regularly ## Specifying a compiler -The default compiler family is **GNU**. To use a different compiler, the compiler family should be passed to either pyccel or epyccel. +The default compiler family is **GNU**. To use a different compiler, the compiler family should be passed to either `pyccel` or `epyccel`. E.g. ```shell pyccel example.py --compiler=intel @@ -23,30 +23,30 @@ epyccel(my_func, compiler='intel') ## User-defined compiler -The user can also define their own compiler in a json file. To use this definition, the location of the json file must be passed to the _compiler_ argument. The json file must define the following: - -- **exec** : The name of the executable -- **mpi\_exec** : The name of the mpi executable -- **language** : The language handled by this compiler -- **module\_output\_flag** : This flag is only required when the language is fortran. It specifies the flag which indicates where .mod files should be saved (e.g. '-J' for gfortran) -- **debug\_flags** : A list of flags used when compiling in debug mode \[optional\] -- **release\_flags** : A list of flags used when compiling in release mode \[optional\] -- **general\_flags** : A list of flags used when compiling in any mode \[optional\] -- **standard\_flags** : A list of flags used to impose the expected language standard \[optional\] -- **libs** : A list of libraries necessary for compiling \[optional\] -- **libdirs** : A list of library directories necessary for compiling \[optional\] -- **includes** : A list of include directories necessary for compiling \[optional\] +The user can also define their own compiler in a JSON file. To use this definition, the location of the JSON file must be passed to the _compiler_ argument. The JSON file must define the following: + +- `exec` : The name of the executable +- `mpi_exec` : The name of the MPI executable +- `language` : The language handled by this compiler +- `module_output_flag` : This flag is only required when the language is Fortran. It specifies the flag which indicates where .mod files should be saved (e.g. '-J' for `gfortran`) +- `debug_flags` : A list of flags used when compiling in debug mode \[optional\] +- `release_flags` : A list of flags used when compiling in release mode \[optional\] +- `general_flags` : A list of flags used when compiling in any mode \[optional\] +- `standard_flags` : A list of flags used to impose the expected language standard \[optional\] +- `libs` : A list of libraries necessary for compiling \[optional\] +- `libdirs` : A list of library directories necessary for compiling \[optional\] +- `includes` : A list of include directories necessary for compiling \[optional\] -In addition, for each accelerator (mpi/openmp/openacc/python) that you will use the json file must define the following: +In addition, for each accelerator (`mpi`/`openmp`/`openacc`/`python`) that you will use the JSON file must define the following: -- **flags** : A list of flags used to impose the expected language standard \[optional\] -- **libs** : A list of libraries necessary for compiling \[optional\] -- **libdirs** : A list of library directories necessary for compiling \[optional\] -- **includes** : A list of include directories necessary for compiling \[optional\] +- `flags` : A list of flags used to impose the expected language standard \[optional\] +- `libs` : A list of libraries necessary for compiling \[optional\] +- `libdirs` : A list of library directories necessary for compiling \[optional\] +- `includes` : A list of include directories necessary for compiling \[optional\] Python is considered to be an accelerator and must additionally specify shared\_suffix. -The default compilers can provide examples compatible with your system once pyccel has been executed at least. To export the json file describing your setup, use the `--export-compile-info` flag and provide a target file name. +The default compilers can provide examples compatible with your system once Pyccel has been executed at least. To export the JSON file describing your setup, use the `--export-compile-info` flag and provide a target file name. E.g. ```shell pyccel --compiler=PGI --language=c --export-compile-info=icc.json diff --git a/tutorial/const_keyword.md b/tutorial/const_keyword.md index fa58ddad1d..5a17ab99c4 100644 --- a/tutorial/const_keyword.md +++ b/tutorial/const_keyword.md @@ -1,4 +1,4 @@ -# Const keyword +# `const` keyword In order to make sure that a function argument is not modified by the function call, Pyccel provides the `const` keyword, which is converted to an equivalent datatype qualifier in the target language. Here is a simple example of its usage: @@ -28,7 +28,7 @@ int64_t func1(t_ndarray arr) /*........................................*/ ``` -The fortran equivalent: +The Fortran equivalent: ```fortran module boo @@ -57,7 +57,7 @@ module boo end module boo ``` -Now we will see what happens if we try to modify a const array: +Now we will see what happens if we try to modify a constant array: ```Python def func1(arr: 'const int[:]', i: 'int', v: 'int', z:'int'): @@ -67,7 +67,7 @@ def func1(arr: 'const int[:]', i: 'int', v: 'int', z:'int'): return 0 ``` -Pyccel will recognize that a const array cannot be changed and will raise an error similar to: +Pyccel will recognise that a constant array cannot be changed and will raise an error similar to: ```sh ERROR at annotation (semantic) stage @@ -77,10 +77,10 @@ pyccel: ## Getting Help -If you face problems with pyccel, please take the following steps: +If you face problems with Pyccel, please take the following steps: -1. Consult our documention in the tutorial directory; +1. Consult our documentation in the tutorial directory; 2. Send an email message to pyccel@googlegroups.com; 3. Open an issue on GitHub. -Thank you! \ No newline at end of file +Thank you! diff --git a/tutorial/decorators.md b/tutorial/decorators.md index c6ee77171a..cf7edb3589 100644 --- a/tutorial/decorators.md +++ b/tutorial/decorators.md @@ -117,7 +117,7 @@ positive or negative. As a result an if block must be added. This implies a (pot cost. Non-literal negative indexes are not especially common, therefore Pyccel does not add this costly if block unless it is specifically requested. This can be done using the `allow_negative_index` decorator. -An example shows how Pyccel handles negative indexes beween Python and C: +An example shows how Pyccel handles negative indexes between Python and C: ```python from pyccel.decorators import allow_negative_index @@ -209,7 +209,7 @@ end module boo ## Elemental -In Python it is often the case that a function with scalar arguments and a single scalar output (if any) is also able to accept Numpy arrays with identical rank and shape - in such a case the scalar function is simply applied element-wise to the input arrays. In order to mimic this behavior in the generated C or Fortran code, Pyccel provides the decorator `elemental`. +In Python it is often the case that a function with scalar arguments and a single scalar output (if any) is also able to accept NumPy arrays with identical rank and shape - in such a case the scalar function is simply applied element-wise to the input arrays. In order to mimic this behaviour in the generated C or Fortran code, Pyccel provides the decorator `elemental`. Important note: applying the `elemental` decorator to a function will not make a difference to the C translation of the function definition itself since C doesn't have the elementwise feature. However, Pyccel implements that functionality by calling the function in a `for` loop when an array argument is passed. In the following example, we will use the function `square` where `@elemental` will be useful: @@ -602,9 +602,9 @@ The generated C code: ## Getting Help -If you face problems with pyccel, please take the following steps: +If you face problems with Pyccel, please take the following steps: -1. Consult our documention in the tutorial directory; +1. Consult our documentation in the tutorial directory; 2. Send an email message to pyccel@googlegroups.com; 3. Open an issue on GitHub. diff --git a/tutorial/function-pointers-as-arguments.md b/tutorial/function-pointers-as-arguments.md index aab0614257..4d269fde05 100644 --- a/tutorial/function-pointers-as-arguments.md +++ b/tutorial/function-pointers-as-arguments.md @@ -155,11 +155,11 @@ module boo end module boo ``` -## Pyccel Optimization Case +## Pyccel Optimisation Case -Now, we will see a special case that is optimized by Pyccel (not optimized in C yet): +Now, we will see a special case that is optimised by Pyccel (not optimised in C yet): -In this example, Pyccel will recognize that foo doesn't change `x`, so it will automatically add `const` or `intent(in)` (depending on the language: C/Fortran) to the data type of `x`. This provides useful information for C/Fortran compilers to make optimizations to the code: +In this example, Pyccel will recognise that foo doesn't change `x`, so it will automatically add `const` or `intent(in)` (depending on the language: C/Fortran) to the data type of `x`. This provides useful information for C/Fortran compilers to make optimisations to the code: ```python def foo(x: 'int[:]', i: 'int'): @@ -245,7 +245,7 @@ if __name__ == '__main__': func1(a, b, foo) ``` -After trying to pyccelize the Python code above, here are the generated codes: +After trying to pyccelise the Python code above, here are the generated codes: The generated code of the Fortran module: @@ -433,9 +433,9 @@ end program prog_prog_boo ## Getting Help -If you face problems with pyccel, please take the following steps: +If you face problems with Pyccel, please take the following steps: -1. Consult our documention in the tutorial directory; +1. Consult our documentation in the tutorial directory; 2. Send an email message to pyccel@googlegroups.com; 3. Open an issue on GitHub. diff --git a/tutorial/header-files.md b/tutorial/header-files.md index 09ff4f9140..4791131ebd 100644 --- a/tutorial/header-files.md +++ b/tutorial/header-files.md @@ -8,8 +8,8 @@ Header files serve two purposes: - Accelerate the parsing process of an imported Python module by parsing only its header file (automatically generated) instead of the full module. ### Examples -#### Link with openmp -We create the file `header.pyh` that contains an openmp function definition: +#### Link with OpenMP +We create the file `header.pyh` that contains an OpenMP function definition: ```python #$ header metavar module_name = 'omp_lib' @@ -59,8 +59,8 @@ end module funcs ``` We then create a static library using these commands: -- gfortran -c funcs.f90 -- ar rcs libfuncs.a funcs.o +- `gfortran -c funcs.f90` +- `ar rcs libfuncs.a funcs.o` In order to use this library the user needs to create a header file, we call it `funcs_headers.pyh` ```python @@ -69,7 +69,7 @@ In order to use this library the user needs to create a header file, we call it #$ header function fib(int) results(int) ``` -After that we can create a Python file `test_funcs.py`,where we can import the fortran functions and use them +After that we can create a Python file `test_funcs.py`,where we can import the Fortran functions and use them ```python from pyccel.decorators import types @@ -82,7 +82,7 @@ def print_fib(x): To compile this file we execute the following command `pyccel test_funcs.py --libs=funcs --libdir=$PWD`, this will create the shared library `test_funcs.so` ## Pickling header files -Parsing a large Pyccel header file with hundreds of function declarations may require a significant amount of time, therefore it is important that this process is only done once when pyccelizing multiple Python source files in a large project. +Parsing a large Pyccel header file with hundreds of function declarations may require a significant amount of time, therefore it is important that this process is only done once when pyccelising multiple Python source files in a large project. To this end, Pyccel uses the [pickle](https://docs.python.org/3/library/pickle.html) Python module to store the result of the parser to a `.pyccel` binary file, which is created in the same directory as the header file. Afterwards Pyccel will load the precompiled parser from the `.pyccel` file, instead of parsing the header file again. diff --git a/tutorial/ndarrays.md b/tutorial/ndarrays.md index 2a2a602b34..08f2dc15fa 100644 --- a/tutorial/ndarrays.md +++ b/tutorial/ndarrays.md @@ -9,7 +9,7 @@ Different ndarrays can share the same data, so that changes made in one ndarray ## Pyccel ndarrays ## -Pyccel uses the same implementation as Numpy ndarrays with some rules due to the difference between the host language (Python) "dynamically typed / internal garbage collector" and the target languages such as C and Fortran which are statically typed languages and don't have a garbage collector. +Pyccel uses the same implementation as NumPy ndarrays with some rules due to the difference between the host language (Python) "dynamically typed / internal garbage collector" and the target languages such as C and Fortran which are statically typed languages and don't have a garbage collector. Below we will show some rules that Pyccel has set to handles those differences. @@ -311,18 +311,18 @@ Some examples: end program prog_ex ``` -## Numpy [ndarray](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html) functions/properties progress in Pyccel ## +## NumPy [ndarray](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html) functions/properties progress in Pyccel ## - Supported [types](https://numpy.org/devdocs/user/basics.types.html): - bool, int, int8, int16, int32, int64, float, float32, float64, complex64 and complex128. They can be used as cast functions too. + `bool`, `int`, `int8`, `int16`, `int32`, `int64`, `float`, `float32`, `float64`, `complex`, `complex64` and `complex128`. They can be used as cast functions too. - Note: np.bool, np.int and np.float are just aliases to the Python native types, and are considered as a deprecated way to work with Python built-in types in NumPy. + Note: `np.bool`, `np.int`, `np.float` and `np.complex` are just aliases to the Python native types, and are considered as a deprecated way to work with Python built-in types in NumPy. - Properties: - - real, imag, shape, amax, amin + - `real`, `imag`, `shape`, `amax`, `amin` - Methods: - - sum \ No newline at end of file + - `sum` diff --git a/tutorial/numpy-functions.md b/tutorial/numpy-functions.md index 44d6916673..8899a88860 100644 --- a/tutorial/numpy-functions.md +++ b/tutorial/numpy-functions.md @@ -1,11 +1,12 @@ -# Supported Numpy function by Pyccel +# Supported NumPy function by Pyccel -In Pyccel we try to support the Numpy functions which developers use the most.. Here are some of them: +In Pyccel we try to support the NumPy functions which developers use the most.. Here are some of them: ## [norm](https://numpy.org/doc/stable/reference/generated/numpy.linalg.norm.html) - Supported parameters: + ```python x: array_like Input array. If axis is None, x must be 1-D or 2-D, unless ord is None. If both axis and ord are None, the 2-norm of x.ravel will be returned. @@ -15,10 +16,11 @@ In Pyccel we try to support the Numpy functions which developers use the most.. If axis is a 2-tuple, it specifies the axes that hold 2-D matrices, and the matrix norms of these matrices are computed. If axis is None then either a vector norm (when x is 1-D) or a matrix norm (when x is 2-D) is returned. The default is None. New in version 1.8.0. + ``` - Supported languages: Fortran (2-norm) -- python code: +- Python code: ```python from numpy.linalg import norm @@ -32,7 +34,7 @@ In Pyccel we try to support the Numpy functions which developers use the most.. print(nrm) ``` -- fortran equivalent: +- Fortran equivalent: ```fortran program prog_test_norm @@ -63,9 +65,9 @@ In Pyccel we try to support the Numpy functions which developers use the most.. ## [real](https://numpy.org/doc/stable/reference/generated/numpy.real.html) and [imag](https://numpy.org/doc/stable/reference/generated/numpy.imag.html) functions -- Supported languages: C, fortran +- Supported languages: C, Fortran -- python code: +- Python code: ```python from numpy import imag, real, array @@ -75,7 +77,7 @@ In Pyccel we try to support the Numpy functions which developers use the most.. print("real part for arr1: " , real_part, "\nimag part for arr1: ", imag_part) ``` -- fortran equivalent: +- Fortran equivalent: ```fortran program prog_test_imag_real @@ -150,7 +152,7 @@ In Pyccel we try to support the Numpy functions which developers use the most.. } ``` -- python code with arrays: +- Python code with arrays: ```python from numpy import imag, real, array @@ -160,7 +162,7 @@ In Pyccel we try to support the Numpy functions which developers use the most.. print("real part for arr1: " , real_part, "\nimag part for arr1: ", imag_part) ``` -- fortran equivalent: +- Fortran equivalent: ```fortran program prog_test_imag_real @@ -239,12 +241,14 @@ In Pyccel we try to support the Numpy functions which developers use the most.. - Supported parameters: + ```python a: array_like, Input data. + ``` -- Supported languages: fortran +- Supported languages: Fortran -- python code: +- Python code: ```python from numpy import array, prod @@ -254,7 +258,7 @@ In Pyccel we try to support the Numpy functions which developers use the most.. print("prd: ", prd) ``` -- fortran equivalent: +- Fortran equivalent: ```fortran program prog_test_prod @@ -278,16 +282,18 @@ In Pyccel we try to support the Numpy functions which developers use the most.. - Supported parameters: + ```python x1: array_like Dividend array. x2: array_like, Divisor array. If x1.shape != x2.shape, they must be broadcastable to a common shape (which becomes the shape of the output). + ``` -- Supported language: fortran. +- Supported language: Fortran. -- python code: +- Python code: ```python from numpy import array, mod @@ -297,7 +303,7 @@ In Pyccel we try to support the Numpy functions which developers use the most.. print("res: ", res) ``` -- fortran equivalent: +- Fortran equivalent: ```fortran program prog_test_mod @@ -322,12 +328,14 @@ In Pyccel we try to support the Numpy functions which developers use the most.. - Supported parameters: + ```python x1, x2: array_like, Input arrays (must be 1d or 2d), scalars not allowed. + ``` -- Supported languages: fortran (1d or 2d arrays only). +- Supported languages: Fortran (1d or 2d arrays only). -- python code: +- Python code: ```python from numpy import array, matmul @@ -337,7 +345,7 @@ In Pyccel we try to support the Numpy functions which developers use the most.. print("res: ", res) ``` -- fortran equivalent: +- Fortran equivalent: ```fortran program prog_test_matmul @@ -361,10 +369,11 @@ In Pyccel we try to support the Numpy functions which developers use the most.. ## [linspace](https://numpy.org/doc/stable/reference/generated/numpy.linspace.html) -- Supported languages: C, fortran +- Supported languages: C, Fortran - Supported parameters: + ```python start, stop: array_like, num: int, optional (Default is 50) @@ -372,8 +381,9 @@ In Pyccel we try to support the Numpy functions which developers use the most.. endpoint: bool, optional (Default is True) dtype: dtype, optional + ``` -- python code: +- Python code: ```python from numpy import linspace @@ -383,7 +393,7 @@ In Pyccel we try to support the Numpy functions which developers use the most.. print(x) ``` -- fortran equivalent: +- Fortran equivalent: ```fortran program prog_prog_test @@ -441,13 +451,15 @@ In Pyccel we try to support the Numpy functions which developers use the most.. ## [Transpose](https://numpy.org/doc/stable/reference/generated/numpy.transpose.html) -- Supported languages: C, fortran +- Supported languages: C, Fortran - Supported parameters: + ```python a: array_like, + ``` -- python code: +- Python code: ```python from numpy import transpose @@ -457,7 +469,7 @@ In Pyccel we try to support the Numpy functions which developers use the most.. print(y.T) ``` -- fortran equivalent: +- Fortran equivalent: ```fortran program prog_prog_tmp @@ -545,19 +557,19 @@ In Pyccel we try to support the Numpy functions which developers use the most.. - Supported [math functions](https://numpy.org/doc/stable/reference/routines.math.html) (optional parameters are not supported): - sqrt, abs, sin, cos, exp, log, tan, arcsin, arccos, arctan, arctan2, sinh, cosh, tanh, arcsinh, arccosh and - arctanh. + `sqrt`, `abs`, `sin`, `cos`, `exp`, `log`, `tan`, `arcsin`, `arccos`, `arctan`, `arctan2`, `sinh`, `cosh`, `tanh`, `arcsinh`, `arccosh` and + `arctanh`. - Supported [array creation routines](https://numpy.org/doc/stable/reference/routines.array-creation.html) (fully supported): - - empty, full, ones, zeros, arange (`like` parameter is not supported). - - empty_like, full_like, and zeros_like, ones_like (`subok` parameter is not supported). - - rand, randint - - where, count_nonzero (fortran only) - - nonzero (fortran only, 1D only) + - `empty`, `full`, `ones`, `zeros`, `arange` (`like` parameter is not supported). + - `empty_like`, `full_like`, `zeros_like`, and `ones_like` (`subok` parameter is not supported). + - `rand`, `randint` + - `where`, `count_nonzero` (Fortran only) + - `nonzero` (Fortran only, 1D only) - others: - - amax, amin, sum, shape, size, floor + - `amax`, `amin`, `sum`, `shape`, `size`, `floor`, `sign` -If discrepancies beyond round-off error are found between [Numpy](https://numpy.org/doc/stable/reference/)'s and [Pyccel](https://github.com/pyccel/pyccel)'s results, please create an issue at and provide a small example of your problem. Do not forget to specify your target language. +If discrepancies beyond round-off error are found between [NumPy](https://numpy.org/doc/stable/reference/)'s and [Pyccel](https://github.com/pyccel/pyccel)'s results, please create an issue at and provide a small example of your problem. Do not forget to specify your target language. diff --git a/tutorial/openmp.md b/tutorial/openmp.md index c7ebc1f8a5..3a55e21633 100644 --- a/tutorial/openmp.md +++ b/tutorial/openmp.md @@ -2,9 +2,9 @@ ## Using the Runtime Library Routines -OpenMP Runtime Library Routines for Pyccel work by importing the OpenMP routine needed from the Pyccel stdlib: +OpenMP Runtime Library Routines for Pyccel work by importing the OpenMP routine needed from the `pyccel.stdlib`: -Please note that files using the OpenMP Runtime library routines will only work when compiled with pyccel (i.e. they won't work in pure python mode). +Please note that files using the OpenMP Runtime library routines will only work when compiled with Pyccel (i.e. they won't work in pure python mode). ```python from pyccel.stdlib.internal.openmp import omp_set_num_threads @@ -65,9 +65,9 @@ Other references: [*OpenMP Clauses*](https://docs.microsoft.com/en-us/cpp/parallel/openmp/reference/openmp-clauses) -### parallel Construct +### `parallel` Construct -#### Syntax of *parallel* +#### Syntax of `parallel` ```python #$ omp parallel [clause[ [,] clause] ... ] @@ -96,9 +96,9 @@ hello from thread: 0 hello from thread: 1 ``` -### loop Construct +### `loop` Construct -#### Syntax of *loop* +#### Syntax of `loop` ```python #$ omp for [nowait] [clause[ [,] clause] ... ] @@ -127,9 +127,9 @@ The output of this program is: 893116 ``` -### single Construct +### `single` Construct -#### Syntax of *single* +#### Syntax of `single` ```python #$ omp single [nowait] [clause[ [,] clause] ... ] @@ -163,9 +163,9 @@ hello from thread number: 3 hello from thread number: 0 ``` -### critical Construct +### `critical` Construct -#### Syntax of *critical* +#### Syntax of `critical` ```python #$ omp critical [(name) [ [,] hint (hint-expression)]] @@ -195,9 +195,9 @@ The output of this program is: 893116 ``` -### barrier Construct +### `barrier` Construct -#### Syntax of *barrier* +#### Syntax of `barrier` ```python #$ omp barrier @@ -233,9 +233,9 @@ The output of this program is: 1786232 ``` -### masked Construct +### `masked` Construct -#### Syntax of *masked* +#### Syntax of `masked` ```python #$ omp masked [ filter(integer-expression) ] @@ -263,16 +263,16 @@ The output of this program is: result : 1 ``` -### taskloop/atomic Construct +### `taskloop`/`atomic` Construct -#### Syntax of *taskloop* +#### Syntax of `taskloop` ```python #$ omp taskloop [clause[ [,]clause] ... ] for-loops ``` -#### Syntax of *atomic* +#### Syntax of `atomic` ```python #$ omp atomic [clause[ [,]clause] ... ] @@ -315,9 +315,9 @@ x1 : 200 x2 : 100 ``` -### simd Construct +### `simd` Construct -#### Syntax of *simd* +#### Syntax of `simd` ```python #$ omp simd [clause[ [,]clause] ... ] @@ -349,9 +349,9 @@ The output of this program is: Result: 893116 ``` -### task / taskwait Construct +### `task` / `taskwait` Construct -#### Syntax of *task* +#### Syntax of `task` ```python #$ omp task [clause[ [,]clause] ... ] @@ -359,7 +359,7 @@ structured-block #$ omp end task ``` -#### Syntax *taskwait* +#### Syntax `taskwait` ```python #$ omp taskwait @@ -368,7 +368,7 @@ structured-block #### Example The ``` #$ omp task ``` pragma is used here to define an explicit task.\ -The ``` #$ omp taskwait ``` pragma is used here to specify that the current task region remains suspended until all child tasks that it generated before the taskwait construct complete execution. +The ``` #$ omp taskwait ``` pragma is used here to specify that the current task region remains suspended until all child tasks that it generated before the `taskwait` construct complete execution. ```python @types('int', results='int') def fib(n): @@ -397,9 +397,9 @@ The output of this program is: 55 ``` -### taskyield Construct +### `taskyield` Construct -#### Syntax of *taskyield* +#### Syntax of `taskyield` ```python #$ omp taskyield @@ -407,7 +407,7 @@ The output of this program is: #### Example -The ``` #$ omp taskyield ``` pragma specifies that the current task can be suspended at this point, in favor of execution of a different task. +The ``` #$ omp taskyield ``` pragma specifies that the current task can be suspended at this point, in favour of execution of a different task. ```python #$ omp task @@ -417,9 +417,9 @@ long_function_2() #$ omp end task ``` -### flush Construct +### `flush` Construct -#### Syntax of *flush* +#### Syntax of `flush` ```python #$ omp flush @@ -454,9 +454,9 @@ Thread 1 released flag: 2 ``` -### cancel Construct +### `cancel` Construct -#### Syntax of *cancel* +#### Syntax of `cancel` ```python #$ omp cancel construct-type-clause[ [ , ] if-clause] @@ -479,9 +479,9 @@ for i in range(len(v)): #$ omp end parallel ``` -### teams/target/distribute Constructs +### `teams`/`target`/`distribute` Constructs -#### Syntax *target* +#### Syntax of `target` ```python #$ omp target [clause[ [,]clause] ... ] @@ -489,7 +489,7 @@ structured-block #$ omp end target ``` -#### Syntax of *teams* +#### Syntax of `teams` ```python #$ omp teams [clause[ [,]clause] ... ] @@ -541,9 +541,9 @@ Team num : 1 Team num : 1 ``` -### sections Construct +### `sections` Construct -#### Syntax of *sections* +#### Syntax of `sections` ```python #$ omp sections [nowait] [clause[ [,]clause] ... ] @@ -605,9 +605,9 @@ sum3 : 28, thread : 1 ## Combined Constructs Usage on Pyccel -### parallel for +### `parallel for` -#### Syntax of *parallel for* +#### Syntax of `parallel for` ```python #$ omp parallel for [clause[ [,]clause] ... ] @@ -616,7 +616,7 @@ loop-nest #### Example -The ```#$ omp parallel for``` construct specifies a parallel construct containing a worksharingloop construct with a canonical loop nest. +The ```#$ omp parallel for``` construct specifies a parallel construct containing a work sharing loop construct with a canonical loop nest. ```python import numpy as np @@ -635,9 +635,9 @@ The output of this program is : result: 28 ``` -### parallel for simd +### `parallel for simd` -#### Syntax of *parallel for simd* +#### Syntax of `parallel for simd` ```python #$ omp parallel for simd [clause[ [,]clause] ... ] @@ -646,7 +646,7 @@ loop-nest #### Example -The ```#$ omp parallel for simd``` construct specifies a parallel construct containing only one worksharing-loop SIMD construct. +The ```#$ omp parallel for simd``` construct specifies a parallel construct containing only one work sharing loop SIMD construct. ```python import numpy as np @@ -675,9 +675,9 @@ z[ 5 ] : 3 z[ 6 ] : 3 z[ 7 ] : 3 ``` -### for simd +### `for simd` -#### Syntax of *for simd* +#### Syntax of `for simd` ```python @@ -685,9 +685,9 @@ z[ 7 ] : 3 for-loops ``` -### teams distribute +### `teams distribute` -#### Syntax of *teams distribute* +#### Syntax of `teams distribute` ```python #$ omp teams distribute [clause[ [,]clause] ... ] @@ -727,27 +727,27 @@ z[ 7 ] : 3 ``` -### teams distribute simd +### `teams distribute simd` -#### Syntax of *teams distribut simd* +#### Syntax of `teams distribute simd` ```python #$ omp teams distribute simd [clause[ [,]clause] ... ] loop-nest ``` -### teams distribute parallel for +### `teams distribute parallel for` -#### Syntax of *teams distribute parallel for* +#### Syntax of `teams distribute parallel for` ```python #$ omp teams distribute parallel for [clause[ [,]clause] ... ] loop-nest ``` -### target parallel +### `target parallel` -#### Syntax of *target parallel* +#### Syntax of `target parallel` ```python #$ omp target parallel [clause[ [,]clause] ... ] @@ -755,27 +755,27 @@ structured-block #$ omp end target parallel ``` -### target parallel for +### `target parallel for` -#### Syntax of *target parallel for* +#### Syntax of `target parallel for` ```python #$ omp target parallel for [clause[ [,]clause] ... ] loop-nest ``` -### target parallel for simd +### `target parallel for simd` -#### Syntax of *target parallel for simd* +#### Syntax of `target parallel for simd` ```python #$ omp target parallel for simd [clause[ [,]clause] ... ] loop-nest ``` -### target teams +### `target teams` -#### Syntax of *target teams* +#### Syntax of `target teams` ```python #$ omp target teams [clause[ [,]clause] ... ] @@ -783,36 +783,36 @@ structured-block #$ omp end target teams ``` -### target teams distribute +### `target teams distribute` -#### Syntax of *target teams distribute* +#### Syntax of `target teams distribute` ```python #$ omp target teams distribute [clause[ [,]clause] ... ] loop-nest ``` -### target teams distribute simd +### `target teams distribute simd` -#### Syntax of *target teams distribute simd* +#### Syntax of `target teams distribute simd` ```python #$ omp target teams distribute simd [clause[ [,]clause] ... ] loop-nest ``` -### target teams distribute parallel for +### `target teams distribute parallel for` -#### Syntax of *target teams distribute parallel for* +#### Syntax of `target teams distribute parallel for` ```python #$ omp target teams distribute parallel for [clause[ [,]clause] ... ] loop-nest ``` -### target teams distribute parallel for simd +### `target teams distribute parallel for simd` -#### Syntax of *target teams distribute parallel for simd* +#### Syntax of `target teams distribute parallel for simd` ```python #$ omp target teams distribute parallel for simd [clause[ [,]clause] ... ] @@ -821,7 +821,7 @@ loop-nest #### Example -The ```#$ omp parallel for simd``` construct specifies a parallel construct containing only one worksharing-loop SIMD construct. +The ```#$ omp parallel for simd``` construct specifies a parallel construct containing only one work sharing loop SIMD construct. ```python r = 0 @@ -842,7 +842,7 @@ result: 49995000 ## Supported Constructs All constructs in the OpenMP 5.1 standard are supported except: -- scope -- workshare -- scan -- interop +- `scope` +- `workshare` +- `scan` +- `interop` diff --git a/tutorial/quickstart.md b/tutorial/quickstart.md index b329c45650..603b2027e6 100644 --- a/tutorial/quickstart.md +++ b/tutorial/quickstart.md @@ -4,10 +4,10 @@ Pyccel is a **static compiler** for Python 3, using Fortran or C as a backend la Pyccel's main goal is to resolve the principal bottleneck in scientific computing: the transition from **prototype** to **production**. Programmers usually develop their prototype code in a user-friendly interactive language like Python, but their final application requires an HPC implementation and therefore a new production code. -In most cases this is written in a statically compiled language like Fortran/C/C++, and it uses SIMD vectorization, parallel multi-threading, MPI parallelization, GPU offloading, etc. +In most cases this is written in a statically compiled language like Fortran/C/C++, and it uses SIMD vectorisation, parallel multi-threading, MPI parallelisation, GPU offloading, etc. We believe that this expensive process can be avoided, or at least drastically reduced, by using Pyccel to accelerate the most computationally intensive parts of the Python prototype. -Not only is the Pyccel-generated Fortran or C code very fast, but it is **human-readable**; hence the expert programmer can easily profile the code on the target machine and further optimize it. +Not only is the Pyccel-generated Fortran or C code very fast, but it is **human-readable**; hence the expert programmer can easily profile the code on the target machine and further optimise it. ## Some Useful Background @@ -96,7 +96,7 @@ We recommend using Python-style annotations, which have the syntax: ```python def fun(arg1: 'type1', arg2: 'type2', ..., argN: 'typeN') -> 'return_type': ``` -or to declare Numpy arrays +or to declare NumPy arrays ```python def fun(arg1: 'type1[:]', arg2: 'type2[:,:]', ..., argN: 'typeN[dimensions]') -> 'return_type': ``` @@ -106,10 +106,10 @@ In general string type hints must be used to provide Pyccel with information abo For scalar variables and arrays Pyccel supports the following data types: -- built-in datatypes: `bool`, `int`, `float`, `complex` -- Numpy integer types: `int8`, `int16`, `int32`, `int64` -- Numpy real types: `float32`, `float64`, `double` -- Numpy complex types: `complex64`, `complex128` +- built-in data types: `bool`, `int`, `float`, `complex` +- NumPy integer types: `int8`, `int16`, `int32`, `int64` +- NumPy real types: `float32`, `float64`, `double` +- NumPy complex types: `complex64`, `complex128` ## How to use Pyccel @@ -121,7 +121,7 @@ Detailed installation instructions are found in the [README](https://github.com/ ### Command Line Usage -After installation, the `pyccel` command will be available on a terminal app (iterm or terminal for MacOs, terminal for Linux). +After installation, the `pyccel` command will be available on a terminal app (iterm or terminal for macOS, terminal for Linux). After typing `pyccel`, the usage should be displayed on the terminal; if this is the case then the installation has succeeded. In essence the `pyccel` command translates the given Python file to a Fortran or C file, and then compiles the generated code to a Python C extension module or a simple executable. @@ -185,7 +185,7 @@ $ pyccel mod.py --language c By default Pyccel also compiles the C code into a Python C extension module named `mod..so`, which is placed in the same directory as `mod.py`. To achieve this Pyccel generates the additional files `mod_wrapper.c` (which interacts directly with the CPython API) and `setup_mod.py` (which defines the build procedure for the extension module), as well as a `build` directory. -If the command `import mod` is now given to the Python interpreter, this will import the Python C extention module `mod..so` instead of the pure Python module `mod.py`. +If the command `import mod` is now given to the Python interpreter, this will import the Python C extension module `mod..so` instead of the pure Python module `mod.py`. These are the contents of the current directory: ```bash @@ -264,7 +264,7 @@ int64_t binomial_coefficient(int64_t n, int64_t k); Let's now see a more complicated example, where the Python module `mod.py` contains a function that performs the matrix-matrix multiplication between two arrays `a` and `b`, and writes the result into the array `c`: -- The three function's arguments are 2D Numpy arrays of double-precision floating point numbers +- The three function's arguments are 2D NumPy arrays of double-precision floating point numbers - Matrices `a` and `c` use C ordering (row-major), matrix `b` uses Fortran ordering (column-major) - Since matrix `c` is modified by the function, it has `intent(inout)` in Fortran - Comments starting with `#$ omp` are translated to OpenMP pragmas @@ -380,7 +380,7 @@ end module mod ### Interactive Usage with `epyccel` -In addition to the `pyccel` command, the Pyccel library provides the `epyccel` Python function, whose name stands for "embedded Pyccel": given a pure Python function `f` with type annotations, `epyccel` returns a "pyccelized" function `f_fast` that can be used in the same Python session. +In addition to the `pyccel` command, the Pyccel library provides the `epyccel` Python function, whose name stands for "embedded Pyccel": given a pure Python function `f` with type annotations, `epyccel` returns a "pyccelised" function `f_fast` that can be used in the same Python session. For example: ```python from pyccel.epyccel import epyccel @@ -390,7 +390,7 @@ f_fast = epyccel(f) ``` In practice `epyccel` copies the contents of `f` into a temporary python file in the `__epyccel__` directory. As a result it is important that all imports are written inside the function when using `epyccel`. -Once the file has been copied, `epyccel` calls the `pyccel` command to generate a Python C extension module that contains a single pyccelized function. +Once the file has been copied, `epyccel` calls the `pyccel` command to generate a Python C extension module that contains a single pyccelised function. Then finally, it imports this function and returns it to the caller. #### Example 4: quicksort algorithm @@ -418,8 +418,8 @@ def quicksort(a: 'float[:]', lo: int, hi: int): lo = i j = hi ``` -We now import this function from an interactive IPython terminal and pyccelize it with the `epyccel` command. -We then use the two functions (original and pyccelized) to sort a random array of 100 elements. +We now import this function from an interactive IPython terminal and pyccelise it with the `epyccel` command. +We then use the two functions (original and pyccelised) to sort a random array of 100 elements. Finally we compare the timings obtained on an Intel Core 3 architecture. ```bash In [1]: from numpy.random import random @@ -441,7 +441,7 @@ In [8]: %timeit y = x.copy(); quicksort_fast(y, 0, 99) In [9]: (280 - 0.435) / (1.76 - 0.435) Out[9]: 210.99245283018868 ``` -After subtracting the amount of time required to create an array copy from the given times, we can conclude that the pyccelized function is approximately 210 times faster than the original Python function. +After subtracting the amount of time required to create an array copy from the given times, we can conclude that the pyccelised function is approximately 210 times faster than the original Python function. ## Other Features @@ -453,9 +453,9 @@ In the future we plan to support GPU programming with [CUDA](https://en.wikipedi ## Getting Help -If you face problems with pyccel, please take the following steps: +If you face problems with Pyccel, please take the following steps: -1. Consult our documention in the tutorial directory; +1. Consult our documentation in the tutorial directory; 2. Send an email message to pyccel@googlegroups.com; 3. Open an issue on GitHub. diff --git a/tutorial/templates.md b/tutorial/templates.md index b15887df88..386aa7d3f5 100644 --- a/tutorial/templates.md +++ b/tutorial/templates.md @@ -2,7 +2,7 @@ ## Template ### Templates using header comments -A **template** in pyccel, is used to allow the same function to take arguments of different types from a selection of types the user specifies. +A **template** in Pyccel, is used to allow the same function to take arguments of different types from a selection of types the user specifies. #### The usage In this example the argument **a**, could either be an integer or float, and the same for the argument **b**: ```python @@ -32,7 +32,7 @@ def f1(): pass pass ``` -In this example the arguments of **f2** can either be bool or complex, they can not be int or float: +In this example the arguments of **f2** can either be boolean or complex, they can not be integer or float: ```python #$ header template T(int|real) def f1(): @@ -53,7 +53,7 @@ def f(a,b): ``` Arguments: - name: the name of the template -- types: the types the tamplate represents. +- types: the types the template represents. --- *Note:* The arguments **name** and **types** could also be passed of the form @@ -63,9 +63,9 @@ The arguments **name** and **types** could also be passed of the form When a function is decorated with the template decorator: - The templates are only available to the decorated function. - The templates overrides any existing templates with the same name (declared as header comment). -- If the function is decorated with two templates with the same name, the first one gets overrided. +- If the function is decorated with two templates with the same name, the first one gets overridden. ##### Examples -In this example the arguments of **f** can either be bool or complex, they can not be int or float. +In this example the arguments of **f** can either be boolean or complex, they can not be integer or float. ```python from pyccel.decorators import types, template #$ header template T(int|real) From 6a11942a452a4808f245fbbe66eb4cddd7b3b4ce Mon Sep 17 00:00:00 2001 From: EmilyBourne Date: Tue, 14 Mar 2023 14:00:45 +0100 Subject: [PATCH 129/193] Fix commit --- pyccel/ast/numpyext.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/pyccel/ast/numpyext.py b/pyccel/ast/numpyext.py index 81efbc0bef..e4dc4d214a 100644 --- a/pyccel/ast/numpyext.py +++ b/pyccel/ast/numpyext.py @@ -369,13 +369,6 @@ def process_dtype(dtype): ------ TypeError: In the case of unrecognized argument type. TypeError: In the case of passed string argument not recognized as valid dtype. - - Returns: - ---------- - dtype: Datatype - The Datatype corresponding to the passed dtype. - precision: int - The precision corresponding to the passed dtype. """ if isinstance(dtype, PythonType): From e52035daa0114aee60289412c3b3f4e005f63ec3 Mon Sep 17 00:00:00 2001 From: EmilyBourne Date: Tue, 14 Mar 2023 14:34:33 +0100 Subject: [PATCH 130/193] pip_installation no longer exists --- .github/workflows/Github_pytest.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index f67217668a..d10b4802fd 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -118,8 +118,11 @@ jobs: uses: ./.github/actions/linux_install - name: Install python (setup-python action doesn't work with containers) uses: ./.github/actions/python_install - - name: Install python dependencies - uses: ./.github/actions/pip_installation + - name: Install Pyccel with tests + run: | + python -m pip install --upgrade pip + python -m pip install .[test] + shell: bash - name: Coverage install uses: ./.github/actions/coverage_install - name: Ccuda tests with pytest @@ -148,8 +151,11 @@ jobs: python-version: 3.7 - name: Install dependencies uses: ./.github/actions/linux_install - - name: Install python dependencies - uses: ./.github/actions/pip_installation + - name: Install Pyccel with tests + run: | + python -m pip install --upgrade pip + python -m pip install .[test] + shell: bash - name: Coverage install uses: ./.github/actions/coverage_install - name: Collect coverage information From 4f538e28f83177cbd467c3c40dffde077c30bf55 Mon Sep 17 00:00:00 2001 From: EmilyBourne Date: Tue, 14 Mar 2023 14:51:59 +0100 Subject: [PATCH 131/193] Add Code owners to protect merges --- .github/CODEOWNERS | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000..63440e5f3b --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,6 @@ +# These owners will be the default owners for everything in +# the repo. Unless a later match takes precedence, +# @global-owner1 and @global-owner2 will be requested for +# review when someone opens a pull request. +* @EmilyBourne @bauom + From 7dd51f37d9af661e39cb6effb79295a8c7b3088b Mon Sep 17 00:00:00 2001 From: EmilyBourne Date: Tue, 14 Mar 2023 15:36:44 +0100 Subject: [PATCH 132/193] Debug coverage problem --- .github/workflows/Github_pytest.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index d10b4802fd..b276a7a24a 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -175,6 +175,8 @@ jobs: - name: Generate coverage report run: | coverage combine + ls + coverage xml -i coverage xml - name: Run codacy-coverage-reporter uses: codacy/codacy-coverage-reporter-action@master From d1f61c54dd761d17784600c1e1b18be1fc362947 Mon Sep 17 00:00:00 2001 From: EmilyBourne Date: Tue, 14 Mar 2023 16:16:25 +0100 Subject: [PATCH 133/193] Remove debugging --- .github/workflows/Github_pytest.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index b276a7a24a..d10b4802fd 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -175,8 +175,6 @@ jobs: - name: Generate coverage report run: | coverage combine - ls - coverage xml -i coverage xml - name: Run codacy-coverage-reporter uses: codacy/codacy-coverage-reporter-action@master From b8e7b8c309649ba5ba626ee2ac5f020c3a4322d4 Mon Sep 17 00:00:00 2001 From: EmilyBourne Date: Wed, 15 Mar 2023 12:57:25 +0100 Subject: [PATCH 134/193] Trigger codacy From a7c3fc99d7669bb958d12963f95a314e668b87f5 Mon Sep 17 00:00:00 2001 From: EmilyBourne Date: Thu, 16 Mar 2023 11:25:49 +0100 Subject: [PATCH 135/193] Trigger codacy From 79c22938ac3f1bc0e94e3c9d734988156ce69a29 Mon Sep 17 00:00:00 2001 From: EmilyBourne Date: Fri, 17 Mar 2023 10:09:47 +0100 Subject: [PATCH 136/193] Correct folder --- .github/actions/pytest_run_cuda/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/pytest_run_cuda/action.yml b/.github/actions/pytest_run_cuda/action.yml index 59c8b5b916..52092a6e02 100644 --- a/.github/actions/pytest_run_cuda/action.yml +++ b/.github/actions/pytest_run_cuda/action.yml @@ -11,7 +11,7 @@ runs: - name: Ccuda tests with pytest run: | # Catch exit 5 (no tests found) - sh -c 'python -m pytest -n auto -rx -m "not (parallel or xdist_incompatible) and ccuda" --ignore=tests/symbolic --ignore=tests/ndarrays; ret=$?; [ $ret = 5 ] && exit 0 || exit $ret' + sh -c 'python -m pytest -n auto -rx -m "not (parallel or xdist_incompatible) and ccuda" --ignore=symbolic --ignore=ndarrays; ret=$?; [ $ret = 5 ] && exit 0 || exit $ret' pyccel-clean shell: ${{ inputs.shell_cmd }} - working-directory: ./ + working-directory: ./tests From 2dbf5f9bbd8a7bd0389cc1597d8b032094542372 Mon Sep 17 00:00:00 2001 From: EmilyBourne Date: Fri, 17 Mar 2023 10:10:17 +0100 Subject: [PATCH 137/193] Include cuda files in MANIFEST --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 656cdd153a..bf873bea31 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -6,6 +6,7 @@ include README.md recursive-include pyccel *.tx recursive-include pyccel *.pyh recursive-include pyccel *.c +recursive-include pyccel *.cu recursive-include pyccel *.f90 recursive-include pyccel *.h recursive-include pyccel *.pyccel From 92155b4ccef7d130833f730b997932286caeaa6f Mon Sep 17 00:00:00 2001 From: EmilyBourne Date: Fri, 17 Mar 2023 10:46:26 +0100 Subject: [PATCH 138/193] Update checkout version --- .github/workflows/Github_pytest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index d10b4802fd..9ad70e2a21 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -104,7 +104,7 @@ jobs: container: nvidia/cuda:11.7.1-devel-ubuntu20.04 if: github.event.pull_request.base.ref != 'master' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Prepare docker run: | apt update && apt install sudo From e83d00751d3d5e21f7e9b66f9ba928adff3ee6d0 Mon Sep 17 00:00:00 2001 From: EmilyBourne Date: Fri, 17 Mar 2023 15:54:32 +0100 Subject: [PATCH 139/193] Import coverage fixes --- .github/workflows/Github_pytest.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index 9ad70e2a21..e15e41581c 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -120,8 +120,11 @@ jobs: uses: ./.github/actions/python_install - name: Install Pyccel with tests run: | + PATH=${PATH}:$HOME/.local/bin + echo $PATH + echo "PATH=${PATH}" >> $GITHUB_ENV python -m pip install --upgrade pip - python -m pip install .[test] + python -m pip install --user .[test] shell: bash - name: Coverage install uses: ./.github/actions/coverage_install @@ -154,10 +157,9 @@ jobs: - name: Install Pyccel with tests run: | python -m pip install --upgrade pip - python -m pip install .[test] + python -m pip install . + python -m pip install coverage shell: bash - - name: Coverage install - uses: ./.github/actions/coverage_install - name: Collect coverage information uses: actions/download-artifact@v3 with: @@ -174,6 +176,8 @@ jobs: run: mv .coverage .coverage.cuda - name: Generate coverage report run: | + INSTALL_DIR=$(cd tests; python -c "import pyccel; print(pyccel.__path__[0])") + echo -e "[paths]\nsource =\n ${INSTALL_DIR}\n */site-packages/pyccel\n[xml]\noutput = cobertura.xml" > .coveragerc coverage combine coverage xml - name: Run codacy-coverage-reporter From 0c639276a4bf926f1295800e799ed7284158713d Mon Sep 17 00:00:00 2001 From: EmilyBourne Date: Fri, 17 Mar 2023 15:55:46 +0100 Subject: [PATCH 140/193] Remove unnecessary echo --- .github/workflows/Github_pytest.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index e15e41581c..45c46c9fb1 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -121,7 +121,6 @@ jobs: - name: Install Pyccel with tests run: | PATH=${PATH}:$HOME/.local/bin - echo $PATH echo "PATH=${PATH}" >> $GITHUB_ENV python -m pip install --upgrade pip python -m pip install --user .[test] From 8fc7fe0f90c05849b6e1dcd0b03f9df87bf8f0a7 Mon Sep 17 00:00:00 2001 From: EmilyBourne Date: Mon, 20 Mar 2023 09:49:52 +0100 Subject: [PATCH 141/193] Ensure cuda files are installed with pyccel (#1345) Include cuda files in the manifest to ensure that they are installed with pyccel. Fixes #1344 . This should help with the coverage as the python files that are run are the installed versions not the local versions. Use coverage's `[paths]` options to rename files tested on different systems. Fixes #1346 . Pull some updates from master regarding the installation and test setup --- .github/actions/pytest_run_cuda/action.yml | 4 +- .github/workflows/Github_pytest.yml | 44 +++++++++++++++------- MANIFEST.in | 1 + pyproject.toml | 5 ++- setup.cfg | 1 + 5 files changed, 37 insertions(+), 18 deletions(-) diff --git a/.github/actions/pytest_run_cuda/action.yml b/.github/actions/pytest_run_cuda/action.yml index 7ab1a8eabc..8f82dda10f 100644 --- a/.github/actions/pytest_run_cuda/action.yml +++ b/.github/actions/pytest_run_cuda/action.yml @@ -10,7 +10,7 @@ runs: steps: - name: Ccuda tests with pytest run: | - python -m pytest -n auto -rx -m "not (parallel or xdist_incompatible) and ccuda" --ignore=tests/symbolic --ignore=tests/ndarrays + python -m pytest -n auto -rx -m "not (parallel or xdist_incompatible) and ccuda" --ignore=symbolic --ignore=ndarrays pyccel-clean shell: ${{ inputs.shell_cmd }} - working-directory: ./ + working-directory: ./tests diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index 50280f1a56..49b69e1a2f 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -17,8 +17,11 @@ jobs: python-version: 3.7 - name: Install dependencies uses: ./.github/actions/linux_install - - name: Install python dependencies - uses: ./.github/actions/pip_installation + - name: Install Pyccel with tests + run: | + python -m pip install --upgrade pip + python -m pip install .[test] + shell: bash - name: Coverage install uses: ./.github/actions/coverage_install - name: Fortran/C tests with pytest @@ -59,8 +62,11 @@ jobs: # uses: msys2/setup-msys2@v2 - name: Install dependencies uses: ./.github/actions/windows_install - - name: Install python dependencies - uses: ./.github/actions/pip_installation + - name: Install Pyccel with tests + run: | + python -m pip install --upgrade pip + python -m pip install .[test] + shell: bash - name: Fortran/C tests with pytest uses: ./.github/actions/pytest_run - name: Python tests with pytest @@ -80,8 +86,11 @@ jobs: python-version: '3.10' - name: Install dependencies uses: ./.github/actions/macos_install - - name: Install python dependencies - uses: ./.github/actions/pip_installation + - name: Install Pyccel with tests + run: | + python -m pip install --upgrade pip + python -m pip install .[test] + shell: bash - name: Fortran/C tests with pytest uses: ./.github/actions/pytest_run - name: Python tests with pytest @@ -95,7 +104,7 @@ jobs: container: nvidia/cuda:11.7.1-devel-ubuntu20.04 if: github.event.pull_request.base.ref != 'master' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Prepare docker run: | apt update && apt install sudo @@ -107,8 +116,13 @@ jobs: run: nvcc --version # cuda install check - name: Install dependencies uses: ./.github/actions/linux_install - - name: Install python dependencies - uses: ./.github/actions/pip_installation + - name: Install Pyccel with tests + run: | + PATH=${PATH}:$HOME/.local/bin + echo "PATH=${PATH}" >> $GITHUB_ENV + python -m pip install --upgrade pip + python -m pip install --user .[test] + shell: bash - name: Coverage install uses: ./.github/actions/coverage_install - name: Ccuda tests with pytest @@ -137,10 +151,11 @@ jobs: python-version: 3.7 - name: Install dependencies uses: ./.github/actions/linux_install - - name: Install python dependencies - uses: ./.github/actions/pip_installation - - name: Coverage install - uses: ./.github/actions/coverage_install + - name: Install coverage + run: | + python -m pip install --upgrade pip + python -m pip install coverage + shell: bash - name: Collect coverage information uses: actions/download-artifact@v3 with: @@ -157,6 +172,7 @@ jobs: run: mv .coverage .coverage.cuda - name: Generate coverage report run: | + echo -e "[paths]\nsource =\n $(pwd)/pyccel\n */site-packages/pyccel\n[xml]\noutput = cobertura.xml" > .coveragerc coverage combine coverage xml - name: Run codacy-coverage-reporter @@ -184,7 +200,7 @@ jobs: uses: actions/setup-python@v4 with: python-version: 3.7 - - name: Install python dependencies + - name: Install Python dependencies run: | python -m pip install --upgrade pip python -m pip install defusedxml diff --git a/MANIFEST.in b/MANIFEST.in index 656cdd153a..bf873bea31 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -6,6 +6,7 @@ include README.md recursive-include pyccel *.tx recursive-include pyccel *.pyh recursive-include pyccel *.c +recursive-include pyccel *.cu recursive-include pyccel *.f90 recursive-include pyccel *.h recursive-include pyccel *.pyccel diff --git a/pyproject.toml b/pyproject.toml index 9f6ed0ea68..1f6c6657d8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,11 @@ [build-system] requires = [ "setuptools >= 37, < 61", + "wheel", "numpy >= 1.16", - "sympy>=1.2", + "sympy >= 1.2", "termcolor >= 1.0.0", - "textx>=2.2", + "textx >= 2.2", "filelock >= 3.4.0", ] build-backend = "setuptools.build_meta" diff --git a/setup.cfg b/setup.cfg index 1fbf13e34c..89c57c51e2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -27,6 +27,7 @@ test = pytest >= 2.7 scipy >= 1.5.0 mpi4py >= 3.0.0 + tblib >= 1.7.0 coverage >= 3.1 astunparse >= 1.6.0 pytest-xdist >= 1.16 From 6e066fe2cb9a5c98b04c423b3e003adac29141ad Mon Sep 17 00:00:00 2001 From: EmilyBourne Date: Mon, 20 Mar 2023 09:53:41 +0100 Subject: [PATCH 142/193] Make faster as done in cuda_main_temp --- .github/workflows/Github_pytest.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index 45c46c9fb1..dfe800f778 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -153,10 +153,9 @@ jobs: python-version: 3.7 - name: Install dependencies uses: ./.github/actions/linux_install - - name: Install Pyccel with tests + - name: Install coverage run: | python -m pip install --upgrade pip - python -m pip install . python -m pip install coverage shell: bash - name: Collect coverage information @@ -175,8 +174,7 @@ jobs: run: mv .coverage .coverage.cuda - name: Generate coverage report run: | - INSTALL_DIR=$(cd tests; python -c "import pyccel; print(pyccel.__path__[0])") - echo -e "[paths]\nsource =\n ${INSTALL_DIR}\n */site-packages/pyccel\n[xml]\noutput = cobertura.xml" > .coveragerc + echo -e "[paths]\nsource =\n $(pwd)/pyccel\n */site-packages/pyccel\n[xml]\noutput = cobertura.xml" > .coveragerc coverage combine coverage xml - name: Run codacy-coverage-reporter From 82d85772855f9aa103a3593b5bfcfb19ff416ded Mon Sep 17 00:00:00 2001 From: EmilyBourne Date: Mon, 20 Mar 2023 10:08:32 +0100 Subject: [PATCH 143/193] Trigger codacy From beb44ad468974257257931813d737bf2d820e19c Mon Sep 17 00:00:00 2001 From: EmilyBourne Date: Mon, 20 Mar 2023 10:14:48 +0100 Subject: [PATCH 144/193] Try with coverage secret From 7638bc6e0a85870e2f8c670c4540c5e64239830f Mon Sep 17 00:00:00 2001 From: Emily Bourne Date: Mon, 20 Mar 2023 13:48:05 +0100 Subject: [PATCH 145/193] Add first commit to provide codacy baseline From b0ad67408039d8598d89f1f464dc162913588b68 Mon Sep 17 00:00:00 2001 From: Emily Bourne Date: Mon, 20 Mar 2023 14:09:19 +0100 Subject: [PATCH 146/193] Only run benchmarks in Pyccel repo --- .github/workflows/bench.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 72b927ff70..014c78fc59 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -2,7 +2,7 @@ name: Benchmarks on: push: - branches: [ master ] + branches: [ pyccel/master ] jobs: From 70a9590945684f7fc6e9c9cdfcd0bdfb09600a4c Mon Sep 17 00:00:00 2001 From: EmilyBourne Date: Tue, 21 Mar 2023 08:55:37 +0100 Subject: [PATCH 147/193] Trigger codacy with correct branch name From b0104bd264eec4f5f430b26f1c275858831f59b3 Mon Sep 17 00:00:00 2001 From: bauom Date: Sun, 2 Apr 2023 12:48:38 +0000 Subject: [PATCH 148/193] complex not supported in cuda --- .github/actions/cuda_install/action.yml | 21 --------------------- pyccel/stdlib/ndarrays/ndarrays.c | 2 ++ pyccel/stdlib/ndarrays/ndarrays.h | 2 ++ 3 files changed, 4 insertions(+), 21 deletions(-) delete mode 100644 .github/actions/cuda_install/action.yml diff --git a/.github/actions/cuda_install/action.yml b/.github/actions/cuda_install/action.yml deleted file mode 100644 index e5e9dfd8fb..0000000000 --- a/.github/actions/cuda_install/action.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: 'Cuda installation commands' - -runs: - using: "composite" - steps: - - name: add cuda package - run: - wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/cuda-ubuntu2004.pin - sudo mv cuda-ubuntu2004.pin /etc/apt/preferences.d/cuda-repository-pin-600 - wget https://developer.download.nvidia.com/compute/cuda/11.7.1/local_installers/cuda-repo-ubuntu2004-11-7-local_11.7.1-515.65.01-1_amd64.deb - sudo dpkg -i cuda-repo-ubuntu2004-11-7-local_11.7.1-515.65.01-1_amd64.deb - sudo cp /var/cuda-repo-ubuntu2004-11-7-local/cuda-*-keyring.gpg /usr/share/keyrings/ - shell: bash - - name: update the package list - run: - sudo apt-get update - shell: bash - - name: install cuda - run: - sudo apt-get -y install cuda - shell: bash \ No newline at end of file diff --git a/pyccel/stdlib/ndarrays/ndarrays.c b/pyccel/stdlib/ndarrays/ndarrays.c index 26d967d09d..6f952730f0 100644 --- a/pyccel/stdlib/ndarrays/ndarrays.c +++ b/pyccel/stdlib/ndarrays/ndarrays.c @@ -443,5 +443,7 @@ NUMPY_SUM_(int32, int64_t, int32) NUMPY_SUM_(int64, int64_t, int64) NUMPY_SUM_(float32, float, float) NUMPY_SUM_(float64, double, double) +#ifndef __NVCC__ NUMPY_SUM_(complex64, float complex, cfloat) NUMPY_SUM_(complex128, double complex, cdouble) +#endif diff --git a/pyccel/stdlib/ndarrays/ndarrays.h b/pyccel/stdlib/ndarrays/ndarrays.h index 89d214f67c..bbfdb19b3f 100644 --- a/pyccel/stdlib/ndarrays/ndarrays.h +++ b/pyccel/stdlib/ndarrays/ndarrays.h @@ -158,8 +158,10 @@ int64_t numpy_sum_int32(t_ndarray arr); int64_t numpy_sum_int64(t_ndarray arr); float numpy_sum_float32(t_ndarray arr); double numpy_sum_float64(t_ndarray arr); +#ifndef __NVCC__ float complex numpy_sum_complex64(t_ndarray arr); double complex numpy_sum_complex128(t_ndarray arr); +#endif #ifdef __cplusplus } From 79a2407453fd423bde412d4bf50d91e5925ef3bc Mon Sep 17 00:00:00 2001 From: bauom Date: Sun, 2 Apr 2023 13:13:44 +0000 Subject: [PATCH 149/193] fixing cuda free --- pyccel/codegen/printing/ccudacode.py | 8 ++---- pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu | 28 ++++++++++---------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index 2d6ac5ad43..ea2dd22e2b 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -346,13 +346,9 @@ def _print_Allocate(self, expr): def _print_Deallocate(self, expr): if isinstance(expr.variable, InhomogeneousTupleVariable): return ''.join(self._print(Deallocate(v)) for v in expr.variable) - cuda = '' - if expr.variable.on_device or expr.variable.is_managed: - cuda = 'cuda_' - return '' if expr.variable.is_alias: - return '{}free_pointer({});\n'.format(cuda, self._print(expr.variable)) - return '{}free_array({});\n'.format(cuda, self._print(expr.variable)) + return 'cuda_free_pointer({});\n'.format(self._print(expr.variable)) + return 'cuda_free_array({});\n'.format(self._print(expr.variable)) def _print_KernelCall(self, expr): func = expr.funcdef diff --git a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu index 2c0d517e19..9da80b70c6 100644 --- a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu +++ b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu @@ -64,26 +64,26 @@ void cuda_array_fill_double(double c, t_ndarray arr) arr.nd_double[i] = c; } -void device_memory(void* devPtr, size_t size) +void device_memory(void** devPtr, size_t size) { - cudaMalloc(&devPtr, size); + cudaMalloc(devPtr, size); } -void managed_memory(void* devPtr, size_t size) +void managed_memory(void** devPtr, size_t size) { - cudaMallocManaged(&devPtr, size); + cudaMallocManaged(devPtr, size); } -void host_memory(void* devPtr, size_t size) +void host_memory(void** devPtr, size_t size) { - cudaMallocHost(&devPtr, size); + cudaMallocHost(devPtr, size); } t_ndarray cuda_array_create(int32_t nd, int64_t *shape, enum e_types type, bool is_view, enum e_memory_locations location) { t_ndarray arr; - void (*fun_ptr_arr[])(void*, size_t) = {managed_memory, host_memory, device_memory}; + void (*fun_ptr_arr[])(void**, size_t) = {managed_memory, host_memory, device_memory}; arr.nd = nd; arr.type = type; @@ -113,14 +113,14 @@ t_ndarray cuda_array_create(int32_t nd, int64_t *shape, } arr.is_view = is_view; arr.length = 1; - (*fun_ptr_arr[location])(&(arr.shape), arr.nd * sizeof(int64_t)); + cudaMallocManaged(&(arr.shape), arr.nd * sizeof(int64_t)); for (int32_t i = 0; i < arr.nd; i++) { arr.length *= shape[i]; arr.shape[i] = shape[i]; } arr.buffer_size = arr.length * arr.type_size; - (*fun_ptr_arr[location])(&(arr.strides), nd * sizeof(int64_t)); + cudaMallocManaged(&(arr.strides), nd * sizeof(int64_t)); for (int32_t i = 0; i < arr.nd; i++) { arr.strides[i] = 1; @@ -136,11 +136,11 @@ int32_t cuda_free_array(t_ndarray arr) { if (arr.shape == NULL) return (0); - free(arr.raw_data); + cudaFree(arr.raw_data); arr.raw_data = NULL; - free(arr.shape); + cudaFree(arr.shape); arr.shape = NULL; - free(arr.strides); + cudaFree(arr.strides); arr.strides = NULL; return (1); } @@ -150,9 +150,9 @@ int32_t cuda_free_pointer(t_ndarray arr) { if (arr.is_view == false || arr.shape == NULL) return (0); - free(arr.shape); + cudaFree(arr.shape); arr.shape = NULL; - free(arr.strides); + cudaFree(arr.strides); arr.strides = NULL; return (1); } From cdfff5bb709c5676b7d041cf9720e4f2f4d68c44 Mon Sep 17 00:00:00 2001 From: bauom Date: Sun, 2 Apr 2023 13:28:12 +0000 Subject: [PATCH 150/193] fixed cuda_arange internal function --- pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu index 9da80b70c6..9f6d4f35db 100644 --- a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu +++ b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu @@ -4,28 +4,36 @@ __global__ void cuda_array_arange_int8(t_ndarray arr, int start) { int index = blockIdx.x * blockDim.x + threadIdx.x; - for(int i = index ; i < arr.length; i+=1) + int stride = gridDim.x * blockDim.x; + + for(int i = index ; i < arr.length; i+=stride) arr.nd_int8[i] = (i + start); } __global__ void cuda_array_arange_int32(t_ndarray arr, int start) { int index = blockIdx.x * blockDim.x + threadIdx.x; - for(int i = index ; i < arr.length; i+=1) + int stride = gridDim.x * blockDim.x; + + for(int i = index ; i < arr.length; i+=stride) arr.nd_int32[i] = (i + start); } __global__ void cuda_array_arange_int64(t_ndarray arr, int start) { int index = blockIdx.x * blockDim.x + threadIdx.x; - for(int i = index ; i < arr.length; i+=1) + int stride = gridDim.x * blockDim.x; + + for(int i = index ; i < arr.length; i+=stride) arr.nd_int64[i] = (i + start); } __global__ void cuda_array_arange_double(t_ndarray arr, int start) { int index = blockIdx.x * blockDim.x + threadIdx.x; - for(int i = index ; i < arr.length; i+=1) + int stride = gridDim.x * blockDim.x; + + for(int i = index ; i < arr.length; i+=stride) arr.nd_double[i] = (i + start); } @@ -34,6 +42,7 @@ void cuda_array_fill_int8(int8_t c, t_ndarray arr) { int index = blockIdx.x * blockDim.x + threadIdx.x; int stride = gridDim.x * blockDim.x; + for(int i = index ; i < arr.length; i+=stride) arr.nd_int8[i] = c; } @@ -43,6 +52,7 @@ void cuda_array_fill_int32(int32_t c, t_ndarray arr) { int index = blockIdx.x * blockDim.x + threadIdx.x; int stride = gridDim.x * blockDim.x; + for(int i = index ; i < arr.length; i+=stride) arr.nd_int32[i] = c; } @@ -52,6 +62,7 @@ void cuda_array_fill_int64(int64_t c, t_ndarray arr) { int index = blockIdx.x * blockDim.x + threadIdx.x; int stride = gridDim.x * blockDim.x; + for(int i = index ; i < arr.length; i+=stride) arr.nd_int64[i] = c; } @@ -60,6 +71,7 @@ void cuda_array_fill_double(double c, t_ndarray arr) { int index = blockIdx.x * blockDim.x + threadIdx.x; int stride = gridDim.x * blockDim.x; + for(int i = index ; i < arr.length; i+=stride) arr.nd_double[i] = c; } From db7b3e99a9323ee0ff797825aef947369721828b Mon Sep 17 00:00:00 2001 From: bauom Date: Sun, 2 Apr 2023 13:30:57 +0000 Subject: [PATCH 151/193] fixed white spaces --- pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu | 56 ++++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu index 9f6d4f35db..946742bec3 100644 --- a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu +++ b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu @@ -3,77 +3,77 @@ __global__ void cuda_array_arange_int8(t_ndarray arr, int start) { - int index = blockIdx.x * blockDim.x + threadIdx.x; + int index = blockIdx.x * blockDim.x + threadIdx.x; int stride = gridDim.x * blockDim.x; - for(int i = index ; i < arr.length; i+=stride) - arr.nd_int8[i] = (i + start); + for(int i = index ; i < arr.length; i+=stride) + arr.nd_int8[i] = (i + start); } __global__ void cuda_array_arange_int32(t_ndarray arr, int start) { - int index = blockIdx.x * blockDim.x + threadIdx.x; + int index = blockIdx.x * blockDim.x + threadIdx.x; int stride = gridDim.x * blockDim.x; - for(int i = index ; i < arr.length; i+=stride) - arr.nd_int32[i] = (i + start); + for(int i = index ; i < arr.length; i+=stride) + arr.nd_int32[i] = (i + start); } __global__ void cuda_array_arange_int64(t_ndarray arr, int start) { - int index = blockIdx.x * blockDim.x + threadIdx.x; + int index = blockIdx.x * blockDim.x + threadIdx.x; int stride = gridDim.x * blockDim.x; - for(int i = index ; i < arr.length; i+=stride) - arr.nd_int64[i] = (i + start); + for(int i = index ; i < arr.length; i+=stride) + arr.nd_int64[i] = (i + start); } __global__ void cuda_array_arange_double(t_ndarray arr, int start) { - int index = blockIdx.x * blockDim.x + threadIdx.x; + int index = blockIdx.x * blockDim.x + threadIdx.x; int stride = gridDim.x * blockDim.x; - for(int i = index ; i < arr.length; i+=stride) - arr.nd_double[i] = (i + start); + for(int i = index ; i < arr.length; i+=stride) + arr.nd_double[i] = (i + start); } __global__ void cuda_array_fill_int8(int8_t c, t_ndarray arr) { - int index = blockIdx.x * blockDim.x + threadIdx.x; - int stride = gridDim.x * blockDim.x; + int index = blockIdx.x * blockDim.x + threadIdx.x; + int stride = gridDim.x * blockDim.x; - for(int i = index ; i < arr.length; i+=stride) - arr.nd_int8[i] = c; + for(int i = index ; i < arr.length; i+=stride) + arr.nd_int8[i] = c; } __global__ void cuda_array_fill_int32(int32_t c, t_ndarray arr) { - int index = blockIdx.x * blockDim.x + threadIdx.x; - int stride = gridDim.x * blockDim.x; + int index = blockIdx.x * blockDim.x + threadIdx.x; + int stride = gridDim.x * blockDim.x; - for(int i = index ; i < arr.length; i+=stride) - arr.nd_int32[i] = c; + for(int i = index ; i < arr.length; i+=stride) + arr.nd_int32[i] = c; } __global__ void cuda_array_fill_int64(int64_t c, t_ndarray arr) { - int index = blockIdx.x * blockDim.x + threadIdx.x; - int stride = gridDim.x * blockDim.x; + int index = blockIdx.x * blockDim.x + threadIdx.x; + int stride = gridDim.x * blockDim.x; - for(int i = index ; i < arr.length; i+=stride) - arr.nd_int64[i] = c; + for(int i = index ; i < arr.length; i+=stride) + arr.nd_int64[i] = c; } __global__ void cuda_array_fill_double(double c, t_ndarray arr) { - int index = blockIdx.x * blockDim.x + threadIdx.x; - int stride = gridDim.x * blockDim.x; + int index = blockIdx.x * blockDim.x + threadIdx.x; + int stride = gridDim.x * blockDim.x; - for(int i = index ; i < arr.length; i+=stride) - arr.nd_double[i] = c; + for(int i = index ; i < arr.length; i+=stride) + arr.nd_double[i] = c; } void device_memory(void** devPtr, size_t size) From 53725c34bf6a3d32b16e0586a9b953de1f70574f Mon Sep 17 00:00:00 2001 From: bauom Date: Sun, 2 Apr 2023 13:36:08 +0000 Subject: [PATCH 152/193] activate tests for developement branch --- .github/workflows/Github_pytest.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/pyccel_lint.yml | 2 +- .github/workflows/spelling.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index 9fffe32b82..968c96468d 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -2,7 +2,7 @@ name: Pyccel tests on: pull_request: - branches: [ master, cuda_main_temp, cuda_main ] + branches: [ master, developement] jobs: Linux: diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index f2809af248..be3c6552e4 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,7 +1,7 @@ name: Python Linting on: pull_request: - branches: [ master, cuda_main_temp, cuda_main ] + branches: [ master, developement] jobs: Linter: diff --git a/.github/workflows/pyccel_lint.yml b/.github/workflows/pyccel_lint.yml index 0e65e80140..d3c85e5d59 100644 --- a/.github/workflows/pyccel_lint.yml +++ b/.github/workflows/pyccel_lint.yml @@ -1,7 +1,7 @@ name: Pyccel Linting on: pull_request: - branches: [ master, cuda_main_temp, cuda_main ] + branches: [ master, developement] jobs: Pyccel-Linter: diff --git a/.github/workflows/spelling.yml b/.github/workflows/spelling.yml index dd3a5b3f8b..e25edacc64 100644 --- a/.github/workflows/spelling.yml +++ b/.github/workflows/spelling.yml @@ -1,7 +1,7 @@ name: Spellcheck Action on: pull_request: - branches: [ master, cuda_main_temp, cuda_main ] + branches: [ master, developement] jobs: Spelling: From 35d4b3d2c939b23585367743b26bdc7889029d62 Mon Sep 17 00:00:00 2001 From: bauom Date: Sun, 2 Apr 2023 13:38:58 +0000 Subject: [PATCH 153/193] typo in branch name --- .github/workflows/Github_pytest.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/pyccel_lint.yml | 2 +- .github/workflows/spelling.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index 968c96468d..d690e0a50c 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -2,7 +2,7 @@ name: Pyccel tests on: pull_request: - branches: [ master, developement] + branches: [ master, development] jobs: Linux: diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index be3c6552e4..9e0fa1f282 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,7 +1,7 @@ name: Python Linting on: pull_request: - branches: [ master, developement] + branches: [ master, development] jobs: Linter: diff --git a/.github/workflows/pyccel_lint.yml b/.github/workflows/pyccel_lint.yml index d3c85e5d59..415e006e29 100644 --- a/.github/workflows/pyccel_lint.yml +++ b/.github/workflows/pyccel_lint.yml @@ -1,7 +1,7 @@ name: Pyccel Linting on: pull_request: - branches: [ master, developement] + branches: [ master, development] jobs: Pyccel-Linter: diff --git a/.github/workflows/spelling.yml b/.github/workflows/spelling.yml index e25edacc64..8e492d9e1d 100644 --- a/.github/workflows/spelling.yml +++ b/.github/workflows/spelling.yml @@ -1,7 +1,7 @@ name: Spellcheck Action on: pull_request: - branches: [ master, developement] + branches: [ master, development] jobs: Spelling: From 251c5c12b0a1726ca193cc0fdcef5cd487fbf367 Mon Sep 17 00:00:00 2001 From: bauom Date: Sun, 2 Apr 2023 14:36:36 +0000 Subject: [PATCH 154/193] updated ccuda codegen signature_function method --- pyccel/codegen/printing/ccudacode.py | 92 +++++++++++++++------------- 1 file changed, 50 insertions(+), 42 deletions(-) diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index ea2dd22e2b..c3d95934b8 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -270,52 +270,60 @@ def __init__(self, filename, prefix_module = None): self._optional_partners = {} def function_signature(self, expr, print_arg_names = True): - """Extract from function definition all the information - (name, input, output) needed to create the signature + """ + Get the Ccuda representation of the function signature. + Extract from the function definition `expr` all the + information (name, input, output) needed to create the + function signature and return a string describing the + function. + This is not a declaration as the signature does not end + with a semi-colon. + Parameters + ---------- + expr : FunctionDef + The function definition for which a signature is needed. + print_arg_names : bool, default : True + Indicates whether argument names should be printed. + Returns + ------- + str + Signature of the function. + """ + if len(expr.results) > 1: + self._additional_args.append(expr.results) + args = list(expr.arguments) + if len(expr.results) == 1: + ret_type = self.get_declare_type(expr.results[0]) + elif len(expr.results) > 1: + ret_type = self._print(datatype('int')) + args += [FunctionDefArgument(a) for a in expr.results] + else: + ret_type = self._print(datatype('void')) + name = expr.name + if not args: + arg_code = 'void' + else: + def get_var_arg(arg, var): + code = "const " * var.is_const + code += self.get_declare_type(var) + ' ' + code += arg.name * print_arg_names + return code - Parameters - ---------- - expr : FunctionDef - the function defintion + var_list = [a.var for a in args] + arg_code_list = [self.function_signature(var, False) if isinstance(var, FunctionAddress) + else get_var_arg(arg, var) for arg, var in zip(args, var_list)] + arg_code = ', '.join(arg_code_list) - print_arg_names : Bool - default value True and False when we don't need to print - arguments names + if self._additional_args : + self._additional_args.pop() - Return - ------ - String - Signature of the function - """ + extern_word = 'extern "C"' + cuda_deco = "__global__" if 'kernel' in expr.decorators else '' - args = list(expr.arguments) - extern_word = 'extern "C" ' - if len(expr.results) == 1: - ret_type = self.get_declare_type(expr.results[0]) - elif len(expr.results) > 1: - ret_type = self._print(datatype('int')) + ' ' - args += [FunctionDefArgument(a.clone(name = a.name, memory_handling ='alias')) for a in expr.results] - else: - ret_type = self._print(datatype('void')) + ' ' - name = expr.name - if not args: - arg_code = 'void' - else: - def get_var_arg(arg, var): - code = "const " * var.is_const - code += self.get_declare_type(var) - code += arg.name * print_arg_names - return code - - var_list = [a.var for a in args] - arg_code_list = [self.function_signature(var, False) if isinstance(var, FunctionAddress) else get_var_arg(arg, var) for arg, var in zip(args, var_list)] - arg_code = ', '.join(arg_code_list) - - cuda_deco = "__global__ " if 'kernel' in expr.decorators else '' - if isinstance(expr, FunctionAddress): - return '{}{}(*{})({})'.format(extern_word, ret_type, name, arg_code) - else: - return '{}{}{}{}({})'.format(extern_word, cuda_deco, ret_type, name, arg_code) + if isinstance(expr, FunctionAddress): + return f'{extern_word} {ret_type} (*{name})({arg_code})' + else: + return f'{extern_word} {cuda_deco} {ret_type} {name}({arg_code})' def _print_Allocate(self, expr): free_code = '' From 58ba91af8f54f63865ea9130ca03b8cb911fa777 Mon Sep 17 00:00:00 2001 From: Fatima-zahra RAMDANI <52450718+framdani@users.noreply.github.com> Date: Mon, 3 Apr 2023 15:07:50 +0000 Subject: [PATCH 155/193] Fix allocation mishandling in CUDA array creation (#1328) This PR fixes a bug where the allocation of the CUDA array was not being properly handled. Fixes [pyccel-cuda#2](https://github.com/pyccel/pyccel-cuda/issues/2). **Changes Made:** - Fixed the allocation of the CUDA array to prevent segmentation fault. - Added tests to ensure proper handling of the CUDA array allocation. --- .github/actions/pytest_run/action.yml | 8 +- .github/actions/pytest_run_cuda/action.yml | 2 +- .github/actions/pytest_run_python/action.yml | 4 +- pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu | 18 +- tests/cuda_ndarrays/conftest.py | 120 +++++ tests/cuda_ndarrays/test_cuda_ndarrays.cu | 503 +++++++++++++++++++ 6 files changed, 639 insertions(+), 16 deletions(-) create mode 100644 tests/cuda_ndarrays/conftest.py create mode 100644 tests/cuda_ndarrays/test_cuda_ndarrays.cu diff --git a/.github/actions/pytest_run/action.yml b/.github/actions/pytest_run/action.yml index 73a3e53080..368dd2aeea 100644 --- a/.github/actions/pytest_run/action.yml +++ b/.github/actions/pytest_run/action.yml @@ -12,17 +12,17 @@ runs: - name: Test with pytest run: | which python - python -m pytest -n auto -rXx -v -m "not (parallel or xdist_incompatible) and c" --ignore=symbolic --ignore=ndarrays + python -m pytest -n auto -rXx -v -m "not (parallel or xdist_incompatible) and c" --ignore=symbolic --ignore=ndarrays --ignore=cuda_ndarrays if [ -n "${SITE_DIR}" ]; then echo "Touching" # Test ndarray folder update (requires parallel tests to avoid clean) touch ${SITE_DIR}/pyccel/stdlib/cwrapper/cwrapper.h python -m pytest -n auto -rXx -v -m c -k test_array_int32_1d_scalar epyccel/test_arrays.py fi - python -m pytest -rXx -m "xdist_incompatible and not parallel and c" --ignore=symbolic --ignore=ndarrays + python -m pytest -rXx -m "xdist_incompatible and not parallel and c" --ignore=symbolic --ignore=ndarrays --ignore=cuda_ndarrays pyccel-clean - python -m pytest -n auto -rXx -m "not (parallel or xdist_incompatible) and not (c or python or ccuda)" --ignore=symbolic --ignore=ndarrays - python -m pytest -rXx -m "xdist_incompatible and not parallel and not (c or python or ccuda)" --ignore=symbolic --ignore=ndarrays + python -m pytest -n auto -rXx -m "not (parallel or xdist_incompatible) and not (c or python or ccuda)" --ignore=symbolic --ignore=ndarrays --ignore=cuda_ndarrays + python -m pytest -rXx -m "xdist_incompatible and not parallel and not (c or python or ccuda)" --ignore=symbolic --ignore=ndarrays --ignore=cuda_ndarrays pyccel-clean python -m pytest ndarrays/ -rXx pyccel-clean diff --git a/.github/actions/pytest_run_cuda/action.yml b/.github/actions/pytest_run_cuda/action.yml index 8f82dda10f..09a8aff400 100644 --- a/.github/actions/pytest_run_cuda/action.yml +++ b/.github/actions/pytest_run_cuda/action.yml @@ -10,7 +10,7 @@ runs: steps: - name: Ccuda tests with pytest run: | - python -m pytest -n auto -rx -m "not (parallel or xdist_incompatible) and ccuda" --ignore=symbolic --ignore=ndarrays + python -m pytest -n auto -rx -m "not (parallel or xdist_incompatible) and ccuda" --ignore=symbolic --ignore=ndarrays --ignore=cuda_ndarrays pyccel-clean shell: ${{ inputs.shell_cmd }} working-directory: ./tests diff --git a/.github/actions/pytest_run_python/action.yml b/.github/actions/pytest_run_python/action.yml index 842fd2eaf6..c9595dde1f 100644 --- a/.github/actions/pytest_run_python/action.yml +++ b/.github/actions/pytest_run_python/action.yml @@ -10,8 +10,8 @@ runs: steps: - name: Python tests with pytest run: | - python -m pytest -n auto -rXx -m "not (parallel or xdist_incompatible) and python" --ignore=symbolic --ignore=ndarrays - python -m pytest -rXx -m "xdist_incompatible and not parallel and python" --ignore=symbolic --ignore=ndarrays + python -m pytest -n auto -rXx -m "not (parallel or xdist_incompatible) and python" --ignore=symbolic --ignore=ndarrays --ignore=cuda_ndarrays + python -m pytest -rXx -m "xdist_incompatible and not parallel and python" --ignore=symbolic --ignore=ndarrays --ignore=cuda_ndarrays pyccel-clean shell: ${{ inputs.shell_cmd }} working-directory: ./tests diff --git a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu index 2c0d517e19..4ce40e388c 100644 --- a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu +++ b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu @@ -64,26 +64,26 @@ void cuda_array_fill_double(double c, t_ndarray arr) arr.nd_double[i] = c; } -void device_memory(void* devPtr, size_t size) +void device_memory(void** devPtr, size_t size) { - cudaMalloc(&devPtr, size); + cudaMalloc(devPtr, size); } -void managed_memory(void* devPtr, size_t size) +void managed_memory(void** devPtr, size_t size) { - cudaMallocManaged(&devPtr, size); + cudaMallocManaged(devPtr, size); } -void host_memory(void* devPtr, size_t size) +void host_memory(void** devPtr, size_t size) { - cudaMallocHost(&devPtr, size); + cudaMallocHost(devPtr, size); } t_ndarray cuda_array_create(int32_t nd, int64_t *shape, enum e_types type, bool is_view, enum e_memory_locations location) { t_ndarray arr; - void (*fun_ptr_arr[])(void*, size_t) = {managed_memory, host_memory, device_memory}; + void (*fun_ptr_arr[])(void**, size_t) = {managed_memory, host_memory, device_memory}; arr.nd = nd; arr.type = type; @@ -113,14 +113,14 @@ t_ndarray cuda_array_create(int32_t nd, int64_t *shape, } arr.is_view = is_view; arr.length = 1; - (*fun_ptr_arr[location])(&(arr.shape), arr.nd * sizeof(int64_t)); + cudaMallocManaged(&(arr.shape), arr.nd * sizeof(int64_t)); for (int32_t i = 0; i < arr.nd; i++) { arr.length *= shape[i]; arr.shape[i] = shape[i]; } arr.buffer_size = arr.length * arr.type_size; - (*fun_ptr_arr[location])(&(arr.strides), nd * sizeof(int64_t)); + cudaMallocManaged(&(arr.strides), nd * sizeof(int64_t)); for (int32_t i = 0; i < arr.nd; i++) { arr.strides[i] = 1; diff --git a/tests/cuda_ndarrays/conftest.py b/tests/cuda_ndarrays/conftest.py new file mode 100644 index 0000000000..8245aa7073 --- /dev/null +++ b/tests/cuda_ndarrays/conftest.py @@ -0,0 +1,120 @@ +# pylint: disable=missing-function-docstring, missing-module-docstring/ +import subprocess +import os +import pathlib +import sys +import shutil +import pytest + +NEEDS_FROM_PARENT = hasattr(pytest.Item, "from_parent") + +def pytest_collect_file(parent, path): + """ + A hook to collect test_*.cu test files. + """ + if path.ext == ".cu" and path.basename.startswith("test_"): + if NEEDS_FROM_PARENT: + return CTestFile.from_parent(path=pathlib.Path(path), parent=parent) + return CTestFile(parent=parent, path=pathlib.Path(path)) + return None + + +class CTestFile(pytest.File): + """ + A custom file handler class for CCuda unit test files. + """ + + @classmethod + def from_parent(cls, parent, **kwargs): + return super().from_parent(parent=parent, **kwargs) + + def collect(self): + """ + Overridden collect method to collect the results from each + CCuda unit test executable. + """ + # Run the exe that corresponds to the .cu file and capture the output. + test_exe = os.path.splitext(self.fspath.basename)[0] + rootdir = self.config.rootdir.strpath + test_exe = os.path.relpath(test_exe) + ndarray_path = os.path.join(rootdir , "pyccel", "stdlib", "ndarrays") + cuda_ndarray_path = os.path.join(rootdir, "pyccel", "stdlib", "cuda_ndarrays") + comp_cmd = [shutil.which("nvcc"), test_exe + ".cu", + os.path.join(ndarray_path, "ndarrays.c"), + os.path.join(cuda_ndarray_path, "cuda_ndarrays.cu"), + "-I", ndarray_path, + "-I", cuda_ndarray_path, + "-o", test_exe,] + + subprocess.run(comp_cmd, check=True) + if sys.platform.startswith("win"): + test_exe += ".exe" + test_output = subprocess.check_output("./" + test_exe) + + # Clean up the unit test output and remove non test data lines. + lines = test_output.decode().split("\n") + lines = [line.strip() for line in lines] + lines = [line for line in lines if line.startswith("[")] + + # Extract the test metadata from the unit test output. + test_results = [] + for line in lines: + token, data = line.split(" ", 1) + token = token[1:-1] + if token in ("PASS", "FAIL"): + file_name, line_number, function_name = data.split(":") + test_results.append({"condition": token, + "file_name": file_name, + "function_name": function_name, + "line_number": int(line_number), + "INFO" : "no data found", + "DSCR" : "" + }) + elif token in ("INFO", "DSCR"): + test_results[-1][token] = data + for test_result in test_results: + if NEEDS_FROM_PARENT: + yield CTestItem.from_parent(name = test_result["function_name"] + " < " + test_result["DSCR"] + " >", + parent = self, test_result = test_result) + else: + yield CTestItem(name = test_result["function_name"] + " < " + test_result["DSCR"] + " >", parent = self, + test_result = test_result) + + +class CTestItem(pytest.Item): + """ + Pytest.Item subclass to handle each test result item. There may be + more than one test result from a test function. + """ + + def __init__(self, *, test_result, **kwargs): + """Overridden constructor to pass test results dict.""" + super().__init__(**kwargs) + self.test_result = test_result + + @classmethod + def from_parent(cls, parent, **kwargs): + return super().from_parent(parent=parent, **kwargs) + + def runtest(self): + """The test has already been run. We just evaluate the result.""" + if self.test_result["condition"] == "FAIL": + raise CTestException(self, self.name) + + def reportinfo(self): + """"Called to display header information about the test case.""" + return self.fspath, self.test_result["line_number"], self.name + + def repr_failure(self, excinfo, style=None): + """ + Called when runtest() raises an exception. The method is used + to format the output of the failed test result. + """ + if isinstance(excinfo.value, CTestException): + return f"Test failed : {self.test_result['file_name']}:{self.test_result['line_number']} {self.test_result['function_name']} < {self.test_result['DSCR']} >\n INFO : {self.test_result['INFO']}" + return super().repr_failure(excinfo) + + + +class CTestException(Exception): + """Custom exception to distinguish C unit test failures from others.""" diff --git a/tests/cuda_ndarrays/test_cuda_ndarrays.cu b/tests/cuda_ndarrays/test_cuda_ndarrays.cu new file mode 100644 index 0000000000..0ce8f38052 --- /dev/null +++ b/tests/cuda_ndarrays/test_cuda_ndarrays.cu @@ -0,0 +1,503 @@ +#include +#include +#include +#include +#include "ndarrays.h" +#include "cuda_ndarrays.h" + +void assert_double(double v1 , double v2, const char *dscr, + const char * func, const char *file, int32_t line) +{ + if (v1 != v2) + { + printf("[FAIL] %s:%d:%s\n", file, line, func); + printf("[INFO] v1:%f != v2:%f\n", v1, v2); + printf("[DSCR] %s\n", dscr); + return ; + } + printf("[PASS] %s:%d:%s\n", file, line, func); + printf("[DSCR] %s\n", dscr); +} + +void assert_float(float v1 , float v2, const char *dscr, + const char * func, const char *file, int32_t line) +{ + if (v1 != v2) + { + printf("[FAIL] %s:%d:%s\n", file, line, func); + printf("[INFO] v1:%f != v2:%f\n", v1, v2); + printf("[DSCR] %s\n", dscr); + return ; + } + printf("[PASS] %s:%d:%s\n", file, line, func); + printf("[DSCR] %s\n", dscr); +} + +void assert_int64(int64_t v1, int64_t v2, const char *dscr, + const char * func, const char *file, int32_t line) +{ + if (v1 != v2) + { + printf("[FAIL] %s:%d:%s\n", file, line, func); + printf("[INFO] v1:%ld != v2:%ld\n", v1, v2); + printf("[DSCR] %s\n", dscr); + return ; + } + printf("[PASS] %s:%d:%s\n", file, line, func); + printf("[DSCR] %s\n", dscr); +} + +void assert_int32(int32_t v1 , int32_t v2, const char *dscr, + const char * func, const char *file, int32_t line) +{ + if (v1 != v2) + { + printf("[FAIL] %s:%d:%s\n", file, line, func); + printf("[INFO] v1:%d != v2:%d\n", v1, v2); + printf("[DSCR] %s\n", dscr); + return ; + } + printf("[PASS] %s:%d:%s\n",file, line, func); + printf("[DSCR] %s\n", dscr); +} + +void assert_int16(int16_t v1 , int16_t v2, const char *dscr, + const char * func, const char *file, int32_t line) +{ + if (v1 != v2) + { + printf("[FAIL] %s:%d:%s\n", file, line, func); + printf("[INFO] v1:%d != v2:%d\n", v1, v2); + printf("[DSCR] %s\n", dscr); + return ; + } + printf("[PASS] %s:%d:%s\n", file, line, func); + printf("[DSCR] %s\n", dscr); +} + +void assert_int8(int8_t v1 , int8_t v2, const char *dscr, + const char * func, const char *file, int32_t line) +{ + if (v1 != v2) + { + printf("[FAIL] %s:%d:%s\n", file, line, func); + printf("[INFO] v1:%d != v2:%d\n", v1, v2); + printf("[DSCR] %s\n", dscr); + return ; + } + printf("[PASS] %s:%d:%s\n", file, line, func); + printf("[DSCR] %s\n", dscr); +} + +void test_cuda_array_create_host_double() +{ + t_ndarray arr = {.shape = NULL}; + + int64_t tmp_shape[] = {INT64_C(14)}; + + arr = cuda_array_create(1, tmp_shape, nd_double, false, allocateMemoryOnHost); + double cuda_array_dummy[] = {1.02, 0.25, 5e-05, 1.0, 200.0, 33.0, 5.0, 57.0, 62.0, 70.0, 103.009, 141.0, 122.0, 26.5}; + cudaMemcpy(arr.nd_double, cuda_array_dummy, arr.buffer_size, cudaMemcpyHostToHost); + + assert_int32(arr.nd, 1, "testing the number of dimensions", __func__, __FILE__, __LINE__); + assert_int64(arr.shape[0], tmp_shape[0], "testing the shape", __func__, __FILE__, __LINE__); + for(int i = 0; i < tmp_shape[0]; i++) + { + assert_double(arr.nd_double[i], cuda_array_dummy[i], "testing the data", __func__, __FILE__, __LINE__); + } + cuda_free_host(arr); +} + +void test_cuda_array_create_managed_double() +{ + t_ndarray arr = {.shape = NULL}; + + int64_t tmp_shape[] = {INT64_C(14)}; + arr = cuda_array_create(1, tmp_shape, nd_double, false, managedMemory); + double cuda_array_dummy[] = {1.02, 0.25, 5e-05, 1.0, 200.0, 33.0, 5.0, 57.0, 62.0, 70.0, 103.009, 141.0, 122.0, 26.5}; + cudaMemcpy(arr.nd_double, cuda_array_dummy, arr.buffer_size, cudaMemcpyHostToDevice); + + assert_int32(arr.nd, 1, "testing the number of dimensions", __func__, __FILE__, __LINE__); + assert_int64(arr.shape[0], tmp_shape[0], "testing the shape", __func__, __FILE__, __LINE__); + for(int i = 0; i < tmp_shape[0]; i++) + { + assert_double(arr.nd_double[i], cuda_array_dummy[i], "testing the data", __func__, __FILE__, __LINE__); + } + cuda_free(arr); +} + +void test_cuda_array_create_device_double() +{ + t_ndarray arr = {.shape = NULL}; + t_ndarray b = {.shape = NULL}; + + int64_t tmp_shape[] = {INT64_C(14)}; + arr = cuda_array_create(1, tmp_shape, nd_float, false, allocateMemoryOnDevice); + double cuda_array_dummy[] = {1.02, 0.25, 5e-05, 1.0, 200.0, 33.0, 5.0, 57.0, 62.0, 70.0, 103.009, 141.0, 122.0, 26.5}; + cudaMemcpy(arr.nd_double, cuda_array_dummy, arr.buffer_size, cudaMemcpyHostToDevice); + + int64_t tmp_shape_0001[] = {INT64_C(14)}; + b = cuda_array_create(1, tmp_shape_0001, nd_double, false, allocateMemoryOnHost); + cudaMemcpy(b.nd_double, arr.nd_double, b.buffer_size, cudaMemcpyDeviceToHost); + + assert_int32(arr.nd, 1, "testing the number of dimensions", __func__, __FILE__, __LINE__); + assert_int64(arr.shape[0], tmp_shape[0], "testing the shape", __func__, __FILE__, __LINE__); + for(int i = 0; i < tmp_shape[0]; i++) + { + assert_double(b.nd_double[i], cuda_array_dummy[i], "testing the data", __func__, __FILE__, __LINE__); + } + cuda_free(arr); + cuda_free_host(b); +} + +void test_cuda_array_create_host_float() +{ + t_ndarray arr = {.shape = NULL}; + + int64_t tmp_shape[] = {INT64_C(14)}; + + arr = cuda_array_create(1, tmp_shape, nd_float, false, allocateMemoryOnHost); + float cuda_array_dummy[] = {1.02, 0.25, 5e-05, 1.0, 200.0, 33.0, 5.0, 57.0, 62.0, 70.0, 103.009, 141.0, 122.0, 26.5}; + cudaMemcpy(arr.nd_float, cuda_array_dummy, arr.buffer_size, cudaMemcpyHostToHost); + + assert_int32(arr.nd, 1, "testing the number of dimensions", __func__, __FILE__, __LINE__); + assert_int64(arr.shape[0], tmp_shape[0], "testing the shape", __func__, __FILE__, __LINE__); + for(int i = 0; i < tmp_shape[0]; i++) + { + assert_float(arr.nd_float[i], cuda_array_dummy[i], "testing the data", __func__, __FILE__, __LINE__); + } + cuda_free_host(arr); +} + +void test_cuda_array_create_managed_float() +{ + t_ndarray arr = {.shape = NULL}; + + int64_t tmp_shape[] = {INT64_C(14)}; + arr = cuda_array_create(1, tmp_shape, nd_float, false, managedMemory); + float cuda_array_dummy[] = {1.02, 0.25, 5e-05, 1.0, 200.0, 33.0, 5.0, 57.0, 62.0, 70.0, 103.009, 141.0, 122.0, 26.5}; + cudaMemcpy(arr.nd_float, cuda_array_dummy, arr.buffer_size, cudaMemcpyHostToDevice); + + assert_int32(arr.nd, 1, "testing the number of dimensions", __func__, __FILE__, __LINE__); + assert_int64(arr.shape[0], tmp_shape[0], "testing the shape", __func__, __FILE__, __LINE__); + for(int i = 0; i < tmp_shape[0]; i++) + { + assert_float(arr.nd_float[i], cuda_array_dummy[i], "testing the data", __func__, __FILE__, __LINE__); + } + cuda_free(arr); +} + +void test_cuda_array_create_device_float() +{ + t_ndarray arr = {.shape = NULL}; + t_ndarray b = {.shape = NULL}; + + int64_t tmp_shape[] = {INT64_C(14)}; + arr = cuda_array_create(1, tmp_shape, nd_float, false, allocateMemoryOnDevice); + float cuda_array_dummy[] = {1.02, 0.25, 5e-05, 1.0, 200.0, 33.0, 5.0, 57.0, 62.0, 70.0, 103.009, 141.0, 122.0, 26.5}; + cudaMemcpy(arr.nd_float, cuda_array_dummy, arr.buffer_size, cudaMemcpyHostToDevice); + + int64_t tmp_shape_0001[] = {INT64_C(14)}; + b = cuda_array_create(1, tmp_shape_0001, nd_double, false, allocateMemoryOnHost); + cudaMemcpy(b.nd_float, arr.nd_float, b.buffer_size, cudaMemcpyDeviceToHost); + + assert_int32(arr.nd, 1, "testing the number of dimensions", __func__, __FILE__, __LINE__); + assert_int64(arr.shape[0], tmp_shape[0], "testing the shape", __func__, __FILE__, __LINE__); + for(int i = 0; i < tmp_shape[0]; i++) + { + assert_float(b.nd_float[i], cuda_array_dummy[i], "testing the data", __func__, __FILE__, __LINE__); + } + cuda_free(arr); + cuda_free_host(b); +} + +void test_cuda_array_create_host_int64() +{ + t_ndarray arr = {.shape = NULL}; + + int64_t tmp_shape[] = {INT64_C(14)}; + + arr = cuda_array_create(1, tmp_shape, nd_int64, false, allocateMemoryOnHost); + int64_t cuda_array_dummy[] = {1, 0, 0, 1, 200, 33, 5, 57, + 62, 70, 103, 141, 122, 26}; + cudaMemcpy(arr.nd_int64, cuda_array_dummy, arr.buffer_size, cudaMemcpyHostToHost); + + assert_int32(arr.nd, 1, "testing the number of dimensions", __func__, __FILE__, __LINE__); + assert_int64(arr.shape[0], tmp_shape[0], "testing the shape", __func__, __FILE__, __LINE__); + for(int i = 0; i < tmp_shape[0]; i++) + { + assert_int64(arr.nd_int64[i], cuda_array_dummy[i], "testing the data", __func__, __FILE__, __LINE__); + } + cuda_free_host(arr); +} + +void test_cuda_array_create_managed_int64() +{ + t_ndarray arr = {.shape = NULL}; + + int64_t tmp_shape[] = {INT64_C(14)}; + arr = cuda_array_create(1, tmp_shape, nd_int64, false, managedMemory); + int64_t cuda_array_dummy[] = {1, 0, 0, 1, 200, 33, 5, 57, + 62, 70, 103, 141, 122, 26}; + cudaMemcpy(arr.nd_int64, cuda_array_dummy, arr.buffer_size, cudaMemcpyHostToDevice); + + + assert_int32(arr.nd, 1, "testing the number of dimensions", __func__, __FILE__, __LINE__); + assert_int64(arr.shape[0], tmp_shape[0], "testing the shape", __func__, __FILE__, __LINE__); + for(int i = 0; i < tmp_shape[0]; i++) + { + assert_int64(arr.nd_int64[i], cuda_array_dummy[i], "testing the data", __func__, __FILE__, __LINE__); + } + cuda_free(arr); +} + +void test_cuda_array_create_device_int64() +{ + t_ndarray arr = {.shape = NULL}; + t_ndarray b = {.shape = NULL}; + + int64_t tmp_shape[] = {INT64_C(14)}; + arr = cuda_array_create(1, tmp_shape, nd_int64, false, allocateMemoryOnDevice); + int64_t cuda_array_dummy[] = {1, 0, 0, 1, 200, 33, 5, 57, + 62, 70, 103, 141, 122, 26}; + cudaMemcpy(arr.nd_double, cuda_array_dummy, arr.buffer_size, cudaMemcpyHostToDevice); + + int64_t tmp_shape_0001[] = {INT64_C(14)}; + b = cuda_array_create(1, tmp_shape_0001, nd_int64, false, allocateMemoryOnHost); + cudaMemcpy(b.nd_int64, arr.nd_int64, b.buffer_size, cudaMemcpyDeviceToHost); + + assert_int32(arr.nd, 1, "testing the number of dimensions", __func__, __FILE__, __LINE__); + assert_int64(arr.shape[0], tmp_shape[0], "testing the shape", __func__, __FILE__, __LINE__); + for(int i = 0; i < tmp_shape[0]; i++) + { + assert_int64(b.nd_int64[i], cuda_array_dummy[i], "testing the data", __func__, __FILE__, __LINE__); + } + cuda_free(arr); + cuda_free_host(b); +} + + +void test_cuda_array_create_host_int32() +{ + t_ndarray arr = {.shape = NULL}; + + int64_t tmp_shape[] = {INT64_C(14)}; + + arr = cuda_array_create(1, tmp_shape, nd_int32, false, allocateMemoryOnHost); + int32_t cuda_array_dummy[] = {1, 0, 0, 1, 200, 33, 5, 57, + 62, 70, 103, 141, 122, 26}; + cudaMemcpy(arr.nd_int32, cuda_array_dummy, arr.buffer_size, cudaMemcpyHostToHost); + + assert_int32(arr.nd, 1, "testing the number of dimensions", __func__, __FILE__, __LINE__); + assert_int64(arr.shape[0], tmp_shape[0], "testing the shape", __func__, __FILE__, __LINE__); + for(int i = 0; i < tmp_shape[0]; i++) + { + assert_int32(arr.nd_int32[i], cuda_array_dummy[i], "testing the data", __func__, __FILE__, __LINE__); + } + cuda_free_host(arr); +} + +void test_cuda_array_create_managed_int32() +{ + t_ndarray arr = {.shape = NULL}; + + int64_t tmp_shape[] = {INT64_C(14)}; + arr = cuda_array_create(1, tmp_shape, nd_int32, false, managedMemory); + int32_t cuda_array_dummy[] = {1, 0, 0, 1, 200, 33, 5, 57, + 62, 70, 103, 141, 122, 26}; + cudaMemcpy(arr.nd_int32, cuda_array_dummy, arr.buffer_size, cudaMemcpyHostToDevice); + + + assert_int32(arr.nd, 1, "testing the number of dimensions", __func__, __FILE__, __LINE__); + assert_int64(arr.shape[0], tmp_shape[0], "testing the shape", __func__, __FILE__, __LINE__); + for(int i = 0; i < tmp_shape[0]; i++) + { + assert_int32(arr.nd_int32[i], cuda_array_dummy[i], "testing the data", __func__, __FILE__, __LINE__); + } + cuda_free(arr); +} + +void test_cuda_array_create_device_int32() +{ + t_ndarray arr = {.shape = NULL}; + t_ndarray b = {.shape = NULL}; + + int64_t tmp_shape[] = {INT64_C(14)}; + arr = cuda_array_create(1, tmp_shape, nd_int32, false, allocateMemoryOnDevice); + int32_t cuda_array_dummy[] = {1, 0, 0, 1, 200, 33, 5, 57, + 62, 70, 103, 141, 122, 26}; + cudaMemcpy(arr.nd_int32, cuda_array_dummy, arr.buffer_size, cudaMemcpyHostToDevice); + + int64_t tmp_shape_0001[] = {INT64_C(14)}; + b = cuda_array_create(1, tmp_shape_0001, nd_int32, false, allocateMemoryOnHost); + cudaMemcpy(b.nd_int32, arr.nd_int32, b.buffer_size, cudaMemcpyDeviceToHost); + + assert_int32(arr.nd, 1, "testing the number of dimensions", __func__, __FILE__, __LINE__); + assert_int64(arr.shape[0], tmp_shape[0], "testing the shape", __func__, __FILE__, __LINE__); + for(int i = 0; i < tmp_shape[0]; i++) + { + assert_int32(b.nd_int32[i], cuda_array_dummy[i], "testing the data", __func__, __FILE__, __LINE__); + } + cuda_free(arr); + cuda_free_host(b); +} + +void test_cuda_array_create_host_int16() +{ + t_ndarray arr = {.shape = NULL}; + + int64_t tmp_shape[] = {INT64_C(14)}; + + arr = cuda_array_create(1, tmp_shape, nd_int16, false, allocateMemoryOnHost); + int16_t cuda_array_dummy[] = {1, 0, 0, 1, 200, 33, 5, 57, + 62, 70, 103, 141, 122, 26}; + cudaMemcpy(arr.nd_int16, cuda_array_dummy, arr.buffer_size, cudaMemcpyHostToHost); + + assert_int32(arr.nd, 1, "testing the number of dimensions", __func__, __FILE__, __LINE__); + assert_int64(arr.shape[0], tmp_shape[0], "testing the shape", __func__, __FILE__, __LINE__); + for(int i = 0; i < tmp_shape[0]; i++) + { + assert_int16(arr.nd_int16[i], cuda_array_dummy[i], "testing the data", __func__, __FILE__, __LINE__); + } + cuda_free_host(arr); +} + +void test_cuda_array_create_managed_int16() +{ + t_ndarray arr = {.shape = NULL}; + + int64_t tmp_shape[] = {INT64_C(14)}; + arr = cuda_array_create(1, tmp_shape, nd_int16, false, managedMemory); + int16_t cuda_array_dummy[] = {1, 0, 0, 1, 200, 33, 5, 57, + 62, 70, 103, 141, 122, 26}; + cudaMemcpy(arr.nd_int16, cuda_array_dummy, arr.buffer_size, cudaMemcpyHostToDevice); + + + assert_int32(arr.nd, 1, "testing the number of dimensions", __func__, __FILE__, __LINE__); + assert_int64(arr.shape[0], tmp_shape[0], "testing the shape", __func__, __FILE__, __LINE__); + for(int i = 0; i < tmp_shape[0]; i++) + { + assert_int16(arr.nd_int16[i], cuda_array_dummy[i], "testing the data", __func__, __FILE__, __LINE__); + } + cuda_free(arr); +} + +void test_cuda_array_create_device_int16() +{ + t_ndarray arr = {.shape = NULL}; + t_ndarray b = {.shape = NULL}; + + int64_t tmp_shape[] = {INT64_C(14)}; + arr = cuda_array_create(1, tmp_shape, nd_int16, false, allocateMemoryOnDevice); + int16_t cuda_array_dummy[] = {1, 0, 0, 1, 200, 33, 5, 57, + 62, 70, 103, 141, 122, 26}; + cudaMemcpy(arr.nd_int16, cuda_array_dummy, arr.buffer_size, cudaMemcpyHostToDevice); + + int64_t tmp_shape_0001[] = {INT64_C(14)}; + b = cuda_array_create(1, tmp_shape_0001, nd_int16, false, allocateMemoryOnHost); + cudaMemcpy(b.nd_int16, arr.nd_int16, b.buffer_size, cudaMemcpyDeviceToHost); + + assert_int32(arr.nd, 1, "testing the number of dimensions", __func__, __FILE__, __LINE__); + assert_int64(arr.shape[0], tmp_shape[0], "testing the shape", __func__, __FILE__, __LINE__); + for(int i = 0; i < tmp_shape[0]; i++) + { + assert_int16(b.nd_int16[i], cuda_array_dummy[i], "testing the data", __func__, __FILE__, __LINE__); + } + cuda_free(arr); + cuda_free_host(b); +} + +void test_cuda_array_create_host_int8() +{ + t_ndarray arr = {.shape = NULL}; + + int64_t tmp_shape[] = {INT64_C(14)}; + + arr = cuda_array_create(1, tmp_shape, nd_int8, false, allocateMemoryOnHost); + int8_t cuda_array_dummy[] = {1, 0, 0, 1, 116, 33, 5, 57, + 62, 70, 103, 120, 122, 26}; + cudaMemcpy(arr.nd_int8, cuda_array_dummy, arr.buffer_size, cudaMemcpyHostToHost); + + assert_int32(arr.nd, 1, "testing the number of dimensions", __func__, __FILE__, __LINE__); + assert_int64(arr.shape[0], tmp_shape[0], "testing the shape", __func__, __FILE__, __LINE__); + for(int i = 0; i < tmp_shape[0]; i++) + { + assert_int8(arr.nd_int8[i], cuda_array_dummy[i], "testing the data", __func__, __FILE__, __LINE__); + } + cuda_free_host(arr); +} + +void test_cuda_array_create_managed_int8() +{ + t_ndarray arr = {.shape = NULL}; + + int64_t tmp_shape[] = {INT64_C(14)}; + arr = cuda_array_create(1, tmp_shape, nd_int8, false, managedMemory); + int8_t cuda_array_dummy[] = {1, 0, 0, 1, 116, 33, 5, 57, + 62, 70, 103, 120, 122, 26}; + cudaMemcpy(arr.nd_int8, cuda_array_dummy, arr.buffer_size, cudaMemcpyHostToDevice); + + + assert_int32(arr.nd, 1, "testing the number of dimensions", __func__, __FILE__, __LINE__); + assert_int64(arr.shape[0], tmp_shape[0], "testing the shape", __func__, __FILE__, __LINE__); + for(int i = 0; i < tmp_shape[0]; i++) + { + assert_int8(arr.nd_int8[i], cuda_array_dummy[i], "testing the data", __func__, __FILE__, __LINE__); + } + cuda_free(arr); +} + +void test_cuda_array_create_device_int8() +{ + t_ndarray arr = {.shape = NULL}; + t_ndarray b = {.shape = NULL}; + + int64_t tmp_shape[] = {INT64_C(14)}; + arr = cuda_array_create(1, tmp_shape, nd_int8, false, allocateMemoryOnDevice); + int8_t cuda_array_dummy[] = {1, 0, 0, 1, 116, 33, 5, 57, + 62, 70, 103, 120, 122, 26}; + cudaMemcpy(arr.nd_int8, cuda_array_dummy, arr.buffer_size, cudaMemcpyHostToDevice); + + int64_t tmp_shape_0001[] = {INT64_C(14)}; + b = cuda_array_create(1, tmp_shape_0001, nd_int8, false, allocateMemoryOnHost); + cudaMemcpy(b.nd_int8, arr.nd_int8, b.buffer_size, cudaMemcpyDeviceToHost); + + assert_int32(arr.nd, 1, "testing the number of dimensions", __func__, __FILE__, __LINE__); + assert_int64(arr.shape[0], tmp_shape[0], "testing the shape", __func__, __FILE__, __LINE__); + for(int i = 0; i < tmp_shape[0]; i++) + { + assert_int8(b.nd_int8[i], cuda_array_dummy[i], "testing the data", __func__, __FILE__, __LINE__); + } + cuda_free(arr); + cuda_free_host(b); +} + +int32_t main(void) +{ + /* Cuda array creation tests */ + test_cuda_array_create_host_double(); + test_cuda_array_create_managed_double(); + test_cuda_array_create_device_double(); + + test_cuda_array_create_host_float(); + test_cuda_array_create_managed_float(); + test_cuda_array_create_device_float(); + + test_cuda_array_create_host_int64(); + test_cuda_array_create_managed_int64(); + test_cuda_array_create_device_int64(); + + test_cuda_array_create_host_int32(); + test_cuda_array_create_managed_int32(); + test_cuda_array_create_device_int32(); + + test_cuda_array_create_host_int16(); + test_cuda_array_create_managed_int16(); + test_cuda_array_create_device_int16(); + + test_cuda_array_create_host_int8(); + test_cuda_array_create_managed_int8(); + test_cuda_array_create_device_int8(); + + return (0); +} From 2430db4a2e30bd25afa988f9953ff9a8cb654e60 Mon Sep 17 00:00:00 2001 From: bauom <40796259+bauom@users.noreply.github.com> Date: Mon, 3 Apr 2023 15:12:15 +0000 Subject: [PATCH 156/193] remove unnecesasry comment --- pyccel/ast/class_defs.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pyccel/ast/class_defs.py b/pyccel/ast/class_defs.py index b04600b330..bf25800c2d 100644 --- a/pyccel/ast/class_defs.py +++ b/pyccel/ast/class_defs.py @@ -164,10 +164,6 @@ methods=[ FunctionDef('shape',[],[],body=[], decorators={'property':'property', 'numpy_wrapper':Shape})]) -# CupyArrayClass = ClassDef('cupy.ndarray', -# methods=[ -# FunctionDef('shape',[],[],body=[], -# decorators={'property':'property', 'numpy_wrapper':Shape})]) #======================================================================================= From c39934dd5d332e61307b9596d5582f2c274b82a1 Mon Sep 17 00:00:00 2001 From: bauom <40796259+bauom@users.noreply.github.com> Date: Mon, 3 Apr 2023 15:26:40 +0000 Subject: [PATCH 157/193] removed debug print --- pyccel/codegen/printing/cwrappercode.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pyccel/codegen/printing/cwrappercode.py b/pyccel/codegen/printing/cwrappercode.py index 7b73230a0d..b69a52865d 100644 --- a/pyccel/codegen/printing/cwrappercode.py +++ b/pyccel/codegen/printing/cwrappercode.py @@ -365,7 +365,6 @@ def get_static_declare_type(self, variable): prec = variable.precision dtype = self.find_in_dtype_registry(dtype, prec) - print(variable, variable.dtype, variable.is_ndarray, dtype) if self.is_c_pointer(variable): return '{0}*'.format(dtype) From 5e10cc78c4dcb6a0bc312bbd3237c3931c821ed7 Mon Sep 17 00:00:00 2001 From: bauom <40796259+bauom@users.noreply.github.com> Date: Mon, 3 Apr 2023 15:28:06 +0000 Subject: [PATCH 158/193] fixed alignement --- pyccel/codegen/utilities.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pyccel/codegen/utilities.py b/pyccel/codegen/utilities.py index 6a8a0bce0e..9c3a6df5cf 100644 --- a/pyccel/codegen/utilities.py +++ b/pyccel/codegen/utilities.py @@ -27,13 +27,13 @@ # map internal libraries to their folders inside pyccel/stdlib and their compile objects # The compile object folder will be in the pyccel dirpath internal_libs = { - "ndarrays" : ("ndarrays", CompileObj("ndarrays.c",folder="ndarrays")), - "cuda_ndarrays" : ("cuda_ndarrays", CompileObj("cuda_ndarrays.cu",folder="cuda_ndarrays")), - "pyc_math_f90" : ("math", CompileObj("pyc_math_f90.f90",folder="math")), - "pyc_math_c" : ("math", CompileObj("pyc_math_c.c",folder="math")), - "cwrapper" : ("cwrapper", CompileObj("cwrapper.c",folder="cwrapper", accelerators=('python',))), - "numpy_f90" : ("numpy", CompileObj("numpy_f90.f90",folder="numpy")), - "numpy_c" : ("numpy", CompileObj("numpy_c.c",folder="numpy")), + "ndarrays" : ("ndarrays", CompileObj("ndarrays.c",folder="ndarrays")), + "cuda_ndarrays" : ("cuda_ndarrays", CompileObj("cuda_ndarrays.cu",folder="cuda_ndarrays")), + "pyc_math_f90" : ("math", CompileObj("pyc_math_f90.f90",folder="math")), + "pyc_math_c" : ("math", CompileObj("pyc_math_c.c",folder="math")), + "cwrapper" : ("cwrapper", CompileObj("cwrapper.c",folder="cwrapper", accelerators=('python',))), + "numpy_f90" : ("numpy", CompileObj("numpy_f90.f90",folder="numpy")), + "numpy_c" : ("numpy", CompileObj("numpy_c.c",folder="numpy")), } internal_libs["cwrapper_ndarrays"] = ("cwrapper_ndarrays", CompileObj("cwrapper_ndarrays.c",folder="cwrapper_ndarrays", accelerators = ('python',), From b60d73140533b2cf44ae00e45a524f848654723b Mon Sep 17 00:00:00 2001 From: bauom <40796259+bauom@users.noreply.github.com> Date: Mon, 3 Apr 2023 15:44:15 +0000 Subject: [PATCH 159/193] fix pytest cuda action --- .github/actions/pytest_run_cuda/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/pytest_run_cuda/action.yml b/.github/actions/pytest_run_cuda/action.yml index 52092a6e02..0c55ccd81a 100644 --- a/.github/actions/pytest_run_cuda/action.yml +++ b/.github/actions/pytest_run_cuda/action.yml @@ -11,7 +11,7 @@ runs: - name: Ccuda tests with pytest run: | # Catch exit 5 (no tests found) - sh -c 'python -m pytest -n auto -rx -m "not (parallel or xdist_incompatible) and ccuda" --ignore=symbolic --ignore=ndarrays; ret=$?; [ $ret = 5 ] && exit 0 || exit $ret' + sh -c 'python -m pytest -n auto -rx -m "not (parallel or xdist_incompatible) and ccuda" --ignore=symbolic --ignore=ndarrays --ignore=cuda_ndarrays; ret=$?; [ $ret = 5 ] && exit 0 || exit $ret' pyccel-clean shell: ${{ inputs.shell_cmd }} working-directory: ./tests From abf004bd2aec46fa488666b4e7499e33b87531fa Mon Sep 17 00:00:00 2001 From: Emily Bourne Date: Mon, 3 Apr 2023 18:22:28 +0200 Subject: [PATCH 160/193] Fix triggers --- .github/workflows/Github_pytest.yml | 2 +- .github/workflows/doc_coverage.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index dfe800f778..20baee0f4b 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -2,7 +2,7 @@ name: Pyccel tests on: pull_request: - branches: [ master, cuda_main, cuda_devel ] + branches: [ master, development ] jobs: Linux: diff --git a/.github/workflows/doc_coverage.yml b/.github/workflows/doc_coverage.yml index ef63106271..57e05f7e91 100644 --- a/.github/workflows/doc_coverage.yml +++ b/.github/workflows/doc_coverage.yml @@ -2,7 +2,7 @@ name: Doc Coverage Action on: pull_request: - branches: [ master ] + branches: [ master, development ] jobs: From 713ee6b682c4d7ebdfc73613fa13b0525039a958 Mon Sep 17 00:00:00 2001 From: EmilyBourne Date: Thu, 13 Apr 2023 14:00:49 +0200 Subject: [PATCH 161/193] Set up bot for cuda --- .github/workflows/Github_pytest.yml | 14 +++++++- .github/workflows/coverage.yml | 49 ++++++++-------------------- ci_tools/bot_interaction.py | 4 ++- ci_tools/bot_messages/show_tests.txt | 5 +-- 4 files changed, 33 insertions(+), 39 deletions(-) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index 0606e04685..bd9b297e48 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -89,9 +89,21 @@ jobs: python_version: ${{ (needs.Bot.outputs.python_version == '') && '3.10' || needs.Bot.outputs.python_version }} ref: ${{ needs.Bot.outputs.REF }} - coverage: + coverage_collection: needs: [Bot, linux, cuda] + if: ${{ always() && needs.Bot.outputs.run_coverage == 'True' && needs.Linux.result == 'success' && needs.Cuda.result != 'failure' }} + if: ${{ needs.Bot.outputs.run_coverage == 'True' }} + uses: + ./.github/workflows/coverage.yml + with: + python_version: ${{ (needs.Bot.outputs.python_version == '') && '3.7' || needs.Bot.outputs.python_version }} + ref: ${{ needs.Bot.outputs.REF }} + cuda_done: ${{ needs.Cuda.result }} + + coverage: + needs: [Bot, linux, cuda, coverage_collection] if: ${{ needs.Bot.outputs.run_coverage == 'True' }} + if: ${{ always() && needs.Bot.outputs.run_coverage == 'True' && needs.CoverageCollection.result == 'success' && needs.Cuda.result != 'failure' }} uses: ./.github/workflows/coverage.yml with: diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index ac7944967c..b6c90506c1 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -9,9 +9,6 @@ on: ref: required: false type: string - cuda_done: - required: true - type: bool jobs: CoverageChecker: @@ -27,41 +24,23 @@ jobs: uses: actions/setup-python@v4 with: python-version: ${{ inputs.python_version }} - - name: Install dependencies - uses: ./.github/actions/linux_install - - name: Install coverage + - name: Install Python dependencies run: | - python -m pip install --upgrade pip - python -m pip install coverage + python -m pip install --upgrade pip + python -m pip install defusedxml shell: bash - name: Collect coverage information uses: actions/download-artifact@v3 - with: - name: coverage-artifact - - name: Rename coverage file - run: mv .coverage .coverage.linux - - name: Collect coverage information - uses: actions/download-artifact@v3 - if: ${{ inputs.cuda_done }} == 'success' - with: - name: cuda-coverage-artifact - - name: Rename coverage file - if: ${{ inputs.cuda_done }} == 'success' - run: mv .coverage .coverage.cuda - - name: Generate coverage report - run: | - echo -e "[paths]\nsource =\n $(pwd)/pyccel\n */site-packages/pyccel\n[xml]\noutput = cobertura.xml" > .coveragerc - coverage combine - coverage xml - - name: Run codacy-coverage-reporter - uses: codacy/codacy-coverage-reporter-action@master - continue-on-error: True - with: - project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} - coverage-reports: cobertura.xml - - name: Save code coverage xml report - uses: actions/upload-artifact@v3 with: name: coverage-artifact-xml - path: cobertura.xml - retention-days: 1 + - name: Collect diff information + run: | + BASE_BRANCH=$GITHUB_BASE_REF + git fetch + git diff origin/${BASE_BRANCH}..HEAD --no-indent-heuristic --unified=0 --output=pull_diff.txt --no-color + ls + shell: bash + - name: Check coverage + run: | + python ci_tools/check_new_coverage.py pull_diff.txt cobertura.xml $GITHUB_EVENT_PATH $GITHUB_STEP_SUMMARY + shell: bash diff --git a/ci_tools/bot_interaction.py b/ci_tools/bot_interaction.py index e3de344ac0..405c8750bc 100644 --- a/ci_tools/bot_interaction.py +++ b/ci_tools/bot_interaction.py @@ -13,7 +13,7 @@ senior_reviewer = ['yguclu', 'EmilyBourne'] trusted_reviewers = ['yguclu', 'EmilyBourne', 'ratnania', 'saidctb', 'bauom'] -pr_test_keys = ['linux', 'windows', 'macosx', 'coverage', 'docs', 'pylint', +pr_test_keys = ['linux', 'windows', 'macosx', 'cuda', 'coverage', 'docs', 'pylint', 'lint', 'spelling'] review_labels = ('needs_initial_review', 'Ready_for_review', 'Ready_to_merge') @@ -76,6 +76,7 @@ def run_tests(pr_id, tests, outputs, event): if outputs['run_coverage']: outputs['run_linux'] = True + outputs['run_cuda'] = True outputs['status_url'] = event['repository']['statuses_url'].format(sha=ref_sha) @@ -347,6 +348,7 @@ def flagged_as_trusted(pr_id, user): outputs = {'run_linux': False, 'run_windows': False, 'run_macosx': False, + 'run_cuda': False, 'run_coverage': False, 'run_docs': False, 'run_pylint': False, diff --git a/ci_tools/bot_messages/show_tests.txt b/ci_tools/bot_messages/show_tests.txt index a8e3322c6e..6dca265a85 100644 --- a/ci_tools/bot_messages/show_tests.txt +++ b/ci_tools/bot_messages/show_tests.txt @@ -1,7 +1,8 @@ The following is a list of keywords which can be used to run tests. Tests in bold are run by pull requests marked as ready for review: - **linux** : Runs the unit tests on a linux system. -- **windows** : Runs the unit tests on a linux system. -- **macosx** : Runs the unit tests on a linux system. +- **windows** : Runs the unit tests on a windows system. +- **macosx** : Runs the unit tests on a macosx system. +- **cuda** : Runs the cuda unit tests on a linux system. - **coverage** : Runs the unit tests on a linux system and checks the coverage of the tests. - **docs** : Checks if the documentation follows the numpydoc format. - **pylint** : Runs pylint on files which are too big to be handled by codacy. From e406bc5038356f39f861939f65bd018a1da12d23 Mon Sep 17 00:00:00 2001 From: EmilyBourne Date: Thu, 13 Apr 2023 14:05:59 +0200 Subject: [PATCH 162/193] Correct double if --- .github/workflows/Github_pytest.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index bd9b297e48..dd0b3fb92a 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -92,7 +92,6 @@ jobs: coverage_collection: needs: [Bot, linux, cuda] if: ${{ always() && needs.Bot.outputs.run_coverage == 'True' && needs.Linux.result == 'success' && needs.Cuda.result != 'failure' }} - if: ${{ needs.Bot.outputs.run_coverage == 'True' }} uses: ./.github/workflows/coverage.yml with: @@ -102,7 +101,6 @@ jobs: coverage: needs: [Bot, linux, cuda, coverage_collection] - if: ${{ needs.Bot.outputs.run_coverage == 'True' }} if: ${{ always() && needs.Bot.outputs.run_coverage == 'True' && needs.CoverageCollection.result == 'success' && needs.Cuda.result != 'failure' }} uses: ./.github/workflows/coverage.yml From 7f4b669bcc015209b60ebe97008b61792c93090a Mon Sep 17 00:00:00 2001 From: EmilyBourne Date: Thu, 13 Apr 2023 14:14:54 +0200 Subject: [PATCH 163/193] Wrong file --- .github/workflows/Github_pytest.yml | 2 +- .github/workflows/coverage_collect.yml | 67 ++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/coverage_collect.yml diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index dd0b3fb92a..4237bfac82 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -93,7 +93,7 @@ jobs: needs: [Bot, linux, cuda] if: ${{ always() && needs.Bot.outputs.run_coverage == 'True' && needs.Linux.result == 'success' && needs.Cuda.result != 'failure' }} uses: - ./.github/workflows/coverage.yml + ./.github/workflows/coverage_collect.yml with: python_version: ${{ (needs.Bot.outputs.python_version == '') && '3.7' || needs.Bot.outputs.python_version }} ref: ${{ needs.Bot.outputs.REF }} diff --git a/.github/workflows/coverage_collect.yml b/.github/workflows/coverage_collect.yml new file mode 100644 index 0000000000..e0c4a0c3bc --- /dev/null +++ b/.github/workflows/coverage_collect.yml @@ -0,0 +1,67 @@ +name: Unit test coverage collection + +on: + workflow_call: + inputs: + python_version: + required: true + type: string + ref: + required: false + type: string + cuda_result: + required: true + type: string + +jobs: + CoverageChecker: + + runs-on: ubuntu-latest + name: Unit tests + + steps: + - uses: actions/checkout@v3 + with: + ref: ${{ inputs.ref }} + - name: Set up Python ${{ inputs.python_version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ inputs.python_version }} + - name: Install dependencies + uses: ./.github/actions/linux_install + - name: Install coverage + run: | + python -m pip install --upgrade pip + python -m pip install coverage + shell: bash + - name: Collect coverage information + uses: actions/download-artifact@v3 + with: + name: coverage-artifact + - name: Rename coverage file + run: mv .coverage .coverage.linux + - name: Collect coverage information + uses: actions/download-artifact@v3 + if: ${{ inputs.cuda_result }} == 'success' + with: + name: cuda-coverage-artifact + - name: Rename coverage file + if: ${{ inputs.cuda_result }} == 'success' + run: mv .coverage .coverage.cuda + - name: Generate coverage report + run: | + echo -e "[paths]\nsource =\n $(pwd)/pyccel\n */site-packages/pyccel\n[xml]\noutput = cobertura.xml" > .coveragerc + coverage combine + coverage xml + - name: Run codacy-coverage-reporter + uses: codacy/codacy-coverage-reporter-action@master + continue-on-error: True + with: + project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} + coverage-reports: cobertura.xml + - name: Save code coverage xml report + uses: actions/upload-artifact@v3 + with: + name: coverage-artifact-xml + path: cobertura.xml + retention-days: 1 From d77057cda95cf6b9408af8d7672a882a39e462ca Mon Sep 17 00:00:00 2001 From: Emily Bourne Date: Sun, 16 Apr 2023 10:50:35 +0200 Subject: [PATCH 164/193] Correct typo --- .github/workflows/Github_pytest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index b23be5d32f..9a54c2ed18 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -98,7 +98,7 @@ jobs: with: python_version: ${{ (needs.Bot.outputs.python_version == '') && '3.7' || needs.Bot.outputs.python_version }} ref: ${{ needs.Bot.outputs.REF }} - cuda_done: ${{ needs.Cuda.result }} + cuda_result: ${{ needs.Cuda.result }} coverage: needs: [Bot, linux, cuda, coverage_collection] From 935e90308bc907a6cc9b1069c67abe57135c802d Mon Sep 17 00:00:00 2001 From: EmilyBourne Date: Mon, 17 Apr 2023 17:22:07 +0200 Subject: [PATCH 165/193] Use rpath with nvidia (#14) This PR adds the -Xcompiler flag to the CUDA code to pass the -Wl,-rpath flags to the linker. The -Wl,-rpath flags are required for the linker to find shared libraries that are not on the path. By using the -Xcompiler flag, the flags are passed to the linker as expected, ensuring that the executable can find the required shared libraries. This should fix the issue where the executable fails to find shared libraries without the -Wl,-rpath flags. --- pyccel/codegen/compiling/compilers.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pyccel/codegen/compiling/compilers.py b/pyccel/codegen/compiling/compilers.py index 372f1b1b24..62983538f3 100644 --- a/pyccel/codegen/compiling/compilers.py +++ b/pyccel/codegen/compiling/compilers.py @@ -333,7 +333,10 @@ def compile_program(self, compile_obj, output_folder, verbose = False): # Get compile options exec_cmd, includes, libs_flags, libdirs_flags, m_code = \ self._get_compile_components(compile_obj, accelerators) - linker_libdirs_flags = ['-Wl,-rpath' if l == '-L' else l for l in libdirs_flags] + if self._info['exec'] in ('nvcc', 'nvc', 'nvfortran'): + linker_libdirs_flags = ['-Xcompiler' if l == '-L' else f'"-Wl,-rpath,{l}"' for l in libdirs_flags] + else: + linker_libdirs_flags = ['-Wl,-rpath' if l == '-L' else l for l in libdirs_flags] if self._info['language'] == 'fortran': j_code = (self._info['module_output_flag'], output_folder) @@ -385,7 +388,10 @@ def compile_shared_library(self, compile_obj, output_folder, verbose = False, sh # Collect compile information exec_cmd, includes, libs_flags, libdirs_flags, m_code = \ self._get_compile_components(compile_obj, accelerators) - linker_libdirs_flags = ['-Wl,-rpath' if l == '-L' and self._info['exec'] != 'nvcc' else l for l in libdirs_flags] + if self._info['exec'] in ('nvcc', 'nvc', 'nvfortran'): + linker_libdirs_flags = ['-Xcompiler' if l == '-L' else f'"-Wl,-rpath,{l}"' for l in libdirs_flags] + else: + linker_libdirs_flags = ['-Wl,-rpath' if l == '-L' else l for l in libdirs_flags] flags.insert(0,"-shared") From 1a2542deabd01b888a789c64c3b38641f01bbd40 Mon Sep 17 00:00:00 2001 From: Emily Bourne Date: Mon, 17 Apr 2023 17:40:31 +0200 Subject: [PATCH 166/193] Missing bot output --- .github/workflows/Github_pytest.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/Github_pytest.yml b/.github/workflows/Github_pytest.yml index 9a54c2ed18..005e163ec3 100644 --- a/.github/workflows/Github_pytest.yml +++ b/.github/workflows/Github_pytest.yml @@ -18,6 +18,7 @@ jobs: run_windows: ${{ steps.run_bot.outputs.run_windows }} run_macosx: ${{ steps.run_bot.outputs.run_macosx }} run_coverage: ${{ steps.run_bot.outputs.run_coverage }} + run_cuda: ${{ steps.run_bot.outputs.run_cuda }} run_docs: ${{ steps.run_bot.outputs.run_docs }} run_pylint: ${{ steps.run_bot.outputs.run_pylint }} run_lint: ${{ steps.run_bot.outputs.run_lint }} From d84d430981464cb99ca69568a38f42248b638080 Mon Sep 17 00:00:00 2001 From: bauom Date: Tue, 18 Apr 2023 13:26:24 +0000 Subject: [PATCH 167/193] changed how we choose default compiler family --- pyccel/codegen/pipeline.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/pyccel/codegen/pipeline.py b/pyccel/codegen/pipeline.py index 912a78cb9e..79b2e32ef4 100644 --- a/pyccel/codegen/pipeline.py +++ b/pyccel/codegen/pipeline.py @@ -194,13 +194,9 @@ def handle_error(stage): if language is None: language = 'fortran' - # Choose Fortran compiler + # Choose default compiler family if compiler is None: - compiler = 'GNU' - - # Choose cuda compiler - if language == 'ccuda': - compiler = 'nvidia' + compiler = 'nvidia' if language == 'ccuda' else 'GNU' fflags = [] if fflags is None else fflags.split() wrapper_flags = [] if wrapper_flags is None else wrapper_flags.split() From d854a0aef4cf1668ccb54073c626b1bae442ceca Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 19 Apr 2023 13:47:36 +0000 Subject: [PATCH 168/193] removed dead code --- pyccel/parser/semantic.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pyccel/parser/semantic.py b/pyccel/parser/semantic.py index 3d2a76d494..8f13417506 100644 --- a/pyccel/parser/semantic.py +++ b/pyccel/parser/semantic.py @@ -2204,7 +2204,6 @@ def _visit_KernelCall(self, expr, **settings): args = self._handle_function_args(expr.args, **settings) return self._handle_kernel(expr, func, args, **settings) - # return KernelCall(func, expr.args, expr.numBlocks, expr.tpblock) def _visit_PyccelOperator(self, expr, **settings): args = [self._visit(a, **settings) for a in expr.args] From 0a8272c4f666d4d4b5a2b920daf7bb440400e205 Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 19 Apr 2023 13:59:57 +0000 Subject: [PATCH 169/193] removed unnecessary property func --- pyccel/ast/core.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pyccel/ast/core.py b/pyccel/ast/core.py index 3b6e3ffef2..6dc912fba8 100644 --- a/pyccel/ast/core.py +++ b/pyccel/ast/core.py @@ -2181,12 +2181,6 @@ def __init__(self, func, args, numBlocks, tpblock, current_function=None): self._tpblock = tpblock super().__init__(func, args, current_function) - @property - def func(self): - """ The number of blocks in which the kernel will run - """ - return self._func - @property def args(self): """ The number of blocks in which the kernel will run From a2dd793393d49e16e78f704d3fdeca93f4819d21 Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 19 Apr 2023 14:05:27 +0000 Subject: [PATCH 170/193] added missed change to tests --- tests/cuda_test/test_kernel_semantic.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/cuda_test/test_kernel_semantic.py b/tests/cuda_test/test_kernel_semantic.py index 2783c5c9f4..e08e4e3711 100644 --- a/tests/cuda_test/test_kernel_semantic.py +++ b/tests/cuda_test/test_kernel_semantic.py @@ -40,7 +40,7 @@ def stack_array_kernel(arr): # Check that the error is correct error_info = [*errors.error_info_map.values()][0][0] - assert error_info.symbol.func == 'stack_array_kernel' + assert error_info.symbol.funcdef == 'stack_array_kernel' assert KERNEL_STACK_ARRAY_ARG == error_info.message @pytest.mark.parametrize( 'language', [ @@ -93,7 +93,7 @@ def kernel_call(): # Check that the error is correct error_info = [*errors.error_info_map.values()][0][0] - assert error_info.symbol.func == 'kernel_call' + assert error_info.symbol.funcdef == 'kernel_call' assert INVALID_KERNEL_CALL_BP_GRID == error_info.message @pytest.mark.parametrize( 'language', [ @@ -120,7 +120,7 @@ def kernel_call(): # Check that the error is correct error_info = [*errors.error_info_map.values()][0][0] - assert error_info.symbol.func == 'kernel_call' + assert error_info.symbol.funcdef == 'kernel_call' assert INVALID_KERNEL_CALL_TP_BLOCK == error_info.message @pytest.mark.parametrize( 'language', [ @@ -167,7 +167,7 @@ def kernel_call(): assert errors.num_messages() == 1 error_info = [*errors.error_info_map.values()][0][0] - assert error_info.symbol.func == 'kernel_call' + assert error_info.symbol.funcdef == 'kernel_call' assert INVALID_KERNEL_CALL_BP_GRID == error_info.message @pytest.mark.parametrize( 'language', [ @@ -191,7 +191,7 @@ def kernel_call(): assert errors.num_messages() == 1 error_info = [*errors.error_info_map.values()][0][0] - assert error_info.symbol.func == 'kernel_call' + assert error_info.symbol.funcdef == 'kernel_call' assert INVALID_KERNEL_CALL_TP_BLOCK == error_info.message @@ -214,5 +214,5 @@ def non_kernel_func(): assert errors.num_messages() == 1 error_info = [*errors.error_info_map.values()][0][0] - assert error_info.symbol.func == 'non_kernel_func' + assert error_info.symbol.funcdef == 'non_kernel_func' assert INVALID_FUNCTION_CALL == error_info.message From 9190df966ad176d7232943709a3368fb2172e83b Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 19 Apr 2023 14:09:48 +0000 Subject: [PATCH 171/193] remove unnecessary code in kernelcall class --- pyccel/ast/core.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pyccel/ast/core.py b/pyccel/ast/core.py index 6dc912fba8..c73894a1b3 100644 --- a/pyccel/ast/core.py +++ b/pyccel/ast/core.py @@ -2175,18 +2175,10 @@ class KernelCall(FunctionCall): def __init__(self, func, args, numBlocks, tpblock, current_function=None): - self._func = func - self._args = args self._numBlocks = numBlocks self._tpblock = tpblock super().__init__(func, args, current_function) - @property - def args(self): - """ The number of blocks in which the kernel will run - """ - return self._args - @property def numBlocks(self): """ The number of blocks in which the kernel will run From 33e34395fbc559e095b5dc3bbf084b0ba2587d04 Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 19 Apr 2023 14:20:57 +0000 Subject: [PATCH 172/193] made CudaNewArray inherit from NumpyNewArray --- pyccel/ast/cudaext.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index 63b837be9d..888c8bd2ae 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -30,7 +30,7 @@ from .operators import PyccelAdd, PyccelLe, PyccelMul, broadcast, PyccelMinus, PyccelDiv from .variable import (Variable, Constant, HomogeneousTupleVariable) -from .numpyext import process_dtype, process_shape +from .numpyext import process_dtype, process_shape, NumpyNewArray #============================================================================== __all__ = ( @@ -48,22 +48,12 @@ ) #============================================================================== -class CudaNewArray(PyccelInternalFunction): +class CudaNewArray(NumpyNewArray): """ Class from which all Cuda functions which imply a call to Allocate inherit """ __slots__ = () - #-------------------------------------------------------------------------- - @staticmethod - def _process_order(rank, order): - - if rank < 2: - return None - - order = str(order).strip('\'"') - if order not in ('C', 'F'): - raise ValueError('unrecognized order = {}'.format(order)) - return order + pass #============================================================================== From 0bbad8f799ce3f8a89038cdeefbad79254c636d0 Mon Sep 17 00:00:00 2001 From: Fatima-zahra RAMDANI <52450718+framdani@users.noreply.github.com> Date: Wed, 19 Apr 2023 14:32:11 +0000 Subject: [PATCH 173/193] Fix deallocate printer (#1266) Update the printer of deallocate to free the array object allocated by one of the cuda's memory allocation APIs. Fixes pyccel/pyccel-cuda#8 **Functions added:** - `cuda_free_host` : frees the memory returned by cudaMallocHost(). - `cuda_free` : frees the array located on the device. - `cuda_free_pointer` : frees a pointer to a cuda array object. --------- Co-authored-by: EmilyBourne Co-authored-by: bauom <40796259+bauom@users.noreply.github.com> --- pyccel/codegen/printing/ccudacode.py | 11 +++++++---- pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu | 19 +++++++++++++++++-- pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h | 9 ++++++++- tests/internal/scripts/ccuda/free_pointer.py | 16 ++++++++++++++++ tests/run_tests_py3.sh | 2 +- 5 files changed, 49 insertions(+), 8 deletions(-) create mode 100644 tests/internal/scripts/ccuda/free_pointer.py diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index c3d95934b8..d1bb4668b2 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -352,11 +352,14 @@ def _print_Allocate(self, expr): return '{}\n{}\n{}\n'.format(free_code, shape_Assign, alloc_code) def _print_Deallocate(self, expr): - if isinstance(expr.variable, InhomogeneousTupleVariable): - return ''.join(self._print(Deallocate(v)) for v in expr.variable) + var_code = self._print(expr.variable) if expr.variable.is_alias: - return 'cuda_free_pointer({});\n'.format(self._print(expr.variable)) - return 'cuda_free_array({});\n'.format(self._print(expr.variable)) + return f"cuda_free_pointer({var_code});\n" + else: + if expr.variable.memory_location == 'host': + return f"cuda_free_host({var_code});\n" + else: + return f"cuda_free({var_code});\n" def _print_KernelCall(self, expr): func = expr.funcdef diff --git a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu index 946742bec3..2563362433 100644 --- a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu +++ b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.cu @@ -144,11 +144,11 @@ t_ndarray cuda_array_create(int32_t nd, int64_t *shape, return (arr); } -int32_t cuda_free_array(t_ndarray arr) +int32_t cuda_free_host(t_ndarray arr) { if (arr.shape == NULL) return (0); - cudaFree(arr.raw_data); + cudaFreeHost(arr.raw_data); arr.raw_data = NULL; cudaFree(arr.shape); arr.shape = NULL; @@ -157,7 +157,21 @@ int32_t cuda_free_array(t_ndarray arr) return (1); } +__host__ __device__ +int32_t cuda_free(t_ndarray arr) +{ + if (arr.shape == NULL) + return (0); + cudaFree(arr.raw_data); + arr.raw_data = NULL; + cudaFree(arr.shape); + arr.shape = NULL; + cudaFree(arr.strides); + arr.strides = NULL; + return (1); +} +__host__ __device__ int32_t cuda_free_pointer(t_ndarray arr) { if (arr.is_view == false || arr.shape == NULL) @@ -168,3 +182,4 @@ int32_t cuda_free_pointer(t_ndarray arr) arr.strides = NULL; return (1); } + diff --git a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h index d0cce3b5f0..8e88ecd998 100644 --- a/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h +++ b/pyccel/stdlib/cuda_ndarrays/cuda_ndarrays.h @@ -23,5 +23,12 @@ void _cuda_array_fill_double(double c, t_ndarray arr); t_ndarray cuda_array_create(int32_t nd, int64_t *shape, enum e_types type, bool is_view, enum e_memory_locations location); int32_t cuda_free_array(t_ndarray dump); -int32_t cuda_free_pointer(t_ndarray dump); + +int32_t cuda_free_host(t_ndarray arr); + +__host__ __device__ +int32_t cuda_free(t_ndarray arr); + +__host__ __device__ +int32_t cuda_free_pointer(t_ndarray arr); #endif diff --git a/tests/internal/scripts/ccuda/free_pointer.py b/tests/internal/scripts/ccuda/free_pointer.py new file mode 100644 index 0000000000..4cc6e8f722 --- /dev/null +++ b/tests/internal/scripts/ccuda/free_pointer.py @@ -0,0 +1,16 @@ +# pylint: disable=missing-function-docstring, disable=unused-variable, missing-module-docstring + +import math +from pyccel.decorators import kernel +from pyccel import cuda + +@kernel +def func(arr:'int[:]'): + i = cuda.grid(0) + arr[i] = math.pow(arr[i], 2) + +if __name__ == '__main__': + a = cuda.array([1,2,3,4], memory_location='device') + func[1,4](a) + c = a + cuda.synchronize() diff --git a/tests/run_tests_py3.sh b/tests/run_tests_py3.sh index 6145dca0d2..16fb78a4f0 100755 --- a/tests/run_tests_py3.sh +++ b/tests/run_tests_py3.sh @@ -15,7 +15,7 @@ SCRIPT_DIR=$(dirname -- "$(realpath -- "$0")") #python3 "$SCRIPT_DIR"/internal/test_internal.py #python3 "$SCRIPT_DIR"/external/test_external.py #python3 "$SCRIPT_DIR"/macro/test_macro.py - +python3 -m pytest "$SCRIPT_DIR"/cuda_test -v python3 -m pytest "$SCRIPT_DIR"/pyccel -v python3 -m pytest "$SCRIPT_DIR"/epyccel -v -m "not parallel" mpirun -n 4 python3 -m pytest "$SCRIPT_DIR"/epyccel/test_epyccel_mpi_modules.py -v From 0d7075c620196d0f358cf501ffe8b149a2b42fca Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 19 Apr 2023 15:15:05 +0000 Subject: [PATCH 174/193] fixing merge --- pyccel/codegen/printing/ccudacode.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index d1bb4668b2..74bcd31a30 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -370,10 +370,10 @@ def _print_KernelCall(self, expr): for a, f in zip(expr.args, func.arguments): a = a.value if a else Nil() f = f.var - if self.stored_in_c_pointer(f): + if self.is_c_pointer(f): if isinstance(a, Variable): args.append(ObjectAddress(a)) - elif not self.stored_in_c_pointer(a): + elif not self.is_c_pointer(a): tmp_var = self.scope.get_temporary_variable(f.dtype) assign = Assign(tmp_var, a) self._additional_code += self._print(assign) From 97207231fef5f1ea2f4381651e327fc81bad2da6 Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 19 Apr 2023 15:16:41 +0000 Subject: [PATCH 175/193] fixed basename issue && a test case --- tests/cuda_ndarrays/conftest.py | 2 +- tests/cuda_ndarrays/test_cuda_ndarrays.cu | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/cuda_ndarrays/conftest.py b/tests/cuda_ndarrays/conftest.py index d6a84a9e0b..112323a6cf 100644 --- a/tests/cuda_ndarrays/conftest.py +++ b/tests/cuda_ndarrays/conftest.py @@ -34,7 +34,7 @@ def collect(self): CCuda unit test executable. """ # Run the exe that corresponds to the .cu file and capture the output. - test_exe = os.path.splitext(self.fspath.basename)[0] + test_exe = os.path.splitext(self.fspath)[0] rootdir = self.config.rootdir.strpath test_exe = os.path.relpath(test_exe) ndarray_path = os.path.join(rootdir , "pyccel", "stdlib", "ndarrays") diff --git a/tests/cuda_ndarrays/test_cuda_ndarrays.cu b/tests/cuda_ndarrays/test_cuda_ndarrays.cu index 0ce8f38052..e3c5e57917 100644 --- a/tests/cuda_ndarrays/test_cuda_ndarrays.cu +++ b/tests/cuda_ndarrays/test_cuda_ndarrays.cu @@ -132,7 +132,7 @@ void test_cuda_array_create_device_double() t_ndarray b = {.shape = NULL}; int64_t tmp_shape[] = {INT64_C(14)}; - arr = cuda_array_create(1, tmp_shape, nd_float, false, allocateMemoryOnDevice); + arr = cuda_array_create(1, tmp_shape, nd_double, false, allocateMemoryOnDevice); double cuda_array_dummy[] = {1.02, 0.25, 5e-05, 1.0, 200.0, 33.0, 5.0, 57.0, 62.0, 70.0, 103.009, 141.0, 122.0, 26.5}; cudaMemcpy(arr.nd_double, cuda_array_dummy, arr.buffer_size, cudaMemcpyHostToDevice); @@ -198,7 +198,7 @@ void test_cuda_array_create_device_float() cudaMemcpy(arr.nd_float, cuda_array_dummy, arr.buffer_size, cudaMemcpyHostToDevice); int64_t tmp_shape_0001[] = {INT64_C(14)}; - b = cuda_array_create(1, tmp_shape_0001, nd_double, false, allocateMemoryOnHost); + b = cuda_array_create(1, tmp_shape_0001, nd_float, false, allocateMemoryOnHost); cudaMemcpy(b.nd_float, arr.nd_float, b.buffer_size, cudaMemcpyDeviceToHost); assert_int32(arr.nd, 1, "testing the number of dimensions", __func__, __FILE__, __LINE__); From 09b35337fe4c66a4a28bacd621adef5d31d31def Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 19 Apr 2023 15:33:54 +0000 Subject: [PATCH 176/193] changed attributes in CudaInternalVar, CudaSynchronize to class variables --- pyccel/ast/cudaext.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index 888c8bd2ae..63dc0af91f 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -131,15 +131,14 @@ def memory_location(self): class CudaSynchronize(PyccelInternalFunction): "Represents a call to Cuda.deviceSynchronize for code generation." - __slots__ = ('_dtype','_precision','_shape','_rank','_order') + __slots__ = () _attribute_nodes = () + _shape = None + _rank = 0 + _dtype = NativeInteger() + _precision = None + _order = None def __init__(self): - #... - self._shape = None - self._rank = 0 - self._dtype = NativeInteger() - self._precision = None - self._order = None super().__init__() class CudaInternalVar(PyccelAstNode): @@ -152,8 +151,11 @@ class CudaInternalVar(PyccelAstNode): Represent the dimension where we want to locate our thread. """ - __slots__ = ('_dim','_dtype','_precision','_shape','_rank','_order') + __slots__ = ('_dim','_dtype', '_precision') _attribute_nodes = ('_dim',) + _shape = None + _rank = 0 + _order = None def __init__(self, dim=None): @@ -165,11 +167,8 @@ def __init__(self, dim=None): raise ValueError("dimension need to be 0, 1 or 2") #... self._dim = dim - self._shape = None - self._rank = 0 self._dtype = dim.dtype self._precision = dim.precision - self._order = None super().__init__() @property From 0553846c7375a522291a4ddbc34f5514cf12c2ea Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 19 Apr 2023 15:59:44 +0000 Subject: [PATCH 177/193] fixed docs string capitalisation --- pyccel/ast/cudaext.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index 63dc0af91f..5f1544aaef 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -248,8 +248,8 @@ class CudaGridDim(CudaInternalVar): class CudaGrid(PyccelAstNode) : """ - CudaGrid locate Thread In the GPU architecture Using CudaThreadIdx, CudaBlockDim, CudaBlockIdx - To calculate the exact index of the thread automatically. + CudaGrid locates a thread in the GPU architecture using `CudaThreadIdx`, `CudaBlockDim`, `CudaBlockIdx` + to calculate the exact index of the thread automatically. Parameters ---------- From 9f989ff8e53005632eef4f421609c3d8ed4db490 Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 19 Apr 2023 16:06:56 +0000 Subject: [PATCH 178/193] removed cuda_constants empty dict --- pyccel/ast/cudaext.py | 5 +---- pyccel/ast/cupyext.py | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index 5f1544aaef..ba52de0a3f 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -290,9 +290,6 @@ def __new__(cls, dim=0): 'CudaGridDim' : 'gridDim' } -cuda_constants = { - -} cuda_mod = Module('cuda', - variables = cuda_constants.values(), + variables = [], funcs = cuda_funcs.values()) \ No newline at end of file diff --git a/pyccel/ast/cupyext.py b/pyccel/ast/cupyext.py index 68a5a07391..92021f0d89 100644 --- a/pyccel/ast/cupyext.py +++ b/pyccel/ast/cupyext.py @@ -471,11 +471,8 @@ def __str__(self): 'size' : PyccelFunctionDef('size' , CupyArraySize), } -cuda_constants = { -} - cupy_mod = Module('cupy', - variables = cuda_constants.values(), + variables = [], funcs = cupy_funcs.values()) #============================================================================== From c7222ff4882df1901dffbd1b1d82caa7a4eef674 Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 19 Apr 2023 16:14:06 +0000 Subject: [PATCH 179/193] removed unnecessary Asname call --- pyccel/ast/utilities.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyccel/ast/utilities.py b/pyccel/ast/utilities.py index 163265b1fa..7fafbb4f5e 100644 --- a/pyccel/ast/utilities.py +++ b/pyccel/ast/utilities.py @@ -81,7 +81,7 @@ def builtin_function(expr, args=None): funcs = [PyccelFunctionDef(d, PyccelInternalFunction) for d in pyccel_decorators.__all__]) pyccel_mod = Module('pyccel',(),(), imports = [Import('decorators', decorators_mod), - Import('cuda', AsName(cuda_mod,'cuda')), + Import('cuda', cuda_mod), ]) # TODO add documentation From 4f9b7b96292eb9875925d51f42dd650c9417abd3 Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 19 Apr 2023 16:39:48 +0000 Subject: [PATCH 180/193] removed useless change --- pyccel/codegen/printing/cwrappercode.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pyccel/codegen/printing/cwrappercode.py b/pyccel/codegen/printing/cwrappercode.py index 3d79449e4e..4ffdb1fd24 100644 --- a/pyccel/codegen/printing/cwrappercode.py +++ b/pyccel/codegen/printing/cwrappercode.py @@ -359,7 +359,6 @@ def get_static_declare_type(self, variable): str The code describing the type. """ - dtype = self._print(variable.dtype) prec = variable.precision From 8e318c5644c41aaa4e7d721345179bc5fcf3d297 Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 19 Apr 2023 16:42:31 +0000 Subject: [PATCH 181/193] updated epyccel docs string --- pyccel/epyccel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyccel/epyccel.py b/pyccel/epyccel.py index fecc7cca52..da6d932848 100644 --- a/pyccel/epyccel.py +++ b/pyccel/epyccel.py @@ -223,7 +223,7 @@ def epyccel( python_function_or_module, **kwargs ): verbose : bool Print additional information (default: False). - language : {'fortran', 'c', 'python'} + language : {'fortran', 'c', 'python', 'ccuda'} Language of generated code (default: 'fortran'). accelerators : iterable of str, optional From 9a12a0eebac927867861618dd4678ed64f443941 Mon Sep 17 00:00:00 2001 From: bauom <40796259+bauom@users.noreply.github.com> Date: Wed, 19 Apr 2023 16:44:41 +0000 Subject: [PATCH 182/193] Update pyccel/decorators.py only importing array from numpy Co-authored-by: EmilyBourne --- pyccel/decorators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyccel/decorators.py b/pyccel/decorators.py index 0e3f04c4e3..e9f51853ba 100644 --- a/pyccel/decorators.py +++ b/pyccel/decorators.py @@ -6,7 +6,7 @@ """ This module contains all the provided decorator methods. """ -import numpy +from numpy import array #TODO use pycode and call exec after that in lambdify From d83b696716f588dd78f31c7de1de448180d7d11d Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 19 Apr 2023 17:44:21 +0000 Subject: [PATCH 183/193] added doc string to kernel decorator --- pyccel/decorators.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/pyccel/decorators.py b/pyccel/decorators.py index e9f51853ba..0e07ac05c9 100644 --- a/pyccel/decorators.py +++ b/pyccel/decorators.py @@ -6,7 +6,6 @@ """ This module contains all the provided decorator methods. """ -from numpy import array #TODO use pycode and call exec after that in lambdify @@ -102,5 +101,23 @@ def identity(f): return identity def kernel(f): - return numpy.array([[f]]) + """ + A decorator for a GPU kernel function. + + This decorator marks a function as a kernel so that it can be executed on a GPU. + It returns a numpy array containing the function object to ensure that the function is an array function, + allowing it to be executed on a GPU. + + Parameters + ---------- + f : Function + The function to be marked as a kernel. + + Returns + ------- + numpy.ndarray: A numpy array containing the function object. + + """ + from numpy import array + return array([[f]]) From e9f9da92087fac2a85214919a790bc7d25968e92 Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 19 Apr 2023 18:03:55 +0000 Subject: [PATCH 184/193] added kernel docs to tutorial/decorators.md --- tutorial/decorators.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tutorial/decorators.md b/tutorial/decorators.md index cf7edb3589..5f64b96a0a 100644 --- a/tutorial/decorators.md +++ b/tutorial/decorators.md @@ -600,6 +600,45 @@ The generated C code: ```c ``` +## GPU decorators + +### kernel + +This decorator is used to mark a Python function as a GPU kernel function using the Pyccel library. The decorated function can then be executed on a GPU by specifying the number of blocks and threads per block as arguments to the function using square brackets. The `kernel` decorator is provided by the Pyccel library and is used to create a GPU kernel from a Python function. + +#### Basic Example + +python Code: + +```Python +from pyccel.decorators import kernel + +@kernel +def func(): + #Code + +if __name__ == '__main__': + # the decorated function. + func[1, 5]() +``` + +the Generated Code (Ccuda): + +```C +extern "C" __global__ void func(void) +{ + /*Code*/ + return; +} + +int main() +{ + /*the decorated function.*/ + func<<<1,5>>>(); + return 0; +} +``` + ## Getting Help If you face problems with Pyccel, please take the following steps: From ff72d7f2268e81ed08a110760745fccc1f8d9d75 Mon Sep 17 00:00:00 2001 From: bauom Date: Wed, 19 Apr 2023 18:07:32 +0000 Subject: [PATCH 185/193] improved doc string of kernel decorator --- pyccel/decorators.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyccel/decorators.py b/pyccel/decorators.py index 0e07ac05c9..2d263c4d9e 100644 --- a/pyccel/decorators.py +++ b/pyccel/decorators.py @@ -102,11 +102,11 @@ def identity(f): def kernel(f): """ - A decorator for a GPU kernel function. - - This decorator marks a function as a kernel so that it can be executed on a GPU. - It returns a numpy array containing the function object to ensure that the function is an array function, + This decorator is used to mark a Python function as a GPU kernel function, allowing it to be executed on a GPU. + The decorator returns a NumPy array containing the decorated function object + to ensure that the function is treated as an array function. + This also allows the function to run in pure Python without errors related to indexing. Parameters ---------- From 6a8a7ede0af16bed827c2a6389bb89c6dfe097e6 Mon Sep 17 00:00:00 2001 From: bauom Date: Mon, 8 May 2023 14:52:08 +0100 Subject: [PATCH 186/193] removed unnecessary pass --- pyccel/ast/cudaext.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index ba52de0a3f..091b6982b7 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -53,7 +53,6 @@ class CudaNewArray(NumpyNewArray): inherit """ __slots__ = () - pass #============================================================================== From e640648a4e6ce37885e69667093a34b9d980f3e6 Mon Sep 17 00:00:00 2001 From: bauom Date: Mon, 8 May 2023 14:55:16 +0100 Subject: [PATCH 187/193] removed unused imports --- pyccel/ast/cudaext.py | 32 +++++++------------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index 091b6982b7..8b04910b81 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -1,34 +1,16 @@ -from functools import reduce -import operator - -import numpy - -from pyccel.errors.errors import Errors -from pyccel.errors.messages import WRONG_LINSPACE_ENDPOINT, NON_LITERAL_KEEP_DIMS, NON_LITERAL_AXIS - -from pyccel.utilities.stage import PyccelStage - from .basic import PyccelAstNode -from .builtins import (PythonInt, PythonBool, PythonFloat, PythonTuple, - PythonComplex, PythonReal, PythonImag, PythonList, - PythonType, PythonConjugate) +from .builtins import (PythonTuple,PythonList) -from .core import Module, Import, PyccelFunctionDef +from .core import Module, PyccelFunctionDef -from .datatypes import (dtype_and_precision_registry as dtype_registry, - default_precision, datatype, NativeInteger, - NativeFloat, NativeComplex, NativeBool, str_dtype, - NativeNumeric) +from .datatypes import NativeInteger -from .internals import PyccelInternalFunction, Slice, max_precision, get_final_precision -from .internals import PyccelArraySize +from .internals import PyccelInternalFunction, get_final_precision -from .literals import LiteralInteger, LiteralFloat, LiteralComplex, convert_to_literal +from .literals import LiteralInteger from .literals import LiteralTrue, LiteralFalse -from .literals import Nil -from .mathext import MathCeil -from .operators import PyccelAdd, PyccelLe, PyccelMul, broadcast, PyccelMinus, PyccelDiv -from .variable import (Variable, Constant, HomogeneousTupleVariable) +from .operators import PyccelAdd, PyccelMul +from .variable import (Variable, HomogeneousTupleVariable) from .numpyext import process_dtype, process_shape, NumpyNewArray From 57e11e2e4b949916b094155872430be9950a2707 Mon Sep 17 00:00:00 2001 From: bauom Date: Mon, 8 May 2023 14:59:48 +0100 Subject: [PATCH 188/193] used f-string --- pyccel/ast/cudaext.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyccel/ast/cudaext.py b/pyccel/ast/cudaext.py index 8b04910b81..3f04c272ee 100644 --- a/pyccel/ast/cudaext.py +++ b/pyccel/ast/cudaext.py @@ -53,7 +53,7 @@ class CudaArray(CudaNewArray): def __init__(self, arg, dtype=None, order='C', memory_location='managed'): if not isinstance(arg, (PythonTuple, PythonList, Variable)): - raise TypeError('Unknown type of %s.' % type(arg)) + raise TypeError(f"Unknown type of {type(arg)}.") is_homogeneous_tuple = isinstance(arg, (PythonTuple, PythonList, HomogeneousTupleVariable)) and arg.is_homogeneous is_array = isinstance(arg, Variable) and arg.is_ndarray @@ -178,7 +178,7 @@ class CudaCopy(CudaNewArray): def __init__(self, arg, memory_location, is_async=False): if not isinstance(arg, Variable): - raise TypeError('unknown type of %s.' % type(arg)) + raise TypeError(f"unknown type of {type(arg)}.") # Verify the memory_location of src if arg.memory_location not in ('device', 'host', 'managed'): From 859e3e420331dc50e73fbaf47ec8cfb6ae799898 Mon Sep 17 00:00:00 2001 From: bauom Date: Mon, 8 May 2023 15:01:55 +0100 Subject: [PATCH 189/193] removed unused imports --- pyccel/ast/cupyext.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/pyccel/ast/cupyext.py b/pyccel/ast/cupyext.py index 92021f0d89..e0082cc6f3 100644 --- a/pyccel/ast/cupyext.py +++ b/pyccel/ast/cupyext.py @@ -11,32 +11,28 @@ import operator from pyccel.errors.errors import Errors -from pyccel.errors.messages import WRONG_LINSPACE_ENDPOINT, NON_LITERAL_KEEP_DIMS, NON_LITERAL_AXIS from pyccel.utilities.stage import PyccelStage from .basic import PyccelAstNode -from .builtins import (PythonInt, PythonBool, PythonFloat, PythonTuple, - PythonComplex, PythonReal, PythonImag, PythonList, - PythonType, PythonConjugate) +from .builtins import (PythonTuple, PythonList) -from .core import Module, Import, PyccelFunctionDef, FunctionCall +from .core import Module, PyccelFunctionDef -from .datatypes import (dtype_and_precision_registry as dtype_registry, - default_precision, datatype, NativeInteger, +from .datatypes import (default_precision, NativeInteger, NativeFloat, NativeComplex, NativeBool, str_dtype, NativeNumeric) -from .internals import PyccelInternalFunction, Slice, max_precision, get_final_precision +from .internals import PyccelInternalFunction, max_precision, get_final_precision from .internals import PyccelArraySize -from .literals import LiteralInteger, LiteralFloat, LiteralComplex, LiteralString, convert_to_literal +from .literals import LiteralInteger, LiteralFloat, LiteralComplex from .literals import LiteralTrue, LiteralFalse from .literals import Nil from .mathext import MathCeil -from .operators import broadcast, PyccelMinus, PyccelDiv -from .variable import (Variable, Constant, HomogeneousTupleVariable) -from .cudaext import CudaNewArray, CudaArray +from .operators import PyccelMinus, PyccelDiv +from .variable import (Variable, HomogeneousTupleVariable) +from .cudaext import CudaNewArray from .numpyext import process_dtype, process_shape, DtypePrecisionToCastFunction errors = Errors() From 1e512e92ae54a913325e56574936ea4e6cbf328b Mon Sep 17 00:00:00 2001 From: bauom Date: Mon, 8 May 2023 15:12:23 +0100 Subject: [PATCH 190/193] removed unused imports ccudacode --- pyccel/codegen/printing/ccudacode.py | 44 +++++----------------------- 1 file changed, 7 insertions(+), 37 deletions(-) diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index 74bcd31a30..2e5ea7931a 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -4,60 +4,30 @@ # go to https://github.com/pyccel/pyccel/blob/master/LICENSE for full license details. # #------------------------------------------------------------------------------------------# # pylint: disable=missing-function-docstring -import functools -from itertools import chain -import re -from pyccel.ast.basic import ScopedNode -from pyccel.ast.builtins import PythonRange, PythonComplex -from pyccel.ast.builtins import PythonPrint, PythonType -from pyccel.ast.builtins import PythonList, PythonTuple +from pyccel.ast.builtins import PythonTuple -from pyccel.ast.core import Declare, For, CodeBlock -from pyccel.ast.core import FuncAddressDeclare, FunctionCall, FunctionCallArgument, FunctionDef -from pyccel.ast.core import Deallocate -from pyccel.ast.core import FunctionAddress, FunctionDefArgument -from pyccel.ast.core import Assign, Import, AugAssign, AliasAssign -from pyccel.ast.core import SeparatorComment -from pyccel.ast.core import Module, AsName +from pyccel.ast.core import (FunctionCall, Deallocate, FunctionAddress, + FunctionDefArgument, Assign, Import, + AliasAssign, Module) -from pyccel.ast.operators import PyccelAdd, PyccelMul, PyccelMinus, PyccelLt, PyccelGt -from pyccel.ast.operators import PyccelAssociativeParenthesis, PyccelMod -from pyccel.ast.operators import PyccelUnarySub, IfTernaryOperator - -from pyccel.ast.datatypes import NativeInteger, NativeBool, NativeComplex -from pyccel.ast.datatypes import NativeFloat, NativeTuple, datatype, default_precision - -from pyccel.ast.internals import Slice, PrecomputedCode, get_final_precision - -from pyccel.ast.literals import LiteralTrue, LiteralFalse, LiteralImaginaryUnit, LiteralFloat -from pyccel.ast.literals import LiteralString, LiteralInteger, Literal -from pyccel.ast.literals import Nil - -from pyccel.ast.mathext import math_constants +from pyccel.ast.datatypes import NativeTuple, datatype +from pyccel.ast.literals import LiteralTrue, Literal, Nil from pyccel.ast.numpyext import NumpyFull, NumpyArray, NumpyArange -from pyccel.ast.numpyext import NumpyReal, NumpyImag, NumpyFloat from pyccel.ast.cupyext import CupyFull, CupyArray, CupyArange from pyccel.ast.cudaext import CudaCopy, cuda_Internal_Var, CudaArray -from pyccel.ast.utilities import expand_to_loops - -from pyccel.ast.variable import IndexedElement -from pyccel.ast.variable import PyccelArraySize, Variable -from pyccel.ast.variable import DottedName -from pyccel.ast.variable import InhomogeneousTupleVariable, HomogeneousTupleVariable +from pyccel.ast.variable import Variable from pyccel.ast.c_concepts import ObjectAddress from pyccel.codegen.printing.ccode import CCodePrinter from pyccel.errors.errors import Errors -from pyccel.errors.messages import (PYCCEL_RESTRICTION_TODO, INCOMPATIBLE_TYPEVAR_TO_FUNC, - PYCCEL_RESTRICTION_IS_ISNOT, UNSUPPORTED_ARRAY_RANK) errors = Errors() From c6e526c093d1f6eb84e95eb568c5d6e044c62732 Mon Sep 17 00:00:00 2001 From: bauom Date: Mon, 8 May 2023 15:20:09 +0100 Subject: [PATCH 191/193] fixed typo ?? --- pyccel/codegen/codegen.py | 4 ++-- pyccel/codegen/printing/ccudacode.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyccel/codegen/codegen.py b/pyccel/codegen/codegen.py index eba821f777..149f831ee7 100644 --- a/pyccel/codegen/codegen.py +++ b/pyccel/codegen/codegen.py @@ -8,7 +8,7 @@ from pyccel.codegen.printing.fcode import FCodePrinter from pyccel.codegen.printing.ccode import CCodePrinter -from pyccel.codegen.printing.ccudacode import CcudaCodePrinter +from pyccel.codegen.printing.ccudacode import CCudaCodePrinter from pyccel.codegen.printing.pycode import PythonCodePrinter from pyccel.ast.core import FunctionDef, Interface, ModuleHeader @@ -20,7 +20,7 @@ printer_registry = { 'fortran':FCodePrinter, 'c':CCodePrinter, - 'ccuda':CcudaCodePrinter, + 'ccuda':CCudaCodePrinter, 'python':PythonCodePrinter } diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index 2e5ea7931a..6b20772c93 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -212,7 +212,7 @@ 'stdbool', 'assert']} -class CcudaCodePrinter(CCodePrinter): +class CCudaCodePrinter(CCodePrinter): """A printer to convert python expressions to strings of ccuda code""" printmethod = "_ccudacode" language = "ccuda" From d2c27301683fb938f48be2f9c9a278de25d68eef Mon Sep 17 00:00:00 2001 From: bauom Date: Mon, 8 May 2023 15:23:48 +0100 Subject: [PATCH 192/193] fixed indent --- pyccel/codegen/printing/ccudacode.py | 100 +++++++++++++-------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index 6b20772c93..d41a756b72 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -240,60 +240,60 @@ def __init__(self, filename, prefix_module = None): self._optional_partners = {} def function_signature(self, expr, print_arg_names = True): - """ - Get the Ccuda representation of the function signature. - Extract from the function definition `expr` all the - information (name, input, output) needed to create the - function signature and return a string describing the - function. - This is not a declaration as the signature does not end - with a semi-colon. - Parameters - ---------- - expr : FunctionDef - The function definition for which a signature is needed. - print_arg_names : bool, default : True - Indicates whether argument names should be printed. - Returns - ------- - str - Signature of the function. - """ - if len(expr.results) > 1: - self._additional_args.append(expr.results) - args = list(expr.arguments) - if len(expr.results) == 1: - ret_type = self.get_declare_type(expr.results[0]) - elif len(expr.results) > 1: - ret_type = self._print(datatype('int')) - args += [FunctionDefArgument(a) for a in expr.results] - else: - ret_type = self._print(datatype('void')) - name = expr.name - if not args: - arg_code = 'void' - else: - def get_var_arg(arg, var): - code = "const " * var.is_const - code += self.get_declare_type(var) + ' ' - code += arg.name * print_arg_names - return code + """ + Get the Ccuda representation of the function signature. + Extract from the function definition `expr` all the + information (name, input, output) needed to create the + function signature and return a string describing the + function. + This is not a declaration as the signature does not end + with a semi-colon. + Parameters + ---------- + expr : FunctionDef + The function definition for which a signature is needed. + print_arg_names : bool, default : True + Indicates whether argument names should be printed. + Returns + ------- + str + Signature of the function. + """ + if len(expr.results) > 1: + self._additional_args.append(expr.results) + args = list(expr.arguments) + if len(expr.results) == 1: + ret_type = self.get_declare_type(expr.results[0]) + elif len(expr.results) > 1: + ret_type = self._print(datatype('int')) + args += [FunctionDefArgument(a) for a in expr.results] + else: + ret_type = self._print(datatype('void')) + name = expr.name + if not args: + arg_code = 'void' + else: + def get_var_arg(arg, var): + code = "const " * var.is_const + code += self.get_declare_type(var) + ' ' + code += arg.name * print_arg_names + return code - var_list = [a.var for a in args] - arg_code_list = [self.function_signature(var, False) if isinstance(var, FunctionAddress) - else get_var_arg(arg, var) for arg, var in zip(args, var_list)] - arg_code = ', '.join(arg_code_list) + var_list = [a.var for a in args] + arg_code_list = [self.function_signature(var, False) if isinstance(var, FunctionAddress) + else get_var_arg(arg, var) for arg, var in zip(args, var_list)] + arg_code = ', '.join(arg_code_list) - if self._additional_args : - self._additional_args.pop() + if self._additional_args : + self._additional_args.pop() - extern_word = 'extern "C"' - cuda_deco = "__global__" if 'kernel' in expr.decorators else '' + extern_word = 'extern "C"' + cuda_deco = "__global__" if 'kernel' in expr.decorators else '' - if isinstance(expr, FunctionAddress): - return f'{extern_word} {ret_type} (*{name})({arg_code})' - else: - return f'{extern_word} {cuda_deco} {ret_type} {name}({arg_code})' + if isinstance(expr, FunctionAddress): + return f'{extern_word} {ret_type} (*{name})({arg_code})' + else: + return f'{extern_word} {cuda_deco} {ret_type} {name}({arg_code})' def _print_Allocate(self, expr): free_code = '' From b390863abb0f2d1d6ccae827b13c8b32be55e351 Mon Sep 17 00:00:00 2001 From: bauom Date: Mon, 8 May 2023 15:27:50 +0100 Subject: [PATCH 193/193] using f-strings --- pyccel/codegen/printing/ccudacode.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyccel/codegen/printing/ccudacode.py b/pyccel/codegen/printing/ccudacode.py index d41a756b72..bfefbdc3dd 100644 --- a/pyccel/codegen/printing/ccudacode.py +++ b/pyccel/codegen/printing/ccudacode.py @@ -317,9 +317,9 @@ def _print_Allocate(self, expr): memory_location = 'allocateMemoryOn' + str(memory_location).capitalize() else: memory_location = 'managedMemory' - alloc_code = "{} = cuda_array_create({}, {}, {}, {}, {});".format( - expr.variable, len(expr.shape), tmp_shape, dtype, is_view, memory_location) - return '{}\n{}\n{}\n'.format(free_code, shape_Assign, alloc_code) + alloc_code = f"{expr.variable} = \ + cuda_array_create({len(expr.shape)}, {tmp_shape}, {dtype}, {is_view}, {memory_location});" + return f"{free_code}\n{shape_Assign}\n{alloc_code}\n" def _print_Deallocate(self, expr): var_code = self._print(expr.variable)