/**
   * Transforms a maximally directed pattern (PDAG) represented in graph <code>g</code> into an
   * arbitrary DAG by modifying <code>g</code> itself. Based on the algorithm described in
   * Chickering (2002) "Optimal structure identification with greedy search" Journal of Machine
   * Learning Research. R. Silva, June 2004
   */
  public static void pdagToDag(Graph g) {
    Graph p = new EdgeListGraph(g);
    List<Edge> undirectedEdges = new ArrayList<Edge>();

    for (Edge edge : g.getEdges()) {
      if (edge.getEndpoint1() == Endpoint.TAIL
          && edge.getEndpoint2() == Endpoint.TAIL
          && !undirectedEdges.contains(edge)) {
        undirectedEdges.add(edge);
      }
    }
    g.removeEdges(undirectedEdges);
    List<Node> pNodes = p.getNodes();

    do {
      Node x = null;

      for (Node pNode : pNodes) {
        x = pNode;

        if (p.getChildren(x).size() > 0) {
          continue;
        }

        Set<Node> neighbors = new HashSet<Node>();

        for (Edge edge : p.getEdges()) {
          if (edge.getNode1() == x || edge.getNode2() == x) {
            if (edge.getEndpoint1() == Endpoint.TAIL && edge.getEndpoint2() == Endpoint.TAIL) {
              if (edge.getNode1() == x) {
                neighbors.add(edge.getNode2());
              } else {
                neighbors.add(edge.getNode1());
              }
            }
          }
        }
        if (neighbors.size() > 0) {
          Collection<Node> parents = p.getParents(x);
          Set<Node> all = new HashSet<Node>(neighbors);
          all.addAll(parents);
          if (!GraphUtils.isClique(all, p)) {
            continue;
          }
        }

        for (Node neighbor : neighbors) {
          Node node1 = g.getNode(neighbor.getName());
          Node node2 = g.getNode(x.getName());

          g.addDirectedEdge(node1, node2);
        }
        p.removeNode(x);
        break;
      }
      pNodes.remove(x);
    } while (pNodes.size() > 0);
  }
Example #2
0
  private boolean dConnected(Graph graph, String x, String y, String... z) {
    Node _x = graph.getNode(x);
    Node _y = graph.getNode(y);

    List<Node> _z = new ArrayList<Node>();

    for (String name : z) {
      _z.add(graph.getNode(name));
    }

    return graph.isDConnectedTo(_x, _y, _z);
  }
  public static void arrangeByKnowledgeTiers(Graph graph, Knowledge knowledge) {
    if (knowledge.getNumTiers() == 0) {
      throw new IllegalArgumentException("There are no Tiers to arrange.");
    }

    List<Node> nodes = graph.getNodes();
    List<String> varNames = new ArrayList<String>();
    int ySpace = 500 / knowledge.getNumTiers();
    ySpace = ySpace < 50 ? 50 : ySpace;

    for (Node node1 : nodes) {
      varNames.add(node1.getName());
    }

    List<String> notInTier = knowledge.getVarsNotInTier(varNames);

    int x = 0;
    int y = 50 - ySpace;

    if (notInTier.size() > 0) {
      y += ySpace;

      for (String name : notInTier) {
        x += 90;
        Node node = graph.getNode(name);

        if (node != null) {
          node.setCenterX(x);
          node.setCenterY(y);
        }
      }
    }

    for (int i = 0; i < knowledge.getNumTiers(); i++) {
      List<String> tier = knowledge.getTier(i);
      y += ySpace;
      x = -25;

      for (String name : tier) {
        x += 90;
        Node node = graph.getNode(name);

        if (node != null) {
          node.setCenterX(x);
          node.setCenterY(y);
        }
      }
    }
  }
