{
   rules =
       new XMLSyntaxRule[] {
         AttributeRule.newBooleanRule(NON_INFORMATIVE, true),
         AttributeRule.newDoubleRule(DF, true),
         new ElementRule(
             SCALE_MATRIX,
             new XMLSyntaxRule[] {new ElementRule(MatrixParameter.class)},
             true),
         new ElementRule(
             DATA,
             new XMLSyntaxRule[] {
               new ElementRule(MatrixParameter.class, 1, Integer.MAX_VALUE)
             })
       };
 }
 {
   rules =
       new XMLSyntaxRule[] {
         AttributeRule.newDoubleRule(MCMCOperator.WEIGHT),
         new ElementRule(CaseToCaseTreeLikelihood.class)
       };
 }
 @Override
 public XMLSyntaxRule[] getSyntaxRules() {
   return new XMLSyntaxRule[] {
     AttributeRule.newDoubleRule(MCMCOperator.WEIGHT),
     new ElementRule(AlloppSpeciesBindings.class),
     new ElementRule(AlloppSpeciesNetworkModel.class)
   };
 }
 public XMLSyntaxRule[] getSyntaxRules() {
   return new XMLSyntaxRule[] {
     AttributeRule.newBooleanRule(BeagleTreeLikelihoodParser.USE_AMBIGUITIES, true),
     AttributeRule.newStringRule(RECONSTRUCTION_TAG_NAME, true),
     AttributeRule.newBooleanRule(INTEGRATE_GAIN_RATE),
     new ElementRule(
         IMMIGRATION_RATE, new XMLSyntaxRule[] {new ElementRule(Parameter.class)}, true),
     new ElementRule(PatternList.class),
     new ElementRule(TreeModel.class),
     new ElementRule(GammaSiteRateModel.class),
     new ElementRule(BranchModel.class, true),
     new ElementRule(BranchRateModel.class, true),
     new ElementRule(TipStatesModel.class, true),
     new ElementRule(SubstitutionModel.class, true),
     AttributeRule.newStringRule(BeagleTreeLikelihoodParser.SCALING_SCHEME, true),
     new ElementRule(
         PARTIALS_RESTRICTION,
         new XMLSyntaxRule[] {
           new ElementRule(TaxonList.class), new ElementRule(Parameter.class),
         },
         true),
     new ElementRule(
         OBSERVATION_PROCESS,
         new XMLSyntaxRule[] {
           AttributeRule.newStringRule(OBSERVATION_TYPE, false),
           AttributeRule.newStringRule(OBSERVATION_TAXON, true)
         })
   };
 }
/** Parser for MicrosatelliteModelSelectOperatorParser */
public class MicrosatelliteModelSelectOperatorParser extends AbstractXMLObjectParser {

  public static final String MODEL_INDICATORS = "modelIndicators";
  public static final String MODEL_CHOOSE = "modelChoose";

  public String getParserName() {
    return "msatModelSelectOperator";
  }

  public Object parseXMLObject(XMLObject xo) throws XMLParseException {
    double weight = xo.getDoubleAttribute(MCMCOperator.WEIGHT);
    Parameter modelChoose = (Parameter) xo.getElementFirstChild(MODEL_CHOOSE);
    XMLObject xoInd = xo.getChild(MODEL_INDICATORS);
    int childNum = xoInd.getChildCount();
    System.out.println("There are 12 potential models");
    Parameter[] modelIndicators = new Parameter[childNum];
    for (int i = 0; i < modelIndicators.length; i++) {
      modelIndicators[i] = (Parameter) xoInd.getChild(i);
    }
    return new MicrosatelliteModelSelectOperator(modelChoose, modelIndicators, weight);
  }
  // ************************************************************************
  // AbstractXMLObjectParser implementation
  // ************************************************************************
  public String getParserDescription() {
    return "This element returns a microsatellite averaging operator on a given parameter.";
  }

  public Class getReturnType() {
    return MCMCOperator.class;
  }

  public XMLSyntaxRule[] getSyntaxRules() {
    return rules;
  }

  private XMLSyntaxRule[] rules =
      new XMLSyntaxRule[] {
        AttributeRule.newDoubleRule(MCMCOperator.WEIGHT),
        new ElementRule(MODEL_CHOOSE, new XMLSyntaxRule[] {new ElementRule(Parameter.class)}),
        new ElementRule(
            MODEL_INDICATORS,
            new XMLSyntaxRule[] {new ElementRule(Parameter.class, 1, Integer.MAX_VALUE)}),
      };
}
public class TreeLengthStatisticParser extends AbstractXMLObjectParser {

  public static final String TREE_LENGTH_STATISTIC = "treeLengthStatistic";

  public String getParserName() {
    return TREE_LENGTH_STATISTIC;
  }

  public Object parseXMLObject(XMLObject xo) throws XMLParseException {

    String name = xo.getAttribute(Statistic.NAME, xo.getId());
    Tree tree = (Tree) xo.getChild(Tree.class);

    return new TreeLengthStatistic(name, tree);
  }

  // ************************************************************************
  // AbstractXMLObjectParser implementation
  // ************************************************************************

  public String getParserDescription() {
    return "A statistic that returns the average of the branch rates";
  }

  public Class getReturnType() {
    return TreeLengthStatistic.class;
  }

  public XMLSyntaxRule[] getSyntaxRules() {
    return rules;
  }

  private final XMLSyntaxRule[] rules = {
    AttributeRule.newStringRule(Statistic.NAME, true), new ElementRule(TreeModel.class),
  };
}
Esempio n. 7
0
/**
 * @author Marc A. Suchard
 * @author Andrew Rambaut
 */
public class WeightedMixtureModel extends AbstractModelLikelihood implements Citable {

  public static final String MIXTURE_MODEL = "mixtureModel";
  //    public static final String MIXTURE_WEIGHTS = "weights";
  public static final String NORMALIZE = "normalize";

  public WeightedMixtureModel(
      List<AbstractModelLikelihood> likelihoodList, Parameter mixtureWeights) {
    super(MIXTURE_MODEL);
    this.likelihoodList = likelihoodList;
    this.mixtureWeights = mixtureWeights;
    for (AbstractModelLikelihood model : likelihoodList) {
      addModel(model);
    }
    addVariable(mixtureWeights);

    StringBuilder sb = new StringBuilder();
    sb.append("Constructing a finite mixture model\n");
    sb.append("\tComponents:\n");
    for (AbstractModelLikelihood model : likelihoodList) {
      sb.append("\t\t\t").append(model.getId()).append("\n");
    }
    sb.append("\tMixing parameter: ").append(mixtureWeights.getId()).append("\n");
    sb.append("\tPlease cite:\n");
    sb.append(Citable.Utils.getCitationString((this)));

    Logger.getLogger("dr.inference.model").info(sb.toString());
  }

  protected void handleModelChangedEvent(Model model, Object object, int index) {}

  protected final void handleVariableChangedEvent(
      Variable variable, int index, Parameter.ChangeType type) {}

  protected void storeState() {}

  protected void restoreState() {}

  protected void acceptState() {}

  public Model getModel() {
    return this;
  }

  public double getLogLikelihood() {
    double logSum = Double.NEGATIVE_INFINITY;
    for (int i = 0; i < likelihoodList.size(); ++i) {
      double pi = mixtureWeights.getParameterValue(i);
      if (pi > 0.0) {
        logSum = LogTricks.logSum(logSum, Math.log(pi) + likelihoodList.get(i).getLogLikelihood());
      }
    }
    return logSum;
  }

  public void makeDirty() {}

  public LogColumn[] getColumns() {
    return new LogColumn[0];
  }

  public static XMLObjectParser PARSER =
      new AbstractXMLObjectParser() {

        public String getParserName() {
          return MIXTURE_MODEL;
        }

        public Object parseXMLObject(XMLObject xo) throws XMLParseException {

          Parameter weights = (Parameter) xo.getChild(Parameter.class);
          List<AbstractModelLikelihood> likelihoodList = new ArrayList<AbstractModelLikelihood>();

          for (int i = 0; i < xo.getChildCount(); i++) {
            if (xo.getChild(i) instanceof Likelihood)
              likelihoodList.add((AbstractModelLikelihood) xo.getChild(i));
          }

          if (weights.getDimension() != likelihoodList.size()) {
            throw new XMLParseException(
                "Dim of " + weights.getId() + " does not match the number of likelihoods");
          }

          if (xo.hasAttribute(NORMALIZE)) {
            if (xo.getBooleanAttribute(NORMALIZE)) {
              double sum = 0;
              for (int i = 0; i < weights.getDimension(); i++) sum += weights.getParameterValue(i);
              for (int i = 0; i < weights.getDimension(); i++)
                weights.setParameterValue(i, weights.getParameterValue(i) / sum);
            }
          }

          if (!normalized(weights))
            throw new XMLParseException(
                "Parameter +" + weights.getId() + " must lie on the simplex");

          return new WeightedMixtureModel(likelihoodList, weights);
        }

        private boolean normalized(Parameter p) {
          double sum = 0;
          for (int i = 0; i < p.getDimension(); i++) sum += p.getParameterValue(i);
          return (sum == 1.0);
        }

        // ************************************************************************
        // AbstractXMLObjectParser implementation
        // ************************************************************************

        public String getParserDescription() {
          return "This element represents a finite mixture of likelihood models.";
        }

        public Class getReturnType() {
          return CompoundModel.class;
        }

        public XMLSyntaxRule[] getSyntaxRules() {
          return rules;
        }

        private final XMLSyntaxRule[] rules = {
          AttributeRule.newBooleanRule(NORMALIZE, true),
          new ElementRule(Likelihood.class, 2, Integer.MAX_VALUE),
          new ElementRule(Parameter.class)
        };
      };

  private final Parameter mixtureWeights;
  List<AbstractModelLikelihood> likelihoodList;

  public static void main(String[] args) {

    final double l1 = -10;
    final double l2 = -2;

    AbstractModelLikelihood like1 =
        new AbstractModelLikelihood("dummy") {

          public Model getModel() {
            return null;
          }

          public double getLogLikelihood() {
            return l1;
          }

          public void makeDirty() {}

          public String prettyName() {
            return null;
          }

          public boolean isUsed() {
            return false;
          }

          @Override
          protected void handleModelChangedEvent(Model model, Object object, int index) {}

          @Override
          protected void handleVariableChangedEvent(
              Variable variable, int index, Variable.ChangeType type) {}

          @Override
          protected void storeState() {}

          @Override
          protected void restoreState() {}

          @Override
          protected void acceptState() {}

          public void setUsed() {}

          public LogColumn[] getColumns() {
            return new LogColumn[0];
          }

          public String getId() {
            return null;
          }

          public void setId(String id) {}
        };

    AbstractModelLikelihood like2 =
        new AbstractModelLikelihood("dummy") {

          public Model getModel() {
            return null;
          }

          public double getLogLikelihood() {
            return l2;
          }

          public void makeDirty() {}

          public String prettyName() {
            return null;
          }

          public boolean isUsed() {
            return false;
          }

          @Override
          protected void handleModelChangedEvent(Model model, Object object, int index) {}

          @Override
          protected void handleVariableChangedEvent(
              Variable variable, int index, Variable.ChangeType type) {}

          @Override
          protected void storeState() {}

          @Override
          protected void restoreState() {}

          @Override
          protected void acceptState() {}

          public void setUsed() {}

          public LogColumn[] getColumns() {
            return new LogColumn[0];
          }

          public String getId() {
            return null;
          }

          public void setId(String id) {}
        };

    List<AbstractModelLikelihood> likelihoodList = new ArrayList<AbstractModelLikelihood>();
    likelihoodList.add(like1);
    likelihoodList.add(like2);

    Parameter weights = new Parameter.Default(2);
    double p1 = 0.05;
    weights.setParameterValue(0, p1);
    weights.setParameterValue(1, 1.0 - p1);

    WeightedMixtureModel mixture = new WeightedMixtureModel(likelihoodList, weights);
    System.err.println("getLogLikelihood() = " + mixture.getLogLikelihood());

    double test = Math.log(p1 * Math.exp(l1) + (1.0 - p1) * Math.exp(l2));
    System.err.println("correct            = " + test);
  }

  public List<Citation> getCitations() {
    List<Citation> citations = new ArrayList<Citation>();
    citations.add(CommonCitations.LEMEY_MIXTURE_2012);
    return citations;
  }
}
/**
 * An independent normal distribution sampler to propose new (independent) values from a provided
 * normal distribution model.
 *
 * @author Marc Suchard
 * @author Guy Baele
 */
public class MultivariateNormalIndependenceSampler extends AbstractCoercableOperator {

  public static final String OPERATOR_NAME = "multivariateNormalIndependenceSampler";
  public static final String SCALE_FACTOR = "scaleFactor";
  public static final String SET_SIZE_MEAN = "setSizeMean";

  private double scaleFactor;
  private final Parameter parameter;
  private final int dim;
  private double setSizeMean;
  private final SelfControlledCaseSeries sccs;

  public MultivariateNormalIndependenceSampler(
      Parameter parameter,
      SelfControlledCaseSeries sccs,
      double setSizeMean,
      double weight,
      double scaleFactor,
      CoercionMode mode) {
    super(mode);
    this.scaleFactor = scaleFactor;
    this.parameter = parameter;
    setWeight(weight);
    dim = parameter.getDimension();
    setWeight(weight);
    this.sccs = sccs;
    this.setSizeMean = setSizeMean;
  }

  public String getPerformanceSuggestion() {
    return "";
  }

  public String getOperatorName() {
    return "independentNormalDistribution(" + parameter.getVariableName() + ")";
  }

  /** change the parameter and return the hastings ratio. */
  public double doOperation() throws OperatorFailedException {

    double[] mean = sccs.getMode();
    double[] currentValue = parameter.getParameterValues();
    double[] newValue = new double[dim];

    Set<Integer> updateSet = new HashSet<Integer>();

    if (setSizeMean != -1.0) {
      final int listLength = Poisson.nextPoisson(setSizeMean);
      while (updateSet.size() < listLength) {
        int newInt = MathUtils.nextInt(parameter.getDimension());
        if (!updateSet.contains(newInt)) {
          updateSet.add(newInt);
        }
      }
    } else {
      for (int i = 0; i < dim; ++i) {
        updateSet.add(i);
      }
    }

    double logq = 0;
    for (Integer i : updateSet) {
      newValue[i] = mean[i] + scaleFactor * MathUtils.nextGaussian();
      if (UPDATE_ALL) {
        parameter.setParameterValueQuietly(i, newValue[i]);
      } else {
        parameter.setParameterValue(i, newValue[i]);
      }

      logq +=
          (NormalDistribution.logPdf(currentValue[i], mean[i], scaleFactor)
              - NormalDistribution.logPdf(newValue[i], mean[i], scaleFactor));
    }

    //        for (Integer i : updateSet) {
    //            parameter.setParameterValueQuietly(i, newValue[i]);
    //        }

    if (UPDATE_ALL) {
      parameter.setParameterValueNotifyChangedAll(0, parameter.getParameterValue(0));
    }

    return logq;
  }

