/*******************************************************************************
* Copyright (c) 2009 University of Edinburgh.
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the BSD Licence, which accompanies this feature
* and can be downloaded from http://groups.inf.ed.ac.uk/pepa/update/licence.txt
******************************************************************************/
package uk.ac.ed.inf.biopepa.core.sba;
import java.util.*;
import java.util.regex.Pattern;
import uk.ac.ed.inf.biopepa.core.compiler.*;
public class SBAModel implements DynamicExpressionModelContext {
private class SystemEquationVisitor {
Set<String> actions = new HashSet<String>();
Map<String, List<SBAReaction>> currentReactions = new HashMap<String, List<SBAReaction>>();
List<SBAReaction> newList;
FunctionChecker fc = new FunctionChecker();
void visit(ComponentNode node) {
// componentCount++;
SBAReaction reaction = null;
SBAComponentBehaviour behaviour;
String name = node.getComponent();
String compartmentName = null;
CompartmentData compartment = node.getCompartment();
if (compartment != null) {
compartmentName = compartment.getName();
compartments.put(compartmentName, compartment.getVolume());
if (components.containsKey(name))
components.get(name).add(node);
else {
Set<ComponentNode> s = new HashSet<ComponentNode>();
s.add(node);
components.put(name, s);
}
} else {
Set<ComponentNode> s = new HashSet<ComponentNode>();
s.add(node);
components.put(name, s);
}
CompiledExpression ce;
ActionData ad;
TransportData td;
String reactionName;
for (PrefixData prefix : compiledBioPEPA.getComponentData(node.getComponent()).getPrefixes()) {
reactionName = prefix.getFunction();
ce = compiledBioPEPA.getFunctionalRate(reactionName).getRightHandSide();
ce.accept(fc);
reaction = new SBAReaction(reactionName, ce);
if (prefix instanceof ActionData) {
// ActionData objects represent an atomic action, unlike
// TransportData which will be broken down into two distinct reactions
ad = (ActionData) prefix;
if (ad.getLocations().size() > 0 &&
!ad.getLocations().contains(compartment.getName())){
continue;
}
behaviour = new SBAComponentBehaviour(name, compartmentName,
prefix.getOperator());
behaviour.setStoichiometry((int) prefix.getStoichometry());
reaction.addComponent(behaviour);
recordReaction(reaction);
} else if (prefix instanceof TransportData) {
td = (TransportData) prefix;
if (compartmentName.equals(td.getSourceLocation())) {
behaviour = new SBAComponentBehaviour(name, compartmentName,
SBAComponentBehaviour.Type.REACTANT);
behaviour.setStoichiometry((int) prefix.getStoichometry());
} else if (compartmentName.equals(td.getTargetLocation())) {
behaviour = new SBAComponentBehaviour(name, td.getTargetLocation(),
SBAComponentBehaviour.Type.PRODUCT);
behaviour.setStoichiometry((int) prefix.getStoichometry());
} else {
continue;
}
if (td.getOperator().equals(PrefixData.Operator.BI_TRANSPORTATION)){
reaction.reversible = true;
}
reaction.transportation = td;
reaction.addComponent(behaviour);
recordReaction(reaction);
/*
* Working version that creates uni-directional copies based
* on source location
*
td = (TransportData) prefix;
if(!compartmentName.equals(td.getSourceLocation())){
continue;
}
behaviour = new SBAComponentBehaviour(name,
compartmentName,
SBAComponentBehaviour.Type.REACTANT);
reaction.addComponent(behaviour);
behaviour = new SBAComponentBehaviour(name,
td.getTargetLocation(),
SBAComponentBehaviour.Type.PRODUCT);
reaction.addComponent(behaviour);
recordReaction(reaction);
if(td.getOperator().equals(PrefixData.Operator.BI_TRANSPORTATION)) {
reaction = new SBAReaction(reactionName, ce);
behaviour = new SBAComponentBehaviour(name,
td.getTargetLocation(),
SBAComponentBehaviour.Type.REACTANT);
reaction.addComponent(behaviour);
behaviour = new SBAComponentBehaviour(name,
compartmentName,
SBAComponentBehaviour.Type.PRODUCT);
reaction.addComponent(behaviour);
recordReaction(reaction);
}
*/
} else {
throw new IllegalArgumentException("Unrecognised subclass of PrefixData.");
}
reaction.addComponent(behaviour);
recordReaction(reaction);
}
}
private final void recordReaction(SBAReaction reaction) {
if (currentReactions.containsKey(reaction.name))
currentReactions.get(reaction.name).add(reaction);
else {
newList = new LinkedList<SBAReaction>();
newList.add(reaction);
currentReactions.put(reaction.name, newList);
}
}
void visit(CooperationNode node) {
visit(node.getLeft());
Map<String, List<SBAReaction>> left = currentReactions;
currentReactions = new HashMap<String, List<SBAReaction>>();
visit(node.getRight());
actions.clear();
for (String s : node.getActions())
actions.add(s);
String action;
for (Map.Entry<String, List<SBAReaction>> me : currentReactions.entrySet()) {
action = me.getKey();
if (actions.contains(action)) {
// Synchronized action
newList = new LinkedList<SBAReaction>();
/*
if (left == null){
System.out.println("left is indeed null\n");
}
if (action == null){
System.out.println("action is null\n");
}
*/
List<SBAReaction>leftActions = left.get(action);
if (leftActions != null){
for (SBAReaction one : left.get(action)){
for (SBAReaction two : me.getValue()){
newList.addAll(SBAReaction.merge(one, two));
}
}
}
left.put(action, newList);
} else {
// Parallel action
if (left.containsKey(action))
left.get(action).addAll(me.getValue());
else {
left.put(action, me.getValue());
}
}
}
currentReactions = left;
}
void visit(SystemEquationNode node) {
if (node instanceof ComponentNode)
visit((ComponentNode) node);
else if (node instanceof CooperationNode)
visit((CooperationNode) node);
else
throw new IllegalArgumentException("Unrecognised subclass of SystemEquationNode.");
}
private class FunctionChecker extends CompiledExpressionVisitor {
@Override
public boolean visit(CompiledDynamicComponent component) {
return false;
}
@Override
public boolean visit(CompiledFunction function) {
switch (function.getFunction()) {
case CEILING:
case FLOOR:
case H:
nonDifferentiableFunctions = true;
break;
default:
}
for (CompiledExpression ce : function.getArguments())
ce.accept(this);
return true;
}
@Override
public boolean visit(CompiledNumber number) {
return true;
}
@Override
public boolean visit(CompiledOperatorNode operator) {
operator.getLeft().accept(this);
operator.getRight().accept(this);
return true;
}
@Override
public boolean visit(CompiledSystemVariable variable) {
switch (variable.getVariable()) {
case TIME:
timeDependentRates = true;
break;
default:
}
return true;
}
}
}
boolean timeDependentRates, nonDifferentiableFunctions;
Map<String, Double> compartments = new HashMap<String, Double>();
ModelCompiler compiledBioPEPA;
// private int componentCount;
private Map<String, Set<ComponentNode>> components = new HashMap<String, Set<ComponentNode>>();
private Map<String, CompiledExpression> dynamicVariables = new HashMap<String, CompiledExpression>();
private Set<String> dontInline = new HashSet<String>();
private Map<String, SBAReaction> reactions = new HashMap<String, SBAReaction>();
public SBAModel(ModelCompiler compiledBioPEPA) {
this.compiledBioPEPA = compiledBioPEPA;
}
public CompartmentData[] getCompartments() {
CompartmentData[] cd = new CompartmentData[compartments.size()];
int i = 0;
for (String s : compartments.keySet())
cd[i++] = compiledBioPEPA.getCompartmentData(s);
return cd;
}
/**
*
* @return
*/
public int getComponentCount() {
return components.size();
}
public String[] getComponentNames() {
// String[] sArray = new String[componentCount];
LinkedList<String> sList = new LinkedList<String>();
// int i = 0;
for (Map.Entry<String, Set<ComponentNode>> me : components.entrySet())
for (ComponentNode node : me.getValue())
sList.add(node.getName());
// sArray[i++] = node.getName();
String[] sArray = sList.toArray(new String[sList.size()]);
Arrays.sort(sArray, String.CASE_INSENSITIVE_ORDER);
return sArray;
}
// Should see about these array producing functions only returning
// either a list, or simply superclass of both lists and arrays
// (iteratable? or something).
public ComponentNode[] getComponents() {
String[] sArray1 = components.keySet().toArray(new String[] {});
Arrays.sort(sArray1, String.CASE_INSENSITIVE_ORDER);
String[] sArray2;
LinkedList<ComponentNode> speciesList = new LinkedList<ComponentNode>();
Map<String, ComponentNode> map = new HashMap<String, ComponentNode>();
for (String s : sArray1) {
map.clear();
for (ComponentNode cn : components.get(s)) {
map.put(cn.getName(), cn);
}
sArray2 = map.keySet().toArray(new String[] {});
Arrays.sort(sArray2, String.CASE_INSENSITIVE_ORDER);
for (String s2 : sArray2)
speciesList.addLast(map.get(s2));
}
ComponentNode[] speciesArray =
speciesList.toArray(new ComponentNode[speciesList.size()]);
return speciesArray;
}
public ComponentNode getNamedComponent(String componentName){
ComponentNode[] componentNodes = getComponents();
for (ComponentNode compNode : componentNodes) {
if (compNode.getName().equals(componentName)) {
return compNode;
}
}
// Maybe we should actually raise an exception here
return null;
}
public long getNamedComponentCount(String componentName) {
ComponentNode compNode = this.getNamedComponent(componentName);
if (compNode != null){
return compNode.getCount();
} else {
// Again maybe we should raise an exception here, even if
// we don't for 'getNamedComponent'
return 0;
}
}
/* Should this throw an exception if the we don't find the component? */
public void setComponentCount(String componentName, long newCount) {
for (ComponentNode compNode : getComponents()) {
if (componentName.equals(compNode.getName())) {
compNode.setCount(newCount);
return;
}
}
}
public CompiledExpression getDynamicExpression(String name) {
return dynamicVariables.get(name);
}
boolean inline(String name) {
return !dontInline.contains(name);
}
public CompiledExpression getStaticExpression(String name) {
if (dynamicVariables.containsKey(name))
return null;
VariableData vd = compiledBioPEPA.getVariableData(name);
if (vd != null)
return vd.getValue();
return null;
}
public String[] getDynamicVariableNames() {
String[] sArray = dynamicVariables.keySet().toArray(new String[] {});
Arrays.sort(sArray, String.CASE_INSENSITIVE_ORDER);
return sArray;
}
public SBAReaction[] getReactions() {
String[] sArray = reactions.keySet().toArray(new String[] {});
Arrays.sort(sArray, String.CASE_INSENSITIVE_ORDER);
SBAReaction[] reactionArray = new SBAReaction[sArray.length];
for (int i = sArray.length - 1; i >= 0; i--)
reactionArray[i] = reactions.get(sArray[i]);
return reactionArray;
}
public void parseBioPEPA() {
SystemEquationVisitor sev = new SystemEquationVisitor();
sev.visit(compiledBioPEPA.getSystemEquation());
Set<String> used = new HashSet<String>();
for (VariableData vd : compiledBioPEPA.getDynamicVariables()) {
dynamicVariables.put(vd.getName(), vd.getValue());
used.add(vd.getName());
if (vd.getUsage() > 1)
dontInline.add(vd.getName());
}
// Singleton reactions which do not need to be renamed
List<SBAReaction> lSBAR;
for (Map.Entry<String, List<SBAReaction>> me : sev.currentReactions.entrySet()) {
lSBAR = me.getValue();
if (lSBAR.size() == 1 && !lSBAR.get(0).isReversible()) {
reactions.put(me.getKey(), me.getValue().get(0));
used.add(me.getKey());
}
}
Pattern p;
ArrayList<Integer> numbers;
int[] intArray;
String name;
int i;
// Multiple reactions which require renaming.
for (Map.Entry<String, List<SBAReaction>> me : sev.currentReactions.entrySet()) {
lSBAR = me.getValue();
if (lSBAR.size() > 1 || lSBAR.get(0).isReversible()) {
// multiple reactions needing renamed
name = me.getKey();
i = name.length() + 1;
numbers = new ArrayList<Integer>();
p = Pattern.compile(name + "_\\d+");
for (String existing : used)
if (p.matcher(existing).matches())
numbers.add(new Integer(existing.substring(i)));
if (numbers.size() > 0) {
intArray = new int[numbers.size()];
for (int index = 0; index < intArray.length; index++)
intArray[index] = numbers.get(index);
Arrays.sort(intArray);
i = intArray[intArray.length - 1] + 1;
} else {
i = 1;
if (used.contains(name))
i++;
}
for (SBAReaction r : lSBAR) {
if (r.isReversible()) {
if (used.contains(name))
r.name = name + "_" + i;
r.forwardName = name + "_" + i++;
r.reversibleName = name + "_" + i++;
used.add(r.forwardName);
used.add(r.reversibleName);
} else {
r.name = name + "_" + i++;
}
reactions.put(r.name, r);
used.add(r.name);
}
}
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (compartments.size() > 0) {
sb.append("// Compartments\n");
for (Map.Entry<String, Double> me : compartments.entrySet())
sb.append(me.getKey()).append(" = ").append(me.getValue()).append(";\n");
sb.append("\n");
}
sb.append("// Components\n");
for (ComponentNode cn : getComponents())
sb.append(cn.getName()).append(" = ").append(cn.getCount()).append(";\n");
sb.append("\n");
sb.append("// Reactions\n");
for (SBAReaction r : reactions.values())
sb.append(r.toString()).append(";\n");
return sb.toString();
}
public boolean containsComponent(String name) {
for (String vName : this.getComponentNames()){
if (vName.equals(name)){
return true;
}
}
return false;
}
public boolean containsVariable(String name) {
for (String vName : this.getDynamicVariableNames()){
if (vName.equals(name)){
return true;
}
}
return false;
}
}