# cone = np.transpose(T_intersected) cone_transpose, ids = unsplit_metabolites(np.transpose(cone), network) cone = np.transpose(cone_transpose) # internal_ids = [] for metab in network.metabolites: if not metab.is_external: id_ind = [ind for ind, id in enumerate(ids) if id == metab.id] if len(id_ind): internal_ids.append(id_ind[0]) ids = list(np.delete(ids, internal_ids)) cone = np.delete(cone, internal_ids, axis=1) if mpi_wrapper.is_first_process(): try: np.savetxt(args.out_path, cone, delimiter=',', header=','.join(ids), comments='') except OverflowError: normalised = np.transpose(normalize_columns(np.transpose(cone))) np.savetxt(args.out_path, normalised, delimiter=',', header=','.join(ids), comments='') if args.print_conversions:
def get_conversion_cone(N, external_metabolites=[], reversible_reactions=[], input_metabolites=[], output_metabolites=[], only_rays=False, verbose=False, redund_after_polco=True): """ Calculates the conversion cone as described in (Urbanczik, 2005). :param N: stoichiometry matrix :param external_metabolites: list of row numbers (0-based) of metabolites that are tagged as in/outputs :param reversible_reactions: list of booleans stating whether the reaction at this column is reversible :param input_metabolites: list of row numbers (0-based) of metabolites that are tagged as inputs :param output_metabolites: list of row numbers (0-based) of metabolites that are tagged as outputs :param only_rays: return only the extreme rays of the conversion cone, and not the elementary vectors (ECMs instead of ECVs) :param verbose: print status messages during enumeration :param redund_after_polco: Optionally remove redundant rays from H_eq and H_ineq before final extreme ray enumeration by Polco :return: matrix with conversion cone "c" as row vectors """ if mpi_wrapper.is_first_process(): amount_metabolites, amount_reactions = N.shape[0], N.shape[1] # External metabolites that have no direction specified in_out_metabolites = np.setdiff1d( external_metabolites, np.append(input_metabolites, output_metabolites, axis=0)) added_virtual_metabolites = np.asarray(np.add( range(len(in_out_metabolites)), amount_metabolites), dtype='int') extended_external_metabolites = np.append(external_metabolites, added_virtual_metabolites, axis=0) in_out_indices = [ external_metabolites.index(index) for index in in_out_metabolites ] if len(external_metabolites) == 0: return to_fractions(np.ndarray(shape=(0, N.shape[0]))) # Compose G of the columns of N G = np.transpose(N) # TODO: remove debug block # G = np.asarray(G * 10**3, dtype=np.int64) G_exp = G[:, :] G_rev = np.ndarray(shape=(0, G.shape[1]), dtype='object') G_irrev = np.ndarray(shape=(0, G.shape[1]), dtype='object') # Add reversible reactions (columns) of N to G in the negative direction as well for reaction_index in range(G.shape[0]): if reaction_index in reversible_reactions: G_exp = np.append(G_exp, [-G[reaction_index, :]], axis=0) G_rev = np.append(G_rev, [-G[reaction_index, :]], axis=0) else: G_irrev = np.append(G_irrev, [G[reaction_index, :]], axis=0) # Calculate H as the union of our linearities and the extreme rays of matrix G (all as row vectors) if verbose: print('Calculating null space of inequalities system G') linearities = np.transpose(iterative_nullspace(G, verbose=verbose)) if linearities.shape[0] == 0: linearities = np.ndarray(shape=(0, G.shape[1])) linearities_deflated = deflate_matrix(linearities, external_metabolites) # Calculate H as the union of our linearities and the extreme rays of matrix G (all as row vectors) if verbose: print('Calculating extreme rays H of inequalities system G') # Calculate generating set of the dual of our initial conversion cone C0, C0* rays = get_extreme_rays(np.append(linearities, G_rev, axis=0), G_irrev, verbose=verbose) # if rays.shape[0] == 0: # print('Warning: given system has no nonzero inequalities H. Returning empty conversion cone.') # return to_fractions(np.ndarray(shape=(0, G.shape[1]))) if verbose: print('Deflating H') if rays.shape[0] == 0: mp_print( 'Warning: first polco-application did not give any rays. Check if this is expected behaviour.' ) rays_deflated = rays else: rays_deflated = deflate_matrix(rays, external_metabolites) if verbose: print('Expanding H with metabolite direction constraints') # Add bidirectional (in- and output) metabolites in reverse direction if rays_deflated.shape[0] == 0: rays_split = rays_deflated else: rays_split = split_columns( rays_deflated, in_out_indices) if not only_rays else rays_deflated linearities_split = split_columns( linearities_deflated, in_out_indices) if not only_rays else linearities_deflated H_ineq = rays_split H_eq = linearities_split # Add input/output constraints to H_ineq if not H_ineq.shape[0]: H_ineq = np.zeros(shape=(1, H_ineq.shape[1])) identity = to_fractions(np.identity(H_ineq.shape[1])) # Bidirectional (in- and output) metabolites. # When enumerating only extreme rays, no splitting is done, and # thus no other dimensions need to have directionality specified. if not only_rays: for list_index, inout_metabolite_index in enumerate( in_out_indices): index = inout_metabolite_index H_ineq = np.append(H_ineq, [identity[index, :]], axis=0) index = len(external_metabolites) + list_index H_ineq = np.append(H_ineq, [identity[index, :]], axis=0) # Inputs for input_metabolite in input_metabolites: index = external_metabolites.index(input_metabolite) H_ineq = np.append(H_ineq, [-identity[index, :]], axis=0) # Outputs for output_metabolite in output_metabolites: index = external_metabolites.index(output_metabolite) H_ineq = np.append(H_ineq, [identity[index, :]], axis=0) if verbose: print('Reducing rows in H by removing redundant rows') # Use redundancy-removal to make H_ineq and H_eq smaller print("Size of H_ineq before redund:", H_ineq.shape[0], H_ineq.shape[1]) print("Size of H_eq before redund:", H_eq.shape[0], H_eq.shape[1]) count_before_ineq = len(H_ineq) count_before_eq = len(H_eq) if verbose: mp_print('Detecting linearities in H_ineq.') H_ineq_transpose, cycle_rays = remove_cycles_redund( np.transpose(H_ineq)) H_ineq = np.transpose(H_ineq_transpose) H_eq = np.concatenate( (H_eq, np.transpose(cycle_rays)), axis=0) # Add found linearities from H_ineq to H_eq # Remove duplicates from H_ineq and H_eq if redund_after_polco: H_ineq_original = H_ineq H_ineq_normalized = np.transpose( normalize_columns(np.transpose(H_ineq.astype(dtype='float')), verbose=verbose)) # unique_inds = find_unique_inds(H_ineq_normalized, verbose=verbose, tol=1e-9) # H_ineq_float = H_ineq_normalized[unique_inds, :] # H_ineq_original = H_ineq_original[unique_inds, :] H_ineq_float, unique_inds = np.unique(H_ineq_normalized, axis=0, return_index=True) H_ineq_original = H_ineq_original[unique_inds, :] # H_ineq_float = unique(H_ineq_normalized) # Find out if rows have been thrown away, and if so, do that as well # unique_inds = find_remaining_rows(H_ineq_float, H_ineq_normalized, verbose=verbose) # H_ineq_original = H_ineq_original[unique_inds, :] else: H_ineq_float = [] H_ineq = [] H_eq = [] if redund_after_polco: H_ineq_float = mpi_wrapper.world_allgather(H_ineq_float) H_ineq_float = H_ineq_float[0] H_ineq = mpi_wrapper.world_allgather(H_ineq) H_ineq = H_ineq[0] H_eq = mpi_wrapper.world_allgather(H_eq) H_eq = H_eq[0] if verbose: mp_print("Size of H_eq after communication step:", H_eq.shape[0], H_eq.shape[1]) use_custom_redund = True # If set to false, redundancy removal with redund from lrslib is used if use_custom_redund: mp_print('Using custom redundancy removal') t1 = time() nonred_inds_ineq, cycle_rays = drop_redundant_rays( np.transpose(H_ineq_float), rays_are_unique=True, linearities=False, normalised=True) mp_print("Custom redund took %f sec" % (time() - t1)) t1 = time() # H_eq = independent_rows(H_eq) mp_print("Removing dependent rows in H_eq took %f sec" % (time() - t1)) else: mp_print('Using classical redundancy removal') t2 = time() H_ineq = redund(H_ineq) mp_print("Redund took %f sec" % (time() - t2)) t2 = time() H_eq = redund(H_eq) mp_print("Redund took %f sec" % (time() - t2)) if mpi_wrapper.is_first_process(): if redund_after_polco: if use_custom_redund: H_ineq = H_ineq_original[nonred_inds_ineq, :] print("Size of H_ineq after redund:", H_ineq.shape[0], H_ineq.shape[1]) print("Size of H_eq after redund:", H_eq.shape[0], H_eq.shape[1]) count_after_ineq = len(H_ineq) count_after_eq = len(H_eq) if verbose: print('Removed %d rows from H in total' % (count_before_eq + count_before_ineq - count_after_eq - count_after_ineq)) # Calculate the extreme rays of the cone C represented by inequalities H_total, resulting in # the elementary conversion modes of the input system. if verbose: print( 'Calculating extreme rays C of inequalities system H_eq, H_ineq' ) linearity_rays = np.ndarray(shape=(0, H_eq.shape[1])) if only_rays and len(in_out_metabolites) > 0: linearities = np.transpose( iterative_nullspace(np.append(H_eq, H_ineq, axis=0), verbose=verbose)) if linearities.shape[0] > 0: if verbose: print('Appending linearities') linearity_rays = np.append(linearity_rays, linearities, axis=0) linearity_rays = np.append(linearity_rays, -linearities, axis=0) H_eq = np.append(H_eq, linearities, axis=0) rays = get_extreme_rays(H_eq if len(H_eq) else None, H_ineq, verbose=verbose) # When calculating only extreme rays, we need to add linealities in both directions if only_rays and len(in_out_metabolites) > 0: rays = np.append(rays, linearity_rays, axis=0) if rays.shape[0] == 0: print('Warning: no feasible Elementary Conversion Modes found') return rays if verbose: print('Inflating rays') if only_rays: rays_inflated = inflate_matrix(rays, external_metabolites, amount_metabolites) else: rays_inflated = inflate_matrix( rays, extended_external_metabolites, amount_metabolites + len(in_out_metabolites)) if verbose: print('Removing non-unique rays') # Merge bidirectional metabolites again, and drop duplicate rows if not only_rays: rays_inflated[:, in_out_metabolites] = np.subtract( rays_inflated[:, in_out_metabolites], rays_inflated[:, G.shape[1]:]) rays_merged = np.asarray(rays_inflated[:, :G.shape[1]], dtype='object') rays_unique = unique(rays_merged) # rays_unique = redund(rays_merged) if verbose: print('Enumerated %d rays' % len(rays_unique)) else: rays_unique = [] rays_unique = mpi_wrapper.world_allgather(rays_unique) rays_unique = rays_unique[0] return rays_unique