def postAnalyzeFile(outputFile, bngLocation, database): """ Performs a postcreation file analysis based on context information """ #print('Transforming generated BNG file to BNG-XML representation for analysis') postAnalysisHelper(outputFile, bngLocation, database) # recreate file using information from the post analysis returnArray = analyzeHelper(database.document, database.reactionDefinitions, database.useID, outputFile, database.speciesEquivalence, database.atomize, database.translator, database) with open(outputFile, 'w') as f: f.write(returnArray.finalString) # recompute bng-xml file consoleCommands.bngl2xml(outputFile) bngxmlFile = '.'.join(outputFile.split('.')[:-1]) + '.xml' # recompute context information contextAnalysis = postAnalysis.ModelLearning(bngxmlFile) # get those species patterns that follow uncommon motifs motifSpecies, motifDefinitions = contextAnalysis.processContextMotifInformation(database.assumptions['lexicalVsstoch'], database) #motifSpecies, motifDefinitions = contextAnalysis.processAllContextInformation() if len(motifDefinitions) > 0: logMess('INFO:CTX003', 'Species with suspect context information were found. Information is being dumped to {0}_context.log'.format(outputFile)) with open('{0}_context.log'.format(outputFile), 'w') as f: pprint.pprint(dict(motifSpecies), stream=f) pprint.pprint(motifDefinitions, stream=f)
def changeToBNGL(functionList,rule,function): oldrule = '' #if the rule contains any mathematical function we need to reformat while any([re.search(r'(\W|^)({0})(\W|$)'.format(x),rule) != None for x in functionList]) and (oldrule != rule): oldrule = rule for x in functionList: rule = re.sub('({0})\(([^,]+),([^)]+)\)'.format(x),function,rule) if rule == oldrule: logMess('ERROR:Translation','Malformed pow or root function %s' % rule) print 'meep' return rule
def sanityCheck(database): ''' checks for critical atomization errors like isomorphism ''' stringrep = {x: str(database.translator[x]) for x in database.translator} repeats = set() for key in range(0, len(database.translator.keys()) - 1): for key2 in range(key + 1, len(database.translator.keys())): if stringrep[database.translator.keys()[key]] == stringrep[database.translator.keys()[key2]]: repeats.add((database.translator.keys()[key], database.translator.keys()[key2])) for repeat in repeats: temp = sorted(repeat) logMess('ERROR:SCT241', '{0}:{1}:produce the same translation:{2}:{1}:was empied'.format(temp[0], temp[1], database.prunnedDependencyGraph[temp[0]][0])) if temp[1] in database.translator: database.translator.pop(repeat[1])
def changeToBNGL(functionList, rule, function): oldrule = '' #if the rule contains any mathematical function we need to reformat while any([ re.search(r'(\W|^)({0})(\W|$)'.format(x), rule) != None for x in functionList ]) and (oldrule != rule): oldrule = rule for x in functionList: rule = re.sub('({0})\(([^,]+),([^)]+)\)'.format(x), function, rule) if rule == oldrule: logMess('ERROR', 'Malformed pow or root function %s' % rule) print 'meep' return rule
def propagateChanges(translator, dependencyGraph): flag = True while flag: flag = False for dependency in dependencyGraph: if dependencyGraph[dependency] == []: continue for molecule in dependencyGraph[dependency][0]: try: if updateSpecies(translator[dependency], translator[getTrueTag(dependencyGraph, molecule)].molecules[0]): flag = True except: logMess('CRITICAL:Program', 'Species is not being properly propagated') flag = False
def postAnalyzeFile(outputFile, bngLocation, database): """ Performs a postcreation file analysis based on context information """ #print('Transforming generated BNG file to BNG-XML representation for analysis') consoleCommands.setBngExecutable(bngLocation) outputDir = os.sep.join(outputFile.split(os.sep)[:-1]) if outputDir != '': retval = os.getcwd() os.chdir(outputDir) consoleCommands.bngl2xml(outputFile.split(os.sep)[-1]) if outputDir != '': os.chdir(retval) bngxmlFile = '.'.join(outputFile.split('.')[:-1]) + '.xml' #print('Sending BNG-XML file to context analysis engine') contextAnalysis = postAnalysis.ModelLearning(bngxmlFile) # analysis of redundant bonds deleteBonds = contextAnalysis.analyzeRedundantBonds(database.assumptions['redundantBonds']) modificationFlag = True for molecule in database.assumptions['redundantBondsMolecules']: if molecule[0] in deleteBonds: for bond in deleteBonds[molecule[0]]: database.translator[molecule[1]].deleteBond(bond) logMess('INFO:Atomization', 'Used context information to determine that the bond {0} in species {1} is not likely'.format(bond,molecule[1])) returnArray = analyzeHelper(database.document, database.reactionDefinitions, database.useID, outputFile, database.speciesEquivalence, database.atomize, database.translator) with open(outputFile, 'w') as f: f.write(returnArray.finalString) # recompute bng-xml file consoleCommands.bngl2xml(outputFile) bngxmlFile = '.'.join(outputFile.split('.')[:-1]) + '.xml' # recompute context information contextAnalysis = postAnalysis.ModelLearning(bngxmlFile) # get those species patterns that follow uncommon motifs motifSpecies, motifDefinitions = contextAnalysis.processContextMotifInformation(database.assumptions['lexicalVsstoch'], database) #motifSpecies, motifDefinitions = contextAnalysis.processAllContextInformation() if len(motifDefinitions) > 0: logMess('WARNING:ContextAnalysis', 'Species with suspect context information were found. Information is being dumped to {0}_context.log'.format(outputFile)) with open('{0}_context.log'.format(outputFile), 'w') as f: pprint.pprint(dict(motifSpecies), stream=f) pprint.pprint(motifDefinitions, stream=f)
def postAnalysisHelper(outputFile, bngLocation, database): consoleCommands.setBngExecutable(bngLocation) outputDir = os.sep.join(outputFile.split(os.sep)[:-1]) if outputDir != '': retval = os.getcwd() os.chdir(outputDir) consoleCommands.bngl2xml(outputFile.split(os.sep)[-1]) if outputDir != '': os.chdir(retval) bngxmlFile = '.'.join(outputFile.split('.')[:-1]) + '.xml' #print('Sending BNG-XML file to context analysis engine') contextAnalysis = postAnalysis.ModelLearning(bngxmlFile) # analysis of redundant bonds deleteBonds = contextAnalysis.analyzeRedundantBonds(database.assumptions['redundantBonds']) for molecule in database.assumptions['redundantBondsMolecules']: if molecule[0] in deleteBonds: for bond in deleteBonds[molecule[0]]: database.translator[molecule[1]].deleteBond(bond) logMess('INFO:CTX002', 'Used context information to determine that the bond {0} in species {1} is not likely'.format(bond,molecule[1]))
def printTranslate(chemical,tags,translator={}): tmp = [] if chemical[0] not in translator: app = chemical[0] + '()' + tags else: translator[chemical[0]].addCompartment(tags) app = str(translator[chemical[0]]) if float(int(chemical[1])) == chemical[1]: for item in range(0,int(chemical[1])): tmp.append(app) else: idx = logMess("ERROR:Simulation","Cannot deal with non integer stoicheometries: {0}* {1}".format(chemical[1],chemical[0])) tmp.append(app) return ' + '.join(tmp)
def printTranslate(chemical, tags, translator={}): tmp = [] if chemical[0] not in translator: app = chemical[0] + '()' + tags else: translator[chemical[0]].addCompartment(tags) app = str(translator[chemical[0]]) if float(int(chemical[1])) == chemical[1]: for item in range(0, int(chemical[1])): tmp.append(app) else: idx = logMess( "ERROR:Simulation", "Cannot deal with non integer stoicheometries: {0}* {1}".format( chemical[1], chemical[0])) tmp.append(app) return ' + '.join(tmp)
def atomize(dependencyGraph, weights, translator, reactionProperties, equivalenceDictionary, bioGridFlag, sbmlAnalyzer, database, parser): ''' The atomizer's main methods. Receives a dependency graph ''' redrawflag = True loops = 0 while redrawflag and loops < 10: loops +=1 bindingCounter = Counter() bindingFailureDict = {} for idx, element in enumerate(weights): # 0 molecule if element[0] == '0': continue # user defined molecules to be the zero molecule if dependencyGraph[element[0]] == [['0']]: zeroSpecies = st.Species() zeroMolecule = st.Molecule('0') zeroSpecies.addMolecule(zeroMolecule) translator[element[0]] = zeroSpecies continue # undivisible molecules elif dependencyGraph[element[0]] == []: if element[0] not in translator: translator[element[0]] = createEmptySpecies(element[0]) else: if len(dependencyGraph[element[0]][0]) == 1: # catalysis createCatalysisRBM(dependencyGraph, element, translator, reactionProperties, equivalenceDictionary, sbmlAnalyzer, database) else: try: createBindingRBM(element, translator, dependencyGraph, bioGridFlag, database.pathwaycommons, parser, database) except BindingException as e: for c in e.combinations: bindingCounter[c] += 1 bindingFailureDict[element[0]] = e.combinations logMess('DEBUG:ATO003', "We don't know how {0} binds together in complex {1}. Not atomizing".format( e.value, element[0])) # there awas an issue during binding, don't atomize translator[element[0]] = createEmptySpecies(element[0]) # evaluate species that weren't bound properly and see if we can get information from all over the model to find the right binding partner bindingTroubleLog = defaultdict(list) modifiedPairs = set() redrawflag = False for molecule in bindingFailureDict: bindingWinner = defaultdict(list) for candidateTuple in bindingFailureDict[molecule]: bindingWinner[bindingCounter[candidateTuple]].append(candidateTuple) bestBindingCandidates = bindingWinner[max(bindingWinner.keys())] if len(bestBindingCandidates) > 1: bindingTroubleLog[tuple(sorted(bestBindingCandidates))].append(molecule) else: bindingPair = bestBindingCandidates[0] if bindingPair not in modifiedPairs: modifiedPairs.add(bindingPair) else: continue c1 = st.Component(bindingPair[1].lower()) c2 = st.Component(bindingPair[0].lower()) molecule1 = translator[translator[bindingPair[0]].molecules[0].name].molecules[0] molecule2 = translator[translator[bindingPair[1]].molecules[0].name].molecules[0] molecule1.addComponent(c1) molecule2.addComponent(c2) redrawflag = True logMess('INFO:ATO031','Determining that {0} binds together based on frequency of the bond in the reaction network.'.format(bindingPair)) for trouble in bindingTroubleLog: logMess('ERROR:ATO202','{0}:{1}:We need information to resolve the bond structure of these complexes . \ Please choose among the possible binding candidates that had the most observed frequency in the reaction network or provide a new one'.format(bindingTroubleLog[trouble],trouble))
def createCatalysisRBM(dependencyGraph, element, translator, reactionProperties, equivalenceDictionary, sbmlAnalyzer, database): ''' if it's a catalysis reaction create a new component/state ''' if dependencyGraph[element[0]][0][0] == element[0]: if element[0] not in translator: translator[element[0]] = createEmptySpecies(element[0]) else: componentStateArray = [] tmp = element[0] existingComponents = [] memory = [] forceActivationSwitch = False while dependencyGraph[tmp] != []: # what kind of catalysis are we dealing with # classification = identifyReaction( # equivalenceDictionary, # dependencyGraph[tmp][0][0],tmp) classifications = None if not classifications: classifications = identifyReaction( equivalenceDictionary, dependencyGraph[tmp][0][0], tmp) classifications = classifications if classifications in reactionProperties else None if classifications is not None: classifications = [classifications] if not classifications: classifications = sbmlAnalyzer.findMatchingModification( tmp, dependencyGraph[tmp][0][0]) if not classifications: classifications = sbmlAnalyzer.findMatchingModification( element[0], dependencyGraph[tmp][0][0]) # if we know what classification it is then add the corresponding # components and states if classifications is not None: for classification in classifications: componentStateArray.append( reactionProperties[classification]) # classificationArray.append([classification, # tmp,dependencyGraph[tmp] # [0][0]]) existingComponents.append( reactionProperties[classification][0]) # if we don't know we can create a force 1:1 modification elif database.forceModificationFlag and classifications is None and not forceActivationSwitch: forceActivationSwitch = True baseName = getTrueTag(dependencyGraph, dependencyGraph[element[0]][0][0]) species = createEmptySpecies(baseName) componentStateArray.append(['{0}'.format(tmp), tmp]) if not (element[0] in database.userLabelDictionary and database.userLabelDictionary[element[0]][0][0] == baseName): logMess('WARNING:LAE002', 'adding forced transformation: {0}:{1}:{2}'.format( baseName, dependencyGraph[element[0]][0][0], element[0])) # return # bail out if we couldn't figure out what modification it is elif classifications is None: logMess('DEBUG:MSC001', 'unregistered modification: {0}:{1}'.format( element[0], dependencyGraph[element[0]])) memory.append(tmp) tmp = dependencyGraph[tmp][0][0] if tmp in memory: raise atoAux.CycleError(memory) baseName = getTrueTag( dependencyGraph, dependencyGraph[element[0]][0][0]) species = createEmptySpecies(baseName) # use the already existing structure if its in the # translator,otherwise empty if baseName in translator: species = translator[baseName] #modifiedSpecies = deepcopy(translator[dependencyGraph[element[0]][0][0]]) # modified species needs to start from the base speceis sine componentStateArray should contain the full set of modifications # check that this works correctly for double modifications modifiedSpecies = deepcopy(translator[baseName]) # this counter is here for multi level modification events (e.g. double # phosporylation) modificationCounter = { componentState[0]: 2 for componentState in componentStateArray} for componentState in componentStateArray: addComponentToMolecule(species, baseName, componentState[0]) addComponentToMolecule( modifiedSpecies, baseName, componentState[0]) tmp = addStateToComponent(species, baseName, componentState[0], componentState[1]) if tmp == componentState[1]: addStateToComponent(species, baseName, componentState[0], componentState[1] + componentState[1]) # this modification was already activated so create a second # modification component if addStateToComponent(modifiedSpecies, baseName, componentState[0], componentState[1]) == componentState[1]: componentName = '{0}{1}'.format( componentState[0], modificationCounter[componentState[0]]) modificationCounter[componentState[0]] += 1 addComponentToMolecule( modifiedSpecies, baseName, componentName) addStateToComponent(modifiedSpecies, baseName, componentName, componentState[1]) addStateToComponent(species, baseName, componentState[0], '0') # update the base species if len(componentStateArray) > 0: translator[baseName] = deepcopy(species) translator[element[0]] = modifiedSpecies
def getComplexationComponents2(moleculeName, species, bioGridFlag, pathwaycommonsFlag=False, parser=None, bondSeeding=[], bondExclusion=[], database=None): ''' method used during the atomization process. It determines how molecules in a species bind together ''' def sortMolecules(array, reverse): return sorted(array, key=lambda molecule: (len(molecule.components), len([ x for x in molecule.components if x.activeState not in [0, '0']]), len(str(molecule)), str(molecule)), reverse=reverse) def getBiggestMolecule(array): sortedMolecule = sortMolecules(array, reverse=False) #sortedMolecule = sorted(sortedMolecule, key=lambda rule: len(rule.components)) return sortedMolecule[-1] def getNamedMolecule(array, name): for molecule in sortMolecules(array, True): if molecule.name == name: return molecule speciesDict = {} # this array will contain all molecules that bind together pairedMolecules = [] for x in sortMolecules(species.molecules, reverse=True): for y in x.components: if y.name not in speciesDict: speciesDict[y.name] = [] speciesDict[y.name].append(x) # this array wil contain all molecules that dont bind to anything orphanedMolecules = [x for x in species.molecules] # seed the list of pairs from the seeds pairedMolecules = copy(bondSeeding) if bondSeeding: orphanedMolecules = [ x for x in orphanedMolecules for y in bondSeeding if x not in y] # determine how molecules bind together redundantBonds = [] for x in sortMolecules(species.molecules, reverse=True): for component in [y for y in x.components if y.name.lower() in speciesDict.keys()]: if x.name.lower() in speciesDict: if(x in speciesDict[component.name.lower()]) and component.name in [y.name.lower() for y in speciesDict[x.name.lower()]]: for mol in speciesDict[x.name.lower()]: if mol.name.lower() == component.name and x != mol and x in \ speciesDict[component.name]: speciesDict[x.name.lower()].remove(mol) speciesDict[component.name].remove(x) if x not in orphanedMolecules and mol not in orphanedMolecules: # FIXME: is it necessary to remove double bonds # in complexes? lhs = set([]) rhs = set([]) repeatedFlag = False for pair in pairedMolecules: if x in pair: lhs.add(pair[0]) lhs.add(pair[1]) elif mol in pair: rhs.add(pair[0]) rhs.add(pair[1]) # is this particular pair of molecules bound together? if x in pair and mol in pair: repeatedFlag = True break # this pair already exists if repeatedFlag: continue redundantBonds.append([x, mol]) intersection = lhs.intersection(rhs) redundantBonds[-1].extend(list(intersection)) if len(redundantBonds[-1]) < 3: redundantBonds.pop() # continue if [x, mol] not in bondSeeding and [mol, x] not in bondSeeding and [x, mol] not in bondExclusion and [mol, x] not in bondExclusion: pairedMolecules.append([x, mol]) if x in orphanedMolecules: orphanedMolecules.remove(x) if mol in orphanedMolecules: orphanedMolecules.remove(mol) if len(redundantBonds) > 0: for x in redundantBonds: if database: atoAux.addAssumptions( 'redundantBonds', tuple(sorted([y.name for y in x])), database.assumptions) atoAux.addAssumptions( 'redundantBondsMolecules', (tuple(sorted([y.name for y in x])), moleculeName), database.assumptions) logMess('WARNING:CTX001', 'Redundant bonds detected between molecules {0} in species {1}'.format( [y.name for y in x], moleculeName)) totalComplex = [set(x) for x in pairedMolecules] isContinuousFlag = True # iterate over orphaned and find unidirectional interactions # e.g. if a molecule has a previous known interaction with the # same kind of molecule, even if it has no available components # e.g. k-mers` for element in speciesDict: for individualMolecule in speciesDict[element]: if individualMolecule in orphanedMolecules: candidatePartner = [x for x in species.molecules if x.name.lower( ) == element and x != individualMolecule] if len(candidatePartner) == 1: pairedMolecules.append( [candidatePartner[0], individualMolecule]) orphanedMolecules.remove(individualMolecule) # determine which pairs form a continuous chain while isContinuousFlag: isContinuousFlag = False for idx in range(0, len(totalComplex) - 1): for idx2 in range(idx + 1, len(totalComplex)): if len([x for x in totalComplex[idx] if x in totalComplex[idx2]]) > 0: totalComplex[idx] = totalComplex[ idx].union(totalComplex[idx2]) totalComplex.pop(idx2) isContinuousFlag = True break if isContinuousFlag: break # now we process those molecules where we need to create a new component for element in orphanedMolecules: for mol1 in species.molecules: # when adding orphaned molecules make sure it's not already in # the list if mol1 == element and mol1 not in set().union(*totalComplex): totalComplex.append(set([mol1])) # now we process for those molecules we are not sure how do they bind while len(totalComplex) > 1: if len(totalComplex[0]) == 1 and len(totalComplex[1]) == 1: mol1 = list(totalComplex[0])[0] mol2 = list(totalComplex[1])[0] else: mol1, mol2 = solveComplexBinding(totalComplex, pathwaycommonsFlag, parser, database.prunnedDependencyGraph[moleculeName][0]) pairedMolecules.append([mol1, mol2]) totalComplex[0] = totalComplex[0].union(totalComplex[1]) totalComplex.pop(1) # totalComplex.extend(orphanedMolecules) return pairedMolecules
def solveComplexBinding(totalComplex, pathwaycommonsFlag, parser, compositionEntry): ''' given two binding complexes it will attempt to find the ways in which they bind using different criteria ''' def sortMolecules(array, reverse): return sorted(array, key=lambda molecule: (len(molecule.components), len([ x for x in molecule.components if x.activeState not in [0, '0']]), len(str(molecule)), str(molecule)), reverse=reverse) def getBiggestMolecule(array): sortedMolecule = sortMolecules(array, reverse=False) #sortedMolecule = sorted(sortedMolecule, key=lambda rule: len(rule.components)) return sortedMolecule[-1] def getNamedMolecule(array, name): for molecule in sortMolecules(array, True): if molecule.name == name: return molecule elif molecule.trueName == name: return molecule names1 = [str(x.trueName) for x in totalComplex[0] ] names2 = [str(x.trueName) for x in totalComplex[1] ] bioGridDict = {} # find all pairs of molecules comb = set([tuple(sorted([x, y])) for x in names1 for y in names2]) comb2 = set([tuple(sorted([x,y])) for x in compositionEntry for y in compositionEntry]) dbPair = set([]) combTemp = set() # search pathway commons for binding candidates if pathwaycommonsFlag: dbPair = isInComplexWith(comb, parser) else: for element in comb: if element[0].upper() in bioGridDict and element[1] in bioGridDict[element[0].upper()] or \ element[1].upper() in bioGridDict and element[0] in bioGridDict[element[1].upper()]: #logMess('INFO:ATO001', 'Biogrid info: {0}:{1}'.format(element[0], element[1])) dbPair.add((element[0], element[1])) # elif pathwaycommonsFlag: # if pwcm.isInComplexWith(element[0], element[1]): # dbPair.add((element[0], element[1])) dbPair = list(dbPair) if dbPair != []: mol1 = mol2 = None # select the best candidate if there's many ways to bind (in general # one that doesn't overlap with an already exising pair) finalDBpair = [] if len(dbPair) > 1: for element in dbPair: mset1 = Counter(element) mset2 = Counter(names1) mset3 = Counter(names2) intersection1 = mset1 & mset2 intersection2 = mset1 & mset3 intersection1 = list(intersection1.elements()) intersection2 = list(intersection2.elements()) if len(intersection1) < 2 and len(intersection2) < 2: finalDBpair.append(element) if len(finalDBpair) > 0: dbPair = finalDBpair if len(dbPair) > 1: # @FIXME: getNamedMolecule should never receive parameters that cause it to return null, but somehow that's what is happening # when you receive a malformed user definition file. The error # should be caught way before we reach this point tmpComplexSubset1 = [getNamedMolecule(totalComplex[0], element[0]) for element in dbPair if getNamedMolecule(totalComplex[0], element[0]) is not None] if not tmpComplexSubset1: tmpComplexSubset1 = [getNamedMolecule(totalComplex[0], element[ 1]) for element in dbPair if getNamedMolecule(totalComplex[0], element[1]) is not None] tmpComplexSubset2 = [getNamedMolecule(totalComplex[1], element[ 0]) for element in dbPair if getNamedMolecule(totalComplex[1], element[0]) is not None] else: tmpComplexSubset2 = [getNamedMolecule(totalComplex[1], element[ 1]) for element in dbPair if getNamedMolecule(totalComplex[1], element[1]) is not None] mol1 = getBiggestMolecule(tmpComplexSubset1) mol2 = getBiggestMolecule(tmpComplexSubset2) #was ATO002 logMess('WARNING:ATO111', "{0}-{1}:The two pairs can bind in these ways according to BioGrid/Pathwaycommons:{2}:Defaulting to:('{3}', '{4}')".format(names1, names2, dbPair, mol1.name, mol2.name)) else: mol1 = getNamedMolecule(totalComplex[0], dbPair[0][0]) if not mol1: mol1 = getNamedMolecule(totalComplex[1], dbPair[0][0]) mol2 = getNamedMolecule(totalComplex[0], dbPair[0][1]) else: mol2 = getNamedMolecule(totalComplex[1], dbPair[0][1]) if not mol2: mol1 = getNamedMolecule(totalComplex[1], dbPair[0][0]) mol2 = getNamedMolecule(totalComplex[0], dbPair[0][1]) logMess('INFO:ATO001', 'Binding information found in BioGrid/Pathwaycommons for for {0}-{1}'.format(mol1.name, mol2.name)) else: #mol1 = getBiggestMolecule(totalComplex[0]) #mol2 = getBiggestMolecule(totalComplex[1]) ''' if pathwaycommonsFlag: logMess('ERROR:ATO201', "We don't know how {0} and {1} bind together and there's no relevant BioGrid/Pathway commons information. Not atomizing".format( [x.name for x in totalComplex[0]], [x.name for x in totalComplex[1]])) # addAssumptions('unknownBond',(mol1.name,mol2.name)) else: logMess('ERROR:ATO202', "We don't know how {0} and {1} bind together. Not atomizing".format( [x.name for x in totalComplex[0]], [x.name for x in totalComplex[1]])) # addAssumptions('unknownBond',(mol1.name,mol2.name)) ''' raise BindingException( '{0}-{1}'.format(sorted([x.name for x in totalComplex[0]]), sorted([x.name for x in totalComplex[1]])), comb) return mol1, mol2
def analyzeHelper(document, reactionDefinitions, useID, outputFile, speciesEquivalence, atomize, translator, database, bioGrid=False): ''' taking the atomized dictionary and a series of data structure, this method does the actual string output. ''' useArtificialRules = False parser = SBML2BNGL(document.getModel(), useID) parser.setConversion(database.isConversion) #database = structures.Databases() #database.assumptions = defaultdict(set) #translator,log,rdf = m2c.transformMolecules(parser,database,reactionDefinitions,speciesEquivalence) #try: #bioGridDict = {} #if biogrid: # bioGridDict = biogrid() #if atomize: # translator = mc.transformMolecules(parser,database,reactionDefinitions,speciesEquivalence,bioGridDict) #else: # translator={} #except: # print 'failure' # return None,None,None,None #translator = {} param,zparam = parser.getParameters() rawSpecies = {} for species in parser.model.getListOfSpecies(): rawtemp = parser.getRawSpecies(species,[x.split(' ')[0] for x in param]) rawSpecies[rawtemp['identifier']] = rawtemp parser.reset() molecules, initialConditions, observables, speciesDict,\ observablesDict, annotationInfo = parser.getSpecies(translator, [x.split(' ')[0] for x in param]) # finally, adjust parameters and initial concentrations according to whatever initialassignments say param, zparam, initialConditions = parser.getInitialAssignments(translator, param, zparam, molecules, initialConditions) # FIXME: this method is a mess, improve handling of assignmentrules since we can actually handle those aParameters, aRules, nonzparam, artificialRules, removeParams, artificialObservables = parser.getAssignmentRules(zparam, param, rawSpecies, observablesDict, translator) compartments = parser.getCompartments() functions = [] assigmentRuleDefinedParameters = [] reactionParameters, rules, rateFunctions = parser.getReactions(translator, len(compartments) > 1, atomize=atomize, parameterFunctions=artificialObservables, database=database) functions.extend(rateFunctions) for element in nonzparam: param.append('{0} 0'.format(element)) param = [x for x in param if x not in removeParams] tags = '@{0}'.format(compartments[0].split(' ')[0]) if len(compartments) == 1 else '@cell' molecules.extend([x.split(' ')[0] for x in removeParams]) if len(molecules) == 0: compartments = [] observables.extend('Species {0} {0}'.format(x.split(' ')[0]) for x in removeParams) for x in removeParams: initialConditions.append(x.split(' ')[0] + tags + ' ' + ' '.join(x.split(' ')[1:])) ## Comment out those parameters that are defined with assignment rules ## TODO: I think this is correct, but it may need to be checked tmpParams = [] for idx, parameter in enumerate(param): for key in artificialObservables: if re.search('^{0}\s'.format(key),parameter)!= None: assigmentRuleDefinedParameters.append(idx) tmpParams.extend(artificialObservables) tmpParams.extend(removeParams) tmpParams = set(tmpParams) correctRulesWithParenthesis(rules,tmpParams) for element in assigmentRuleDefinedParameters: param[element] = '#' + param[element] deleteMolecules = [] deleteMoleculesFlag = True for key in artificialObservables: flag = -1 for idx,observable in enumerate(observables): if 'Species {0} {0}()'.format(key) in observable: flag = idx if flag != -1: observables.pop(flag) functions.append(artificialObservables[key]) flag = -1 if '{0}()'.format(key) in molecules: flag = molecules.index('{0}()'.format(key)) if flag != -1: if deleteMoleculesFlag: deleteMolecules.append(flag) else: deleteMolecules.append(key) #result =validateReactionUsage(molecules[flag],rules) #if result != None: # logMess('ERROR','Pseudo observable {0} in reaction {1}'.format(molecules[flag],result)) #molecules.pop(flag) flag = -1 for idx,specie in enumerate(initialConditions): if ':{0}('.format(key) in specie: flag = idx if flag != -1: initialConditions[flag] = '#' + initialConditions[flag] for flag in sorted(deleteMolecules,reverse=True): if deleteMoleculesFlag: logMess('WARNING:SIM101','{0} reported as function, but usage is ambiguous'.format(molecules[flag]) ) result = validateReactionUsage(molecules[flag], rules) if result is not None: logMess('ERROR:Simulation','Pseudo observable {0} in reaction {1}'.format(molecules[flag],result)) #since we are considering it an observable delete it from the molecule and #initial conditions list #s = molecules.pop(flag) #initialConditions = [x for x in initialConditions if '$' + s not in x] else: logMess('WARNING:SIM101','{0} reported as species, but usage is ambiguous.'.format(flag) ) artificialObservables.pop(flag) sbmlfunctions = parser.getSBMLFunctions() functions.extend(aRules) #print functions processFunctions(functions,sbmlfunctions,artificialObservables,rateFunctions) for interation in range(0,3): for sbml2 in sbmlfunctions: for sbml in sbmlfunctions: if sbml == sbml2: continue if sbml in sbmlfunctions[sbml2]: sbmlfunctions[sbml2] = writer.extendFunction(sbmlfunctions[sbml2],sbml,sbmlfunctions[sbml]) functions = reorderFunctions(functions) functions = changeNames(functions, aParameters) # change reference for observables with compartment name functions = changeNames(functions, observablesDict) # print [x for x in functions if 'functionRate60' in x] functions = unrollFunctions(functions) rules = changeRates(rules, aParameters) if len(compartments) > 1 and 'cell 3 1.0' not in compartments: compartments.append('cell 3 1.0') #sbml always has the 'cell' default compartment, even when it #doesn't declare it elif len(compartments) == 0 and len(molecules) != 0: compartments.append('cell 3 1.0') if len(artificialRules) + len(rules) == 0: logMess('ERROR:SIM203','The file contains no reactions') if useArtificialRules or len(rules) == 0: rules =['#{0}'.format(x) for x in rules] evaluate = evaluation(len(observables),translator) artificialRules.extend(rules) rules = artificialRules else: artificialRules =['#{0}'.format(x) for x in artificialRules] evaluate = evaluation(len(observables),translator) rules.extend(artificialRules) commentDictionary = {} if atomize: commentDictionary['notes'] = "'This is an atomized translation of an SBML model created on {0}.".format(time.strftime("%d/%m/%Y")) else: commentDictionary['notes'] = "'This is a plain translation of an SBML model created on {0}.".format(time.strftime("%d/%m/%Y")) commentDictionary['notes'] += " The original model has {0} molecules and {1} reactions. The translated model has {2} molecules and {3} rules'".format(parser.model.getNumSpecies(),parser.model.getNumReactions(),len(molecules),len(set(rules))) meta = parser.getMetaInformation(commentDictionary) finalString = writer.finalText(meta, param + reactionParameters, molecules, initialConditions, list(OrderedDict.fromkeys(observables)), list(OrderedDict.fromkeys(rules)), functions, compartments, annotationInfo, outputFile) logMess('INFO:SUM001','File contains {0} molecules out of {1} original SBML species'.format(len(molecules), len(observables))) # rate of each classified rule evaluate2 = 0 if len(observables) == 0 else len(molecules)*1.0/len(observables) # add unit information to annotations annotationInfo['units'] = parser.getUnitDefinitions() return AnalysisResults(len(rules), len(observables), evaluate, evaluate2, len(compartments), parser.getSpeciesAnnotation(), finalString, speciesDict, None, annotationInfo) '''
def bnglFunction(rule, functionTitle, reactants, compartments=[], parameterDict={}, reactionDict={}): def powParse(match): if match.group(1) == 'root': exponent = '(1/%s)' % match.group(3) else: exponent = match.group(3) if match.group(1) in ['root', 'pow']: operator = '^' return '({0}){1}({2})'.format(match.group(2), operator, exponent) def compParse(match): translator = { 'gt': '>', 'lt': '<', 'and': '&&', 'or': '||', 'geq': '>=', 'leq': '<=', 'eq': '==' } exponent = match.group(3) operator = translator[match.group(1)] return '{0} {1} {2}'.format(match.group(2), operator, exponent) def ceilfloorParse(math): flag = False if math.group(1) == 'ceil': flag = True if flag: return 'min(rint({0}+0.5),rint({0} + 1))'.format(math.group(2)) else: return 'min(rint({0}-0.5),rint({0}+0.5))'.format(math.group(2)) def parameterRewrite(match): return match.group(1) + 'param_' + match.group(2) + match.group(3) def constructFromList(argList, optionList): parsedString = '' idx = 0 translator = { 'gt': '>', 'lt': '<', 'and': '&&', 'or': '||', 'geq': '>=', 'leq': '<=', 'eq': '==' } while idx < len(argList): if type(argList[idx]) is list: parsedString += '(' + constructFromList( argList[idx], optionList) + ')' elif argList[idx] in optionList: if argList[idx] == 'ceil': parsedString += 'min(rint(({0}) + 0.5),rint(({0}) + 1))'.format( constructFromList(argList[idx + 1], optionList)) idx += 1 elif argList[idx] == 'floor': parsedString += 'min(rint(({0}) -0.5),rint(({0}) + 0.5))'.format( constructFromList(argList[idx + 1], optionList)) idx += 1 elif argList[idx] in ['pow']: index = rindex(argList[idx + 1], ',') parsedString += '((' + constructFromList( argList[idx + 1][0:index], optionList) + ')' parsedString += ' ^ ' + '(' + constructFromList( argList[idx + 1][index + 1:], optionList) + '))' idx += 1 elif argList[idx] in ['sqr', 'sqrt']: tag = '1/' if argList[idx] == 'sqrt' else '' parsedString += '((' + constructFromList( argList[idx + 1], optionList) + ') ^ ({0}2))'.format(tag) idx += 1 elif argList[idx] == 'root': index = rindex(argList[idx + 1], ',') tmp = '1/(' + constructFromList(argList[idx + 1][0:index], optionList) + '))' parsedString += '((' + constructFromList( argList[idx + 1][index + 1:], optionList) + ') ^ ' + tmp idx += 1 elif argList[idx] == 'piecewise': index1 = argList[idx + 1].index(',') try: index2 = argList[idx + 1][index1 + 1:].index(',') + index1 + 1 try: index3 = argList[idx + 1][index2 + 1:].index(',') + index2 + 1 except ValueError: index3 = -1 except ValueError: parsedString += constructFromList( [argList[idx + 1][index1 + 1:]], optionList) index2 = -1 if index2 != -1: condition = constructFromList( [argList[idx + 1][index1 + 1:index2]], optionList) result = constructFromList([argList[idx + 1][:index1]], optionList) if index3 == -1: result2 = constructFromList( [argList[idx + 1][index2 + 1:]], optionList) else: result2 = constructFromList( ['piecewise', argList[idx + 1][index2 + 1:]], optionList) parsedString += 'if({0},{1},{2})'.format( condition, result, result2) idx += 1 elif argList[idx] in ['and', 'or']: symbolDict = {'and': ' && ', 'or': ' || '} indexArray = [-1] elementArray = [] for idx2, element in enumerate(argList[idx + 1]): if element == ',': indexArray.append(idx2) indexArray.append(len(argList[idx + 1])) tmpStr = argList[idx + 1] for idx2, _ in enumerate(indexArray[0:-1]): elementArray.append( constructFromList( tmpStr[indexArray[idx2] + 1:indexArray[idx2 + 1]], optionList)) parsedString += symbolDict[argList[idx]].join(elementArray) idx += 1 elif argList[idx] == 'lambda': tmp = '(' upperLimit = rindex(argList[idx + 1], ',') parsedParams = [] for x in argList[idx + 1][0:upperLimit]: if x == ',': tmp += ', ' else: tmp += 'param_' + x parsedParams.append(x) #tmp = ''.join([x for x in constructFromList(argList[idx+1][0:upperLimit])]) tmp2 = ') = ' + constructFromList( argList[idx + 1][rindex(argList[idx + 1], ',') + 1:], optionList) for x in parsedParams: while re.search(r'(\W|^)({0})(\W|$)'.format(x), tmp2) != None: tmp2 = re.sub(r'(\W|^)({0})(\W|$)'.format(x), r'\1param_\2 \3', tmp2) idx += 1 parsedString += tmp + tmp2 else: parsedString += argList[idx] idx += 1 return parsedString def changeToBNGL(functionList, rule, function): oldrule = '' #if the rule contains any mathematical function we need to reformat while any([ re.search(r'(\W|^)({0})(\W|$)'.format(x), rule) != None for x in functionList ]) and (oldrule != rule): oldrule = rule for x in functionList: rule = re.sub('({0})\(([^,]+),([^)]+)\)'.format(x), function, rule) if rule == oldrule: logMess('ERROR', 'Malformed pow or root function %s' % rule) print 'meep' return rule #rule = changeToBNGL(['pow','root'],rule,powParse) rule = changeToBNGL(['gt', 'lt', 'leq', 'geq', 'eq'], rule, compParse) #rule = changeToBNGL(['and','or'],rule,compParse) flag = True contentRule = pyparsing.Word( pyparsing.alphanums + '_' ) | ',' | '.' | '+' | '-' | '*' | '/' | '^' | '&' | '>' | '<' | '=' | '|' parens = pyparsing.nestedExpr('(', ')', content=contentRule) finalString = '' #remove ceil,floor if any([ re.search(r'(\W|^)({0})(\W|$)'.format(x), rule) != None for x in ['ceil', 'floor', 'pow', 'sqrt', 'sqr', 'root', 'and', 'or'] ]): argList = parens.parseString('(' + rule + ')').asList() rule = constructFromList( argList[0], ['floor', 'ceil', 'pow', 'sqrt', 'sqr', 'root', 'and', 'or']) while 'piecewise' in rule: argList = parens.parseString('(' + rule + ')').asList() rule = constructFromList(argList[0], ['piecewise']) #remove references to lambda functions if 'lambda(' in rule: lambdaList = parens.parseString('(' + rule + ')') functionBody = constructFromList(lambdaList[0].asList(), ['lambda']) flag = False rule = '{0}{1}'.format(functionTitle, functionBody) tmp = rule #delete the compartment from the rate function since cBNGL already does it for compartment in compartments: tmp = re.sub('^{0}\s*[*]'.format(compartment[0]), '', tmp) tmp = re.sub('([*]\s*{0})$'.format(compartment[0]), '', tmp) if compartment[0] in tmp: tmp = re.sub(r'(\W|^)({0})(\W|$)'.format(compartment[0]), r'\1 {0} \3'.format(str(compartment[1])), tmp) #tmp = re.sub(r'(\W)({0})(\W)'.format(compartment[0]),r'\1%s\3' % str(compartment[1]),tmp) logMess( 'INFO', 'Exchanging reference to compartment %s for its dimensions' % compartment[0]) #change references to time for time() #tmp =re.sub(r'(\W|^)(time)(\W|$)',r'\1time()\3',tmp) #tmp =re.sub(r'(\W|^)(Time)(\W|$)',r'\1time()\3',tmp) while re.search(r'(\W|^)inf(\W|$)', tmp) != None: tmp = re.sub(r'(\W|^)(inf)(\W|$)', r'\1 1e20 \3', tmp) #BNGL has ^ for power. if flag: finalString = '%s = %s' % (functionTitle, tmp) else: finalString = tmp #change references to local parameters for parameter in parameterDict: finalString = re.sub(r'(\W|^)({0})(\W|$)'.format(parameter), r'\g<1>{0}\g<3>'.format(parameterDict[parameter]), finalString) #change references to reaction Id's to their netflux equivalent for reaction in reactionDict: if reaction in finalString: finalString = re.sub( r'(\W|^)({0})(\W|$)'.format(reaction), r'\g<1>{0}\g<3>'.format(reactionDict[reaction]), finalString) #combinations '+ -' break ibonetgen finalString = re.sub(r'(\W|^)([-])(\s)+', r'\1-', finalString) #changing reference of 't' to time() #finalString = re.sub(r'(\W|^)(t)(\W|$)',r'\1time()\3',finalString) #pi finalString = re.sub(r'(\W|^)(pi)(\W|$)', r'\g<1>3.1415926535\g<3>', finalString) #print reactants,finalString #log for log 10 finalString = re.sub(r'(\W|^)log\(', r'\1 ln(', finalString) #reserved keyword: e finalString = re.sub(r'(\W|^)(e)(\W|$)', r'\g<1>are\g<3>', finalString) #changing ceil #avoiding variables whose name starts with a number #removing mass-action elements tmp = finalString #print finalString,reactants #for reactant in reactants: # finalString = re.sub(r'(\W|^)({0}\s+\*)'.format(reactant[0]),r'\1',finalString) # finalString = re.sub(r'(\W|^)(\*\s+{0}(\s|$))'.format(reactant[0]),r'\1',finalString) #print finalString #if finalString != tmp: # logMess('WARNING','Removed mass action elements from ) return finalString
def bnglFunction(rule,functionTitle,reactants,compartments=[],parameterDict={},reactionDict={}): def powParse(match): if match.group(1) == 'root': exponent = '(1/%s)' % match.group(3) else: exponent = match.group(3) if match.group(1) in ['root','pow']: operator = '^' return '({0}){1}({2})'.format(match.group(2),operator,exponent) def compParse(match): translator = {'gt':'>','lt':'<','and':'&&','or':'||','geq':'>=','leq':'<=','eq':'=='} exponent = match.group(3) operator = translator[match.group(1)] return '{0} {1} {2}'.format(match.group(2),operator,exponent) def ceilfloorParse(math): flag = False if math.group(1) == 'ceil': flag = True if flag: return 'min(rint({0}+0.5),rint({0} + 1))'.format(math.group(2)) else: return 'min(rint({0}-0.5),rint({0}+0.5))'.format(math.group(2)) def parameterRewrite(match): return match.group(1) + 'param_' + match.group(2) + match.group(3) def constructFromList(argList,optionList): parsedString = '' idx = 0 translator = {'gt':'>','lt':'<','and':'&&','or':'||','geq':'>=','leq':'<=','eq':'=='} while idx < len(argList): if type(argList[idx]) is list: parsedString += '(' + constructFromList(argList[idx],optionList) + ')' elif argList[idx] in optionList: if argList[idx] == 'ceil': parsedString += 'min(rint(({0}) + 0.5),rint(({0}) + 1))'.format(constructFromList(argList[idx+1],optionList)) idx += 1 elif argList[idx] == 'floor': parsedString += 'min(rint(({0}) -0.5),rint(({0}) + 0.5))'.format(constructFromList(argList[idx+1],optionList)) idx += 1 elif argList[idx] in ['pow']: index = rindex(argList[idx+1],',') parsedString += '(('+ constructFromList(argList[idx+1][0:index],optionList) + ')' parsedString += ' ^ ' + '(' + constructFromList(argList[idx+1][index+1:] ,optionList) + '))' idx += 1 elif argList[idx] in ['sqr','sqrt']: tag = '1/' if argList[idx] == 'sqrt' else '' parsedString += '((' + constructFromList(argList[idx+1],optionList) + ') ^ ({0}2))'.format(tag) idx += 1 elif argList[idx] == 'root': index = rindex(argList[idx+1],',') tmp = '1/('+ constructFromList(argList[idx+1][0:index],optionList) + '))' parsedString += '((' + constructFromList(argList[idx+1][index+1:] ,optionList) + ') ^ ' + tmp idx += 1 elif argList[idx] == 'piecewise': index1 = argList[idx+1].index(',') try: index2 = argList[idx+1][index1+1:].index(',') + index1+1 try: index3 = argList[idx+1][index2+1:].index(',') + index2+1 except ValueError: index3 = -1 except ValueError: parsedString += constructFromList([argList[idx+1][index1+1:]],optionList) index2 = -1 if index2 != -1: condition = constructFromList([argList[idx+1][index1+1:index2]],optionList) result = constructFromList([argList[idx+1][:index1]],optionList) if index3 == -1: result2 = constructFromList([argList[idx+1][index2+1:]],optionList) else: result2 = constructFromList(['piecewise', argList[idx+1][index2+1:]],optionList) parsedString += 'if({0},{1},{2})'.format(condition,result,result2) idx+=1 elif argList[idx] in ['and', 'or']: symbolDict = {'and':' && ','or':' || '} indexArray = [-1] elementArray = [] for idx2,element in enumerate(argList[idx+1]): if element ==',': indexArray.append(idx2) indexArray.append(len(argList[idx+1])) tmpStr = argList[idx+1] for idx2,_ in enumerate(indexArray[0:-1]): elementArray.append(constructFromList(tmpStr[indexArray[idx2]+1:indexArray[idx2+1]],optionList)) parsedString += symbolDict[argList[idx]].join(elementArray) idx+=1 elif argList[idx] == 'lambda': tmp = '(' upperLimit = rindex(argList[idx+1],',') parsedParams = [] for x in argList[idx+1][0:upperLimit]: if x == ',': tmp += ', ' else: tmp += 'param_' + x parsedParams.append(x) #tmp = ''.join([x for x in constructFromList(argList[idx+1][0:upperLimit])]) tmp2 = ') = ' + constructFromList(argList[idx+1][rindex(argList[idx+1],',')+1:],optionList) for x in parsedParams: while re.search(r'(\W|^)({0})(\W|$)'.format(x),tmp2) != None: tmp2 = re.sub(r'(\W|^)({0})(\W|$)'.format(x),r'\1param_\2 \3',tmp2) idx+= 1 parsedString += tmp + tmp2 else: parsedString += argList[idx] idx += 1 return parsedString def changeToBNGL(functionList,rule,function): oldrule = '' #if the rule contains any mathematical function we need to reformat while any([re.search(r'(\W|^)({0})(\W|$)'.format(x),rule) != None for x in functionList]) and (oldrule != rule): oldrule = rule for x in functionList: rule = re.sub('({0})\(([^,]+),([^)]+)\)'.format(x),function,rule) if rule == oldrule: logMess('ERROR','Malformed pow or root function %s' % rule) print 'meep' return rule #rule = changeToBNGL(['pow','root'],rule,powParse) rule = changeToBNGL(['gt','lt','leq','geq','eq'],rule,compParse) #rule = changeToBNGL(['and','or'],rule,compParse) flag = True contentRule = pyparsing.Word(pyparsing.alphanums + '_') | ',' | '.' | '+' | '-' | '*' | '/' | '^' | '&' | '>' | '<' | '=' | '|' parens = pyparsing.nestedExpr( '(', ')', content=contentRule) finalString = '' #remove ceil,floor if any([re.search(r'(\W|^)({0})(\W|$)'.format(x),rule) != None for x in ['ceil','floor','pow','sqrt','sqr','root','and','or']]): argList = parens.parseString('('+ rule + ')').asList() rule = constructFromList(argList[0],['floor','ceil','pow','sqrt','sqr','root','and','or']) while 'piecewise' in rule: argList = parens.parseString('('+ rule + ')').asList() rule = constructFromList(argList[0],['piecewise']) #remove references to lambda functions if 'lambda(' in rule: lambdaList = parens.parseString('(' + rule + ')') functionBody = constructFromList(lambdaList[0].asList(),['lambda']) flag = False rule = '{0}{1}'.format(functionTitle,functionBody) tmp = rule #delete the compartment from the rate function since cBNGL already does it for compartment in compartments: tmp = re.sub('^{0}\s*[*]'.format(compartment[0]),'',tmp) tmp = re.sub('([*]\s*{0})$'.format(compartment[0]),'',tmp) if compartment[0] in tmp: tmp =re.sub(r'(\W|^)({0})(\W|$)'.format(compartment[0]),r'\1 {0} \3'.format(str(compartment[1])),tmp) #tmp = re.sub(r'(\W)({0})(\W)'.format(compartment[0]),r'\1%s\3' % str(compartment[1]),tmp) logMess('INFO','Exchanging reference to compartment %s for its dimensions' % compartment[0]) #change references to time for time() #tmp =re.sub(r'(\W|^)(time)(\W|$)',r'\1time()\3',tmp) #tmp =re.sub(r'(\W|^)(Time)(\W|$)',r'\1time()\3',tmp) while re.search(r'(\W|^)inf(\W|$)',tmp) != None: tmp =re.sub(r'(\W|^)(inf)(\W|$)',r'\1 1e20 \3',tmp) #BNGL has ^ for power. if flag: finalString = '%s = %s' % (functionTitle,tmp) else: finalString = tmp #change references to local parameters for parameter in parameterDict: finalString = re.sub(r'(\W|^)({0})(\W|$)'.format(parameter),r'\g<1>{0}\g<3>'.format(parameterDict[parameter]),finalString) #change references to reaction Id's to their netflux equivalent for reaction in reactionDict: if reaction in finalString: finalString = re.sub(r'(\W|^)({0})(\W|$)'.format(reaction),r'\g<1>{0}\g<3>'.format(reactionDict[reaction]),finalString) #combinations '+ -' break ibonetgen finalString = re.sub(r'(\W|^)([-])(\s)+',r'\1-',finalString) #changing reference of 't' to time() #finalString = re.sub(r'(\W|^)(t)(\W|$)',r'\1time()\3',finalString) #pi finalString = re.sub(r'(\W|^)(pi)(\W|$)',r'\g<1>3.1415926535\g<3>',finalString) #print reactants,finalString #log for log 10 finalString = re.sub(r'(\W|^)log\(',r'\1 ln(',finalString) #reserved keyword: e finalString = re.sub(r'(\W|^)(e)(\W|$)',r'\g<1>are\g<3>',finalString) #changing ceil #avoiding variables whose name starts with a number #removing mass-action elements tmp = finalString #print finalString,reactants #for reactant in reactants: # finalString = re.sub(r'(\W|^)({0}\s+\*)'.format(reactant[0]),r'\1',finalString) # finalString = re.sub(r'(\W|^)(\*\s+{0}(\s|$))'.format(reactant[0]),r'\1',finalString) #print finalString #if finalString != tmp: # logMess('WARNING','Removed mass action elements from ) return finalString