Example #4
0
  private Graph changeLatentNames(Graph full, Clusters measurements, List<String> latentVarList) {
    Graph g2 = null;

    try {
      g2 = (Graph) new MarshalledObject(full).get();
    } catch (IOException e) {
      e.printStackTrace();
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    }

    for (int i = 0; i < measurements.getNumClusters(); i++) {
      List<String> d = measurements.getCluster(i);
      String latentName = latentVarList.get(i);

      for (Node node : full.getNodes()) {
        if (!(node.getNodeType() == NodeType.LATENT)) {
          continue;
        }

        List<Node> _children = full.getChildren(node);

        _children.removeAll(ReidentifyVariables.getLatents(full));

        List<String> childNames = getNames(_children);

        if (new HashSet<String>(childNames).equals(new HashSet<String>(d))) {
          g2.getNode(node.getName()).setName(latentName);
        }
      }
    }

    return g2;
  }
Example #5
0
  // Trying to trip up the breadth first algorithm.
  public void testDSeparation3() {
    Graph graph = GraphConverter.convert("x-->s1,x-->s2,s1-->s3,s3-->s2,s3<--y");
    assertTrue(
        graph.isDSeparatedFrom(graph.getNode("x"), graph.getNode("y"), new ArrayList<Node>()));

    graph = GraphConverter.convert("1-->2,2<--4,2-->7,2-->3");
    assertTrue(
        graph.isDSeparatedFrom(graph.getNode("4"), graph.getNode("1"), new ArrayList<Node>()));

    graph = GraphConverter.convert("X1-->X5,X1-->X6,X2-->X3,X4-->X6,X5-->X3,X6-->X5,X7-->X3");
    assertTrue(dConnected(graph, "X2", "X4", "X3", "X6"));

    graph = GraphConverter.convert("X1<--X2,X1<--X3,X2-->X3,X3<--X4");
    assertTrue(dConnected(graph, "X1", "X4", "X3"));

    graph = GraphConverter.convert("X2-->X7,X3-->X2,X5-->X1,X5-->X2,X6-->X1,X7-->X6,X2->X4");
    assertTrue(dConnected(graph, "X1", "X3"));

    graph = GraphConverter.convert("1-->3,1-->4,2-->5,4-->5,4-->7,6-->5,7-->3");
    assertTrue(dConnected(graph, "1", "4"));
  }
  /**
   * Build a graph given an HMM. The graph will not be surrounded by dummy nodes. The number of
   * nodes in the graph is the number of emitting states in the hmm plus one, to account for a
   * final, non-emitting state.
   *
   * @param hmm the HMM
   * @return the graph
   */
  private Graph buildModelGraph(SenoneHMM hmm) {
    Graph graph = new Graph();
    Node prevNode;
    Node stateNode = null;
    float[][] tmat = hmm.getTransitionMatrix();

    prevNode = new Node(NodeType.DUMMY);
    graph.addNode(prevNode);
    graph.setInitialNode(prevNode);

    // 'hmm.getOrder() + 1' to account for final, non-emitting state.
    for (int i = 0; i < hmm.getOrder() + 1; i++) {
      /* create a new node for the next hmmState */
      stateNode = new Node(NodeType.STATE, hmm.getUnit().getName());
      stateNode.setObject(hmm.getState(i));
      graph.addNode(stateNode);
      /* Link the new node into the graph */
      if (i == 0) {
        graph.linkNodes(prevNode, stateNode);
      }
      for (int j = 0; j <= i; j++) {
        // System.out.println("TMAT: " + j + " " + i + " " +
        // tmat[j][i]);
        if (tmat[j][i] != LogMath.LOG_ZERO) {
          // 'j + 1' to account for the initial dummy node
          graph.linkNodes(graph.getNode(j + 1), stateNode);
        }
      }
      prevNode = stateNode;
    }
    /* All words are done. Just add the final dummy */
    // stateNode = new Node(NodeType.DUMMY);
    // graph.linkNodes(prevNode, stateNode);
    graph.setFinalNode(stateNode);

    return graph;
  }
  /**
   * Transforms a DAG represented in graph <code>graph</code> into a maximally directed pattern
   * (PDAG) by modifying <code>g</code> itself. Based on the algorithm described in Chickering
   * (2002) "Optimal structure identification with greedy search" Journal of Machine Learning
   * Research. It works for both BayesNets and SEMs. R. Silva, June 2004
   */
  public static void dagToPdag(Graph graph) {
    // do topological sort on the nodes
    Graph graphCopy = new EdgeListGraph(graph);
    Node orderedNodes[] = new Node[graphCopy.getNodes().size()];
    int count = 0;
    while (graphCopy.getNodes().size() > 0) {
      Set<Node> exogenousNodes = new HashSet<Node>();

      for (Node next : graphCopy.getNodes()) {
        if (graphCopy.isExogenous(next)) {
          exogenousNodes.add(next);
          orderedNodes[count++] = graph.getNode(next.getName());
        }
      }

      graphCopy.removeNodes(new ArrayList<Node>(exogenousNodes));
    }
    // ordered edges - improvised, inefficient implementation
    count = 0;
    Edge edges[] = new Edge[graph.getNumEdges()];
    boolean edgeOrdered[] = new boolean[graph.getNumEdges()];
    Edge orderedEdges[] = new Edge[graph.getNumEdges()];

    for (Edge edge : graph.getEdges()) {
      edges[count++] = edge;
    }

    for (int i = 0; i < edges.length; i++) {
      edgeOrdered[i] = false;
    }

    while (count > 0) {
      for (Node orderedNode : orderedNodes) {
        for (int k = orderedNodes.length - 1; k >= 0; k--) {
          for (int q = 0; q < edges.length; q++) {
            if (!edgeOrdered[q]
                && edges[q].getNode1() == orderedNodes[k]
                && edges[q].getNode2() == orderedNode) {
              edgeOrdered[q] = true;
              orderedEdges[orderedEdges.length - count] = edges[q];
              count--;
            }
          }
        }
      }
    }

    // label edges
    boolean compelledEdges[] = new boolean[graph.getNumEdges()];
    boolean reversibleEdges[] = new boolean[graph.getNumEdges()];
    for (int i = 0; i < graph.getNumEdges(); i++) {
      compelledEdges[i] = false;
      reversibleEdges[i] = false;
    }
    for (int i = 0; i < graph.getNumEdges(); i++) {
      if (compelledEdges[i] || reversibleEdges[i]) {
        continue;
      }
      Node x = orderedEdges[i].getNode1();
      Node y = orderedEdges[i].getNode2();
      for (int j = 0; j < orderedEdges.length; j++) {
        if (orderedEdges[j].getNode2() == x && compelledEdges[j]) {
          Node w = orderedEdges[j].getNode1();
          if (!graph.isParentOf(w, y)) {
            for (int k = 0; k < orderedEdges.length; k++) {
              if (orderedEdges[k].getNode2() == y) {
                compelledEdges[k] = true;
                break;
              }
            }
          } else {
            for (int k = 0; k < orderedEdges.length; k++) {
              if (orderedEdges[k].getNode1() == w && orderedEdges[k].getNode2() == y) {
                compelledEdges[k] = true;
                break;
              }
            }
          }
        }
        if (compelledEdges[i]) {
          break;
        }
      }
      if (compelledEdges[i]) {
        continue;
      }
      boolean foundZ = false;

      for (Edge orderedEdge : orderedEdges) {
        Node z = orderedEdge.getNode1();
        if (z != x && orderedEdge.getNode2() == y && !graph.isParentOf(z, x)) {
          compelledEdges[i] = true;
          for (int k = i + 1; k < graph.getNumEdges(); k++) {
            if (orderedEdges[k].getNode2() == y && !reversibleEdges[k]) {
              compelledEdges[k] = true;
            }
          }
          foundZ = true;
          break;
        }
      }

      if (!foundZ) {
        reversibleEdges[i] = true;

        for (int j = i + 1; j < orderedEdges.length; j++) {
          if (!compelledEdges[j] && orderedEdges[j].getNode2() == y) {
            reversibleEdges[j] = true;
          }
        }
      }
    }

    // undirect edges that are reversible
    for (int i = 0; i < reversibleEdges.length; i++) {
      if (reversibleEdges[i]) {
        graph.setEndpoint(orderedEdges[i].getNode1(), orderedEdges[i].getNode2(), Endpoint.TAIL);
        graph.setEndpoint(orderedEdges[i].getNode2(), orderedEdges[i].getNode1(), Endpoint.TAIL);
      }
    }
  }
