Ejemplo n.º 1
0
def translate_vensim(mdl_file):
    """
    Translate a vensim model file into a python class.

    Supported functionality:\n\n"""
    # Todo: Test translation independent of load
    #  We can make smaller unit tests that way...

    # Step 1 parser gets the subscript dictionary
    with open(mdl_file, 'rU') as file:
        text = file.read()

    # Todo: consider making this easier to understand, maybe with a second parsimonious grammar
    f2 = re.findall(
        r'([a-zA-Z][^:~\n.\[\]\+\-\!/\(\)\\\&\=]+:+[^:~.\[\]\+\-\!/\(\),\\\&\=]+,+[^:~.\[\]\+\-\!/\(\)\\\&\=]+~)+',
        text)
    for i in range(len(f2)):
        f2[i] = re.sub(r'[\n\t~]', '', f2[i])

    dictofsubs = {}
    for i in f2:
        Family = builder.make_python_identifier(i.split(":")[0])
        Elements = i.split(":")[1].split(",")
        for i in range(len(Elements)):
            Elements[i] = builder.make_python_identifier(Elements[i].strip())
        dictofsubs[Family] = dict(zip(Elements, range(len(Elements))))

    for i in dictofsubs:
        for j in dictofsubs:
            try:
                if set(dictofsubs[j].keys()).issubset(
                        dictofsubs[i].keys()) and j != i:
                    tempj = []
                    for key in (dictofsubs[i]):
                        if key in dictofsubs[j].keys():
                            tempj.append(dictofsubs[i][key])
                    tempj.append(i)
                    dictofsubs[j] = sorted(tempj)
            except:
                pass
    # Step 2 parser writes the file
    outfile_name = mdl_file[:-4] + '.py'
    parser = TextParser(file_grammar, outfile_name, text, dictofsubs)
    parser.builder.write()

    return parser.filename
Ejemplo n.º 2
0
 def visit_Identifier(self, n, vc):
     string = n.text
     return builder.make_python_identifier(string)
Ejemplo n.º 3
0
 def visit_Identifier(self, n, vc):
     string = n.text
     return builder.make_python_identifier(string)
