def testSimpleAssignement(self): # simple assignment tests #bool code_str = "value=True" inf_symbols = type_inference.infer_types(code_str) self.assertEqual(inf_symbols["value"], dtypes.typeclass(bool)) # int code_str = "value = 1" inf_symbols = type_inference.infer_types(code_str) self.assertEqual(inf_symbols["value"], dtypes.typeclass(int)) # float code_str = "value = 1.1" inf_symbols = type_inference.infer_types(code_str) self.assertEqual(inf_symbols["value"], dtypes.typeclass(float)) # string: should return a char* code_str = "value = 'hello'" inf_symbols = type_inference.infer_types(code_str) self.assertEqual(inf_symbols["value"], dtypes.pointer(dtypes.int8)) # assignment with previous symbols prev_symbols = {"char_num": dtypes.typeclass(np.int8)} code_str = "value = char_num" inf_symbols = type_inference.infer_types(code_str, prev_symbols) self.assertEqual(inf_symbols["value"], dtypes.typeclass(np.int8)) # aug assignment code_str = "value += 1.1" inf_symbols = type_inference.infer_types(code_str) self.assertEqual(inf_symbols["value"], dtypes.typeclass(float)) # annotated assignments code_str = "value : int = 1" inf_symbols = type_inference.infer_types(code_str) self.assertEqual(inf_symbols["value"], dtypes.typeclass(int)) code_str = "value : str" inf_symbols = type_inference.infer_types(code_str) self.assertEqual(inf_symbols["value"], dtypes.pointer(dtypes.int8)) # type conversion # in this case conversion is stricter (int-> int32) code_str = "value = int(1.1)" inf_symbols = type_inference.infer_types(code_str) self.assertEqual(inf_symbols["value"], dtypes.typeclass(np.int)) code_str = "value = float(1)" inf_symbols = type_inference.infer_types(code_str) self.assertEqual(inf_symbols["value"], dtypes.typeclass(np.float))
def testForLoop(self): # for loop for_loop_code = """for x in range(6): res += 1""" inf_symbols = type_inference.infer_types(for_loop_code) self.assertEqual(inf_symbols["res"], dtypes.typeclass(int)) #It is not possible to annotate the type of the variable in the loop guard # But it is ok to do so outside of the loop # https://stackoverflow.com/questions/41641449/how-do-i-annotate-types-in-a-for-loop/41641489#41641489 for_loop_code = """i: int for i in range(5): x += i""" inf_symbols = type_inference.infer_types(for_loop_code) self.assertEqual(inf_symbols["x"], dtypes.typeclass(int)) self.assertEqual(inf_symbols["i"], dtypes.typeclass(int))
def infer_connector_types(self, sdfg, state): # If a Python tasklet, use type inference to figure out all None output # connectors if all(cval.type is not None for cval in self.out_connectors.values()): return if self.code.language != dtypes.Language.Python: return if any(cval.type is None for cval in self.in_connectors.values()): raise TypeError('Cannot infer output connectors of tasklet "%s", ' 'not all input connectors have types' % str(self)) # Avoid import loop from dace.codegen.tools.type_inference import infer_types # Get symbols defined at beginning of node, and infer all types in # tasklet syms = state.symbols_defined_at(self) syms.update(self.in_connectors) new_syms = infer_types(self.code.code, syms) for cname, oconn in self.out_connectors.items(): if oconn.type is None: if cname not in new_syms: raise TypeError('Cannot infer type of tasklet %s output ' '"%s", please specify manually.' % (self.label, cname)) self.out_connectors[cname] = new_syms[cname]
def testFunction(self): code_str = """def f(): x = 2 y = 7.5 """ inf_symbols = type_inference.infer_types(code_str) self.assertEqual(inf_symbols["x"], dtypes.typeclass(int)) self.assertEqual(inf_symbols["y"], dtypes.typeclass(float))
def testInputList(self): # infer input parameter is a list of code_string code1 = "var1 = x + 1.1" code2 = "var2 = var1 + 2" defined_symbols = {"x": dtypes.typeclass(int)} inf_symbols = type_inference.infer_types([code1, code2], defined_symbols) self.assertEqual(inf_symbols["var1"], dtypes.typeclass(float)) self.assertEqual(inf_symbols["var2"], dtypes.typeclass(float))
def _Assign(self, t): self.fill() # Handle the case of a tuple output if len(t.targets) > 1: self.dispatch_lhs_tuple(t.targets) else: target = t.targets[0] if isinstance(target, ast.Tuple): if len(target.elts) > 1: self.dispatch_lhs_tuple(target.elts) target = None else: target = target.elts[0] if target and not isinstance( target, (ast.Subscript, ast.Attribute)) and not self.locals.is_defined( target.id, self._indent): # if the target is already defined, do not redefine it if self.defined_symbols is None or target.id not in self.defined_symbols: # we should try to infer the type if self.type_inference is True: # Perform type inference # Build dictionary with symbols def_symbols = {} def_symbols.update( self.locals.get_name_type_associations()) def_symbols.update(self.defined_symbols) inferred_symbols = type_inference.infer_types( t, def_symbols) inferred_type = inferred_symbols[target.id] self.locals.define(target.id, t.lineno, self._indent, inferred_type) if self.language == dace.dtypes.Language.OpenCL and ( inferred_type is not None and inferred_type.veclen > 1): # if the veclen is greater than one, this should be defined with a vector data type self.write("{}{} ".format( dace.dtypes._OCL_VECTOR_TYPES[ inferred_type.type], inferred_type.veclen)) else: self.write(dace.dtypes._CTYPES[inferred_type.type] + " ") else: self.locals.define(target.id, t.lineno, self._indent) self.write("auto ") # dispatch target if target: self.dispatch(target) self.write(" = ") self.dispatch(t.value) #self.dtype = inferred_type self.write(';')
def testExpressionAssignment(self): code_str = "res = 5 + 3.1" symbols = type_inference.infer_types(code_str) self.assertEqual(symbols["res"], dtypes.typeclass(float)) code_str = "res = 5 + 1" inf_symbols = type_inference.infer_types(code_str) self.assertEqual(inf_symbols["res"], dtypes.typeclass(int)) # use already defined symbol code_str = "res2 = 1 + res" symbols = type_inference.infer_types(code_str, symbols) self.assertEqual(symbols["res2"], dtypes.typeclass(float)) code_str = "res3 = 1 + int(res*res2)" symbols = type_inference.infer_types(code_str, symbols) self.assertEqual(symbols["res3"], dtypes.typeclass(int))
def testIf(self): code_str = """if cond1: a = 1*2 elif cond2: b = 1.2*3.4 else: c = True""" inf_symbols = type_inference.infer_types(code_str) self.assertEqual(inf_symbols["a"], dtypes.typeclass(int)) self.assertEqual(inf_symbols["b"], dtypes.typeclass(float)) self.assertEqual(inf_symbols["c"], dtypes.typeclass(bool))
def testVarious(self): # code snippets that contains constructs not directly involved in type inference # (borrowed by astunparse tests) while_code = """def g(): while True: break z = 3 """ inf_symbols = type_inference.infer_types(while_code) self.assertEqual(inf_symbols["z"], dtypes.typeclass(int)) raise_from_code = """try: 1 / 0 except ZeroDivisionError as e: raise ArithmeticError from e """ inf_symbols = type_inference.infer_types(raise_from_code) try_except_finally_code = """try: suite1 except ex1: suite2 except ex2: suite3 else: suite4 finally: suite5 """ inf_symbols = type_inference.infer_types(try_except_finally_code) #function def with arguments function_def_return_code = """def f(arg : float): res = 5 + arg return res """ inf_symbols = type_inference.infer_types(function_def_return_code) self.assertEqual(inf_symbols["res"], dtypes.typeclass(float)) self.assertEqual(inf_symbols["arg"], dtypes.typeclass(float))
def testCCode(self): # tests for situations that could arise from C/C++ tasklet codes ############################################################################################### # Pointer: this is a situation that could happen in FPGA backend due to OpenCL Keyword Remover: # if in a tasklet, there is an assignment in which the right hand side is a connector and the # corresponding memlet is dynamic, the OpenCL Keyword Remover, will update the code to be "target = *rhs" # In a similar situation the type inference should not consider the leading * stmt = ast.parse("value = float_var") stmt.body[0].value.id = "*float_var" # effect of OpenCL Keyword Remover prev_symbols = {"float_var": dtypes.typeclass(float)} inf_symbols = type_inference.infer_types(stmt, prev_symbols) self.assertEqual(inf_symbols["value"], dtypes.typeclass(float))
def _arg(self, t): if t.annotation: self.dispatch(t.annotation) self.write(' ') else: self.write("auto ") self.write(t.arg) if self.type_inference: # Build dictionary with symbols def_symbols = self.defined_symbols.copy() def_symbols.update(self.locals.get_name_type_associations()) inferred_symbols = type_inference.infer_types(t, def_symbols) inferred_type = inferred_symbols[t.arg] self.locals.define(t.arg, t.lineno, self._indent, inferred_type) else: self.locals.define(t.arg, t.lineno, self._indent)
def _Assign(self, t): self.fill() # Handle the case of a tuple output if len(t.targets) > 1: self.dispatch_lhs_tuple(t.targets) else: target = t.targets[0] if isinstance(target, ast.Tuple): if len(target.elts) > 1: self.dispatch_lhs_tuple(target.elts) target = target.elts[0] if not isinstance( target, (ast.Subscript, ast.Attribute)) and not self.locals.is_defined( target.id, self._indent): # the target is not already defined: we should try to infer the type if self.type_inference is True: # Perform type inference # Build dictionary with symbols def_symbols = {} def_symbols.update( self.locals.get_name_type_associations()) def_symbols.update(self.defined_symbols) inferred_symbols = type_inference.infer_types( t, def_symbols) inferred_type = inferred_symbols[target.id] self.locals.define(target.id, t.lineno, self._indent, inferred_type) self.write(dace.dtypes._CTYPES[inferred_type.type] + " ") else: self.locals.define(target.id, t.lineno, self._indent) self.write("auto ") # dispatch target self.dispatch(target) #if not infer_type: # inferred_type = self.dispatch(target, True) #self.dtype = inferred_type self.write(" = ") self.dispatch(t.value) #self.dtype = inferred_type self.write(';')
def testInputAST(self): # infer input parameter is an AST code_str = """var1 = int(in_x) var2: int = in_y var3 = 2.1 if (i>1 and i<10) else 2.1 # A comment res = var1 + var3 * var2 """ #create AST tree = ast.parse(code_str) defined_symbols = {"in_x": dtypes.typeclass(np.float32), "in_y": dtypes.typeclass(np.float32)} inf_symbols = type_inference.infer_types(code_str, defined_symbols) self.assertEqual(inf_symbols["var1"], dtypes.typeclass(int)) self.assertEqual(inf_symbols["var2"], dtypes.typeclass(int)) self.assertEqual(inf_symbols["var3"], dtypes.typeclass(float)) self.assertEqual(inf_symbols["res"], dtypes.typeclass(float))
def testDefaultDataTypes(self): # check that configuration about defult data types is enforced config_data_types = Config.get('compiler', 'default_data_types') code_str = """value1 = 10 value2=3.14 value3=5000000000""" inf_symbols = type_inference.infer_types(code_str) if config_data_types.lower() == "python": self.assertEqual(inf_symbols["value1"], dtypes.typeclass(np.int64)) self.assertEqual(inf_symbols["value2"], dtypes.typeclass(np.float64)) elif config_data_types.lower() == "c": self.assertEqual(inf_symbols["value1"], dtypes.typeclass(np.int32)) self.assertEqual(inf_symbols["value2"], dtypes.typeclass(np.float32)) # in any case, value3 needs uint64 self.assertEqual(inf_symbols["value3"], dtypes.typeclass(np.uint64))
def _AnnAssign(self, t): self.fill() if isinstance(t.target, ast.Tuple): if len(t.target.elts) > 1: self.dispatch_lhs_tuple(t.target.elts) else: target = t.target.elts[0] else: target = t.target # Assignment of the form x: int = 0 is converted to int x = (int)0; if not self.locals.is_defined(target.id, self._indent): if self.type_inference is True: # get the type indicated into the annotation def_symbols = self.defined_symbols.copy() def_symbols.update(self.locals.get_name_type_associations()) inferred_symbols = type_inference.infer_types(t, def_symbols) inferred_type = inferred_symbols[target.id] self.locals.define(target.id, t.lineno, self._indent, inferred_type) else: self.locals.define(target.id, t.lineno, self._indent) self.dispatch(t.annotation) self.write(' ') if not t.simple: self.write("(") self.dispatch(t.target) if not t.simple: self.write(")") if t.value: self.write(" = (") self.dispatch(t.annotation) self.write(")") self.dispatch(t.value) self.write(';')
def testArrayAccess(self): code_str = "tmp = array[i]" symbols = type_inference.infer_types( code_str, {"array": dtypes.typeclass(float)}) self.assertEqual(symbols["tmp"], dtypes.typeclass(float))
def testAssignmentIf(self): code_str = "res = 5 if x > 10 else 3.1" inf_symbols = type_inference.infer_types(code_str) self.assertEqual(inf_symbols["res"], dtypes.typeclass(float))
def infer_connector_types(self, sdfg, state): # If a MLIR tasklet, simply read out the types (it's explicit) if self.code.language == dtypes.Language.MLIR: # Inline import because mlir.utils depends on pyMLIR which may not be installed # Doesn't cause crashes due to missing pyMLIR if a MLIR tasklet is not present from dace.codegen.targets.mlir import utils mlir_ast = utils.get_ast(self.code.code) mlir_is_generic = utils.is_generic(mlir_ast) mlir_entry_func = utils.get_entry_func(mlir_ast, mlir_is_generic) mlir_result_type = utils.get_entry_result_type( mlir_entry_func, mlir_is_generic) mlir_out_name = next(iter(self.out_connectors.keys())) if self.out_connectors[ mlir_out_name] is None or self.out_connectors[ mlir_out_name].ctype == "void": self.out_connectors[mlir_out_name] = utils.get_dace_type( mlir_result_type) elif self.out_connectors[mlir_out_name] != utils.get_dace_type( mlir_result_type): warnings.warn( "Type mismatch between MLIR tasklet out connector and MLIR code" ) for mlir_arg in utils.get_entry_args(mlir_entry_func, mlir_is_generic): if self.in_connectors[ mlir_arg[0]] is None or self.in_connectors[ mlir_arg[0]].ctype == "void": self.in_connectors[mlir_arg[0]] = utils.get_dace_type( mlir_arg[1]) elif self.in_connectors[mlir_arg[0]] != utils.get_dace_type( mlir_arg[1]): warnings.warn( "Type mismatch between MLIR tasklet in connector and MLIR code" ) return # If a Python tasklet, use type inference to figure out all None output # connectors if all(cval.type is not None for cval in self.out_connectors.values()): return if self.code.language != dtypes.Language.Python: return if any(cval.type is None for cval in self.in_connectors.values()): raise TypeError('Cannot infer output connectors of tasklet "%s", ' 'not all input connectors have types' % str(self)) # Avoid import loop from dace.codegen.tools.type_inference import infer_types # Get symbols defined at beginning of node, and infer all types in # tasklet syms = state.symbols_defined_at(self) syms.update(self.in_connectors) new_syms = infer_types(self.code.code, syms) for cname, oconn in self.out_connectors.items(): if oconn.type is None: if cname not in new_syms: raise TypeError('Cannot infer type of tasklet %s output ' '"%s", please specify manually.' % (self.label, cname)) self.out_connectors[cname] = new_syms[cname]