From 7936dd3855b11bc17ce337bb64bac38bed407504 Mon Sep 17 00:00:00 2001 From: walterxie Date: Mon, 20 Jul 2026 15:51:04 +1200 Subject: [PATCH 1/3] migrate RateStatistic #119 --- .../{ => spec}/evolution/RateStatistic.java | 52 +++++++++++-------- 1 file changed, 30 insertions(+), 22 deletions(-) rename beast-base/src/main/java/beast/base/{ => spec}/evolution/RateStatistic.java (85%) diff --git a/beast-base/src/main/java/beast/base/evolution/RateStatistic.java b/beast-base/src/main/java/beast/base/spec/evolution/RateStatistic.java similarity index 85% rename from beast-base/src/main/java/beast/base/evolution/RateStatistic.java rename to beast-base/src/main/java/beast/base/spec/evolution/RateStatistic.java index 18806066..729c421d 100644 --- a/beast-base/src/main/java/beast/base/evolution/RateStatistic.java +++ b/beast-base/src/main/java/beast/base/spec/evolution/RateStatistic.java @@ -23,38 +23,37 @@ * Boston, MA 02110-1301 USA */ -package beast.base.evolution; +package beast.base.spec.evolution; -import java.io.PrintStream; - import beast.base.core.BEASTObject; import beast.base.core.Description; -import beast.base.core.Function; import beast.base.core.Input; -import beast.base.core.Loggable; import beast.base.core.Input.Validate; -import beast.base.evolution.branchratemodel.BranchRateModel; -import beast.base.evolution.likelihood.GenericTreeLikelihood; +import beast.base.core.Loggable; import beast.base.evolution.tree.Node; import beast.base.evolution.tree.Tree; +import beast.base.spec.domain.PositiveReal; +import beast.base.spec.evolution.branchratemodel.Base; +import beast.base.spec.evolution.likelihood.GenericTreeLikelihood; +import beast.base.spec.type.RealScalar; import beast.base.util.DiscreteStatistics; - +import java.io.PrintStream; @Description("A statistic that tracks the mean, variance and coefficent of variation of rates. " + "It has three dimensions, one for each statistic.") -public class RateStatistic extends BEASTObject implements Loggable, Function { +public class RateStatistic extends BEASTObject implements Loggable, RealScalar { final public Input likelihoodInput = new Input<>("treeLikelihood", "TreeLikelihood containing branch rate model that provides rates for a tree"); - final public Input branchRateModelInput = new Input<>("branchratemodel", "model that provides rates for a tree", Validate.XOR, likelihoodInput); + final public Input branchRateModelInput = new Input<>("branchratemodel", "model that provides rates for a tree", Validate.XOR, likelihoodInput); final public Input treeInput = new Input<>("tree", "tree for which the rates apply", Validate.REQUIRED); final public Input internalInput = new Input<>("internal", "consider internal nodes, default true", true); final public Input externalInput = new Input<>("external", "consider external nodes, default true", true); private Tree tree = null; - private BranchRateModel branchRateModel = null; + private Base branchRateModel = null; private boolean internal = true; private boolean external = true; @@ -144,25 +143,34 @@ public double[] calcValues() { * Valuable implementation * */ - @Override - public int getDimension() { - return 3; - } +// @Override +// public int getDimension() { +// return 3; +// } +// +// @Override +// public double getArrayValue() { +// return calcValues()[0]; +// } +// +// @Override +// public double getArrayValue(final int dim) { +// if (dim > 3) { +// throw new IllegalArgumentException(); +// } +// return calcValues()[dim]; +// } @Override - public double getArrayValue() { + public double get() { return calcValues()[0]; } @Override - public double getArrayValue(final int dim) { - if (dim > 3) { - throw new IllegalArgumentException(); - } - return calcValues()[dim]; + public PositiveReal getDomain() { + return PositiveReal.INSTANCE; } - /** * Loggable implementation * */ From 9126d0360c31ca7563b2348b519840f19c9caa78 Mon Sep 17 00:00:00 2001 From: walterxie Date: Tue, 21 Jul 2026 11:16:01 +1200 Subject: [PATCH 2/3] migrate RateStatistic and update xml, module-info, and version.xml #119 --- .../beast/base/evolution/RateStatistic.java | 190 ++++++++++++++++++ .../base/spec/evolution/RateStatistic.java | 68 ++++--- beast-base/src/main/java/module-info.java | 1 + .../beast2vs1/testUCRelaxedClockLogNormal.xml | 2 +- version.xml | 1 + 5 files changed, 234 insertions(+), 28 deletions(-) create mode 100644 beast-base/src/main/java/beast/base/evolution/RateStatistic.java diff --git a/beast-base/src/main/java/beast/base/evolution/RateStatistic.java b/beast-base/src/main/java/beast/base/evolution/RateStatistic.java new file mode 100644 index 00000000..e5292262 --- /dev/null +++ b/beast-base/src/main/java/beast/base/evolution/RateStatistic.java @@ -0,0 +1,190 @@ +/* + * RateStatistic.java + * + * Copyright (C) 2002-2006 Alexei Drummond and Andrew Rambaut + * + * 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 beast.base.evolution; + + +import beast.base.core.*; +import beast.base.core.Input.Validate; +import beast.base.evolution.branchratemodel.BranchRateModel; +import beast.base.evolution.likelihood.GenericTreeLikelihood; +import beast.base.evolution.tree.Node; +import beast.base.evolution.tree.Tree; +import beast.base.util.DiscreteStatistics; + +import java.io.PrintStream; + + +/** + * @deprecated use {@link beast.base.spec.evolution.RateStatistic} instead + */ +@Deprecated +@Description("A statistic that tracks the mean, variance and coefficent of variation of rates. " + + "It has three dimensions, one for each statistic.") +public class RateStatistic extends BEASTObject implements Loggable, Function { + + final public Input likelihoodInput = new Input<>("treeLikelihood", "TreeLikelihood containing branch rate model that provides rates for a tree"); + final public Input branchRateModelInput = new Input<>("branchratemodel", "model that provides rates for a tree", Validate.XOR, likelihoodInput); + final public Input treeInput = new Input<>("tree", "tree for which the rates apply", Validate.REQUIRED); + final public Input internalInput = new Input<>("internal", "consider internal nodes, default true", true); + final public Input externalInput = new Input<>("external", "consider external nodes, default true", true); + + private Tree tree = null; + private BranchRateModel branchRateModel = null; + private boolean internal = true; + private boolean external = true; + + final static int MEAN = 0; + final static int VARIANCE = 1; + final static int COEFFICIENT_OF_VARIATION = 2; + + @Override + public void initAndValidate() { + tree = treeInput.get(); + branchRateModel = branchRateModelInput.get(); + if (branchRateModel == null) { + branchRateModel = likelihoodInput.get().branchRateModelInput.get(); + } + this.internal = internalInput.get(); + this.external = externalInput.get(); + } + + /** + * calculate the three statistics from scratch * + */ + public double[] calcValues() { + int length = 0; + int offset = 0; + + final int nrOfLeafs = tree.getLeafNodeCount(); + + if (external) { + length += nrOfLeafs; + } + if (internal) { + length += tree.getInternalNodeCount() - 1; + } + + final double[] rates = new double[length]; + // need those only for mean + final double[] branchLengths = new double[length]; + + final Node[] nodes = tree.getNodesAsArray(); + + /** handle leaf nodes **/ + if (external) { + for (int i = 0; i < nrOfLeafs; i++) { + final Node child = nodes[i]; + final Node parent = child.getParent(); + branchLengths[i] = parent.getHeight() - child.getHeight(); + rates[i] = branchRateModel.getRateForBranch(child); + } + offset = nrOfLeafs; + } + + /** handle internal nodes **/ + if (internal) { + final int n = tree.getNodeCount(); + int k = offset; + for (int i = nrOfLeafs; i < n; i++) { + final Node child = nodes[i]; + if (!child.isRoot()) { + final Node parent = child.getParent(); + branchLengths[k] = parent.getHeight() - child.getHeight(); + rates[k] = branchRateModel.getRateForBranch(child); + k++; + } + } + } + + final double[] values = new double[3]; + double totalWeightedRate = 0.0; + double totalTreeLength = 0.0; + for (int i = 0; i < rates.length; i++) { + totalWeightedRate += rates[i] * branchLengths[i]; + totalTreeLength += branchLengths[i]; + } + values[MEAN] = totalWeightedRate / totalTreeLength; + // Q2R why not? +// final double mean = DiscreteStatistics.mean(rates); +// values[VARIANCE] = DiscreteStatistics.variance(rates, mean); +// values[COEFFICIENT_OF_VARIATION] = Math.sqrt(D values[VARIANCE]) / mean; + values[VARIANCE] = DiscreteStatistics.variance(rates); + final double mean = DiscreteStatistics.mean(rates); + values[COEFFICIENT_OF_VARIATION] = Math.sqrt(DiscreteStatistics.variance(rates, mean)) / mean; + return values; + } + + + /** + * Valuable implementation * + */ + + @Override + public int getDimension() { + return 3; + } + + @Override + public double getArrayValue() { + return calcValues()[0]; + } + + @Override + public double getArrayValue(final int dim) { + if (dim > 3) { + throw new IllegalArgumentException(); + } + return calcValues()[dim]; + } + + + /** + * Loggable implementation * + */ + + @Override + public void init(final PrintStream out) { + String id = getID(); + if (id == null) { + id = ""; + } + out.print(id + ".mean\t" + id + ".variance\t" + id + ".coefficientOfVariation\t"); + } + + + @Override + public void log(final long sample, final PrintStream out) { + final double[] values = calcValues(); + out.print(values[0] + "\t" + values[1] + "\t" + values[2] + "\t"); + } + + + @Override + public void close(final PrintStream out) { + // nothing to do + } + +} diff --git a/beast-base/src/main/java/beast/base/spec/evolution/RateStatistic.java b/beast-base/src/main/java/beast/base/spec/evolution/RateStatistic.java index 729c421d..79c4c06c 100644 --- a/beast-base/src/main/java/beast/base/spec/evolution/RateStatistic.java +++ b/beast-base/src/main/java/beast/base/spec/evolution/RateStatistic.java @@ -36,15 +36,18 @@ import beast.base.spec.domain.PositiveReal; import beast.base.spec.evolution.branchratemodel.Base; import beast.base.spec.evolution.likelihood.GenericTreeLikelihood; -import beast.base.spec.type.RealScalar; +import beast.base.spec.type.RealVector; import beast.base.util.DiscreteStatistics; import java.io.PrintStream; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; @Description("A statistic that tracks the mean, variance and coefficent of variation of rates. " + "It has three dimensions, one for each statistic.") -public class RateStatistic extends BEASTObject implements Loggable, RealScalar { +public class RateStatistic extends BEASTObject implements Loggable, RealVector { final public Input likelihoodInput = new Input<>("treeLikelihood", "TreeLikelihood containing branch rate model that provides rates for a tree"); final public Input branchRateModelInput = new Input<>("branchratemodel", "model that provides rates for a tree", Validate.XOR, likelihoodInput); @@ -57,6 +60,7 @@ public class RateStatistic extends BEASTObject implements Loggable, RealScalar

