uk.ac.ed.bio.SynthSys.SBMLDataTools.SBMLTimeCourseDataHelperTest.java Source code

Java tutorial

Introduction

Here is the source code for uk.ac.ed.bio.SynthSys.SBMLDataTools.SBMLTimeCourseDataHelperTest.java

Source

/**
 * For license details see associated LICENSE.txt file.
 */

package uk.ac.ed.bio.SynthSys.SBMLDataTools;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import org.apache.commons.math3.analysis.interpolation.SplineInterpolator;
import org.junit.Test;
import org.sbml.jsbml.ASTNode;
import org.sbml.jsbml.AssignmentRule;
import org.sbml.jsbml.Model;
import org.sbml.jsbml.Parameter;
import org.sbml.jsbml.Rule;
import org.sbml.jsbml.SBMLDocument;

import uk.ac.ed.bio.SynthSys.SBMLDataTools.SBMLTimeCourseDataHelper;

/**
 * Test class for SBMLTimeCourseDataHelper
 * 
 * @author Ally Hume
 */
public class SBMLTimeCourseDataHelperTest {

    // Times and value data
    private double _times[];
    private double _values[];

    /**
     * Tests that the correct exception is produced when passing a NULL model.
     */
    @Test
    public void addParameterArrayVersionNullModel() {

        boolean caughtException = false;
        setSinData();

        try {
            SBMLTimeCourseDataHelper.addParameter(null, "myParam", _times, _values,
                    new PolynomialInterpolator(new SplineInterpolator()));
        } catch (IllegalArgumentException iae) {
            caughtException = true;
            assertEquals("sbmlModel parameter cannot be null", iae.getMessage());
        }

        if (!caughtException)
            fail("Expected IllegalArgumentException");

    }

    /**
     * Tests that the correct exception is produced when passing a NULL parameter name.
     */
    @Test
    public void addParameterArrayVersionNullParameterName() {

        boolean caughtException = false;
        setSinData();
        SBMLDocument doc = new SBMLDocument(3, 1);
        Model model = doc.createModel("test_model");

        try {
            SBMLTimeCourseDataHelper.addParameter(model, null, _times, _values,
                    new PolynomialInterpolator(new SplineInterpolator()));
        } catch (IllegalArgumentException iae) {
            caughtException = true;
            assertEquals("parameterName parameter cannot be null", iae.getMessage());
        }

        if (!caughtException)
            fail("Expected IllegalArgumentException");
    }

    /**
     * Tests that the correct exception is produced when passing NULL times.
     */
    @Test
    public void addParameterArrayVersionNullTimes() {

        boolean caughtException = false;
        setSinData();
        SBMLDocument doc = new SBMLDocument(3, 1);
        Model model = doc.createModel("test_model");

        try {
            SBMLTimeCourseDataHelper.addParameter(model, "myParam", null, _values,
                    new PolynomialInterpolator(new SplineInterpolator()));
        } catch (IllegalArgumentException iae) {
            caughtException = true;
            assertEquals("times parameter cannot be null", iae.getMessage());
        }

        if (!caughtException)
            fail("Expected IllegalArgumentException");
    }

    /**
     * Tests that the correct exception is produced when passing NULL values.
     */
    @Test
    public void addParameterArrayVersionNullValues() {

        boolean caughtException = false;
        setSinData();
        SBMLDocument doc = new SBMLDocument(3, 1);
        Model model = doc.createModel("test_model");

        try {
            SBMLTimeCourseDataHelper.addParameter(model, "myParam", _times, null,
                    new PolynomialInterpolator(new SplineInterpolator()));
        } catch (IllegalArgumentException iae) {
            caughtException = true;
            assertEquals("values parameter cannot be null", iae.getMessage());
        }

        if (!caughtException)
            fail("Expected IllegalArgumentException");
    }

    /**
     * Tests that the correct exception is produced when passing times and values arrays of 
     * different lengths.
     */
    @Test
    public void addParameterArrayVersionDifferentLengthArrays() {
        boolean caughtException = false;
        setSinData();
        SBMLDocument doc = new SBMLDocument(3, 1);
        Model model = doc.createModel("test_model");

        // Make longer values array
        double[] values = new double[_values.length + 1];
        System.arraycopy(_values, 0, values, 0, _values.length);
        values[values.length - 1] = 0.0;

        try {
            SBMLTimeCourseDataHelper.addParameter(model, "myParam", _times, values,
                    new PolynomialInterpolator(new SplineInterpolator()));
        } catch (IllegalArgumentException iae) {
            caughtException = true;
            assertEquals("Number of data points in values parameter differs from times parameter",
                    iae.getMessage());
        }

        if (!caughtException)
            fail("Expected IllegalArgumentException");
    }

    /**
     * Tests that the correct exception is produced when passing too few data points.
     */
    @Test
    public void tooFewDataPoints() {
        boolean caughtException = false;

        _times = new double[] { -3.0, -2.0 };
        _values = new double[_times.length];

        for (int i = 0; i < _times.length; ++i) {
            _values[i] = Math.sin(_times[i]);
        }

        SBMLDocument doc = new SBMLDocument(3, 1);
        Model model = doc.createModel("test_model");

        try {
            SBMLTimeCourseDataHelper.addParameter(model, "myParam", _times, _values,
                    new PolynomialInterpolator(new SplineInterpolator()));
        } catch (IllegalArgumentException iae) {
            caughtException = true;
            assertEquals("Data in the times and values parameters must contain at least 3 data points",
                    iae.getMessage());
        }
        if (!caughtException)
            fail("Expected IllegalArgumentException");
    }