  private static final boolean UPDATE_ALL = false;

  public static XMLObjectParser PARSER =
      new AbstractXMLObjectParser() {

        public String getParserName() {
          return OPERATOR_NAME;
        }

        public Object parseXMLObject(XMLObject xo) throws XMLParseException {

          CoercionMode mode = CoercionMode.parseMode(xo);

          double weight = xo.getDoubleAttribute(WEIGHT);
          double scaleFactor = xo.getDoubleAttribute(SCALE_FACTOR);

          if (scaleFactor <= 0.0) {
            throw new XMLParseException("scaleFactor must be greater than 0.0");
          }

          Parameter parameter = (Parameter) xo.getChild(Parameter.class);

          SelfControlledCaseSeries sccs =
              (SelfControlledCaseSeries) xo.getChild(SelfControlledCaseSeries.class);

          double setSizeMean = xo.getAttribute(SET_SIZE_MEAN, -1.0);

          return new MultivariateNormalIndependenceSampler(
              parameter, sccs, setSizeMean, weight, scaleFactor, mode);
        }

        // ************************************************************************
        // AbstractXMLObjectParser implementation
        // ************************************************************************

        public XMLSyntaxRule[] getSyntaxRules() {
          return rules;
        }

        private final XMLSyntaxRule[] rules = {
          AttributeRule.newDoubleRule(SCALE_FACTOR),
          AttributeRule.newDoubleRule(WEIGHT),
          AttributeRule.newBooleanRule(AUTO_OPTIMIZE, true),
          AttributeRule.newDoubleRule(SET_SIZE_MEAN, true),
          new ElementRule(SelfControlledCaseSeries.class),
          new ElementRule(Parameter.class),
        };

        public String getParserDescription() {
          return "This element returns an independence sampler from a provided normal distribution model.";
        }

        public Class getReturnType() {
          return MultivariateNormalIndependenceSampler.class;
        }
      };

  public double getCoercableParameter() {
    return Math.log(scaleFactor);
  }

  public void setCoercableParameter(double value) {
    scaleFactor = Math.exp(value);
  }

  public double getRawParameter() {
    return scaleFactor;
  }
}
/** @author Max Tolkoff */
public class BlockUpperTriangularMatrixParameterParser extends AbstractXMLObjectParser {
  private static final String BLOCK_UPPER_TRIANGULAR_MATRIX = "blockUpperTriangularMatrixParameter";
  private static final String COLUMN_DIMENSION = "columnDimension";
  private static final String TRANSPOSE = "transpose";
  private static final String DIAGONAL_RESTRICTION = "diagonalRestriction";

  @Override
  public Object parseXMLObject(XMLObject xo) throws XMLParseException {

    final String name = xo.hasId() ? xo.getId() : null;
    final boolean transpose = xo.getAttribute(TRANSPOSE, false);
    //        int rowDim=xo.getChildCount();
    //        int colDim;
    final boolean diagonalRestriction = xo.getAttribute(DIAGONAL_RESTRICTION, false);
    Parameter temp = null;
    //        if(xo.hasAttribute(COLUMN_DIMENSION)) {
    //            colDim = xo.getAttribute(COLUMN_DIMENSION, 1);
    //        }
    //        else
    //        {
    //            temp=(Parameter) xo.getChild(xo.getChildCount()-1);
    //            colDim=temp.getDimension();
    //        }

    Parameter[] params = new Parameter[xo.getChildCount()];

    for (int i = 0; i < xo.getChildCount(); i++) {
      temp = (Parameter) xo.getChild(i);
      params[i] = temp;
    }

    BlockUpperTriangularMatrixParameter ltmp =
        new BlockUpperTriangularMatrixParameter(name, params, diagonalRestriction);
    if (transpose) {
      return ltmp.transposeBlock();
    } else {
      return ltmp; // To change body of implemented methods use File | Settings | File Templates.
    }
  }

  @Override
  public XMLSyntaxRule[] getSyntaxRules() {
    return rules;
  }

  private final XMLSyntaxRule[] rules = {
    new ElementRule(Parameter.class, 0, Integer.MAX_VALUE),
    AttributeRule.newBooleanRule(TRANSPOSE, true),
    AttributeRule.newIntegerRule(COLUMN_DIMENSION, true),
    AttributeRule.newBooleanRule(DIAGONAL_RESTRICTION, true),
  };

  @Override
  public String getParserDescription() {
    return "Returns a blockUpperTriangularMatrixParameter which is a compoundParameter which forces the last element to be of full length, the second to last element to be of full length-1, etc."; // To change body of implemented methods use File | Settings | File Templates.
  }

  @Override
  public Class getReturnType() {
    return BlockUpperTriangularMatrixParameter
        .class; // To change body of implemented methods use File | Settings | File Templates.
  }

  @Override
  public String getParserName() {
    return BLOCK_UPPER_TRIANGULAR_MATRIX; // To change body of implemented methods use File |
    // Settings | File Templates.
  }
}
public class BinomialLikelihoodParser extends AbstractXMLObjectParser {

  public static final String TRIALS = "trials";
  public static final String COUNTS = "counts";
  public static final String PROPORTION = "proportion";
  public static final String VALUES = "values";
  public static final String ON_LOGIT_SCALE = "onLogitScale";

  public String getParserName() {
    return BinomialLikelihood.BINOMIAL_LIKELIHOOD;
  }

  public Object parseXMLObject(XMLObject xo) throws XMLParseException {

    final boolean onLogitScale = xo.getAttribute(ON_LOGIT_SCALE, false);

    XMLObject cxo = xo.getChild(COUNTS);
    Parameter countsParam = (Parameter) cxo.getChild(Parameter.class);

    cxo = xo.getChild(PROPORTION);
    Parameter proportionParam = (Parameter) cxo.getChild(Parameter.class);

    if (proportionParam.getDimension() != 1
        && proportionParam.getDimension() != countsParam.getDimension()) {
      throw new XMLParseException(
          "Proportion dimension ("
              + proportionParam.getDimension()
              + ") "
              + "must equal 1 or counts dimension ("
              + countsParam.getDimension()
              + ")");
    }

    cxo = xo.getChild(TRIALS);
    Parameter trialsParam;
    if (cxo.hasAttribute(VALUES)) {
      int[] tmp = cxo.getIntegerArrayAttribute(VALUES);
      double[] v = new double[tmp.length];
      for (int i = 0; i < tmp.length; ++i) {
        v[i] = tmp[i];
      }
      trialsParam = new Parameter.Default(v);
    } else {
      trialsParam = (Parameter) cxo.getChild(Parameter.class);
    }

    if (trialsParam.getDimension() != countsParam.getDimension()) {
      throw new XMLParseException(
          "Trials dimension ("
              + trialsParam.getDimension()
              + ") must equal counts dimension ("
              + countsParam.getDimension()
              + ")");
    }

    return new BinomialLikelihood(trialsParam, proportionParam, countsParam, onLogitScale);
  }

  // ************************************************************************
  // AbstractXMLObjectParser implementation
  // ************************************************************************

  public XMLSyntaxRule[] getSyntaxRules() {
    return rules;
  }

  private final XMLSyntaxRule[] rules = {
    AttributeRule.newBooleanRule(ON_LOGIT_SCALE, true),
    new ElementRule(COUNTS, new XMLSyntaxRule[] {new ElementRule(Parameter.class)}),
    new ElementRule(PROPORTION, new XMLSyntaxRule[] {new ElementRule(Parameter.class)}),
    new XORRule(
        new ElementRule(
            TRIALS,
            new XMLSyntaxRule[] {
              AttributeRule.newIntegerArrayRule(VALUES, false),
            }),
        new ElementRule(TRIALS, new XMLSyntaxRule[] {new ElementRule(Parameter.class)})),
  };

  public String getParserDescription() {
    return "Calculates the likelihood of some data given some parametric or empirical distribution.";
  }

  public Class getReturnType() {
    return Likelihood.class;
  }
}
Esempio n. 11
0
/**
 * @author Marc A. Suchard
 * @author Philippe Lemey
 */
public class NewPolygon2D {

  public static final String POLYGON = "polygon";
  public static final String CLOSED = "closed";
  public static final String FILL_VALUE = "fillValue";

  public NewPolygon2D(GeneralPath path) {
    this.path = path;
  }

  public NewPolygon2D(Element e) {

    List<Element> children = e.getChildren();
    //        for (int a = 0; a < children.size(); a++) {
    for (Element childElement : children) {
      //            Element childElement = (Element) children.get(a);
      if (childElement.getName().equals(KMLCoordinates.COORDINATES)) {

        String value = childElement.getTextTrim();
        StringTokenizer st1 = new StringTokenizer(value, "\n");
        int count = st1.countTokens(); // System.out.println(count);

        //                point2Ds = new LinkedList<Point2D>();
        this.path = new GeneralPath();

        for (int i = 0; i < count; i++) {
          String line = st1.nextToken();
          StringTokenizer st2 = new StringTokenizer(line, ",");
          if (st2.countTokens() != 3)
            throw new IllegalArgumentException(
                "All KML coordinates must contain (X,Y,Z) values.  Three dimensions not found in element '"
                    + line
                    + "'");
          final float x = Float.valueOf(st2.nextToken());
          final float y = Float.valueOf(st2.nextToken());
          if (i == 0) {
            path.moveTo(x, y);
          } else {
            path.lineTo(x, y);
          }
          //                    point2Ds.add(new Point2D.Double(x, y));
        }
        //                length = point2Ds.size() - 1;
        break;
      }
    }
  }

  public NewPolygon2D() {
    path = new GeneralPath();
  }

  public void moveTo(Point2D pt) {
    path.moveTo((float) pt.getX(), (float) pt.getY());
  }

  public void lineTo(Point2D pt) {
    path.lineTo((float) pt.getX(), (float) pt.getY());
  }

  public boolean contains(Point2D pt) {
    return path.contains(pt);
  }

  public void closePath() {
    path.closePath();
  }

  public void setFillValue(double value) {
    fillValue = value;
  }

  public double getFillValue() {
    return fillValue;
  }

  public NewPolygon2D clip(Rectangle2D boundingBox) {
    Area thisArea = new Area(path);
    thisArea.intersect(new Area(boundingBox));
    PathIterator iterator = thisArea.getPathIterator(null);
    double[] v = new double[2];
    while (!iterator.isDone()) {
      int type = iterator.currentSegment(v);
      System.err.println(":" + v[0] + v[1] + "\n");
      iterator.next();
    }
    System.exit(-1);

    GeneralPath path = new GeneralPath(thisArea);
    path.closePath();
    NewPolygon2D newPolygon = new NewPolygon2D(path);
    newPolygon.setFillValue(this.getFillValue());
    return newPolygon;
  }

  public String toString() {
    StringBuffer sb = new StringBuffer();
    sb.append("polygon(");
    sb.append(fillValue);
    sb.append(")[\n");
    PathIterator iterator = path.getPathIterator(null);
    float[] values = new float[2];
    while (!iterator.isDone()) {
      int type = iterator.currentSegment(values);
      Point2D pt = new Point2D.Double(values[0], values[1]);
      sb.append("\t");
      sb.append(pt);
      sb.append("\n");
      iterator.next();
    }
    //        sb.append(iterator);
    //        for(Point2D pt : point2Ds) {
    //            sb.append("\t");
    //            sb.append(pt);
    //            sb.append("\n");
    //        }
    //        sb.append(path.toString());
    sb.append("]");
    return sb.toString();
  }

  public static XMLObjectParser PARSER =
      new AbstractXMLObjectParser() {

        public String getParserName() {
          return POLYGON;
        }

        public Object parseXMLObject(XMLObject xo) throws XMLParseException {

          KMLCoordinates coordinates = (KMLCoordinates) xo.getChild(KMLCoordinates.class);
          boolean closed = xo.getAttribute(CLOSED, false);

          if ((!closed && coordinates.length < 3) || (closed && coordinates.length < 4))
            throw new XMLParseException(
                "Insufficient points in polygon '" + xo.getId() + "' to define a polygon in 2D");

          NewPolygon2D polygon = new NewPolygon2D();
          polygon.moveTo(new Point2D.Double(coordinates.x[0], coordinates.y[0]));
          int length = coordinates.length;
          if (closed) length--;
          for (int i = 1; i < length; i++)
            polygon.lineTo(new Point2D.Double(coordinates.x[i], coordinates.y[i]));
          polygon.lineTo(new Point2D.Double(coordinates.x[0], coordinates.y[0]));
          //            polygon.closePath();

          polygon.setFillValue(xo.getAttribute(FILL_VALUE, 0.0));

          return polygon;
        }

        // ************************************************************************
        // AbstractXMLObjectParser implementation
        // ************************************************************************

        public String getParserDescription() {
          return "This element represents a polygon.";
        }

        public Class getReturnType() {
          return Polygon2D.class;
        }

        public XMLSyntaxRule[] getSyntaxRules() {
          return rules;
        }

        private XMLSyntaxRule[] rules =
            new XMLSyntaxRule[] {
              new ElementRule(KMLCoordinates.class),
              AttributeRule.newBooleanRule(CLOSED, true),
              AttributeRule.newDoubleRule(FILL_VALUE, true),
            };
      };

  public static void main(String[] args) {
    NewPolygon2D polygon = new NewPolygon2D();
    polygon.moveTo(new Point2D.Double(-10, -10));
    polygon.lineTo(new Point2D.Double(-10, 50));
    polygon.lineTo(new Point2D.Double(10, 50));
    polygon.lineTo(new Point2D.Double(10, -10));
    polygon.lineTo(new Point2D.Double(-10, -10));
    //        polygon.closePath();
    System.out.println(polygon);
    System.out.println("");
    //        System.exit(-1);

    Point2D pt = new Point2D.Double(0, 0);
    System.out.println("polygon contains " + pt + ": " + polygon.contains(pt));
    pt = new Point2D.Double(100, 100);
    System.out.println("polygon contains " + pt + ": " + polygon.contains(pt));
    System.out.println("");

    Rectangle2D boundingBox =
        new Rectangle2D.Double(0, 0, 100, 100); // defines lower-left corner and width/height
    System.out.println(boundingBox);
    NewPolygon2D myClip = polygon.clip(boundingBox);
    System.out.println(myClip);
  }

  private double fillValue;
  private GeneralPath path;
}
Esempio n. 12
0
/**
 * @author Marc A. Suchard
 * @author Philippe Lemey
 */
