/*
* MatrixParameter.java
*
* Copyright (c) 2002-2012 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.inference.model;
import dr.xml.*;
import java.util.StringTokenizer;
/**
* @author Marc Suchard
*/
public class MatrixParameter extends CompoundParameter {
public final static String MATRIX_PARAMETER = "matrixParameter";
public MatrixParameter(String name) {
super(name);
}
public MatrixParameter(String name, Parameter[] parameters) {
super(name, parameters);
dimensionsEstablished = true;
}
public double getParameterValue(int row, int col) {
return getParameter(col).getParameterValue(row);
}
public double[][] getParameterAsMatrix() {
final int I = getRowDimension();
final int J = getColumnDimension();
double[][] parameterAsMatrix = new double[I][J];
for (int i = 0; i < I; i++) {
for (int j = 0; j < J; j++)
parameterAsMatrix[i][j] = getParameterValue(i, j);
}
return parameterAsMatrix;
}
public void setColumnDimension(int columnDimension) {
if (dimensionsEstablished) {
throw new IllegalArgumentException("Attempt to change dimensions after initialization");
}
this.columnDimension = columnDimension;
setupParameters();
}
public void setRowDimension(int rowDimension) {
if (dimensionsEstablished) {
throw new IllegalArgumentException("Attempt to change dimensions after initialization");
}
this.rowDimension = rowDimension;
setupParameters();
}
private void setupParameters() {
if (columnDimension > 0 && rowDimension > 0) {
dimensionsEstablished = true;
for (int i = 0; i < rowDimension; i++) {
Parameter row = new Parameter.Default(columnDimension, 0.0);
row.addBounds(new DefaultBounds(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, columnDimension));
addParameter(row);
}
}
}
public int getColumnDimension() {
return getParameterCount();
}
public int getRowDimension() {
return getParameter(0).getDimension();
}
public String toSymmetricString() {
StringBuilder sb = new StringBuilder("{");
int dim = getRowDimension();
int total = dim * (dim + 1) / 2;
for (int i = 0; i < dim; i++) {
for (int j = i; j < dim; j++) {
sb.append(String.format("%5.4e", getParameterValue(i, j)));
total--;
if (total > 0)
sb.append(",");
}
}
sb.append("}");
return sb.toString();
}
public static MatrixParameter parseFromSymmetricString(String string) {
String clip = string.replace("{", "").replace("}", "").trim();
StringTokenizer st = new StringTokenizer(clip, ",");
int count = st.countTokens();
int dim = (-1 + (int) Math.sqrt(1 + 8 * count)) / 2;
Parameter[] parameter = new Parameter[dim];
for (int i = 0; i < dim; i++)
parameter[i] = new Parameter.Default(dim);
for (int i = 0; i < dim; i++) {
for (int j = i; j < dim; j++) {
double datum = new Double(st.nextToken());
parameter[i].setParameterValue(j, datum);
parameter[j].setParameterValue(i, datum);
}
}
return new MatrixParameter(null, parameter);
}
public static MatrixParameter parseFromSymmetricDoubleArray(Object[] data) {
int dim = (-1 + (int) Math.sqrt(1 + 8 * data.length)) / 2;
Parameter[] parameter = new Parameter[dim];
for (int i = 0; i < dim; i++)
parameter[i] = new Parameter.Default(dim);
int index = 0;
for (int i = 0; i < dim; i++) {
for (int j = i; j < dim; j++) {
double datum = (Double) data[index++];
parameter[i].setParameterValue(j, datum);
parameter[j].setParameterValue(i, datum);
}
}
return new MatrixParameter(null, parameter);
}
private boolean dimensionsEstablished = false;
private int columnDimension = 0;
private int rowDimension = 0;
// **************************************************************
// XMLElement IMPLEMENTATION
// **************************************************************
// public Element createElement(Document d) {
// throw new RuntimeException("Not implemented yet!");
// }
private static final String ROW_DIMENSION = "rows";
private static final String COLUMN_DIMENSION = "columns";
private static final String TRANSPOSE = "transpose";
public static XMLObjectParser PARSER = new AbstractXMLObjectParser() {
public String getParserName() {
return MATRIX_PARAMETER;
}
public Object parseXMLObject(XMLObject xo) throws XMLParseException {
final String name = xo.hasId() ? xo.getId() : null;
boolean transposed = xo.getAttribute(TRANSPOSE, false);
MatrixParameter matrixParameter;
if (!transposed) {
matrixParameter = new MatrixParameter(name);
} else {
matrixParameter = new TransposedMatrixParameter(name);
}
if (xo.hasAttribute(ROW_DIMENSION)) {
int rowDimension = xo.getIntegerAttribute(ROW_DIMENSION);
matrixParameter.setRowDimension(rowDimension);
}
if (xo.hasAttribute(COLUMN_DIMENSION)) {
int columnDimension = xo.getIntegerAttribute(COLUMN_DIMENSION);
matrixParameter.setColumnDimension(columnDimension);
}
int dim = 0;
for (int i = 0; i < xo.getChildCount(); i++) {
Parameter parameter = (Parameter) xo.getChild(i);
matrixParameter.addParameter(parameter);
if (i == 0)
dim = parameter.getDimension();
else if (dim != parameter.getDimension())
throw new XMLParseException("All parameters must have the same dimension to construct a rectangular matrix");
}
return matrixParameter;
}
//************************************************************************
// AbstractXMLObjectParser implementation
//************************************************************************
public String getParserDescription() {
return "A matrix parameter constructed from its component parameters.";
}
public XMLSyntaxRule[] getSyntaxRules() {
return rules;
}
private final XMLSyntaxRule[] rules = {
new ElementRule(Parameter.class, 0, Integer.MAX_VALUE),
AttributeRule.newIntegerRule(ROW_DIMENSION, true),
AttributeRule.newIntegerRule(COLUMN_DIMENSION, true)
};
public Class getReturnType() {
return MatrixParameter.class;
}
};
}