/*
* PiecewiseLinearPopulation.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 java.util.Collections;
/**
* @author Alexei Drummond
* @author Andrew Rambaut
*/
public class PiecewiseLinearPopulation extends PiecewiseConstantPopulation {
/**
* Construct demographic model with default settings
*
* @param units of time
*/
public PiecewiseLinearPopulation(Type units) {
super(units);
}
/**
* Construct demographic model with default settings
*/
public PiecewiseLinearPopulation(double[] intervals, double[] thetas, Type units) {
super(intervals, thetas, units);
}
// **************************************************************
// Implementation of abstract methods
// **************************************************************
/**
* @return the value of the demographic function for the given epoch and time relative to start of epoch.
*/
protected final double getDemographic(int epoch, double t) {
// if in last epoch then the population is flat.
if (epoch == (thetas.length - 1)) {
return getEpochDemographic(epoch);
}
final double popSize1 = getEpochDemographic(epoch);
final double popSize2 = getEpochDemographic(epoch + 1);
final double width = getEpochDuration(epoch);
assert 0 <= t && t <= width;
return popSize1 + (t / width) * (popSize2 - popSize1);
}
public DemographicFunction getCopy() {
PiecewiseLinearPopulation df = new PiecewiseLinearPopulation(new double[intervals.length], new double[thetas.length], getUnits());
System.arraycopy(intervals, 0, df.intervals, 0, intervals.length);
System.arraycopy(thetas, 0, df.thetas, 0, thetas.length);
return df;
}
/**
* @return the value of the intensity function for the given epoch.
*/
protected final double getIntensity(int epoch) {
final double N1 = getEpochDemographic(epoch);
final double N2 = getEpochDemographic(epoch + 1);
final double w = getEpochDuration(epoch);
if (N1 != N2) {
return w * Math.log(N2 / N1) / (N2 - N1);
} else {
return w / N1;
}
}
/**
* @return the value of the intensity function for the given epoch and time relative to start of epoch.
*/
protected final double getIntensity(int epoch, double relativeTime) {
assert relativeTime <= getEpochDuration(epoch);
final double N1 = getEpochDemographic(epoch);
final double N2 = getEpochDemographic(epoch + 1);
final double w = getEpochDuration(epoch);
if (N1 != N2) {
return w * Math.log(N1 * w / (N2 * relativeTime + N1 * (w - relativeTime))) / (N1 - N2);
} else {
return relativeTime / N1;
}
}
/**
* @param targetI the intensity
* @return the time corresponding to the given target intensity
*/
public final double getInverseIntensity(double targetI) {
if (cif != null) {
int epoch = Collections.binarySearch(cif, targetI);
if (epoch < 0) {
epoch = -epoch - 1;
if (epoch > 0) {
return endTime.get(epoch - 1) +
getInverseIntensity(epoch, targetI - cif.get(epoch - 1));
} else {
assert epoch == 0;
return getInverseIntensity(0, targetI);
}
} else {
return endTime.get(epoch);
}
} else {
int epoch = 0;
double cI = 0;
double eI = getIntensity(epoch);
double time = 0.0;
while (cI + eI < targetI) {
cI += eI;
time += getEpochDuration(epoch);
epoch += 1;
eI = getIntensity(epoch);
}
time += getInverseIntensity(epoch, targetI - cI);
return time;
}
}
/**
* @param epoch the epoch
* @param I intensity corresponding to relative time within this epoch
* @return the relative time within the given epoch to accumulate the given residual intensity
*/
private double getInverseIntensity(int epoch, double I) {
final double N1 = getEpochDemographic(epoch);
final double N2 = getEpochDemographic(epoch + 1);
final double w = getEpochDuration(epoch);
final double dn = N2 - N1;
double time;
if (dn != 0.0) {
time = N1 * w * (Math.exp(I * dn / w) - 1.0) / dn;
} else {
time = I * N1;
}
return time;
}
}