/**
   * Simulate next step for the given particle.
   *
   * @param nextPts
   * @param particleNum
   */
  public void simulateForwardStep(ArrayList<Point> nextPts, Particle thisParticle) {
    HashMap<Vertex, Point> thisObservedPosMap = new HashMap<Vertex, Point>();
    HashMap<Vertex, AntPath> thisAntPathMap = new HashMap<Vertex, AntPath>();

    thisParticle.currentFalsePositives = new ArrayList<Point>();
    for (Point pt : nextPts) {
      Vertex vx = new ObsVertex();
      thisObservedPosMap.put(vx, pt);
    }

    for (AntPath ap : thisParticle.getPaths()) {
      Vertex vx = new PathVertex();
      thisAntPathMap.put(vx, ap);
    }

    /*
     * Compute probability graph for this particle.
     */
    SimpleWeightedGraph<Vertex, DefaultWeightedComparableEdge> probabilityGraph =
        new SimpleWeightedGraph<Vertex, DefaultWeightedComparableEdge>(
            DefaultWeightedComparableEdge.class);
    ValueSortedMap<DefaultWeightedComparableEdge, Double> edgeMap =
        new ValueSortedMap<DefaultWeightedComparableEdge, Double>(true);
    computeLogProbabilityGraph(probabilityGraph, edgeMap, thisObservedPosMap, thisAntPathMap);

    //		displayLogProbabilityGraph(probabilityGraph,thisObservedPosMap,thisAntPathMap,vertexSums);

    /*
     * Generate new ant locations based on probability graph.  We do this by sampling the most likely event,
     * removing this event from the probability graph, updating the prob. graph, then repeating until all
     * observations/causes are accounted for.
     *
     * To do this (relatively) efficiently, we maintain a sorted hash-map of possible observation/cause pairs.
     * To avoid having to rescale the entire hash-map every step, we maintain the current sum of weights.
     *
     * We're also keeping track of the posterior log-probability of the simulated step.
     */

    double logprob = 0;
    int nfp = 0;
    int nfn = 0;

    int doOutput = 0;
    while (edgeMap.size() > 0) {

      /*
       * Sample an event from the probability graph.
       */

      DefaultWeightedComparableEdge event = sampleEdge(probabilityGraph, edgeMap);

      edgeMap.remove(event);
      double edgeWeight = probabilityGraph.getEdgeWeight(event);
      double thisLogProb = edgeWeight;
      logprob = logprob + thisLogProb;

      Vertex v1 = probabilityGraph.getEdgeSource(event);
      Vertex v2 = probabilityGraph.getEdgeTarget(event);

      if (doOutput > 0) {
        System.err.println("edgeWeight: " + edgeWeight);
        doOutput = outputInterestingStuff(v1, v2, probabilityGraph);
      }

      if (v1.getClass().equals(ObsVertex.class)) {
        assert (v2.getClass().equals(PathVertex.class));
        Vertex tv = v1;
        v1 = v2;
        v2 = tv;
      }

      /*
       * Update the probability graph.
       */
      if (!v1.equals(falsePositive)) {
        Set<DefaultWeightedComparableEdge> es = probabilityGraph.edgesOf(v1);
        for (DefaultWeightedComparableEdge ed : es) edgeMap.remove(ed);
        boolean tt = probabilityGraph.removeVertex(v1);
        assert (tt);
      } else {
        nfp += 1;
        thisParticle.currentFalsePositives.add(thisObservedPosMap.get(v2));
      }

      if (!v2.equals(falseNegative)) {
        Set<DefaultWeightedComparableEdge> es = probabilityGraph.edgesOf(v2);
        for (DefaultWeightedComparableEdge ed : es) edgeMap.remove(ed);
        boolean tt = probabilityGraph.removeVertex(v2);
        assert (tt);
      } else nfn += 1;

      // Utils.computeVertexSums(probabilityGraph,vertexSums);

      /*
       * Update AntPath trajectory.
       */

      if (!v1.equals(falsePositive)) {
        AntPath ap = thisAntPathMap.get(v1);
        assert (ap != null);
        Point obs = thisObservedPosMap.get(v2);

        Point newPos = new Point();
        double thislp = sampleConditionalPos(ap, obs, newPos) + thisLogProb;
        ap.updatePosition(newPos, obs, thislp);
      }

      /*
       * Compute likelihoods.
       */
    }
  }
  /**
   * Compute the log-probability that each observation arises for each ant. This is represented by a
   * simple weighted graph, where the absence of an edge represents zero probability that the given
   * observation was caused by the given ant. Currently the log-prob is set to be a truncated iid
   * Gaussian around an ant location.
   *
   * @param probabilityGraph The probability graph being constructed.
   * @param edgeMap A sorted map of the edges in probabilityGraph.
   * @param thisObservedPosMap Observed points along with index.
   * @param thisParticlePosMap Current particle locations.
   * @param obsSums
   * @param antSums
   * @return Sum of edge weights
   */
  private double computeLogProbabilityGraph(
      SimpleWeightedGraph<Vertex, DefaultWeightedComparableEdge> probabilityGraph,
      ValueSortedMap<DefaultWeightedComparableEdge, Double> edgeMap,
      HashMap<Vertex, Point> thisObservedPosMap,
      HashMap<Vertex, AntPath> thisParticlePathMap) {
    /** Initialize vertices. */
    edgeMap.clear();

    probabilityGraph.addVertex(falseNegative);
    probabilityGraph.addVertex(falsePositive);

    for (Vertex v : thisObservedPosMap.keySet()) {
      probabilityGraph.addVertex(v);
      DefaultWeightedComparableEdge edge = probabilityGraph.addEdge(v, falsePositive);
      probabilityGraph.setEdgeWeight(edge, falsePositiveLogProb);
      edgeMap.put(edge, falsePositiveLogProb);
    }
    for (Vertex v : thisParticlePathMap.keySet()) {
      probabilityGraph.addVertex(v);
      DefaultWeightedComparableEdge edge = probabilityGraph.addEdge(v, falseNegative);
      probabilityGraph.setEdgeWeight(edge, falseNegativeLogProb);
      edgeMap.put(edge, falseNegativeLogProb);
    }

    /** Compute probability of each observation given each ant path */
    for (Entry<Vertex, Point> obsEntry : thisObservedPosMap.entrySet()) {
      for (Entry<Vertex, AntPath> parEntry : thisParticlePathMap.entrySet()) {
        double logprob = observationLogProbGivenAntPath(obsEntry.getValue(), parEntry.getValue());
        if (logprob > logProbThreshold) {
          DefaultWeightedComparableEdge edge =
              probabilityGraph.addEdge(obsEntry.getKey(), parEntry.getKey());
          probabilityGraph.setEdgeWeight(edge, logprob);
          edgeMap.put(edge, logprob);
        }
      }
    }

    /** Adjust probabilities according to log-probability of an AntPath existing. */
    for (Entry<Vertex, AntPath> antEntry : thisParticlePathMap.entrySet()) {
      Set<DefaultWeightedComparableEdge> edge = probabilityGraph.edgesOf(antEntry.getKey());
      double antProb = antEntry.getValue().getCurrentLogProb();
      for (DefaultWeightedComparableEdge e : edge) {
        double ew = probabilityGraph.getEdgeWeight(e);
        probabilityGraph.setEdgeWeight(e, ew + antProb);
        // edgeMap.remove(edge);
        edgeMap.put(e, ew + antProb);
      }
    }

    /**
     * ANG -- to do Interaction effects: -- probability of false negative higher when multiple ants
     * in same area. however, there is a high probability that there will be SOME observation in the
     * area. -- probability of a false positive higher when there is one ant in a region. this is
     * because sometimes one ant is split into two.
     */

    /** compute vertex sums */
    // Utils.computeVertexSums(probabilityGraph);
    double totalLogProb = Utils.maxstar(edgeMap);

    return totalLogProb;
  }