def setModel(self, model): """ set the MetabolicModel and reset flux and related variables """ self.model = model self.flux = MetabolicFlux() self.metFlux, self.inRatios, self.outRatios = {}, {}, {} self.minFlux, self.maxFlux = {}, {}
def main(): # 1. Parse command line usage = "Usage: %prog <solution-file> [options]" version = "%prog\n" + COPYRIGHT_VERSION_STRING parser = OptionParser(usage=usage, version=version) parser.add_option("-r", "--reactions", dest="reactionFile", help="use " "reversibilities from reaction FILE", metavar="FILE") parser.add_option("-p", "--parameters", dest="paramFile", help="use constraints from the given scenario FILE", metavar="FILE") options, args = parser.parse_args() # 2. Read solution file solution = MetabolicFlux() try: solution.readFromFile(args[0]) except IndexError: print("Error: No solution file given.\nUsage is\n " + os.path.basename(sys.argv[0]) + " <solution-file> [options]") exit() except IOError, strerror: print("An error occurred while trying to read file %s:" % os.path.basename(args[0])) print strerror exit()
def main(): # 1. Parse command line usage = "Usage: %prog <file1> <file2> [options]" version = "%prog\n" + COPYRIGHT_VERSION_STRING parser = OptionParser(usage=usage, version=version) parser.add_option("-o", "--output", dest="outputFile", help="output FILE " "(output is written to console if this is missing)", metavar="FILE") parser.add_option("-c", "--cutoff", dest="cutoff", help="Cutoff below which" " difference is considered zero; default is 1E-10") parser.set_defaults(cutoff="1E-10") options, args = parser.parse_args() if len(args) < 2: print "Error: Need two solution files." print("Usage is\n " + os.path.basename(sys.argv[0]) + " <file1> " "<file2> [options]") exit() try: cutoff = float(options.cutoff) except ValueError: print "Error: Invalid floating point value for cutoff." exit() if cutoff < 0.: print( "Warning: Cutoff is less than zero. Setting cutoff to zero, " "i.e. no cutoff.") cutoff = 0. basename = map(os.path.basename, args) # Use full path for identifying files if basenames are identical if basename[0] == basename[1]: basename = args # 2. Parse solution files and compute absolute differences solution = MetabolicFlux(), MetabolicFlux() for i in range(2): # Get pair of MetabolicFlux objects try: solution[i].readFromFile(args[i]) except IOError, strerror: print("An error occurred while trying to read file %s:" % os.path.basename(basename[i])) print strerror exit() except SyntaxError, strerror: print "An error occurred parsing file %s:" % basename[i] print strerror exit()
def checkSyntax(self): """ check assertions for syntax errors without actually performing any potentially time-consuming analyses This function prints a message to the console for every assertion that contains a syntax error. Returns: True if all assertions can be evaluated, False if error occurs """ # Backup member variables and re-initialize with dummy values to make # sure that all references exist bakflux, bakMetFlux = self.flux, self.metFlux bakInRatios, bakOutRatios = self.inRatios, self.outRatios bakMinFlux, bakMaxFlux = self.minFlux, self.maxFlux self.flux = MetabolicFlux(self.model, [0.] * len(self.model)) self.metFlux = dict( zip(self.model.metabolites, [0.] * len(self.model.metabolites))) self.inRatios = dict( zip(self.model.metabolites, [{}] * len(self.model.metabolites))) self.outRatios = dict( zip(self.model.metabolites, [{}] * len(self.model.metabolites))) self.minFlux = dict( zip(self.model.reactionDict.keys(), [0.] * len(self.model))) self.maxFlux = dict( zip(self.model.reactionDict.keys(), [0.] * len(self.model))) excepted = [] for i in range(len(self.assertions)): try: eval(self.assertions[i]) except (SyntaxError, NameError): excepted.append(repr(self.assertions_orig[i])) # Restore member variables to original values self.flux, self.metFlux = bakflux, bakMetFlux self.inRatios, self.outRatios = bakInRatios, bakOutRatios self.minFlux, self.maxFlux = bakMinFlux, bakMaxFlux if excepted: print( "The following assertions cannot be evaluated due to errors" ":\n " + "\n ".join(excepted)) # The following was removed because the Python error messages are misleading. # for a in excepted: # print "\n %r\n %s: %s\n" % (a[0], a[1], a[2]) return False return True
def parseSolutionFileByHandle(f): """ read the FVA solution file given as a file object (must be open) Returns: (fbaSolution, minmax) tuple with fbaSolution -- MetabolicFlux object with flux distribution, and lb, ub minmax -- dict { reaction : flux minimum, flux maximum } """ reactions = set() reactionVec, fluxVec = [], [] boundsDict = {} minmax = {} line_no = 0 for line in f: line_no += 1 if (line.lstrip().upper().startswith("NAME") or line == "" or line.isspace()): continue try: rea, values_str = map(str.rstrip, line.split(":")) except ValueError: raise SyntaxError("Syntax error in line %u:\nLine must " "contain exactly one colon (':')." % line_no) if rea in reactions: raise SyntaxError("Syntax error in line %u: Duplicate " "reaction." % line_no) reactions.add(rea) reactionVec.append(rea) try: values = map(float, values_str.split(None, 6)[:6]) except ValueError: raise SyntaxError("Syntax error in line %u: Invalid " "floating point value." % line_no) try: fluxVec.append(values[1]) minmax[rea] = values[0], values[2] boundsDict[rea] = values[4], values[5] except IndexError: raise SyntaxError("Syntax error in line %u:\nLine must " "contain exactly six values (min_flux, " "fba_flux, max_flux, diff, lb, ub)." % line_no) return MetabolicFlux(reactionVec, fluxVec, boundsDict), minmax
def parallel_ko_worker(comm, model, solver, wtVec, fbaParams, koGroups=None, weights=None, numIter=1, useMoma=When.AUTO, lethalityCutoff=_CUTOFF_LETHAL): """ compute process (worker) for parallel knockout analysis Keyword arguments: comm -- MPI communicator model -- the MetabolicModel solver -- name of QP/NLP solver (or "default") wtVec -- FBA solution for wildtype fbaParams -- parameters for FBA (incl. linear equality and inequality constraints) koGroups -- list of pairs (group name, list of reactions) weights -- weight vector for weighted MOMA (None -> perform regular MOMA, else: weight flux i with weights[i]) numIter -- number of NLP runs (if NLP solver is used) for MOMA useMoma -- selector of optimization strategy, enum values (class When): NEVER - always perform FBA (LP) AUTO - perform FBA first, followed by MOMA if not lethal ALWAYS - always perform MOMA (QP) lethalityCutoff -- threshold for biomass flux signifying lethality """ if not koGroups: koGroups = _makeDefaultKoGroups(model) # print "worker: %d reactions, %d knockout groups" % (len(model), # len(koGroups)) wtSolution = MetabolicFlux(model, wtVec) nanVec = array([nan] * len(wtVec)) infVec = array([inf] * len(wtVec)) moma = MomaAnalyzer(solver) # index of reaction group to be knocked out (as 0-dimensional numpy array) i = array(0, 'i') comm.Recv(i) while i >= 0: group, reaList = koGroups[i] # Restrict flux through all reactions in group to zero tmp_lb = {} # {index in model : LB value} tmp_ub = {} # { - '' - : UB '' } for rea in reaList: try: rIndex = model.reactionDict[rea] except KeyError: # Skip any blocked reactions (knockout has no effect) continue tmp_lb[rIndex] = model.reactions[rIndex].lb tmp_ub[rIndex] = model.reactions[rIndex].ub model.reactions[rIndex].lb = model.reactions[rIndex].ub = 0. if not tmp_lb: print " - skipped group '%s' (only blocked reactions)" % group comm.Send(wtVec) comm.Recv(i) continue obj_val = lethalityCutoff + 1. # Perform FBA first to check for lethality if useMoma != When.ALWAYS: fba = FbAnalyzer(fbaParams.solver) # Note: Model is already reduced obj_val, solutionFlux = fba.runOnModel(model, fbaParams, rmDeadEnds=False)[:2] status = SolverStatus.PREPARED if not solutionFlux: obj_val = 0. # Perform MOMA if knockout not already predicted by FBA to be lethal if useMoma != When.NEVER and abs(obj_val) > lethalityCutoff: solutionFlux, status = moma.runOnModel(model, wtSolution, fbaParams.linConstraints, numIter, weights)[1:3] # Send result to dispatcher if status == SolverStatus.UNKNOWN: # If status is 'unknown, not converged', send vector with only 'nan' # entries comm.Send(nanVec) elif status in (SolverStatus.PRIM_INFEAS, SolverStatus.DUAL_INFEAS): # If status is 'infeasible', send vector with only 'inf' entries comm.Send(infVec) elif len(solutionFlux) == 0: if status == SolverStatus.PREPARED: # FBA infeasible comm.Send(infVec) else: comm.Send(nanVec) else: solutionVec = array(solutionFlux.getVecOrderedByModel(model)) comm.Send(solutionVec) # Restore original constraints for rea in reaList: try: rIndex = model.reactionDict[rea] except KeyError: continue model.reactions[rIndex].lb = tmp_lb[rIndex] model.reactions[rIndex].ub = tmp_ub[rIndex] # Get next index comm.Recv(i)
def parallel_ko_dispatch(comm, numprocs, model, solver, objective, wtSolution, wtObjVal=None, koGroups=None, filePrefix=None, weights=None): """ root process (dispatcher) for parallel knockout analysis Keyword arguments: comm -- MPI communicator numprocs -- number of processes (including root process) model -- the MetabolicModel solver -- name of solver (or "default") objective -- coefficient vector of linear objective function of FBA wtSolution -- FBA solution for wildtype wtObjVal -- objective function value for wildtype solution (optional) koGroups -- list of pairs (group name, list of reactions) filePrefix -- if given, write MOMA/FBA solutions to files starting with this prefix; if empty, solutions are discarded weights -- weight vector for weighted MOMA (None -> perform regular MOMA, else: weight flux i with weights[i]) Returns list of (distance, diff, obj_val) tuples, indexed like koGroups, distance -- value of actual MOMA/FBA objective function at solution diff -- summed absolute difference between mutant and wildtype solutions obj_val -- value of FBA objective function at MOMA/FBA solution for mutant """ if not koGroups: koGroups = _makeDefaultKoGroups(model) # print "dispatch: %d reactions, %d knockout groups" % (len(model), # len(koGroups)) wtVec = wtSolution.getVecOrderedByModel(model) if wtObjVal is None: wtObjVal = dot(objective, wtVec) moma = MomaAnalyzer(solver) nJobs = len(koGroups) stopMsg = array(-1, 'i') # stop message (-1) status = MPI.Status() result = [None] * nJobs jobList = [-1 ] * numprocs # mapping of process -> job (jobList[0] is dummy) # receive buffer for MOMA/FBA solution vectors solutionVec = empty(len(model), 'd') # index of next job to be processed (as 0-dimensional numpy array) nxt = array(0, 'i') free_processes = set(range(1, numprocs)) # set of processes free for work nDone = 0 # number of finished jobs while nDone < nJobs: while free_processes and nxt < nJobs: # Assign reaction to free process print koGroups[nxt][0] proc_index = free_processes.pop() # Send index of next job to next free process comm.Send(nxt, proc_index) # Mark in job list (int() is used to convert from ref. to literal) jobList[proc_index] = int(nxt) nxt += 1 # If there are more processes than jobs, stop extra processes while free_processes: comm.Send(stopMsg, free_processes.pop()) # Receive results from compute processes comm.Recv(solutionVec, MPI.ANY_SOURCE, status=status) proc_index = status.Get_source() # Test if solutionVec[0] is nan (nan means not converged) if solutionVec[0] != solutionVec[0]: result[jobList[proc_index]] = (nan, nan, 0.) elif solutionVec[0] == inf: # inf means infeasible, i.e. no solution exists result[jobList[proc_index]] = (inf, inf, 0.) else: jobIndex = jobList[proc_index] solutionFlux = MetabolicFlux(model, solutionVec) diff = sum(solutionFlux.absDiff(wtSolution).fluxDict.values()) obj_val = max(0., dot(objective, solutionVec)) distance = moma.evalObjFunc(wtVec, solutionVec, weights) result[jobIndex] = (distance, diff, obj_val) if filePrefix: solutionFlux.writeToFile(filePrefix + koGroups[jobIndex][0] + _FILE_SUFFIX) # Increase job counter and mark reporting process as free nDone += 1 free_processes.add(proc_index) # Stop last compute process while free_processes: comm.Send(stopMsg, free_processes.pop()) return result
_mpi_exit(comm, 1) if not fbaSolution.hasSameReactions(model): print( "Error in file %s: FVA solution and model must have the " "same reactions." % os.path.basename(options.wMomaFvaFile)) _mpi_exit(comm, 1) weights = MomaAnalyzer.getWeightsFromFluxVar( model, wmomaMinmax, options.alpha, options.beta) # 7.a Read wildtype solution from file (if given) if options.wtSolution: # Read wildtype solution from file wtSolution = MetabolicFlux() try: wtSolution.readFromFile(options.wtSolution) except IOError, strerror: print("An error occurred while trying to read file %s:" % os.path.basename(options.wtSolution)) print strerror _mpi_exit(comm, 1) except SyntaxError, strerror: print("An error occurred parsing file %s:" % os.path.basename(options.wtSolution)) print strerror _mpi_exit(comm, 1) if not wtSolution.hasSameReactions(model): print(
class ModelWatcher(object): """ Class for plausibility analysis of metabolic models """ commentSign = '#' def __init__(self, model=None, fvaTolerance=.95): """ initialize watcher class Keyword arguments: model -- MetabolicModel to be checked fvaTolerance -- objective function threshold for FVA """ self.setModel(model) self.clearAssertions() self.fvaTolerance = fvaTolerance def clearAssertions(self): self.assertions, self.assertions_orig = [], [] def setModel(self, model): """ set the MetabolicModel and reset flux and related variables """ self.model = model self.flux = MetabolicFlux() self.metFlux, self.inRatios, self.outRatios = {}, {}, {} self.minFlux, self.maxFlux = {}, {} def readAssertionsFromFile(self, filename): """ parse the assertion file identified by filename -- wrapper for readAssertionsFromFileHandle() """ with open(filename) as f: return self.readAssertionsFromFileHandle(f) def readAssertionsFromFileHandle(self, f): """ read the assertion file given as a file object Modified member variables: assertions_orig -- list of assertions in original format assertions -- list of Python statements to be evaluated Comments and empty (or all-space) lines are removed """ s = f.read() # Remove comments - everything from comment sign ('#') to end of line commentPos = 0 while commentPos >= 0: commentPos = s.find(self.commentSign, commentPos) if commentPos >= 0: eolPos = s.find('\n', commentPos) if eolPos < 0: # Last line - delete everything starting from comment sign s = s[:commentPos] commentPos = -1 else: # Excise comment s = s[:commentPos] + s[eolPos:] if s.find(':') >= 0: raise SyntaxError('Illegal character found (:)') self.assertions_orig = s.split('\n') # Replace reaction and metabolite names with unique non-matching IDs # including a type specifier (r or m) idDict = dict( zip(self.model.reactionDict.keys(), "r" * len(self.model)) + zip(self.model.metaboliteDict.keys(), "m" * len(self.model.metaboliteDict))) # Find non-overlapping longest matches (greedy) of reaction and # metabolite names and replace with unique non-matching IDs counter = 0 replaceDict, replaceDictRev = {}, {} for ID in sorted(idDict.keys(), key=len, reverse=True): pos = 0 while pos >= 0: pos = s.find(ID, pos) if pos >= 0: if ID in replaceDict: replaceId = replaceDict[ID] else: # Generate new ID replaceId = ":%u%c:" % (counter, idDict[ID]) replaceDict[ID] = replaceId replaceDictRev[replaceId] = ID counter += 1 s = s[:pos] + replaceId + s[pos + len(ID):] pos = pos + len(replaceId) + 1 reaString = r"(:\d+r:)" metString = r"(:\d+m:)" inratioPattern = re.compile(r"inratio\s*\(\s*" + metString + r"\s*,\s*" + reaString + r"\s*\)") outratioPattern = re.compile(r"outratio\s*\(\s*" + metString + r"\s*,\s*" + reaString + r"\s*\)") minPattern = re.compile(r"min\s*\(\s*" + reaString + r"\s*\)") maxPattern = re.compile(r"max\s*\(\s*" + reaString + r"\s*\)") reaPattern = re.compile(reaString) metPattern = re.compile(metString) # Replace inratio/outratio expressions with references to self.inRatios/ # self.outRatios s = inratioPattern.sub( lambda x: "self.inRatios[%r].get(%r, 0.)" % (replaceDictRev[x.group(1)], replaceDictRev[x.group(2)]), s) s = outratioPattern.sub( lambda x: "self.outRatios[%r].get(%r, 0.)" % (replaceDictRev[x.group(1)], replaceDictRev[x.group(2)]), s) # Replace min/max expressions with references to self.minFlux/ # self.maxFlux s = minPattern.sub( lambda x: "self.minFlux[%r]" % replaceDictRev[x.group(1)], s) s = maxPattern.sub( lambda x: "self.maxFlux[%r]" % replaceDictRev[x.group(1)], s) # Replace reaction identifiers with references to self.flux s = reaPattern.sub( lambda x: "self.flux[%r]" % replaceDictRev[x.group(0)], s) # Replace metabolite identifiers with references to self.metFlux s = metPattern.sub( lambda x: "self.metFlux[%r]" % replaceDictRev[x.group(0)], s) self.assertions = s.split('\n') # Remove blank lines (cannot be evaluated) for i in range(len(self.assertions) - 1, -1, -1): if self.assertions[i] == "" or self.assertions[i].isspace(): del self.assertions[i] for i in range(len(self.assertions_orig) - 1, -1, -1): if (self.assertions_orig[i] == "" or self.assertions_orig[i].isspace()): del self.assertions_orig[i] def checkSyntax(self): """ check assertions for syntax errors without actually performing any potentially time-consuming analyses This function prints a message to the console for every assertion that contains a syntax error. Returns: True if all assertions can be evaluated, False if error occurs """ # Backup member variables and re-initialize with dummy values to make # sure that all references exist bakflux, bakMetFlux = self.flux, self.metFlux bakInRatios, bakOutRatios = self.inRatios, self.outRatios bakMinFlux, bakMaxFlux = self.minFlux, self.maxFlux self.flux = MetabolicFlux(self.model, [0.] * len(self.model)) self.metFlux = dict( zip(self.model.metabolites, [0.] * len(self.model.metabolites))) self.inRatios = dict( zip(self.model.metabolites, [{}] * len(self.model.metabolites))) self.outRatios = dict( zip(self.model.metabolites, [{}] * len(self.model.metabolites))) self.minFlux = dict( zip(self.model.reactionDict.keys(), [0.] * len(self.model))) self.maxFlux = dict( zip(self.model.reactionDict.keys(), [0.] * len(self.model))) excepted = [] for i in range(len(self.assertions)): try: eval(self.assertions[i]) except (SyntaxError, NameError): excepted.append(repr(self.assertions_orig[i])) # Restore member variables to original values self.flux, self.metFlux = bakflux, bakMetFlux self.inRatios, self.outRatios = bakInRatios, bakOutRatios self.minFlux, self.maxFlux = bakMinFlux, bakMaxFlux if excepted: print( "The following assertions cannot be evaluated due to errors" ":\n " + "\n ".join(excepted)) # The following was removed because the Python error messages are misleading. # for a in excepted: # print "\n %r\n %s: %s\n" % (a[0], a[1], a[2]) return False return True def run(self, fbaParams, syntaxOk=True, printExcepted=False): """ check assertions against results of FBA, FVA, & split-ratio analysis This function performs FBA, split-ratio analysis, and FVA and evaluates the assertions. It enumerates all assertions that do not hold. Keyword arguments: fbaParams -- objective function and solver for FBA syntaxOk -- if False, previously performed syntax check has failed printExcepted -- if False, syntax errors in assertions are not reported """ # 1. Perform flux balance analysis on the metabolic network fba = FbAnalyzer(fbaParams.solver) self.flux = fba.runOnModel(self.model, fbaParams)[1] if len(self.flux) == 0: print( "No FBA solution was obtained. The optimization problem is " "either unbounded or infeasible.") return # 2. Compute metabolite fluxes and split ratios splitRatios = self.flux.computeAllSplitRatios(self.model) self.metFlux, self.outRatios, self.inRatios = {}, {}, {} for met in splitRatios: outRatios, inRatios = splitRatios[met] self.metFlux[met] = sum(inRatios[rea][1] for rea in inRatios) self.outRatios[met] = dict( (rea, outRatios[rea][0]) for rea in outRatios) self.inRatios[met] = dict( (rea, inRatios[rea][0]) for rea in inRatios) # 3. Perform flux variability analysis on the model fva = FvAnalyzer("default") # always use GLPK (=> fastFVA) minmax = fva.runOnModel(self.model, fbaParams, self.fvaTolerance, self.flux)[0] self.minFlux, self.maxFlux = {}, {} for i in range(len(self.model)): self.minFlux[self.model.reactions[i].name], self.maxFlux[ self.model.reactions[i].name] = minmax[i] # 4. Check assertions failed = [] excepted = [] for i in range(len(self.assertions)): try: if not eval(self.assertions[i]): failed.append(repr(self.assertions_orig[i])) except (SyntaxError, NameError): excepted.append(repr(self.assertions_orig[i])) if printExcepted and excepted: print( "The following assertions could not be evaluated due to " "errors:\n " + "\n ".join(excepted)) if failed: print "The following assertions failed:\n " + "\n ".join(failed) else: if not syntaxOk or (printExcepted and excepted): print "All other assertions hold." else: print "All assertions hold."
def main(): # 1. Parse command line usage = "Usage: %prog <solution-file> [options]" version = "Split-ratio analysis\n" + COPYRIGHT_VERSION_STRING parser = OptionParser(usage=usage, version=version) parser.add_option("-r", "--reactions", dest="reactionFile", help="use " "metabolic model given by reaction FILE", metavar="FILE") parser.add_option("-m", "--metabolite", dest="metabolite", help="NAME of " "the single metabolite to be analyzed (optional)", metavar="NAME") parser.add_option("-o", "--output", dest="outputFile", help="perform " "analysis for all metabolites and write output to FILE", metavar="FILE") parser.add_option("-t", "--omit-fluxes-below", dest="threshold", type="float", help="omit metabolites in output whose flux" " is below the given THRESHOLD (only with -o)", metavar="THRESHOLD") parser.add_option("-u", "--omit-unbranched", dest="omitUnbranched", action="store_true", help="if set, metabolites with " "unbranched flux are omitted (only with -o)") parser.add_option("-c", "--cutoff", dest="cutoff", type="float", help="use " "the given cutoff VALUE (default 0)", metavar="VALUE") parser.add_option("-f", "--cutoff-is-absolute", dest="cutoffIsAbsolute", action="store_true", help="if set, cutoff is in absolute " "flux units rather than a value between 0 and 1") parser.add_option( "-a", "--list-all", dest="listAll", action="store_true", help="if set, cutoff is ignored, and even zero fluxes are" " shown (per definition as outgoing)") parser.set_defaults(threshold=-1., omitUnBranched=False, cutoff=0., cutoffIsAbsolute=False, listAll=False) options, args = parser.parse_args() parser.check_required('-r') if not options.metabolite and not options.outputFile: print "Neither metabolite nor output file given. Nothing to do." exit() # 2. Read solution file solution = MetabolicFlux() try: solution.readFromFile(args[0]) except IndexError: print("Error: No solution file given.\nUsage is\n " + os.path.basename(sys.argv[0]) + " <solution-file> [options]") exit() except IOError, strerror: print("An error occurred while trying to read file %s:" % os.path.basename(args[0])) print strerror exit()
try: model.addReactionsFromFile(options.reactionFile, rparser) except IOError, strerror: print("An error occurred while trying to read file %s:" % os.path.basename(options.reactionFile)) print strerror exit() except SyntaxError, strerror: print("Error in reaction file %s:" % os.path.basename(options.reactionFile)) print strerror exit() # 3. Parse solution file solution = MetabolicFlux() try: solution.readFromFile(options.solutionFile) except IOError, strerror: print("An error occurred while trying to read file %s:" % os.path.basename(options.solutionFile)) print strerror exit() except SyntaxError, strerror: print("Error in solution file %s:" % os.path.basename(options.solutionFile)) print strerror exit() # 4. Count metabolites' participation in active reactions
def runOnModel(self, model, wtSolution, linConstraints=[], numIter=1, weights=None, blockedReactions=[]): """ construct and solve the quadratic optimization problem - this function runs directly on a MetabolicModel and a MetabolicFlux Keyword arguments: model -- the MetabolicModel wtSolution -- FBA solution for the wildtype (given as MetabolicFlux) linConstraints -- list of LinearConstraint objects numIter -- number of iterations of NLP to perform weights -- weight vector for weighted MOMA (None -> perform regular MOMA, else: weight flux i with weights[i]) blockedReactions -- remove the given blocked reactions before analysis (faster and gives an optimal solution, as well) Returns: distance, solution, status, dimReduced distance -- minimum possible distance from wtSolution with the given matrix & constraints solution -- a solution with minimal distance to wtSolution (as MetabolicFlux) status -- SolverStatus after optimization dimReduced -- pair (nRows, nColumns) with dimensions of reduced matrix """ if blockedReactions: modelRed = model.getSubModelByExcludeList(blockedReactions) cbz = model.canBeZero(blockedReactions) nonZeroDeadEnds = [ blockedReactions[i] for i in range(len(blockedReactions)) if not cbz[i] ] if nonZeroDeadEnds: print( "The following blocked reactions are constrained to a " "non-zero flux:\n " + "\n ".join(nonZeroDeadEnds) + "\nThe problem is infeasible.") return (nan, MetabolicFlux(), SolverStatus.PRIM_INFEAS, array(modelRed.getStoichiometricMatrix()).shape) reactionsRed = set(modelRed.getReactionNames()) if weights is None: weightsRed = None else: weightsRed = [0.] * len(modelRed) for rea in wtSolution: if rea in reactionsRed: weightsRed[modelRed.reactionDict[rea]] = \ weights[model.reactionDict[rea]] weightsRed = array(weightsRed) else: modelRed = model weightsRed = weights matrix = array(modelRed.getStoichiometricMatrix()) dimReduced = matrix.shape lb, ub = map(array, modelRed.getBounds()) try: eqs, ineqs = ParamParser.linConstraintsToVectors( linConstraints, modelRed.reactionDict) except ValueError: # If any linear constraint is contradictory, return empty solution return (nan, MetabolicFlux(), SolverStatus.PRIM_INFEAS, array(modelRed.getStoichiometricMatrix()).shape) distance, solution, status = self.run( matrix, lb, ub, wtSolution.getVecOrderedByModel(modelRed), eqs, ineqs, numIter, weightsRed) flux = MetabolicFlux(modelRed, solution) # Add removed reactions with flux 0. and original bounds to solution if len(modelRed) != len(model) and solution != []: reactionsRed = set(modelRed.getReactionNames()) for rea in model: if rea.name not in reactionsRed: flux.fluxDict[rea.name] = 0. flux.boundsDict[rea.name] = (rea.lb, rea.ub) return distance, flux, status, dimReduced
print("An error occurred while trying to read file %s:" % os.path.basename(options.paramFile)) print strerror exit() except SyntaxError, strerror: print("Error in scenario file %s:" % os.path.basename(options.paramFile)) print strerror exit() except ValueError, strerror: print strerror exit() # 4. Parse solution file for wildtype wtFlux = MetabolicFlux() try: wtFlux.readFromFile(options.wtSolution) except IOError, strerror: print("An error occurred while trying to read file %s:" % os.path.basename(options.wtSolution)) print strerror exit() except SyntaxError, strerror: print("An error occurred parsing file %s:" % os.path.basename(options.wtSolution)) print strerror exit() if not wtFlux.hasSameReactions(model): print "Error: Solution and model must have the same reactions."
def runOnModel(self, model, fbaParams, threshp=.95, solution=None, splitFluxes=True, rmDeadEnds=True): """ perform flux variability analysis on the given model with objective value <= threshp * maximum Keyword arguments: model -- the MetabolicModel fbaParams -- FBA parameters threshp -- threshold percentage (objective_value <= thresp*maximum) solution -- optional: either FBA solution file or solution as MetabolicFlux object splitFluxes -- if True, run split fluxes (resulting in non-negative flux variables) before FBA rmDeadEnds -- if True, remove all reactions with dead ends before analysis (faster and gives an optimal solution, as well) Returns: minmax, sFlux, lb, ub, dimReduced minmax -- list of pairs (minimum, maximum), indexed like reactions sFlux -- FBA solution (vector indexed like reactions) lb, ub -- lower/upper bounds vectors, indexed like reactions dimReduced -- pair (nRows, nColumns) with dimensions of reduced matrix """ if rmDeadEnds: deadReactions = model.findDeadEnds(True)[1] modelRed = model.getSubModelByExcludeList(deadReactions) cbz = model.canBeZero(deadReactions) nonZeroDeadEnds = [ deadReactions[i] for i in range(len(deadReactions)) if not cbz[i] ] if nonZeroDeadEnds: print( "The following blocked reactions are constrained to a " "non-zero flux:\n " + "\n ".join(nonZeroDeadEnds) + "\nThe problem is infeasible.") lbFull, ubFull = map(array, model.getBounds()) return ([], MetabolicFlux(), lbFull, ubFull, array(modelRed.getStoichiometricMatrix()).shape) else: modelRed = model matrix = array(modelRed.getStoichiometricMatrix()) dimReduced = matrix.shape lb, ub = map(array, modelRed.getBounds()) # Build (negative) objective function vector objective = ParamParser.convertObjFuncToLinVec(fbaParams.objStr, modelRed.reactionDict, -1, fbaParams.maxmin) maxmin_factor = -1. if fbaParams.maxmin else 1. # Case 1. If an FBA solution is given, use that if isinstance(solution, MetabolicFlux): # Sort solution like matrix columns sFlux = solution.getVecOrderedByModel(modelRed) # Evaluate the objective function at solution obj_value = maxmin_factor * dot(objective, sFlux) # Case 2. If solution file is given, read solution elif solution is not None: sFlux = MetabolicFlux() try: sFlux.readFromFile(solution) except IOError, strerror: print("An error occurred while trying to read file %s:" % os.path.basename(solution)) print strerror exit() except SyntaxError, strerror: print("An error occurred parsing file %s:" % os.path.basename(solution)) print strerror exit()
def runOnModel(self, model, fbaParams, splitFluxes=True, minimizeTotalFlux=False, rmDeadEnds=True): """ run flux-balance analysis on the given MetabolicModel Keyword arguments: model -- the MetabolicModel with reactions and bounds fbaParams -- optimization parameters (incl. objective function) splitFluxes -- if True, run split fluxes (resulting in non-negative flux variables) before analysis minimizeTotalFlux -- if True, find a solution with minimal total absolute flux rmDeadEnds -- if True, remove all reactions with dead ends before analysis (faster and gives an optimal solution, as well) Returns: obj_value, solution, dimReduced obj_value -- optimal value of objective function solution -- a solution where the objective function assumes obj_value dimReduced -- pair (nRows, nColumns) with dimensions of reduced matrix """ maxmin = fbaParams.maxmin objStr = fbaParams.objStr if isinstance(maxmin, str): maxmin = {"max": True, "min": False}[maxmin.lower()] if rmDeadEnds: deadReactions = model.findDeadEnds(True)[1] modelRed = model.getSubModelByExcludeList(deadReactions) cbz = model.canBeZero(deadReactions) nonZeroDeadEnds = [ deadReactions[i] for i in range(len(deadReactions)) if not cbz[i] ] if nonZeroDeadEnds: print( "The following blocked reactions are constrained to a " "non-zero flux:\n " + "\n ".join(nonZeroDeadEnds) + "\nThe problem is infeasible.") return (0., MetabolicFlux(), array(modelRed.getStoichiometricMatrix()).shape) else: modelRed = model matrix = array(modelRed.getStoichiometricMatrix()) dimReduced = matrix.shape reaction_names = modelRed.getReactionNames() reactions = modelRed.reactionDict lb, ub = modelRed.getBounds() if splitFluxes: matrixSplit, reactionsSplit, lbSplit, ubSplit = \ self.splitFluxes(matrix, reaction_names, lb, ub) try: ParamParser.convertObjFuncToLinVec(objStr, reactionsSplit, len(lbSplit), maxmin) except KeyError, s: if deadReactions: print( "Error while trying to construct the objective " "function vector:\n%s\nThis may be due to removal " "of nonfunctional reactions." % s) return 0., MetabolicFlux(), dimReduced obj_value, solutionSplit = self.run(reactionsSplit, matrixSplit, lbSplit, ubSplit, fbaParams) solution = [] try: if solutionSplit == []: solution = [] else: if minimizeTotalFlux: # Optimize solution by limiting total flux limit, obj_value, solutionSplit, nSteps = \ self.runWithTotalFluxMinimization(reactionsSplit, matrixSplit, lbSplit, ubSplit, fbaParams, sum(solutionSplit), 1e-8, 1e-12) print( "Found optimal flux limit of %.10g in %u steps." % (limit, nSteps)) solution = array( FbAnalyzer.rejoinFluxes(solutionSplit, reactionsSplit, reactions)) except NameError, strerror: print strerror
matrixSplit, lbSplit, ubSplit, fbaParams, sum(solutionSplit), 1e-8, 1e-12) print( "Found optimal flux limit of %.10g in %u steps." % (limit, nSteps)) solution = array( FbAnalyzer.rejoinFluxes(solutionSplit, reactionsSplit, reactions)) except NameError, strerror: print strerror else: obj_value, solution = self.run(reactions, matrix, lb, ub, fbaParams) flux = MetabolicFlux(modelRed, solution) # Add removed reactions with flux 0. and original bounds to solution if len(modelRed) != len(model) and len(solution) != 0: reactionsRed = set(reaction_names) for rea in model: if rea.name not in reactionsRed: flux.fluxDict[rea.name] = 0. flux.boundsDict[rea.name] = (rea.lb, rea.ub) return obj_value, flux, dimReduced def main(): # 1. Parse command line