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_documentation( info_map, level, group, template_map, base_dir ): parsermode = template_map[ 'PARSERMODE' ].lower() for group_name, subroutines in group.iteritems(): filename = group_name.lower() + '.qbk' result = template_map[ parsermode + '.qbk' ] prototypes = [] first_typename = None originating_sources = [] no_arguments_level0 = [] no_arguments_level2 = [] for subroutine in subroutines: if parsermode == 'blas': originating_sources.append( "[@http://www.netlib.org/blas/" + subroutine.lower() + ".f " + subroutine.lower() + ".f]" ) if parsermode == 'lapack': originating_sources.append( "[@http://www.netlib.org/lapack/" + lapack_value_type[subroutine[0]] + "/" + subroutine.lower() + ".f " + subroutine.lower() + ".f]" ) level2_arg_list_pre = [] for arg in info_map[ subroutine ][ 'arguments' ]: if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_2' ] != None: level2_arg_list_pre += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_2' ] ] # Keep track of the numbers of arguments if len( info_map[ subroutine ][ 'arguments' ] ) not in no_arguments_level0: no_arguments_level0.append( len( info_map[ subroutine ][ 'arguments' ] ) ) if len( level2_arg_list_pre ) not in no_arguments_level2: no_arguments_level2.append( len( level2_arg_list_pre ) ) level2_arg_list = [] for arg in level2_arg_list_pre: readable_arg = '' if '::real_type' in arg: splitted = arg.split( "::real_type" ) readable_arg = "Scalar" + splitted[1] if 'const' in arg: readable_arg = 'const ' + readable_arg elif '::type' in arg: splitted = arg.split( "::type" ) readable_arg = "Scalar" + splitted[1] if 'const' in arg: readable_arg = 'const ' + readable_arg else: readable_arg = arg if (first_typename == None ) and ('Vector' in arg or 'Matrix' in arg): first_typename = arg.replace( "const ", "" ).replace( "&", "" ).split(" ")[0] level2_arg_list.append( readable_arg ) prototype = template_map[ parsermode + '_prototype.qbk' ] prototype = prototype.replace( "$LEVEL2", ", ".join( level2_arg_list ) ) if prototype not in prototypes: prototypes.append( prototype ) prototype_overloads = '' if len(prototypes) == 1: prototype_overloads = 'is one prototype of' else: prototype_overloads = 'are ' + number_to_text[ len(prototypes) ] + ' prototypes of' prototypes = bindings.proper_indent( "\n".join( prototypes ) ) #prototypes.append( prototype ) result = result.replace( "$PROTOTYPES", prototypes ) result = result.replace( "$PROTOTYPE_OVERLOADS", prototype_overloads ) result = result.replace( "$ORIGINATING_SOURCES", readable_join( originating_sources ) ) result = result.replace( '$PURPOSE', combine_purposes( subroutines, info_map ) ) result = result.replace( '$LIBRARY_INT_TYPE', "int_t" ) result = result.replace( '$SUBROUTINES', readable_join( subroutines ) ) result = result.replace( '$header', 'boost/numeric/bindings/' + parsermode + '/' + level + '/' + group_name.lower() + '.hpp' ) if len( no_arguments_level0 ) == 1: result = result.replace( '$NO_ARGUMENTS_LEVEL0', number_to_text[ no_arguments_level0[0] ] ) else: result = result.replace( '$NO_ARGUMENTS_LEVEL0', "TODO" ) if len( no_arguments_level2 ) == 1: result = result.replace( '$NO_ARGUMENTS_LEVEL2', number_to_text[ no_arguments_level2[0] ] ) else: result = result.replace( '$NO_ARGUMENTS_LEVEL2', "TODO" ) if parsermode == 'blas': result = result.replace( '$DISPATCH_TABLE', write_blas_dispatch_table( subroutines, info_map ) ) result = result.replace( '$FRIENDLY_NAME', blas_friendly_name( group_name, info_map, template_map ) ) if parsermode == 'lapack': result = result.replace( '$DISPATCH_TABLE', write_lapack_dispatch_table( subroutines, info_map ) ) #result = result.replace( '$FRIENDLY_NAME', lapack_friendly_name( group_name, info_map, template_map ) ) if first_typename != None: result = result.replace( "$FIRST_TYPENAME", first_typename ) result = result.replace( '$GROUPNAME', group_name ) result = result.replace( '$groupname', group_name.lower() ) print "Writing " + base_dir + "/" + filename 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/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 write_documentation(info_map, level, group, template_map, base_dir): parsermode = template_map['PARSERMODE'].lower() for group_name, subroutines in group.iteritems(): filename = group_name.lower() + '.qbk' result = template_map[parsermode + '.qbk'] prototypes = [] first_typename = None originating_sources = [] no_arguments_level0 = [] no_arguments_level2 = [] for subroutine in subroutines: if parsermode == 'blas': originating_sources.append("[@http://www.netlib.org/blas/" + subroutine.lower() + ".f " + subroutine.lower() + ".f]") if parsermode == 'lapack': originating_sources.append("[@http://www.netlib.org/lapack/" + lapack_value_type[subroutine[0]] + "/" + subroutine.lower() + ".f " + subroutine.lower() + ".f]") level2_arg_list_pre = [] for arg in info_map[subroutine]['arguments']: if info_map[subroutine]['argument_map'][arg]['code'][ 'level_2'] != None: level2_arg_list_pre += [ info_map[subroutine]['argument_map'][arg]['code'] ['level_2'] ] # Keep track of the numbers of arguments if len(info_map[subroutine] ['arguments']) not in no_arguments_level0: no_arguments_level0.append( len(info_map[subroutine]['arguments'])) if len(level2_arg_list_pre) not in no_arguments_level2: no_arguments_level2.append(len(level2_arg_list_pre)) level2_arg_list = [] for arg in level2_arg_list_pre: readable_arg = '' if '::real_type' in arg: splitted = arg.split("::real_type") readable_arg = "Scalar" + splitted[1] if 'const' in arg: readable_arg = 'const ' + readable_arg elif '::type' in arg: splitted = arg.split("::type") readable_arg = "Scalar" + splitted[1] if 'const' in arg: readable_arg = 'const ' + readable_arg else: readable_arg = arg if (first_typename == None) and ('Vector' in arg or 'Matrix' in arg): first_typename = arg.replace("const ", "").replace("&", "").split(" ")[0] level2_arg_list.append(readable_arg) prototype = template_map[parsermode + '_prototype.qbk'] prototype = prototype.replace("$LEVEL2", ", ".join(level2_arg_list)) if prototype not in prototypes: prototypes.append(prototype) prototype_overloads = '' if len(prototypes) == 1: prototype_overloads = 'is one prototype of' else: prototype_overloads = 'are ' + number_to_text[len( prototypes)] + ' prototypes of' prototypes = bindings.proper_indent("\n".join(prototypes)) #prototypes.append( prototype ) result = result.replace("$PROTOTYPES", prototypes) result = result.replace("$PROTOTYPE_OVERLOADS", prototype_overloads) result = result.replace("$ORIGINATING_SOURCES", readable_join(originating_sources)) result = result.replace('$PURPOSE', combine_purposes(subroutines, info_map)) result = result.replace('$LIBRARY_INT_TYPE', "int_t") result = result.replace('$SUBROUTINES', readable_join(subroutines)) result = result.replace( '$header', 'boost/numeric/bindings/' + parsermode + '/' + level + '/' + group_name.lower() + '.hpp') if len(no_arguments_level0) == 1: result = result.replace('$NO_ARGUMENTS_LEVEL0', number_to_text[no_arguments_level0[0]]) else: result = result.replace('$NO_ARGUMENTS_LEVEL0', "TODO") if len(no_arguments_level2) == 1: result = result.replace('$NO_ARGUMENTS_LEVEL2', number_to_text[no_arguments_level2[0]]) else: result = result.replace('$NO_ARGUMENTS_LEVEL2', "TODO") if parsermode == 'blas': result = result.replace( '$DISPATCH_TABLE', write_blas_dispatch_table(subroutines, info_map)) result = result.replace( '$FRIENDLY_NAME', blas_friendly_name(group_name, info_map, template_map)) if parsermode == 'lapack': result = result.replace( '$DISPATCH_TABLE', write_lapack_dispatch_table(subroutines, info_map)) #result = result.replace( '$FRIENDLY_NAME', lapack_friendly_name( group_name, info_map, template_map ) ) if first_typename != None: result = result.replace("$FIRST_TYPENAME", first_typename) result = result.replace('$GROUPNAME', group_name) result = result.replace('$groupname', group_name.lower()) print "Writing " + base_dir + "/" + filename open(os.path.join(base_dir, filename), 'w').write(result)