public class Polygon2D {
  public static final String POLYGON = "polygon";
  public static final String CLOSED = "closed";
  public static final String FILL_VALUE = "fillValue";
  public static final String CIRCLE = "circle";
  public static final String NUMBER_OF_POINTS = "numberOfPoints";
  public static final String RADIUS = "radius";
  public static final String CENTER = "center";
  public static final String LATITUDE = "latitude";
  public static final String LONGITUDE = "longitude";

  public Polygon2D(double[] x, double[] y) {
    if (x.length != y.length) {
      throw new RuntimeException("Unbalanced arrays");
    }

    if (x[0] != x[x.length - 1] && y[0] != y[y.length - 1]) {
      double[] newX = new double[x.length + 1];
      double[] newY = new double[y.length + 1];
      System.arraycopy(x, 0, newX, 0, x.length);
      System.arraycopy(y, 0, newY, 0, y.length);
      newX[x.length] = x[0];
      newY[y.length] = y[0];
      this.x = newX;
      this.y = newY;
    } else {
      this.x = x;
      this.y = y;
    }
    length = this.x.length - 1;
  }

  public Polygon2D(List<Point2D> points, boolean closed) {
    this.point2Ds = points;
    if (!closed) {
      Point2D start = points.get(0);
      points.add(start);
    }
    convertPointsToArrays();
    length = points.size() - 1;
  }

  public Polygon2D() {
    length = 0;
    point2Ds = new ArrayList<Point2D>();
  }

  public String getID() {
    return id;
  }

  public Polygon2D(Element e) {

    //        System.err.println("parsing polygon");

    List<Element> children = e.getChildren();
    id = e.getAttributeValue(XMLParser.ID);

    parseCoordinates(e);
  }

  private void parseCoordinates(Element element) {

    if (element.getName().equalsIgnoreCase(KMLCoordinates.COORDINATES)) {
      String value = element.getTextTrim();
      StringTokenizer st1 = new StringTokenizer(value, KMLCoordinates.POINT_SEPARATORS);
      int count = st1.countTokens();
      //            System.out.println(count + " tokens");

      point2Ds = new ArrayList<Point2D>(count);
      for (int i = 0; i < count; i++) {
        String line = st1.nextToken();
        StringTokenizer st2 = new StringTokenizer(line, KMLCoordinates.SEPARATOR);
        if (st2.countTokens() < 2 || st2.countTokens() > 3)
          throw new IllegalArgumentException(
              "All KML coordinates must contain (X,Y) or (X,Y,Z) values.  Error in element '"
                  + line
                  + "'");
        final double x = Double.valueOf(st2.nextToken());
        final double y = Double.valueOf(st2.nextToken());
        point2Ds.add(new Point2D.Double(x, y));
      }
      convertPointsToArrays();
      length = point2Ds.size() - 1;
    } else {
      for (Object child : element.getChildren()) {

        if (child instanceof Element) {
          parseCoordinates((Element) child);
        }
      }
    }
  }

  Shape getShape() {
    GeneralPath path = new GeneralPath();

    List<Point2D> points = point2Ds;
    path.moveTo((float) points.get(0).getX(), (float) points.get(0).getY());

    for (int i = 1; i < points.size(); i++) {
      path.lineTo((float) points.get(i).getX(), (float) points.get(i).getY());
    }
    path.closePath();
    return path;
  }

  private void convertPointsToArrays() {
    final int length = point2Ds.size();
    if (x == null || x.length != length) {
      x = new double[length];
      y = new double[length];
    }
    Iterator<Point2D> it = point2Ds.iterator();
    for (int i = 0; i < length; i++) {
      final Point2D point = it.next();
      x[i] = point.getX();
      y[i] = point.getY();
    }
  }

  public void addPoint2D(Point2D point2D) {
    if (point2Ds.size() == 0) point2Ds.add(point2D);
    else if (point2Ds.size() == 1) {
      point2Ds.add(point2D);
      point2Ds.add(point2Ds.get(0));
    } else {
      Point2D last = point2Ds.remove(point2Ds.size() - 1);
      point2Ds.add(point2D);
      if (!last.equals(point2D)) point2Ds.add(last);
    }
    convertPointsToArrays();
    length = point2Ds.size() - 1;
  }

  public Point2D getPoint2D(int x) {
    if (x > length + 1) {
      throw new RuntimeException("Polygon only has length" + length);
    } else {
      return point2Ds.get(x);
    }
  }

  public boolean containsPoint2D(Point2D Point2D) {

    final double inX = Point2D.getX();
    final double inY = Point2D.getY();
    boolean contains = false;

    // Take a horizontal ray from (inX,inY) to the right.
    // If ray across the polygon edges an odd # of times, the point is inside.
    for (int i = 0, j = length - 1; i < length; j = i++) {
      if ((((y[i] <= inY) && (inY < y[j])) || ((y[j] <= inY) && (inY < y[i])))
          && (inX < (x[j] - x[i]) * (inY - y[i]) / (y[j] - y[i]) + x[i])) contains = !contains;
    }
    return contains;
  }

  public boolean bordersPoint2D(Point2D Point2D) {
    boolean borders = false;

    Iterator<Point2D> it = point2Ds.iterator();
    for (int i = 0; i < length; i++) {
      Point2D point = it.next();
      if (point.equals(Point2D)) {
        borders = true;
      }
    }
    return borders;
  }

  public void setFillValue(double value) {
    fillValue = value;
  }

  public double getFillValue() {
    return fillValue;
  }

  public double getLength() {
    return length;
  }

  //    public boolean containsPoint2D(Point2D Point2D) { // this takes 3 times as long as the above
  // code, why???
  //
  //        final double inX = Point2D.getX();
  //        final double inY = Point2D.getY();
  //        boolean contains = false;
  //
  //        // Take a horizontal ray from (inX,inY) to the right.
  //        // If ray across the polygon edges an odd # of times, the Point2D is inside.
  //
  //        final Point2D end   = point2Ds.get(length-1); // assumes closed
  //        double xi = end.getX();
  //        double yi = end.getY();
  //
  //        Iterator<Point2D> listIterator = point2Ds.iterator();
  //
  //        for(int i=0; i<length; i++) {
  //
  //            final double xj = xi;
  //            final double yj = yi;
  //
  //            final Point2D next = listIterator.next();
  //            xi = next.getX();
  //            yi = next.getY();
  //
  //            if ((((yi <= inY) && (inY < yj)) ||
  //                    ((yj <= inY) && (inY < yi))) &&
  //                    (inX < (xj - xi) * (inY - yi) / (yj - yi) + xi))
  //                contains = !contains;
  //        }
  //        return contains;
  //    }

  private enum Side {
    left,
    right,
    top,
    bottom
  }

  public Polygon2D clip(Rectangle2D boundingBox) {

    LinkedList<Point2D> clippedPolygon = new LinkedList<Point2D>();

    Point2D p; // current Point2D
    Point2D p2; // next Point2D

    // make copy of original polygon to work with
    LinkedList<Point2D> workPoly = new LinkedList<Point2D>(point2Ds);

    // loop through all for clipping edges
    for (Side side : Side.values()) {
      clippedPolygon.clear();
      for (int i = 0; i < workPoly.size() - 1; i++) {
        p = workPoly.get(i);
        p2 = workPoly.get(i + 1);
        if (isInsideClip(p, side, boundingBox)) {
          if (isInsideClip(p2, side, boundingBox))
            // here both point2Ds are inside the clipping window so add the second one
            clippedPolygon.add(p2);
          else
            // the seond Point2D is outside so add the intersection Point2D
            clippedPolygon.add(intersectionPoint2D(side, p, p2, boundingBox));
        } else {
          // so first Point2D is outside the window here
          if (isInsideClip(p2, side, boundingBox)) {
            // the following Point2D is inside so add the insection Point2D and also p2
            clippedPolygon.add(intersectionPoint2D(side, p, p2, boundingBox));
            clippedPolygon.add(p2);
          }
        }
      }
      // make sure that first and last element are the same, we want a closed polygon
      if (!clippedPolygon.getFirst().equals(clippedPolygon.getLast()))
        clippedPolygon.add(clippedPolygon.getFirst());
      // we have to keep on working with our new clipped polygon
      workPoly = new LinkedList<Point2D>(clippedPolygon);
    }
    return new Polygon2D(clippedPolygon, true);
  }

  public void transformByMapping(CartogramMapping mapping) {
    for (int i = 0; i < length + 1; i++) {
      point2Ds.set(i, mapping.map(point2Ds.get(i)));
    }
    convertPointsToArrays();
  }

  public void swapXYs() {
    for (int i = 0; i < length + 1; i++) {
      point2Ds.set(i, new Point2D.Double(point2Ds.get(i).getY(), point2Ds.get(i).getX()));
    }
    convertPointsToArrays();
  }

  public void rescale(
      double longMin,
      double longwidth,
      double gridXSize,
      double latMax,
      double latwidth,
      double gridYSize) {
    for (int i = 0; i < length + 1; i++) {
      point2Ds.set(
          i,
          new Point2D.Double(
              ((point2Ds.get(i).getX() - longMin) * (gridXSize / longwidth)),
              ((latMax - point2Ds.get(i).getY()) * (gridYSize / latwidth))));
    }
    convertPointsToArrays();
  }

  public void rescaleToPositiveCoordinates() {

    double[][] xyMinMax = getXYMinMax();
    double shiftX = 0;
    double shiftY = 0;

    if (xyMinMax[0][0] < 0) {
      shiftX = -xyMinMax[0][0];
    }
    if (xyMinMax[1][0] < 0) {
      shiftY = -xyMinMax[1][0];
    }

    if ((shiftX < 0) || (shiftY < 0)) {
      for (int i = 0; i < length + 1; i++) {
        point2Ds.set(
            i,
            new Point2D.Double(point2Ds.get(i).getX() + shiftX, point2Ds.get(i).getY() + shiftY));
      }
      convertPointsToArrays();
    }
  }

  public double[][] getXYMinMax() {

    int[] indicesX = new int[x.length];
    int[] indicesY = new int[y.length];
    HeapSort.sort(x, indicesX);
    HeapSort.sort(y, indicesY);

    double[][] returnArray = new double[2][2];
    returnArray[0][0] = x[indicesX[0]];
    returnArray[0][1] = x[indicesX[indicesX.length - 1]];
    returnArray[1][0] = y[indicesY[0]];
    returnArray[1][1] = y[indicesY[indicesY.length - 1]];

    return returnArray;
  }

  // Here is a formula for the area of a polygon with vertices {(xk,yk): k = 1,...,n}:
  //   Area = 1/2 [(x1*y2 - x2*y1) + (x2*y3 - x3*y2) + ... + (xn*y1 - x1*yn)].
  //   This formula appears in an Article by Gil Strang of MIT
  //   on p. 253 of the March 1993 issue of The American Mathematical Monthly, with the note that it
  // is
  //   "known, but not well known". There is also a very brief discussion of proofs and other
  // references,
  //   including an article by Bart Braden of Northern Kentucky U., a known Mathematica enthusiast.
  public double calculateArea() {

    //        rescaleToPositiveCoordinates();

    double area = 0;
    // we can implement it like this because the polygon is closed (point2D.get(0) =
    // point2D.get(length + 1)
    for (int i = 0; i < length; i++) {
      area += (x[i] * y[i + 1] - x[i + 1] * y[i]);
    }

    return (Math.abs(area / 2));
  }

  public Point2D getCentroid() {

    //        rescaleToPositiveCoordinates();

    Point2D centroid = new Point2D.Double();
    double area = calculateArea();
    double cx = 0, cy = 0;

    double factor;

    // we can implement it like this because the polygon is closed (point2D.get(0) =
    // point2D.get(length + 1)
    for (int i = 0; i < length; i++) {
      factor = (x[i] * y[i + 1] - x[i + 1] * y[i]);
      cx += (x[i] * x[i + 1]) * factor;
      cy += (y[i] * y[i + 1]) * factor;
    }
    double constant = 1 / (area * 6);
    cx *= constant;
    cy *= constant;
    centroid.setLocation(cx, cy);
    System.out.println("centroid = " + cx + "," + cy);
    return centroid;
  }

  private static LinkedList<Point2D> getCirclePoints(
      double centerLat, double centerLong, int numberOfPoints, double radius) {

    LinkedList<Point2D> Point2Ds = new LinkedList<Point2D>();

    double lat1, long1;
    double d_rad;
    double delta_pts;
    double radial, lat_rad, dlon_rad, lon_rad;

    // convert coordinates to radians
    lat1 = Math.toRadians(centerLat);
    long1 = Math.toRadians(centerLong);

    // radius is in meters
    d_rad = radius / 6378137;

    // loop through the array and write points
    for (int i = 0; i <= numberOfPoints; i++) {
      delta_pts = 360 / (double) numberOfPoints;
      radial = Math.toRadians((double) i * delta_pts);

      // This algorithm is limited to distances such that dlon < pi/2
      lat_rad =
          Math.asin(
              Math.sin(lat1) * Math.cos(d_rad)
                  + Math.cos(lat1) * Math.sin(d_rad) * Math.cos(radial));
      dlon_rad =
          Math.atan2(
              Math.sin(radial) * Math.sin(d_rad) * Math.cos(lat1),
              Math.cos(d_rad) - Math.sin(lat1) * Math.sin(lat_rad));
      lon_rad = ((long1 + dlon_rad + Math.PI) % (2 * Math.PI)) - Math.PI;

      Point2Ds.add(new Point2D.Double(Math.toDegrees(lat_rad), Math.toDegrees(lon_rad)));
    }
    return Point2Ds;
  }

  private static boolean isInsideClip(Point2D p, Side side, Rectangle2D boundingBox) {
    if (side == Side.top) return (p.getY() <= boundingBox.getMaxY());
    else if (side == Side.bottom) return (p.getY() >= boundingBox.getMinY());
    else if (side == Side.left) return (p.getX() >= boundingBox.getMinX());
    else if (side == Side.right) return (p.getX() <= boundingBox.getMaxX());
    else throw new RuntimeException("Error in Polygon");
  }

  private static Point2D intersectionPoint2D(
      Side side, Point2D p1, Point2D p2, Rectangle2D boundingBox) {

    if (side == Side.top) {
      final double topEdge = boundingBox.getMaxY();
      return new Point2D.Double(
          p1.getX() + (topEdge - p1.getY()) * (p2.getX() - p1.getX()) / (p2.getY() - p1.getY()),
          topEdge);
    } else if (side == Side.bottom) {
      final double bottomEdge = boundingBox.getMinY();
      return new Point2D.Double(
          p1.getX() + (bottomEdge - p1.getY()) * (p2.getX() - p1.getX()) / (p2.getY() - p1.getY()),
          bottomEdge);
    } else if (side == Side.right) {
      final double rightEdge = boundingBox.getMaxX();
      return new Point2D.Double(
          rightEdge,
          p1.getY() + (rightEdge - p1.getX()) * (p2.getY() - p1.getY()) / (p2.getX() - p1.getX()));
    } else if (side == Side.left) {
      final double leftEdge = boundingBox.getMinX();
      return new Point2D.Double(
          leftEdge,
          p1.getY() + (leftEdge - p1.getX()) * (p2.getY() - p1.getY()) / (p2.getX() - p1.getX()));
    }
    return null;
  }