Ejemplo n.º 4
0
def translate_jitia(mdl_file):
    """
    Translate a jitia model file into a python class.

    Supported functionality:\n\n"""
    # model=mdl_file.replace('.dyn','.py')
    # builder.Builder(model,dictofsubs)

    f1 = open(mdl_file, 'r')
    f1 = ' '.join(f1)
    f1 = re.sub(r'(MACRO(.|\n)*MEND)*', '', f1)

    f2 = re.findall(r'([A-Z]+ [^\n]*(\n    [^\n]*)*)', f1)
    for i, j in enumerate(f2):
        f2[i] = j[0]

    f3 = []
    for i in f2:
        f3.append(i.split('\n      '))
    exceptions = ('FOR  ', 'N TIME=', 'SPEC', 'SAVE')
    symbols = r'(\,|\^|\(|\)|\[|\]|\=|\<|\>|(?<![0-9]e)\+(?![0-9])|(?<![0-9]e)\-(?![0-9])|(?<=[0-9])\+(?=[0-9])|(?<=[0-9])\-(?=[0-9])|\*|\/)'
    numbers = r'(?i)((\+|\-)*(?<!([a-zA-Z]|[0-9]|\_))([0-9]*\.?[0-9]+(e(\+|\-)?[0-9]*)*)(?!([a-zA-Z]|[0-9]|\_)))'
    functions = [
        'MAX', 'MIN', 'POS', 'IF', 'THEN', 'ELSE', 'AND', 'OR', 'SUM', 'SUMV',
        'SAMPLE', 'TABLE', 'STEP', 'WEIGHT', 'TUNER', 'TUN', 'FIFZE', 'FIFGE',
        'DELAY3M', 'DATAOFF', 'SCLPRD', 'FAVAIL', 'PRDV', 'TUNE1', 'DATAOO',
        'SMOOTH', 'DELAY', 'SUPPOS', 'AVVAL', 'SUPPOS', 'TABHL'
    ]
    functiondic = {
        'MISSING()': '0',
        'MAX': 'np.maximum',
        'MIN': 'np.minimum',
        'SUM': 'np.sum',
        'if_then_else': 'np.where',
        'SUMV': 'functions.sumv',
        'ABS': 'abs',
        'INTEGER': 'int',
        'EXP': 'np.exp',
        'PI': 'np.pi',
        'SIN': 'np.sin',
        'COS': 'np.cos',
        'SQRT': 'np.sqrt',
        'TAN': 'np.tan',
        'LOGNORMAL': 'np.random.lognormal',
        'RANDOM NORMAL': 'functions.bounded_normal',
        'POISSON': 'np.random.poisson',
        'LN': 'np.log',
        'EXPRND': 'np.random.exponential',
        'RANDOM UNIFORM': 'np.random.rand',
        'ARCCOS': 'np.arccos',
        'ARCSIN': 'np.arcsin',
        'ARCTAN': 'np.arctan',
        'STEP': 'functions.step',
        'MODULO': 'np.mod',
        'PULSE': 'functions.pulse',
        'PULSE TRAIN': 'functions.pulse_train',
        'RAMP': 'functions.ramp',
        'FIFZE': 'functions.fifze',
        'FIFGE': 'functions.fifge',
        'SCLPRD': 'functions.sclprd',
        'PRDV': 'functions.prdv',
        'SMOOTH3I': '_smooth3i_',
        'SMOOTH3': '_smooth3_',
        'SMOOTHI': '_smoothi_',
        'SMOOTH': '_smooth_',
        'LOGN': 'np.log',
        'ROUND': 'np.round',
        'DELAY3I': '_delay3i_',
        'DELAY3': '_delay3_',
        'DELAY1I': '_delayi_',
        'DELAY1': '_delay_',
        'DELAYI': '_delayi_',
        'DELAY': '_delay_',
        'FLOOR': 'np.floor',
        'VECTORMIN': 'np.min',
        'VECTORMAX': 'np.max',
        'PROD': 'np.prod',
        'TABHL': 'functions.tabhl'
    }

    functiondiccase = '(' + '|'.join(functiondic) + ')'

    def unique(seq):
        Set = set(seq)
        return list(Set)

    checking = []
    subscripts = []
    for variable in f3:
        if variable[0].startswith('FOR  '):
            checking.append(''.join(variable).split('  ', 1)[-1])
            subscripts.append(''.join(variable).split('  ', 1)[-1])
        elif variable[0].startswith('SAVE '):
            checking.append(''.join(variable).split(' ', 1)[-1])
        else:
            checking.append(','.join(
                re.findall(r'((?<=[A-Z] )[^\=]*(?=\=))', variable[0])))
    checking.append(','.join(functions))
    checking.append(','.join(functiondic.keys()))
    checking = ' '.join(checking)
    checking = re.sub(r'(\,|\=|\^|\(|\))', ' ', checking)
    checking = checking.split(' ')
    checking = filter(None, checking)
    checking = unique(checking)
    checking = '|'.join(checking)
    checking = '(?i)^(' + re.sub(r'(%s|%s)' % (symbols, numbers), '',
                                 checking).replace('||', '|') + ')$'
    subscripts = ','.join(subscripts)
    subscripts = re.sub('\^', '', subscripts)
    subscripts = re.sub('=', ',', subscripts)
    subscripts = subscripts.split(',')
    subscripts.append('\*')
    subscripts = filter(None, subscripts)
    # for i,j in enumerate(subscripts):
    #     if re.search(r'\d+\-\d+',j):
    #         subscripts[i]=''
    #         j=j.split('-')
    #         for k in range(int(j[0]),int(j[1])):
    #              subscripts[i]+=str(k)+'|'
    #         subscripts[i].strip('|')
    subs = r'(?i)(' + '|'.join(subscripts) + ')'
    # subs = re.sub('(\d+)-(\d+)',lambda s:'|'.join(['%s'%x for x in range(int(s.group(1)),int(s.group(2))+1)]),subs)
    for position, variable in enumerate(f3):
        for j in range(len(variable)):
            if not variable[-1].endswith(' ') and not variable[0].startswith(
                    exceptions):
                variable.pop(-1)
            else:
                break
        for counter, value in enumerate(variable):
            value = re.sub(symbols, ' ', value)
            value = re.sub(numbers, ' ', value)
            value = re.sub('\s+', ' ', value)
            value = value.split(' ')
            if counter != 0:
                if len(variable[counter]
                       ) < 40 and variable[counter][-1] != '^':
                    variable[counter + 1:] = ''
                if not re.search(
                        '(\d|\=|\>|\<|\+|\-|\*|\/|\(|\)|\[|\]|\:|\,)',
                        variable[counter]) and len(variable[counter]) > 55:
                    variable[counter:] = ''
                for word in value:
                    if not re.search(checking,
                                     word) and word != '' and not re.search(
                                         r'(?i)(else|then)', word):
                        variable[counter:] = ''
                        break
        for j in range(len(variable)):
            if not variable[-1].endswith(' ') and not variable[0].startswith(
                    exceptions) and variable[0] != variable[-1]:
                variable.pop(-1)
            else:
                break

        f3[position] = ''.join(variable).strip().replace('^', ' ').replace(
            '\n', ' ')
        f3[position] = re.sub(r'^(.*?)\((.*?)\)(\=)', r'\1[\2]\3',
                              f3[position])
        f3[position] = re.sub('\((?=%s\s*(\,|\)))' % subs, '[', f3[position])
        f3[position] = re.sub(r'(\[%s\s*(\,\s*%s)*\s*)\)' % (subs, subs),
                              r'\1]', f3[position])
        # if re.match('(?i)\w *c school duration',f3[position]):
        #     print f3[position]
        f3[position] = re.sub(r'(?i)(%s)' % functiondiccase,
                              lambda s: s.group(1).upper(), f3[position])
        # if f3[position].startswith('N  LKNPE'):
        #     print f3[position]
    auxiliaries = []
    levels = []
    tables = []
    initials = []
    subscripts = []

    for variable in f3:
        if variable.startswith('FOR  '):
            subscripts.append(variable.split('  ', 1)[-1])
        elif variable.startswith(('A  ', 'C  ', 'R  ')):
            auxiliaries.append(variable.split('  ', 1)[-1])
        elif variable.startswith('SPEC '):
            auxiliaries.append(variable.split(' ', 1)[-1])
        elif variable.startswith('L  '):
            levels.append(variable.split('  ', 1)[-1])
        elif variable.startswith('T  '):
            tables.append(variable.split('  ', 1)[-1])
        elif variable.startswith('N '):
            if variable.startswith('N TIME'):
                variable = variable.replace('TIME', ' INITIAL TIME')
            initials.append(variable.split('  ', 1)[-1])
    dictofsubs = {}
    for i in subscripts:
        Family = builder.make_python_identifier(i.split("=")[0])
        if not re.search('-', i):
            Elements = i.split("=")[1].split(",")
            for i in range(len(Elements)):
                Elements[i] = builder.make_python_identifier(
                    Elements[i].strip())
            dictofsubs[Family] = dict(zip(Elements, range(len(Elements))))
        elif not re.search(r'[a-zA-Z]', i.split('=')[1]):
            Elements = i.split("=")[1].split("-")
            Elements = range(int(Elements[0]), int(Elements[1]) + 1)
            for i, j in enumerate(Elements):
                Elements[i] = str(j)
            dictofsubs[Family] = dict(zip(Elements, range(len(Elements))))
        elif re.search('-', i.split('=')[1]):
            Elements = i.split('=')[1]
            dictofsubs[Family] = re.sub(
                r'([\w\s]+)',
                lambda s: builder.make_python_identifier(s.group(1)), Elements)
        for subfamily in dictofsubs:
            if isinstance(dictofsubs[subfamily], str):
                fromelem = dictofsubs[subfamily].split('-')[0]
                toelem = dictofsubs[subfamily].split('-')[1]
                for family in dictofsubs:
                    if fromelem in dictofsubs[family] and toelem in dictofsubs[
                            family] and isinstance(dictofsubs[family], dict):
                        dictofsubs[subfamily] = range(
                            dictofsubs[family][fromelem],
                            dictofsubs[family][toelem] + 1)
                        dictofsubs[subfamily].append(family)

    def getelempos(allelements):
        getelempos.sumfor = ''
        position = []
        elements = allelements.replace(' ', '').split(',')
        for pos, element in enumerate(elements):
            if element in dictofsubs.keys():
                if isinstance(dictofsubs[element], dict):
                    position.append(':')
                else:
                    position.append(sorted(dictofsubs[element][:-1]))
            else:
                if element == '*':
                    position.append(':')
                    if len(position) == 1 or (position[-2] == ':'
                                              and len(position) > 1):
                        sumat = pos
                    else:
                        sumat = pos - 1
                    getelempos.sumfor = ',' + str(sumat)
                for d in dictofsubs.itervalues():
                    try:
                        position.append(d[element])
                    except:
                        pass
        if len(re.findall(r'\*', allelements)) > 1:
            getelempos.sumfor = ''
        return tuple(position)

    getelempos.sumfor = ''

    def getnumofelements(element):
        """

        Parameters
        ----------
        element <string of subscripts>

        returns a list of the sizes of the dimensions. A 4x3x6 array would return
        [4,3,6]

        """
        # todo: make this elementstr or something
        if element == '':  # or ("subscript_"+element) in subscripts:
            return 0
        position = []
        elements = element.replace('!', '').replace('', '').split(',')
        for element in elements:
            if element in dictofsubs.keys():
                if isinstance(dictofsubs[element], list):
                    position.append(
                        (getnumofelements(dictofsubs[element][-1]))[0])
                else:
                    position.append(len(dictofsubs[element]))
            else:
                if element == '*':
                    position.append(1)
                for d in dictofsubs.itervalues():
                    try:
                        (d[element])
                    except:
                        pass
                    else:
                        position.append(len(d))

        return position

    pysd.builder.dictofsubs = dictofsubs
    pysd.builder.getelempos = getelempos
    pysd.builder.getnumofelements = getnumofelements

    #######################################################
    new_model = mdl_file.replace('.dyn', '.py')
    Builder = builder.Builder(new_model, dictofsubs)
    macrolist = macroextractor(mdl_file)
    #######################################################
    lookups = []
    for line in tables:
        line = re.findall(r'([^\[\=]*)(\[([^\]]*)\])?\=(.*)', line)[0]
        identifier = builder.make_python_identifier(line[0])
        sub = re.sub(
            r'([^\[\]\(\)\+\-\*\/\,\.]+)',
            lambda s: builder.make_python_identifier(s.group(1))
            if not re.match('\d*$', s.group(1)) else s.group(1), line[2])
        expression = line[3].split(',')
        # if line[0].startswith('Data Table for Inventory by LOS and Rank'):
        #     print sub,identifier
        copairlist = []
        for i in range(len(expression) / 2):
            copair = (expression[2 * i], expression[2 * i + 1])
            copairlist.append(copair)
        lookups.append([identifier, [sub], [copairlist]])
    for pos1, value1 in enumerate(lookups):
        for pos2, value2 in enumerate(lookups):
            if pos1 != pos2:
                try:
                    if value1[0] == value2[0]:
                        lookups[pos1][1] += value2[1]
                        lookups[pos1][2] += value2[2]
                        lookups[pos2] = ''
                except:
                    pass
    lookups = filter(None, lookups)
    lookuptofix = []
    for line in lookups:
        lookuptofix.append(line[0])
        try:
            Builder.add_lookup(line[0], '', line[1], line[2])
        except Exception as e:
            print line[0]
            print e
    lookuptofix = '(?i)(' + '|'.join(lookuptofix) + ')\(\)\[*[^\]\(]*\]*\('
    stocks = []
    init_val = []
    for line in levels:
        line = re.findall(r'([^\[\=]*)(\[([^\]]*)\])?\=.*?\+DT\*\((.*)\)',
                          line)[0]
        identifier = builder.make_python_identifier(line[0])
        sub = re.sub(r'([^\[\]\(\)\+\-\*\/\,\.]+)',
                     lambda s: builder.make_python_identifier(s.group(1)),
                     line[2])
        expression = formatexpression(line[3], identifier, functiondic,
                                      macrolist)
        expression = re.sub(
            r'\[([^\]]*)\]', lambda s: '[' + ','.join(
                map(str, getelempos(s.group(1).replace('()', '')))) + ']' +
            getelempos.sumfor, expression)
        expression = re.sub(
            r'\,(%s\(\))\)' % subs, lambda s: ',' + ','.join(
                map(str, getnumofelements(s.group(1).replace('()', '')))) +
            ')', expression)
        expression = re.sub(r'%s' % lookuptofix, r'\1(', expression)
        subinexpression = '[' + ','.join(map(str, getelempos(sub))) + ']'
        expression = re.sub(
            r'(?<!\(|\w|,)(\w+)\(\)\[\:(\,\:)*\]',
            r'functions.shorthander(\1(),\1.dimension_dir,loc_dimension_dir,_subscript_dict)'
            + subinexpression, expression.replace(' ', ''))
        stocks.append([identifier, [sub], [expression], [], []])
    for line in initials:
        line = re.findall(r'([^\[\=]*)(\[([^\]]*)\])?\=(.*)', line)[0]
        identifier = builder.make_python_identifier(line[0])
        sub = re.sub(r'([^\[\]\(\)\+\-\*\/\,\.]+)',
                     lambda s: builder.make_python_identifier(s.group(1)),
                     line[2])
        expression = formatexpression(line[3], identifier, functiondic,
                                      macrolist)
        expression = re.sub(
            r'\[([^\]]*)\]', lambda s: '[' + ','.join(
                map(str, getelempos(s.group(1).replace('()', '')))) + ']' +
            getelempos.sumfor, expression)
        expression = re.sub(
            r'\,(%s\(\))\)' % subs, lambda s: ',' + ','.join(
                map(str, getnumofelements(s.group(1).replace('()', '')))) +
            ')', expression)
        expression = re.sub(r'%s' % lookuptofix, r'\1(', expression)
        subinexpression = '[' + ','.join(map(str, getelempos(sub))) + ']'
        if not re.search(
                r'(np.sum|functions.sumv|functions.prdv|functions.sclprd|smooth|delay|functions.tabhl)',
                expression):
            expression = re.sub(
                r'(\w+)\(\)\[\:(\,\:)*\]',
                r'functions.shorthander(\1(),\1.dimension_dir,loc_dimension_dir,_subscript_dict)'
                + subinexpression, expression.replace(' ', ''))
            # expression=re.sub(r'((?<!(\w|\[|\]|\+|\-|\.))[\d\.]+(?!(\w|\[|\]|\+|\-|\.)))',lambda s: s.group(1) if re.search(r'\.',s.group(1)) else s.group(1)+'.0',expression)
        init_val.append([identifier, [sub], [expression]])
    for pos1, value1 in enumerate(stocks):
        for pos2, value2 in enumerate(stocks):
            if pos1 != pos2:
                try:
                    if value1[0] == value2[0]:
                        stocks[pos1][1] += value2[1]
                        stocks[pos1][2] += value2[2]
                        stocks[pos2] = ''
                except:
                    pass
    stocks = filter(None, stocks)
    for pos1, value1 in enumerate(stocks):
        for pos2, value2 in enumerate(init_val):
            try:
                if value1[0] == value2[0]:
                    stocks[pos1][3] += value2[1]
                    stocks[pos1][4] += value2[2]
                    init_val[pos2] = ''
            except:
                pass
    init_val = filter(None, init_val)

    for line in stocks:
        try:
            Builder.add_stock(line[0], line[1], line[2], line[4], line[3])
        except:
            print line[0]

    flaux = []
    for line in auxiliaries:
        line = re.findall(r'([^\[\=]*)(\[([^\]]*)\])?\=(.*)', line)[0]
        identifier = builder.make_python_identifier(line[0])
        sub = re.sub(r'([^\[\]\(\)\+\-\*\/\,\.]+)',
                     lambda s: builder.make_python_identifier(s.group(1)),
                     line[2])

        expression = formatexpression(line[3], identifier, functiondic,
                                      macrolist)
        expression = re.sub(
            r'\[([^\]]*)\]', lambda s: '[' + ','.join(
                map(str, getelempos(s.group(1).replace('()', '')))) + ']' +
            getelempos.sumfor, expression)
        expression = re.sub(
            r'(\,(\w*)\(\)([\,\)]))', lambda s: ',' + ','.join(
                map(str, getnumofelements(s.group(2)))) + s.group(3)
            if s.group(2) in dictofsubs.keys() else s.group(1), expression)
        expression = re.sub(r'(?<!\w)%s' % lookuptofix, r'\1(', expression)
        subinexpression = '[' + ','.join(map(str, getelempos(sub))) + ']'
        if not re.search(
                r'(np.sum|functions.sumv|functions.prdv|functions.sclprd|smooth|delay|functions.tabhl)',
                expression):
            expression = re.sub(
                r'(\w+)\(\)\[\:(\,\:)*\]',
                r'functions.shorthander(\1(),\1.dimension_dir,loc_dimension_dir,_subscript_dict)'
                + subinexpression, expression.replace(' ', ''))
            # expression=re.sub(r'((?<!(\w|\[|\]|\+|\-|\.))[\d\.]+(?!(\w|\[|\]|\+|\-|\.)))',lambda s: s.group(1) if re.search(r'\.',s.group(1)) else s.group(1)+'.0',expression)
        expression = smooth(expression, [sub], Builder)
        expression = delay(expression, [sub], Builder)
        # if len(re.findall(r'(?<!\,|\[)[\+\-\*\/]',expression))!=len(re.findall(r'(?<!\,|\[)[\+\-\*\/]',line[3])):
        #     print identifier
        flaux.append([identifier, [sub], [expression]])
    for pos, line_init in enumerate(init_val):
        for line_aux in flaux:
            if line_init[0] == line_aux[0]:
                init_val[pos][0] = '_init_' + line_init[0]
        # init_val[pos][2]=['functions.initial("%s",'%(line_init[0]+line_init[1][0])+line_init[2][0]+')']
    flaux = flaux + init_val
    for pos1, value1 in enumerate(flaux):
        for pos2, value2 in enumerate(flaux):
            if pos1 != pos2:
                try:
                    if value1[0] == value2[0]:
                        flaux[pos1][1] += value2[1]
                        flaux[pos1][2] += value2[2]
                        flaux[pos2] = ''
                except:
                    pass
    flaux = filter(None, flaux)
    for line in flaux:
        Builder.add_flaux(line[0], line[1], line[2])

    Builder.write()
    macros(mdl_file, checking, functiondic)
    return new_model