def outputC(sympyexpr, output_varname_str, filename="stdout", params="", prestring="", poststring=""): outCparams = parse_outCparams_string(params) preindent = outCparams.preindent TYPE = par.parval_from_str("PRECISION") if outCparams.enable_TYPE == "False": TYPE = "" # Step 0: Initialize # commentblock: comment block containing the input SymPy string, # set only if outCverbose==True # outstring: the output C code string commentblock = "" outstring = "" # Step 1: If SIMD_enable==True, then check if TYPE=="double". If not, error out. # Otherwise set TYPE="REAL_SIMD_ARRAY", which should be #define'd # within the C code. For example for AVX-256, the C code should have # #define REAL_SIMD_ARRAY __m256d if outCparams.SIMD_enable == "True": if not (TYPE == "double" or TYPE == ""): print( "SIMD output currently only supports double precision or typeless. Sorry!" ) exit(1) if TYPE == "double": TYPE = "REAL_SIMD_ARRAY" else: TYPE = "" # Step 2a: Apply sanity checks when either sympyexpr or # output_varname_str is a list. if type(output_varname_str) is list and type(sympyexpr) is not list: print( "Error: Provided a list of output variable names, but only one SymPy expression." ) exit(1) if type(sympyexpr) is list: if type(output_varname_str) is not list: print( "Error: Provided a list of SymPy expressions, but no corresponding list of output variable names" ) exit(1) elif len(output_varname_str) != len(sympyexpr): print("Error: Length of SymPy expressions list (" + str(len(sympyexpr)) + ") != Length of corresponding output variable name list (" + str(len(output_varname_str)) + ")") exit(1) # Step 2b: If sympyexpr and output_varname_str are not lists, # convert them to lists of one element each, to # simplify proceeding code. if type(output_varname_str) is not list and type(sympyexpr) is not list: output_varname_strtmp = [output_varname_str] output_varname_str = output_varname_strtmp sympyexprtmp = [sympyexpr] sympyexpr = sympyexprtmp # Step 3: If outCparams.verbose = True, then output the original SymPy # expression(s) in code comments prior to actual C code if outCparams.outCverbose == "True": commentblock += preindent + "/*\n" + preindent + " * Original SymPy expression" if len(output_varname_str) > 1: commentblock += "s" commentblock += ":\n" for i in range(len(output_varname_str)): if i == 0: if len(output_varname_str) != 1: commentblock += preindent + " * \"[" else: commentblock += preindent + " * \"" else: commentblock += preindent + " * " commentblock += output_varname_str[i] + " = " + str(sympyexpr[i]) if i == len(output_varname_str) - 1: if len(output_varname_str) != 1: commentblock += "]\"\n" else: commentblock += "\"\n" else: commentblock += ",\n" commentblock += preindent + " */\n" # Step 4: Add proper indentation of C code: if outCparams.includebraces == "True": indent = outCparams.preindent + " " else: indent = outCparams.preindent + "" # Step 5: Should the output variable, e.g., outvar, be declared? # If so, start output line with e.g., "double outvar " outtypestring = "" if outCparams.declareoutputvars == "True": outtypestring = outCparams.preindent + indent + TYPE + " " else: outtypestring = outCparams.preindent + indent # Step 6a: If common subexpression elimination (CSE) disabled, then # just output the SymPy string in the most boring way, # nearly consistent with SymPy's ccode() function, # though with support for float & long double types # as well. SIMD_decls = "" if outCparams.CSE_enable == "False": # If CSE is disabled: for i in range(len(sympyexpr)): outstring += outtypestring + ccode_postproc( sp.ccode(sympyexpr[i], output_varname_str[i])) + "\n" # Step 6b: If CSE enabled, then perform CSE using SymPy and then # resulting C code. else: # If CSE is enabled: SIMD_const_varnms = [] SIMD_const_values = [] CSE_results = sp.cse(sympyexpr, sp.numbered_symbols(outCparams.CSE_varprefix), order='canonical') for commonsubexpression in CSE_results[0]: FULLTYPESTRING = "const " + TYPE + " " if outCparams.enable_TYPE == "False": FULLTYPESTRING = "" if outCparams.SIMD_enable == "True": outstring += outCparams.preindent + indent + FULLTYPESTRING + str(commonsubexpression[0]) + " = " + \ str(expr_convert_to_SIMD_intrins(commonsubexpression[1],SIMD_const_varnms,SIMD_const_values,outCparams.SIMD_debug)) + ";\n" else: outstring += outCparams.preindent + indent + FULLTYPESTRING + ccode_postproc( sp.ccode(commonsubexpression[1], commonsubexpression[0])) + "\n" for i, result in enumerate(CSE_results[1]): if outCparams.SIMD_enable == "True": outstring += outtypestring + output_varname_str[i] + " = " + \ str(expr_convert_to_SIMD_intrins(result,SIMD_const_varnms,SIMD_const_values,outCparams.SIMD_debug)) + ";\n" else: outstring += outtypestring + ccode_postproc( sp.ccode(result, output_varname_str[i])) + "\n" # Step 6b.i: If SIMD_enable == True , and # there is at least one SIMD const variable, # then declare the SIMD_const_varnms and SIMD_const_values arrays if outCparams.SIMD_enable == "True" and len(SIMD_const_varnms) != 0: # Step 6a) Sort the list of definitions. Idea from: # https://stackoverflow.com/questions/9764298/is-it-possible-to-sort-two-listswhich-reference-each-other-in-the-exact-same-w SIMD_const_varnms, SIMD_const_values = \ (list(t) for t in zip(*sorted(zip(SIMD_const_varnms, SIMD_const_values)))) # Step 6b) Remove duplicates uniq_varnms = superfast_uniq(SIMD_const_varnms) uniq_values = superfast_uniq(SIMD_const_values) SIMD_const_varnms = uniq_varnms SIMD_const_values = uniq_values if len(SIMD_const_varnms) != len(SIMD_const_values): print( "Error: SIMD constant declaration arrays SIMD_const_varnms[] and SIMD_const_values[] have inconsistent sizes!" ) exit(1) for i in range(len(SIMD_const_varnms)): if outCparams.enable_TYPE == "False": SIMD_decls += outCparams.preindent + indent + SIMD_const_varnms[ i] + " = " + SIMD_const_values[i] + ";" else: SIMD_decls += outCparams.preindent + indent + "const double " + outCparams.CSE_varprefix + SIMD_const_varnms[ i] + " = " + SIMD_const_values[i] + ";\n" SIMD_decls += outCparams.preindent + indent + "const REAL_SIMD_ARRAY " + SIMD_const_varnms[ i] + " = ConstSIMD(" + outCparams.CSE_varprefix + SIMD_const_varnms[ i] + ");\n" SIMD_decls += "\n" # Step 7: Construct final output string final_Ccode_output_str = commentblock # Step 7a: Output C code in indented curly brackets if # outCparams.includebraces = True if outCparams.includebraces == "True": final_Ccode_output_str += outCparams.preindent + "{\n" final_Ccode_output_str += prestring + SIMD_decls + outstring + poststring if outCparams.includebraces == "True": final_Ccode_output_str += outCparams.preindent + "}\n" # Step 8: If filename == "stdout", then output # C code to standard out (useful for copy-paste or interactive # mode). Otherwise output to file specified in variable name. if filename == "stdout": # Output to standard out (stdout; "the screen") print(final_Ccode_output_str) elif filename == "returnstring": return final_Ccode_output_str else: # Output to the file specified by the function input parameter string 'filename': with open(filename, outCparams.outCfileaccess) as file: file.write(final_Ccode_output_str) successstr = "" if outCparams.outCfileaccess == "a": successstr = "Appended " elif outCparams.outCfileaccess == "w": successstr = "Wrote " print(successstr + "to file \"" + filename + "\"")
def outputC(sympyexpr, output_varname_str, filename = "stdout", params = "", prestring = "", poststring = ""): outCparams = parse_outCparams_string(params) preindent = outCparams.preindent TYPE = par.parval_from_str("PRECISION") if outCparams.enable_TYPE == "False": TYPE = "" # Step 0: Initialize # commentblock: comment block containing the input SymPy string, # set only if outCverbose==True # outstring: the output C code string commentblock = "" outstring = "" # Step 1: If enable_SIMD==True, then check if TYPE=="double". If not, error out. # Otherwise set TYPE="REAL_SIMD_ARRAY", which should be #define'd # within the C code. For example for AVX-256, the C code should have # #define REAL_SIMD_ARRAY __m256d if outCparams.enable_SIMD == "True": if TYPE not in ('double', ''): print("SIMD output currently only supports double precision or typeless. Sorry!") sys.exit(1) if TYPE == "double": TYPE = "REAL_SIMD_ARRAY" # Step 2a: Apply sanity checks when either sympyexpr or # output_varname_str is a list. if isinstance(output_varname_str, list) and not isinstance(sympyexpr, list): print("Error: Provided a list of output variable names, but only one SymPy expression.") sys.exit(1) if isinstance(sympyexpr, list): if not isinstance(output_varname_str, list): print("Error: Provided a list of SymPy expressions, but no corresponding list of output variable names") sys.exit(1) elif len(output_varname_str) != len(sympyexpr): print("Error: Length of SymPy expressions list ("+str(len(sympyexpr))+ ") != Length of corresponding output variable name list ("+str(len(output_varname_str))+")") sys.exit(1) # Step 2b: If sympyexpr and output_varname_str are not lists, # convert them to lists of one element each, to # simplify proceeding code. if not isinstance(output_varname_str, list) and not isinstance(sympyexpr, list): output_varname_strtmp = [output_varname_str] output_varname_str = output_varname_strtmp sympyexprtmp = [sympyexpr] sympyexpr = sympyexprtmp sympyexpr = sympyexpr[:] # pass-by-value (copy list) # Step 3: If outCparams.verbose = True, then output the original SymPy # expression(s) in code comments prior to actual C code if outCparams.outCverbose == "True": commentblock += preindent+"/*\n"+preindent+" * Original SymPy expression" if len(output_varname_str)>1: commentblock += "s" commentblock += ":\n" for i, varname in enumerate(output_varname_str): if i == 0: if len(output_varname_str) != 1: commentblock += preindent+" * \"[" else: commentblock += preindent+" * \"" else: commentblock += preindent+" * " commentblock += varname + " = " + str(sympyexpr[i]) if i == len(output_varname_str)-1: if len(output_varname_str) != 1: commentblock += "]\"\n" else: commentblock += "\"\n" else: commentblock += ",\n" commentblock += preindent+" */\n" # Step 4: Add proper indentation of C code: if outCparams.includebraces == "True": indent = outCparams.preindent+" " else: indent = outCparams.preindent+"" # Step 5: Should the output variable, e.g., outvar, be declared? # If so, start output line with e.g., "double outvar " outtypestring = "" if outCparams.declareoutputvars == "True": outtypestring = indent+TYPE + " " else: outtypestring = indent # Step 6a: If common subexpression elimination (CSE) disabled, then # just output the SymPy string in the most boring way, # nearly consistent with SymPy's ccode() function, # though with support for float & long double types # as well. SIMD_RATIONAL_decls = RATIONAL_decls = "" if outCparams.CSE_enable == "False": # If CSE is disabled: for i in range(len(sympyexpr)): outstring += outtypestring + ccode_postproc(sp.ccode(sympyexpr[i], output_varname_str[i], user_functions=custom_functions_for_SymPy_ccode))+"\n" # Step 6b: If CSE enabled, then perform CSE using SymPy and then # resulting C code. else: # If CSE is enabled: SIMD_const_varnms = [] SIMD_const_values = [] varprefix = '' if outCparams.CSE_varprefix == 'tmp' else outCparams.CSE_varprefix if outCparams.CSE_preprocess == "True" or outCparams.enable_SIMD == "True": # If CSE_preprocess == True, then perform partial factorization # If enable_SIMD == True, then declare _NegativeOne_ in preprocessing factor_negative = eval(outCparams.enable_SIMD) and eval(outCparams.SIMD_find_more_subs) sympyexpr, map_sym_to_rat = cse_preprocess(sympyexpr, prefix=varprefix, declare=eval(outCparams.enable_SIMD), negative=factor_negative, factor=eval(outCparams.CSE_preprocess)) for v in map_sym_to_rat: p, q = float(map_sym_to_rat[v].p), float(map_sym_to_rat[v].q) if outCparams.enable_SIMD == "False": RATIONAL_decls += indent + 'const double ' + str(v) + ' = ' # Since Integer is a subclass of Rational in SymPy, we need only check whether # the denominator q = 1 to determine if a rational is an integer. if q != 1: RATIONAL_decls += str(p) + '/' + str(q) + ';\n' else: RATIONAL_decls += str(p) + ';\n' sympy_version = sp.__version__.replace('rc', '...').replace('b', '...') sympy_major_version = int(sympy_version.split(".")[0]) sympy_minor_version = int(sympy_version.split(".")[1]) if sympy_major_version < 1 or (sympy_major_version == 1 and sympy_minor_version < 3): print('Warning: SymPy version', sympy_version, 'does not support CSE postprocessing.') CSE_results = sp.cse(sympyexpr, sp.numbered_symbols(outCparams.CSE_varprefix + '_'), order=outCparams.CSE_sorting) else: CSE_results = cse_postprocess(sp.cse(sympyexpr, sp.numbered_symbols(outCparams.CSE_varprefix + '_'), order=outCparams.CSE_sorting)) for commonsubexpression in CSE_results[0]: FULLTYPESTRING = "const " + TYPE + " " if outCparams.enable_TYPE == "False": FULLTYPESTRING = "" if outCparams.enable_SIMD == "True": outstring += indent + FULLTYPESTRING + str(commonsubexpression[0]) + " = " + \ str(expr_convert_to_SIMD_intrins(commonsubexpression[1],map_sym_to_rat,varprefix,outCparams.SIMD_find_more_FMAsFMSs)) + ";\n" else: outstring += indent + FULLTYPESTRING + ccode_postproc(sp.ccode(commonsubexpression[1], commonsubexpression[0], user_functions=custom_functions_for_SymPy_ccode)) + "\n" for i, result in enumerate(CSE_results[1]): if outCparams.enable_SIMD == "True": outstring += outtypestring + output_varname_str[i] + " = " + \ str(expr_convert_to_SIMD_intrins(result,map_sym_to_rat,varprefix,outCparams.SIMD_find_more_FMAsFMSs)) + ";\n" else: outstring += outtypestring+ccode_postproc(sp.ccode(result,output_varname_str[i], user_functions=custom_functions_for_SymPy_ccode))+"\n" # Complication: SIMD functions require numerical constants to be stored in SIMD arrays # Resolution: This function extends lists "SIMD_const_varnms" and "SIMD_const_values", # which store the name of each constant SIMD array (e.g., _Integer_1) and # the value of each variable (e.g., 1.0). if outCparams.enable_SIMD == "True": for v in map_sym_to_rat: p, q = float(map_sym_to_rat[v].p), float(map_sym_to_rat[v].q) SIMD_const_varnms.extend([str(v)]) if q != 1: SIMD_const_values.extend([str(p) + '/' + str(q)]) else: SIMD_const_values.extend([str(p)]) # Step 6b.i: If enable_SIMD == True , and # there is at least one SIMD const variable, # then declare the SIMD_const_varnms and SIMD_const_values arrays if outCparams.enable_SIMD == "True" and len(SIMD_const_varnms) != 0: # Step 6a) Sort the list of definitions. Idea from: # https://stackoverflow.com/questions/9764298/is-it-possible-to-sort-two-listswhich-reference-each-other-in-the-exact-same-w SIMD_const_varnms, SIMD_const_values = \ (list(t) for t in zip(*sorted(zip(SIMD_const_varnms, SIMD_const_values)))) # Step 6b) Remove duplicates uniq_varnms = superfast_uniq(SIMD_const_varnms) uniq_values = superfast_uniq(SIMD_const_values) SIMD_const_varnms = uniq_varnms SIMD_const_values = uniq_values if len(SIMD_const_varnms) != len(SIMD_const_values): print("Error: SIMD constant declaration arrays SIMD_const_varnms[] and SIMD_const_values[] have inconsistent sizes!") sys.exit(1) for i in range(len(SIMD_const_varnms)): if outCparams.enable_TYPE == "False": SIMD_RATIONAL_decls += indent + SIMD_const_varnms[i] + " = " + SIMD_const_values[i]+";" else: SIMD_RATIONAL_decls += indent + "const double " + "tmp" + SIMD_const_varnms[i] + " = " + SIMD_const_values[i] + ";\n" SIMD_RATIONAL_decls += indent + "const REAL_SIMD_ARRAY " + SIMD_const_varnms[i] + " = ConstSIMD(" + "tmp" + SIMD_const_varnms[i] + ");\n" SIMD_RATIONAL_decls += "\n" # Step 7: Construct final output string final_Ccode_output_str = commentblock # Step 7a: Output C code in indented curly brackets if # outCparams.includebraces = True if outCparams.includebraces == "True": final_Ccode_output_str += outCparams.preindent+"{\n" final_Ccode_output_str += prestring + RATIONAL_decls + SIMD_RATIONAL_decls + outstring + poststring if outCparams.includebraces == "True": final_Ccode_output_str += outCparams.preindent+"}\n" # Step 8: If filename == "stdout", then output # C code to standard out (useful for copy-paste or interactive # mode). Otherwise output to file specified in variable name. if filename == "stdout": # Output to standard out (stdout; "the screen") print(final_Ccode_output_str) elif filename == "returnstring": return final_Ccode_output_str else: # Output to the file specified by the function input parameter string 'filename': with open(filename, outCparams.outCfileaccess) as file: file.write(final_Ccode_output_str) successstr = "" if outCparams.outCfileaccess == "a": successstr = "Appended " elif outCparams.outCfileaccess == "w": successstr = "Wrote " print(successstr + "to file \"" + filename + "\"")