private double getPMulticluster(List<List<Integer>> clusters, int numRestarts) {
    if (false) {
      Graph g = new EdgeListGraph();
      List<Node> latents = new ArrayList<Node>();
      for (int i = 0; i < clusters.size(); i++) {
        GraphNode latent = new GraphNode("L" + i);
        latent.setNodeType(NodeType.LATENT);
        latents.add(latent);
        g.addNode(latent);

        List<Node> cluster = variablesForIndices(clusters.get(i));

        for (int j = 0; j < cluster.size(); j++) {
          g.addNode(cluster.get(j));
          g.addDirectedEdge(latent, cluster.get(j));
        }
      }
      SemPm pm = new SemPm(g);

      //            pm.fixOneLoadingPerLatent();

      SemOptimizerPowell semOptimizer = new SemOptimizerPowell();
      semOptimizer.setNumRestarts(numRestarts);

      SemEstimator est = new SemEstimator(cov, pm, semOptimizer);
      est.setScoreType(SemIm.ScoreType.Fgls);
      est.estimate();
      return est.getEstimatedSem().getPValue();
    } else {
      double max = Double.NEGATIVE_INFINITY;

      for (int i = 0; i < numRestarts; i++) {
        Mimbuild2 mimbuild = new Mimbuild2();

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

        for (List<Integer> _cluster : clusters) {
          _clusters.add(variablesForIndices(_cluster));
        }

        List<String> names = new ArrayList<String>();

        for (int j = 0; j < clusters.size(); j++) {
          names.add("L" + j);
        }

        mimbuild.search(_clusters, names, cov);

        double c = mimbuild.getpValue();
        if (c > max) max = c;
      }

      return max;
    }
  }
  @Test
  public void test8() {
    RandomUtil.getInstance().setSeed(29999483L);

    Node x = new GraphNode("X");
    Node y = new GraphNode("Y");

    List<Node> nodes = new ArrayList<>();
    nodes.add(x);
    nodes.add(y);

    Graph graph = new EdgeListGraphSingleConnections(nodes);

    graph.addDirectedEdge(x, y);

    SemPm spm = new SemPm(graph);
    SemIm sim = new SemIm(spm);

    sim.setEdgeCoef(x, y, 20);
    sim.setErrVar(x, 1);
    sim.setErrVar(y, 1);

    GeneralizedSemPm pm = new GeneralizedSemPm(spm);
    GeneralizedSemIm im = new GeneralizedSemIm(pm, sim);

    print(im);

    try {
      pm.setParameterEstimationInitializationExpression("b1", "U(10, 30)");
      pm.setParameterEstimationInitializationExpression("T1", "U(.1, 3)");
      pm.setParameterEstimationInitializationExpression("T2", "U(.1, 3)");
    } catch (ParseException e) {
      e.printStackTrace();
    }

    DataSet data = im.simulateDataRecursive(1000, false);

    GeneralizedSemEstimator estimator = new GeneralizedSemEstimator();
    GeneralizedSemIm estIm = estimator.estimate(pm, data);

    print(estIm);
    //        System.out.println(estimator.getReport());

    double aSquaredStar = estimator.getaSquaredStar();

    assertEquals(0.69, aSquaredStar, 0.01);
  }
  private double getP(List<Integer> cluster, int numRestarts) {
    if (true) {
      Node latent = new GraphNode("L");
      latent.setNodeType(NodeType.LATENT);
      Graph g = new EdgeListGraph();
      g.addNode(latent);
      List<Node> measures = variablesForIndices(cluster);
      for (Node node : measures) {
        g.addNode(node);
        g.addDirectedEdge(latent, node);
      }
      SemPm pm = new SemPm(g);

      //            pm.fixOneLoadingPerLatent();

      SemOptimizerPowell semOptimizer = new SemOptimizerPowell();
      semOptimizer.setNumRestarts(numRestarts);

      SemEstimator est = new SemEstimator(cov, pm, semOptimizer);
      est.setScoreType(SemIm.ScoreType.Fgls);
      est.estimate();
      return est.getEstimatedSem().getPValue();
    } else {
      double max = Double.NEGATIVE_INFINITY;

      for (int i = 0; i < numRestarts; i++) {
        Mimbuild2 mimbuild = new Mimbuild2();

        List<List<Node>> clusters1 = new ArrayList<List<Node>>();
        clusters1.add(variablesForIndices(new ArrayList<Integer>(cluster)));

        List<String> names = new ArrayList<String>();
        names.add("L");

        mimbuild.search(clusters1, names, cov);

        double c = mimbuild.getpValue();
        if (c > max) max = c;
      }

      return max;
    }
  }
  private double getClusterP2(List<Node> c) {
    Graph g = new EdgeListGraph(c);
    Node l = new GraphNode("L");
    l.setNodeType(NodeType.LATENT);
    g.addNode(l);

    for (Node n : c) {
      g.addDirectedEdge(l, n);
    }

    SemPm pm = new SemPm(g);
    SemEstimator est;
    if (dataModel instanceof DataSet) {
      est = new SemEstimator((DataSet) dataModel, pm, new SemOptimizerEm());
    } else {
      est = new SemEstimator((CovarianceMatrix) dataModel, pm, new SemOptimizerEm());
    }
    SemIm estIm = est.estimate();
    double pValue = estIm.getPValue();
    return pValue == 1 ? Double.NaN : pValue;
  }
  private Graph convertSearchGraphNodes(Set<Set<Node>> clusters) {
    Graph graph = new EdgeListGraph(variables);

    List<Node> latents = new ArrayList<Node>();
    for (int i = 0; i < clusters.size(); i++) {
      Node latent = new GraphNode(MimBuild.LATENT_PREFIX + (i + 1));
      latent.setNodeType(NodeType.LATENT);
      latents.add(latent);
      graph.addNode(latent);
    }

    List<Set<Node>> _clusters = new ArrayList<Set<Node>>(clusters);

    for (int i = 0; i < latents.size(); i++) {
      for (Node node : _clusters.get(i)) {
        if (!graph.containsNode(node)) graph.addNode(node);
        graph.addDirectedEdge(latents.get(i), node);
      }
    }

    return graph;
  }
  @Test
  public void test15() {
    RandomUtil.getInstance().setSeed(29999483L);

    try {
      Node x1 = new GraphNode("X1");
      Node x2 = new GraphNode("X2");
      Node x3 = new GraphNode("X3");
      Node x4 = new GraphNode("X4");

      Graph g = new EdgeListGraphSingleConnections();
      g.addNode(x1);
      g.addNode(x2);
      g.addNode(x3);
      g.addNode(x4);

      g.addDirectedEdge(x1, x2);
      g.addDirectedEdge(x2, x3);
      g.addDirectedEdge(x3, x4);
      g.addDirectedEdge(x1, x4);

      GeneralizedSemPm pm = new GeneralizedSemPm(g);

      pm.setNodeExpression(x1, "E_X1");
      pm.setNodeExpression(x2, "a1 * X1 + E_X2");
      pm.setNodeExpression(x3, "a2 * X2 + E_X3");
      pm.setNodeExpression(x4, "a3 * X1 + a4 * X3 ^ 2 + E_X4");

      pm.setNodeExpression(pm.getErrorNode(x1), "Gamma(c1, c2)");
      pm.setNodeExpression(pm.getErrorNode(x2), "ChiSquare(c3)");
      pm.setNodeExpression(pm.getErrorNode(x3), "ChiSquare(c4)");
      pm.setNodeExpression(pm.getErrorNode(x4), "ChiSquare(c5)");

      pm.setParameterExpression("c1", "5");
      pm.setParameterExpression("c2", "2");
      pm.setParameterExpression("c3", "10");
      pm.setParameterExpression("c4", "10");
      pm.setParameterExpression("c5", "10");

      pm.setParameterEstimationInitializationExpression("c1", "U(1, 5)");
      pm.setParameterEstimationInitializationExpression("c2", "U(1, 5)");
      pm.setParameterEstimationInitializationExpression("c3", "U(1, 5)");
      pm.setParameterEstimationInitializationExpression("c4", "U(1, 5)");
      pm.setParameterEstimationInitializationExpression("c5", "U(1, 5)");

      GeneralizedSemIm im = new GeneralizedSemIm(pm);

      print("True model: ");
      print(im);

      DataSet data = im.simulateDataRecursive(1000, false);

      GeneralizedSemEstimator estimator = new GeneralizedSemEstimator();
      GeneralizedSemIm estIm = estimator.estimate(pm, data);

      print("\n\n\nEstimated model: ");
      print(estIm);
      print(estimator.getReport());

      double aSquaredStar = estimator.getaSquaredStar();

      assertEquals(.79, aSquaredStar, 0.01);
    } catch (ParseException e) {
      e.printStackTrace();
    }
  }
  @Test
  public void test14() {
    RandomUtil.getInstance().setSeed(29999483L);

    try {
      Node x1 = new GraphNode("X1");
      Node x2 = new GraphNode("X2");
      Node x3 = new GraphNode("X3");
      Node x4 = new GraphNode("X4");

      Graph g = new EdgeListGraphSingleConnections();
      g.addNode(x1);
      g.addNode(x2);
      g.addNode(x3);
      g.addNode(x4);

      g.addDirectedEdge(x1, x2);
      g.addDirectedEdge(x2, x3);
      g.addDirectedEdge(x3, x4);
      g.addDirectedEdge(x1, x4);

      GeneralizedSemPm pm = new GeneralizedSemPm(g);

      pm.setNodeExpression(x1, "E_X1");
      pm.setNodeExpression(x2, "a1 * tan(X1) + E_X2");
      pm.setNodeExpression(x3, "a2 * tan(X2) + E_X3");
      pm.setNodeExpression(x4, "a3 * tan(X1) + a4 * tan(X3) ^ 2 + E_X4");

      pm.setNodeExpression(pm.getErrorNode(x1), "N(0, c1)");
      pm.setNodeExpression(pm.getErrorNode(x2), "N(0, c2)");
      pm.setNodeExpression(pm.getErrorNode(x3), "N(0, c3)");
      pm.setNodeExpression(pm.getErrorNode(x4), "N(0, c4)");

      pm.setParameterExpression("a1", "1");
      pm.setParameterExpression("a2", "1");
      pm.setParameterExpression("a3", "1");
      pm.setParameterExpression("a4", "1");
      pm.setParameterExpression("c1", "4");
      pm.setParameterExpression("c2", "4");
      pm.setParameterExpression("c3", "4");
      pm.setParameterExpression("c4", "4");

      GeneralizedSemIm im = new GeneralizedSemIm(pm);

      print("True model: ");
      print(im);

      DataSet data = im.simulateDataRecursive(1000, false);

      GeneralizedSemIm imInit = new GeneralizedSemIm(pm);
      imInit.setParameterValue("c1", 8);
      imInit.setParameterValue("c2", 8);
      imInit.setParameterValue("c3", 8);
      imInit.setParameterValue("c4", 8);

      GeneralizedSemEstimator estimator = new GeneralizedSemEstimator();
      GeneralizedSemIm estIm = estimator.estimate(pm, data);

      print("\n\n\nEstimated model: ");
      print(estIm);
      print(estimator.getReport());

      double aSquaredStar = estimator.getaSquaredStar();

      assertEquals(71.25, aSquaredStar, 0.01);
    } catch (ParseException e) {
      e.printStackTrace();
    }
  }