  public String toString() {
    StringBuffer sb = new StringBuffer();
    sb.append(POLYGON).append("[\n");
    for (Point2D pt : point2Ds) {
      sb.append("\t");
      sb.append(pt);
      sb.append("\n");
    }
    sb.append("]");
    return sb.toString();
  }

  public static void readKMLElement(Element element, List<Polygon2D> polygons) {

    if (element.getName().equalsIgnoreCase(POLYGON)) {
      Polygon2D polygon = new Polygon2D(element);
      polygons.add(polygon);
    } else {
      for (Object child : element.getChildren()) {

        if (child instanceof Element) {
          readKMLElement((Element) child, polygons);
        }
      }
    }
  }

  public static List<Polygon2D> readKMLFile(String fileName) {

    List<Polygon2D> polygons = new ArrayList<Polygon2D>();
    try {

      SAXBuilder builder = new SAXBuilder();
      builder.setValidation(false);
      builder.setIgnoringElementContentWhitespace(true);
      Document doc = builder.build(new File(fileName));
      Element root = doc.getRootElement();
      if (!root.getName().equalsIgnoreCase("KML")) throw new RuntimeException("Not a KML file");

      readKMLElement(root, polygons);

    } catch (IOException e) {
      e.printStackTrace();
    } catch (JDOMException e) {
      e.printStackTrace();
    }
    return polygons;
  }
  //
  //    public Element toXML() {
  //        return new KMLCoordinates(x,y).toXML();
  //    }

  public static XMLObjectParser PARSER =
      new AbstractXMLObjectParser() {

        public String getParserName() {
          return POLYGON;
        }

        public Object parseXMLObject(XMLObject xo) throws XMLParseException {

          LinkedList<Point2D> Point2Ds = new LinkedList<Point2D>();
          boolean closed;
          Polygon2D polygon;

          if (xo.getChild(Polygon2D.class) != null) { // This is a regular polygon

            polygon = (Polygon2D) xo.getChild(Polygon2D.class);

          } else { // This is an arbitrary polygon

            KMLCoordinates coordinates = (KMLCoordinates) xo.getChild(KMLCoordinates.class);
            closed = xo.getAttribute(CLOSED, false);

            if ((!closed && coordinates.length < 3) || (closed && coordinates.length < 4))
              throw new XMLParseException(
                  "Insufficient point2Ds in polygon '"
                      + xo.getId()
                      + "' to define a polygon in 2D");

            for (int i = 0; i < coordinates.length; i++)
              Point2Ds.add(new Point2D.Double(coordinates.x[i], coordinates.y[i]));

            polygon = new Polygon2D(Point2Ds, closed);
          }

          polygon.setFillValue(xo.getAttribute(FILL_VALUE, 0.0));

          return polygon;
        }

        // ************************************************************************
        // AbstractXMLObjectParser implementation
        // ************************************************************************

        public String getParserDescription() {
          return "This element represents a polygon.";
        }

        public Class getReturnType() {
          return Polygon2D.class;
        }

        public XMLSyntaxRule[] getSyntaxRules() {
          return rules;
        }

        private XMLSyntaxRule[] rules =
            new XMLSyntaxRule[] {
              new XORRule(new ElementRule(KMLCoordinates.class), new ElementRule(Polygon2D.class)),
              AttributeRule.newBooleanRule(CLOSED, true),
              AttributeRule.newDoubleRule(FILL_VALUE, true),
            };
      };

  public static XMLObjectParser CIRCLE_PARSER =
      new AbstractXMLObjectParser() {

        public String getParserName() {
          return CIRCLE;
        }

        public Object parseXMLObject(XMLObject xo) throws XMLParseException {

          double latitude = xo.getDoubleAttribute(LATITUDE);
          double longitude = xo.getDoubleAttribute(LONGITUDE);
          double radius = xo.getDoubleAttribute(RADIUS);
          int num = xo.getAttribute(NUMBER_OF_POINTS, 50); // default = 50

          LinkedList<Point2D> Point2Ds = getCirclePoints(latitude, longitude, num, radius);

          return new Polygon2D(Point2Ds, true);
        }

        // ************************************************************************
        // AbstractXMLObjectParser implementation
        // ************************************************************************

        public String getParserDescription() {
          return "This element represents a regular circle polygon.";
        }

        public Class getReturnType() {
          return Polygon2D.class;
        }

        public XMLSyntaxRule[] getSyntaxRules() {
          return rules;
        }

        private XMLSyntaxRule[] rules =
            new XMLSyntaxRule[] {
              AttributeRule.newDoubleRule(LATITUDE),
              AttributeRule.newDoubleRule(LONGITUDE),
              AttributeRule.newDoubleRule(RADIUS),
              AttributeRule.newIntegerRule(NUMBER_OF_POINTS, true),
            };
      };

  public static void main(String[] args) {
    Polygon2D polygon = new Polygon2D();
    polygon.addPoint2D(new Point2D.Double(-10, -10));
    polygon.addPoint2D(new Point2D.Double(-10, 50));
    polygon.addPoint2D(new Point2D.Double(10, 50));
    polygon.addPoint2D(new Point2D.Double(10, -10));
    System.out.println(polygon);
    System.out.println("");

    Point2D pt = new Point2D.Double(0, 0);
    System.out.println("polygon contains " + pt + ": " + polygon.containsPoint2D(pt));
    pt = new Point2D.Double(100, 100);
    System.out.println("polygon contains " + pt + ": " + polygon.containsPoint2D(pt));
    System.out.println("");

    Rectangle2D boundingBox =
        new Rectangle2D.Double(0, 0, 100, 100); // defines lower-left corner and width/height
    System.out.println(boundingBox);
    Polygon2D myClip = polygon.clip(boundingBox);
    System.out.println(myClip);
  }

  protected List<Point2D> point2Ds;

  protected int length;
  private double fillValue;
  private String id;

  protected double[] x;
  protected double[] y;
}
/** @author Wai Lok Sibon Li */
public class ContinuousBranchRatesParser extends AbstractXMLObjectParser {

  public static final String CONTINUOUS_BRANCH_RATES = "continuousBranchRates";
  public static final String DISTRIBUTION = "distribution";
  // public static final String RATE_CATEGORIES = "rateCategories";
  public static final String RATE_CATEGORY_QUANTILES = "rateCategoryQuantiles";
  public static final String SINGLE_ROOT_RATE = "singleRootRate";
  // public static final String OVERSAMPLING = "overSampling";
  public static final String NORMALIZE = "normalize";
  public static final String NORMALIZE_BRANCH_RATE_TO = "normalizeBranchRateTo";
  // public static final String NORMALIZED_MEAN = "normalizedMean";

  public String getParserName() {
    return CONTINUOUS_BRANCH_RATES;
  }

  public Object parseXMLObject(XMLObject xo) throws XMLParseException {

    // final int overSampling = xo.getAttribute(OVERSAMPLING, 1);
    final boolean normalize = xo.getAttribute(NORMALIZE, false);
    final double normalizeBranchRateTo = xo.getAttribute(NORMALIZE_BRANCH_RATE_TO, Double.NaN);

    TreeModel tree = (TreeModel) xo.getChild(TreeModel.class);
    ParametricDistributionModel distributionModel =
        (ParametricDistributionModel) xo.getElementFirstChild(DISTRIBUTION);

    // Parameter rateCategoryParameter = (Parameter) xo.getElementFirstChild(RATE_CATEGORIES);

    Parameter rateCategoryQuantilesParameter =
        (Parameter) xo.getElementFirstChild(RATE_CATEGORY_QUANTILES);

    Logger.getLogger("dr.evomodel").info("Using continuous relaxed clock model.");
    // Logger.getLogger("dr.evomodel").info("  over sampling = " + overSampling);
    Logger.getLogger("dr.evomodel")
        .info("  parametric model = " + distributionModel.getModelName());
    // Logger.getLogger("dr.evomodel").info("   rate categories = " +
    // rateCategoryParameter.getDimension());
    Logger.getLogger("dr.evomodel")
        .info("   rate categories = " + rateCategoryQuantilesParameter.getDimension());
    if (normalize) {
      Logger.getLogger("dr.evomodel")
          .info("   mean rate is normalized to " + normalizeBranchRateTo);
    }

    if (xo.hasAttribute(SINGLE_ROOT_RATE)) {
      // singleRootRate = xo.getBooleanAttribute(SINGLE_ROOT_RATE);
      Logger.getLogger("dr.evomodel").warning("   WARNING: single root rate is not implemented!");
    }

    /* if (xo.hasAttribute(NORMALIZED_MEAN)) {
        dbr.setNormalizedMean(xo.getDoubleAttribute(NORMALIZED_MEAN));
    }*/

    return new ContinuousBranchRates(
        tree, /*rateCategoryParameter, */
        rateCategoryQuantilesParameter,
        distributionModel, /*overSampling,*/
        normalize,
        normalizeBranchRateTo);
  }

  // ************************************************************************
  // AbstractXMLObjectParser implementation
  // ************************************************************************

  public String getParserDescription() {
    return "This element returns a continuous relaxed clock model."
        + "The branch rates are drawn from a continuous parametric distribution.";
  }

  public Class getReturnType() {
    return ContinuousBranchRates.class;
  }

  public XMLSyntaxRule[] getSyntaxRules() {
    return rules;
  }

  private XMLSyntaxRule[] rules =
      new XMLSyntaxRule[] {
        AttributeRule.newBooleanRule(
            SINGLE_ROOT_RATE,
            true,
            "Whether only a single rate should be used for the two children branches of the root"),
        // AttributeRule.newDoubleRule(NORMALIZED_MEAN, true, "The mean rate to constrain branch
        // rates to once branch lengths are taken into account"),
        // AttributeRule.newIntegerRule(OVERSAMPLING, true, "The integer factor for oversampling the
        // distribution model (1 means no oversampling)"),
        AttributeRule.newBooleanRule(
            NORMALIZE, true, "Whether the mean rate has to be normalized to a particular value"),
        AttributeRule.newDoubleRule(
            NORMALIZE_BRANCH_RATE_TO, true, "The mean rate to normalize to, if normalizing"),
        new ElementRule(TreeModel.class),
        new ElementRule(
            DISTRIBUTION,
            ParametricDistributionModel.class,
            "The distribution model for rates among branches",
            false),
        /*new ElementRule(RATE_CATEGORIES, Parameter.class, "The rate categories parameter", false),      */
        new ElementRule(RATE_CATEGORY_QUANTILES, Parameter.class, "The quantiles for", false),
      };
}
/** @author Marc Suchard */
public class MultivariateDistributionLikelihood extends AbstractDistributionLikelihood {

  public static final String MVN_PRIOR = "multivariateNormalPrior";
  public static final String MVN_MEAN = "meanParameter";
  public static final String MVN_PRECISION = "precisionParameter";
  public static final String MVN_CV = "coefficientOfVariation";
  public static final String WISHART_PRIOR = "multivariateWishartPrior";
  public static final String INV_WISHART_PRIOR = "multivariateInverseWishartPrior";
  public static final String DIRICHLET_PRIOR = "dirichletPrior";
  public static final String DF = "df";
  public static final String SCALE_MATRIX = "scaleMatrix";
  public static final String MVGAMMA_PRIOR = "multivariateGammaPrior";
  public static final String MVGAMMA_SHAPE = "shapeParameter";
  public static final String MVGAMMA_SCALE = "scaleParameter";
  public static final String COUNTS = "countsParameter";
  public static final String NON_INFORMATIVE = "nonInformative";
  public static final String MULTIVARIATE_LIKELIHOOD = "multivariateDistributionLikelihood";
  public static final String DATA_AS_MATRIX = "dataAsMatrix";
  // public static final String TREE_TRAIT = "treeTraitNormalDistribution";
  public static final String TREE_TRAIT = "treeTraitNormalDistributionLikelihood";
  public static final String TREE_TRAIT_NORMAL = "treeTraitNormalDistribution";
  public static final String ROOT_VALUE = "rootValue";
  public static final String CONDITION = "conditionOnRoot";

  public static final String DATA = "data";

  private final MultivariateDistribution distribution;
  private final Transform[] transforms;

  public MultivariateDistributionLikelihood(
      String name, ParametricMultivariateDistributionModel model) {
    this(name, model, null);
  }

  public MultivariateDistributionLikelihood(
      String name, ParametricMultivariateDistributionModel model, Transform[] transforms) {
    super(model);
    this.distribution = model;
    this.transforms = transforms;
  }

  public MultivariateDistributionLikelihood(String name, MultivariateDistribution distribution) {
    this(name, distribution, null);
  }

  public MultivariateDistributionLikelihood(
      String name, MultivariateDistribution distribution, Transform[] transforms) {
    super(new DefaultModel(name));
    this.distribution = distribution;
    this.transforms = transforms;
  }

  public MultivariateDistributionLikelihood(MultivariateDistribution distribution) {
    this(distribution, null);
  }

  public MultivariateDistributionLikelihood(
      MultivariateDistribution distribution, Transform[] transforms) {
    this(distribution.getType(), distribution, transforms);
  }

  public String toString() {
    return getClass().getName() + "(" + getLogLikelihood() + ")";
  }

  public double calculateLogLikelihood() {
    double logL = 0.0;

    for (Attribute<double[]> data : dataList) {
      double[] x = data.getAttributeValue();
      if (transforms != null) {
        double[] y = new double[x.length];
        for (int i = 0; i < x.length; ++i) {
          logL += transforms[i].getLogJacobian(x[i]);
          y[i] = transforms[i].transform(x[i]);
        }
        logL += distribution.logPdf(y);
      } else {
        logL += distribution.logPdf(x);
      }
    }
    return logL;
  }

  @Override
  public void addData(Attribute<double[]> data) {
    super.addData(data);

    if (data instanceof Variable && getModel() instanceof DefaultModel) {
      ((DefaultModel) getModel()).addVariable((Variable) data);
    }
  }

  public MultivariateDistribution getDistribution() {
    return distribution;
  }

