import json import copy def check_same_op(src, dst): if src['type'] != dst['type']: return False if 'main' in src: if 'main' not in dst: return False else: return src['main'] == dst['main'] if 'main' in dst: return False return True class Function: def __init__(self, type, op, input_number, output_number): self.name = None self.type = type self.op = op self.input_number = input_number self.output_number = output_number def op_to_function(op): num_inputs = 0 op = copy.deepcopy(op) if 'inputIndexes' in op: num_inputs = len(op['inputIndexes']) del op['inputIndexes'] num_outputs = len(op['outputIndexes']) del op['outputIndexes'] if 'name' in op: del op['name'] optype = op['type'] op = json.dumps(op, indent=4) return Function(optype, op, num_inputs, num_outputs) def remove_parameters(op): if 'main' not in op: return main_p = op['main'] if op['main_type'] == 'Convolution2D': if 'weight' in main_p: del main_p['weight'] if 'bias' in main_p: del main_p['bias'] if 'external' in main_p: del main_p['external'] if 'quanParameter' in main_p: del main_p['quanParameter'] elif op['main_type'] == 'LayerNorm': if 'gamma' in main_p: del main_p['gamma'] if 'beta' in main_p: del main_p['beta'] class MNNInfo: def __init__(self): self.inputs = None self.outputs = None self.origin = None self.sep_functions = None self.function_list = None self.consts = None self.tensor_name = None def load_mnn(filename, remove_parameter = True): dst = MNNInfo() mnn = {} function_list = {} sep_functions = [] inputs = [] consts = [] with open(filename) as f: mnn = json.load(f) oplists = mnn['oplists'] if remove_parameter: for op in oplists: remove_parameters(op) for i in range(0, len(oplists)): seperate = True op = oplists[i] if 'inputIndexes' not in op or len(op["inputIndexes"]) == 0: if op['type'] == 'Input': inputs.append(op) continue for j in range(0, i): if check_same_op(op, oplists[j]): function_list[i] = function_list[j] seperate = False break if seperate: function_list[i] = op_to_function(op) function_list[i].name = function_list[i].type + '%d' %i sep_functions.append(function_list[i]) dst.origin = mnn dst.sep_functions = sep_functions dst.function_list = function_list dst.inputs = inputs dst.consts = consts dst.tensor_name = {} tensorName = mnn['tensorName'] for i in range(0, len(tensorName)): dst.tensor_name[tensorName[i]] = i if 'outputName' in mnn: dst.outputs = mnn['outputName'] else: mask = {} for i in range(0, len(tensorName)): mask[i] = 0 # Find not middle as output dst.outputs = [] oplists = mnn['oplists'] for op in oplists: if 'inputIndexes' in op: for index in op['inputIndexes']: mask[index] |= 1 for index in op['outputIndexes']: mask[index] |= 2 for i in range(0, len(tensorName)): if mask[i] == 2: dst.outputs.append(tensorName[i]) return dst def make_python(funcname, mnninfo): mnn = mnninfo.origin sep_functions = mnninfo.sep_functions function_list = mnninfo.function_list tensor_names = mnn['tensorName'] inputs = mnninfo.inputs inputnames = list(map(lambda op:tensor_names[op['outputIndexes'][0]], inputs)) inputindexes = list(map(lambda op:op['outputIndexes'][0], inputs)) main_str = "import MNN.expr as F\n" main_str += 'def ' + funcname + '(' + inputnames[0] for i in range(1, len(inputnames)): main_str += ', ' + inputnames[i] main_str += '):\n' indent = " " for func in sep_functions: main_str += indent + func.name + ' = \"\"\"' + func.op main_str += indent + '\"\"\"\n' main_str += indent + 'stackes = {}\n' for i in range(0, len(inputindexes)): main_str += indent + 'stackes[%d]' %inputindexes[i] + ' = ' + inputnames[i] + '\n' main_str += indent + '# Call Funciton\n' oplists = mnn['oplists'] for i in range(0, len(oplists)): op = oplists[i] if op['type'] == 'Input': continue main_str += indent + 'p = F.jsonop([' if 'inputIndexes' in op and len(op["inputIndexes"]) > 0: main_str += 'stackes[%d]' %op["inputIndexes"][0] for j in range(1, len(op['inputIndexes'])): main_str += ', stackes[%d]' %op["inputIndexes"][j] main_str += '], ' + function_list[i].name + ', %d)\n' %len(op['outputIndexes']) main_str += indent + 'stackes[%d]' %op['outputIndexes'][0] for j in range(1, len(op['outputIndexes'])): main_str += ', stackes[%d]' %op['outputIndexes'][j] main_str += ' = p[0]' for j in range(1, len(op['outputIndexes'])): main_str += ', p[%d]' %j main_str += '\n' # Return output outputs = mnninfo.outputs for output in outputs: index = mnninfo.tensor_name[output] main_str += indent + 'stackes[%d]' %index + '.name = \'' + output + '\'\n' main_str += indent + 'return stackes[%d]' %mnninfo.tensor_name[outputs[0]] for i in range(1, len(outputs)): main_str += ', stackes[%d]' %mnninfo.tensor_name[outputs[i]] main_str +='\n' print("Write to python ", funcname + '.py') with open(funcname + '.py', 'w') as f: f.write(main_str) return def make_cpp(cppname, mnninfo): mnn = mnninfo.origin sep_functions = mnninfo.sep_functions function_list = mnninfo.function_list inputs = mnninfo.inputs outputs = mnninfo.outputs consts = mnninfo.consts hpp_file_name = cppname + '.hpp' cpp_file_name = cppname + '.cpp' cppstr = "#include \n" cppstr += "#include \"" + hpp_file_name + "\"\n" # Init Sub function for i in range(len(sep_functions)): func = sep_functions[i] cppstr += 'static std::vector ' + func.name + '(VARP x0' for j in range(1, func.input_number): cppstr += ', VARP x%d' %j cppstr += ') {\n' cppstr += "static const char* " + func.name + "_main" + " = R\"func(\n" cppstr += func.op cppstr += ")func\";\n" cppstr += 'return _JSONOp({x0' for j in range(1, func.input_number): cppstr += ', x%d' %j cppstr += '}, ' cppstr += func.name + "_main" cppstr += ', %d' %func.output_number cppstr += ');\n' cppstr += '}\n' oplists = mnn['oplists'] function_name = cppname.replace('/', '_').replace('\\', '_') tensor_names = mnn['tensorName'] # Init input cppstr += 'std::map ' + function_name + '(const std::map& ____inputs) {\n' cppstr += 'std::vector t(%d);\n' %len(tensor_names) cppstr += '// Init Inputs\n' for op in inputs: name = tensor_names[op['outputIndexes'][0]] cppstr += 't[%d]' %op['outputIndexes'][0] + ' = ____inputs.find(\"' + name + '\")->second;\n' # Call function cppstr += '// Call Funciton\n' for i in range(0, len(oplists)): op = oplists[i] if 'inputIndexes' not in op or len(op["inputIndexes"]) == 0: continue cppstr +='\n' cppstr += '{\n' cppstr += "VARPS tmp = " + function_list[i].name if 'inputIndexes' in op: cppstr += '(t[%d]' %op['inputIndexes'][0] for v in range(1, len(op['inputIndexes'])): cppstr += ',t[%d]' %op['inputIndexes'][v] cppstr +=');\n' else: cppstr += '();\n' for i in range(len(op['outputIndexes'])): index = op['outputIndexes'][i] cppstr += 't[%d] = ' %index + 'tmp[%d];\n' %i cppstr += '}\n' # Generate outputs cppstr += '// Collect Outputs\n' cppstr += 'std::map _____outputs;\n' for output in outputs: index = mnninfo.tensor_name[output] cppstr += 't[%d] ' %index + '->setName(\"' + output+'\");\n' cppstr += '_____outputs[\"' + output + '\"] = t[%d];\n' %index cppstr += 'return _____outputs;\n' cppstr += '}\n' cppstr = cppstr.replace("VARP", "MNN::Express::VARP") print("Write to ", hpp_file_name, cpp_file_name) with open(cpp_file_name, 'w') as f: f.write(cppstr) with open(hpp_file_name, 'w') as f: f.write("#ifndef " + hpp_file_name.replace('.', '_')+ '\n') f.write("#define " + hpp_file_name.replace('.', '_') + '\n') f.write("#include \n") f.write("std::map " + function_name + '(const std::map& ____inputs);\n') f.write("#endif\n") if __name__ == '__main__': import sys remove_weight = False if len(sys.argv) > 3: remove_weight = True mnninfo = load_mnn(sys.argv[1], remove_weight) make_python(sys.argv[2], mnninfo) make_cpp(sys.argv[2], mnninfo)