/*
* EmpiricalPiecewiseConstant.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;
/**
*
* @version $Id: EmpiricalPiecewiseConstant.java,v 1.4 2004/10/01 23:30:16 alexei Exp $
*
* @author Alexei Drummond
* @author Andrew Rambaut
*/
public class EmpiricalPiecewiseConstant extends DemographicFunction.Abstract {
/**
* Creates a piecewise constant model with the given break points.
* @param intervals an array of intervals, Each interval represents time
* over which a different population size is assumed, the first interval starts from the
* present and proceeds back into the past.
* @param popSizes the effective population sizes of each interval. The last population size represents the
* time from the end of the last interval out to infinity.
* @param lag a lag to align the break points with the most recent sample time (must be positive)
*/
public EmpiricalPiecewiseConstant(double[] intervals, double[] popSizes, double lag, Type units) {
super(units);
if (popSizes == null || intervals == null) { throw new IllegalArgumentException(); }
if (popSizes.length != intervals.length + 1) { throw new IllegalArgumentException(); }
if (lag < 0.0) throw new IllegalArgumentException("Lag must be greater than 1.");
this.intervals = intervals;
this.popSizes = popSizes;
this.lag = lag;
}
public void setLag(double lag) {
this.lag = lag;
}
public void setPopulationSizes(double[] popSizes) {
this.popSizes = popSizes;
}
// **************************************************************
// Implementation of abstract methods
// **************************************************************
public double getDemographic(double t) {
int epoch = 0;
double t1 = t+lag;
while (t1 > getEpochDuration(epoch)) {
t1 -= getEpochDuration(epoch);
epoch += 1;
}
return getDemographic(epoch, t1);
}
public double getIntensity(double t) {
// find the first epoch that is involved
double t2 = lag;
int epoch = 0;
while (t2 > getEpochDuration(epoch)) {
t2 -= getEpochDuration(epoch);
epoch += 1;
}
// add last fraction of first epoch
double intensity = getIntensity(epoch)-getIntensity(epoch, t2);
double t1 = t-(getEpochDuration(epoch)-t2);
epoch += 1;
while (t1 > getEpochDuration(epoch)) {
t1 -= getEpochDuration(epoch);
intensity += getIntensity(epoch);
epoch += 1;
}
// add last fraction of intensity
// when t1 may be negative (for example when t is in the first epoch) the intensity need
// to be substracted
intensity += t1 >= 0 ? getIntensity(epoch, t1) : getIntensity(epoch-1, t1);
return intensity;
}
public double getInverseIntensity(double x) {
throw new RuntimeException("Not implemented!");
}
public double getUpperBound(int i) { return 1e9;}
public double getLowerBound(int i) { return Double.MIN_VALUE;}
public int getNumArguments() { return 1; }
public String getArgumentName(int i) {
return "lag";
}
public double getArgument(int i) {
return lag;
}
public void setArgument(int i, double value) {
lag = value;
}
public DemographicFunction getCopy() {
EmpiricalPiecewiseConstant df = new EmpiricalPiecewiseConstant(new double[intervals.length], new double[popSizes.length], lag, getUnits());
System.arraycopy(intervals, 0, df.intervals, 0, intervals.length);
System.arraycopy(popSizes, 0, df.popSizes, 0, popSizes.length);
return df;
}
/**
* @return the value of the demographic function for the given epoch and time relative to start of epoch.
*/
protected double getDemographic(int epoch, double t) {
return getEpochDemographic(epoch);
}
/**
* @return the value of the intensity function for the given epoch.
*/
protected double getIntensity(int epoch) {
return getEpochDuration(epoch) / getEpochDemographic(epoch);
}
/**
* @return the value of the intensity function for the given epoch and time relative to start of epoch.
*/
protected double getIntensity(int epoch, double relativeTime) {
return relativeTime / getEpochDemographic(epoch);
}
/**
* @return the duration of the specified epoch (in whatever units this demographic model is specified in).
*/
public double getEpochDuration(int epoch) {
if (epoch < intervals.length) {
return intervals[epoch];
}
return Double.POSITIVE_INFINITY;
}
/**
* @return the pop size of a given epoch.
*/
public double getEpochDemographic(int epoch) {
if (epoch >= popSizes.length) { throw new IllegalArgumentException(); }
return popSizes[epoch];
}
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append(popSizes[0]);
for (int i =1; i < popSizes.length; i++) {
buffer.append("\t").append(popSizes[i]);
}
return buffer.toString();
}
double[] intervals;
double[] popSizes;
double lag;
}