  public static Transform[] parseListOfTransforms(XMLObject xo, int maxDim)
      throws XMLParseException {
    Transform[] transforms = null;

    boolean anyTransforms = false;
    for (int i = 0; i < xo.getChildCount(); ++i) {
      if (xo.getChild(i) instanceof Transform.ParsedTransform) {
        Transform.ParsedTransform t = (Transform.ParsedTransform) xo.getChild(i);
        if (transforms == null) {
          transforms = Transform.Util.getListOfNoTransforms(maxDim);
        }

        t.end = Math.max(t.end, maxDim);
        if (t.start < 0 || t.end < 0 || t.start > t.end) {
          throw new XMLParseException("Invalid bounds for transform in " + xo.getId());
        }
        for (int j = t.start; j < t.end; j += t.every) {
          transforms[j] = t.transform;
          anyTransforms = true;
        }
      }
    }
    if (anyTransforms) {
      StringBuilder sb =
          new StringBuilder("Using distributional transforms in " + xo.getId() + "\n");
      for (int i = 0; i < transforms.length; ++i) {
        if (transforms[i] != Transform.NONE) {
          sb.append("\t")
              .append(transforms[i].getTransformName())
              .append(" on index ")
              .append(i + 1)
              .append("\n");
        }
      }
      sb.append("Please cite:\n").append(Citable.Utils.getCitationString(Transform.LOG));
      Logger.getLogger("dr.utils.Transform").info(sb.toString());
    }
    return transforms;
  }

  public static XMLObjectParser DIRICHLET_PRIOR_PARSER =
      new AbstractXMLObjectParser() {

        public String getParserName() {
          return DIRICHLET_PRIOR;
        }

        public Object parseXMLObject(XMLObject xo) throws XMLParseException {

          XMLObject cxo = xo.getChild(COUNTS);
          Parameter counts = (Parameter) cxo.getChild(Parameter.class);

          DirichletDistribution dirichlet = new DirichletDistribution(counts.getParameterValues());

          MultivariateDistributionLikelihood likelihood =
              new MultivariateDistributionLikelihood(dirichlet);

          cxo = xo.getChild(DATA);
          for (int j = 0; j < cxo.getChildCount(); j++) {
            if (cxo.getChild(j) instanceof Parameter) {
              likelihood.addData((Parameter) cxo.getChild(j));
            } else {
              throw new XMLParseException(
                  "illegal element in " + xo.getName() + " element " + cxo.getName());
            }
          }

          return likelihood;
        }

        public XMLSyntaxRule[] getSyntaxRules() {
          return rules;
        }

        private final XMLSyntaxRule[] rules = {
          new ElementRule(COUNTS, new XMLSyntaxRule[] {new ElementRule(Parameter.class)}),
          new ElementRule(
              DATA, new XMLSyntaxRule[] {new ElementRule(Parameter.class)}, 1, Integer.MAX_VALUE),
        };

        public String getParserDescription() {
          return "Calculates the likelihood of some data under a Dirichlet distribution.";
        }

        public Class getReturnType() {
          return Likelihood.class;
        }
      };

  public static XMLObjectParser INV_WISHART_PRIOR_PARSER =
      new AbstractXMLObjectParser() {

        public String getParserName() {
          return INV_WISHART_PRIOR;
        }

        public Object parseXMLObject(XMLObject xo) throws XMLParseException {

          int df = xo.getIntegerAttribute(DF);

          XMLObject cxo = xo.getChild(SCALE_MATRIX);
          MatrixParameter scaleMatrix = (MatrixParameter) cxo.getChild(MatrixParameter.class);
          InverseWishartDistribution invWishart =
              new InverseWishartDistribution(df, scaleMatrix.getParameterAsMatrix());

          MultivariateDistributionLikelihood likelihood =
              new MultivariateDistributionLikelihood(invWishart);

          cxo = xo.getChild(DATA);
          for (int j = 0; j < cxo.getChildCount(); j++) {
            if (cxo.getChild(j) instanceof MatrixParameter) {
              likelihood.addData((MatrixParameter) cxo.getChild(j));
            } else {
              throw new XMLParseException(
                  "illegal element in " + xo.getName() + " element " + cxo.getName());
            }
          }

          return likelihood;
        }

        public XMLSyntaxRule[] getSyntaxRules() {
          return rules;
        }

        private final XMLSyntaxRule[] rules = {
          AttributeRule.newDoubleRule(DF),
          new ElementRule(
              SCALE_MATRIX, new XMLSyntaxRule[] {new ElementRule(MatrixParameter.class)}),
        };

        public String getParserDescription() {
          return "Calculates the likelihood of some data under an Inverse-Wishart distribution.";
        }

        public Class getReturnType() {
          return Likelihood.class;
        }
      };

  public static XMLObjectParser WISHART_PRIOR_PARSER =
      new AbstractXMLObjectParser() {

        public String getParserName() {
          return WISHART_PRIOR;
        }

        public Object parseXMLObject(XMLObject xo) throws XMLParseException {

          MultivariateDistributionLikelihood likelihood;

          if (xo.hasAttribute(NON_INFORMATIVE) && xo.getBooleanAttribute(NON_INFORMATIVE)) {
            // Make non-informative settings
            XMLObject cxo = xo.getChild(DATA);
            int dim = ((MatrixParameter) cxo.getChild(0)).getColumnDimension();
            likelihood = new MultivariateDistributionLikelihood(new WishartDistribution(dim));
          } else {
            if (!xo.hasAttribute(DF) || !xo.hasChildNamed(SCALE_MATRIX)) {
              throw new XMLParseException("Must specify both a df and scaleMatrix");
            }

            double df = xo.getDoubleAttribute(DF);

            XMLObject cxo = xo.getChild(SCALE_MATRIX);
            MatrixParameter scaleMatrix = (MatrixParameter) cxo.getChild(MatrixParameter.class);

            likelihood =
                new MultivariateDistributionLikelihood(
                    new WishartDistribution(df, scaleMatrix.getParameterAsMatrix()));
          }

          XMLObject cxo = xo.getChild(DATA);
          for (int j = 0; j < cxo.getChildCount(); j++) {
            if (cxo.getChild(j) instanceof MatrixParameter) {
              likelihood.addData((MatrixParameter) cxo.getChild(j));
            } else {
              throw new XMLParseException(
                  "illegal element in " + xo.getName() + " element " + cxo.getName());
            }
          }

          return likelihood;
        }

        public XMLSyntaxRule[] getSyntaxRules() {
          return rules;
        }

        private final XMLSyntaxRule[] rules;

        {
          rules =
              new XMLSyntaxRule[] {
                AttributeRule.newBooleanRule(NON_INFORMATIVE, true),
                AttributeRule.newDoubleRule(DF, true),
                new ElementRule(
                    SCALE_MATRIX,
                    new XMLSyntaxRule[] {new ElementRule(MatrixParameter.class)},
                    true),
                new ElementRule(
                    DATA,
                    new XMLSyntaxRule[] {
                      new ElementRule(MatrixParameter.class, 1, Integer.MAX_VALUE)
                    })
              };
        }

        public String getParserDescription() {
          return "Calculates the likelihood of some data under a Wishart distribution.";
        }

        public Class getReturnType() {
          return Likelihood.class;
        }
      };

  public static XMLObjectParser MULTIVARIATE_LIKELIHOOD_PARSER =
      new AbstractXMLObjectParser() {

        public String getParserName() {
          return MULTIVARIATE_LIKELIHOOD;
        }

        public Object parseXMLObject(XMLObject xo) throws XMLParseException {

          XMLObject cxo = xo.getChild(DistributionLikelihoodParser.DISTRIBUTION);
          ParametricMultivariateDistributionModel distribution =
              (ParametricMultivariateDistributionModel)
                  cxo.getChild(ParametricMultivariateDistributionModel.class);

          // Parse transforms here
          int maxDim = distribution.getMean().length;
          Transform[] transforms = parseListOfTransforms(xo, maxDim);

          MultivariateDistributionLikelihood likelihood =
              new MultivariateDistributionLikelihood(xo.getId(), distribution, transforms);

          boolean dataAsMatrix = xo.getAttribute(DATA_AS_MATRIX, false);

          cxo = xo.getChild(DATA);
          if (cxo != null) {
            for (int j = 0; j < cxo.getChildCount(); j++) {
              if (cxo.getChild(j) instanceof Parameter) {
                Parameter data = (Parameter) cxo.getChild(j);
                if (data instanceof MatrixParameter) {
                  MatrixParameter matrix = (MatrixParameter) data;
                  if (dataAsMatrix) {
                    likelihood.addData(matrix);
                  } else {
                    if (matrix.getParameter(0).getDimension() != distribution.getMean().length)
                      throw new XMLParseException(
                          "dim("
                              + data.getStatisticName()
                              + ") = "
                              + matrix.getParameter(0).getDimension()
                              + " is not equal to dim("
                              + distribution.getType()
                              + ") = "
                              + distribution.getMean().length
                              + " in "
                              + xo.getName()
                              + "element");

                    for (int i = 0; i < matrix.getParameterCount(); i++) {
                      likelihood.addData(matrix.getParameter(i));
                    }
                  }
                } else {
                  if (data.getDimension() != distribution.getMean().length)
                    throw new XMLParseException(
                        "dim("
                            + data.getStatisticName()
                            + ") = "
                            + data.getDimension()
                            + " is not equal to dim("
                            + distribution.getType()
                            + ") = "
                            + distribution.getMean().length
                            + " in "
                            + xo.getName()
                            + "element");
                  likelihood.addData(data);
                }
              } else {
                throw new XMLParseException("illegal element in " + xo.getName() + " element");
              }
            }
          }

          return likelihood;
        }

        public XMLSyntaxRule[] getSyntaxRules() {
          return rules;
        }

        private final XMLSyntaxRule[] rules = {
          new ElementRule(
              DistributionLikelihoodParser.DISTRIBUTION,
              new XMLSyntaxRule[] {new ElementRule(ParametricMultivariateDistributionModel.class)}),
          AttributeRule.newBooleanRule(DATA_AS_MATRIX, true),
          new ElementRule(Transform.ParsedTransform.class, 0, Integer.MAX_VALUE),
          new ElementRule(
              DATA,
              new XMLSyntaxRule[] {new ElementRule(Parameter.class, 1, Integer.MAX_VALUE)},
              true)
        };

        public String getParserDescription() {
          return "Calculates the likelihood of some data under a given multivariate distribution.";
        }

        public Class getReturnType() {
          return MultivariateDistributionLikelihood.class;
        }
      };

  public static XMLObjectParser MVN_PRIOR_PARSER =
      new AbstractXMLObjectParser() {

        public String getParserName() {
          return MVN_PRIOR;
        }

        public Object parseXMLObject(XMLObject xo) throws XMLParseException {

          XMLObject cxo = xo.getChild(MVN_MEAN);
          Parameter mean = (Parameter) cxo.getChild(Parameter.class);

          cxo = xo.getChild(MVN_PRECISION);
          MatrixParameter precision = (MatrixParameter) cxo.getChild(MatrixParameter.class);

          if (mean.getDimension() != precision.getRowDimension()
              || mean.getDimension() != precision.getColumnDimension())
            throw new XMLParseException(
                "Mean and precision have wrong dimensions in " + xo.getName() + " element");

          Transform[] transforms = parseListOfTransforms(xo, mean.getDimension());

          MultivariateDistributionLikelihood likelihood =
              new MultivariateDistributionLikelihood(
                  new MultivariateNormalDistribution(
                      mean.getParameterValues(), precision.getParameterAsMatrix()),
                  transforms);
          cxo = xo.getChild(DATA);
          if (cxo != null) {
            for (int j = 0; j < cxo.getChildCount(); j++) {
              if (cxo.getChild(j) instanceof Parameter) {
                Parameter data = (Parameter) cxo.getChild(j);
                if (data instanceof MatrixParameter) {
                  MatrixParameter matrix = (MatrixParameter) data;
                  if (matrix.getParameter(0).getDimension() != mean.getDimension())
                    throw new XMLParseException(
                        "dim("
                            + data.getStatisticName()
                            + ") = "
                            + matrix.getParameter(0).getDimension()
                            + " is not equal to dim("
                            + mean.getStatisticName()
                            + ") = "
                            + mean.getDimension()
                            + " in "
                            + xo.getName()
                            + "element");

                  for (int i = 0; i < matrix.getParameterCount(); i++) {
                    likelihood.addData(matrix.getParameter(i));
                  }
                } else {
                  if (data.getDimension() != mean.getDimension())
                    throw new XMLParseException(
                        "dim("
                            + data.getStatisticName()
                            + ") = "
                            + data.getDimension()
                            + " is not equal to dim("
                            + mean.getStatisticName()
                            + ") = "
                            + mean.getDimension()
                            + " in "
                            + xo.getName()
                            + "element");
                  likelihood.addData(data);
                }
              } else {
                throw new XMLParseException("illegal element in " + xo.getName() + " element");
              }
            }
          }

          return likelihood;
        }

        public XMLSyntaxRule[] getSyntaxRules() {
          return rules;
        }

        private final XMLSyntaxRule[] rules = {
          new ElementRule(MVN_MEAN, new XMLSyntaxRule[] {new ElementRule(Parameter.class)}),
          new ElementRule(
              MVN_PRECISION, new XMLSyntaxRule[] {new ElementRule(MatrixParameter.class)}),
          new ElementRule(Transform.ParsedTransform.class, 0, Integer.MAX_VALUE),
          new ElementRule(
              DATA,
              new XMLSyntaxRule[] {new ElementRule(Parameter.class, 1, Integer.MAX_VALUE)},
              true)
        };

        public String getParserDescription() {
          return "Calculates the likelihood of some data under a given multivariate-normal distribution.";
        }

        public Class getReturnType() {
          return MultivariateDistributionLikelihood.class;
        }
      };

