def is_equivalent(self, other): if not isinstance(other, Stream): return False # Test type if self.dtype != other.dtype: return False # Test dimensionality if len(self.shape) != len(other.shape): return False # Test shape for dim, otherdim in zip(self.shape, other.shape): # If both are symbols, ensure equality if symbolic.issymbolic(dim) and symbolic.issymbolic(otherdim): if dim != otherdim: return False # If one is a symbol and the other is a constant # make sure they are equivalent elif symbolic.issymbolic(otherdim): if symbolic.eval(otherdim) != dim: return False elif symbolic.issymbolic(dim): if symbolic.eval(dim) != otherdim: return False else: # Any other case (constant vs. constant), check for equality if otherdim != dim: return False return True
def update_resolved_symbol(self, sym): """ Notifies an array that a symbol has been resolved so that it can be resized. """ self.resize( [symbolic.eval(s, 0) for s in self.descriptor.shape], refcheck=False) self._symlist = symbolic.symlist(self.descriptor.shape)
def ndarray(shape, dtype=numpy.float64, *args, **kwargs): """ Returns a numpy ndarray where all symbols have been evaluated to numbers and types are converted to numpy types. """ new_shape = [symbolic.eval(s) for s in shape] new_dtype = dtype.type if isinstance(dtype, dtypes.typeclass) else dtype return numpy.ndarray(shape=new_shape, dtype=new_dtype, *args, **kwargs)
def __new__(cls, shape, dtype=types.float32, materialize_func=None, allow_conflicts=False, *args, **kwargs): """ Initializes a DaCe ND-array. @param shape: The array shape (may contain symbols). @param dtype: The array data type. @param materialize_func: An optional string that contains a method to materialize array contents on demand. If not None, the array is not allocated within the DaCe program. @param allow_conflicts: If True, suppresses warnings on conflicting array writes in DaCe programs without a matching conflict resolution memlet. """ # Avoiding import loops from dace import data tmpshape = shape shape = [symbolic.eval(s, 0) for s in shape] kwargs.update({'dtype': dtype.type}) res = numpy.ndarray.__new__(cls, shape, *args, **kwargs) res._symlist = symbolic.symlist(tmpshape) for _, sym in res._symlist.items(): sym._arrays_to_update.append(res) if not isinstance(dtype, types.typeclass): dtype = types.typeclass(dtype.type) res.descriptor = data.Array( dtype, tmpshape, materialize_func=materialize_func, transient=False, allow_conflicts=allow_conflicts) return res
def simulate(dace_program: DaceProgram, *args): """ Simulate a DaCe program using Python. :param dace_program: A program function annotated with `@dace.program`. :param *args: Program arguments to pass. """ pdp, modules = dace_program.generate_pdp() # Transform the decorated AST into working python code (annotated so # that debugging works) simulated_ast = SimulatorTransformer(pdp).visit(pdp.ast) mod = ast.Module(body=simulated_ast, lineno=1) mod = ast.fix_missing_locations(mod) # Compile the transformed AST codeobj = compile(mod, pdp.filename, 'exec') fname = dace_program.name if Config.get_bool('debugprint'): print("Simulating DaCe program with name", fname) param_symbols = {} if len(pdp.params) != len(args): raise SyntaxError('Argument number mismatch in \'' + fname + '\', expecting ' + str(len(args))) ################################################################## # Disallow external variables # EXCEPTIONS: # * The dace module ('import dace') # * The math module ('import math') # * Constants (types int, float, dace.int*, dace.float*) # * DaCe symbols that have been defined in @dace.program args ################################################################## f_globals = {} # WORKAROUND: Works around a bug in CPython 2.x where True and # False are undefined f_globals['True'] = True f_globals['False'] = False ###################### # Allow certain namespaces/modules and constants f_globals.update(pdp.globals) # Resolve symbols symbols = {} symbols.update(symbolic.getsymbols( args)) # from parameter values (externally defined as "dace.symbol") symbols.update(param_symbols) # from parameter values (constant inputs) resolve = {} for gname, gval in f_globals.items(): if isinstance(gval, symbolic.symbol): if gval.name in symbols: resolve[gname] = gval.get() # Raise exception if undefined else: resolve[gname] = None # Mark unrelated symbols for removal f_globals.update(resolve) # Remove unrelated symbols from globals for rk, rv in resolve.items(): if rv is None: del f_globals[rk] # Resolve symbols in arguments as well newargs = tuple(symbolic.eval(a) for a in args) ################################################################## # Store parameter objects pdp.arrayobjs = { k: v for k, v in zip(pdp.params, newargs) if isinstance(v, numpy.ndarray) } # Simulate f ################################ # Obtain function object gen_module = {} gen_module.update(f_globals) exec(codeobj, gen_module) cfunc = gen_module[fname] # Run function result = cfunc(*newargs) ################################ return result
def _construct_args(self, **kwargs): """ Main function that controls argument construction for calling the C prototype of the SDFG. Organizes arguments first by `sdfg.arglist`, then data descriptors by alphabetical order, then symbols by alphabetical order. """ # Argument construction sig = self._sdfg.signature_arglist(with_types=False) typedict = self._sdfg.arglist() if len(kwargs) > 0: # Construct mapping from arguments to signature arglist = [] argtypes = [] argnames = [] for a in sig: try: arglist.append(kwargs[a]) argtypes.append(typedict[a]) argnames.append(a) except KeyError: raise KeyError("Missing program argument \"{}\"".format(a)) else: arglist = [] argtypes = [] argnames = [] sig = [] # Type checking for a, arg, atype in zip(argnames, arglist, argtypes): if not isinstance(arg, np.ndarray) and isinstance(atype, dt.Array): raise TypeError( 'Passing an object (type %s) to an array in argument "%s"' % (type(arg).__name__, a)) if isinstance(arg, np.ndarray) and not isinstance(atype, dt.Array): raise TypeError( 'Passing an array to a scalar (type %s) in argument "%s"' % (atype.dtype.ctype, a)) if not isinstance(atype, dt.Array) and not isinstance( atype.dtype, dace.callback) and not isinstance( arg, atype.dtype.type): print('WARNING: Casting scalar argument "%s" from %s to %s' % (a, type(arg).__name__, atype.dtype.type)) # Call a wrapper function to make NumPy arrays from pointers. for index, (arg, argtype) in enumerate(zip(arglist, argtypes)): if isinstance(argtype.dtype, dace.callback): arglist[index] = argtype.dtype.get_trampoline(arg) # Retain only the element datatype for upcoming checks and casts argtypes = [t.dtype.as_ctypes() for t in argtypes] sdfg = self._sdfg # As in compilation, add symbols used in array sizes to parameters symparams = {} symtypes = {} for symname in sdfg.undefined_symbols(False): try: symval = symbolic.symbol(symname) symparams[symname] = symval.get() symtypes[symname] = symval.dtype.as_ctypes() except UnboundLocalError: try: symparams[symname] = kwargs[symname] except KeyError: raise UnboundLocalError('Unassigned symbol %s' % symname) arglist.extend( [symparams[k] for k in sorted(symparams.keys()) if k not in sig]) argtypes.extend( [symtypes[k] for k in sorted(symtypes.keys()) if k not in sig]) # Obtain SDFG constants constants = sdfg.constants # Remove symbolic constants from arguments callparams = tuple( (arg, atype) for arg, atype in zip(arglist, argtypes) if not symbolic.issymbolic(arg) or ( hasattr(arg, 'name') and arg.name not in constants)) # Replace symbols with their values callparams = tuple( (atype(symbolic.eval(arg)), atype) if symbolic.issymbolic(arg, constants) else (arg, atype) for arg, atype in callparams) # Replace arrays with their pointers newargs = tuple( (ctypes.c_void_p(arg.__array_interface__['data'][0]), atype) if isinstance(arg, np.ndarray) else (arg, atype) for arg, atype in callparams) newargs = tuple( atype(arg) if (not isinstance(arg, ctypes._SimpleCData)) else arg for arg, atype in newargs) self._lastargs = newargs return self._lastargs
def _construct_args(self, *args, **kwargs): """ Main function that controls argument construction for calling the C prototype of the SDFG. Organizes arguments first by `sdfg.arglist`, then data descriptors by alphabetical order, then symbols by alphabetical order. """ if len(kwargs) > 0 and len(args) > 0: raise AttributeError( 'Compiled SDFGs can only be called with either arguments ' + '(e.g. "program(a,b,c)") or keyword arguments ' + '("program(A=a,B=b)"), but not both') # Argument construction sig = self._sdfg.signature_arglist(with_types=False) typedict = self._sdfg.arglist() if len(kwargs) > 0: # Construct mapping from arguments to signature arglist = [] argtypes = [] argnames = [] for a in sig: try: arglist.append(kwargs[a]) argtypes.append(typedict[a]) argnames.append(a) except KeyError: raise KeyError("Missing program argument \"{}\"".format(a)) elif len(args) > 0: arglist = list(args) argtypes = [typedict[s] for s in sig] argnames = sig sig = [] else: arglist = [] argtypes = [] argnames = [] sig = [] # Type checking for a, arg, atype in zip(argnames, arglist, argtypes): if not isinstance(arg, np.ndarray) and isinstance(atype, dt.Array): raise TypeError( 'Passing an object (type %s) to an array in argument "%s"' % (type(arg).__name__, a)) if isinstance(arg, np.ndarray) and not isinstance(atype, dt.Array): raise TypeError( 'Passing an array to a scalar (type %s) in argument "%s"' % (atype.dtype.ctype, a)) if not isinstance(atype, dt.Array) and not isinstance( arg, atype.dtype.type): print('WARNING: Casting scalar argument "%s" from %s to %s' % (a, type(arg).__name__, atype.dtype.type)) # Retain only the element datatype for upcoming checks and casts argtypes = [t.dtype.type for t in argtypes] sdfg = self._sdfg # As in compilation, add symbols used in array sizes to parameters symparams = {} symtypes = {} for symname in sdfg.undefined_symbols(False): # Ignore arguments (as they may not be symbols but constants, # see below) if symname in sdfg.arg_types: continue try: symval = symbolic.symbol(symname) symparams[symname] = symval.get() symtypes[symname] = symval.dtype.type except UnboundLocalError: try: symparams[symname] = kwargs[symname] except KeyError: raise UnboundLocalError('Unassigned symbol %s' % symname) arglist.extend( [symparams[k] for k in sorted(symparams.keys()) if k not in sig]) argtypes.extend( [symtypes[k] for k in sorted(symtypes.keys()) if k not in sig]) # Obtain SDFG constants constants = sdfg.constants # Remove symbolic constants from arguments callparams = tuple( (arg, atype) for arg, atype in zip(arglist, argtypes) if not symbolic.issymbolic(arg) or ( hasattr(arg, 'name') and arg.name not in constants)) # Replace symbols with their values callparams = tuple( (atype(symbolic.eval(arg)), atype) if symbolic.issymbolic(arg, constants) else (arg, atype) for arg, atype in callparams) # Replace arrays with their pointers newargs = tuple( (ctypes.c_void_p(arg.__array_interface__['data'][0]), atype) if (isinstance(arg, ndarray.ndarray) or isinstance(arg, np.ndarray)) else (arg, atype) for arg, atype in callparams) newargs = tuple(types._FFI_CTYPES[atype](arg) if ( atype in types._FFI_CTYPES and not isinstance(arg, ctypes.c_void_p)) else arg for arg, atype in newargs) self._lastargs = newargs return self._lastargs