Example #8
0
  private void addRequiredEdges(Graph graph) {
    if (true) return;
    if (knowledgeEmpty()) return;

    for (Iterator<KnowledgeEdge> it = getKnowledge().requiredEdgesIterator(); it.hasNext(); ) {
      KnowledgeEdge next = it.next();

      Node nodeA = graph.getNode(next.getFrom());
      Node nodeB = graph.getNode(next.getTo());

      if (!graph.isAncestorOf(nodeB, nodeA)) {
        graph.removeEdges(nodeA, nodeB);
        graph.addDirectedEdge(nodeA, nodeB);
        TetradLogger.getInstance()
            .log("insertedEdges", "Adding edge by knowledge: " + graph.getEdge(nodeA, nodeB));
      }
    }
    for (Edge edge : graph.getEdges()) {
      final String A = edge.getNode1().getName();
      final String B = edge.getNode2().getName();

      if (knowledge.isForbidden(A, B)) {
        Node nodeA = edge.getNode1();
        Node nodeB = edge.getNode2();

        if (nodeA != null
            && nodeB != null
            && graph.isAdjacentTo(nodeA, nodeB)
            && !graph.isChildOf(nodeA, nodeB)) {
          if (!graph.isAncestorOf(nodeA, nodeB)) {
            graph.removeEdges(nodeA, nodeB);
            graph.addDirectedEdge(nodeB, nodeA);
            TetradLogger.getInstance()
                .log("insertedEdges", "Adding edge by knowledge: " + graph.getEdge(nodeB, nodeA));
          }
        }
        if (!graph.isChildOf(nodeA, nodeB)
            && getKnowledge().isForbidden(nodeA.getName(), nodeB.getName())) {
          if (!graph.isAncestorOf(nodeA, nodeB)) {
            graph.removeEdges(nodeA, nodeB);
            graph.addDirectedEdge(nodeB, nodeA);
            TetradLogger.getInstance()
                .log("insertedEdges", "Adding edge by knowledge: " + graph.getEdge(nodeB, nodeA));
          }
        }
      } else if (knowledge.isForbidden(B, A)) {
        Node nodeA = edge.getNode2();
        Node nodeB = edge.getNode1();

        if (nodeA != null
            && nodeB != null
            && graph.isAdjacentTo(nodeA, nodeB)
            && !graph.isChildOf(nodeA, nodeB)) {
          if (!graph.isAncestorOf(nodeA, nodeB)) {
            graph.removeEdges(nodeA, nodeB);
            graph.addDirectedEdge(nodeB, nodeA);
            TetradLogger.getInstance()
                .log("insertedEdges", "Adding edge by knowledge: " + graph.getEdge(nodeB, nodeA));
          }
        }
        if (!graph.isChildOf(nodeA, nodeB)
            && getKnowledge().isForbidden(nodeA.getName(), nodeB.getName())) {
          if (!graph.isAncestorOf(nodeA, nodeB)) {
            graph.removeEdges(nodeA, nodeB);
            graph.addDirectedEdge(nodeB, nodeA);
            TetradLogger.getInstance()
                .log("insertedEdges", "Adding edge by knowledge: " + graph.getEdge(nodeB, nodeA));
          }
        }
      }
    }
  }
