/* * SpatialKernel.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.evomodel.epidemiology.casetocase; import dr.inference.model.AbstractModel; import dr.inference.model.Model; import dr.inference.model.Parameter; import dr.inference.model.Variable; import dr.math.IntegrableUnivariateFunction; import dr.math.RiemannApproximation; import dr.xml.*; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; /** * An abstract spatial kernel function class (and some examples) with a list of parameters. * * @author Matthew Hall */ public abstract class SpatialKernel extends AbstractModel implements IntegrableUnivariateFunction { private ArrayList<Parameter> params; public SpatialKernel(String name){ super(name); } public ArrayList<Parameter> getParams(){ return params; } public Parameter getParam(int index){ return params.get(index); } public void setParam(int index, Parameter value){ params.set(index, value); } public static final String SPATIAL_KERNEL_FUNCTION = "spatialKernelFunction"; public static final String PARAMETERS = "parameters"; public static final String KERNEL_TYPE = "type"; public static final String INTEGRATOR_STEPS = "integratorSteps"; public static final String ALPHA = "kernel.alpha"; public static final String R_0 = "kernel.r0"; public enum Type{ EXPONENTIAL ("exponential", Exponential.class), POWER_LAW ("powerLaw", PowerLaw.class), GAUSSIAN ("gaussian", Gaussian.class), LOGISTIC ("logistic", Logistic.class); private final String xmlName; private final Class kernelClass; String getXmlName(){ return xmlName; } SpatialKernel makeKernelFunction(ArrayList<Parameter> parameters) throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException { // Constructor[] construct = kernelClass.getConstructors(); Constructor constructor = kernelClass.getConstructor(SpatialKernel.class, String.class, ArrayList.class); return (SpatialKernel)constructor.newInstance(null, xmlName, parameters); } Type(String xmlName, Class kernelClass){ this.xmlName = xmlName; this.kernelClass = kernelClass; } } public static double EuclideanDistance(double[] point1, double[] point2){ return Math.sqrt(Math.pow(point1[0]-point2[0],2) + Math.pow(point1[1]-point2[1], 2)); } public double getUpperBound(){ return Double.POSITIVE_INFINITY; } public double getLowerBound(){ return 0; } protected void handleModelChangedEvent(Model model, Object object, int index){ fireModelChanged(); } protected void storeState(){ } protected void restoreState(){ } public double value(double distance){ return evaluate(distance); } protected void handleVariableChangedEvent(Variable variable, int index, Parameter.ChangeType type){ fireModelChanged(); } protected void acceptState(){ // nothing to do? } // no need to do this unless there is one... public void configureIntegrator(int sampleSize){} public abstract SpatialKernel newInstance(ArrayList<Parameter> params) throws InstantiationException; public class Exponential extends SpatialKernel { private Parameter alpha; public SpatialKernel newInstance(ArrayList<Parameter> params) throws InstantiationException { return new Exponential(Type.EXPONENTIAL.getXmlName(), params); } public Exponential(String name, ArrayList<Parameter> params) throws InstantiationException { super(name); if(params.size()!=1){ throw new InstantiationException("Wrong number of parameters for this spatial kernal function"); } if(!params.get(0).getId().equals(ALPHA)){ throw new InstantiationException("No parameter named alpha"); } this.alpha = params.get(0); addVariable(alpha); } public Exponential() throws InstantiationException { this(Type.EXPONENTIAL.getXmlName(), null); } public double evaluate(double argument) { return evaluate(argument, alpha.getParameterValue(0)); } public double evaluate(double argument, double alpha){ return Math.exp(-argument * alpha); } public double evaluateIntegral(double a, double b){ double aValue = alpha.getParameterValue(0); return -(1/aValue)*Math.exp(-aValue*b) + (1/aValue)*Math.exp(-aValue*a); } } public class PowerLaw extends SpatialKernel { private Parameter alpha; public SpatialKernel newInstance(ArrayList<Parameter> params) throws InstantiationException{ return new PowerLaw(Type.POWER_LAW.getXmlName(), params); } public PowerLaw(String name, ArrayList<Parameter> params) throws InstantiationException { super(name); if(params.size()!=1){ throw new InstantiationException("Wrong number of parameters for this spatial kernal function"); } if(!params.get(0).getId().equals(ALPHA)){ throw new InstantiationException("No parameter named alpha"); } this.alpha = params.get(0); addVariable(alpha); } public PowerLaw() throws InstantiationException { this(Type.POWER_LAW.getXmlName(), null); } public double evaluate(double argument) { return evaluate(argument, alpha.getParameterValue(0)); } public double evaluate(double argument, double alpha){ return Math.pow(argument, -alpha); } public double evaluateIntegral(double a, double b){ double aValue = params.get(0).getParameterValue(0); return -aValue*Math.pow(b, -aValue-1) + -aValue*Math.pow(a, -aValue-1); } } public class Gaussian extends SpatialKernel { private Parameter alpha; RiemannApproximation integrator; public SpatialKernel newInstance(ArrayList<Parameter> params) throws InstantiationException{ return new Gaussian(Type.GAUSSIAN.getXmlName(), params); } public Gaussian(String name, ArrayList<Parameter> params) throws InstantiationException { this(name, params, 25); } public Gaussian() throws InstantiationException { this(Type.GAUSSIAN.getXmlName(), null); } public void configureIntegrator(int sampleSize){ integrator = new RiemannApproximation(sampleSize); } public Gaussian(String name, ArrayList<Parameter> params, int steps) throws InstantiationException { super(name); if(params.size()!=1){ throw new InstantiationException("Wrong number of parameters for this spatial kernal function"); } if(!params.get(0).getId().equals(ALPHA)){ throw new InstantiationException("No parameter named alpha"); } this.alpha = params.get(0); addVariable(alpha); integrator = new RiemannApproximation(steps); } public double evaluate(double argument) { return evaluate(argument, alpha.getParameterValue(0)); } public double evaluate(double argument, double alpha){ return Math.exp(-Math.pow(argument, 2) * alpha); } public double evaluateIntegral(double a, double b){ return integrator.integrate(this, a, b); } } public class Logistic extends SpatialKernel { private Parameter alpha; private Parameter r_0; RiemannApproximation integrator; public SpatialKernel newInstance(ArrayList<Parameter> params) throws InstantiationException{ return new Logistic(Type.LOGISTIC.getXmlName(), params); } public Logistic() throws InstantiationException { this(Type.GAUSSIAN.getXmlName(), null); } public Logistic(String name, ArrayList<Parameter> params) throws InstantiationException { this(name, params, 25); } public void configureIntegrator(int sampleSize){ integrator = new RiemannApproximation(sampleSize); } public Logistic(String name, ArrayList<Parameter> params, int steps) throws InstantiationException { super(name); if(params.size()!=2){ throw new InstantiationException("Wrong number of parameters for this spatial kernal function"); } boolean hasAlpha = false; boolean hasR0 = false; for(Parameter parameter : params){ if(parameter.getId().equals(ALPHA)){ hasAlpha = true; this.alpha = parameter; } if( parameter.getId().equals(R_0)){ hasR0 = true; this.r_0 = parameter; } } if(!hasAlpha || !hasR0){ throw new InstantiationException("Kernel function does not have the required parameters"); } addVariable(alpha); addVariable(r_0); integrator = new RiemannApproximation(steps); } public double evaluate(double argument) { return evaluate(argument, alpha.getParameterValue(0), r_0.getParameterValue(0)); } public double evaluate(double argument, double alpha, double r_0){ return 1/(1+Math.pow((argument/r_0), alpha)); } public double evaluateIntegral(double a, double b){ return integrator.integrate(this, a, b); } } public static XMLObjectParser PARSER = new AbstractXMLObjectParser() { public String getParserName(){ return SPATIAL_KERNEL_FUNCTION; } public Object parseXMLObject(XMLObject xo) throws XMLParseException { try{ String type = (String)xo.getAttribute("type"); XMLObject parameters = xo.getChild(PARAMETERS); ArrayList<Parameter> params = new ArrayList<Parameter>(); for(int i=0; i<parameters.getChildCount(); i++){ Parameter cxo = (Parameter)parameters.getChild(i); params.add(cxo); } SpatialKernel kernelFunction = null; for(Type value : Type.values()){ if(value.getXmlName().equals(type)){ kernelFunction = value.makeKernelFunction(params); } } if(kernelFunction==null){ throw new XMLParseException("Unknown spatial kernel type"); } if(xo.hasAttribute(INTEGRATOR_STEPS)){ kernelFunction.configureIntegrator(Integer.parseInt((String)xo.getAttribute(INTEGRATOR_STEPS))); } return kernelFunction; } catch(Exception e){ throw new XMLParseException("Failed to initiate spatial kernel ("+e.toString()+")"); } } @Override public XMLSyntaxRule[] getSyntaxRules() { return rules; } public final XMLSyntaxRule[] rules = new XMLSyntaxRule[]{ new ElementRule(PARAMETERS, new XMLSyntaxRule[]{ new ElementRule(Parameter.class, 1, Integer.MAX_VALUE) }, 1, 1), AttributeRule.newStringRule(KERNEL_TYPE), AttributeRule.newIntegerRule(INTEGRATOR_STEPS, true) }; public String getParserDescription() { return "This element represents a spatial kernel function with a single parameter."; } public Class getReturnType() { return SpatialKernel.class; } }; }