/*
* LogisticGrowthN0.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
*/
/*
* LogisticGrowthN0.java
*
* Daniel Wilson 4th October 2011
*
*/
package dr.evomodel.epidemiology;
import dr.evolution.coalescent.*;
/**
* This class models logistic growth.
*
* @author Daniel Wilson
* @author Alexei Drummond
* @author Andrew Rambaut
* @author Matthew Hall
* @version $Id: LogisticGrowth.java,v 1.15 2008/03/21 20:25:56 rambaut Exp $
*/
public class LogisticGrowthN0 extends ExponentialGrowth {
/**
* Construct demographic model with default settings
*/
public LogisticGrowthN0(Type units) {
super(units);
}
public void setT50(double value) {
t50 = value;
}
public double getT50() {
return t50;
}
// Implementation of abstract methods
/**
* Gets the value of the demographic function N(t) at time t.
*
* @param t the time
* @return the value of the demographic function N(t) at time t.
*/
public double getDemographic(double t) {
double N0 = getN0();
double r = getGrowthRate();
double T50 = getT50();
return N0 * (1 + Math.exp(-r * T50)) / (1 + Math.exp(-r * (T50-t)));
}
public double getLogDemographic(double t) {
return Math.log(getDemographic(t));
}
/**
* Returns value of demographic intensity function at time t
* (= integral 1/N(x) dx from 0 to t).
*/
public double getIntensity(double t) {
double N0 = getN0();
double r = getGrowthRate();
double T50 = getT50();
double exp_rT50 = Math.exp(-r*T50);
return (t + exp_rT50 * (Math.exp(r * t) - 1)/r) / (N0 * (1 + exp_rT50));
}
/**
* Returns the inverse function of getIntensity
*
* If exp(-qt) = a(t-k) then t = k + (1/q) * W(q*exp(-q*k)/a) where W(x) is the Lambert W function.
*
* for our purposes:
*
* q = -r
* a = (q/exp(q*T50))
* k = N0*(1+exp(q*T50))*x - (1/q)exp(q*T50)
*
* For large x, W0(x) is approximately equal to ln(x) - ln(ln(x)); if q*exp(-q*k)/a rounds to infinity, we log it
* and use this instead
*/
public double getInverseIntensity(double x) {
double q = -getGrowthRate();
double T50 = getT50();
double N0 = getN0();
double a = (q/Math.exp(q*T50));
double k = N0*(1+Math.exp(q*T50))*x - (1/q)*Math.exp(q*T50);
double lambertInput = q*Math.exp(-q*k)/a;
double lambertResult;
if(lambertInput==Double.POSITIVE_INFINITY){
//use the asymptote; note q/a = exp(q*T50)
double logInput = q*T50-q*k;
lambertResult = logInput - Math.log(logInput);
} else {
lambertResult = LambertW.branch0(lambertInput);
}
return k + (1/q)*lambertResult;
}
public double getIntegral(double start, double finish) {
return getIntensity(finish) - getIntensity(start);
}
public int getNumArguments() {
return 3;
}
public String getArgumentName(int n) {
switch (n) {
case 0:
return "N0";
case 1:
return "r";
case 2:
return "t50";
}
throw new IllegalArgumentException("Argument " + n + " does not exist");
}
public double getArgument(int n) {
switch (n) {
case 0:
return getN0();
case 1:
return getGrowthRate();
case 2:
return getT50();
}
throw new IllegalArgumentException("Argument " + n + " does not exist");
}
public void setArgument(int n, double value) {
switch (n) {
case 0:
setN0(value);
break;
case 1:
setGrowthRate(value);
break;
case 2:
setT50(value);
break;
default:
throw new IllegalArgumentException("Argument " + n + " does not exist");
}
}
public double getLowerBound(int n) {
return 0.0;
}
public double getUpperBound(int n) {
return Double.POSITIVE_INFINITY;
}
public DemographicFunction getCopy() {
LogisticGrowthN0 df = new LogisticGrowthN0(getUnits());
df.setN0(getN0());
df.setGrowthRate(getGrowthRate());
df.setT50(getT50());
return df;
}
//
// private stuff
//
private double t50;
}