/******************************************************************************* * 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.compiler; import java.util.*; import uk.ac.ed.inf.biopepa.core.dom.AST; import uk.ac.ed.inf.biopepa.core.dom.FunctionCall; public class CompiledFunction extends CompiledExpression { /** * Adding a new function * * 1. Add to dom.AST.Literals. 2. Add to the enum below. 3. Add to the * switch call in getFunctionEvaluator (below) 4. Add to the appropriate * evaluate method in compiler.FunctionEvaluators (static evaluation) 5. Add * to classes that extend CompiledExpressionVisitor (dynamic evaluation) * (following known at time of comment) 1. sba.ISBJava.RatesVisitor 2. * sba.SBAModel.SystemEquationVisitor.FunctionChecker (needs changing if a * function isn't differentiable) 3. sba.export.SBMLExport.SBMLRateGenerator * 4. sba.export.SBMLExport.SBMLParseable 5. * sba.export.SBMLExport.ParameterVisitor (shouldn't need changing) 6. * analysis.ReactantRateParticipationCheck.ReactantParticipantVisitor * (shouldn't need changing) * * @author ajduguid * */ public enum Function { LOG(AST.Literals.LOGARITHM.getToken(), 1, false), EXP(AST.Literals.EXP.getToken(), 1, false), H( AST.Literals.HEAVISIDE.getToken(), 1, false), FLOOR(AST.Literals.FLOOR.getToken(), 1, false), CEILING( AST.Literals.CEILING.getToken(), 1, false), fMA(AST.Literals.MASS_ACTION.getToken(), 1, true), fMM( AST.Literals.MICHAELIS_MENTEN.getToken(), 2, true), TANH(AST.Literals.TANH.getToken(), 1, false); int arg; String name; boolean rate; Function(String name, int arg, boolean rate) { this.arg = arg; this.name = name; this.rate = rate; } public String getID() { return name; } public boolean isRateLaw() { return rate; } public int args() { return arg; } } public static Function getFunction(String function) { for (Function f : Function.values()) if (f.name.equals(function)) return f; return null; } Function function = null; List<CompiledExpression> arguments = new ArrayList<CompiledExpression>(3); public Function getFunction() { return function; } void setFunction(Function function) { if (arguments.size() > function.arg) throw new IllegalStateException(); this.function = function; } void setArgument(int index, CompiledExpression argument) { if (function != null && function.arg < index) throw new IllegalArgumentException(); for (int i = arguments.size(); i <= index; i++) arguments.add(null); arguments.set(index, argument); } public List<CompiledExpression> getArguments() { return Collections.unmodifiableList(arguments); } public String toString() { StringBuilder sb = new StringBuilder(); sb.append(function.name).append("("); for (CompiledExpression en : arguments) sb.append(en.toString()).append(", "); sb.replace(sb.length() - 2, sb.length(), ")"); return sb.toString(); } public static Function checkFunction(ModelCompiler compiler, FunctionCall call) throws CompilerException { Function f = getFunction(call.getName().getIdentifier()); if (f == null) { compiler.problemRequestor.accept(ProblemKind.UNSUPPORTED_FUNCTION_USED, call); throw new CompilerException(); } if (call.arguments().size() != f.arg) { compiler.problemRequestor.accept(ProblemKind.INVALID_NUMBER_OF_ARGUMENTS, call); throw new CompilerException(); } return f; } public static IPredefinedFunctionEvaluator getFunctionEvaluator(ModelCompiler compiler, FunctionCall call) throws CompilerException { Function f = checkFunction(compiler, call); switch (f) { case LOG: case EXP: case H: case FLOOR: case CEILING: case TANH: return new FunctionEvaluators.GenericOneArgumentFunction(compiler, call); default: throw new IllegalStateException(); } } public boolean accept(CompiledExpressionVisitor visitor) { return visitor.visit(this); } public boolean isDynamic () { // Technically we probably should be a bit more // savvy than this, there might be some function // that doesn't depend on dynamic arguments in which case // we would need to look at the function and possibly the // arguments, for example fMA can always be considered // dynamic regardless of what the arguments are (they are likely // to be static). But if we had a function such as cos then // we would only need to look at the arguments return true; /* for (CompiledExpression ce : arguments){ if (ce != null && ce.isDynamic()){ return true; } } return false; */ } public CompiledFunction clone() { CompiledFunction cf = new CompiledFunction(); cf.function = function; for (CompiledExpression ce : arguments) { if (ce != null) cf.arguments.add(ce.clone()); else cf.arguments.add(null); } if (expandedForm != null) cf.expandedForm = expandedForm.clone(); return cf; } }