  public static XMLObjectParser MVGAMMA_PRIOR_PARSER =
      new AbstractXMLObjectParser() {

        public String getParserName() {
          return MVGAMMA_PRIOR;
        }

        public Object parseXMLObject(XMLObject xo) throws XMLParseException {

          double[] shape;
          double[] scale;

          if (xo.hasChildNamed(MVGAMMA_SHAPE)) {

            XMLObject cxo = xo.getChild(MVGAMMA_SHAPE);
            shape = ((Parameter) cxo.getChild(Parameter.class)).getParameterValues();

            cxo = xo.getChild(MVGAMMA_SCALE);
            scale = ((Parameter) cxo.getChild(Parameter.class)).getParameterValues();

            if (shape.length != scale.length)
              throw new XMLParseException(
                  "Shape and scale have wrong dimensions in " + xo.getName() + " element");

          } else {

            XMLObject cxo = xo.getChild(MVN_MEAN);
            double[] mean = ((Parameter) cxo.getChild(Parameter.class)).getParameterValues();

            cxo = xo.getChild(MVN_CV);
            double[] cv = ((Parameter) cxo.getChild(Parameter.class)).getParameterValues();

            if (mean.length != cv.length)
              throw new XMLParseException(
                  "Mean and CV have wrong dimensions in " + xo.getName() + " element");

            final int dim = mean.length;
            shape = new double[dim];
            scale = new double[dim];

            for (int i = 0; i < dim; i++) {
              double c2 = cv[i] * cv[i];
              shape[i] = 1.0 / c2;
              scale[i] = c2 * mean[i];
            }
          }

          MultivariateDistributionLikelihood likelihood =
              new MultivariateDistributionLikelihood(
                  new MultivariateGammaDistribution(shape, scale));
          XMLObject cxo = xo.getChild(DATA);
          for (int j = 0; j < cxo.getChildCount(); j++) {
            if (cxo.getChild(j) instanceof Parameter) {
              Parameter data = (Parameter) cxo.getChild(j);
              likelihood.addData(data);
              if (data.getDimension() != shape.length)
                throw new XMLParseException(
                    "dim("
                        + data.getStatisticName()
                        + ") != "
                        + shape.length
                        + " in "
                        + xo.getName()
                        + "element");
            } else {
              throw new XMLParseException("illegal element in " + xo.getName() + " element");
            }
          }
          return likelihood;
        }

        public XMLSyntaxRule[] getSyntaxRules() {
          return rules;
        }

        private final XMLSyntaxRule[] rules = {
          new XORRule(
              new ElementRule(
                  MVGAMMA_SHAPE, new XMLSyntaxRule[] {new ElementRule(Parameter.class)}),
              new ElementRule(MVN_MEAN, new XMLSyntaxRule[] {new ElementRule(Parameter.class)})),
          new XORRule(
              new ElementRule(
                  MVGAMMA_SCALE, new XMLSyntaxRule[] {new ElementRule(Parameter.class)}),
              new ElementRule(MVN_CV, new XMLSyntaxRule[] {new ElementRule(Parameter.class)})),
          new ElementRule(
              DATA, new XMLSyntaxRule[] {new ElementRule(Parameter.class, 1, Integer.MAX_VALUE)})
        };

        public String getParserDescription() {
          return "Calculates the likelihood of some data under a given multivariate-gamma distribution.";
        }

        public Class getReturnType() {
          return MultivariateDistributionLikelihood.class;
        }
      };

  public static XMLObjectParser TREE_TRAIT_MODEL =
      new AbstractXMLObjectParser() {

        public String getParserName() {
          return TREE_TRAIT_NORMAL;
        }

        public Object parseXMLObject(XMLObject xo) throws XMLParseException {

          boolean conditionOnRoot = xo.getAttribute(CONDITION, false);

          FullyConjugateMultivariateTraitLikelihood traitModel =
              (FullyConjugateMultivariateTraitLikelihood)
                  xo.getChild(FullyConjugateMultivariateTraitLikelihood.class);

          TreeTraitNormalDistributionModel treeTraitModel;

          if (xo.getChild(ROOT_VALUE) != null) {
            XMLObject cxo = xo.getChild(ROOT_VALUE);
            Parameter rootValue = (Parameter) cxo.getChild(Parameter.class);
            treeTraitModel =
                new TreeTraitNormalDistributionModel(traitModel, rootValue, conditionOnRoot);
          } else {
            treeTraitModel = new TreeTraitNormalDistributionModel(traitModel, conditionOnRoot);
          }
          return treeTraitModel;
        }

        public XMLSyntaxRule[] getSyntaxRules() {
          return rules;
        }

        private final XMLSyntaxRule[] rules = {
          AttributeRule.newBooleanRule(CONDITION, true),
          new ElementRule(FullyConjugateMultivariateTraitLikelihood.class)
        };

        public String getParserDescription() {
          return "Parses TreeTraitNormalDistributionModel";
        }

        public Class getReturnType() {
          return TreeTraitNormalDistributionModel.class;
        }
      };

  public static XMLObjectParser TREE_TRAIT_DISTRIBUTION =
      new AbstractXMLObjectParser() {

        public String getParserName() {
          return TREE_TRAIT;
        }

        public Object parseXMLObject(XMLObject xo) throws XMLParseException {
          /*
          boolean conditionOnRoot = xo.getAttribute(CONDITION, false);

          FullyConjugateMultivariateTraitLikelihood traitModel = (FullyConjugateMultivariateTraitLikelihood)
                  xo.getChild(FullyConjugateMultivariateTraitLikelihood.class);
          */

          TreeTraitNormalDistributionModel treeTraitModel =
              (TreeTraitNormalDistributionModel)
                  xo.getChild(TreeTraitNormalDistributionModel.class);

          MultivariateDistributionLikelihood likelihood =
              new MultivariateDistributionLikelihood(
                  //  new TreeTraitNormalDistributionModel(traitModel, conditionOnRoot)
                  treeTraitModel);

          XMLObject cxo = xo.getChild(DATA);
          for (int j = 0; j < cxo.getChildCount(); j++) {
            if (cxo.getChild(j) instanceof Parameter) {
              likelihood.addData((Parameter) cxo.getChild(j));
            } else {
              throw new XMLParseException(
                  "illegal element in " + xo.getName() + " element " + cxo.getName());
            }
          }
          return likelihood;
        }

        public XMLSyntaxRule[] getSyntaxRules() {
          return rules;
        }

        private final XMLSyntaxRule[] rules = {
          //  AttributeRule.newBooleanRule(CONDITION, true),
          //   new ElementRule(FullyConjugateMultivariateTraitLikelihood.class),
          new ElementRule(TreeTraitNormalDistributionModel.class),
          new ElementRule(
              DATA, new XMLSyntaxRule[] {new ElementRule(Parameter.class, 1, Integer.MAX_VALUE)})
        };

        public String getParserDescription() {
          return "Calculates the likelihood of some data under a given multivariate-gamma distribution.";
        }

        public Class getReturnType() {
          return MultivariateDistributionLikelihood.class;
        }
      };
}
/**
 * A Gibbs operator for allocation of items to clusters under a distance dependent Chinese
 * restaurant process.
 *
 * @author Gabriela Cybis
 * @author Marc Suchard
 */
public class DistanceDependentCRPGibbsOperator extends SimpleMCMCOperator implements GibbsOperator {

  public static final String DDCRP_GIBBS_OPERATOR = "distanceDependentCRPGibbsOperator";

  private final Parameter chiParameter;
  private final double[][] depMatrix;
  public NPAntigenicLikelihood modelLikelihood;
  private Parameter links = null;
  private Parameter assignments = null;
  //  double[][] x;
  double k0;
  double v0;
  double[] m;
  double[][] T0Inv;
  double logDetT0;

  public DistanceDependentCRPGibbsOperator(
      Parameter links,
      Parameter assignments,
      Parameter chiParameter,
      NPAntigenicLikelihood Likelihood,
      double weight) {

    this.links = links;
    this.assignments = assignments;
    this.modelLikelihood = Likelihood;
    this.chiParameter = chiParameter;
    this.depMatrix = Likelihood.getLogDepMatrix();

    for (int i = 0; i < links.getDimension(); i++) {
      links.setParameterValue(i, i);
    }

    setWeight(weight);

    // double[][] x=modelLikelihood.getData();
    // modelLikelihood.printInformtion(x[0][0]);

    this.m = new double[2];
    m[0] = modelLikelihood.priorMean.getParameterValue(0);
    m[1] = modelLikelihood.priorMean.getParameterValue(1);

    this.v0 = 2;

    this.k0 =
        modelLikelihood.priorPrec.getParameterValue(0)
            / modelLikelihood.clusterPrec.getParameterValue(0);

    this.T0Inv = new double[2][2];
    T0Inv[0][0] = v0 / modelLikelihood.clusterPrec.getParameterValue(0);
    T0Inv[1][1] = v0 / modelLikelihood.clusterPrec.getParameterValue(0);
    T0Inv[1][0] = 0.0;
    T0Inv[0][1] = 0.0;

    this.logDetT0 = -Math.log(T0Inv[0][0] * T0Inv[1][1]);
  }

  /** @return the parameter this operator acts on. */
  public Parameter getParameter() {
    return (Parameter) links;
  }

  /**
   * @return the Variable this operator acts on.
   *     <p>public Variable getVariable() { return clusteringParameter; }
   */
  /** change the parameter and return the hastings ratio. */
  public final double doOperation() {

    int index = MathUtils.nextInt(links.getDimension());

    int oldGroup = (int) assignments.getParameterValue(index);

    /*
     * Set index customer link to index and all connected to it to a new assignment (min value empty)
     */
    int minEmp = minEmpty(modelLikelihood.getLogLikelihoodsVector());
    links.setParameterValue(index, index);
    int[] visited = connected(index, links);

    int ii = 0;
    while (visited[ii] != 0) {
      assignments.setParameterValue(visited[ii] - 1, minEmp);
      ii++;
    }

    /*
     * Adjust likvector for group separated
     */

    modelLikelihood.setLogLikelihoodsVector(oldGroup, getLogLikGroup(oldGroup));

    modelLikelihood.setLogLikelihoodsVector(minEmp, getLogLikGroup(minEmp));

    int maxFull = maxFull(modelLikelihood.getLogLikelihoodsVector());

    double[] liks = modelLikelihood.getLogLikelihoodsVector();
    /*
     * computing likelihoods of joint groups
     */

    double[] crossedLiks = new double[maxFull + 1];

    for (int ll = 0; ll < maxFull + 1; ll++) {
      if (ll != minEmp) {
        crossedLiks[ll] = getLogLik2Group(ll, minEmp);
      }
    }

    /*
     * Add logPrior
     */
    double[] logP = new double[links.getDimension()];

    for (int jj = 0; jj < links.getDimension(); jj++) {
      logP[jj] += depMatrix[index][jj];

      int n = (int) assignments.getParameterValue(jj);
      if (n != minEmp) {
        logP[jj] += crossedLiks[n] - liks[n] - liks[minEmp];
      }
    }

    logP[index] = Math.log(chiParameter.getParameterValue(0));

    /*
     * possibilidade de mandar p zero as probs muito pequenas
     */

    /*
     *  Gibbs sampling
     */

    this.rescale(logP); // Improve numerical stability
    this.exp(logP); // Transform back to probability-scale

    int k = MathUtils.randomChoicePDF(logP);

    links.setParameterValue(index, k);

    int newGroup = (int) assignments.getParameterValue(k);
    ii = 0;
    while (visited[ii] != 0) {
      assignments.setParameterValue(visited[ii] - 1, newGroup);
      ii++;
    }

    /*
     * updating conditional likelihood vector
     */
    modelLikelihood.setLogLikelihoodsVector(newGroup, getLogLikGroup(newGroup));
    if (newGroup != minEmp) {
      modelLikelihood.setLogLikelihoodsVector(minEmp, 0);
    }

    sampleMeans(maxFull);

    return 0.0;
  }

  /*
   * find min Empty
   */

  public int minEmpty(double[] logLikVector) {
    int isEmpty = 0;
    int i = 0;
    while (isEmpty == 0) {
      if (logLikVector[i] == 0) {
        isEmpty = 1;
      } else {
        if (i == logLikVector.length - 1) {
          isEmpty = 1;
        }
        i++;
      }
    }
    return i;
  }

  /*
   * find max Full
   */

  public int maxFull(double[] logLikVector) {
    int isEmpty = 1;
    int i = logLikVector.length - 1;
    while (isEmpty == 1) {
      if (logLikVector[i] != 0) {
        isEmpty = 0;
      } else {
        i--;
      }
    }
    return i;
  }
  /*
   * find customers connected to i
   */

  public int[] connected(int i, Parameter clusteringParameter) {
    int n = clusteringParameter.getDimension();
    int[] visited = new int[n + 1];
    visited[0] = i + 1;
    int tv = 1;

    for (int j = 0; j < n; j++) {
      if (visited[j] != 0) {
        int curr = visited[j] - 1;

        /*look forward
         */

        int forward = (int) clusteringParameter.getParameterValue(curr);
        visited[tv] = forward + 1;
        tv++;
        // Check to see if is isn't already on the list

        for (int ii = 0; ii < tv - 1; ii++) {
          if (visited[ii] == forward + 1) {
            tv--;
            visited[tv] = 0;
          }
        }

        /*look back
         */
        for (int jj = 0; jj < n; jj++) {
          if ((int) clusteringParameter.getParameterValue(jj) == curr) {
            visited[tv] = jj + 1;
            tv++;

            for (int ii = 0; ii < tv - 1; ii++) {
              if (visited[ii] == jj + 1) {
                tv--;
                visited[tv] = 0;
              }
            }
          }
        }
      }
    }
    return visited;
  }

  private void printInformtion(Parameter par) {
    StringBuffer sb = new StringBuffer("parameter \n");
    for (int j = 0; j < par.getDimension(); j++) {
      sb.append(par.getParameterValue(j));
    }

    Logger.getLogger("dr.evomodel").info(sb.toString());
  };

  public double getLogLikGroup(int groupNumber) {
    double L = 0.0;

    int ngroup = 0;
    for (int i = 0; i < assignments.getDimension(); i++) {
      if ((int) assignments.getParameterValue(i) == groupNumber) {
        ngroup++;
      }
    }

    if (ngroup != 0) {
      double[][] group = new double[ngroup][2];

      double mean[] = new double[2];

      int count = 0;
      for (int i = 0; i < assignments.getDimension(); i++) {
        if ((int) assignments.getParameterValue(i) == groupNumber) {
          group[count][0] = modelLikelihood.getData()[i][0];
          group[count][1] = modelLikelihood.getData()[i][1];
          mean[0] += group[count][0];
          mean[1] += group[count][1];
          count++;
        }
      }

      mean[0] /= ngroup;
      mean[1] /= ngroup;

      double kn = k0 + ngroup;
      double vn = v0 + ngroup;

      double[][] sumdif = new double[2][2];

      for (int i = 0; i < ngroup; i++) {
        sumdif[0][0] += (group[i][0] - mean[0]) * (group[i][0] - mean[0]);
        sumdif[0][1] += (group[i][0] - mean[0]) * (group[i][1] - mean[1]);
        sumdif[1][0] += (group[i][0] - mean[0]) * (group[i][1] - mean[1]);
        sumdif[1][1] += (group[i][1] - mean[1]) * (group[i][1] - mean[1]);
      }

      double[][] TnInv = new double[2][2];
      TnInv[0][0] =
          T0Inv[0][0] + ngroup * (k0 / kn) * (mean[0] - m[0]) * (mean[0] - m[0]) + sumdif[0][0];
      TnInv[0][1] =
          T0Inv[0][1] + ngroup * (k0 / kn) * (mean[1] - m[1]) * (mean[0] - m[0]) + sumdif[0][1];
      TnInv[1][0] =
          T0Inv[1][0] + ngroup * (k0 / kn) * (mean[0] - m[0]) * (mean[1] - m[1]) + sumdif[1][0];
      TnInv[1][1] =
          T0Inv[1][1] + ngroup * (k0 / kn) * (mean[1] - m[1]) * (mean[1] - m[1]) + sumdif[1][1];

      double logDetTn = -Math.log(TnInv[0][0] * TnInv[1][1] - TnInv[0][1] * TnInv[1][0]);

      L += -(ngroup) * Math.log(Math.PI);
      L += Math.log(k0) - Math.log(kn);
      L += (vn / 2) * logDetTn - (v0 / 2) * logDetT0;
      L += GammaFunction.lnGamma(vn / 2) + GammaFunction.lnGamma((vn / 2) - 0.5);
      L += -GammaFunction.lnGamma(v0 / 2) - GammaFunction.lnGamma((v0 / 2) - 0.5);
    }
    return L;
  }

