Exemplo n.º 1
0
            #     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:
Exemplo n.º 2
0
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