/*******************************************************************************
* 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.analysis;
import java.util.*;
import uk.ac.ed.inf.biopepa.core.BioPEPAException;
import uk.ac.ed.inf.biopepa.core.compiler.*;
import uk.ac.ed.inf.biopepa.core.compiler.CompiledFunction.Function;
import uk.ac.ed.inf.biopepa.core.dom.VariableDeclaration.Kind;
import uk.ac.ed.inf.biopepa.core.dom.*;
public class PredefinedLaws {
private static Map<String, FunctionCall> functionMap = new HashMap<String, FunctionCall>();
private static List<ProblemInfo> problems = new ArrayList<ProblemInfo>();
private static ExpressionVisitor expressionVisitor = new ExpressionVisitor();
private static EquationVisitor equationVisitor = new EquationVisitor();
static List<ProblemInfo> checkPredefinedLaws(Model model, ModelCompiler compiledModel) {
VariableDeclaration vd;
ExpressionStatement systemEquation = null;
functionMap.clear();
problems.clear();
for (Statement statement : model.statements()) {
if (statement instanceof VariableDeclaration) {
vd = (VariableDeclaration) statement;
if (vd.getKind().equals(Kind.FUNCTION)) {
try {
expressionVisitor.scan(vd.getRightHandSide());
} catch (BioPEPAException e) {
problems.add(new ProblemInfo(e.getMessage(), vd.getRightHandSide().getSourceRange()));
continue;
}
if (expressionVisitor.functionsFound.size() > 1) {
Function f;
int i = 0;
for (FunctionCall fc : expressionVisitor.functionsFound) {
f = CompiledFunction.getFunction(fc.getName().getIdentifier());
if (f.isRateLaw())
i++;
}
if (i > 1)
problems.add(new ProblemInfo("Embedded function call", vd.getRightHandSide()
.getSourceRange()));
} else if (expressionVisitor.functionsFound.size() == 1)
functionMap.put(vd.getName().getIdentifier(), expressionVisitor.functionsFound.get(0));
}
} else if (statement instanceof ExpressionStatement)
systemEquation = (ExpressionStatement) statement;
}
equationVisitor.model = compiledModel;
try {
systemEquation.accept(equationVisitor);
} catch (BioPEPAException e) {
// TODO
problems.add(new ProblemInfo(e.getMessage(), systemEquation.getSourceRange()));
return problems;
}
for (Map.Entry<String, FunctionCall> me : functionMap.entrySet()) {
String s;
if (equationVisitor.map.containsKey(me.getKey())) {
FunctionCall fc = me.getValue();
s = fc.getName().getIdentifier();
Function f = CompiledFunction.getFunction(fc.getName().getIdentifier());
for (int[] iArray : equationVisitor.map.get(me.getKey()))
if (f.isRateLaw() && !match(s, iArray)) {
problems
.add(new ProblemInfo(
"Predefined function does not have the correct number of components with required behaviours",
me.getValue().getSourceRange()));
break;
}
}
}
return problems;
}
private static class ExpressionVisitor extends DoNothingVisitor {
List<FunctionCall> functionsFound = new ArrayList<FunctionCall>();
public void scan(Expression expression) throws BioPEPAException {
functionsFound.clear();
expression.accept(this);
}
public boolean visit(Cooperation cooperation) throws BioPEPAException {
throw new BioPEPAException("Unexpected cooperation");
}
public boolean visit(FunctionCall functionCall) throws BioPEPAException {
functionsFound.add(functionCall);
for (Expression e : functionCall.arguments())
e.accept(this);
return true;
}
public boolean visit(InfixExpression infixExpression) throws BioPEPAException {
infixExpression.getLeftHandSide().accept(this);
infixExpression.getRightHandSide().accept(this);
return true;
}
public boolean visit(Prefix prefix) throws BioPEPAException {
throw new BioPEPAException("Unexpected prefix");
}
public boolean visit(PropertyInitialiser propertyInitialiser) throws BioPEPAException {
throw new BioPEPAException("Unexpected property initialiser");
}
public boolean visit(PropertyLiteral propertyLiteral) throws BioPEPAException {
throw new BioPEPAException("Unexpected property literal");
}
}
private static class EquationVisitor extends UnsupportedVisitor {
Map<String, List<int[]>> map;
ModelCompiler model;
String s;
public boolean visit(ExpressionStatement statement) throws BioPEPAException {
statement.getExpression().accept(this);
return true;
}
public boolean visit(Component component) throws BioPEPAException {
map = new HashMap<String, List<int[]>>();
int[] i;
String sn, ln = null;
List<String> locations;
Name n = component.getName();
boolean source = false;
if (n instanceof LocatedName) {
sn = ((LocatedName) n).getName();
/*
* We can do the following because we know the model has
* compiled and each component in the model component has one
* location.
*/
ln = ((LocatedName) n).getLocations().names().get(0).getIdentifier();
} else
sn = n.getIdentifier();
for (PrefixData pd : model.getComponentData(sn).getPrefixes()) {
if (ln != null) {
/*
* Again, because of checks already done we know every
* species in the model component will have a location if
* locations are used. We know this as these static checks
* are the last to be performed.
*/
if (pd instanceof ActionData) {
locations = ((ActionData) pd).getLocations();
if (locations.size() > 0 && !locations.contains(ln))
continue;
} else if (pd instanceof TransportData) {
if (((TransportData) pd).getSourceLocation().equals(ln))
source = true;
else if (((TransportData) pd).getTargetLocation().equals(ln))
source = false;
else
continue;
}
}
s = pd.getFunction();
i = new int[] { 0, 0, 0, 0, 0 };
switch (pd.getOperator()) {
case REACTANT:
i[0] = 1;
break;
case ACTIVATOR:
i[1] = 1;
break;
case INHIBITOR:
i[2] = 1;
break;
case GENERIC:
i[3] = 1;
break;
case PRODUCT:
i[4] = 1;
break;
case UNI_TRANSPORTATION:
i[0] = (source ? 1 : 0);
i[4] = (source ? 0 : 1);
case BI_TRANSPORTATION:
i[0] = 1;
i[4] = 1;
break;
default:
throw new BioPEPAException("Unsupported Bio-PEPA operator.");
}
if (!map.containsKey(s))
map.put(s, new ArrayList<int[]>());
map.get(s).add(i);
}
return true;
}
public boolean visit(Cooperation cooperation) throws BioPEPAException {
List<Name> tActions = cooperation.getActionSet().names();
cooperation.getLeftHandSide().accept(this);
Map<String, List<int[]>> left = map;
cooperation.getRightHandSide().accept(this);
// Wildcard sets aren't available from Model so additional code is
// required.
List<int[]> l;
if (tActions.size() == 1 && tActions.get(0).getIdentifier().equals(Cooperation.WILDCARD)) {
for (Map.Entry<String, List<int[]>> me : left.entrySet()) {
s = me.getKey();
if (map.containsKey(s)) {
l = new ArrayList<int[]>();
for (int[] i1 : me.getValue())
for (int[] i2 : map.get(s))
l.add(merge(i1, i2));
map.put(s, l);
} else
map.put(s, me.getValue());
}
return true;
}
// else standard cooperation set
for (Expression expression : tActions) {
s = ((Name) expression).getIdentifier();
if (left.containsKey(s) && map.containsKey(s)) {
// merge into map
l = new ArrayList<int[]>();
for (int[] i1 : left.remove(s))
for (int[] i2 : map.get(s))
l.add(merge(i1, i2));
map.put(s, l);
}
}
for (Map.Entry<String, List<int[]>> me : left.entrySet()) {
if (map.containsKey(me.getKey())) {
map.get(me.getKey()).addAll(me.getValue());
// problems.add(new ProblemInfo(ProblemKind.SYNTAX_ERROR,
// "Non cooperation over shared action name",
// cooperation.getActionSet().getSourceRange()));
} else
map.put(me.getKey(), me.getValue());
}
return true;
}
/*
* (non-Javadoc)
*
* @see
* uk.ac.ed.inf.biopepa.core.dom.UnsupportedVisitor#visit(uk.ac.ed.inf
* .biopepa.core.dom.Name)
*/
public boolean visit(Name name) throws BioPEPAException {
name.getBinding().getVariableDeclaration().getRightHandSide().accept(this);
return true;
}
}
private static int[] merge(int[] one, int[] two) {
int[] iArray = new int[one.length];
for (int i = 0; i < one.length; i++)
iArray[i] = one[i] + two[i];
return iArray;
}
private static boolean match(String fName, int[] count) {
if (AST.Literals.MASS_ACTION.toString().equals(fName))
return count[0] + count[4] > 0 && count[2] == 0;
if (AST.Literals.MICHAELIS_MENTEN.toString().equals(fName))
return count[0] == 1 && count[1] == 1 && count[2] == 0 && count[3] == 0 && count[4] == 1;
if (AST.Literals.COMPETITIVE_INHIBITION.toString().equals(fName))
return count[0] == 1 && count[1] == 1 && count[2] == 1 && count[3] == 0 && count[4] == 1;
return false;
}
}