From 50ebc192b07b387533c298f9e7a0a2cefe528ff5 Mon Sep 17 00:00:00 2001 From: Peter Schubert <Peter.Schubert@hhu.de> Date: Tue, 19 Jul 2022 16:39:41 +0200 Subject: [PATCH] Initial base functionality --- modelpruner/core/model_pruner.py | 199 ++++-- modelpruner/core/protected_parts.py | 6 +- modelpruner/models/fba_model.py | 130 +++- modelpruner/problems/lp_problem.py | 80 ++- sample_data/SBML_models/Deniz_model_fba.xml | 698 ++++++++++++++++++++ sample_data/data/Deniz_model_fba_nrp2.xlsx | Bin 0 -> 14046 bytes sample_prune_model.py | 50 ++ 7 files changed, 1030 insertions(+), 133 deletions(-) create mode 100644 sample_data/SBML_models/Deniz_model_fba.xml create mode 100644 sample_data/data/Deniz_model_fba_nrp2.xlsx create mode 100644 sample_prune_model.py diff --git a/modelpruner/core/model_pruner.py b/modelpruner/core/model_pruner.py index f2cd574..742cf3e 100644 --- a/modelpruner/core/model_pruner.py +++ b/modelpruner/core/model_pruner.py @@ -1,9 +1,11 @@ +import sys import os import re import time import numpy as np +import pickle import sbmlxdf from modelpruner.models.fba_model import FbaModel @@ -12,97 +14,172 @@ from modelpruner.core.protected_parts import ProtectedParts class ModelPruner: - def __init__(self, sbml_fname, protected_parts, reduced_fname=None): + def __init__(self, sbml_fname, protected_parts, reduced_fname=None, resume=False): if reduced_fname is None: - self.reduced_fname = re.sub('.xml', '_red.xml', sbml_fname) - - if os.path.exists(sbml_fname): - self.sbml_model = sbmlxdf.Model(sbml_fname) - print(f'Protected data loaded: {sbml_fname} ' - f'(last modified: {time.ctime(os.path.getmtime(sbml_fname))})') + self.reduced_fname = re.sub(r'.xml$', '_red.xml', sbml_fname) + self._snapshot_sbml = re.sub(r'.xml$', '_snapshot.xml', sbml_fname) + self._snapshot_pkl = re.sub(r'.xml$', '_snapshot.pkl', sbml_fname) + + if resume is True: + if os.path.exists(self._snapshot_sbml): + self.sbml_model = sbmlxdf.Model(self._snapshot_sbml) + print(f'Resume from snapshot from {self._snapshot_sbml} ' + f'(last modified: {time.ctime(os.path.getmtime(self._snapshot_sbml))})') + else: + print(f'{self._snapshot_sbml} not found!') + raise FileNotFoundError else: - print(f'{sbml_fname} not found!') - raise FileNotFoundError + if os.path.exists(sbml_fname): + self.sbml_model = sbmlxdf.Model(sbml_fname) + print(f'Full SBML model loaded from {sbml_fname} ' + f'(last modified: {time.ctime(os.path.getmtime(sbml_fname))})') + else: + print(f'{sbml_fname} not found!') + raise FileNotFoundError self.tolerance = 1e-8 # later accessible by set_params() ? self.fba_model = FbaModel(self.sbml_model) self.nrp = ProtectedParts(protected_parts) + self.protected_sids = self.nrp.initial_protected_sids + + if resume is True and os.path.exists(self._snapshot_pkl): + with open(self._snapshot_pkl, 'rb') as f: + self.protected_rids = pickle.load(f) + print(f'snapshot restored at {len(self.fba_model.rids):4d} remaining reactions ' + f'from: {self._snapshot_pkl}') + else: + self.protected_rids = self.get_total_protected_rids(self.nrp.initial_protected_rids) + + self.fba_model.update_flux_bounds(self.nrp.overwrite_bounds) self.base_obj_dir = self.fba_model.obj_dir self.base_flux_bounds = self.fba_model.flux_bounds.copy() self.base_coefs = self.fba_model.obj_coefs.copy() - self.protected_sids = set(self.nrp.initial_protected_sids) - self.protected_rids = self.get_total_protected_rids(set(self.nrp.initial_protected_rids)) - self.essential_rids = self.protected_rids - self.free_rids = set(self.fba_model.rids).difference(self.essential_rids) + self.set_function_optimum() self.check_protected_parts() - # overwrite bounds on fba - self.fba_model.update_flux_bounds(self.nrp.overwrite_bounds) - def check_protected_parts(self): extra_p_rids = self.protected_rids.difference(set(self.fba_model.rids)) extra_p_sids = self.protected_sids.difference(set(self.fba_model.sids)) pf_rids = set() for pf in self.nrp.protected_functions.values(): - pf_rids = pf_rids.union({rid for rid in pf.objective}) - pf_rids = pf_rids.union({rid for rid in pf.overwrite_bounds}) + pf_rids |= {rid for rid in pf.objective} + pf_rids |= {rid for rid in pf.overwrite_bounds} if pf.fba_success is False: print(f'protected function {pf.obj_id} has unsuccessful FBA ' f' with optimal value {pf.optimal}') extra_pf_rids = pf_rids.difference(set(self.fba_model.rids)) + pf_unprotected_rids = pf_rids.difference(set(self.protected_rids)) if len(extra_p_rids) > 0: print('protected reactions that do not exist in model:', extra_p_rids) if len(extra_p_sids) > 0: print('protected metabolites that do not exist in model:', extra_p_sids) if len(extra_pf_rids) > 0: - print('reactions in protected function that do not exist in model:', extra_pf_rids) + print('reactions in protected functions that do not exist in model:', extra_pf_rids) + if len(pf_unprotected_rids) > 0: + print('reactions in protected functions that are not protected:', pf_unprotected_rids) - zero_flux_rids = self.get_blocked_reactions() - blocked_p_rids = self.protected_rids.intersection(zero_flux_rids) - blocked_pf_rids = pf_rids.intersection(zero_flux_rids) + blocked_rids = self.reaction_types_fva(self.fba_model.rids)['blocked_rids'] + blocked_p_rids = self.nrp.initial_protected_sids.intersection(blocked_rids) + blocked_pf_rids = pf_rids.intersection(blocked_rids) if len(blocked_p_rids) > 0: print('protected reactions that are blocked (no flux):', blocked_p_rids) if len(blocked_pf_rids) > 0: - print('reactions in protected function that are blocked (no flux):', blocked_pf_rids) - - def get_blocked_reactions(self): - # identify blocked reactions - # check that none of the blocked reactions are protected - # actually remove blocked reactions, even they are included in - - fba_solution = self.fba_model.fba_optimize() - zero_flux_mask = np.abs(fba_solution['x']) < self.tolerance - zero_flux_rids = self.fba_model.rids[zero_flux_mask] - - fva_solution = self.fba_model.fva_optimize(zero_flux_rids, fract_of_optimum=0.0) - flux_ranges = np.vstack((fva_solution['min'], fva_solution['max'])).T - zero_flux_mask = np.all(abs(flux_ranges) < self.tolerance, axis=1) - zero_flux_rids = set(fva_solution['rids'][zero_flux_mask]) - return zero_flux_rids - - def prune(self): + print('reactions in protected functions that are blocked (no flux):', blocked_pf_rids) + def prune(self, snapshot_freq=None): + print(f'initial S-matrix: {self.fba_model.s_mat.shape}; ' + f' protected reactions: {len(self.protected_rids)}:', self.protected_rids) self.remove_parallel_reactions() + print('protected functions feasibility:', self.check_pf_feasibility()) - def reaction_types_fva(self): - - free_rids = list(set(self.fba_model.rids).difference(self.protected_rids)) + if type(snapshot_freq) is int: + next_snapshot = max(0, len(self.fba_model.rids) - snapshot_freq) + else: + next_snapshot = None + + while len(self.protected_rids) < self.fba_model.s_mat.shape[1]: + free_rids = list(set(self.fba_model.rids).difference(self.protected_rids)) + reaction_types = self.reaction_types_fva(free_rids) + if len(reaction_types['blocked_rids']) > 0: + self.fba_model.remove_reactions(reaction_types['blocked_rids'], self.protected_rids) + print(f"{len(reaction_types['blocked_rids'])} blocked reaction(s) removed:", + reaction_types['blocked_rids']) + if len(reaction_types['essential_rids']) > 0: + self.protected_rids |= reaction_types['essential_rids'] + print(f"{len(reaction_types['essential_rids'])} reaction(s) added to protected reactions:", + reaction_types['essential_rids']) + + print(f"{len(reaction_types['candidate_rids_sorted'])} candidates:", + reaction_types['candidate_rids_sorted'][:4], '...') + # try to remove one reaction while still satisfying all protected functions + feasible = False + essential_rids = set() + for rid in reaction_types['candidate_rids_sorted']: + orig_flux_bounds = self.fba_model.get_flux_bounds(rid) + self.fba_model.update_flux_bounds({rid: [0.0, 0.0]}) + feasible = self.check_pf_feasibility() + self.fba_model.update_flux_bounds({rid: orig_flux_bounds}) + if feasible: + self.fba_model.remove_reactions({rid}, self.protected_rids) + print(f'{rid} removed from the model') + break + else: + essential_rids.add(rid) + if len(essential_rids) > 0: + self.protected_rids |= essential_rids + print(f"{len(essential_rids)} reaction(s) added to protected reactions:", essential_rids) + + if feasible is False: + print('no more reactions to remove') + + # store intermediate results (snapshots) + print(f'snapshot check next: {next_snapshot}, rids: {len(self.fba_model.rids)}') + if (next_snapshot is not None) and (len(self.fba_model.rids) < next_snapshot): + self.export_pruned_model(self._snapshot_sbml) + with open(self._snapshot_pkl, 'wb') as f: + pickle.dump(self.protected_rids, f) + next_snapshot = max(0, len(self.fba_model.rids) - snapshot_freq) + print('snapshot data saved.') + + print(f'S-matrix: {self.fba_model.s_mat.shape}; ' + f'protected reactions: {len(self.protected_rids)}') + + # check if blocked reactions exist in the finally pruned model + blocked_rids = self.reaction_types_fva(self.fba_model.rids)['blocked_rids'] + if len(blocked_rids) > 0: + print(f'{len(blocked_rids)} blocked reaction(s) in pruned model', blocked_rids) + + # remove any snapshot files + for fname in [self._snapshot_sbml, self._snapshot_pkl]: + if os.path.exists(fname): + os.remove(fname) + + def reaction_types_fva(self, free_rids): flux_min_pfs = np.zeros((len(self.nrp.protected_functions), len(free_rids))) flux_max_pfs = np.zeros((len(self.nrp.protected_functions), len(free_rids))) + n_pf = len(self.nrp.protected_functions) for idx, pf in enumerate(self.nrp.protected_functions.values()): self.set_temp_func_conditions(pf) res = self.fba_model.fva_optimize(free_rids, fract_of_optimum=0.0) self.restore_base_conditions() + if res['success'] is False: + print('FVA unsuccessful') + return {'blocked_rids': [], 'essential_rids': [], 'candidate_rids_sorted': []} + flux_min_pfs[idx] = res['min'] flux_max_pfs[idx] = res['max'] + sys.stdout.write('\rFVA[{}{}] {:3.0f}%'.format( + '=' * int(idx+1), ' ' * int(n_pf - idx-1), (idx+1)/n_pf*100)) + sys.stdout.flush() + print() flux_min_pfs[np.abs(flux_min_pfs) < self.tolerance] = 0.0 flux_max_pfs[np.abs(flux_max_pfs) < self.tolerance] = 0.0 @@ -144,18 +221,17 @@ class ModelPruner: drop_group_rids = set(group_rids) - {reversible} else: drop_group_rids = set(group_rids[1:]) - drop_rids = drop_rids.union(drop_group_rids) + drop_rids |= drop_group_rids self.fba_model.remove_reactions(drop_rids, self.protected_sids) - print(f'{len(drop_rids)} parallel reactions dropped:', drop_rids) + print(f'{len(drop_rids)} parallel reaction(s) dropped:', drop_rids) def set_function_optimum(self): """using FBA to set optimal value for protected functions""" - for obj_id in self.nrp.protected_functions: - pf = self.nrp.protected_functions[obj_id] + for obj_id, pf in self.nrp.protected_functions.items(): self.set_temp_func_conditions(pf) - res = self.fba_model.fba_optimize(short_result=True) + self.restore_base_conditions() pf.optimal = res['fun'] pf.fba_success = res['success'] @@ -165,8 +241,6 @@ class ModelPruner: else: pf.set_target_lb(pf.target_frac * pf.optimal) - self.restore_base_conditions() - def get_total_protected_rids(self, protected_rids): # identify reactions that need protection due to protected metabolites # over and above the protected rids provided in protected parts @@ -179,11 +253,12 @@ class ModelPruner: # identify rids in protected functions and ensure they are protected pf_rids = set() for pf in self.nrp.protected_functions.values(): - pf_rids = pf_rids.union({rid for rid in pf.objective}) - pf_rids = pf_rids.union({rid for rid in pf.overwrite_bounds}) + pf_rids |= {rid for rid in pf.objective} + pf_rids |= {rid for rid in pf.overwrite_bounds} - total_protect_rids = protected_rids.union(protected_sid_rids).union(pf_rids) - return total_protect_rids + protected_rids |= protected_sid_rids + protected_rids |= pf_rids + return protected_rids def check_pf_feasibility(self): """check that current model still satisfies the protected functions @@ -217,8 +292,8 @@ class ModelPruner: def restore_base_conditions(self): self.fba_model.obj_dir = self.base_obj_dir - self.fba_model.flux_bounds = self.base_flux_bounds - self.fba_model.obj_coefs = self.base_coefs + self.fba_model.flux_bounds = self.base_flux_bounds.copy() + self.fba_model.obj_coefs = self.base_coefs.copy() def get_reduction_status(self): """ Return status in model reduction process @@ -231,12 +306,18 @@ class ModelPruner: status = self.fba_model.get_model_status() status['n_protected_rids'] = len(self.protected_rids) status['n_protected_sids'] = len(self.protected_sids) - status['essential_rids'] = len(self.essential_rids) return status def print_status(self): status = self.get_reduction_status() print("reactions removed/remaining/protected: " - f"{status['n_full_rids' ] -status['n_red_rids']:4d}/{status['n_red_rids']:4d}/" - f"{status['essential_rids']:4d}; dof: {status['dof']:3d}") + f"{status['n_full_rids' ] - status['n_rids']:4d}/{status['n_rids']:4d}/" + f"/{len(self.protected_rids):4d}; dof: {status['dof']:3d}") + + def export_pruned_model(self, pruned_sbml): + success = self.fba_model.export_pruned_model(pruned_sbml) + if success is True: + print('pruned model exported to', pruned_sbml) + else: + print('pruned model could not be exported') diff --git a/modelpruner/core/protected_parts.py b/modelpruner/core/protected_parts.py index 71de8b2..b35343a 100644 --- a/modelpruner/core/protected_parts.py +++ b/modelpruner/core/protected_parts.py @@ -14,7 +14,7 @@ class ProtectedParts: if type(protected_parts) == str: if os.path.exists(protected_parts): nrp = self.load_raw_protected_data(protected_parts) - print(f'Protected data loaded: {protected_parts} ' + print(f'Protected data loaded from {protected_parts} ' f'(last modified: {time.ctime(os.path.getmtime(protected_parts))})') else: print(f'{protected_parts} not found!') @@ -23,8 +23,8 @@ class ProtectedParts: assert type(protected_parts) == dict, 'expected filename or a dict' nrp = protected_parts - self.initial_protected_rids = nrp.get('reactions', []) - self.initial_protected_sids = nrp.get('metabolites', []) + self.initial_protected_rids = set(nrp.get('reactions', [])) + self.initial_protected_sids = set(nrp.get('metabolites', [])) if 'bounds' in nrp: num_cols = ['fbcLb', 'fbcUb'] nrp['bounds'][num_cols] = nrp['bounds'][num_cols].applymap( diff --git a/modelpruner/models/fba_model.py b/modelpruner/models/fba_model.py index 8e8ae17..3496c3a 100644 --- a/modelpruner/models/fba_model.py +++ b/modelpruner/models/fba_model.py @@ -1,5 +1,6 @@ +import re import math import numpy as np import sbmlxdf @@ -11,10 +12,10 @@ class FbaModel: def __init__(self, sbml_model): - model_dict = sbml_model.to_df() - df_reactions = model_dict['reactions'] - df_species = model_dict['species'] - df_fbc_objectives = model_dict['fbcObjectives'] + self.model_dict = sbml_model.to_df() + df_reactions = self.model_dict['reactions'] + df_species = self.model_dict['species'] + df_fbc_objectives = self.model_dict['fbcObjectives'] self.full_model_shape = {'n_full_rids': len(df_reactions), 'n_full_sids': len(df_species)} @@ -105,6 +106,7 @@ class FbaModel: if lp is None: return {'success': False, 'message': 'not an FBA model'} res = lp.solve(short_result) + del lp if short_result is False: res['x'] = self.combine_fwd_bwd(res['x']) res['reduced_costs'] = self.combine_fwd_bwd(res['reduced_costs']) @@ -125,40 +127,42 @@ class FbaModel: return {'success': False, 'message': 'not an FBA model'} res = lp.solve(short_result=True) + if res['success'] is False: + del lp + return {'success': False, 'message': res['generic_status']} + if rids is None: rids = self.rids - flux_min = np.zeros(len(rids)) flux_max = np.zeros(len(rids)) - if res['success']: - # fix FBA objective as constraint - fwd_bwd_obj_coefs = np.hstack((self.obj_coefs, -self.obj_coefs[self.reversible])) - if self.obj_dir == 'maximize': - row_bds = np.array([res['fun'] * fract_of_optimum, np.nan]) + # fix FBA objective as constraint + fwd_bwd_obj_coefs = np.hstack((self.obj_coefs, -self.obj_coefs[self.reversible])) + if self.obj_dir == 'maximize': + row_bds = np.array([res['fun'] * fract_of_optimum, np.nan]) + else: + if fract_of_optimum > 0.0: + row_bds = np.array([np.nan, res['fun'] / fract_of_optimum]) else: - if fract_of_optimum > 0.0: - row_bds = np.array([np.nan, res['fun'] / fract_of_optimum]) - else: - row_bds = np.array([np.nan, 1000.0]) - lp.add_constraint(fwd_bwd_obj_coefs, row_bds) - - n_cols = len(self.rids) - for idx, rid in enumerate(rids): - # select reaction maximize/minimize flux - new_obj_coefs = np.zeros(n_cols) - new_obj_coefs[self.rid2idx[rid]] = 1 - new_fwd_bwd_obj_coefs = np.hstack((new_obj_coefs, -new_obj_coefs[self.reversible])) - lp.set_obj_coefs(new_fwd_bwd_obj_coefs) - - lp.set_obj_dir('minimize') - res = lp.solve(short_result=True) - flux_min[idx] = res['fun'] if res['success'] else np.nan - - lp.set_obj_dir('maximize') - res = lp.solve(short_result=True) - flux_max[idx] = res['fun'] if res['success'] else np.nan - - return {'rids': np.array(rids), 'min': flux_min, 'max': flux_max} + row_bds = np.array([np.nan, 1000.0]) + lp.add_constraint(fwd_bwd_obj_coefs, row_bds) + + n_cols = len(self.rids) + for idx, rid in enumerate(rids): + # select reaction maximize/minimize flux + new_obj_coefs = np.zeros(n_cols) + new_obj_coefs[self.rid2idx[rid]] = 1 + new_fwd_bwd_obj_coefs = np.hstack((new_obj_coefs, -new_obj_coefs[self.reversible])) + lp.set_obj_coefs(new_fwd_bwd_obj_coefs) + + lp.set_obj_dir('minimize') + res = lp.solve(short_result=True) + flux_min[idx] = res['fun'] if res['success'] else np.nan + + lp.set_obj_dir('maximize') + res = lp.solve(short_result=True) + flux_max[idx] = res['fun'] if res['success'] else np.nan + del lp + return {'success': True, 'rids': np.array(rids), 'min': flux_min, 'max': flux_max} def update_flux_bounds(self, flux_bounds): """selectivly set lower/upper flux bounds for given reactions. @@ -169,6 +173,7 @@ class FbaModel: :type flux_bounds: dict (key: reaction id, value: list with two float values for lower/upper bound) """ for rid, (lb, ub) in flux_bounds.items(): + assert rid in self.rids if math.isfinite(lb): self.flux_bounds[0, self.rid2idx[rid]] = lb if lb < 0: @@ -176,6 +181,10 @@ class FbaModel: if math.isfinite(ub): self.flux_bounds[1, self.rid2idx[rid]] = ub + def get_flux_bounds(self, rid): + assert rid in self.rids + return [self.flux_bounds[0, self.rid2idx[rid]], self.flux_bounds[1, self.rid2idx[rid]]] + def remove_reactions(self, drop_rids, protected_sids): # remove_reactions # also remove corresponding metabolite columns @@ -242,8 +251,8 @@ class FbaModel: :rtype: dict """ status = {'dof': self.s_mat.shape[1] - np.linalg.matrix_rank(self.s_mat), - 'n_red_rids': len(self.rids), - 'n_red_sids': len(self.sids)} + 'n_rids': len(self.rids), + 'n_sids': len(self.sids)} status.update(self.full_model_shape) return status @@ -257,3 +266,54 @@ class FbaModel: self.__obj_dir = 'minimize' else: self.__obj_dir = 'maximize' + + def export_pruned_model(self, pruned_sbml): + + pruned_mdict = self.model_dict.copy() + pruned_mdict['reactions'] = self.model_dict['reactions'].loc[self.rids] + pruned_mdict['species'] = self.model_dict['species'].loc[self.sids] + + # update model attributes (ids, names and modification date) + pruned_mdict['modelAttrs']['id'] += '_pruned' + pruned_mdict['modelAttrs']['name'] += '_pruned' + pruned_mdict['modelAttrs']['metaid'] += '_pruned' + pruned_mdict['modelAttrs']['modifiedHistory'] += '; localtime' + + # clean groups table + keep_idxs = [] + if 'groups' in pruned_mdict: + df_groups_pruned = pruned_mdict['groups'].copy() + for idx, row in df_groups_pruned.iterrows(): + members_pruned = [] + for member in sbmlxdf.misc.record_generator(row['members']): + params = sbmlxdf.misc.extract_params(member) + if 'idRef' in params: + if params['idRef'] in self.rids: + members_pruned.append(member) + else: + members_pruned.append(member) + df_groups_pruned.at[idx, 'members'] = '; '.join(members_pruned) + if len(members_pruned) > 0: + keep_idxs.append(idx) + pruned_mdict['groups'] = df_groups_pruned.loc[keep_idxs] + + # clean gene products + gp_ids = set() + if 'fbcGeneProdAssoc' in pruned_mdict['reactions']: + for gpr in pruned_mdict['reactions']['fbcGeneProdAssoc']: + if type(gpr) is str: + if 'assoc=' in gpr: + gpr = re.sub('assoc=', '', gpr) + gpr = re.sub(r'[()]', ' ', gpr) + gpr = re.sub(r'and', ' ', gpr) + gpr = re.sub(r'or', ' ', gpr) + gp_ids = gp_ids.union(set(re.findall(r'\w+', gpr))) + if 'fbcGeneProducts' in pruned_mdict: + pruned_mdict['fbcGeneProducts'] = pruned_mdict['fbcGeneProducts'].loc[list(gp_ids)] + + pruned_model = sbmlxdf.Model() + pruned_model.from_df(pruned_mdict) + err = pruned_model.validate_sbml() + if len(err) > 0: + print('SBML validation result: %s', ', '.join([k + ': ' + str(v) for k, v in err.items()])) + return pruned_model.export_sbml(pruned_sbml) diff --git a/modelpruner/problems/lp_problem.py b/modelpruner/problems/lp_problem.py index b52d803..3bc4932 100644 --- a/modelpruner/problems/lp_problem.py +++ b/modelpruner/problems/lp_problem.py @@ -14,43 +14,43 @@ import swiglpk as glpk # status reported simplex_status = { 0: 'LP problem instance successfully solved', - 1: 'Unable to start search, initial basis specified in the ' + glpk.GLP_EBADB: 'Unable to start search, initial basis specified in the ' 'problem object is invalid. Number basic variables is not same as the number rows.', - 2: 'Unable to start search, basis matrix corresponding to initial basis is singular.', - 3: 'Unable to start search, basis matrix corresponding to initial basis is ill-conditioned.', - 4: 'Unable to start search, some double-bounded variables have incorrect bounds.', - 5: 'Search prematurely terminated: solver failure.', - 6: 'Search prematurely terminated: objective function being ' + glpk.GLP_ESING: 'Unable to start search, basis matrix corresponding to initial basis is singular.', + glpk.GLP_ECOND: 'Unable to start search, basis matrix corresponding to initial basis is ill-conditioned.', + glpk.GLP_EBOUND: 'Unable to start search, some double-bounded variables have incorrect bounds.', + glpk.GLP_EFAIL: 'Search prematurely terminated: solver failure.', + glpk.GLP_EOBJLL: 'Search prematurely terminated: objective function being ' 'maximized reached its lower limit and continues decreasing.', - 7: 'Search prematurely terminated: objective function being ' + glpk.GLP_EOBJUL: 'Search prematurely terminated: objective function being ' 'minimized reached its upper limit and continues increasing.', - 8: 'Search prematurely terminated: simplex iteration limit exceeded.', - 9: 'Search prematurely terminated: time limit exceeded.', - 10: 'LP problem instance has no primal feasible solution.', - 11: 'LP problem instance has no dual feasible solution.'} - -generic_status = {1: 'solution is undefined', - 2: 'solution is feasible', - 3: 'solution is infeasible', - 4: 'problem has no feasible solution', - 5: 'solution is optimal', - 6: 'problem has unbounded solution'} - -dual_status = {1: 'dual solution is undefined', - 2: 'dual solution is feasible', - 3: 'dual solution is infeasible', - 4: 'no dual feasible solution exists'} - -prim_status = {1: 'primal solution is undefined', - 2: 'primal solution is feasible', - 3: 'primal solution is infeasible', - 4: 'no primal feasible solution exists'} - -var_status = {1: 'basic variable', - 2: 'non-basic variable on its lower bound', - 3: 'non-basic variable on its upper bound', - 4: 'non-basic free (unbounded) variable', - 5: 'non-basic fixed variable'} + glpk.GLP_EITLIM: 'Search prematurely terminated: simplex iteration limit exceeded.', + glpk.GLP_ETMLIM: 'Search prematurely terminated: time limit exceeded.', + glpk.GLP_ENOPFS: 'LP problem instance has no primal feasible solution.', + glpk.GLP_ENODFS: 'LP problem instance has no dual feasible solution.'} + +generic_status = {glpk.GLP_UNDEF: 'solution is undefined', + glpk.GLP_FEAS: 'solution is feasible', + glpk.GLP_INFEAS: 'solution is infeasible', + glpk.GLP_NOFEAS: 'problem has no feasible solution', + glpk.GLP_OPT: 'solution is optimal', + glpk.GLP_UNBND: 'problem has unbounded solution'} + +dual_status = {glpk.GLP_UNDEF: 'dual solution is undefined', + glpk.GLP_FEAS: 'dual solution is feasible', + glpk.GLP_INFEAS: 'dual solution is infeasible', + glpk.GLP_NOFEAS: 'no dual feasible solution exists'} + +prim_status = {glpk.GLP_UNDEF: 'primal solution is undefined', + glpk.GLP_FEAS: 'primal solution is feasible', + glpk.GLP_INFEAS: 'primal solution is infeasible', + glpk.GLP_NOFEAS: 'no primal feasible solution exists'} + +var_status = {glpk.GLP_BS: 'basic variable', + glpk.GLP_NL: 'non-basic variable on its lower bound', + glpk.GLP_NU: 'non-basic variable on its upper bound', + glpk.GLP_NF: 'non-basic free (unbounded) variable', + glpk.GLP_NS: 'non-basic fixed variable'} scaling_options = {'GM': glpk.GLP_SF_GM, 'EQ': glpk.GLP_SF_EQ, @@ -69,6 +69,8 @@ class LpProblem: self.lp = glpk.glp_create_prob() self.ncols = 0 self.nrows = 0 + self.msg_lev = glpk.GLP_MSG_ERR + self.tm_lim = 10 * 1000 self.res = {} @staticmethod @@ -245,12 +247,18 @@ class LpProblem: :return: result information from the optimization stored in a dictionary :rtype: dict """ + # configure options + smcp = glpk.glp_smcp() + glpk.glp_init_smcp(smcp) + smcp.msg_lev = self.msg_lev + smcp.tm_lim = self.tm_lim + if scaling is not None: opt = scaling_options.get(scaling, glpk.GLP_SF_AUTO) glpk.glp_scale_prob(self.lp, opt) # sjj = [glpk.glp_get_sjj(self.lp, 1+i) for i in range(glpk.glp_get_num_cols(self.lp))] # rii = [glpk.glp_get_rii(self.lp, 1+i) for i in range(glpk.glp_get_num_rows(self.lp))] - simplex_result = glpk.glp_simplex(self.lp, None) + simplex_result = glpk.glp_simplex(self.lp, smcp) return self.results(simplex_result, short_result) def get_row_value(self, row): @@ -279,6 +287,7 @@ class LpProblem: self.res = { 'fun': glpk.glp_get_obj_val(self.lp), 'success': glpk.glp_get_status(self.lp) == glpk.GLP_OPT, + 'generic_status': generic_status[glpk.glp_get_status(self.lp)], } if short_result is False: self.res['x'] = np.array([glpk.glp_get_col_prim(self.lp, 1 + i) for i in range(self.ncols)]) @@ -286,7 +295,6 @@ class LpProblem: self.res['shadow_prices'] = np.array([glpk.glp_get_row_dual(self.lp, 1 + i) for i in range(self.nrows)]) self.res['simplex_result'] = simplex_result self.res['simplex_message'] = simplex_status[simplex_result], - self.res['generic_status'] = generic_status[glpk.glp_get_status(self.lp)] self.res['primal_status'] = prim_status[glpk.glp_get_prim_stat(self.lp)] self.res['dual_status'] = dual_status[glpk.glp_get_dual_stat(self.lp)] return self.res diff --git a/sample_data/SBML_models/Deniz_model_fba.xml b/sample_data/SBML_models/Deniz_model_fba.xml new file mode 100644 index 0000000..e3b9f55 --- /dev/null +++ b/sample_data/SBML_models/Deniz_model_fba.xml @@ -0,0 +1,698 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Created by sbmlxdf version 0.2.7 on 2022-07-18 14:55 with libSBML version 5.19.0. --> +<sbml xmlns="http://www.sbml.org/sbml/level3/version2/core" xmlns:fbc="http://www.sbml.org/sbml/level3/version1/fbc/version2" xmlns:groups="http://www.sbml.org/sbml/level3/version1/groups/version1" level="3" version="2" fbc:required="false" groups:required="false"> + <model metaid="Deniz_model_fba" id="Deniz_model_fba" name="Deniz_model_fba" substanceUnits="substance" timeUnits="time" extentUnits="substance" fbc:strict="true"> + <notes> + <body xmlns="http://www.w3.org/1999/xhtml"> + <h2>FBA model based on Deinz's model 11_12, presented Nov. 2020. </h2> + <p> including biomass reaction, external metabolites, ATPM maintenance reaction, oxidative and fermenative phenotypes</p> + <p>Based on Deniz Sezer </p> + </body> + </notes> + <annotation> + <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/"> + <rdf:Description rdf:about="#Deniz_model_fba"> + <dcterms:creator> + <rdf:Bag> + <rdf:li rdf:parseType="Resource"> + <vCard4:hasName rdf:parseType="Resource"> + <vCard4:family-name>Schubert</vCard4:family-name> + <vCard4:given-name>Peter</vCard4:given-name> + </vCard4:hasName> + <vCard4:hasEmail>Peter.Schubert@hhu.de</vCard4:hasEmail> + <vCard4:organization-name>HHU-CCB</vCard4:organization-name> + </rdf:li> + </rdf:Bag> + </dcterms:creator> + <dcterms:created rdf:parseType="Resource"> + <dcterms:W3CDTF>2022-07-18T14:55:18+02:00</dcterms:W3CDTF> + </dcterms:created> + <dcterms:modified rdf:parseType="Resource"> + <dcterms:W3CDTF>2022-07-18T14:55:18+02:00</dcterms:W3CDTF> + </dcterms:modified> + </rdf:Description> + </rdf:RDF> + </annotation> + <listOfUnitDefinitions> + <unitDefinition id="mmol_per_gDW_per_hr" name="Millimoles per gram (dry weight) per hour"> + <listOfUnits> + <unit kind="mole" exponent="1" scale="-3" multiplier="1"/> + <unit kind="gram" exponent="-1" scale="0" multiplier="1"/> + <unit kind="second" exponent="-1" scale="0" multiplier="3600"/> + </listOfUnits> + </unitDefinition> + <unitDefinition id="substance" name="Millimoles per gram (dry weight)"> + <listOfUnits> + <unit kind="mole" exponent="1" scale="-3" multiplier="1"/> + <unit kind="gram" exponent="-1" scale="0" multiplier="1"/> + </listOfUnits> + </unitDefinition> + <unitDefinition id="time" name="Hour"> + <listOfUnits> + <unit kind="second" exponent="1" scale="0" multiplier="3600"/> + </listOfUnits> + </unitDefinition> + </listOfUnitDefinitions> + <listOfCompartments> + <compartment id="c" name="cytosol" spatialDimensions="3" size="1e-06" units="litre" constant="true"/> + <compartment id="e" name="extracellular space" spatialDimensions="3" size="1" units="litre" constant="true"/> + </listOfCompartments> + <listOfSpecies> + <species id="M_aa_c" name="M_aa_c" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/> + <species id="M_aapre_c" name="M_aapre_c" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/> + <species id="M_accoa_c" name="M_accoa_c" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/> + <species id="M_adp_c" name="M_adp_c" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/> + <species id="M_atp_c" name="M_atp_c" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/> + <species id="M_co2_c" name="M_co2_c" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/> + <species id="M_for_c" name="M_for_c" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/> + <species id="M_glc__D_c" name="M_glc__D_c" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/> + <species id="M_lac_c" name="M_lac_c" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/> + <species id="M_nh4_c" name="M_nh4_c" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/> + <species id="M_o2_c" name="M_o2_c" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/> + <species id="M_aa_e" name="M_aa_e" compartment="e" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/> + <species id="M_co2_e" name="M_co2_e" compartment="e" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/> + <species id="M_for_e" name="M_for_e" compartment="e" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/> + <species id="M_glc__D_e" name="M_glc__D_e" compartment="e" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"> + <notes> + <body xmlns="http://www.w3.org/1999/xhtml"> main carbon source </body> + </notes> + </species> + <species id="M_lac_e" name="M_lac_e" compartment="e" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/> + <species id="M_nh4_e" name="M_nh4_e" compartment="e" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/> + <species id="M_o2_e" name="M_o2_e" compartment="e" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/> + <species id="M_rib_c" name="M_rib_c" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/> + <species id="Biomass" name="Biomass" compartment="c" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"> + <notes> + <body xmlns="http://www.w3.org/1999/xhtml"> this is a note </body> + </notes> + </species> + </listOfSpecies> + <listOfParameters> + <parameter id="default_lb" name="default_lb" value="-1000" units="mmol_per_gDW_per_hr" constant="true"/> + <parameter id="default_ub" name="default_ub" value="1000" units="mmol_per_gDW_per_hr" constant="true"/> + <parameter id="zero_bound" name="zero_bound" value="0" units="mmol_per_gDW_per_hr" constant="true"/> + <parameter id="R_EX_glc__D_e_lb" name="R_EX_glc__D_e_lb" value="-10" units="mmol_per_gDW_per_hr" constant="true"/> + <parameter id="R_ATPM_lb" name="R_ATPM_lb" value="3.15" units="mmol_per_gDW_per_hr" constant="true"/> + </listOfParameters> + <listOfReactions> + <reaction id="R_EX_aa" name="R_EX_aa" reversible="true" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub"> + <listOfReactants> + <speciesReference species="M_aa_e" stoichiometry="1" constant="true"/> + </listOfReactants> + </reaction> + <reaction id="R_EX_co2" name="R_EX_co2" reversible="true" fbc:lowerFluxBound="default_lb" fbc:upperFluxBound="default_ub"> + <listOfReactants> + <speciesReference species="M_co2_e" stoichiometry="1" constant="true"/> + </listOfReactants> + </reaction> + <reaction id="R_EX_for" name="R_EX_for" reversible="true" fbc:lowerFluxBound="default_lb" fbc:upperFluxBound="default_ub"> + <listOfReactants> + <speciesReference species="M_for_e" stoichiometry="1" constant="true"/> + </listOfReactants> + </reaction> + <reaction id="R_EX_glc__D" name="R_EX_glc__D" reversible="true" fbc:lowerFluxBound="R_EX_glc__D_e_lb" fbc:upperFluxBound="default_ub"> + <notes> + <body xmlns="http://www.w3.org/1999/xhtml"> carbon source transporter </body> + </notes> + <listOfReactants> + <speciesReference species="M_glc__D_e" stoichiometry="1" constant="true"/> + </listOfReactants> + </reaction> + <reaction id="R_EX_lac" name="R_EX_lac" reversible="true" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub"> + <listOfReactants> + <speciesReference species="M_lac_e" stoichiometry="1" constant="true"/> + </listOfReactants> + </reaction> + <reaction id="R_EX_nh4" name="R_EX_nh4" reversible="true" fbc:lowerFluxBound="default_lb" fbc:upperFluxBound="default_ub"> + <listOfReactants> + <speciesReference species="M_nh4_e" stoichiometry="1" constant="true"/> + </listOfReactants> + </reaction> + <reaction id="R_EX_o2" name="R_EX_o2" reversible="true" fbc:lowerFluxBound="default_lb" fbc:upperFluxBound="default_ub"> + <listOfReactants> + <speciesReference species="M_o2_e" stoichiometry="1" constant="true"/> + </listOfReactants> + </reaction> + <reaction id="R_DM_Biomass" name="R_DM_Biomass" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub"> + <listOfReactants> + <speciesReference species="Biomass" stoichiometry="1" constant="true"/> + </listOfReactants> + </reaction> + <reaction id="R_aa_imp" name="R_aa_imp" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub"> + <listOfReactants> + <speciesReference species="M_aa_e" stoichiometry="1" constant="true"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="M_aa_c" stoichiometry="1" constant="true"/> + </listOfProducts> + <fbc:geneProductAssociation> + <fbc:and> + <fbc:geneProductRef fbc:geneProduct="G_b2215"/> + <fbc:geneProductRef fbc:geneProduct="G_b2836"/> + </fbc:and> + </fbc:geneProductAssociation> + </reaction> + <reaction id="R_co2_tex" name="R_co2_tex" reversible="true" fbc:lowerFluxBound="default_lb" fbc:upperFluxBound="default_ub"> + <listOfReactants> + <speciesReference species="M_co2_e" stoichiometry="1" constant="true"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="M_co2_c" stoichiometry="1" constant="true"/> + </listOfProducts> + </reaction> + <reaction id="R_for_tex" name="R_for_tex" reversible="true" fbc:lowerFluxBound="default_lb" fbc:upperFluxBound="default_ub"> + <listOfReactants> + <speciesReference species="M_for_e" stoichiometry="1" constant="true"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="M_for_c" stoichiometry="1" constant="true"/> + </listOfProducts> + <fbc:geneProductAssociation> + <fbc:or> + <fbc:geneProductRef fbc:geneProduct="G_b1377"/> + <fbc:geneProductRef fbc:geneProduct="G_b0929"/> + </fbc:or> + </fbc:geneProductAssociation> + </reaction> + <reaction id="R_for_texi" name="R_for_texi" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub"> + <listOfReactants> + <speciesReference species="M_for_c" stoichiometry="1" constant="true"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="M_for_e" stoichiometry="1" constant="true"/> + </listOfProducts> + </reaction> + <reaction id="R_glc__D_tex" name="R_glc__D_tex" reversible="true" fbc:lowerFluxBound="default_lb" fbc:upperFluxBound="default_ub"> + <listOfReactants> + <speciesReference species="M_glc__D_e" stoichiometry="1" constant="true"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="M_glc__D_c" stoichiometry="1" constant="true"/> + </listOfProducts> + <fbc:geneProductAssociation> + <fbc:or> + <fbc:geneProductRef fbc:geneProduct="G_b1377"/> + <fbc:geneProductRef fbc:geneProduct="G_b0929"/> + </fbc:or> + </fbc:geneProductAssociation> + </reaction> + <reaction id="R_lac_tex" name="R_lac_tex" reversible="true" fbc:lowerFluxBound="default_lb" fbc:upperFluxBound="default_ub"> + <listOfReactants> + <speciesReference species="M_lac_e" stoichiometry="1" constant="true"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="M_lac_c" stoichiometry="1" constant="true"/> + </listOfProducts> + <fbc:geneProductAssociation> + <fbc:or> + <fbc:and> + <fbc:geneProductRef fbc:geneProduct="G_b3670"/> + <fbc:geneProductRef fbc:geneProduct="G_b3671"/> + </fbc:and> + <fbc:and> + <fbc:geneProductRef fbc:geneProduct="G_b0077"/> + <fbc:geneProductRef fbc:geneProduct="G_b0078"/> + </fbc:and> + </fbc:or> + </fbc:geneProductAssociation> + </reaction> + <reaction id="R_nh4_imp" name="R_nh4_imp" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub"> + <listOfReactants> + <speciesReference species="M_nh4_e" stoichiometry="1" constant="true"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="M_nh4_c" stoichiometry="1" constant="true"/> + </listOfProducts> + <fbc:geneProductAssociation> + <fbc:geneProductRef fbc:geneProduct="G_b1377"/> + </fbc:geneProductAssociation> + </reaction> + <reaction id="R_o2_tex" name="R_o2_tex" reversible="true" fbc:lowerFluxBound="default_lb" fbc:upperFluxBound="default_ub"> + <listOfReactants> + <speciesReference species="M_o2_e" stoichiometry="1" constant="true"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="M_o2_c" stoichiometry="1" constant="true"/> + </listOfProducts> + <fbc:geneProductAssociation> + <fbc:geneProductRef fbc:geneProduct="G_b2215"/> + </fbc:geneProductAssociation> + </reaction> + <reaction id="R_mr2" name="R_mr2" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub"> + <listOfReactants> + <speciesReference species="M_glc__D_c" stoichiometry="1" constant="true"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="M_accoa_c" stoichiometry="3" constant="true"/> + </listOfProducts> + <fbc:geneProductAssociation> + <fbc:geneProductRef fbc:geneProduct="G_b0929"/> + </fbc:geneProductAssociation> + </reaction> + <reaction id="R_ppp1" name="R_ppp1" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub"> + <listOfReactants> + <speciesReference species="M_glc__D_c" stoichiometry="1" constant="true"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="M_rib_c" stoichiometry="1" constant="true"/> + </listOfProducts> + <fbc:geneProductAssociation> + <fbc:geneProductRef fbc:geneProduct="G_b0241"/> + </fbc:geneProductAssociation> + </reaction> + <reaction id="R_ppp2" name="R_ppp2" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub"> + <listOfReactants> + <speciesReference species="M_rib_c" stoichiometry="1" constant="true"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="M_accoa_c" stoichiometry="3" constant="true"/> + </listOfProducts> + <fbc:geneProductAssociation> + <fbc:geneProductRef fbc:geneProduct="G_b4034"/> + </fbc:geneProductAssociation> + </reaction> + <reaction id="R_mrOx" name="R_mrOx" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub"> + <listOfReactants> + <speciesReference species="M_accoa_c" stoichiometry="1" constant="true"/> + <speciesReference species="M_adp_c" stoichiometry="4" constant="true"/> + <speciesReference species="M_o2_c" stoichiometry="2" constant="true"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="M_atp_c" stoichiometry="4" constant="true"/> + <speciesReference species="M_co2_c" stoichiometry="2" constant="true"/> + </listOfProducts> + <fbc:geneProductAssociation> + <fbc:geneProductRef fbc:geneProduct="G_b4033"/> + </fbc:geneProductAssociation> + </reaction> + <reaction id="R_mrFerm" name="R_mrFerm" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub"> + <listOfReactants> + <speciesReference species="M_accoa_c" stoichiometry="1" constant="true"/> + <speciesReference species="M_adp_c" stoichiometry="2" constant="true"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="M_atp_c" stoichiometry="2" constant="true"/> + <speciesReference species="M_for_c" stoichiometry="1" constant="true"/> + </listOfProducts> + <fbc:geneProductAssociation> + <fbc:geneProductRef fbc:geneProduct="G_b4032"/> + </fbc:geneProductAssociation> + </reaction> + <reaction id="R_mr5" name="R_mr5" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub"> + <listOfReactants> + <speciesReference species="M_glc__D_c" stoichiometry="1" constant="true"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="M_aapre_c" stoichiometry="1" constant="true"/> + </listOfProducts> + <fbc:geneProductAssociation> + <fbc:geneProductRef fbc:geneProduct="G_b4035"/> + </fbc:geneProductAssociation> + </reaction> + <reaction id="R_mr7" name="R_mr7" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub"> + <listOfReactants> + <speciesReference species="M_aapre_c" stoichiometry="1" constant="true"/> + <speciesReference species="M_nh4_c" stoichiometry="1" constant="true"/> + <speciesReference species="M_atp_c" stoichiometry="1" constant="true"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="M_aa_c" stoichiometry="1" constant="true"/> + <speciesReference species="M_adp_c" stoichiometry="1" constant="true"/> + </listOfProducts> + <fbc:geneProductAssociation> + <fbc:geneProductRef fbc:geneProduct="G_b4036"/> + </fbc:geneProductAssociation> + </reaction> + <reaction id="R_mr9" name="R_mr9" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub"> + <listOfReactants> + <speciesReference species="M_lac_c" stoichiometry="1" constant="true"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="M_glc__D_c" stoichiometry="1" constant="true"/> + </listOfProducts> + <fbc:geneProductAssociation> + <fbc:geneProductRef fbc:geneProduct="G_b4213"/> + </fbc:geneProductAssociation> + </reaction> + <reaction id="R_mr11" name="R_mr11" reversible="true" fbc:lowerFluxBound="default_lb" fbc:upperFluxBound="default_ub"> + <listOfReactants> + <speciesReference species="M_aa_c" stoichiometry="1" constant="true"/> + <speciesReference species="M_adp_c" stoichiometry="1" constant="true"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="M_accoa_c" stoichiometry="3" constant="true"/> + <speciesReference species="M_nh4_c" stoichiometry="1" constant="true"/> + <speciesReference species="M_atp_c" stoichiometry="1" constant="true"/> + </listOfProducts> + <fbc:geneProductAssociation> + <fbc:geneProductRef fbc:geneProduct="G_b2835"/> + </fbc:geneProductAssociation> + </reaction> + <reaction id="R_ATPM" name="R_ATPM" reversible="false" fbc:lowerFluxBound="R_ATPM_lb" fbc:upperFluxBound="default_ub"> + <listOfReactants> + <speciesReference species="M_atp_c" stoichiometry="1" constant="true"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="M_adp_c" stoichiometry="1" constant="true"/> + </listOfProducts> + <fbc:geneProductAssociation> + <fbc:geneProductRef fbc:geneProduct="G_b2836"/> + </fbc:geneProductAssociation> + </reaction> + <reaction id="R_Biomass_core" name="R_Biomass_core" reversible="false" fbc:lowerFluxBound="zero_bound" fbc:upperFluxBound="default_ub"> + <notes> + <body xmlns="http://www.w3.org/1999/xhtml"> biomass reaction </body> + </notes> + <listOfReactants> + <speciesReference species="M_aa_c" stoichiometry="1" constant="true"/> + <speciesReference species="M_atp_c" stoichiometry="3" constant="true"/> + </listOfReactants> + <listOfProducts> + <speciesReference species="M_adp_c" stoichiometry="3" constant="true"/> + <speciesReference species="Biomass" stoichiometry="1" constant="true"/> + </listOfProducts> + </reaction> + </listOfReactions> + <fbc:listOfObjectives fbc:activeObjective="obj"> + <fbc:objective fbc:id="obj" fbc:type="maximize"> + <fbc:listOfFluxObjectives> + <fbc:fluxObjective fbc:reaction="R_Biomass_core" fbc:coefficient="1"/> + </fbc:listOfFluxObjectives> + </fbc:objective> + </fbc:listOfObjectives> + <fbc:listOfGeneProducts> + <fbc:geneProduct metaid="G_b1377" fbc:id="G_b1377" fbc:label="b1377"> + <annotation> + <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/"> + <rdf:Description rdf:about="#G_b1377"> + <bqbiol:is> + <rdf:Bag> + <rdf:li rdf:resource="http://identifiers.org/uniprot/P77747"/> + </rdf:Bag> + </bqbiol:is> + <bqbiol:isEncodedBy> + <rdf:Bag> + <rdf:li rdf:resource="http://identifiers.org/asap/ABE-0004608"/> + <rdf:li rdf:resource="http://identifiers.org/ecogene/EG13375"/> + <rdf:li rdf:resource="http://identifiers.org/ncbigene/946313"/> + <rdf:li rdf:resource="http://identifiers.org/ncbigi/gi:16129338"/> + </rdf:Bag> + </bqbiol:isEncodedBy> + </rdf:Description> + </rdf:RDF> + </annotation> + </fbc:geneProduct> + <fbc:geneProduct metaid="G_b2215" fbc:id="G_b2215" fbc:label="b2215"> + <annotation> + <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/"> + <rdf:Description rdf:about="#G_b2215"> + <bqbiol:is> + <rdf:Bag> + <rdf:li rdf:resource="http://identifiers.org/uniprot/P06996"/> + </rdf:Bag> + </bqbiol:is> + <bqbiol:isEncodedBy> + <rdf:Bag> + <rdf:li rdf:resource="http://identifiers.org/asap/ABE-0007321"/> + <rdf:li rdf:resource="http://identifiers.org/ecogene/EG10670"/> + <rdf:li rdf:resource="http://identifiers.org/ncbigene/946716"/> + <rdf:li rdf:resource="http://identifiers.org/ncbigi/gi:16130152"/> + </rdf:Bag> + </bqbiol:isEncodedBy> + </rdf:Description> + </rdf:RDF> + </annotation> + </fbc:geneProduct> + <fbc:geneProduct metaid="G_b0929" fbc:id="G_b0929" fbc:label="b0929"> + <annotation> + <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/"> + <rdf:Description rdf:about="#G_b0929"> + <bqbiol:is> + <rdf:Bag> + <rdf:li rdf:resource="http://identifiers.org/uniprot/P02931"/> + </rdf:Bag> + </bqbiol:is> + <bqbiol:isEncodedBy> + <rdf:Bag> + <rdf:li rdf:resource="http://identifiers.org/asap/ABE-0003153"/> + <rdf:li rdf:resource="http://identifiers.org/ecogene/EG10671"/> + <rdf:li rdf:resource="http://identifiers.org/ncbigene/945554"/> + <rdf:li rdf:resource="http://identifiers.org/ncbigi/gi:16128896"/> + </rdf:Bag> + </bqbiol:isEncodedBy> + </rdf:Description> + </rdf:RDF> + </annotation> + </fbc:geneProduct> + <fbc:geneProduct metaid="G_b0241" fbc:id="G_b0241" fbc:label="b0241"> + <annotation> + <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/"> + <rdf:Description rdf:about="#G_b0241"> + <bqbiol:is> + <rdf:Bag> + <rdf:li rdf:resource="http://identifiers.org/uniprot/P02932"/> + </rdf:Bag> + </bqbiol:is> + <bqbiol:isEncodedBy> + <rdf:Bag> + <rdf:li rdf:resource="http://identifiers.org/asap/ABE-0000823"/> + <rdf:li rdf:resource="http://identifiers.org/ecogene/EG10729"/> + <rdf:li rdf:resource="http://identifiers.org/ncbigene/944926"/> + <rdf:li rdf:resource="http://identifiers.org/ncbigi/gi:16128227"/> + </rdf:Bag> + </bqbiol:isEncodedBy> + </rdf:Description> + </rdf:RDF> + </annotation> + </fbc:geneProduct> + <fbc:geneProduct metaid="G_b4034" fbc:id="G_b4034" fbc:label="b4034"> + <annotation> + <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/"> + <rdf:Description rdf:about="#G_b4034"> + <bqbiol:is> + <rdf:Bag> + <rdf:li rdf:resource="http://identifiers.org/uniprot/P0AEX9"/> + </rdf:Bag> + </bqbiol:is> + <bqbiol:isEncodedBy> + <rdf:Bag> + <rdf:li rdf:resource="http://identifiers.org/asap/ABE-0013200"/> + <rdf:li rdf:resource="http://identifiers.org/ecogene/EG10554"/> + <rdf:li rdf:resource="http://identifiers.org/ncbigene/948538"/> + <rdf:li rdf:resource="http://identifiers.org/ncbigi/gi:16131860"/> + </rdf:Bag> + </bqbiol:isEncodedBy> + </rdf:Description> + </rdf:RDF> + </annotation> + </fbc:geneProduct> + <fbc:geneProduct metaid="G_b4033" fbc:id="G_b4033" fbc:label="b4033"> + <annotation> + <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/"> + <rdf:Description rdf:about="#G_b4033"> + <bqbiol:is> + <rdf:Bag> + <rdf:li rdf:resource="http://identifiers.org/uniprot/P02916"/> + </rdf:Bag> + </bqbiol:is> + <bqbiol:isEncodedBy> + <rdf:Bag> + <rdf:li rdf:resource="http://identifiers.org/asap/ABE-0013195"/> + <rdf:li rdf:resource="http://identifiers.org/ecogene/EG10555"/> + <rdf:li rdf:resource="http://identifiers.org/ncbigene/948532"/> + <rdf:li rdf:resource="http://identifiers.org/ncbigi/gi:16131859"/> + </rdf:Bag> + </bqbiol:isEncodedBy> + </rdf:Description> + </rdf:RDF> + </annotation> + </fbc:geneProduct> + <fbc:geneProduct metaid="G_b4032" fbc:id="G_b4032" fbc:label="b4032"> + <annotation> + <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/"> + <rdf:Description rdf:about="#G_b4032"> + <bqbiol:is> + <rdf:Bag> + <rdf:li rdf:resource="http://identifiers.org/uniprot/P68183"/> + </rdf:Bag> + </bqbiol:is> + <bqbiol:isEncodedBy> + <rdf:Bag> + <rdf:li rdf:resource="http://identifiers.org/asap/ABE-0013193"/> + <rdf:li rdf:resource="http://identifiers.org/ecogene/EG10556"/> + <rdf:li rdf:resource="http://identifiers.org/ncbigene/948530"/> + <rdf:li rdf:resource="http://identifiers.org/ncbigi/gi:16131858"/> + </rdf:Bag> + </bqbiol:isEncodedBy> + </rdf:Description> + </rdf:RDF> + </annotation> + </fbc:geneProduct> + <fbc:geneProduct metaid="G_b4035" fbc:id="G_b4035" fbc:label="b4035"> + <annotation> + <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/"> + <rdf:Description rdf:about="#G_b4035"> + <bqbiol:is> + <rdf:Bag> + <rdf:li rdf:resource="http://identifiers.org/uniprot/P68187"/> + </rdf:Bag> + </bqbiol:is> + <bqbiol:isEncodedBy> + <rdf:Bag> + <rdf:li rdf:resource="http://identifiers.org/asap/ABE-0013216"/> + <rdf:li rdf:resource="http://identifiers.org/ecogene/EG10558"/> + <rdf:li rdf:resource="http://identifiers.org/ncbigene/948537"/> + <rdf:li rdf:resource="http://identifiers.org/ncbigi/gi:16131861"/> + </rdf:Bag> + </bqbiol:isEncodedBy> + </rdf:Description> + </rdf:RDF> + </annotation> + </fbc:geneProduct> + <fbc:geneProduct metaid="G_b4036" fbc:id="G_b4036" fbc:label="b4036"> + <annotation> + <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/"> + <rdf:Description rdf:about="#G_b4036"> + <bqbiol:is> + <rdf:Bag> + <rdf:li rdf:resource="http://identifiers.org/uniprot/P02943"/> + </rdf:Bag> + </bqbiol:is> + <bqbiol:isEncodedBy> + <rdf:Bag> + <rdf:li rdf:resource="http://identifiers.org/asap/ABE-0013218"/> + <rdf:li rdf:resource="http://identifiers.org/ecogene/EG10528"/> + <rdf:li rdf:resource="http://identifiers.org/ncbigene/948548"/> + <rdf:li rdf:resource="http://identifiers.org/ncbigi/gi:16131862"/> + </rdf:Bag> + </bqbiol:isEncodedBy> + </rdf:Description> + </rdf:RDF> + </annotation> + </fbc:geneProduct> + <fbc:geneProduct metaid="G_b4213" fbc:id="G_b4213" fbc:label="b4213"> + <annotation> + <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/"> + <rdf:Description rdf:about="#G_b4213"> + <bqbiol:is> + <rdf:Bag> + <rdf:li rdf:resource="http://identifiers.org/uniprot/P08331"/> + </rdf:Bag> + </bqbiol:is> + <bqbiol:isEncodedBy> + <rdf:Bag> + <rdf:li rdf:resource="http://identifiers.org/asap/ABE-0013782"/> + <rdf:li rdf:resource="http://identifiers.org/ecogene/EG10160"/> + <rdf:li rdf:resource="http://identifiers.org/ncbigene/948729"/> + <rdf:li rdf:resource="http://identifiers.org/ncbigi/gi:16132035"/> + </rdf:Bag> + </bqbiol:isEncodedBy> + </rdf:Description> + </rdf:RDF> + </annotation> + </fbc:geneProduct> + <fbc:geneProduct metaid="G_b2835" fbc:id="G_b2835" fbc:label="b2835"> + <annotation> + <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/"> + <rdf:Description rdf:about="#G_b2835"> + <bqbiol:is> + <rdf:Bag> + <rdf:li rdf:resource="http://identifiers.org/uniprot/P39196"/> + </rdf:Bag> + </bqbiol:is> + <bqbiol:isEncodedBy> + <rdf:Bag> + <rdf:li rdf:resource="http://identifiers.org/asap/ABE-0009300"/> + <rdf:li rdf:resource="http://identifiers.org/ecogene/EG12455"/> + <rdf:li rdf:resource="http://identifiers.org/ncbigene/947317"/> + <rdf:li rdf:resource="http://identifiers.org/ncbigi/gi:16130739"/> + </rdf:Bag> + </bqbiol:isEncodedBy> + </rdf:Description> + </rdf:RDF> + </annotation> + </fbc:geneProduct> + <fbc:geneProduct metaid="G_b2836" fbc:id="G_b2836" fbc:label="b2836"> + <annotation> + <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:vCard="http://www.w3.org/2001/vcard-rdf/3.0#" xmlns:vCard4="http://www.w3.org/2006/vcard/ns#" xmlns:bqbiol="http://biomodels.net/biology-qualifiers/" xmlns:bqmodel="http://biomodels.net/model-qualifiers/"> + <rdf:Description rdf:about="#G_b2836"> + <bqbiol:is> + <rdf:Bag> + <rdf:li rdf:resource="http://identifiers.org/uniprot/P31119"/> + </rdf:Bag> + </bqbiol:is> + <bqbiol:isEncodedBy> + <rdf:Bag> + <rdf:li rdf:resource="http://identifiers.org/asap/ABE-0009302"/> + <rdf:li rdf:resource="http://identifiers.org/ecogene/EG11679"/> + <rdf:li rdf:resource="http://identifiers.org/ncbigene/947315"/> + <rdf:li rdf:resource="http://identifiers.org/ncbigi/gi:16130740"/> + </rdf:Bag> + </bqbiol:isEncodedBy> + </rdf:Description> + </rdf:RDF> + </annotation> + </fbc:geneProduct> + <fbc:geneProduct metaid="G_b3670" fbc:id="G_b3670" fbc:label="b3670"/> + <fbc:geneProduct metaid="G_b3671" fbc:id="G_b3671" fbc:label="b3671"/> + <fbc:geneProduct metaid="G_b0077" fbc:id="G_b0077" fbc:label="b0077"/> + <fbc:geneProduct metaid="G_b0078" fbc:id="G_b0078" fbc:label="b0078"/> + </fbc:listOfGeneProducts> + <groups:listOfGroups> + <groups:group groups:id="g1" groups:name="Carbon metabolism" groups:kind="partonomy"> + <groups:listOfMembers> + <groups:member groups:idRef="R_mr2"/> + <groups:member groups:idRef="R_mr5"/> + <groups:member groups:idRef="R_mr7"/> + <groups:member groups:idRef="R_mr9"/> + <groups:member groups:idRef="R_mr11"/> + </groups:listOfMembers> + </groups:group> + <groups:group groups:id="g2" groups:name="Fermentation" groups:kind="partonomy"> + <groups:listOfMembers> + <groups:member groups:idRef="R_mrFerm"/> + </groups:listOfMembers> + </groups:group> + <groups:group groups:id="g3" groups:name="Maintenance" groups:kind="partonomy"> + <groups:listOfMembers> + <groups:member groups:idRef="R_ATPM"/> + </groups:listOfMembers> + </groups:group> + <groups:group groups:id="g4" groups:name="Oxidative Phosphorylation" groups:kind="partonomy"> + <groups:listOfMembers> + <groups:member groups:idRef="R_mrOx"/> + </groups:listOfMembers> + </groups:group> + <groups:group groups:id="g5" groups:name="Glucolysis" groups:kind="partonomy"> + <groups:listOfMembers> + <groups:member groups:idRef="R_mr2"/> + </groups:listOfMembers> + </groups:group> + <groups:group groups:id="g6" groups:name="Pentose Phosphate Pathway" groups:kind="partonomy"> + <groups:listOfMembers> + <groups:member groups:idRef="R_ppp1"/> + <groups:member groups:idRef="R_ppp2"/> + </groups:listOfMembers> + </groups:group> + <groups:group groups:id="g7" groups:name="Transporter" groups:kind="partonomy"> + <groups:listOfMembers> + <groups:member groups:idRef="R_co2_tex"/> + <groups:member groups:idRef="R_glc__D_tex"/> + <groups:member groups:idRef="R_lac_tex"/> + <groups:member groups:idRef="R_o2_tex"/> + </groups:listOfMembers> + </groups:group> + <groups:group groups:id="g8" groups:name="Transport, Exporter" groups:kind="partonomy"> + <groups:listOfMembers> + <groups:member groups:idRef="R_for_texi"/> + </groups:listOfMembers> + </groups:group> + <groups:group groups:id="g9" groups:name="Transport, Importer" groups:kind="partonomy"> + <groups:listOfMembers> + <groups:member groups:idRef="R_aa_imp"/> + <groups:member groups:idRef="R_nh4_imp"/> + </groups:listOfMembers> + </groups:group> + </groups:listOfGroups> + </model> +</sbml> diff --git a/sample_data/data/Deniz_model_fba_nrp2.xlsx b/sample_data/data/Deniz_model_fba_nrp2.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..24fcb71c4eff84d47b7f7e513c08bb9dc728d027 GIT binary patch literal 14046 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 literal 0 HcmV?d00001 diff --git a/sample_prune_model.py b/sample_prune_model.py new file mode 100644 index 0000000..134b319 --- /dev/null +++ b/sample_prune_model.py @@ -0,0 +1,50 @@ +# sample_reduce_model.py +# sample script for network reduction of a SBML fbc model + +import sys +import os +# import logging + +import modelpruner + + +def prune_model(): + + # activate logging + # logfile = './log/nr.log' + # logformat = '%(asctime)s %(levelname)s:%(name)s %(message)s' + # logdir = os.path.dirname(logfile) + # if not os.path.exists(logdir): + # os.mkdir(logdir) + # logging.basicConfig(filename=logfile, filemode='w', + # level=logging.INFO, format=logformat) + # logging.info('Started') + + # print some environment information + print('Python version: {:d}.{:d}.{:d}'.format(sys.version_info.major, + sys.version_info.minor, + sys.version_info.micro)) + print('modelpruner version: {:s}'.format(modelpruner.__version__)) + print('working directory :', os.getcwd()) + + # file names + full_sbml = 'sample_data/SBML_models/Deniz_model_fba.xml' + pruned_sbml = 'sample_data/SBML_models/Deniz_model_fba_reduced.xml' + protected_fname = 'sample_data/data/Deniz_model_fba_nrp2.xlsx' + + # load the original model + mp = modelpruner.ModelPruner(full_sbml, protected_fname) + + print('---- network reduction -----') + mp.prune() + + # export pruned model in sbml format + mp.export_pruned_model(pruned_sbml) + print('pruned model converted to SBML:', pruned_sbml) + print('finally protected reactions:', mp.protected_rids) + print(mp.print_status()) + # logging.info('Finished') + + +if __name__ == '__main__': + prune_model() -- GitLab