Example #9
0
  /** Do an actual deletion (Definition 13 from Chickering, 2002). */
  private void delete(Node x, Node y, List<Node> subset, Graph graph, double bump) {

    Edge trueEdge = null;

    if (trueGraph != null) {
      Node _x = trueGraph.getNode(x.getName());
      Node _y = trueGraph.getNode(y.getName());
      trueEdge = trueGraph.getEdge(_x, _y);
    }

    if (log && verbose) {
      Edge oldEdge = graph.getEdge(x, y);

      String label = trueGraph != null && trueEdge != null ? "*" : "";
      TetradLogger.getInstance()
          .log(
              "deletedEdges",
              (graph.getNumEdges() - 1)
                  + ". DELETE "
                  + oldEdge
                  + " "
                  + subset
                  + " ("
                  + bump
                  + ") "
                  + label);
      out.println(
          (graph.getNumEdges() - 1)
              + ". DELETE "
              + oldEdge
              + " "
              + subset
              + " ("
              + bump
              + ") "
              + label);
    } else {
      int numEdges = graph.getNumEdges() - 1;
      if (numEdges % 50 == 0) out.println(numEdges);
    }

    graph.removeEdge(x, y);

    for (Node h : subset) {
      Edge oldEdge = graph.getEdge(y, h);

      graph.removeEdge(y, h);
      graph.addDirectedEdge(y, h);

      if (log) {
        TetradLogger.getInstance()
            .log("directedEdges", "--- Directing " + oldEdge + " to " + graph.getEdge(y, h));
      }

      if (verbose) {
        out.println("--- Directing " + oldEdge + " to " + graph.getEdge(y, h));
      }

      if (Edges.isUndirectedEdge(graph.getEdge(x, h))) {
        if (!graph.isAdjacentTo(x, h))
          throw new IllegalArgumentException("Not adjacent: " + x + ", " + h);
        oldEdge = graph.getEdge(x, h);

        graph.removeEdge(x, h);
        graph.addDirectedEdge(x, h);

        if (log) {
          TetradLogger.getInstance()
              .log("directedEdges", "--- Directing " + oldEdge + " to " + graph.getEdge(x, h));
        }

        if (verbose) {
          out.println("--- Directing " + oldEdge + " to " + graph.getEdge(x, h));
        }
      }
    }
  }