    /**
     * Tests that the correct exception is produced when passing times data that is not in 
     * ascending order.
     */
    @Test
    public void timesDataNotAscending() {
        boolean caughtException = false;

        _times = new double[] { -3.0, -2.0, -1.0, 0, -1.0 };
        _values = new double[_times.length];

        for (int i = 0; i < _times.length; ++i) {
            _values[i] = Math.sin(_times[i]);
        }

        SBMLDocument doc = new SBMLDocument(3, 1);
        Model model = doc.createModel("test_model");

        try {
            SBMLTimeCourseDataHelper.addParameter(model, "myParam", _times, _values,
                    new PolynomialInterpolator(new SplineInterpolator()));
        } catch (IllegalArgumentException iae) {
            caughtException = true;
            assertEquals("Data in times parameter must be in ascending order", iae.getMessage());
        }
        if (!caughtException)
            fail("Expected IllegalArgumentException");
    }

    /**
     * Tests that correct execution produced fitted data that closely matches the function that
     * was sampled to produce the external data.  Here the sine function is used.
     */
    @Test
    public void normalExcecution() {
        setSinData();
        SBMLDocument doc = new SBMLDocument(3, 1);
        Model model = doc.createModel("test_model");

        SBMLTimeCourseDataHelper.addParameter(model, "myParam", _times, _values,
                new PolynomialInterpolator(new SplineInterpolator()));

        // Model must now have a parameter called myParam with specific properties
        assertEquals("Model must have one parameter", 1, model.getParameterCount());
        Parameter param = model.getParameter(0);
        assertEquals("Parameter id: ", "myParam", param.getId());
        assertEquals("Parameter name: ", "myParam", param.getName());
        assertEquals("Parameter isConstant: ", false, param.isConstant());

        // Model must have an assignment rule that is associated with the parameter
        assertEquals("Model must have one rule", 1, model.getRuleCount());
        Rule rule = model.getRule(0);
        assertTrue("Rule must be assignment rule", rule instanceof AssignmentRule);
        AssignmentRule assignmentRule = (AssignmentRule) rule;
        assertEquals("Variable of assignment rule", "myParam", assignmentRule.getVariable());

        // Now we can compare data with the fitted data - for the sine function the spline
        // should fit quite well
        for (double t = _times[0]; t < _times[_times.length - 1]; t += 0.005) {
            double sin = Math.sin(t);
            double fitted = evaluateMathML(assignmentRule.getMath(), t);

            assertTrue("Fitted must be close to actual, t=" + t + " sine=" + sin + " fitted=" + fitted + " diff="
                    + Math.abs(sin - fitted), Math.abs(sin - fitted) < 0.01);
        }
    }

    /**
     * Helper method to evaluate a MathML equation at a given time.
     * 
     * @param node   MathML equation
     * @param t      time, the only parameter to the equation
     * 
     * @return the value of the equation at the specified time
     */
    private double evaluateMathML(ASTNode node, double t) {

        double result;

        switch (node.getType()) {

        case FUNCTION_PIECEWISE:
            for (int i = 0; i < node.getNumChildren() / 2; ++i) {

                if (evaluateBooleanMathML(node.getChild(i * 2 + 1), t)) {
                    return evaluateMathML(node.getChild(i * 2), t);
                }
            }
            // error no match found
            throw new RuntimeException("No match found in the ranges of the piecewise");

        case POWER:
            result = Math.pow(evaluateMathML(node.getLeftChild(), t), evaluateMathML(node.getRightChild(), t));
            return result;

        case TIMES:
            result = 1;
            for (int i = 0; i < node.getNumChildren(); ++i) {
                result *= evaluateMathML(node.getChild(i), t);
            }
            return result;

        case PLUS:
            result = 0;
            for (int i = 0; i < node.getNumChildren(); ++i) {
                result += evaluateMathML(node.getChild(i), t);
            }
            return result;

        case MINUS:
            result = evaluateMathML(node.getChild(0), t);
            for (int i = 1; i < node.getNumChildren(); ++i) {
                result -= evaluateMathML(node.getChild(i), t);
            }
            return result;

        case NAME_TIME:
            result = t;
            return result;

        case REAL:
        case INTEGER:
            result = Double.parseDouble(node.toString());
            return result;

        default:
            // Should probably throw an exception
            throw new RuntimeException("No rule for type: " + node.getType());
        }
    }

    /**
     * Evaluates a boolean Math ML expression
     * 
     * @param node  MathML node that is a boolean expression
     * @param t     time, the only parameter used in the expression
     * 
     * @return the boolean result of evaluating the expression
     */
    private boolean evaluateBooleanMathML(ASTNode node, double t) {

        boolean result = false;

        switch (node.getType()) {

        case LOGICAL_AND:
            result = evaluateBooleanMathML(node.getLeftChild(), t)
                    && evaluateBooleanMathML(node.getRightChild(), t);
            return result;

        case RELATIONAL_GEQ:
            result = evaluateMathML(node.getLeftChild(), t) >= evaluateMathML(node.getRightChild(), t);
            return result;

        case RELATIONAL_LEQ:
            result = evaluateMathML(node.getLeftChild(), t) <= evaluateMathML(node.getRightChild(), t);
            return result;

        default:
            throw new RuntimeException("No code to handle boolean expression of type " + node.getType());
        }
    }

    /**
     * Sets the _times and _values class variables to arrays that contains the time points and
     * values for a sine function.
     */
    private void setSinData() {
        _times = new double[] { -3.0, -2.0, -1.0, 0, 1.0, 2.0, 3.0 };
        _values = new double[_times.length];

        for (int i = 0; i < _times.length; ++i) {
            _values[i] = Math.sin(_times[i]);
        }
    }
}