def generate_const_variants( prefix, argument_list, template_map ): print "Const variant generation prefix: ", prefix print "Generating const variants for ", argument_list permute_indices = [] result = [] comments = [] # use this arg list may contain a modified version of the # argument list, where in some overruled cases the 'const ' # will be removed from the argument (force permutation) use_this_arg_list = [] for i in range( 0, len(argument_list) ): argument = argument_list[i] if 'const' not in argument and \ 'char' not in argument and \ 'ptrdiff_t' not in argument and \ 'typename' not in argument and \ '$LIBRARY_INT_TYPE' not in argument and \ 'fortran_bool_t' not in argument and \ 'Vector' not in argument and \ 'Matrix' not in argument and \ '&' in argument: permute_indices.append( i ) else: arg_name = argument.split( ' ' )[ -1 ].upper() my_key = prefix + '.' + arg_name + '.level2_permute' if netlib.my_has_key( my_key, template_map ): permute_indices.append( i ) argument = argument.replace( 'const ', '' ) use_this_arg_list.append( argument ) print " To be permuted: ", permute_indices for i in range( 0, pow( 2, len( permute_indices ) ) ): #print "i: ", i new_arg_list = [] new_arg_list += use_this_arg_list new_comments = [] for j in range( 0, len( permute_indices ) ): if ( i & (1<<j) ): #print permute_indices[j], ": const " + use_this_arg_list[ permute_indices[ j ] ] new_arg_list[ permute_indices[ j ] ] = "const " + use_this_arg_list[ permute_indices[ j ] ] arg = new_arg_list[ permute_indices[ j ] ] new_comments.append( "// * " + arg[ :arg.find("&" ) ] + "&" ) else: arg = new_arg_list[ permute_indices[ j ] ] new_comments.append( "// * " + arg[ :arg.find("&" ) ] + "&" ) # else: #print permute_indices[j], "don't add const" if new_arg_list not in result: result.append( new_arg_list ) comments.append( new_comments ) #new_arg_list = [] #print "result: ", result return result, comments
def generate_const_variants(prefix, argument_list, template_map): print "Const variant generation prefix: ", prefix print "Generating const variants for ", argument_list permute_indices = [] result = [] comments = [] # use this arg list may contain a modified version of the # argument list, where in some overruled cases the 'const ' # will be removed from the argument (force permutation) use_this_arg_list = [] for i in range(0, len(argument_list)): argument = argument_list[i] if 'const' not in argument and \ 'char' not in argument and \ 'ptrdiff_t' not in argument and \ 'typename' not in argument and \ '$LIBRARY_INT_TYPE' not in argument and \ 'fortran_bool_t' not in argument and \ 'Vector' not in argument and \ 'Matrix' not in argument and \ '&' in argument: permute_indices.append(i) else: arg_name = argument.split(' ')[-1].upper() my_key = prefix + '.' + arg_name + '.level2_permute' if netlib.my_has_key(my_key, template_map): permute_indices.append(i) argument = argument.replace('const ', '') use_this_arg_list.append(argument) print " To be permuted: ", permute_indices for i in range(0, pow(2, len(permute_indices))): #print "i: ", i new_arg_list = [] new_arg_list += use_this_arg_list new_comments = [] for j in range(0, len(permute_indices)): if (i & (1 << j)): #print permute_indices[j], ": const " + use_this_arg_list[ permute_indices[ j ] ] new_arg_list[permute_indices[ j]] = "const " + use_this_arg_list[permute_indices[j]] arg = new_arg_list[permute_indices[j]] new_comments.append("// * " + arg[:arg.find("&")] + "&") else: arg = new_arg_list[permute_indices[j]] new_comments.append("// * " + arg[:arg.find("&")] + "&") # else: #print permute_indices[j], "don't add const" if new_arg_list not in result: result.append(new_arg_list) comments.append(new_comments) #new_arg_list = [] #print "result: ", result return result, comments
def parse_file( filename, info_map, template_map ): pp = pprint.PrettyPrinter( indent = 2 ) source = open( filename ).read() for match in re.compile( '^(cuComplex|cuDoubleComplex|float|double|void|int) ?CUBLASAPI ?cublas([SDCZI][a-z0-9]+) ?\(([^\)]+)\)', re.M | re.S ).findall( source ): print "----" result_type = match[0] blas_routine = match[1].upper().strip() print "CUBLAS routine:", match[1], " BLAS equivalent:", blas_routine arguments = {} for arg in match[2].replace('\n','').split( ',' ): arg = arg.strip() arg_name = arg.split( " " )[-1].replace( "*", "" ).strip().upper() arguments[ arg_name ] = {} arguments[ arg_name ][ "original" ] = arg arguments[ arg_name ][ "pointer" ] = "*" in arg pp.pprint( arguments ) if blas_routine in info_map: print "Found ", blas_routine, " in Fortran info_map." info_map[ blas_routine ][ "cublas_routine" ] = 'cublas' + match[1] #pp.pprint( info_map[ blas_routine ] ) # read aliases, if they are there my_key = info_map[ blas_routine ][ 'group_name' ].lower() + '.all.cblas_alias' alias_map = {} print my_key if netlib.my_has_key( my_key, template_map ) != None: #print "Has key.." for line in template_map[ netlib.my_has_key( my_key, template_map ) ].splitlines(): #print "Line:", line alias_map[ line.split( "," )[0] ] = line.split(",")[1] for arg in info_map[ blas_routine ][ 'argument_map' ]: cublas_arg = '' if arg in arguments: cublas_arg = arg elif 'S' + arg in arguments: cublas_arg = 'S' + arg # E.g., BLAS DPARAM equals CUBLAS SPARAM elif 'S' + arg[1:] in arguments and arg == 'DPARAM': cublas_arg = 'S' + arg[1:] elif 'C' + arg in arguments: cublas_arg = 'C' + arg elif arg in alias_map: if alias_map[ arg ] in arguments: cublas_arg = alias_map[ arg ] print "Looking for BLAS argument ", arg, " CUBLAS equivalent: ", cublas_arg if cublas_arg in arguments: print "Found matching argument, inserting call_cublas_header stuff" call_cublas_header = info_map[ blas_routine ][ "argument_map" ][ arg ][ "code" ][ "call_blas_header" ] print "Original: ", call_cublas_header if not arguments[ cublas_arg ][ "pointer" ]: call_cublas_header = call_cublas_header.replace( "&", "" ) call_cublas_header = call_cublas_header.replace( "complex_ptr", "void_ptr" ); info_map[ blas_routine ][ "argument_map" ][ arg ][ "code" ][ "call_cublas_header" ] = call_cublas_header else: print "Could not match cublas argument '" + cublas_arg + "' to known arguments." print arg, arguments exit(0)
def parse_file( filename, info_map, template_map ): parser_mode = template_map[ 'PARSERMODE' ] prefix_map = { 'BLAS': 'cblas_', 'LAPACK': 'clapack_' } prefix = prefix_map[ parser_mode ] print prefix pp = pprint.PrettyPrinter( indent = 2 ) source = open( filename ).read() for match in re.compile( '(void|float|int|double|CBLAS_INDEX) +' + prefix + '([^\(]+)\(([^\)]+)\)', re.M | re.S ).findall( source ): print "----" result_type = match[0] fortran_routine = match[1].split("_sub")[0].upper().strip() c_routine = prefix + match[1] print "C" + parser_mode + " routine:", c_routine , " " + parser_mode + " equivalent:", fortran_routine arguments = {} for arg in match[2].replace('\n','').split( ',' ): arg = arg.strip() arg_name = arg.split( " " )[-1].replace( "*", "" ).strip().upper() arguments[ arg_name ] = {} arguments[ arg_name ][ "original" ] = arg arguments[ arg_name ][ "pointer" ] = "*" in arg pp.pprint( arguments ) if fortran_routine in info_map: print "Found ", fortran_routine, " in Fortran info_map." info_map[ fortran_routine ][ prefix + "routine" ] = c_routine # read aliases, if they are there my_key = info_map[ fortran_routine ][ 'group_name' ].lower() + '.all.cblas_alias' alias_map = {} #print my_key if netlib.my_has_key( my_key, template_map ) != None: #print "Has key.." for line in template_map[ netlib.my_has_key( my_key, template_map ) ].splitlines(): #print "Line:", line alias_map[ line.split( "," )[0] ] = line.split(",")[1] #print alias_map # Try to match and insert arguments # argument_map is the data gathered through the Fortran interface for arg in info_map[ fortran_routine ][ 'argument_map' ]: cblas_arg = '' if arg in arguments: cblas_arg = arg elif arg in alias_map: if alias_map[ arg ] in arguments: cblas_arg = alias_map[ arg ] print "Looking for " + parser_mode + " argument ", arg, " CBLAS equivalent: ", cblas_arg if cblas_arg in arguments: print "Found matching argument, inserting call_cblas_header stuff" call_cblas_header = info_map[ fortran_routine ][ "argument_map" ][ arg ][ "code" ][ "call_blas_header" ] print "Original: ", call_cblas_header if not arguments[ cblas_arg ][ "pointer" ]: call_cblas_header = call_cblas_header.replace( "&", "" ) call_cblas_header = call_cblas_header.replace( "complex_ptr", "void_ptr" ); print "Result: ", call_cblas_header if arg == 'UPLO': info_map[ fortran_routine ][ "argument_map" ][ arg ][ "code" ][ "call_" + prefix + "header" ] = \ prefix + "option< UpLo >::value" elif arg == 'DIAG': info_map[ fortran_routine ][ "argument_map" ][ arg ][ "code" ][ "call_" + prefix + "header" ] = \ prefix + "option< Diag >::value" elif arg == 'SIDE': info_map[ fortran_routine ][ "argument_map" ][ arg ][ "code" ][ "call_" + prefix + "header" ] = \ prefix + "option< " + netlib.template_parameter[ arg ] + " >::value" elif arg == 'TRANS' or arg == 'TRANSA' or arg == 'TRANSB': info_map[ fortran_routine ][ "argument_map" ][ arg ][ "code" ][ "call_" + prefix + "header" ] = \ prefix + "option< " + netlib.template_parameter[ arg ] + " >::value" else: info_map[ fortran_routine ][ "argument_map" ][ arg ][ "code" ][ "call_" + prefix + "header" ] = call_cblas_header else: if arg == 'INFO' and result_type == 'int': info_map[ fortran_routine ][ "argument_map" ][ arg ][ "code" ][ "call_" + prefix + "header" ] = None print "INFO is the return type, adding it with code None" elif arg == 'WORK' or arg == 'LWORK': info_map[ fortran_routine ][ "argument_map" ][ arg ][ "code" ][ "call_" + prefix + "header" ] = None info_map[ fortran_routine ][ "clapack_disable_workspace" ] = True else: exit(0) if "ORDER" in arguments: print "Adding order argument." info_map[ fortran_routine ][ "has_" + prefix + "order_arg" ] = True else: print "Not adding order argument." info_map[ fortran_routine ][ "has_" + prefix + "order_arg" ] = False
def write_functions(info_map, group, template_map, base_dir): # # group.keys() is a vector of different grouped function names # like gees, dgesv, etc. # for group_name, subroutines in group.iteritems(): filename = group_name.lower() + '.hpp' includes = [ #'#include <boost/numeric/bindings/detail/void_ptr.hpp>', #'#include <boost/numeric/bindings/traits/traits.hpp>', #'#include <boost/numeric/bindings/traits/type_traits.hpp>', '#include <boost/numeric/bindings/remove_imaginary.hpp>', '#include <boost/numeric/bindings/is_mutable.hpp>', '#include <boost/numeric/bindings/value_type.hpp>', '#include <boost/numeric/bindings/stride.hpp>', '#include <boost/numeric/bindings/size.hpp>', '#include <boost/numeric/bindings/begin.hpp>', #'#include <boost/mpl/bool.hpp>', '#include <boost/type_traits/is_same.hpp>', '#include <boost/type_traits/remove_const.hpp>', '#include <boost/static_assert.hpp>', '#include <boost/assert.hpp>', ] for subroutine in subroutines: group_name_l = info_map[subroutine]['group_name'].lower() if template_map.has_key(group_name_l + '.includes'): includes += template_map[group_name_l + '.includes'].splitlines() # # LEVEL 0 HANDLING # overloads = template_map['backend_blas_overloads'] for select_backend in [ 'blas_overloads', 'cblas_overloads', 'cublas_overloads' ]: sub_overloads = '' for subroutine in subroutines: group_name_l = info_map[subroutine]['group_name'].lower() # stuff like float, complex<double>, etc. subroutine_value_type = documentation. \ subroutine_value_type[ ",".join( [ info_map[ subroutine ][ 'value_type' ], info_map[ subroutine ][ 'precision' ] ] ) ] sub_template = template_map[select_backend] # add the argument list here arg_list = [] typename_list = [] blas_arg_list = [] cblas_arg_list = [] cublas_arg_list = [] level0_static_asserts = [] check_for_unused = [] for arg in info_map[subroutine]['arguments']: print "Subroutine ", subroutine, " arg ", arg arg_list += [ info_map[subroutine]['argument_map'][arg]['code'] ['level_0'] ] blas_arg_list += [ info_map[subroutine]['argument_map'][arg]['code'] ['call_blas_header'] ] # # Find potential arguments that may cause warnings because they are not used, and # store these in check_for_unused # if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0' ] != None and \ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0_typename' ] != None: keyword = info_map[subroutine]['argument_map'][arg][ 'code']['level_0'].split(' ')[-1] check_for_unused += [keyword] if 'call_cblas_header' in info_map[subroutine][ 'argument_map'][arg]['code']: cblas_arg_list += [ info_map[subroutine]['argument_map'][arg]['code'] ['call_cblas_header'] ] else: print "WARNING: couldn't find cblas call." if info_map[subroutine]['argument_map'][arg]['code'][ 'level_0_typename'] != None: typename_list += [ info_map[subroutine]['argument_map'][arg]['code'] ['level_0_typename'] ] if "has_cblas_order_arg" in info_map[subroutine]: check_for_unused.append('order') if info_map[subroutine]["has_cblas_order_arg"] == True: arg_list.insert(0, "const Order order") cblas_arg_list.insert(0, "cblas_option< Order >::value") typename_list.insert(0, "typename Order") level0_static_asserts.append( "BOOST_STATIC_ASSERT( (is_same<Order, tag::column_major>::value) );" ) includes += [ "#include <boost/type_traits/is_same.hpp>" ] sub_template = sub_template.replace("$TYPES", ", ".join(typename_list)) sub_template = sub_template.replace("template< >\n", "") sub_template = sub_template.replace("$LEVEL0", ", ".join(arg_list)) sub_template = sub_template.replace("$CALL_BLAS_HEADER", ", ".join(blas_arg_list)) sub_template = sub_template.replace("$CALL_CBLAS_HEADER", ", ".join(cblas_arg_list)) sub_template = sub_template.replace("$SUBROUTINE", subroutine) sub_template = sub_template.replace("$SPECIALIZATION", subroutine_value_type) sub_template = sub_template.replace( '$STATIC_ASSERTS', "\n ".join(level0_static_asserts)) if select_backend == 'blas_overloads': sub_template = sub_template.replace( '$LIBRARY_INT_TYPE', "fortran_int_t") else: # the C integer-type defaults to 'int' sub_template = sub_template.replace( '$LIBRARY_INT_TYPE', "int") # CBLAS stuff if 'cblas_routine' in info_map[subroutine]: cblas_routine = info_map[subroutine]['cblas_routine'] else: cblas_routine = '// NOT FOUND' sub_template = sub_template.replace("$CBLAS_ROUTINE", cblas_routine) # CUBLAS stuff if 'cublas_routine' in info_map[subroutine]: cublas_routine = info_map[subroutine]['cublas_routine'] for arg in info_map[subroutine]['arguments']: cublas_arg_list += [ info_map[subroutine]['argument_map'][arg]['code'] ['call_cublas_header'] ] else: cublas_routine = '// NOT FOUND' # # Count potentially unused arguments. If the count is 1; it is only present in the # parameter list. In that case, the argument may be removed from the code # print "Check for unused:", check_for_unused for parameter in check_for_unused: if sub_template.count(parameter) == 1: sub_template = sub_template.replace( ' ' + parameter, '') sub_template = sub_template.replace("$CALL_CUBLAS_HEADER", ", ".join(cublas_arg_list)) sub_template = sub_template.replace("$CUBLAS_ROUTINE", cublas_routine) sub_template = sub_template.replace('$groupname', group_name.lower()) sub_template = sub_template.replace( '$RESULT_TYPE', info_map[subroutine]['return_value_type']) sub_template = sub_template.replace( '$RETURN_STATEMENT', info_map[subroutine]['return_statement']) sub_template = bindings.search_replace( sub_template, group_name_l + '.level0.gsub', template_map) sub_overloads += bindings.proper_indent(sub_template) overloads = overloads.replace('$' + select_backend.upper(), sub_overloads) # # Prepare for levels 1 and 2 # cases = {} # first, see what kind of functions we have # needed for argument check etc. for subroutine in subroutines: if info_map[subroutine]['value_type'] == 'real': if not cases.has_key('real'): cases['real'] = {} cases['real']['subroutines'] = [] cases['real']['subroutines'] += [subroutine] if info_map[subroutine]['value_type'][:7] == 'complex': if not cases.has_key('complex'): cases['complex'] = {} cases['complex']['subroutines'] = [] cases['complex']['subroutines'] += [subroutine] # # LEVEL 1 and 2 HANDLING # level1_map = {} level2_map = {} for value_type, case_map in cases.iteritems(): level1_template = '' level2_template = '' level1_template = template_map['blas_level1'] level2_template = template_map['blas_level2'] subroutine = case_map['subroutines'][0] group_name_l = info_map[subroutine]['group_name'].lower() # take this subroutine for arguments etc. # (last entry -> complex version if available, which can be important) subroutine = subroutines[-1] # include templates come before anything else; they can hold any # $ID my_include_key = group_name_l + '.' + value_type + '.include_templates' if netlib.my_has_key(my_include_key, template_map): include_template_list = template_map[netlib.my_has_key( my_include_key, template_map)].strip().replace(' ', '').split(",") include_templates = '' for template in include_template_list: include_templates += template_map['template_' + template] level1_template = level1_template.replace( '$INCLUDE_TEMPLATES', bindings.proper_indent(include_templates)) level2_template = level2_template.replace( '$INCLUDE_TEMPLATES', bindings.proper_indent(include_templates)) else: level1_template = level1_template.replace( '\n$INCLUDE_TEMPLATES', '') level2_template = level2_template.replace( '\n$INCLUDE_TEMPLATES', '') level0_arg_list = [] level1_arg_list = [] level2_arg_list = [] call_level1_arg_list = [] level1_type_arg_list = [] level1_assert_list = [] level1_static_assert_list = [] keyword_type_list = [] typedef_list = [] # # Are we dealing with a transpose option here? # Because CBLAS allows to pass the order of the matrices, here we # inject code that determines the default data order. # if 'matrix' in info_map[subroutine]['grouped_arguments'][ 'by_type']: has_trans = False matrix_wo_trans = [] matrix_with_trans = [] for matrix_arg in info_map[subroutine]['grouped_arguments'][ 'by_type']['matrix']: if 'ref_trans' in info_map[subroutine]['argument_map'][ matrix_arg]: has_trans = True matrix_type = info_map[subroutine]['argument_map'][ matrix_arg]['code']['level_1_static_assert'] matrix_with_trans += [matrix_type] else: matrix_wo_trans.append( info_map[subroutine]['argument_map'][matrix_arg] ['code']['level_1_static_assert']) # # Matrices have trans options in this case. If there is one without, # that one will determine the order of the call # if has_trans: includes += [ '#include <boost/numeric/bindings/trans_tag.hpp>' ] if len(matrix_wo_trans) > 0: typedef_list.insert( 0, 'typedef typename result_of::data_order< ' + matrix_wo_trans[0] + \ ' >::type order;' ) includes += [ '#include <boost/numeric/bindings/data_order.hpp>' ] else: typedef_list.insert( 0, 'typedef typename detail::default_order< ' + matrix_with_trans[0] + \ ' >::type order;' ) includes += [ '#include <boost/numeric/bindings/blas/detail/default_order.hpp>' ] else: # so, there's no trans option # but, what if there's an order? (e.g., syr) -- then use ` if "has_cblas_order_arg" in info_map[subroutine]: typedef_list.insert( 0, 'typedef typename result_of::data_order< ' + matrix_wo_trans[0] + \ ' >::type order;' ) includes += [ '#include <boost/numeric/bindings/data_order.hpp>' ] # # Add an include in case of the uplo or diag options # if 'UPLO' in info_map[subroutine]['arguments']: includes += ['#include <boost/numeric/bindings/uplo_tag.hpp>'] if 'DIAG' in info_map[subroutine]['arguments']: includes += ['#include <boost/numeric/bindings/diag_tag.hpp>'] # # Create static assertions, first by value type # has_comment = False for value_type_tmp_key in info_map[subroutine][ 'grouped_arguments']['by_value_type'].keys(): # look up whether they are template params static_asserts = [] for arg in info_map[subroutine]['grouped_arguments'][ 'by_value_type'][value_type_tmp_key]: if info_map[subroutine]['argument_map'][arg]['code'][ 'level_1_type'] != None: static_asserts.append(arg) if len(static_asserts) > 1: arg_A = static_asserts[0] for arg_B in static_asserts[1:]: print "Adding static assert for argA", arg_A, " argb", arg_B arg_left = info_map[subroutine]['argument_map'][arg_A][ 'code']['level_1_static_assert'] arg_right = info_map[subroutine]['argument_map'][ arg_B]['code']['level_1_static_assert'] if arg_left != None and arg_right != None: assert_line = 'BOOST_STATIC_ASSERT( (is_same< ' + \ 'typename remove_const< typename $NAMESPACEvalue_type< ' + arg_left + ' >::type >::type, ' + \ 'typename remove_const< typename $NAMESPACEvalue_type< ' + arg_right + ' >::type >::type' \ ' >::value) );' if not has_comment: #level1_static_assert_list += [ '// Here, we assert... ' ] has_comment = True level1_static_assert_list += [assert_line] # # Matrices should adhere to their storage scheme # if 'matrix' in info_map[subroutine]['grouped_arguments'][ 'by_type']: for matrix_id in info_map[subroutine]['grouped_arguments'][ 'by_type']['matrix']: info_map_item = info_map[subroutine]['argument_map'][ matrix_id] if 'banded' in info_map_item and info_map_item[ 'banded' ] == True and \ info_map_item[ 'code' ][ 'level_1_type' ] != None: assert_line = 'BOOST_STATIC_ASSERT( ($NAMESPACEhas_band_array< ' + info_map_item[ 'code' ][ 'level_1_static_assert' ] + \ ' >::value) );' level1_static_assert_list += [assert_line] includes += [ "#include <boost/numeric/bindings/has_band_array.hpp>" ] elif 'packed' in info_map_item and info_map_item[ 'packed' ] == True and \ info_map_item[ 'code' ][ 'level_1_type' ] != None: assert_line = 'BOOST_STATIC_ASSERT( ($NAMESPACEhas_triangular_array< ' + info_map_item[ 'code' ][ 'level_1_static_assert' ] + \ ' >::value) );' level1_static_assert_list += [assert_line] includes += [ "#include <boost/numeric/bindings/has_triangular_array.hpp>" ] elif info_map_item['code']['level_1_type'] != None: assert_line = 'BOOST_STATIC_ASSERT( ($NAMESPACEhas_linear_array< ' + info_map_item[ 'code' ][ 'level_1_static_assert' ] + \ ' >::value) );' level1_static_assert_list += [assert_line] includes += [ "#include <boost/numeric/bindings/has_linear_array.hpp>" ] # # Vectors should have linear arrays # if 'vector' in info_map[subroutine]['grouped_arguments'][ 'by_type']: for vector_id in info_map[subroutine]['grouped_arguments'][ 'by_type']['vector']: info_map_item = info_map[subroutine]['argument_map'][ vector_id] if 'ref_stride' in info_map_item and info_map_item['code'][ 'level_1_type'] != None: assert_line = 'BOOST_STATIC_ASSERT( ($NAMESPACEhas_linear_array< ' + info_map_item[ 'code' ][ 'level_1_static_assert' ] + \ ' >::value) );' level1_static_assert_list += [assert_line] includes += [ "#include <boost/numeric/bindings/has_linear_array.hpp>" ] # Make sure the mutable stuff is mutable if 'output' in info_map[subroutine]['grouped_arguments']['by_io']: for arg in info_map[subroutine]['grouped_arguments']['by_io'][ 'output']: if info_map[subroutine]['argument_map'][arg]['code'][ 'level_1_type'] != None: assert_line = 'BOOST_STATIC_ASSERT( ($NAMESPACEis_mutable< ' + \ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_static_assert' ] + ' >::value) );' level1_static_assert_list += [assert_line] # import the code by argument for arg in info_map[subroutine]['arguments']: level0_arg_list += [ info_map[subroutine]['argument_map'][arg]['code'] ['call_level_0'] ] if info_map[subroutine]['argument_map'][arg]['code'][ 'level_1'] != None: level1_arg_list += [ info_map[subroutine]['argument_map'][arg]['code'] ['level_1'] ] if info_map[subroutine]['argument_map'][arg]['code'][ 'call_level_1'] != None: call_level1_arg_list += [ info_map[subroutine]['argument_map'][arg]['code'] ['call_level_1'] ] if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] != None and \ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] not in level1_type_arg_list: level1_type_arg_list += [ info_map[subroutine]['argument_map'][arg]['code'] ['level_1_type'] ] if info_map[subroutine]['argument_map'][arg]['code'][ 'level_1_assert'] != []: level1_assert_list += info_map[subroutine]['argument_map'][ arg]['code']['level_1_assert'] if info_map[subroutine]['argument_map'][arg]['code'][ 'typedef'] != None: # make sure trans tags always preceed other tags, as they may be dependant if 'TRANS' in arg: at_i = 0 if len(typedef_list ) > 0 and '_order<' in typedef_list[0]: at_i = 1 typedef_list.insert( at_i, info_map[subroutine]['argument_map'][arg] ['code']['typedef']) else: typedef_list.append( info_map[subroutine]['argument_map'][arg]['code'] ['typedef']) if info_map[subroutine]['argument_map'][arg]['code'][ 'level_2'] != None: level2_arg_list += [ info_map[subroutine]['argument_map'][arg]['code'] ['level_2'] ] if 'banded' in info_map[subroutine]['argument_map'][arg]: includes += [ '#include <boost/numeric/bindings/bandwidth.hpp>' ] # Insert the order_type() if appropriate if "has_cblas_order_arg" in info_map[ subroutine ] and \ info_map[ subroutine ][ 'has_cblas_order_arg' ]: level0_arg_list.insert(0, "order()") # Level 1 replacements level1_template = level1_template.replace( "$TYPEDEFS", "\n ".join(typedef_list)) level1_template = level1_template.replace( "$CALL_LEVEL0", ", ".join(level0_arg_list)) level1_template = level1_template.replace( "$CALL_LEVEL1", ", ".join(call_level1_arg_list)) level1_template = level1_template.replace( "$LEVEL1", ", ".join(level1_arg_list)) level1_template = level1_template.replace( "$TYPES", ", ".join(level1_type_arg_list)) level1_template = level1_template.replace( "$ASSERTS", "\n ".join(sorted(level1_assert_list))) level1_template = level1_template.replace( '$RESULT_TYPE', info_map[subroutine]['level1_result_type']) level1_template = level1_template.replace( '$RETURN_STATEMENT', info_map[subroutine]['return_statement']) level1_template = level1_template.replace( "$KEYWORDS", ", ".join(keyword_type_list)) if len(level1_static_assert_list) > 0: level1_template = level1_template.replace( "$STATIC_ASSERTS", "\n ".join(level1_static_assert_list)) else: level1_template = level1_template.replace( "\n $STATIC_ASSERTS", "") # Level 2 replacements # some special stuff is done here, such as replacing real_type with a # type-traits deduction, etc.. # more important: all non-const and const variants of functions are written here level2_functions = [] level2_arg_lists, level2_comments = \ bindings.generate_const_variants( group_name.lower() + '.' + value_type, \ level2_arg_list, template_map ) for level2_idx in range(0, len(level2_arg_lists)): level2_function = level2_template.replace( "$LEVEL2", \ ", ".join( level2_arg_lists[ level2_idx ] ) ) if len("".join(level2_comments[level2_idx])) > 0: level2_function = level2_function.replace( "$COMMENTS", \ "\n".join( level2_comments[ level2_idx ] ) ) level2_functions.append(level2_function) level2_template = "\n".join(level2_functions) level2_template = level2_template.replace("$COMMENTS\n", "") #level2_template = level2_template.replace( "$LEVEL2", ", ".join( level2_arg_list ) ) if len(level1_type_arg_list) > 0: my_key = group_name_l + '.' + value_type + '.first_typename' if netlib.my_has_key(my_key, template_map): first_typename = template_map[ netlib.my_has_key( \ my_key, template_map ) ].strip() else: first_typename = '' for tn in level1_type_arg_list: bare_type = tn.split(" ")[-1] if first_typename == '' and bare_type[:6].lower() in [ 'matrix', 'vector' ]: first_typename = bare_type first_typename_datatype = first_typename[:6].lower( ) # 'matrix' or 'vector' or 'scalar' else: level1_type_arg_list.insert(0, 'typename Value') first_typename = 'Value' first_typename_datatype = 'typename Value' level2_template = level2_template.replace("$FIRST_TYPENAME", first_typename) level2_template = level2_template.replace("$TYPEOF_FIRST_TYPENAME", first_typename_datatype) level2_template = level2_template.replace( "$CALL_LEVEL1", ", ".join(call_level1_arg_list)) level2_template = level2_template.replace( "$TYPES", ", ".join(level1_type_arg_list)) level2_template = level2_template.replace( '$RETURN_STATEMENT', info_map[subroutine]['return_statement']) level2_template = level2_template.replace(' $STATIC_ASSERTS\n', '') if first_typename == 'Value': level2_template = level2_template.replace( 'typename $NAMESPACEvalue_type< Value >::type', 'Value') level2_template = level2_template.replace( 'typename remove_imaginary< Value >::type', 'Value') level1_map[value_type] = bindings.proper_indent(level1_template) level2_map[value_type] = bindings.proper_indent(level2_template) # # LEVEL 1 and 2 FINALIZATION # for mapping in [level1_map, level2_map]: if len(mapping) > 1: # compare real and complex cases all_keys = mapping.keys() if mapping[all_keys[0]] == mapping[all_keys[1]]: print "literally everything is the same!!, falling back to 1 case" del mapping[all_keys[1]] level1 = '' for value_type in level1_map.keys(): level1 += level1_map[value_type] level2 = '' for value_type in level2_map.keys(): level2 += level2_map[value_type] # # handle addition of includes # includes_code = '' unique_includes = [] for include in includes: if include not in unique_includes: unique_includes += [include] sorted_includes = sorted(unique_includes, lambda x, y: cmp(x.lower(), y.lower())) if len(sorted_includes) > 0: includes_code = "\n".join(sorted_includes) result = template_map['blas.hpp'] result = result.replace('$INCLUDES', includes_code) result = result.replace('$OVERLOADS', overloads) result = result.replace('$LEVEL1', level1) result = result.replace('$LEVEL2', level2) result = result.replace('$GROUPNAME', group_name) result = result.replace('$groupname', group_name.lower()) result = result.replace('$DIRNAME', base_dir.split("/")[-1].upper()) result = result.replace('$dirname', base_dir.split("/")[-1].lower()) result = result.replace('$INTEGER_TYPE', netlib.generic_integer_type) result = result.replace('$LIBRARY_INT_TYPE', netlib.generic_integer_type) result = result.replace('$NAMESPACE', "bindings::") result = result.replace('template< >', '') result = result.replace('\n\n\n', '\n\n') result = result.replace("\n \n", "\n") result = result.replace('\n \n', '\n') result = result.replace('\n \n', '\n') # replace the global variables as last (this is convenient) #result = result.replace( '$INDENT', ' ' ) #result = result.replace( '$groupname', group_name.lower() ) #result = result.replace( '$DESCRIPTION', info_map[ group[g][0] ][ 'description' ] ) open(os.path.join(base_dir, filename), 'w').write(result)
def write_functions( info_map, group, template_map, base_dir ): # # group.keys() is a vector of different grouped function names # like gees, dgesv, etc. # for group_name, subroutines in group.iteritems(): filename = group_name.lower() + '.hpp' includes = [ '#include <boost/assert.hpp>', '#include <boost/numeric/bindings/remove_imaginary.hpp>', '#include <boost/numeric/bindings/value_type.hpp>', '#include <boost/numeric/bindings/begin.hpp>', '#include <boost/numeric/bindings/size.hpp>', #'#include <boost/numeric/bindings/tag.hpp>', '#include <boost/numeric/bindings/stride.hpp>', '#include <boost/numeric/bindings/is_mutable.hpp>', #'#include <boost/numeric/bindings/traits/traits.hpp>', #'#include <boost/numeric/bindings/traits/type_traits.hpp>', #'#include <boost/mpl/bool.hpp>', '#include <boost/type_traits/is_same.hpp>', '#include <boost/type_traits/remove_const.hpp>', '#include <boost/static_assert.hpp>' ] for subroutine in subroutines: group_name_l = info_map[ subroutine ][ 'group_name' ].lower() if template_map.has_key( group_name_l + '.includes' ): includes += template_map[ group_name_l + '.includes' ].splitlines() # # # LEVEL 0 HANDLING # # # If the first subroutine has a clapack_ routine, assume we are going # to provide specialisations # provide_clapack_backend = 'clapack_routine' in info_map[ subroutines[0] ] overloads = '' backend_includes = '' if provide_clapack_backend: overloads = template_map[ 'backend_lapack_with_clapack' ] backend_includes = template_map[ 'lapack_backend_includes_with_clapack' ] else: overloads = template_map[ 'backend_lapack_default' ] backend_includes = template_map[ 'lapack_backend_includes_default' ] for select_backend in [ 'lapack_overloads', 'clapack_overloads' ]: sub_overloads = '' for subroutine in subroutines: sub_template = template_map[ select_backend ] have_clapack = 'clapack_routine' in info_map[ subroutine ] # add the argument list here arg_list = [] lapack_arg_list = [] clapack_arg_list = [] typename_list = [] level0_static_asserts = [] check_for_unused = [] for arg in info_map[ subroutine ][ 'arguments' ]: print "Subroutine ", subroutine, " arg ", arg if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0' ] != None: arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0' ] ] # # Find potential arguments that may cause warnings because they are not used, and # store these in check_for_unused # if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0' ] != None and \ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0_typename' ] != None: keyword = info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0' ].split( ' ')[-1] check_for_unused += [ keyword ] if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_lapack_header' ] != None: lapack_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_lapack_header' ] ] if have_clapack and info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_clapack_header' ] != None: clapack_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_clapack_header' ] ] if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0_typename' ] != None: typename_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0_typename' ] ] if "has_clapack_order_arg" in info_map[ subroutine ]: if info_map[ subroutine ][ "has_clapack_order_arg" ] == True: arg_list.insert( 0, "Order" ) clapack_arg_list.insert( 0, "clapack_option< Order >::value" ) typename_list.insert( 0, "typename Order" ) level0_static_asserts.append( "BOOST_STATIC_ASSERT( (is_same<Order, tag::column_major>::value) );" ) includes += [ "#include <boost/type_traits/is_same.hpp>" ] sub_template = sub_template.replace( "$TYPES", ", ".join( typename_list ) ) sub_template = sub_template.replace( "template< >\n", "" ) sub_template = sub_template.replace( "$LEVEL0", ", ".join( arg_list ) ) sub_template = sub_template.replace( "$CALL_LAPACK_HEADER", ", ".join( lapack_arg_list ) ) sub_template = sub_template.replace( "$CALL_CLAPACK_HEADER", ", ".join( clapack_arg_list ) ) sub_template = sub_template.replace( "$SUBROUTINE", subroutine ) sub_template = sub_template.replace( '$groupname', group_name.lower() ) sub_template = sub_template.replace( "$SPECIALIZATION", documentation.routine_value_type[ subroutine[0] ] ) sub_template = sub_template.replace( '$STATIC_ASSERTS', "\n ".join( level0_static_asserts ) ) if select_backend == 'lapack_overloads': sub_template = sub_template.replace( '$LIBRARY_INT_TYPE', "fortran_int_t" ) else: sub_template = sub_template.replace( '$LIBRARY_INT_TYPE', "int" ) # CLAPACK stuff if 'clapack_routine' in info_map[ subroutine ]: clapack_routine = info_map[ subroutine ][ 'clapack_routine' ] else: clapack_routine = '// NOT FOUND' sub_template = sub_template.replace( "$CLAPACK_ROUTINE", clapack_routine ) # # Count potentially unused arguments. If the count is 1; it is only present in the # parameter list. In that case, the argument may be removed from the code # for parameter in check_for_unused: if sub_template.count( parameter ) == 1: sub_template = sub_template.replace( ' ' + parameter, '' ) # Finalize this sub_overload sub_overloads += bindings.proper_indent( sub_template ) # fill in for the appropriate back-end print "Replacing ", '$' + select_backend.upper() overloads = overloads.replace( '$' + select_backend.upper(), sub_overloads ) cases = {} # first, see what kind of functions we have # needed for argument check etc. for subroutine in subroutines: if info_map[ subroutine ][ 'value_type' ] == 'real': if not cases.has_key( 'real' ): cases[ 'real' ] = {} cases[ 'real' ][ 'subroutines' ] = [] cases[ 'real' ][ 'subroutines' ] += [ subroutine ] if info_map[ subroutine ][ 'value_type' ] == 'complex': if not cases.has_key( 'complex' ): cases[ 'complex' ] = {} cases[ 'complex' ][ 'subroutines' ] = [] cases[ 'complex' ][ 'subroutines' ] += [ subroutine ] # Figure out what the real/complex type selector argument might be type_selector_candidates = [] if cases.has_key( 'real' ) and cases.has_key( 'complex' ): # we have real and complex scenarios, these keys only exist # if we also have associated routines for arg in info_map[ cases[ 'real' ][ 'subroutines' ][0] ][ 'arguments' ]: if arg in info_map[ cases[ 'complex' ][ 'subroutines' ][0] ][ 'arguments' ]: if info_map[ cases[ 'real' ][ 'subroutines' ][0] ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] != None and \ info_map[ cases[ 'complex' ][ 'subroutines' ][0] ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] != None and \ info_map[ cases[ 'real' ][ 'subroutines' ][0] ][ 'argument_map' ][ arg ][ 'value_type_variant' ] == 'real' and \ info_map[ cases[ 'complex' ][ 'subroutines' ][0] ][ 'argument_map' ][ arg ][ 'value_type_variant' ] == 'complex': type_selector_candidates += [ arg ] # # # LEVEL 1 and 2 HANDLING # # level1_map = {} level2_map = {} for value_type, case_map in cases.iteritems(): # take this subroutine for arguments etc. subroutine = case_map[ 'subroutines' ][ 0 ] group_name_l = info_map[ subroutine ][ 'group_name' ].lower() print "taking",subroutine level1_template = '' level2_template = '' if info_map[ subroutine ][ 'grouped_arguments' ][ 'by_io' ].has_key( 'workspace' ): level1_template = template_map[ 'level1_workspace' ] level2_template = template_map[ 'level2_workspace' ] else: level1_template = template_map[ 'level1_noworkspace' ] level2_template = template_map[ 'level2_noworkspace' ] # include templates come before anything else; they can hold any # $ID my_include_key = group_name_l + '.' + value_type + '.include_templates' if netlib.my_has_key( my_include_key, template_map ): include_template_list = template_map[ netlib.my_has_key( my_include_key, template_map ) ].strip().replace(' ','').split(",") include_templates = '' for template in include_template_list: include_templates += template_map[ 'template_' + template ] level1_template = level1_template.replace( '$INCLUDE_TEMPLATES', bindings.proper_indent(include_templates) ) level2_template = level2_template.replace( '$INCLUDE_TEMPLATES', bindings.proper_indent(include_templates) ) else: level1_template = level1_template.replace( '\n$INCLUDE_TEMPLATES', '' ) level2_template = level2_template.replace( '\n$INCLUDE_TEMPLATES', '' ) # continue with first replacements level1_template = level1_template.replace( '$groupname', group_name.lower() ) level1_template = level1_template.replace( "$SPECIALIZATION", value_type ) level0_arg_list = [] level1_arg_list = [] level2_arg_list = [] level1_type_arg_list = [] level1_assert_list = [] level1_static_assert_list = [] call_level1_arg_list = [] workspace_query_arg_list = [] user_defined_arg_list = [] user_defined_opt_arg_list = [] keyword_type_list = [] typedef_list = [] # # Are we dealing with a transpose option here? # Because CLAPACK allows to pass the order of the matrices, here we # inject code that determines the default data order. # if 'matrix' in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_type' ]: has_trans = False matrix_wo_trans = [] matrix_wo_trans_arg = [] matrix_with_trans = [] matrix_wo_trans_arg_removed = [] for matrix_arg in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_type' ][ 'matrix' ]: if 'ref_trans' in info_map[ subroutine ][ 'argument_map' ][ matrix_arg ]: has_trans = True matrix_type = info_map[ subroutine ][ 'argument_map' ][ matrix_arg ][ 'code' ][ 'level_1_static_assert' ] matrix_with_trans += [ matrix_type ] else: matrix_wo_trans.append( info_map[ subroutine ][ 'argument_map' ][ matrix_arg ][ 'code' ][ 'level_1_static_assert' ] ) matrix_wo_trans_arg.append( matrix_arg ) # # Matrices have trans options in this case. If there is one without, # that one will determine the order of the call # if has_trans: includes += [ '#include <boost/numeric/bindings/trans_tag.hpp>' ] if len( matrix_wo_trans )>0: # Take the first from the matrix_wo_trans list for the order argument # remove this item from that list, so we have a correct list for static asserting # on column major data order later on typedef_list.insert( 0, 'typedef typename result_of::data_order< ' + matrix_wo_trans[0] + \ ' >::type order;' ) includes += [ '#include <boost/numeric/bindings/data_order.hpp>' ] del matrix_wo_trans[0] matrix_wo_trans_arg_removed = [ matrix_wo_trans_arg[0] ] del matrix_wo_trans_arg[0] else: typedef_list.insert( 0, 'typedef typename blas::detail::default_order< ' + matrix_with_trans[0] + \ ' >::type order;' ) includes += [ '#include <boost/numeric/bindings/blas/detail/default_order.hpp>' ] else: # so, there's no trans option # but, what if there's an order? (e.g., syr) -- then use ` if "has_clapack_order_arg" in info_map[ subroutine ]: typedef_list.insert( 0, 'typedef typename result_of::data_order< ' + matrix_wo_trans[0] + \ ' >::type order;' ) includes += [ '#include <boost/numeric/bindings/data_order.hpp>' ] del matrix_wo_trans[0] del matrix_wo_trans_arg[0] elif 'TRANS' in info_map[ subroutine ][ 'arguments' ]: # FIXME: workaround such that tridiagonal stuff has a chance to pass compile test even if a 'TRANS' argument is present. typedef_list.insert( 0, 'typedef tag::column_major order;' ) # in LAPACK, every matrix that is not # * transposeable # * used for order determination (CLAPACK). In this case, the wrong order will # be caught in the detail/overload function (static assert on column_major order) # and a matrix that has # * a leading dimension trait # should be column major, even in case of a row_major order used by CLAPACK # See http://math-atlas.sourceforge.net/faq.html#RowSolve # This effectively says every RHS matrix should be column major if len( matrix_wo_trans_arg ) > 0: for matrix_arg in matrix_wo_trans_arg: # In some cases, level1 assert stuff isn't set due to a workspace array # being passed as a matrix. Test for None in this case. Make sure the matrix has # a leading dimension trait. # TODO reconsider if the leading dimension trait is needed if 'ref_lda' in info_map[ subroutine ][ 'argument_map' ][ matrix_arg ] and \ info_map[ subroutine ][ 'argument_map' ][ matrix_arg ][ 'code' ][ 'level_1_static_assert' ] != None: assert_line = 'BOOST_STATIC_ASSERT( (bindings::is_column_major< ' + \ info_map[ subroutine ][ 'argument_map' ][ matrix_arg ][ 'code' ][ 'level_1_static_assert' ] + ' >::value) );' level1_static_assert_list += [ assert_line ] # this looks like we're adding lots of includes, but it will be cleaned up later, # and this makes sure we're only adding an include if the function is really used. includes += [ '#include <boost/numeric/bindings/is_column_major.hpp>' ] # In case of, e.g., getrs, MatrixB should be column major for CLAPACK, too. # This is not detected because MatrixA may be row major and transposed to column_major ordering. # So, MatrixB should be column_major although it is determining the order. # I.e., the order should be column_major. Look in the template system for these overrides. for matrix_arg in matrix_wo_trans_arg_removed: my_key = group_name_l + '.' + value_type + '.' + matrix_arg + '.is_column_major' print "Looking for column_major override ", my_key if netlib.my_has_key( my_key, template_map ): assert_line = 'BOOST_STATIC_ASSERT( (bindings::is_column_major< ' + \ info_map[ subroutine ][ 'argument_map' ][ matrix_arg ][ 'code' ][ 'level_1_static_assert' ] + ' >::value) );' level1_static_assert_list += [ assert_line ] includes += [ '#include <boost/numeric/bindings/is_column_major.hpp>' ] # # Add an include in case of the uplo or diag options # if 'UPLO' in info_map[ subroutine ][ 'arguments' ]: includes += [ '#include <boost/numeric/bindings/uplo_tag.hpp>' ] if 'DIAG' in info_map[ subroutine ][ 'arguments' ]: includes += [ '#include <boost/numeric/bindings/diag_tag.hpp>' ] # # Create static assertions, first by value type # for value_type_tmp_key in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_value_type' ].keys(): # look up whether they are template params static_asserts = [] for arg in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_value_type' ][ value_type_tmp_key ]: if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] != None: static_asserts.append( arg ) if len(static_asserts)>1: arg_A = static_asserts[0] for arg_B in static_asserts[1:]: print "Adding static assert for argA", arg_A, " argb", arg_B assert_line = 'BOOST_STATIC_ASSERT( (boost::is_same< ' + \ 'typename remove_const< typename $NAMESPACEvalue_type< ' + info_map[ subroutine ][ 'argument_map' ][ arg_A ][ 'code' ][ 'level_1_static_assert' ] + ' >::type >::type, ' + \ 'typename remove_const< typename $NAMESPACEvalue_type< ' + info_map[ subroutine ][ 'argument_map' ][ arg_B ][ 'code' ][ 'level_1_static_assert' ] + ' >::type >::type' \ ' >::value) );' level1_static_assert_list += [ assert_line ] # # Make sure the mutable stuff is mutable # if 'output' in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_io' ]: for arg in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_io' ][ 'output' ]: if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] != None: assert_line = 'BOOST_STATIC_ASSERT( ($NAMESPACEis_mutable< ' + \ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_static_assert' ] + ' >::value) );' level1_static_assert_list += [ assert_line ] # # import the code, by argument # for arg in info_map[ subroutine ][ 'arguments' ]: if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_level_0' ] != None: level0_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_level_0' ] ] if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1' ] != None: level1_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1' ] ] if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_2' ] != None: level2_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_2' ] ] if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] != None and \ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] not in level1_type_arg_list: level1_type_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] ] if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_assert' ] != []: level1_assert_list += info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_assert' ] if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_level_1' ] != None: call_level1_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_level_1' ] ] if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'opt_workspace_query' ] != None: workspace_query_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'opt_workspace_query' ] ] if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'typedef' ] != None: # make sure trans tags always preceed other tags, as they may be dependant if 'TRANS' in arg: at_i = 0 if len(typedef_list)>0 and ' order;' in typedef_list[0]: at_i = 1 typedef_list.insert( at_i, info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'typedef' ] ) else: typedef_list.append( info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'typedef' ] ) if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'keyword_type' ] != None: keyword_type_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'keyword_type' ] ] if 'banded' in info_map[ subroutine ][ 'argument_map' ][ arg ]: includes += [ '#include <boost/numeric/bindings/bandwidth.hpp>' ] if info_map[ subroutine ][ 'user_defined_variables' ] != None: for arg in info_map[ subroutine ][ 'user_defined_variables' ]: print arg if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'user_defined_init' ] != None: user_defined_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'user_defined_init' ] ] if info_map[ subroutine ][ 'user_defined_opt_variables' ] != None: for arg in info_map[ subroutine ][ 'user_defined_opt_variables' ]: print arg if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'user_defined_init' ] != None: user_defined_opt_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'user_defined_init' ] ] # # Insert the order_type() if appropriate # if "has_clapack_order_arg" in info_map[ subroutine ]: level0_arg_list.insert( 0, "order()" ) workspace_query_arg_list.insert( 0, "order()" ) # # LEVEL 1 REPLACEMENTS # level1_template = level1_template.replace( "$TYPEDEFS", "\n ".join( typedef_list ) ) level1_template = level1_template.replace( "$CALL_LEVEL0", ", ".join( level0_arg_list ) ) level1_template = level1_template.replace( "$CALL_LEVEL1", ", ".join( call_level1_arg_list ) ) level1_template = level1_template.replace( "$LEVEL1", ", ".join( level1_arg_list ) ) level1_template = level1_template.replace( "$TYPES", ", ".join( level1_type_arg_list ) ) level1_template = level1_template.replace( "$ASSERTS", "\n ".join( sorted( level1_assert_list ) ) ) level1_template = level1_template.replace( "$KEYWORDS", ", ".join( keyword_type_list ) ) if len( level1_static_assert_list ) > 0: level1_template = level1_template.replace( "$STATIC_ASSERTS", "\n ".join( level1_static_assert_list ) ) else: level1_template = level1_template.replace( "\n $STATIC_ASSERTS", "" ) if len( user_defined_arg_list ) > 0: level1_template = level1_template.replace( "$INIT_USER_DEFINED_VARIABLES", indent_lines( "\n".join(user_defined_arg_list), 8 ) ) else: level1_template = level1_template.replace( "\n $INIT_USER_DEFINED_VARIABLES", "" ) # # LEVEL 2 REPLACEMENTS # # some special stuff is done here, such as replacing real_type with a # type-traits deduction, etc.. # more important: all non-const and const variants of functions are written here # level2_functions = [] level2_arg_lists, level2_comments = \ bindings.generate_const_variants( \ group_name.lower() + '.' + value_type, level2_arg_list, template_map ) for level2_idx in range( 0, len( level2_arg_lists ) ): level2_function = level2_template.replace( "$LEVEL2", \ ", ".join( level2_arg_lists[ level2_idx ] ) ) if len( "".join(level2_comments[ level2_idx ] ) ) > 0: level2_function = level2_function.replace( "$COMMENTS", \ "\n".join( level2_comments[ level2_idx ] ) ) level2_functions.append( level2_function ) level2_template = "\n".join( level2_functions ) level2_template = level2_template.replace( "$COMMENTS\n", "" ) # # Determine a right type to select for real or complex variants # first_typename = '' print "Type selectors: ", type_selector_candidates if len( type_selector_candidates ) > 0: first_typename_arg = type_selector_candidates[0] first_typename_code = info_map[ subroutine ][ 'argument_map' ][ first_typename_arg ][ 'code' ][ 'level_1_type' ] first_typename = first_typename_code.split(" ")[-1] else: for tn in level1_type_arg_list: bare_type = tn.split(" ")[-1] if first_typename == '' and bare_type[:6].lower() in [ 'matrix', 'vector' ]: first_typename = bare_type # generate the word "matrix" or "vector", to select the right traits first_typename_datatype = first_typename[0:6].lower() level2_template = level2_template.replace( "$FIRST_TYPENAME", first_typename ) level2_template = level2_template.replace( "$LAST_TYPENAME", level1_type_arg_list[-1][9:] ) level2_template = level2_template.replace( "$TYPEOF_FIRST_TYPENAME", first_typename_datatype ) level2_template = level2_template.replace( "$CALL_LEVEL1", ", ".join( call_level1_arg_list ) ) level2_template = level2_template.replace( "$TYPES", ", ".join( level1_type_arg_list ) ) # # Workspace stuff # if info_map[ subroutine ][ 'grouped_arguments' ][ 'by_io' ].has_key( 'workspace' ): # Add an include for the workspace stuff includes += [ '#include <boost/numeric/bindings/lapack/workspace.hpp>' ] includes += [ '#include <boost/numeric/bindings/detail/array.hpp>' ] # Continue workspace_size = len( info_map[ subroutine ][ 'grouped_arguments' ][ 'by_io' ][ 'workspace' ] ) workspace_args = info_map[ subroutine ][ 'grouped_arguments' ][ 'by_io' ][ 'workspace' ] level1_template = level1_template.replace( "$WORKSPACE_SIZE", str(workspace_size) ) level1_template = level1_template.replace( "$WORKSPACE_TYPENAMES", "typename " + ", typename ".join( workspace_args ) ) level1_template = level1_template.replace( "$WORKSPACE_TYPES", ", ".join( workspace_args ) ) # $TMP_WORKARRAYS is something like "tmp_work, tmp_rwork" tmp_workspace_args = [] for name in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_io' ][ 'workspace' ]: tmp_workspace_args += [ 'tmp_' + name.lower() ] level1_template = level1_template.replace( "$TMP_WORKARRAYS", ", ".join( tmp_workspace_args ) ) # $SETUP_WORKARRAYS looks like # traits::detail::array< value_type > $TMP_NAME .... setup_min_workarrays = '' setup_opt_workarrays_pre = [] setup_opt_workarrays_post = [] for name in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_io' ][ 'workspace' ]: # minimal case sub_min_template = template_map[ 'setup_min_workspace' ] sub_min_template = sub_min_template.replace( '$WORKSPACE_FUNC', name.lower() ) sub_min_template = sub_min_template.replace( '$WORKSPACE_TYPE', info_map[ subroutine ][ 'argument_map' ][ name ][ 'code' ][ 'workspace_type' ] ) if info_map[ subroutine ][ 'argument_map' ][ name ][ 'code' ][ 'min_workspace_call' ] != None: sub_min_template = sub_min_template.replace( "$CALL_MIN_SIZE", info_map[ subroutine ][ 'argument_map' ][ name ][ 'code' ][ 'min_workspace_call' ] ) setup_min_workarrays += sub_min_template # optimal case if info_map[ subroutine ][ 'argument_map' ][ name ][ 'code' ][ 'opt_workspace_pre' ] != None: setup_opt_workarrays_pre += [ info_map[ subroutine ][ 'argument_map' ][ name ][ 'code' ][ 'opt_workspace_pre' ] ] if info_map[ subroutine ][ 'argument_map' ][ name ][ 'code' ][ 'opt_workspace_post' ] != None: setup_opt_workarrays_post += [ info_map[ subroutine ][ 'argument_map' ][ name ][ 'code' ][ 'opt_workspace_post' ] ] # if the length of setup_opt_workarrays_post equals 0, it's equal to the minimal_case opt_workspace_template = '' if len( setup_opt_workarrays_post ) == 0: print "EQUAL to MINIMAL CASE!" opt_workspace_template = template_map[ 'level1_opt_workspace_is_min' ] else: includes += [ '#include <boost/numeric/bindings/traits/detail/utils.hpp>' ] opt_workspace_template = template_map[ 'level1_opt_workspace' ] opt_workspace_template = opt_workspace_template.replace( "$WORKSPACE_QUERY", ", ".join( workspace_query_arg_list ) ) if provide_clapack_backend: # special workaround for clapack frontend of 'getri' opt_workspace_template = opt_workspace_template.replace( "$SETUP_OPT_WORKARRAYS_POST", "\n ".join( setup_opt_workarrays_post ) + '\n#endif') opt_workspace_template = opt_workspace_template.replace( " $SETUP_OPT_WORKARRAYS_PRE", '#if defined BOOST_NUMERIC_BINDINGS_LAPACK_CLAPACK\n' + ' $NAMESPACEdetail::array< ' + info_map[ subroutine ][ 'argument_map' ][ 'WORK' ][ 'code' ][ 'workspace_type' ] + ' > tmp_work( 0 );\n#else\n ' + "\n ".join( setup_opt_workarrays_pre ) ) else: opt_workspace_template = opt_workspace_template.replace( "$SETUP_OPT_WORKARRAYS_POST", "\n ".join( setup_opt_workarrays_post ) ) opt_workspace_template = opt_workspace_template.replace( "$SETUP_OPT_WORKARRAYS_PRE", "\n ".join( setup_opt_workarrays_pre ) ) opt_workspace_template = opt_workspace_template.replace( "$CALL_LEVEL1", ", ".join( call_level1_arg_list ) ) opt_workspace_template = opt_workspace_template.replace( "$TMP_WORKARRAYS", ", ".join( tmp_workspace_args ) ) if len( user_defined_opt_arg_list ) > 0: opt_workspace_template = opt_workspace_template.replace( "$INIT_USER_DEFINED_OPT_VARIABLES", indent_lines( "\n".join(user_defined_arg_list), 8 ) ) else: print "removing $INIT_USER_DEFINED_OPT_VARIABLES" opt_workspace_template = opt_workspace_template.replace( " $INIT_USER_DEFINED_OPT_VARIABLES\n", "" ) level1_template = level1_template.replace( "$OPT_WORKSPACE_FUNC", opt_workspace_template.rstrip() ) level1_template = level1_template.replace( "$SETUP_MIN_WORKARRAYS_POST", setup_min_workarrays.rstrip() ) # # INSERT THE MINIMAL WORKSPACE FUNCTIONS # min_size_funcs = '' for name in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_io' ][ 'workspace' ]: sub_template = template_map[ 'min_size_func' ] sub_template = sub_template.replace( "$WORKSPACE_FUNC", name.lower() ) # first: user-defined stuff (overrules any auto-detected stuff) resulting_code = '' my_key = group_name_l + '.' + value_type + '.min_size_' + name.lower() if netlib.my_has_key( my_key, template_map ): resulting_code = indent_lines( template_map[ netlib.my_has_key( my_key, template_map ) ].rstrip(), 8 ) elif info_map[ subroutine ][ 'argument_map' ][ name ][ 'code' ][ 'min_workspace' ] != None: resulting_code = 'return ' + info_map[ subroutine ][ 'argument_map' ][ name ][ 'code' ][ 'min_workspace' ] + ';' if resulting_code != '' and provide_clapack_backend: # special workaround for clapack frontend of 'getri' sub_template = sub_template.replace( " $MIN_SIZE_IMPLEMENTATION", '#if defined BOOST_NUMERIC_BINDINGS_LAPACK_CLAPACK\n return 0;\n' + '#else\n ' + resulting_code + '\n#endif' ) elif resulting_code != '': sub_template = sub_template.replace( "$MIN_SIZE_IMPLEMENTATION", resulting_code.rstrip() ) # Do about the same for the argument stuff. if info_map[ subroutine ][ 'argument_map' ][ name ][ 'code' ][ 'min_workspace_args' ] != None: sub_template = sub_template.replace( "$TYPES", info_map[ subroutine ][ 'argument_map' ][ name ][ 'code' ][ 'min_workspace_args' ][ 'types' ] ) sub_template = sub_template.replace( "$ARGUMENTS", info_map[ subroutine ][ 'argument_map' ][ name ][ 'code' ][ 'min_workspace_args' ][ 'code' ] ) #sub_template += 'FOUND' min_size_funcs += sub_template min_size_funcs = min_size_funcs.rstrip() level1_template = level1_template.replace( "$MIN_SIZE_FUNCS", min_size_funcs ) level1_map[ value_type ] = bindings.proper_indent( level1_template ) level2_map[ value_type ] = bindings.proper_indent( level2_template ) # # LEVEL 1 and 2 FINALIZATION # for mapping in [ level1_map, level2_map ]: if len(mapping) > 1: # compare real and complex cases all_keys = mapping.keys() if mapping[ all_keys[0] ] == mapping[ all_keys[1] ]: print "literally everything is the same!!, falling back to 1 case" del mapping[ all_keys[ 1 ] ] level1 = '' if len( level1_map ) > 1: level1 = template_map[ 'level1_pre_header' ] includes += [ '#include <boost/utility/enable_if.hpp>' ] includes += [ '#include <boost/numeric/bindings/is_real.hpp>' ] includes += [ '#include <boost/numeric/bindings/is_complex.hpp>' ] for value_type in level1_map.keys(): if len( level1_map ) == 1: header = template_map[ 'level1_header1' ] else: header = template_map[ 'level1_header2' ] level1 += header.replace( "$SPECIALIZATION", value_type ) level1 += level1_map[ value_type ] level2 = '' for value_type in level2_map.keys(): level2 += level2_map[ value_type ] # # handle addition of includes # includes_code = '' unique_includes = [] for include in includes: if include not in unique_includes: unique_includes += [ include ] sorted_includes = sorted( unique_includes, lambda x, y: cmp( x.lower(), y.lower() ) ) if len( sorted_includes ) > 0: includes_code = "\n".join( sorted_includes ) result = template_map[ 'lapack.hpp' ] result = result.replace( '$INCLUDES', includes_code ) result = result.replace( '$BACKEND_INCLUDES', backend_includes ) result = result.replace( '$OVERLOADS', overloads ) result = result.replace( '$LEVEL1', level1 ) result = result.replace( '$LEVEL2', level2 ) result = result.replace( '$GROUPNAME', group_name ) result = result.replace( '$groupname', group_name.lower() ) result = result.replace( '$DIRNAME', base_dir.split("/")[-1].upper() ) result = result.replace( '$dirname', base_dir.split("/")[-1].lower() ) result = result.replace( '$INTEGER_TYPE', netlib.generic_integer_type ) result = result.replace( '$LIBRARY_INT_TYPE', "fortran_int_t" ) result = result.replace( '$NAMESPACE', "bindings::" ) result = result.replace( ' template< >\n', '' ) result = result.replace( '\n\n\n', '\n\n' ) result = result.replace( "\n \n", "\n" ) result = result.replace( "\n \n", "\n" ) result = result.replace( "\n \n", "\n" ) # replace the global variables as last (this is convenient) #result = result.replace( '$INDENT', ' ' ) #result = result.replace( '$groupname', group_name.lower() ) #result = result.replace( '$DESCRIPTION', info_map[ group[g][0] ][ 'description' ] ) open( os.path.join( base_dir, filename ), 'w' ).write( result )
def parse_file(filename, info_map, template_map): parser_mode = template_map['PARSERMODE'] prefix_map = {'BLAS': 'cblas_', 'LAPACK': 'clapack_'} prefix = prefix_map[parser_mode] print prefix pp = pprint.PrettyPrinter(indent=2) source = open(filename).read() for match in re.compile( '(void|float|int|double|CBLAS_INDEX) +' + prefix + '([^\(]+)\(([^\)]+)\)', re.M | re.S).findall(source): print "----" result_type = match[0] fortran_routine = match[1].split("_sub")[0].upper().strip() c_routine = prefix + match[1] print "C" + parser_mode + " routine:", c_routine, " " + parser_mode + " equivalent:", fortran_routine arguments = {} for arg in match[2].replace('\n', '').split(','): arg = arg.strip() arg_name = arg.split(" ")[-1].replace("*", "").strip().upper() arguments[arg_name] = {} arguments[arg_name]["original"] = arg arguments[arg_name]["pointer"] = "*" in arg pp.pprint(arguments) if fortran_routine in info_map: print "Found ", fortran_routine, " in Fortran info_map." info_map[fortran_routine][prefix + "routine"] = c_routine # read aliases, if they are there my_key = info_map[fortran_routine]['group_name'].lower( ) + '.all.cblas_alias' alias_map = {} #print my_key if netlib.my_has_key(my_key, template_map) != None: #print "Has key.." for line in template_map[netlib.my_has_key( my_key, template_map)].splitlines(): #print "Line:", line alias_map[line.split(",")[0]] = line.split(",")[1] #print alias_map # Try to match and insert arguments # argument_map is the data gathered through the Fortran interface for arg in info_map[fortran_routine]['argument_map']: cblas_arg = '' if arg in arguments: cblas_arg = arg elif arg in alias_map: if alias_map[arg] in arguments: cblas_arg = alias_map[arg] print "Looking for " + parser_mode + " argument ", arg, " CBLAS equivalent: ", cblas_arg if cblas_arg in arguments: print "Found matching argument, inserting call_cblas_header stuff" call_cblas_header = info_map[fortran_routine][ "argument_map"][arg]["code"]["call_blas_header"] print "Original: ", call_cblas_header if not arguments[cblas_arg]["pointer"]: call_cblas_header = call_cblas_header.replace("&", "") call_cblas_header = call_cblas_header.replace( "complex_ptr", "void_ptr") print "Result: ", call_cblas_header if arg == 'UPLO': info_map[ fortran_routine ][ "argument_map" ][ arg ][ "code" ][ "call_" + prefix + "header" ] = \ prefix + "option< UpLo >::value" elif arg == 'DIAG': info_map[ fortran_routine ][ "argument_map" ][ arg ][ "code" ][ "call_" + prefix + "header" ] = \ prefix + "option< Diag >::value" elif arg == 'SIDE': info_map[ fortran_routine ][ "argument_map" ][ arg ][ "code" ][ "call_" + prefix + "header" ] = \ prefix + "option< " + netlib.template_parameter[ arg ] + " >::value" elif arg == 'TRANS' or arg == 'TRANSA' or arg == 'TRANSB': info_map[ fortran_routine ][ "argument_map" ][ arg ][ "code" ][ "call_" + prefix + "header" ] = \ prefix + "option< " + netlib.template_parameter[ arg ] + " >::value" else: info_map[fortran_routine]["argument_map"][arg]["code"][ "call_" + prefix + "header"] = call_cblas_header else: if arg == 'INFO' and result_type == 'int': info_map[fortran_routine]["argument_map"][arg]["code"][ "call_" + prefix + "header"] = None print "INFO is the return type, adding it with code None" elif arg == 'WORK' or arg == 'LWORK': info_map[fortran_routine]["argument_map"][arg]["code"][ "call_" + prefix + "header"] = None info_map[fortran_routine][ "clapack_disable_workspace"] = True else: exit(0) if "ORDER" in arguments: print "Adding order argument." info_map[fortran_routine]["has_" + prefix + "order_arg"] = True else: print "Not adding order argument." info_map[fortran_routine]["has_" + prefix + "order_arg"] = False
def write_functions( info_map, group, template_map, base_dir ): # # group.keys() is a vector of different grouped function names # like gees, dgesv, etc. # for group_name, subroutines in group.iteritems(): filename = group_name.lower() + '.hpp' includes = [ #'#include <boost/numeric/bindings/detail/void_ptr.hpp>', #'#include <boost/numeric/bindings/traits/traits.hpp>', #'#include <boost/numeric/bindings/traits/type_traits.hpp>', '#include <boost/numeric/bindings/remove_imaginary.hpp>', '#include <boost/numeric/bindings/is_mutable.hpp>', '#include <boost/numeric/bindings/value_type.hpp>', '#include <boost/numeric/bindings/stride.hpp>', '#include <boost/numeric/bindings/size.hpp>', '#include <boost/numeric/bindings/begin.hpp>', #'#include <boost/mpl/bool.hpp>', '#include <boost/type_traits/is_same.hpp>', '#include <boost/type_traits/remove_const.hpp>', '#include <boost/static_assert.hpp>', '#include <boost/assert.hpp>', ] for subroutine in subroutines: group_name_l = info_map[ subroutine ][ 'group_name' ].lower() if template_map.has_key( group_name_l + '.includes' ): includes += template_map[ group_name_l + '.includes' ].splitlines() # # LEVEL 0 HANDLING # overloads = template_map[ 'backend_blas_overloads' ] for select_backend in [ 'blas_overloads', 'cblas_overloads', 'cublas_overloads' ]: sub_overloads = '' for subroutine in subroutines: group_name_l = info_map[ subroutine ][ 'group_name' ].lower() # stuff like float, complex<double>, etc. subroutine_value_type = documentation. \ subroutine_value_type[ ",".join( [ info_map[ subroutine ][ 'value_type' ], info_map[ subroutine ][ 'precision' ] ] ) ] sub_template = template_map[ select_backend ] # add the argument list here arg_list = [] typename_list = [] blas_arg_list = [] cblas_arg_list = [] cublas_arg_list = [] level0_static_asserts = [] check_for_unused = [] for arg in info_map[ subroutine ][ 'arguments' ]: print "Subroutine ", subroutine, " arg ", arg arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0' ] ] blas_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_blas_header' ] ] # # Find potential arguments that may cause warnings because they are not used, and # store these in check_for_unused # if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0' ] != None and \ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0_typename' ] != None: keyword = info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0' ].split( ' ')[-1] check_for_unused += [ keyword ] if 'call_cblas_header' in info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ]: cblas_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_cblas_header' ] ] else: print "WARNING: couldn't find cblas call." if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0_typename' ] != None: typename_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0_typename' ] ] if "has_cblas_order_arg" in info_map[ subroutine ]: check_for_unused.append( 'order' ) if info_map[ subroutine ][ "has_cblas_order_arg" ] == True: arg_list.insert( 0, "const Order order" ) cblas_arg_list.insert( 0, "cblas_option< Order >::value" ) typename_list.insert( 0, "typename Order" ) level0_static_asserts.append( "BOOST_STATIC_ASSERT( (is_same<Order, tag::column_major>::value) );" ) includes += [ "#include <boost/type_traits/is_same.hpp>" ] sub_template = sub_template.replace( "$TYPES", ", ".join( typename_list ) ) sub_template = sub_template.replace( "template< >\n", "" ) sub_template = sub_template.replace( "$LEVEL0", ", ".join( arg_list ) ) sub_template = sub_template.replace( "$CALL_BLAS_HEADER", ", ".join( blas_arg_list ) ) sub_template = sub_template.replace( "$CALL_CBLAS_HEADER", ", ".join( cblas_arg_list ) ) sub_template = sub_template.replace( "$SUBROUTINE", subroutine ) sub_template = sub_template.replace( "$SPECIALIZATION", subroutine_value_type ) sub_template = sub_template.replace( '$STATIC_ASSERTS', "\n ".join( level0_static_asserts ) ) if select_backend == 'blas_overloads': sub_template = sub_template.replace( '$LIBRARY_INT_TYPE', "fortran_int_t" ) else: # the C integer-type defaults to 'int' sub_template = sub_template.replace( '$LIBRARY_INT_TYPE', "int" ) # CBLAS stuff if 'cblas_routine' in info_map[ subroutine ]: cblas_routine = info_map[ subroutine ][ 'cblas_routine' ] else: cblas_routine = '// NOT FOUND' sub_template = sub_template.replace( "$CBLAS_ROUTINE", cblas_routine ) # CUBLAS stuff if 'cublas_routine' in info_map[ subroutine ]: cublas_routine = info_map[ subroutine ][ 'cublas_routine' ] for arg in info_map[ subroutine ][ 'arguments' ]: cublas_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_cublas_header' ] ] else: cublas_routine = '// NOT FOUND' # # Count potentially unused arguments. If the count is 1; it is only present in the # parameter list. In that case, the argument may be removed from the code # print "Check for unused:" , check_for_unused for parameter in check_for_unused: if sub_template.count( parameter ) == 1: sub_template = sub_template.replace( ' ' + parameter, '' ) sub_template = sub_template.replace( "$CALL_CUBLAS_HEADER", ", ".join( cublas_arg_list ) ) sub_template = sub_template.replace( "$CUBLAS_ROUTINE", cublas_routine ) sub_template = sub_template.replace( '$groupname', group_name.lower() ) sub_template = sub_template.replace( '$RESULT_TYPE', info_map[ subroutine ][ 'return_value_type' ] ) sub_template = sub_template.replace( '$RETURN_STATEMENT', info_map[ subroutine ][ 'return_statement' ] ) sub_template = bindings.search_replace( sub_template, group_name_l + '.level0.gsub', template_map ) sub_overloads += bindings.proper_indent( sub_template ) overloads = overloads.replace( '$' + select_backend.upper(), sub_overloads ) # # Prepare for levels 1 and 2 # cases = {} # first, see what kind of functions we have # needed for argument check etc. for subroutine in subroutines: if info_map[ subroutine ][ 'value_type' ] == 'real': if not cases.has_key( 'real' ): cases[ 'real' ] = {} cases[ 'real' ][ 'subroutines' ] = [] cases[ 'real' ][ 'subroutines' ] += [ subroutine ] if info_map[ subroutine ][ 'value_type' ][ :7 ] == 'complex': if not cases.has_key( 'complex' ): cases[ 'complex' ] = {} cases[ 'complex' ][ 'subroutines' ] = [] cases[ 'complex' ][ 'subroutines' ] += [ subroutine ] # # LEVEL 1 and 2 HANDLING # level1_map = {} level2_map = {} for value_type, case_map in cases.iteritems(): level1_template = '' level2_template = '' level1_template = template_map[ 'blas_level1' ] level2_template = template_map[ 'blas_level2' ] subroutine = case_map[ 'subroutines' ][ 0 ] group_name_l = info_map[ subroutine ][ 'group_name' ].lower() # take this subroutine for arguments etc. # (last entry -> complex version if available, which can be important) subroutine = subroutines[-1] # include templates come before anything else; they can hold any # $ID my_include_key = group_name_l + '.' + value_type + '.include_templates' if netlib.my_has_key( my_include_key, template_map ): include_template_list = template_map[ netlib.my_has_key( my_include_key, template_map ) ].strip().replace(' ','').split(",") include_templates = '' for template in include_template_list: include_templates += template_map[ 'template_' + template ] level1_template = level1_template.replace( '$INCLUDE_TEMPLATES', bindings.proper_indent(include_templates) ) level2_template = level2_template.replace( '$INCLUDE_TEMPLATES', bindings.proper_indent(include_templates) ) else: level1_template = level1_template.replace( '\n$INCLUDE_TEMPLATES', '' ) level2_template = level2_template.replace( '\n$INCLUDE_TEMPLATES', '' ) level0_arg_list = [] level1_arg_list = [] level2_arg_list = [] call_level1_arg_list = [] level1_type_arg_list = [] level1_assert_list = [] level1_static_assert_list = [] keyword_type_list = [] typedef_list = [] # # Are we dealing with a transpose option here? # Because CBLAS allows to pass the order of the matrices, here we # inject code that determines the default data order. # if 'matrix' in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_type' ]: has_trans = False matrix_wo_trans = [] matrix_with_trans = [] for matrix_arg in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_type' ][ 'matrix' ]: if 'ref_trans' in info_map[ subroutine ][ 'argument_map' ][ matrix_arg ]: has_trans = True matrix_type = info_map[ subroutine ][ 'argument_map' ][ matrix_arg ][ 'code' ][ 'level_1_static_assert' ] matrix_with_trans += [ matrix_type ] else: matrix_wo_trans.append( info_map[ subroutine ][ 'argument_map' ][ matrix_arg ][ 'code' ][ 'level_1_static_assert' ] ) # # Matrices have trans options in this case. If there is one without, # that one will determine the order of the call # if has_trans: includes += [ '#include <boost/numeric/bindings/trans_tag.hpp>' ] if len( matrix_wo_trans )>0: typedef_list.insert( 0, 'typedef typename result_of::data_order< ' + matrix_wo_trans[0] + \ ' >::type order;' ) includes += [ '#include <boost/numeric/bindings/data_order.hpp>' ] else: typedef_list.insert( 0, 'typedef typename detail::default_order< ' + matrix_with_trans[0] + \ ' >::type order;' ) includes += [ '#include <boost/numeric/bindings/blas/detail/default_order.hpp>' ] else: # so, there's no trans option # but, what if there's an order? (e.g., syr) -- then use ` if "has_cblas_order_arg" in info_map[ subroutine ]: typedef_list.insert( 0, 'typedef typename result_of::data_order< ' + matrix_wo_trans[0] + \ ' >::type order;' ) includes += [ '#include <boost/numeric/bindings/data_order.hpp>' ] # # Add an include in case of the uplo or diag options # if 'UPLO' in info_map[ subroutine ][ 'arguments' ]: includes += [ '#include <boost/numeric/bindings/uplo_tag.hpp>' ] if 'DIAG' in info_map[ subroutine ][ 'arguments' ]: includes += [ '#include <boost/numeric/bindings/diag_tag.hpp>' ] # # Create static assertions, first by value type # has_comment = False for value_type_tmp_key in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_value_type' ].keys(): # look up whether they are template params static_asserts = [] for arg in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_value_type' ][ value_type_tmp_key ]: if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] != None: static_asserts.append( arg ) if len(static_asserts)>1: arg_A = static_asserts[0] for arg_B in static_asserts[1:]: print "Adding static assert for argA", arg_A, " argb", arg_B arg_left = info_map[ subroutine ][ 'argument_map' ][ arg_A ][ 'code' ][ 'level_1_static_assert' ] arg_right = info_map[ subroutine ][ 'argument_map' ][ arg_B ][ 'code' ][ 'level_1_static_assert' ] if arg_left != None and arg_right != None: assert_line = 'BOOST_STATIC_ASSERT( (is_same< ' + \ 'typename remove_const< typename $NAMESPACEvalue_type< ' + arg_left + ' >::type >::type, ' + \ 'typename remove_const< typename $NAMESPACEvalue_type< ' + arg_right + ' >::type >::type' \ ' >::value) );' if not has_comment: #level1_static_assert_list += [ '// Here, we assert... ' ] has_comment = True level1_static_assert_list += [ assert_line ] # # Matrices should adhere to their storage scheme # if 'matrix' in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_type' ]: for matrix_id in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_type' ][ 'matrix' ]: info_map_item = info_map[ subroutine ][ 'argument_map' ][ matrix_id ] if 'banded' in info_map_item and info_map_item[ 'banded' ] == True and \ info_map_item[ 'code' ][ 'level_1_type' ] != None: assert_line = 'BOOST_STATIC_ASSERT( ($NAMESPACEhas_band_array< ' + info_map_item[ 'code' ][ 'level_1_static_assert' ] + \ ' >::value) );' level1_static_assert_list += [ assert_line ] includes += [ "#include <boost/numeric/bindings/has_band_array.hpp>" ] elif 'packed' in info_map_item and info_map_item[ 'packed' ] == True and \ info_map_item[ 'code' ][ 'level_1_type' ] != None: assert_line = 'BOOST_STATIC_ASSERT( ($NAMESPACEhas_triangular_array< ' + info_map_item[ 'code' ][ 'level_1_static_assert' ] + \ ' >::value) );' level1_static_assert_list += [ assert_line ] includes += [ "#include <boost/numeric/bindings/has_triangular_array.hpp>" ] elif info_map_item[ 'code' ][ 'level_1_type' ] != None: assert_line = 'BOOST_STATIC_ASSERT( ($NAMESPACEhas_linear_array< ' + info_map_item[ 'code' ][ 'level_1_static_assert' ] + \ ' >::value) );' level1_static_assert_list += [ assert_line ] includes += [ "#include <boost/numeric/bindings/has_linear_array.hpp>" ] # # Vectors should have linear arrays # if 'vector' in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_type' ]: for vector_id in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_type' ][ 'vector' ]: info_map_item = info_map[ subroutine ][ 'argument_map' ][ vector_id ] if 'ref_stride' in info_map_item and info_map_item[ 'code' ][ 'level_1_type' ] != None: assert_line = 'BOOST_STATIC_ASSERT( ($NAMESPACEhas_linear_array< ' + info_map_item[ 'code' ][ 'level_1_static_assert' ] + \ ' >::value) );' level1_static_assert_list += [ assert_line ] includes += [ "#include <boost/numeric/bindings/has_linear_array.hpp>" ] # Make sure the mutable stuff is mutable if 'output' in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_io' ]: for arg in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_io' ][ 'output' ]: if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] != None: assert_line = 'BOOST_STATIC_ASSERT( ($NAMESPACEis_mutable< ' + \ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_static_assert' ] + ' >::value) );' level1_static_assert_list += [ assert_line ] # import the code by argument for arg in info_map[ subroutine ][ 'arguments' ]: level0_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_level_0' ] ] if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1' ] != None: level1_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1' ] ] if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_level_1' ] != None: call_level1_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_level_1' ] ] if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] != None and \ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] not in level1_type_arg_list: level1_type_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] ] if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_assert' ] != []: level1_assert_list += info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_assert' ] if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'typedef' ] != None: # make sure trans tags always preceed other tags, as they may be dependant if 'TRANS' in arg: at_i = 0 if len(typedef_list)>0 and '_order<' in typedef_list[0]: at_i = 1 typedef_list.insert( at_i, info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'typedef' ] ) else: typedef_list.append( info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'typedef' ] ) if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_2' ] != None: level2_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_2' ] ] if 'banded' in info_map[ subroutine ][ 'argument_map' ][ arg ]: includes += [ '#include <boost/numeric/bindings/bandwidth.hpp>' ] # Insert the order_type() if appropriate if "has_cblas_order_arg" in info_map[ subroutine ] and \ info_map[ subroutine ][ 'has_cblas_order_arg' ]: level0_arg_list.insert( 0, "order()" ) # Level 1 replacements level1_template = level1_template.replace( "$TYPEDEFS", "\n ".join( typedef_list ) ) level1_template = level1_template.replace( "$CALL_LEVEL0", ", ".join( level0_arg_list ) ) level1_template = level1_template.replace( "$CALL_LEVEL1", ", ".join( call_level1_arg_list ) ) level1_template = level1_template.replace( "$LEVEL1", ", ".join( level1_arg_list ) ) level1_template = level1_template.replace( "$TYPES", ", ".join( level1_type_arg_list ) ) level1_template = level1_template.replace( "$ASSERTS", "\n ".join( sorted( level1_assert_list ) ) ) level1_template = level1_template.replace( '$RESULT_TYPE', info_map[ subroutine ][ 'level1_result_type' ] ) level1_template = level1_template.replace( '$RETURN_STATEMENT', info_map[ subroutine ][ 'return_statement' ] ) level1_template = level1_template.replace( "$KEYWORDS", ", ".join( keyword_type_list ) ) if len( level1_static_assert_list ) > 0: level1_template = level1_template.replace( "$STATIC_ASSERTS", "\n ".join( level1_static_assert_list ) ) else: level1_template = level1_template.replace( "\n $STATIC_ASSERTS", "" ) # Level 2 replacements # some special stuff is done here, such as replacing real_type with a # type-traits deduction, etc.. # more important: all non-const and const variants of functions are written here level2_functions = [] level2_arg_lists, level2_comments = \ bindings.generate_const_variants( group_name.lower() + '.' + value_type, \ level2_arg_list, template_map ) for level2_idx in range( 0, len( level2_arg_lists ) ): level2_function = level2_template.replace( "$LEVEL2", \ ", ".join( level2_arg_lists[ level2_idx ] ) ) if len( "".join(level2_comments[ level2_idx ] ) ) > 0: level2_function = level2_function.replace( "$COMMENTS", \ "\n".join( level2_comments[ level2_idx ] ) ) level2_functions.append( level2_function ) level2_template = "\n".join( level2_functions ) level2_template = level2_template.replace( "$COMMENTS\n", "" ) #level2_template = level2_template.replace( "$LEVEL2", ", ".join( level2_arg_list ) ) if len(level1_type_arg_list)>0: my_key = group_name_l + '.' + value_type + '.first_typename' if netlib.my_has_key( my_key, template_map ): first_typename = template_map[ netlib.my_has_key( \ my_key, template_map ) ].strip() else: first_typename = '' for tn in level1_type_arg_list: bare_type = tn.split(" ")[-1] if first_typename == '' and bare_type[:6].lower() in [ 'matrix', 'vector' ]: first_typename = bare_type first_typename_datatype = first_typename[:6].lower() # 'matrix' or 'vector' or 'scalar' else: level1_type_arg_list.insert( 0, 'typename Value' ) first_typename = 'Value' first_typename_datatype = 'typename Value' level2_template = level2_template.replace( "$FIRST_TYPENAME", first_typename ) level2_template = level2_template.replace( "$TYPEOF_FIRST_TYPENAME", first_typename_datatype ) level2_template = level2_template.replace( "$CALL_LEVEL1", ", ".join( call_level1_arg_list ) ) level2_template = level2_template.replace( "$TYPES", ", ".join( level1_type_arg_list ) ) level2_template = level2_template.replace( '$RETURN_STATEMENT', info_map[ subroutine ][ 'return_statement' ] ) level2_template = level2_template.replace( ' $STATIC_ASSERTS\n', '' ) if first_typename == 'Value': level2_template = level2_template.replace( 'typename $NAMESPACEvalue_type< Value >::type', 'Value' ) level2_template = level2_template.replace( 'typename remove_imaginary< Value >::type', 'Value' ) level1_map[ value_type ] = bindings.proper_indent( level1_template ) level2_map[ value_type ] = bindings.proper_indent( level2_template ) # # LEVEL 1 and 2 FINALIZATION # for mapping in [ level1_map, level2_map ]: if len(mapping) > 1: # compare real and complex cases all_keys = mapping.keys() if mapping[ all_keys[0] ] == mapping[ all_keys[1] ]: print "literally everything is the same!!, falling back to 1 case" del mapping[ all_keys[ 1 ] ] level1 = '' for value_type in level1_map.keys(): level1 += level1_map[ value_type ] level2 = '' for value_type in level2_map.keys(): level2 += level2_map[ value_type ] # # handle addition of includes # includes_code = '' unique_includes = [] for include in includes: if include not in unique_includes: unique_includes += [ include ] sorted_includes = sorted( unique_includes, lambda x, y: cmp( x.lower(), y.lower() ) ) if len( sorted_includes ) > 0: includes_code = "\n".join( sorted_includes ) result = template_map[ 'blas.hpp' ] result = result.replace( '$INCLUDES', includes_code ) result = result.replace( '$OVERLOADS', overloads ) result = result.replace( '$LEVEL1', level1 ) result = result.replace( '$LEVEL2', level2 ) result = result.replace( '$GROUPNAME', group_name ) result = result.replace( '$groupname', group_name.lower() ) result = result.replace( '$DIRNAME', base_dir.split("/")[-1].upper() ) result = result.replace( '$dirname', base_dir.split("/")[-1].lower() ) result = result.replace( '$INTEGER_TYPE', netlib.generic_integer_type ) result = result.replace( '$LIBRARY_INT_TYPE', netlib.generic_integer_type ) result = result.replace( '$NAMESPACE', "bindings::" ) result = result.replace( 'template< >', '' ) result = result.replace( '\n\n\n', '\n\n' ) result = result.replace( "\n \n", "\n" ) result = result.replace( '\n \n', '\n' ) result = result.replace( '\n \n', '\n' ) # replace the global variables as last (this is convenient) #result = result.replace( '$INDENT', ' ' ) #result = result.replace( '$groupname', group_name.lower() ) #result = result.replace( '$DESCRIPTION', info_map[ group[g][0] ][ 'description' ] ) open( os.path.join( base_dir, filename ), 'w' ).write( result )