/*
* MultiEpochExponential.java
*
* Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard
*
* This file is part of BEAST.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership and licensing.
*
* BEAST is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* BEAST is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with BEAST; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
package dr.evolution.coalescent;
import dr.math.matrixAlgebra.Vector;
/**
* This class models a multi-phase exponential growth
*
* @author Marc A. Suchard
*/
public class MultiEpochExponential extends ConstantPopulation {
/**
* Construct demographic model with default settings
*/
public MultiEpochExponential(Type units, int numEpoch) {
super(units);
transitionTime = new double[numEpoch - 1];
rate = new double[numEpoch];
}
public void setTransitionTime(int index, double transitionTime) {
this.transitionTime[index] = transitionTime;
}
public void setGrowthRate(int index, double rate) {
this.rate[index] = rate;
}
public double getDemographic(double t) {
double logDemographic = 0.0;
double lastTransitionTime = 0.0;
int currentEpoch = 0;
// Account for all epochs before t
while (currentEpoch < transitionTime.length && t > transitionTime[currentEpoch]) {
logDemographic += -rate[currentEpoch] * (transitionTime[currentEpoch] - lastTransitionTime);
lastTransitionTime = transitionTime[currentEpoch];
++currentEpoch;
}
// Account for epoch that holds t
logDemographic += -rate[currentEpoch] * (t - lastTransitionTime);
return getN0() * Math.exp(logDemographic);
}
private double integrateConstant(double start, double finish, double logDemographic) {
double integral = (finish - start) / Math.exp(logDemographic);
return integral;
}
private double integrateExponential(double start, double finish, double logDemographic, double rate) {
double integral = (Math.exp(finish * rate) - Math.exp(start * rate)) / Math.exp(logDemographic) / rate;
// System.err.println("\tint: " + integral + " " + start + " " + finish + " " + logDemographic + " " + rate);
// System.err.println("\t\t" + Math.exp(finish * rate) + " - " + Math.exp(start * rate));
// System.err.println("\t\t" + Math.exp(finish * rate - logDemographic - Math.log(rate)) + " - " + Math.exp(start * rate - logDemographic - Math.log(rate)));
return integral;
}
public double getAnalyticIntegral(double start, double finish) {
if (start == finish) {
return 0.0;
}
double integral = 0.0;
double logDemographic = 0.0;
double lastTransitionTime = 0.0;
int currentEpoch = 0;
// Account for all epochs before start
while (currentEpoch < transitionTime.length && start > transitionTime[currentEpoch]) {
logDemographic += -rate[currentEpoch] * (transitionTime[currentEpoch] - lastTransitionTime);
lastTransitionTime = transitionTime[currentEpoch];
++currentEpoch;
}
// Account for all epochs before finish
while (currentEpoch < transitionTime.length && finish > transitionTime[currentEpoch]) {
// Add to integral
double incr = 0.0;
if (rate[currentEpoch] == 0.0) {
integral += incr = integrateConstant(start, transitionTime[currentEpoch], logDemographic);
} else {
integral += incr = integrateExponential(
start - lastTransitionTime,
transitionTime[currentEpoch] - lastTransitionTime,
logDemographic, rate[currentEpoch]);
}
// System.err.println("begin incr = " + incr + " for " + start + " -> " + transitionTime[currentEpoch] + " or " +
// (start - lastTransitionTime) + " -> " + (transitionTime[currentEpoch] - lastTransitionTime) + " @ " + rate[currentEpoch] + " & " + Math.exp(logDemographic));
// Update demographic function
logDemographic += -rate[currentEpoch] * (transitionTime[currentEpoch] - lastTransitionTime);
lastTransitionTime = transitionTime[currentEpoch];
start = lastTransitionTime;
++currentEpoch;
}
// End of integral
double incr = 0.0;
if (rate[currentEpoch] == 0.0) {
integral += incr = integrateConstant(start, finish, logDemographic);
} else {
integral += incr = integrateExponential(
start - lastTransitionTime,
finish - lastTransitionTime,
logDemographic, rate[currentEpoch]);
}
// System.err.println("final incr = " + incr + " for " + start + " -> " + finish + " or " +
// (start - lastTransitionTime) + " -> " + (finish - lastTransitionTime) + " @ " + rate[currentEpoch] + " & " + Math.exp(logDemographic));
if (Double.isNaN(integral) || Double.isInfinite(integral)) {
System.err.println(integral + " " + start + " " + finish + new Vector(rate) + "\n");
}
return integral / getN0();
}
/**
* Calculates the integral 1/N(x) dx between start and finish.
*/
public double getIntegral(double start, double finish) {
double analytic = getAnalyticIntegral(start, finish);
if (DEBUG) {
double numeric = getNumericalIntegral(start, finish);
if (Math.abs(analytic - numeric) > 1E-10) {
System.err.println(analytic);
System.err.println(numeric);
throw new RuntimeException("Error in analytic calculation");
}
}
return analytic;
}
public double getIntensity(double t) { throw new RuntimeException("Not implemented!"); }
public double getInverseIntensity(double x) {
throw new RuntimeException("Not implemented!");
}
public int getNumArguments() {
throw new RuntimeException("Not implemented!");
}
public String getArgumentName(int n) {
throw new RuntimeException("Not implemented!");
}
public double getArgument(int n) {
throw new RuntimeException("Not implemented!");
}
public void setArgument(int n, double value) {
throw new RuntimeException("Not implemented!");
}
public double getLowerBound(int n) {
throw new RuntimeException("Not implemented!");
}
public double getUpperBound(int n) {
throw new RuntimeException("Not implemented!");
}
//
// private stuff
//
final private double[] transitionTime;
final private double[] rate;
static final private boolean DEBUG = false;
}