  /*  OLD

  public double getLogLikGroup(int groupNumber){
        double L =0.0;


        int ngroup=0;
        for (int i=0;i<assignments.getDimension(); i++){
            if((int) assignments.getParameterValue(i) == groupNumber){
                ngroup++;}}


        if (ngroup != 0){
            double[] group = new double[2*ngroup];

            int count = 0;
            for (int i=0;i<assignments.getDimension(); i++){
                if((int) assignments.getParameterValue(i) == groupNumber){
                    group[count] = modelLikelihood.getData()[i][0];
                    group[ngroup+count] = modelLikelihood.getData()[i][1];
                    count+=1;}}


            double[][] var = new double[2*ngroup][2*ngroup];
            double[] mean = new double[2*ngroup];

            double m0 = modelLikelihood.getPriorMean().getParameterValue(0);
            double m1 = modelLikelihood.getPriorMean().getParameterValue(1);
            double vp = modelLikelihood.getPriorPrec().getParameterValue(0);
            double vc = modelLikelihood.getClusterPrec().getParameterValue(0);


            for (int i=0; i<ngroup; i++){
                mean[i]=m0;
                mean[ngroup+i]=m1;


                for (int l=0;l<ngroup;l++){
                    var[i][ngroup+l]=0;
                    var[ngroup+i][l]=0;

                    if (l==i){var[i][l]= vp+ vc;
                        var[ngroup+i][ngroup+l]= vp+ vc;}

                    else { var[i][l] = vp;
                        var[ngroup+i][ngroup+l]= vp;}
                }
            }




            double[][] precision = new SymmetricMatrix(var).inverse().toComponents();
            L = new MultivariateNormalDistribution(mean, precision).logPdf(group);

        }



        return L;

    }


   */

  public double getLogLik2Group(int group1, int group2) {
    double L = 0.0;

    int ngroup1 = 0;
    for (int i = 0; i < assignments.getDimension(); i++) {
      if ((int) assignments.getParameterValue(i) == group1) {
        ngroup1++;
      }
    }

    int ngroup2 = 0;
    for (int i = 0; i < assignments.getDimension(); i++) {
      if ((int) assignments.getParameterValue(i) == group2) {
        ngroup2++;
      }
    }

    int ngroup = (ngroup1 + ngroup2);

    if (ngroup != 0) {
      double[][] group = new double[ngroup][2];

      double mean[] = new double[2];

      int count = 0;
      for (int i = 0; i < assignments.getDimension(); i++) {
        if ((int) assignments.getParameterValue(i) == group1) {
          group[count][0] = modelLikelihood.getData()[i][0];
          group[count][1] = modelLikelihood.getData()[i][1];
          mean[0] += group[count][0];
          mean[1] += group[count][1];
          count += 1;
        }
      }

      for (int i = 0; i < assignments.getDimension(); i++) {
        if ((int) assignments.getParameterValue(i) == group2) {
          group[count][0] = modelLikelihood.getData()[i][0];
          group[count][1] = modelLikelihood.getData()[i][1];
          mean[0] += group[count][0];
          mean[1] += group[count][1];
          count += 1;
        }
      }

      mean[0] /= ngroup;
      mean[1] /= ngroup;

      double kn = k0 + ngroup;
      double vn = v0 + ngroup;

      double[][] sumdif = new double[2][2];

      for (int i = 0; i < ngroup; i++) {
        sumdif[0][0] += (group[i][0] - mean[0]) * (group[i][0] - mean[0]);
        sumdif[0][1] += (group[i][0] - mean[0]) * (group[i][1] - mean[1]);
        sumdif[1][0] += (group[i][0] - mean[0]) * (group[i][1] - mean[1]);
        sumdif[1][1] += (group[i][1] - mean[1]) * (group[i][1] - mean[1]);
      }

      double[][] TnInv = new double[2][2];
      TnInv[0][0] =
          T0Inv[0][0] + ngroup * (k0 / kn) * (mean[0] - m[0]) * (mean[0] - m[0]) + sumdif[0][0];
      TnInv[0][1] =
          T0Inv[0][1] + ngroup * (k0 / kn) * (mean[1] - m[1]) * (mean[0] - m[0]) + sumdif[0][1];
      TnInv[1][0] =
          T0Inv[1][0] + ngroup * (k0 / kn) * (mean[0] - m[0]) * (mean[1] - m[1]) + sumdif[1][0];
      TnInv[1][1] =
          T0Inv[1][1] + ngroup * (k0 / kn) * (mean[1] - m[1]) * (mean[1] - m[1]) + sumdif[1][1];

      double logDetTn = -Math.log(TnInv[0][0] * TnInv[1][1] - TnInv[0][1] * TnInv[1][0]);

      L += -(ngroup) * Math.log(Math.PI);
      L += Math.log(k0) - Math.log(kn);
      L += (vn / 2) * logDetTn - (v0 / 2) * logDetT0;
      L += GammaFunction.lnGamma(vn / 2) + GammaFunction.lnGamma((vn / 2) - 0.5);
      L += -GammaFunction.lnGamma(v0 / 2) - GammaFunction.lnGamma((v0 / 2) - 0.5);
    }

    return L;
  }

  /*public double getLogLik2Group(int group1, int group2){
          double L =0.0;


          int ngroup1=0;
          for (int i=0;i<assignments.getDimension(); i++){
              if((int) assignments.getParameterValue(i) == group1 ){
                  ngroup1++;}}

          int ngroup2=0;
          for (int i=0;i<assignments.getDimension(); i++){
              if((int) assignments.getParameterValue(i) == group2 ){
                  ngroup2++;}}

          int ngroup = (ngroup1+ngroup2);

          if (ngroup != 0){
              double[] group = new double[2*ngroup];

              int count = 0;
              for (int i=0;i<assignments.getDimension(); i++){
                  if((int) assignments.getParameterValue(i) == group1 ){
                      group[count] = modelLikelihood.getData()[i][0];
                      group[count+ngroup] = modelLikelihood.getData()[i][1];
                      count+=1;}}

              for (int i=0;i<assignments.getDimension(); i++){
                  if((int) assignments.getParameterValue(i) == group2 ){
                      group[count] = modelLikelihood.getData()[i][0];
                      group[count+ngroup] = modelLikelihood.getData()[i][1];
                      count+=1;}}




              double[][] var = new double[2*ngroup][2*ngroup];
              double[] mean = new double[2*ngroup];

              double m0 = modelLikelihood.getPriorMean().getParameterValue(0);
              double m1 = modelLikelihood.getPriorMean().getParameterValue(1);
              double vp = modelLikelihood.getPriorPrec().getParameterValue(0);
              double vc = modelLikelihood.getClusterPrec().getParameterValue(0);


              for (int i=0; i<ngroup; i++){
                  mean[i]=m0;
                  mean[i+ngroup]=m1;

                  for (int l=0;l<ngroup;l++){
                      var[i][ngroup+l]=0;
                      var[ngroup+i][l]=0;

                      if (l==i){var[i][l]= vp+ vc;
                          var[ngroup+i][ngroup+l]= vp+ vc;}

                      else { var[i][l] = vp;
                          var[ngroup+i][ngroup+l]= vp;}
                  }
              }


              double[][] precision = new SymmetricMatrix(var).inverse().toComponents();
              L = new MultivariateNormalDistribution(mean, precision).logPdf(group);

          }


          return L;

      }
  */

  public void sampleMeans(int maxFull) {

    double[][] means = new double[maxFull + 2][2];

    // sample mean vector for each cluster

    for (int i = 0; i < maxFull + 1; i++) {

      // Find all elements in cluster

      int ngroup = 0;
      for (int ii = 0; ii < assignments.getDimension(); ii++) {
        if ((int) assignments.getParameterValue(ii) == i) {
          ngroup++;
        }
      }

      if (ngroup != 0) {
        double[][] group = new double[ngroup][2];
        double[] groupMean = new double[2];

        int count = 0;
        for (int ii = 0; ii < assignments.getDimension(); ii++) {
          if ((int) assignments.getParameterValue(ii) == i) {
            group[count][0] = modelLikelihood.getData()[ii][0];
            group[count][1] = modelLikelihood.getData()[ii][1];
            groupMean[0] += group[count][0];
            groupMean[1] += group[count][1];
            count += 1;
          }
        }

        groupMean[0] /= ngroup;
        groupMean[1] /= ngroup;

        double kn = k0 + ngroup;
        double vn = v0 + ngroup;

        double[][] sumdif = new double[2][2];

        for (int jj = 0; jj < ngroup; jj++) {
          sumdif[0][0] += (group[jj][0] - groupMean[0]) * (group[jj][0] - groupMean[0]);
          sumdif[0][1] += (group[jj][0] - groupMean[0]) * (group[jj][1] - groupMean[1]);
          sumdif[1][0] += (group[jj][0] - groupMean[0]) * (group[jj][1] - groupMean[1]);
          sumdif[1][1] += (group[jj][1] - groupMean[1]) * (group[jj][1] - groupMean[1]);
        }

        double[][] TnInv = new double[2][2];
        TnInv[0][0] =
            T0Inv[0][0]
                + ngroup * (k0 / kn) * (groupMean[0] - m[0]) * (groupMean[0] - m[0])
                + sumdif[0][0];
        TnInv[0][1] =
            T0Inv[0][1]
                + ngroup * (k0 / kn) * (groupMean[1] - m[1]) * (groupMean[0] - m[0])
                + sumdif[0][1];
        TnInv[1][0] =
            T0Inv[1][0]
                + ngroup * (k0 / kn) * (groupMean[0] - m[0]) * (groupMean[1] - m[1])
                + sumdif[1][0];
        TnInv[1][1] =
            T0Inv[1][1]
                + ngroup * (k0 / kn) * (groupMean[1] - m[1]) * (groupMean[1] - m[1])
                + sumdif[1][1];

        Matrix Tn = new SymmetricMatrix(TnInv).inverse();

        double[] posteriorMean = new double[2];
        // compute posterior mean

        posteriorMean[0] = (k0 * m[0] + ngroup * groupMean[0]) / (k0 + ngroup);
        posteriorMean[1] = (k0 * m[1] + ngroup * groupMean[1]) / (k0 + ngroup);

        // compute posterior Precision
        double[][] posteriorPrecision =
            new WishartDistribution(vn, Tn.toComponents()).nextWishart();
        posteriorPrecision[0][0] *= kn;
        posteriorPrecision[1][0] *= kn;
        posteriorPrecision[0][1] *= kn;
        posteriorPrecision[1][1] *= kn;

        double[] sample =
            new MultivariateNormalDistribution(posteriorMean, posteriorPrecision)
                .nextMultivariateNormal();
        means[i][0] = sample[0];
        means[i][1] = sample[1];
      }
    }

    // Fill in cluster means for each observation

    for (int j = 0; j < assignments.getDimension(); j++) {
      double[] group = new double[2];
      group[0] = means[(int) assignments.getParameterValue(j)][0];
      group[1] = means[(int) assignments.getParameterValue(j)][1];

      modelLikelihood.setMeans(j, group);
    }
  }

  private void exp(double[] logX) {
    for (int i = 0; i < logX.length; ++i) {
      logX[i] = Math.exp(logX[i]);
      //  if(logX[i]<1E-5){logX[i]=0;}
    }
  }

  private void rescale(double[] logX) {
    double max = this.max(logX);
    for (int i = 0; i < logX.length; ++i) {
      logX[i] -= max;
    }
  }

  private double max(double[] x) {
    double max = x[0];
    for (double xi : x) {
      if (xi > max) {
        max = xi;
      }
    }
    return max;
  }

  // MCMCOperator INTERFACE
  public final String getOperatorName() {
    return DDCRP_GIBBS_OPERATOR;
  }

  public final void optimize(double targetProb) {

    throw new RuntimeException("This operator cannot be optimized!");
  }

  public boolean isOptimizing() {
    return false;
  }

  public void setOptimizing(boolean opt) {
    throw new RuntimeException("This operator cannot be optimized!");
  }

  public double getMinimumAcceptanceLevel() {
    return 0.1;
  }

  public double getMaximumAcceptanceLevel() {
    return 0.4;
  }

  public double getMinimumGoodAcceptanceLevel() {
    return 0.20;
  }

  public double getMaximumGoodAcceptanceLevel() {
    return 0.30;
  }

  public String getPerformanceSuggestion() {
    if (Utils.getAcceptanceProbability(this) < getMinimumAcceptanceLevel()) {
      return "";
    } else if (Utils.getAcceptanceProbability(this) > getMaximumAcceptanceLevel()) {
      return "";
    } else {
      return "";
    }
  }