Example #10
0
  // serial.
  private void insert(Node x, Node y, List<Node> t, Graph graph, double bump) {
    if (graph.isAdjacentTo(x, y)) {
      return; // The initial graph may already have put this edge in the graph.
      //            throw new IllegalArgumentException(x + " and " + y + " are already adjacent in
      // the graph.");
    }

    Edge trueEdge = null;

    if (trueGraph != null) {
      Node _x = trueGraph.getNode(x.getName());
      Node _y = trueGraph.getNode(y.getName());
      trueEdge = trueGraph.getEdge(_x, _y);
    }

    graph.addDirectedEdge(x, y);

    if (log) {
      String label = trueGraph != null && trueEdge != null ? "*" : "";
      TetradLogger.getInstance()
          .log(
              "insertedEdges",
              graph.getNumEdges()
                  + ". INSERT "
                  + graph.getEdge(x, y)
                  + " "
                  + t
                  + " "
                  + bump
                  + " "
                  + label);
    } else {
      int numEdges = graph.getNumEdges() - 1;
      if (verbose) {
        if (numEdges % 50 == 0) out.println(numEdges);
      }
    }

    if (verbose) {
      String label = trueGraph != null && trueEdge != null ? "*" : "";
      out.println(
          graph.getNumEdges()
              + ". INSERT "
              + graph.getEdge(x, y)
              + " "
              + t
              + " "
              + bump
              + " "
              + label);
    } else {
      int numEdges = graph.getNumEdges() - 1;
      if (verbose) {
        if (numEdges % 50 == 0) out.println(numEdges);
      }
    }

    for (Node _t : t) {
      Edge oldEdge = graph.getEdge(_t, y);

      if (oldEdge == null) throw new IllegalArgumentException("Not adjacent: " + _t + ", " + y);

      graph.removeEdge(_t, y);
      graph.addDirectedEdge(_t, y);

      if (log && verbose) {
        TetradLogger.getInstance()
            .log("directedEdges", "--- Directing " + oldEdge + " to " + graph.getEdge(_t, y));
        out.println("--- Directing " + oldEdge + " to " + graph.getEdge(_t, y));
      }
    }
  }
