package uk.ac.ed.inf.biopepa.core.compiler;
import java.util.Map;
/*
* This class is intended to be a simple evaluator for
* compiled expressions which will work for evaluating
* dynamic components.
* NOTE: it is incomplete in the sense that if the compiled
* expression is a kinetic law such as fMA(r) then this will
* fail as there is no context (ie. the reaction) in which to
* evaluate this rate function. However this class can be used
* to retrospectively add dynamic variables to results. It could
* also be extended to allow for rate based expressions.
* (see CompiledExpressionRateEvaluator).
*/
public class CompiledExpressionEvaluator extends CompiledExpressionVisitor {
Map<String, Number> componentCounts;
double currentTime;
DynamicExpressionModelContext modelContext;
public CompiledExpressionEvaluator (DynamicExpressionModelContext model,
Map<String, Number> componentCounts,
double time){
this.componentCounts = componentCounts;
this.modelContext = model;
this.currentTime = time;
}
protected double result;
public double getResult(){
return this.result;
}
@Override
public boolean visit(CompiledDynamicComponent component) {
String name = component.getName();
if (modelContext.containsComponent(name)){
Number count = componentCounts.get(name);
this.result = count.doubleValue();
return false;
} else if (modelContext.containsVariable(name)){
/*
* We should be careful to avoid an infinite
* loop here, if the definition is (mutually) recursive
* then basically we're in trouble.
*/
CompiledExpression varExp = modelContext.getDynamicExpression(name);
varExp.accept(this);
return false;
} else {
throw new IllegalStateException ();
}
}
@Override
public boolean visit(CompiledFunction function) {
if (function.getFunction().isRateLaw()) {
/*
* The rate kinetic functions cannot be evaluated within
* this context because there is no surrounding reaction
* with which to evaulate the function. For example fMA
* requires the reactants of the reaction. If evaluating
* a reaction rate we should use a subclass of this one
* which takes in the reaction in the constructor and
* can override this method with one which evaluates the
* the rate laws properly.
*/
throw new IllegalStateException ();
} else {
// If it is not a rate law then we can attempt to
// interpret it as a normal maths function.
if (function.getFunction().args() == 1) {
function.getArguments().get(0).accept(this);
double argument = result ;
switch (function.getFunction()) {
case LOG:
result = Math.log(argument);
break;
case EXP:
result = Math.exp(argument);
break;
case H:
result = (argument > 0) ? 1 : 0;
break;
case FLOOR:
result = Math.floor(argument);
break;
case CEILING:
result = Math.ceil(argument);
break;
case TANH:
result = Math.tanh(argument);
break;
default:
throw new IllegalStateException();
}
} else {
throw new IllegalStateException ();
}
return false;
}
}
@Override
public boolean visit(CompiledNumber number) {
result = number.getNumber().doubleValue();
return false;
}
@Override
public boolean visit(CompiledOperatorNode operator) {
operator.getLeft().accept(this);
double left = result ;
operator.getRight().accept(this);
double right = result ;
switch (operator.getOperator()) {
case PLUS:
result = left + right;
break;
case MINUS:
result = left - right;
break;
case DIVIDE:
result = left / right;
break;
case MULTIPLY:
result = left * right;
break;
case POWER:
result = Math.pow(left, right);
break;
default:
throw new IllegalStateException ();
}
return false;
}
@Override
public boolean visit(CompiledSystemVariable variable) {
switch (variable.getVariable()) {
case TIME:
result = currentTime ;
break;
default:
throw new IllegalStateException();
}
return false;
}
}