  public static XMLObjectParser PARSER =
      new AbstractXMLObjectParser() {
        public static final String CHI = "chi";
        public static final String LIKELIHOOD = "likelihood";
        public static final String ASSIGNMENTS = "assignments";
        public static final String LINKS = "links";
        public static final String DEP_MATRIX = "depMatrix";

        public String getParserName() {
          return DDCRP_GIBBS_OPERATOR;
        }

        /* (non-Javadoc)
         * @see dr.xml.AbstractXMLObjectParser#parseXMLObject(dr.xml.XMLObject)
         */
        public Object parseXMLObject(XMLObject xo) throws XMLParseException {

          double weight = xo.getDoubleAttribute(MCMCOperator.WEIGHT);

          XMLObject cxo = xo.getChild(ASSIGNMENTS);
          Parameter assignments = (Parameter) cxo.getChild(Parameter.class);

          cxo = xo.getChild(LINKS);
          Parameter links = (Parameter) cxo.getChild(Parameter.class);

          cxo = xo.getChild(CHI);
          Parameter chiParameter = (Parameter) cxo.getChild(Parameter.class);

          cxo = xo.getChild(LIKELIHOOD);
          NPAntigenicLikelihood likelihood =
              (NPAntigenicLikelihood) cxo.getChild(NPAntigenicLikelihood.class);

          return new DistanceDependentCRPGibbsOperator(
              links, assignments, chiParameter, likelihood, weight);
        }

        // ************************************************************************
        // AbstractXMLObjectParser implementation
        // ************************************************************************

        public String getParserDescription() {
          return "An operator that picks a new allocation of an item to a cluster under the Dirichlet process.";
        }

        public Class getReturnType() {
          return DistanceDependentCRPGibbsOperator.class;
        }

        public XMLSyntaxRule[] getSyntaxRules() {
          return rules;
        }

        private final XMLSyntaxRule[] rules = {
          AttributeRule.newDoubleRule(MCMCOperator.WEIGHT),
          new ElementRule(
              CHI,
              new XMLSyntaxRule[] {
                new ElementRule(Parameter.class),
              }),
          new ElementRule(
              LIKELIHOOD,
              new XMLSyntaxRule[] {
                new ElementRule(Likelihood.class),
              },
              true),
          new ElementRule(ASSIGNMENTS, new XMLSyntaxRule[] {new ElementRule(Parameter.class)}),
          new ElementRule(LINKS, new XMLSyntaxRule[] {new ElementRule(Parameter.class)}),
        };
      };

  public int getStepCount() {
    return 1;
  }
}
/** Parses a GeneralSubstitutionModel or one of its more specific descendants. */
public class GeneralSubstitutionModelParser extends AbstractXMLObjectParser {

  public static final String GENERAL_SUBSTITUTION_MODEL = "generalSubstitutionModel";
  public static final String DATA_TYPE = "dataType";
  public static final String RATES = "rates";
  public static final String RELATIVE_TO = "relativeTo";
  public static final String FREQUENCIES = "frequencies";
  public static final String INDICATOR = "rateIndicator";

  public static final String SVS_GENERAL_SUBSTITUTION_MODEL = "svsGeneralSubstitutionModel";
  public static final String SVS_COMPLEX_SUBSTITUTION_MODEL = "svsComplexSubstitutionModel";

  public String getParserName() {
    return GENERAL_SUBSTITUTION_MODEL;
  }

  public String[] getParserNames() {
    return new String[] {
      getParserName(), SVS_GENERAL_SUBSTITUTION_MODEL, SVS_COMPLEX_SUBSTITUTION_MODEL
    };
  }

  public Object parseXMLObject(XMLObject xo) throws XMLParseException {

    Parameter ratesParameter = null;
    FrequencyModel freqModel = null;

    if (xo.hasChildNamed(FREQUENCIES)) {
      XMLObject cxo = xo.getChild(FREQUENCIES);
      freqModel = (FrequencyModel) cxo.getChild(FrequencyModel.class);
    }

    DataType dataType = DataTypeUtils.getDataType(xo);

    if (dataType == null) dataType = (DataType) xo.getChild(DataType.class);

    //        if (xo.hasAttribute(DataType.DATA_TYPE)) {
    //            String dataTypeStr = xo.getStringAttribute(DataType.DATA_TYPE);
    //            if (dataTypeStr.equals(Nucleotides.DESCRIPTION)) {
    //                dataType = Nucleotides.INSTANCE;
    //            } else if (dataTypeStr.equals(AminoAcids.DESCRIPTION)) {
    //                dataType = AminoAcids.INSTANCE;
    //            } else if (dataTypeStr.equals(Codons.DESCRIPTION)) {
    //                dataType = Codons.UNIVERSAL;
    //            } else if (dataTypeStr.equals(TwoStates.DESCRIPTION)) {
    //                dataType = TwoStates.INSTANCE;
    //            }
    //        }

    if (dataType == null) dataType = freqModel.getDataType();

    if (dataType != freqModel.getDataType()) {
      throw new XMLParseException(
          "Data type of "
              + getParserName()
              + " element does not match that of its frequencyModel.");
    }

    XMLObject cxo = xo.getChild(RATES);
    ratesParameter = (Parameter) cxo.getChild(Parameter.class);

    int states = dataType.getStateCount();
    Logger.getLogger("dr.evomodel")
        .info("  General Substitution Model (stateCount=" + states + ")");

    boolean hasRelativeRates =
        cxo.hasChildNamed(RELATIVE_TO)
            || (cxo.hasAttribute(RELATIVE_TO) && cxo.getIntegerAttribute(RELATIVE_TO) > 0);

    int nonReversibleRateCount = ((dataType.getStateCount() - 1) * dataType.getStateCount());
    int reversibleRateCount = (nonReversibleRateCount / 2);

    boolean isNonReversible = ratesParameter.getDimension() == nonReversibleRateCount;
    boolean hasIndicator = xo.hasChildNamed(INDICATOR);

    if (!hasRelativeRates) {
      Parameter indicatorParameter = null;

      if (ratesParameter.getDimension() != reversibleRateCount
          && ratesParameter.getDimension() != nonReversibleRateCount) {
        throw new XMLParseException(
            "Rates parameter in "
                + getParserName()
                + " element should have "
                + (reversibleRateCount)
                + " dimensions for reversible model or "
                + nonReversibleRateCount
                + " dimensions for non-reversible. "
                + "However parameter dimension is "
                + ratesParameter.getDimension());
      }

      if (hasIndicator) { // this is using BSSVS
        cxo = xo.getChild(INDICATOR);
        indicatorParameter = (Parameter) cxo.getChild(Parameter.class);

        if (indicatorParameter.getDimension() != ratesParameter.getDimension()) {
          throw new XMLParseException(
              "Rates and indicator parameters in "
                  + getParserName()
                  + " element must be the same dimension.");
        }

        boolean randomize =
            xo.getAttribute(
                dr.evomodelxml.substmodel.ComplexSubstitutionModelParser.RANDOMIZE, false);
        if (randomize) {
          BayesianStochasticSearchVariableSelection.Utils.randomize(
              indicatorParameter, dataType.getStateCount(), !isNonReversible);
        }
      }

      if (isNonReversible) {
        //                if (xo.hasChildNamed(ROOT_FREQ)) {
        //                    cxo = xo.getChild(ROOT_FREQ);
        //                    FrequencyModel rootFreq = (FrequencyModel)
        // cxo.getChild(FrequencyModel.class);
        //
        //                    if (dataType != rootFreq.getDataType()) {
        //                        throw new XMLParseException("Data type of " + getParserName() + "
        // element does not match that of its rootFrequencyModel.");
        //                    }
        //
        //                    Logger.getLogger("dr.evomodel").info("  Using BSSVS Complex
        // Substitution Model");
        //                    return new SVSComplexSubstitutionModel(getParserName(), dataType,
        // freqModel, ratesParameter, indicatorParameter);
        //
        //                } else {
        //                    throw new XMLParseException("Non-reversible model missing " +
        // ROOT_FREQ + " element");
        //                }
        Logger.getLogger("dr.evomodel").info("  Using BSSVS Complex Substitution Model");
        return new SVSComplexSubstitutionModel(
            getParserName(), dataType, freqModel, ratesParameter, indicatorParameter);
      } else {
        Logger.getLogger("dr.evomodel").info("  Using BSSVS General Substitution Model");
        return new SVSGeneralSubstitutionModel(
            getParserName(), dataType, freqModel, ratesParameter, indicatorParameter);
      }

    } else {
      // if we have relativeTo attribute then we use the old GeneralSubstitutionModel

      if (ratesParameter.getDimension() != reversibleRateCount - 1) {
        throw new XMLParseException(
            "Rates parameter in "
                + getParserName()
                + " element should have "
                + (reversibleRateCount - 1)
                + " dimensions. However parameter dimension is "
                + ratesParameter.getDimension());
      }

      int relativeTo = 0;
      if (hasRelativeRates) {
        relativeTo = cxo.getIntegerAttribute(RELATIVE_TO) - 1;
      }

      if (relativeTo < 0 || relativeTo >= reversibleRateCount) {
        throw new XMLParseException(RELATIVE_TO + " must be 1 or greater");
      } else {
        int t = relativeTo;
        int s = states - 1;
        int row = 0;
        while (t >= s) {
          t -= s;
          s -= 1;
          row += 1;
        }
        int col = t + row + 1;

        Logger.getLogger("dr.evomodel")
            .info("  Rates relative to " + dataType.getCode(row) + "<->" + dataType.getCode(col));
      }

      if (ratesParameter == null) {
        if (reversibleRateCount == 1) {
          // simplest model for binary traits...
        } else {
          throw new XMLParseException("No rates parameter found in " + getParserName());
        }
      }

      return new GeneralSubstitutionModel(
          getParserName(), dataType, freqModel, ratesParameter, relativeTo);
    }
  }

  // ************************************************************************
  // AbstractXMLObjectParser implementation
  // ************************************************************************

  public String getParserDescription() {
    return "A general reversible model of sequence substitution for any data type.";
  }

  public Class getReturnType() {
    return GeneralSubstitutionModelParser.class;
  }

  public XMLSyntaxRule[] getSyntaxRules() {
    return rules;
  }

  private final XMLSyntaxRule[] rules = {
    new XORRule(
        new StringAttributeRule(
            DataType.DATA_TYPE,
            "The type of sequence data",
            DataType.getRegisteredDataTypeNames(),
            false),
        new ElementRule(DataType.class),
        true),
    new ElementRule(FREQUENCIES, FrequencyModel.class),
    new ElementRule(RATES, new XMLSyntaxRule[] {new ElementRule(Parameter.class)}),
    new ElementRule(
        INDICATOR,
        new XMLSyntaxRule[] {
          new ElementRule(Parameter.class),
        },
        true),
    AttributeRule.newBooleanRule(ComplexSubstitutionModelParser.RANDOMIZE, true),
  };
}
public class BranchCategoriesParser extends AbstractXMLObjectParser {

  public static final String BRANCH_CATEGORIES = "branchCategories";
  public static final String CATEGORY = "category";
  public static final String ALLOCATION = "rateCategories";

  public static final String RANDOMIZE = "randomize";

  public String getParserName() {
    return BRANCH_CATEGORIES;
  }

  public Object parseXMLObject(XMLObject xo) throws XMLParseException {

    Parameter allocationParameter = (Parameter) xo.getElementFirstChild(ALLOCATION);
    CountableBranchCategoryProvider cladeModel;
    TreeModel treeModel = (TreeModel) xo.getChild(TreeModel.class);

    if (!xo.getAttribute(RANDOMIZE, true)) {
      CountableBranchCategoryProvider.CladeBranchCategoryModel cm =
          new CountableBranchCategoryProvider.CladeBranchCategoryModel(
              treeModel, allocationParameter);
      for (int i = 0; i < xo.getChildCount(); ++i) {
        if (xo.getChild(i) instanceof XMLObject) {
          XMLObject xoc = (XMLObject) xo.getChild(i);
          if (xoc.getName().equals(LocalClockModelParser.CLADE)) {
            TaxonList taxonList = (TaxonList) xoc.getChild(TaxonList.class);

            boolean includeStem = xoc.getAttribute(LocalClockModelParser.INCLUDE_STEM, false);
            boolean excludeClade = xoc.getAttribute(LocalClockModelParser.EXCLUDE_CLADE, false);
            int rateCategory = xoc.getIntegerAttribute(CATEGORY) - 1; // XML index-start = 1 not 0
            try {
              cm.setClade(taxonList, rateCategory, includeStem, excludeClade, false);
            } catch (Tree.MissingTaxonException e) {
              throw new XMLParseException(
                  "Unable to find taxon for clade in countable mixture model: " + e.getMessage());
            }
          } else if (xoc.getName().equals(LocalClockModelParser.TRUNK)) {
            TaxonList taxonList = (TaxonList) xoc.getChild(TaxonList.class);

            boolean includeStem = xoc.getAttribute(LocalClockModelParser.INCLUDE_STEM, false);
            boolean excludeClade = xoc.getAttribute(LocalClockModelParser.EXCLUDE_CLADE, false);
            int rateCategory = xoc.getIntegerAttribute(CATEGORY) - 1; // XML index-start = 1 not 0
            try {
              cm.setClade(taxonList, rateCategory, includeStem, excludeClade, true);
            } catch (Tree.MissingTaxonException e) {
              throw new XMLParseException(
                  "Unable to find taxon for trunk in countable mixture model: " + e.getMessage());
            }
          }
        }
      }
      cladeModel = cm;
    } else {
      CountableBranchCategoryProvider.IndependentBranchCategoryModel cm =
          new CountableBranchCategoryProvider.IndependentBranchCategoryModel(
              treeModel, allocationParameter);
      cm.randomize();
      cladeModel = cm;
    }

    return cladeModel;
  }

  // ************************************************************************
  // AbstractXMLObjectParser implementation
  // ************************************************************************

  public String getParserDescription() {
    return "This element provides a set of branch categories.";
  }

  public Class getReturnType() {
    return CountableBranchCategoryProvider.class;
  }

  public XMLSyntaxRule[] getSyntaxRules() {
    return rules;
  }

  private final XMLSyntaxRule[] rules = {
    new ElementRule(TreeModel.class),
    new ElementRule(ALLOCATION, Parameter.class, "Allocation parameter", false),
    AttributeRule.newBooleanRule(RANDOMIZE, true),
    new ElementRule(
        LocalClockModelParser.CLADE,
        new XMLSyntaxRule[] {
          //                            AttributeRule.newBooleanRule(RELATIVE, true),
          AttributeRule.newIntegerRule(CATEGORY, false),
          AttributeRule.newBooleanRule(
              LocalClockModelParser.INCLUDE_STEM,
              true,
              "determines whether or not the stem branch above this clade is included in the siteModel (default false)."),
          AttributeRule.newBooleanRule(
              LocalClockModelParser.EXCLUDE_CLADE,
              true,
              "determines whether to exclude actual branches of the clade from the siteModel (default false)."),
          new ElementRule(
              Taxa.class, "A set of taxa which defines a clade to apply a different site model to"),
        },
        0,
        Integer.MAX_VALUE),
    new ElementRule(
        LocalClockModelParser.TRUNK,
        new XMLSyntaxRule[] {
          //                            AttributeRule.newBooleanRule(RELATIVE, true),
          AttributeRule.newIntegerRule(CATEGORY, false),
          AttributeRule.newBooleanRule(
              LocalClockModelParser.INCLUDE_STEM,
              true,
              "determines whether or not the stem branch above this clade is included in the siteModel (default false)."),
          AttributeRule.newBooleanRule(
              LocalClockModelParser.EXCLUDE_CLADE,
              true,
              "determines whether to exclude actual branches of the clade from the siteModel (default false)."),
          new ElementRule(
              Taxa.class, "A set of taxa which defines a clade to apply a different site model to"),
        },
        0,
        Integer.MAX_VALUE),
  };
}