Spaces:
No application file
No application file
# Copyright 2001 by Tarjei Mikkelsen. All rights reserved. | |
# | |
# This file is part of the Biopython distribution and governed by your | |
# choice of the "Biopython License Agreement" or the "BSD 3-Clause License". | |
# Please see the LICENSE file that should have been included as part of this | |
# package. | |
"""BioPython Pathway module. | |
Bio.Pathway is a lightweight class library designed to support the following tasks: | |
- Data interchange and preprocessing between pathway databases and analysis software. | |
- Quick prototyping of pathway analysis algorithms | |
The basic object in the Bio.Pathway model is Interaction, which represents an arbitrary | |
interaction between any number of biochemical species. | |
Network objects are used to represent the connectivity between species in pathways | |
and reaction networks. | |
For applications where it is not necessary to explicitly represent network connectivity, | |
the specialized classes Reaction and System should be used in place of Interacton and | |
Network. | |
The Bio.Pathway classes, especially Interaction, are intentionally | |
designed to be very flexible. Their intended use are as wrappers around database | |
specific records, such as BIND objects. The value-added in this module is a | |
framework for representing collections of reactions in a way that supports | |
graph theoretic and numeric analysis. | |
Note: This module should be regarded as a prototype only. API changes are likely. | |
Comments and feature requests are most welcome. | |
""" | |
from functools import reduce | |
from Bio.Pathway.Rep.MultiGraph import MultiGraph | |
class Reaction: | |
"""Abstraction for a biochemical transformation. | |
This class represents a (potentially reversible) biochemical | |
transformation of the type:: | |
a S1 + b S2 + ... --> c P1 + d P2 + ... | |
where: | |
- a, b, c, d ... are positive numeric stochiometric coefficients, | |
- S1, S2, ... are substrates | |
- P1, P2, ... are products | |
A Reaction should be viewed as the net result of one or more individual | |
reaction steps, where each step is potentially facilitated by a different | |
catalyst. Support for 'Reaction algebra' will be added at some point in | |
the future. | |
Attributes: | |
- reactants -- dict of involved species to their stochiometric coefficients: | |
reactants[S] = stochiometric constant for S | |
- catalysts -- list/tuple of tuples of catalysts required for this reaction | |
- reversible -- true iff reaction is reversible | |
- data -- reference to arbitrary additional data | |
Invariants: | |
- for all S in reactants: reactants[S] != 0 | |
- for all C in catalysts: catalysts[C] != 0 | |
""" | |
def __init__(self, reactants=None, catalysts=(), reversible=0, data=None): | |
"""Initialize a new Reaction object.""" | |
# enforce invariants on reactants: | |
if reactants is None: | |
self.reactants = {} | |
else: | |
self.reactants = reactants.copy() | |
# loop over original, edit the copy | |
for r, value in reactants.items(): | |
if value == 0: | |
del self.reactants[r] | |
self.catalysts = sorted(set(catalysts)) | |
self.data = data | |
self.reversible = reversible | |
def __eq__(self, r): | |
"""Return true iff self is equal to r.""" | |
return ( | |
isinstance(r, Reaction) | |
and self.reactants == r.reactants | |
and self.catalysts == r.catalysts | |
and self.data == r.data | |
and self.reversible == r.reversible | |
) | |
def __hash__(self): | |
"""Return a hashcode for self.""" | |
t = tuple(self.species()) | |
return hash(t) | |
def __repr__(self): | |
"""Return a debugging string representation of self.""" | |
return "Reaction(%r, %r, %r, %r)" % ( | |
self.reactants, | |
self.catalysts, | |
self.reversible, | |
self.data, | |
) | |
def __str__(self): | |
"""Return a string representation of self.""" | |
substrates = "" | |
products = "" | |
all_species = sorted(self.reactants) | |
for species in all_species: | |
stoch = self.reactants[species] | |
if stoch < 0: | |
# species is a substrate: | |
if substrates != "": | |
substrates = substrates + " + " | |
if stoch != -1: | |
substrates = substrates + str(abs(stoch)) + " " | |
substrates = substrates + str(species) | |
elif stoch > 0: | |
# species is a product: | |
if products != "": | |
products = products + " + " | |
if stoch != 1: | |
products = products + str(stoch) + " " | |
products = products + str(species) | |
else: | |
raise AttributeError("Invalid 0 coefficient in Reaction.reactants") | |
if self.reversible: | |
return substrates + " <=> " + products | |
else: | |
return substrates + " --> " + products | |
def reverse(self): | |
"""Return a new Reaction that is the reverse of self.""" | |
reactants = {} | |
for r in self.reactants: | |
reactants[r] = -self.reactants[r] | |
return Reaction(reactants, self.catalysts, self.reversible, self.data) | |
def species(self): | |
"""Return a list of all Species involved in self.""" | |
return list(self.reactants) | |
class System: | |
"""Abstraction for a collection of reactions. | |
This class is used in the Bio.Pathway framework to represent an arbitrary | |
collection of reactions without explicitly defined links. | |
Attributes: | |
- None | |
""" | |
def __init__(self, reactions=()): | |
"""Initialize a new System object.""" | |
self.__reactions = set(reactions) | |
def __repr__(self): | |
"""Return a debugging string representation of self.""" | |
return "System(" + ",".join(map(repr, self.__reactions)) + ")" | |
def __str__(self): | |
"""Return a string representation of self.""" | |
return ( | |
"System of " | |
+ str(len(self.__reactions)) | |
+ " reactions involving " | |
+ str(len(self.species())) | |
+ " species" | |
) | |
def add_reaction(self, reaction): | |
"""Add reaction to self.""" | |
self.__reactions.add(reaction) | |
def remove_reaction(self, reaction): | |
"""Remove reaction from self.""" | |
self.__reactions.remove(reaction) | |
def reactions(self): | |
"""Return a list of the reactions in this system. | |
Note the order is arbitrary! | |
""" | |
# TODO - Define __lt__ so that Reactions can be sorted on Python? | |
return list(self.__reactions) | |
def species(self): | |
"""Return a list of the species in this system.""" | |
return sorted( | |
set(reduce(lambda s, x: s + x, [x.species() for x in self.reactions()], [])) | |
) | |
def stochiometry(self): | |
"""Compute the stoichiometry matrix for self. | |
Returns (species, reactions, stoch) where: | |
- species = ordered list of species in this system | |
- reactions = ordered list of reactions in this system | |
- stoch = 2D array where stoch[i][j] is coef of the | |
jth species in the ith reaction, as defined | |
by species and reactions above | |
""" | |
# Note: This an inefficient and ugly temporary implementation. | |
# To be practical, stochiometric matrices should probably | |
# be implemented by sparse matrices, which would require | |
# NumPy dependencies. | |
# | |
# PS: We should implement automatic checking for NumPy here. | |
species = self.species() | |
reactions = self.reactions() | |
stoch = [] * len(reactions) | |
for i in range(len(reactions)): | |
stoch[i] = 0 * len(species) | |
for s in reactions[i].species(): | |
stoch[species.index(s)] = reactions[i].reactants[s] | |
return (species, reactions, stoch) | |
class Interaction: | |
"""An arbitrary interaction between any number of species. | |
This class definition is intended solely as a minimal wrapper interface that should | |
be implemented and extended by more specific abstractions. | |
Attributes: | |
- data -- reference to arbitrary additional data | |
""" | |
def __init_(self, data): | |
self.data = data | |
def __hash__(self): | |
"""Return a hashcode for self.""" | |
return hash(self.data) | |
def __repr__(self): | |
"""Return a debugging string representation of self.""" | |
return "Interaction(" + repr(self.data) + ")" | |
def __str__(self): | |
"""Return a string representation of self.""" | |
return "<" + str(self.data) + ">" | |
class Network: | |
"""A set of species that are explicitly linked by interactions. | |
The network is a directed multigraph with labeled edges. The nodes in the graph | |
are the biochemical species involved. The edges represent an interaction between | |
two species, and the edge label is a reference to the associated Interaction | |
object. | |
Attributes: | |
- None | |
""" | |
def __init__(self, species=()): | |
"""Initialize a new Network object.""" | |
self.__graph = MultiGraph(species) | |
def __repr__(self): | |
"""Return a debugging string representation of this network.""" | |
return "<Network: __graph: " + repr(self.__graph) + ">" | |
def __str__(self): | |
"""Return a string representation of this network.""" | |
return "Network of %i species and %i interactions." % ( | |
len(self.species()), | |
len(self.interactions()), | |
) | |
def add_species(self, species): | |
"""Add species to this network.""" | |
self.__graph.add_node(species) | |
def add_interaction(self, source, sink, interaction): | |
"""Add interaction to this network.""" | |
self.__graph.add_edge(source, sink, interaction) | |
def source(self, species): | |
"""Return list of unique sources for species.""" | |
return self.__graph.parents(species) | |
def source_interactions(self, species): | |
"""Return list of (source, interaction) pairs for species.""" | |
return self.__graph.parent_edges(species) | |
def sink(self, species): | |
"""Return list of unique sinks for species.""" | |
return self.__graph.children(species) | |
def sink_interactions(self, species): | |
"""Return list of (sink, interaction) pairs for species.""" | |
return self.__graph.child_edges(species) | |
def species(self): | |
"""Return list of the species in this network.""" | |
return self.__graph.nodes() | |
def interactions(self): | |
"""Return list of the unique interactions in this network.""" | |
return self.__graph.labels() | |