Example #11
0
  private void createGraph() {
    graph = new Graph();
    String[] columns = Arrays.copyOf(assayTable[0], assayTable[0].length, String[].class);

    int index = 0;

    ProcessNode lastProcess = null;
    NodeWithComments lastMaterialOrData = null;
    NodeWithComments lastSample = null;
    ISAFactorValue lastFactorValue = null;
    ProtocolExecutionNode lastProtocolExecutionNode = null;
    List<ProtocolExecutionNode> protocolExecutionNodes = new ArrayList<ProtocolExecutionNode>();

    for (String column : columns) {

      if (column.matches(Date.REGEXP)) {
        Date date = new Date(index, column);
        if (lastProtocolExecutionNode != null) {
          lastProtocolExecutionNode.addDate(date);
        }

      } else if (column.matches(Performer.REGEXP)) {
        Performer performer = new Performer(index, column);
        if (lastProtocolExecutionNode != null) {
          lastProtocolExecutionNode.addPerformer(performer);
        }

      } else if (column.matches(ProtocolExecutionNode.REGEXP)) {
        ProtocolExecutionNode protocolExecutionNode = new ProtocolExecutionNode(index, column);
        protocolExecutionNodes.add(protocolExecutionNode);
        lastProtocolExecutionNode = protocolExecutionNode;

        if (lastMaterialOrData != null) {
          protocolExecutionNode.setInputNode(lastMaterialOrData);
        }

        graph.addNode(protocolExecutionNode);

      } else if (column.matches(ProcessNode.REGEXP)) {
        ProcessNode processNode = new ProcessNode(index, column);

        graph.addNode(processNode);
        if (lastMaterialOrData != null) {
          // but it could be a DataNode rather than a material node... do I need a new object?
          // processNode.setInputNode(
          //       new MaterialNode(lastMaterialOrData.getIndex(), lastMaterialOrData.getName()));
          processNode.setInputNode(lastMaterialOrData);
        }
        lastProcess = processNode;
        if (lastProcess != null) {
          lastProcess.addProtocolExecutionNodes(protocolExecutionNodes);
          protocolExecutionNodes = new ArrayList<ProtocolExecutionNode>();
        }

      } else if (column.contains(ISADataNode.CONTAINS) && !column.contains("Comment")) {
        NodeWithComments dataNode = new DataNode(index, column);
        graph.addNode(dataNode);
        lastMaterialOrData = dataNode;

        if (lastProcess != null) {
          lastProcess.setOutputNode(dataNode);
          lastProcess = null;
        } else if (lastProtocolExecutionNode != null) {
          lastProtocolExecutionNode.setOutputNode(dataNode);
          lastProtocolExecutionNode = null;
        }

        if (lastMaterialOrData != null
            && lastProcess == null
            && lastProtocolExecutionNode != null) {
          lastProtocolExecutionNode.setOutputNode(dataNode);
          protocolExecutionNodes = new ArrayList<ProtocolExecutionNode>();
        }

      } else if (column.matches(ISAMaterialAttribute.REGEXP)) {

        ISAMaterialAttribute materialAttribute = new MaterialAttribute(index, column);
        if (lastMaterialOrData != null && lastMaterialOrData instanceof MaterialNode) {
          ((MaterialNode) graph.getNode(lastMaterialOrData.getIndex()))
              .addMaterialAttribute(materialAttribute);
        }

      } else if (column.matches(MaterialNode.REGEXP)) {

        NodeWithComments materialNode = null;
        if (column.matches(ISASampleNode.REGEXP)) {
          materialNode = new SampleNode(index, column);
          lastSample = materialNode;
        } else {
          materialNode = new MaterialNode(index, column);
        }
        // if there is a previous material node
        // and no process node, add a dummy process node
        if (lastMaterialOrData != null
            && lastProcess == null
            && lastProtocolExecutionNode != null) {
          lastProtocolExecutionNode.setOutputNode(materialNode);
          protocolExecutionNodes = new ArrayList<ProtocolExecutionNode>();
        }

        graph.addNode(materialNode);
        lastMaterialOrData = materialNode;
        if (lastProcess != null) {
          lastProcess.setOutputNode(materialNode);
          lastProcess = null;
        }

      } else if (column.matches(ISAFactorValue.REGEXP)) {

        ISAFactorValue factorValue = new FactorValue(index, column);
        lastFactorValue = factorValue;
        if (lastSample != null && lastSample instanceof SampleNode) {
          ((SampleNode) graph.getNode(lastSample.getIndex())).addFactorValue(factorValue);
        }

      } else if (column.matches(ISAUnit.REGEXP)) {

        ISAUnit unit = new Unit(index, column);
        if (lastFactorValue != null) {
          lastFactorValue.setUnit(unit);
        }

      } else if (column.matches(ProcessParameter.REGEXP)) {

        ProcessParameter parameter = new ProcessParameter(index, column);

        if (lastProtocolExecutionNode != null) {
          ((ProcessNode) graph.getNode(lastProtocolExecutionNode.getIndex()))
              .addParameter(parameter);
        }

      } else if (column.matches(CommentNode.REGEXP)) {

        CommentNode commentNode = new CommentNode(index, column);

        if (lastProcess != null
            && lastMaterialOrData != null
            && lastProtocolExecutionNode != null) {

          // lastProcess greater index
          if (lastProcess.getIndex() > lastMaterialOrData.getIndex()
              && lastProcess.getIndex() > lastProtocolExecutionNode.getIndex()) {
            lastProcess.addComment(commentNode);
          } else if (lastMaterialOrData.getIndex() > lastProcess.getIndex()
              && lastMaterialOrData.getIndex() > lastProtocolExecutionNode.getIndex()) {
            lastMaterialOrData.addComment(commentNode);
          } else if (lastProtocolExecutionNode.getIndex() > lastProcess.getIndex()
              && lastProtocolExecutionNode.getIndex() > lastMaterialOrData.getIndex()) {
            lastProtocolExecutionNode.addComment(commentNode);
          }

        } else { // one of them is null

          if (lastProcess != null && lastMaterialOrData != null) {

            if (lastProcess.getIndex() > lastMaterialOrData.getIndex()) {
              lastProcess.addComment(commentNode);
            } else {
              lastMaterialOrData.addComment(commentNode);
            }

          } else if (lastProcess != null && lastProtocolExecutionNode != null) {

            if (lastProcess.getIndex() > lastProtocolExecutionNode.getIndex()) {
              lastProcess.addComment(commentNode);
            } else {
              lastProtocolExecutionNode.addComment(commentNode);
            }

          } else if (lastMaterialOrData != null && lastProtocolExecutionNode != null) {

            if (lastMaterialOrData.getIndex() > lastProtocolExecutionNode.getIndex()) {
              lastMaterialOrData.addComment(commentNode);
            } else {
              lastProtocolExecutionNode.addComment(commentNode);
            }

          } else {

            // only one is not null

            if (lastMaterialOrData != null) {
              lastMaterialOrData.addComment(commentNode);
            } else if (lastProcess != null) {
              lastProcess.addComment(commentNode);
            } else if (lastProtocolExecutionNode != null) {
              lastProtocolExecutionNode.addComment(commentNode);
            }
          }
        }
      }
      index++;
    }
  }