package uk.ac.ed.inf.biopepa.core.sba;
import java.util.HashSet;
import java.util.Set;
import uk.ac.ed.inf.biopepa.core.analysis.ReactantsParticipantsVisitor;
import uk.ac.ed.inf.biopepa.core.compiler.ComponentNode;
import uk.ac.ed.inf.biopepa.core.sba.SBAComponentBehaviour.Type;
public class AnalysisUtils {
/*
* Returns the component behaviour of the given component if it is
* a reactant in the given reaction and otherwise returns null.
*/
public static SBAComponentBehaviour reactantBehaviour(String component,
SBAReaction reaction){
for (SBAComponentBehaviour cb : reaction.getReactants()){
if (cb.getName().equals(component)){
return cb;
}
}
return null;
}
// Returns true if the given component is a reactant in the given
// reaction.
public static boolean componentIsReactant(String component,
SBAReaction reaction){
return null != reactantBehaviour(component, reaction);
}
/*
* As reactantBehaviour above, returns the component behaviour for
* the given component if it is a product in the given reaction and
* otherwise null
*/
public static SBAComponentBehaviour productBehaviour(String component,
SBAReaction reaction){
for (SBAComponentBehaviour cb : reaction.getProducts()){
if (cb.getName().equals(component)){
return cb;
}
}
return null;
}
/*
* Returns true if the component is a reactant that does not lose
* population
*/
/*
* Returns true if the given component is a product of the given reaction.
*/
public static boolean componentIsProduct(String component,
SBAReaction reaction){
return null != productBehaviour(component, reaction);
}
/*
* Returns the component behaviour of the given component if it
* is involved in the given reaction (that is if it is either or both
* a product or reactant of the given reaction) and null otherwise.
*
* This has the plausible problem that if a component has more than
* one behaviour associated with a given reaction it will only return
* the first such. Currently it is illegal in BioPEPA to have more than
* one, but I'm not sure how well that is enforced nor whether it is
* likely to remain, as some people do like reactions such as:
* A + B --> B + B
*/
public static SBAComponentBehaviour involvedBehaviour(String component,
SBAReaction reaction){
SBAComponentBehaviour cb = reactantBehaviour(component, reaction);
if (cb != null){
return cb;
}
return productBehaviour(component, reaction);
}
/*
* Returns true if the component is either or both a reactant or
* a product in the given reaction.
*/
public static boolean compInvolvedInReaction(String component,
SBAReaction reaction){
if (componentIsReactant(component, reaction)){
return true;
}
if (componentIsProduct(component, reaction)){
return true;
}
return false;
}
public static boolean rateAffected(SBAReaction r, String compName) {
// First we check if the rate does include all of the
// reactants via a rate law, this is the common situation.
// If so then we simply need to check if the component is
// a reactant.
ReactantsParticipantsVisitor rpv = new ReactantsParticipantsVisitor();
r.getRate().accept(rpv);
if (rpv.getReactantsInvolved()) {
for (SBAComponentBehaviour rb : r.getReactants()) {
if (rb.getName().equals(compName)) {
return true;
}
}
}
// If either the component isn't a reactant or a rate
// law is not used, then we simply need to check if
// the component affects the rate, we assume that it
// affects the rate in a positive way, so we are wrong
// if from example the rate E - V, but such rates are
// unusual.
return rpv.getExternalParticipants().contains(compName);
}
/*
* Returns the set of components which affect the rate of the
* given reaction.
*/
public static Set<String> reactionRateModifiers(SBAReaction r) {
ReactantsParticipantsVisitor rpv = new ReactantsParticipantsVisitor();
r.getRate().accept(rpv);
Set<String> results = rpv.getExternalParticipants();
if (rpv.getReactantsInvolved()) {
for (SBAComponentBehaviour reactant : r.getReactants()) {
results.add(reactant.getName());
}
}
return results;
}
/*
* Returns the net gain for a given component of the firing of the given
* reaction. This will be negative if the given component is consumed and
* positive if it is produced. It will be zero if the component is neither
* produced nor consumed, eg: A + B -> A + C will have 0 for A, -1 for B and
* +1 for C.
*/
public static int netGainForReaction(SBAReaction reaction, String comp) {
int netGain = 0;
// First decrease the netGain for any molecules of the given
// component which are consumed.
for (SBAComponentBehaviour reactant : reaction.getReactants()) {
if (reactant.getType().equals(Type.REACTANT) &&
reactant.getName().equals(comp)) {
netGain -= reactant.getStoichiometry();
}
}
// Then increase the netGain for any molecules of the given
// component which are produced.
for (SBAComponentBehaviour product : reaction.getProducts()) {
if (product.getName().equals(comp)) {
netGain += product.getStoichiometry();
}
}
return netGain;
}
// Returns true if the given reaction actually consumes the given
// component behaviour
public static boolean reactionConsumes(SBAReaction r,
String reactant) {
int netGain = netGainForReaction(r, reactant);
return netGain < 0;
}
// Returns true if the given reaction actually produces the given
// component behaviour, should be called with
// for (product : r.getProducts()) { (r, product) }
public static boolean reactionProduces(SBAReaction r,
String product) {
int netGain = netGainForReaction(r, product);
return netGain > 0;
}
// Returns true if the given reaction produces or consumes any
// number of molecules of the given component.
public static boolean reactionModifiesPopulation(SBAReaction r,
String compName){
int netGain = netGainForReaction(r, compName);
return netGain != 0;
}
public static boolean reactionHelps(SBAReaction helper,
SBAReaction helpee) {
// A reaction directly helps another if any of the
// species is produced by the reaction which positively
// affect the rate of the helpee.
Set<String> rateAffectors = reactionRateModifiers(helpee);
for (String reactant : rateAffectors) {
if (reactionProduces(helper, reactant)) {
return true;
}
}
// If none of the reactants of the helpee are produced
// by the helper then it doesn't indeed help it.
return false;
}
public static boolean reactionHinders(SBAReaction hinderer,
SBAReaction hinderee) {
// A reaction directly hinders another if it consumes
// any of the hindered reaction's reactants, when we say
// reactants we mean any component which positively affect
// the rate of the hinderee
Set<String> rateAffectors = reactionRateModifiers(hinderee);
for (String reactant : rateAffectors) {
if (reactionConsumes(hinderer, reactant)) {
return true;
}
}
// If none of the supposedly hindered reactants are
// consumed by the hinderer reaction then there isn't
// a direct hindered relationship (in that direction).
return false;
}
/*
* Returns true if the KIG graph (see below) should have an edge
* between the two given components.
* Essentially there is an (undirected) edge between components A and B
* if there exists a reaction 'r' such that either A affects the rate of
* 'r' and 'r' changes the population of B, or vice versa.
*/
public static boolean componentsConnected(SBAModel model,
String compOne, String compTwo){
for (SBAReaction reaction : model.getReactions()){
boolean aAffectsR = rateAffected(reaction, compOne);
boolean rAdjustsB = reactionModifiesPopulation(reaction, compTwo);
boolean bAffectsR = rateAffected(reaction, compTwo);
boolean rAdjustsA = reactionModifiesPopulation(reaction, compOne);
if ( (aAffectsR && rAdjustsB) ||
(bAffectsR && rAdjustsA)){
return true;
}
}
// If we have not returned true then there should be no edge
return false;
}
/*
* Returns the components to which the given component is connected
* in the KIG graph (see MIDIA algorithm, software, google Clive Bowsher)
* Essentially there is an (undirected) edge between components A and B
* if there exists a reaction 'r' such that either A affects the rate of
* 'r' and 'r' changes the population of B, or vice versa.
*/
public static Set<String> componentEdges(SBAModel model, String compName){
Set<String> results = new HashSet<String>();
for (ComponentNode compnode : model.getComponents()){
// Ignore the self loop edge which almost every component
// will have, unless it is only an enzyme (ie. its population
// count does not change).
String candidateName = compnode.getName();
if (!candidateName.equals(compName) &&
componentsConnected(model, compName, candidateName)){
results.add(candidateName);
}
}
return results;
}
}