3) { -// throw new IllegalArgumentException(); -// } -// return calcValues()[dim]; -// } + @Deprecated + public int getDimension() { + return size(); + } + + @Deprecated + public double getArrayValue() { + return get(0); + } + + @Deprecated + public double getArrayValue(final int dim) { + return get(dim); + } + + // new API + + @Override + public int size() { + return 3; + } + + @Override + public double get(int i) { + if (i < 0 || i >= size()) { + throw new IllegalArgumentException(); + } + return calcValues()[i]; + } @Override - public double get() { - return calcValues()[0]; + public List getElements() { + return Arrays.stream(calcValues()).boxed().collect(Collectors.toList()); } @Override diff --git a/beast-base/src/main/java/module-info.java b/beast-base/src/main/java/module-info.java index 49ef4f24..aa657e17 100644 --- a/beast-base/src/main/java/module-info.java +++ b/beast-base/src/main/java/module-info.java @@ -281,6 +281,7 @@ beast.base.spec.inference.distribution.TruncatedReal, beast.base.spec.inference.distribution.TruncatedInt, beast.base.spec.FunctionOfTensor, + beast.base.spec.evolution.RateStatistic, beast.base.spec.evolution.Sum, beast.base.spec.evolution.IntSum, beast.base.spec.evolution.TreeWithMetaDataLogger, diff --git a/beast-base/src/test/resources/beast.base/examples/beast2vs1/testUCRelaxedClockLogNormal.xml b/beast-base/src/test/resources/beast.base/examples/beast2vs1/testUCRelaxedClockLogNormal.xml index 0e881147..9f8e4776 100644 --- a/beast-base/src/test/resources/beast.base/examples/beast2vs1/testUCRelaxedClockLogNormal.xml +++ b/beast-base/src/test/resources/beast.base/examples/beast2vs1/testUCRelaxedClockLogNormal.xml @@ -142,7 +142,7 @@ - +