示例#1
0
  /**
   * This method draws tick marks for the x axis. The x axis represents pi_e, the true probability
   * of effect in treatment, and ranges in value from pi_c to 1.0.
   *
   * @param g the graphics context.
   */
  private void drawXTickMarks(Graphics g) {
    Rectangle plotRect = getPlotRect();
    int yPos = plotRect.y + plotRect.height;
    NumberFormat nf = NumberFormatUtil.getInstance().getNumberFormat();

    for (double d = 0.0; d <= 1.0; d += 0.1) {
      int xPos = getXPos(d);

      g.setColor(boundaryColor);
      g.drawLine(xPos, yPos, xPos, yPos - 10);
      g.drawString(nf.format(d), xPos - 3, yPos + 12);
    }
  }
示例#2
0
  /** @return a string representation of the coefficients and variances of the model. */
  public String toString() {
    StringBuilder buf = new StringBuilder();
    NumberFormat nf = NumberFormatUtil.getInstance().getNumberFormat();

    buf.append("\nStandardized SEM:");
    buf.append("\n\nEdge coefficients (parameters):\n");

    for (Edge edge : edgeParameters.keySet()) {
      if (!Edges.isDirectedEdge(edge)) {
        continue;
      }

      buf.append("\n" + edge + " " + nf.format(edgeParameters.get(edge)));
    }

    buf.append("\n\nError covariances (parameters):\n");

    for (Edge edge : edgeParameters.keySet()) {
      if (!Edges.isBidirectedEdge(edge)) {
        continue;
      }

      buf.append("\n" + edge + " " + nf.format(edgeParameters.get(edge)));
    }

    buf.append("\n\nError variances (calculated):\n");

    for (Node error : getErrorNodes()) {
      double variance = getErrorVariance(error);
      buf.append("\n" + error + " " + (Double.isNaN(variance) ? "Undefined" : nf.format(variance)));
    }

    buf.append("\n");

    return buf.toString();
  }
/**
 * Checks conditional independence of variable in a continuous data set using Fisher's Z test. See
 * Spirtes, Glymour, and Scheines, "Causation, Prediction and Search," 2nd edition, page 94.
 *
 * @author Joseph Ramsey
 * @author Frank Wimberly adapted IndTestCramerT for Fisher's Z
 */
public final class IndTestFisherZShortTriangular implements IndependenceTest {

  /** The covariance matrix. */
  private final ShortTriangularMatrix covMatrix;

  /** The variables of the covariance matrix, in order. (Unmodifiable list.) */
  private List<Node> variables;

  /** The significance level of the independence tests. */
  private double alpha;

  /**
   * The value of the Fisher's Z statistic associated with the las calculated partial correlation.
   */
  private double fisherZ;

  /**
   * The FisherZD independence test, used when Fisher Z throws an exception (i.e., when there's a
   * collinearity).
   */
  private IndTestFisherZGeneralizedInverse deterministicTest;

  /** Formats as 0.0000. */
  private static NumberFormat nf = NumberFormatUtil.getInstance().getNumberFormat();

  /** Stores a reference to the dataset being analyzed. */
  private DataSet dataSet;

  /** A stored p value, if the deterministic test was used. */
  private double pValue = Double.NaN;

  // ==========================CONSTRUCTORS=============================//

  /**
   * Constructs a new Independence test which checks independence facts based on the correlation
   * matrix implied by the given data set (must be continuous). The given significance level is
   * used.
   *
   * @param dataSet A data set containing only continuous columns.
   * @param alpha The alpha level of the test.
   */
  public IndTestFisherZShortTriangular(DataSet dataSet, double alpha) {
    if (!(dataSet.isContinuous())) {
      throw new IllegalArgumentException("Data set must be continuous.");
    }

    this.covMatrix = new ShortTriangularMatrix(dataSet.getNumColumns());
    this.covMatrix.becomeCorrelationMatrix(dataSet);
    this.variables = dataSet.getVariables();
    setAlpha(alpha);

    this.deterministicTest = new IndTestFisherZGeneralizedInverse(dataSet, alpha);
    this.dataSet = dataSet;
  }

  /**
   * Constructs a new Fisher Z independence test with the listed arguments.
   *
   * @param data A 2D continuous data set with no missing values.
   * @param variables A list of variables, a subset of the variables of <code>data</code>.
   * @param alpha The significance cutoff level. p values less than alpha will be reported as
   *     dependent.
   */
  public IndTestFisherZShortTriangular(TetradMatrix data, List<Node> variables, double alpha) {
    DataSet dataSet = ColtDataSet.makeContinuousData(variables, data);
    this.covMatrix = new ShortTriangularMatrix(dataSet.getNumColumns());
    this.covMatrix.becomeCorrelationMatrix(dataSet);
    this.variables = dataSet.getVariables();
    setAlpha(alpha);

    this.deterministicTest = new IndTestFisherZGeneralizedInverse(dataSet, alpha);
  }

  //    /**
  //     * Constructs a new independence test that will determine conditional
  //     * independence facts using the given correlation matrix and the given
  //     * significance level.
  //     */
  //    public IndTestFisherZShortTriangular(CovarianceMatrix corrMatrix, double alpha) {
  //        this.covMatrix = corrMatrix;
  //        this.variables = Collections.unmodifiableList(corrMatrix.getVariables());
  //        setAlpha(alpha);
  //    }

  // ==========================PUBLIC METHODS=============================//

  /** Creates a new IndTestCramerT instance for a subset of the variables. */
  public IndependenceTest indTestSubset(List<Node> vars) {
    //        if (vars.isEmpty()) {
    //            throw new IllegalArgumentException("Subset may not be empty.");
    //        }
    //
    //        for (Node ar : vars) {
    //            if (!variables.contains(ar)) {
    //                throw new IllegalArgumentException(
    //                        "All vars must be original vars");
    //            }
    //        }
    //
    //        int[] indices = new int[vars.size()];
    //
    //        for (int i = 0; i < indices.length; i++) {
    //            indices[i] = variables.indexOf(vars.get(i));
    //        }
    //
    //        CovarianceMatrix newCovMatrix = getSubmatrix(indices);
    //
    //        double alphaNew = getAlpha();
    //        return new IndTestFisherZShortTriangular(newCovMatrix, alphaNew);

    throw new UnsupportedOperationException();
  }

  /**
   * Determines whether variable x is independent of variable y given a list of conditioning
   * variables z.
   *
   * @param x the one variable being compared.
   * @param y the second variable being compared.
   * @param z the list of conditioning variables.
   * @return true iff x _||_ y | z.
   * @throws RuntimeException if a matrix singularity is encountered.
   */
  public boolean isIndependent(Node x, Node y, List<Node> z) {
    TetradMatrix submatrix = subMatrix(x, y, z);
    double r = 0;

    try {
      r = StatUtils.partialCorrelation(submatrix);

      if (Double.isNaN((r)) || r < -1. || r > 1.) throw new RuntimeException();
    } catch (Exception e) {
      DepthChoiceGenerator gen = new DepthChoiceGenerator(z.size(), z.size());
      int[] choice;

      while ((choice = gen.next()) != null) {
        try {
          List<Node> z2 = new ArrayList<Node>(z);
          z2.removeAll(GraphUtils.asList(choice, z));
          submatrix = subMatrix(x, y, z2);
          r = StatUtils.partialCorrelation(submatrix);
        } catch (Exception e2) {
          continue;
        }

        //                if (Double.isNaN(r)) continue;
        //
        //                if (r > 1.) r = 1.;
        //                 if (r < -1.) r = -1.;

        if (Double.isNaN(r) || r < -1. || r > 1.) continue;

        break;
      }
    }

    // Either dividing by a zero standard deviation (in which case it's dependent) or doing a
    // regression
    // (effectively) with a multicolliarity
    if (Double.isNaN(r)) {
      int[] _z = new int[z.size()];
      //            for (int i = 0; i < _z.length; i++) _z[i] = i + 2;
      //
      ////            double varx = StatUtils.partialVariance(submatrix, 0, _z); // submatrix.get(0,
      // 0);
      ////            double vary = StatUtils.partialVariance(submatrix, 1, _z); //submatrix.get(1,
      // 1);
      //
      //            double varx = submatrix.get(0, 0);
      //            double vary = submatrix.get(1, 1);
      //
      //            if (varx * vary == 0) {
      return true;
      //            }
    }

    if (r > 1.) r = 1.;
    if (r < -1.) r = -1.;

    this.fisherZ =
        Math.sqrt(sampleSize() - z.size() - 3.0) * 0.5 * (Math.log(1.0 + r) - Math.log(1.0 - r));

    if (Double.isNaN(this.fisherZ)) {
      throw new IllegalArgumentException(
          "The Fisher's Z "
              + "score for independence fact "
              + x
              + " _||_ "
              + y
              + " | "
              + z
              + " is undefined. r = "
              + r);
    }

    boolean independent = getPValue() > alpha;

    if (independent) {
      TetradLogger.getInstance()
          .log("independencies", SearchLogUtils.independenceFactMsg(x, y, z, getPValue()));
    } else {
      TetradLogger.getInstance()
          .log("dependencies", SearchLogUtils.dependenceFactMsg(x, y, z, getPValue()));
    }

    return independent;
  }

  private TetradMatrix subMatrix(Node x, Node y, List<Node> z) {
    int dim = z.size() + 2;
    int[] indices = new int[dim];
    indices[0] = variables.indexOf(x);
    indices[1] = variables.indexOf(y);
    for (int k = 0; k < z.size(); k++) {
      indices[k + 2] = variables.indexOf(z.get(k));
    }

    TetradMatrix submatrix = new TetradMatrix(dim, dim);

    for (int i = 0; i < dim; i++) {
      for (int j = 0; j < dim; j++) {
        int i1 = indices[i];
        int i2 = indices[j];
        submatrix.set(i, j, covMatrix.getDouble(i1, i2));
      }
    }
    return submatrix;
  }

  public boolean isIndependent(Node x, Node y, Node... z) {
    return isIndependent(x, y, Arrays.asList(z));
  }

  public boolean isDependent(Node x, Node y, List<Node> z) {
    return !isIndependent(x, y, z);
  }

  public boolean isDependent(Node x, Node y, Node... z) {
    List<Node> zList = Arrays.asList(z);
    return isDependent(x, y, zList);
  }

  /** @return the probability associated with the most recently computed independence test. */
  public double getPValue() {
    if (!Double.isNaN(this.pValue)) {
      return Double.NaN;
    } else {
      return 2.0 * (1.0 - RandomUtil.getInstance().normalCdf(0, 1, Math.abs(fisherZ)));
    }
  }

  /**
   * Sets the significance level at which independence judgments should be made. Affects the cutoff
   * for partial correlations to be considered statistically equal to zero.
   */
  public void setAlpha(double alpha) {
    if (alpha < 0.0 || alpha > 1.0) {
      throw new IllegalArgumentException("Significance out of range.");
    }

    this.alpha = alpha;
    //        this.thresh = Double.NaN;
  }

  /** Gets the getModel significance level. */
  public double getAlpha() {
    return this.alpha;
  }

  /**
   * @return the list of variables over which this independence checker is capable of determinine
   *     independence relations-- that is, all the variables in the given graph or the given data
   *     set.
   */
  public List<Node> getVariables() {
    return this.variables;
  }

  /** @return the variable with the given name. */
  public Node getVariable(String name) {
    for (int i = 0; i < getVariables().size(); i++) {
      Node variable = getVariables().get(i);
      if (variable.getName().equals(name)) {
        return variable;
      }
    }

    return null;
  }

  /** @return the list of variable varNames. */
  public List<String> getVariableNames() {
    List<Node> variables = getVariables();
    List<String> variableNames = new ArrayList<String>();
    for (Node variable1 : variables) {
      variableNames.add(variable1.getName());
    }
    return variableNames;
  }

  /**
   * If <code>isDeterminismAllowed()</code>, deters to IndTestFisherZD; otherwise throws
   * UnsupportedOperationException.
   */
  public boolean determines(List<Node> z, Node x) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
    //        int[] parents = new int[z.size()];
    //
    //        for (int j = 0; j < parents.length; j++) {
    //            parents[j] = covMatrix.getVariables().indexOf(z.get(j));
    //        }
    //
    //        int i = covMatrix.getVariables().indexOf(x);
    //
    //        TetradMatrix matrix2D = covMatrix.getMatrix();
    //        double variance = matrix2D.get(i, i);
    //
    //        if (parents.length > 0) {
    //
    //            // Regress z onto i, yielding regression coefficients b.
    //            TetradMatrix Czz =
    //                   matrix2D.viewSelection(parents, parents);
    //            TetradMatrix inverse;
    //            try {
    //                inverse = TetradAlgebra.inverse(Czz);
    ////                inverse = MatrixUtils.ginverse(Czz);
    //            }
    //            catch (Exception e) {
    //                return true;
    //            }
    //
    //            TetradVector Cyz = matrix2D.viewColumn(i);
    //            Cyz = Cyz.viewSelection(parents);
    //            TetradVector b = TetradAlgebra.times(inverse, Cyz);
    //
    //            variance -= TetradAlgebra.times(Cyz, b);
    //        }
    //
    //        return variance < 0.01;
  }

  /** @return the data set being analyzed. */
  public DataSet getData() {
    return dataSet;
  }

  @Override
  public ICovarianceMatrix getCov() {
    return null;
  }

  @Override
  public List<DataSet> getDataSets() {
    return null;
  }

  @Override
  public int getSampleSize() {
    return 0;
  }

  @Override
  public List<TetradMatrix> getCovMatrices() {
    return null;
  }

  public void shuffleVariables() {
    List<Node> nodes = new ArrayList(this.variables);
    Collections.shuffle(nodes);
    this.variables = Collections.unmodifiableList(nodes);
  }

  /** @return a string representation of this test. */
  public String toString() {
    return "Fisher's Z, alpha = " + nf.format(getAlpha());
  }

  // ==========================PRIVATE METHODS============================//

  /**
   * Computes that value x such that P(abs(N(0,1) > x) < alpha. Note that this is a two sided test
   * of the null hypothesis that the Fisher's Z value, which is distributed as N(0,1) is not equal
   * to 0.0.
   */
  private double cutoffGaussian(double alpha) {
    double upperTail = 1.0 - alpha / 2.0;
    double epsilon = 1e-14;

    // Find an upper bound.
    double lowerBound = -1.0;
    double upperBound = 0.0;

    while (ProbUtils.normalCdf(upperBound) < upperTail) {
      lowerBound += 1.0;
      upperBound += 1.0;
    }

    while (upperBound >= lowerBound + epsilon) {
      double midPoint = lowerBound + (upperBound - lowerBound) / 2.0;

      if (ProbUtils.normalCdf(midPoint) <= upperTail) {
        lowerBound = midPoint;
      } else {
        upperBound = midPoint;
      }
    }

    return lowerBound;
  }

  private int sampleSize() {
    return dataSet.getNumRows();
  }

  //    private CovarianceMatrix covMatrix() {
  //        return covMatrix;
  //    }
}
示例#4
0
  public void setup() {
    this.varNames = params.getVarNames();

    for (Object parentModel : parentModels) {
      if (parentModel instanceof DataWrapper) {
        DataWrapper wrapper = (DataWrapper) parentModel;
        DataModel dataModel = wrapper.getSelectedDataModel();
        new IndTestChooser().adjustIndTestParams(dataModel, params);
        break;
      } else if (parentModel instanceof GraphWrapper) {
        GraphWrapper wrapper = (GraphWrapper) parentModel;
        new IndTestChooser().adjustIndTestParams(wrapper.getGraph(), params);
        break;
      } else if (parentModel instanceof DagWrapper) {
        DagWrapper wrapper = (DagWrapper) parentModel;
        new IndTestChooser().adjustIndTestParams(wrapper.getGraph(), params);
        break;
      } else if (parentModel instanceof SemGraphWrapper) {
        SemGraphWrapper wrapper = (SemGraphWrapper) parentModel;
        new IndTestChooser().adjustIndTestParams(wrapper.getGraph(), params);
        break;
      }
    }

    DataModel dataModel1 = null;
    Graph graph = null;

    for (Object parentModel1 : parentModels) {
      if (parentModel1 instanceof DataWrapper) {
        DataWrapper dataWrapper = (DataWrapper) parentModel1;
        dataModel1 = dataWrapper.getSelectedDataModel();
      }

      if (parentModel1 instanceof GraphWrapper) {
        GraphWrapper graphWrapper = (GraphWrapper) parentModel1;
        graph = graphWrapper.getGraph();
      }

      if (parentModel1 instanceof DagWrapper) {
        DagWrapper dagWrapper = (DagWrapper) parentModel1;
        graph = dagWrapper.getDag();
      }

      if (parentModel1 instanceof SemGraphWrapper) {
        SemGraphWrapper semGraphWrapper = (SemGraphWrapper) parentModel1;
        graph = semGraphWrapper.getGraph();
      }
    }

    if (dataModel1 != null) {
      varNames = new ArrayList(dataModel1.getVariableNames());
    } else if (graph != null) {
      Iterator it = graph.getNodes().iterator();
      varNames = new ArrayList();

      Node temp;

      while (it.hasNext()) {
        temp = (Node) it.next();

        if (temp.getNodeType() == NodeType.MEASURED) {
          varNames.add(temp.getName());
        }
      }
    } else {
      throw new NullPointerException(
          "Null model (no graph or data model " + "passed to the search).");
    }

    params.setVarNames(varNames);
    JButton knowledgeButton = new JButton("Edit");

    IntTextField depthField = new IntTextField(params.getIndTestParams().getDepth(), 4);
    depthField.setFilter(
        new IntTextField.Filter() {
          public int filter(int value, int oldValue) {
            try {
              params.getIndTestParams().setDepth(value);
              Preferences.userRoot().putInt("depth", value);
              return value;
            } catch (Exception e) {
              return oldValue;
            }
          }
        });

    double alpha = params.getIndTestParams().getAlpha();

    if (!Double.isNaN(alpha)) {
      alphaField = new DoubleTextField(alpha, 4, NumberFormatUtil.getInstance().getNumberFormat());
      alphaField.setFilter(
          new DoubleTextField.Filter() {
            public double filter(double value, double oldValue) {
              try {
                params.getIndTestParams().setAlpha(value);
                Preferences.userRoot().putDouble("alpha", value);
                return value;
              } catch (Exception e) {
                return oldValue;
              }
            }
          });
    }

    setBorder(new MatteBorder(10, 10, 10, 10, super.getBackground()));
    setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));

    Box b2 = Box.createHorizontalBox();
    b2.add(new JLabel("Knowledge:"));
    b2.add(Box.createGlue());
    b2.add(knowledgeButton);
    add(b2);
    add(Box.createVerticalStrut(10));

    if (!Double.isNaN(alpha)) {
      Box b0 = Box.createHorizontalBox();
      b0.add(new JLabel("Alpha Value:"));
      b0.add(Box.createGlue());
      b0.add(alphaField);
      add(b0);
      add(Box.createVerticalStrut(10));
    }

    Box b1 = Box.createHorizontalBox();
    b1.add(new JLabel("Search Depth:"));
    b1.add(Box.createGlue());
    b1.add(depthField);
    add(b1);
    add(Box.createVerticalStrut(10));

    knowledgeButton.addActionListener(
        new ActionListener() {
          public final void actionPerformed(ActionEvent e) {
            openKnowledgeEditor();
          }
        });
  }
  /**
   * Constructs the Gui used to edit properties; called from each constructor. Constructs labels and
   * text fields for editing each property and adds appropriate listeners.
   */
  public void setup() {
    setLayout(new BorderLayout());

    JRadioButton manualRetain = new JRadioButton();
    JRadioButton randomRetain = new JRadioButton();

    manualRetain.setText("Manually, retaining previous values where possible.");
    randomRetain.setText(
        "Using a symmetric prior for each row of each conditional" + " probability table.");

    ButtonGroup group = new ButtonGroup();
    group.add(manualRetain);
    group.add(randomRetain);

    final DoubleTextField symmetricAlphaField =
        new DoubleTextField(
            params.getDouble("symmetricAlpha", 1.0),
            5,
            NumberFormatUtil.getInstance().getNumberFormat());
    symmetricAlphaField.setFilter(
        new DoubleTextField.Filter() {
          public double filter(double value, double oldValue) {
            try {
              params.set("symmetricAlpha", value);
              return value;
            } catch (IllegalArgumentException e) {
              return oldValue;
            }
          }
        });

    if (getParams().getString("initializationMode", "manualRetain").equals("manualRetain")) {
      manualRetain.setSelected(true);
      symmetricAlphaField.setEnabled(false);
    } else if (getParams()
        .getString("initializationMode", "manualRetain")
        .equals("symmetricPrior")) {
      randomRetain.setSelected(true);
      symmetricAlphaField.setEnabled(true);
    } else {
      throw new IllegalStateException();
    }

    manualRetain.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            getParams().set("initializationMode", "manualRetain");
            symmetricAlphaField.setEnabled(false);
          }
        });

    randomRetain.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            getParams().set("initializationMode", "symmetricPrior");
            symmetricAlphaField.setEnabled(true);
          }
        });

    // continue workbench construction.
    Box b1 = Box.createVerticalBox();

    Box b2 = Box.createHorizontalBox();
    b2.add(new JLabel("Pseudocounts for this Dirichlet Bayes IM should be initialized:"));
    b2.add(Box.createHorizontalGlue());

    Box b3 = Box.createHorizontalBox();
    b3.add(manualRetain);
    b3.add(Box.createHorizontalGlue());

    Box b4 = Box.createHorizontalBox();
    b4.add(randomRetain);
    b4.add(Box.createHorizontalGlue());

    Box b5 = Box.createHorizontalBox();
    b5.add(Box.createRigidArea(new Dimension(30, 0)));
    b5.add(new JLabel("All pseudocounts = "));
    b5.add(symmetricAlphaField);
    b5.add(Box.createHorizontalGlue());

    b1.add(b2);
    b1.add(Box.createVerticalStrut(5));
    b1.add(b3);
    b1.add(b4);
    b1.add(b5);
    b1.add(Box.createHorizontalGlue());
    add(b1, BorderLayout.CENTER);
  }
/**
 * Lets the user interact with a SEM estimator.
 *
 * @author Joseph Ramsey
 */
public final class SemEstimatorEditor extends JPanel {
  private final SemEstimatorWrapper wrapper;
  private final JPanel panel;
  private final NumberFormat nf = NumberFormatUtil.getInstance().getNumberFormat();
  private final JComboBox scoreBox;
  private final IntTextField restarts;

  public SemEstimatorEditor(SemPm semPm, DataSet dataSet) {
    this(new SemEstimatorWrapper(dataSet, semPm, new Parameters()));
  }

  public SemEstimatorEditor(SemEstimatorWrapper _wrapper) {
    this.wrapper = _wrapper;
    panel = new JPanel();
    setLayout(new BorderLayout());

    JComboBox optimizerCombo = new JComboBox();
    optimizerCombo.addItem("Regression");
    optimizerCombo.addItem("EM");
    optimizerCombo.addItem("Powell");
    optimizerCombo.addItem("Random Search");
    optimizerCombo.addItem("RICF");

    optimizerCombo.addActionListener(
        new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent e) {
            JComboBox box = (JComboBox) e.getSource();
            wrapper.setSemOptimizerType((String) box.getSelectedItem());
          }
        });

    scoreBox = new JComboBox();
    restarts = new IntTextField(1, 2);

    scoreBox.addItem("Fgls");
    scoreBox.addItem("Fml");

    scoreBox.addActionListener(
        new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent e) {
            JComboBox box = (JComboBox) e.getSource();
            String type = (String) box.getSelectedItem();
            if ("Fgls".equals(type)) {
              wrapper.setScoreType(SemIm.ScoreType.Fgls);
            } else if ("Fml".equals(type)) {
              wrapper.setScoreType(SemIm.ScoreType.Fml);
            }
          }
        });

    restarts.setFilter(
        new IntTextField.Filter() {
          public int filter(int value, int oldValue) {
            try {
              wrapper.setNumRestarts(value);
              return value;
            } catch (Exception e) {
              return oldValue;
            }
          }
        });

    String semOptimizerType = wrapper.getParams().getString("semOptimizerType", "Regression");

    optimizerCombo.setSelectedItem(semOptimizerType);
    SemIm.ScoreType scoreType =
        (SemIm.ScoreType) wrapper.getParams().get("scoreType", SemIm.ScoreType.Fgls);
    if (scoreType == null) scoreType = SemIm.ScoreType.Fgls;
    scoreBox.setSelectedItem(scoreType.toString());
    restarts.setValue(wrapper.getParams().getInt("numRestarts", 1));

    JButton estimateButton = new JButton("Estimate Again");

    estimateButton.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            Window owner = (Window) getTopLevelAncestor();

            new WatchedProcess(owner) {
              public void watch() {
                reestimate();
              }
            };
          }
        });

    JButton report = new JButton("Report");

    report.addActionListener(
        (new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            final JTextArea textArea = new JTextArea();
            JScrollPane scroll = new JScrollPane(textArea);

            textArea.append(compileReport());

            Box b = Box.createVerticalBox();
            Box b2 = Box.createHorizontalBox();
            b2.add(scroll);
            textArea.setCaretPosition(0);
            b.add(b2);

            JPanel panel = new JPanel();
            panel.setLayout(new BorderLayout());
            panel.add(b);

            EditorWindow window =
                new EditorWindow(panel, "All Paths", "Close", false, SemEstimatorEditor.this);
            DesktopController.getInstance().addEditorWindow(window, JLayeredPane.PALETTE_LAYER);
            window.setVisible(true);
          }
        }));

    Box lowerBarA = Box.createHorizontalBox();
    lowerBarA.add(new JLabel("Score"));
    lowerBarA.add(scoreBox);
    lowerBarA.add(Box.createHorizontalGlue());
    lowerBarA.add(new JLabel("Random Restarts"));
    lowerBarA.add(restarts);

    Box lowerBarB = Box.createHorizontalBox();
    lowerBarB.add(new JLabel("Choose Optimizer:  "));
    lowerBarB.add(optimizerCombo);
    lowerBarB.add(Box.createHorizontalGlue());
    lowerBarB.add(estimateButton);

    Box lowerBar = Box.createVerticalBox();
    lowerBar.add(lowerBarA);
    lowerBar.add(lowerBarB);

    if (wrapper.getMultipleResultList().size() > 1) {
      lowerBar.add(report);
    }

    add(lowerBar, BorderLayout.SOUTH);

    resetSemImEditor();

    add(panel, BorderLayout.CENTER);
    validate();
  }

  private String compileReport() {
    StringBuilder builder = new StringBuilder();

    builder.append("Datset\tFrom\tTo\tType\tValue\tSE\tT\tP");

    java.util.List<SemEstimator> estimators = wrapper.getMultipleResultList();

    for (int i = 0; i < estimators.size(); i++) {
      SemEstimator estimator = estimators.get(i);

      SemIm estSem = estimator.getEstimatedSem();
      String dataName = estimator.getDataSet().getName();

      for (Parameter parameter : estSem.getFreeParameters()) {
        builder.append("\n");
        builder.append(dataName + "\t");
        builder.append(parameter.getNodeA() + "\t");
        builder.append(parameter.getNodeB() + "\t");
        builder.append(typeString(parameter) + "\t");
        builder.append(asString(paramValue(estSem, parameter)) + "\t");
        /*
         Maximum number of free parameters for which statistics will be
         calculated. (Calculating standard errors is high complexity.) Set this to
         zero to turn  off statistics calculations (which can be problematic
         sometimes).
        */
        int maxFreeParamsForStatistics = 200;
        builder.append(
            asString(estSem.getStandardError(parameter, maxFreeParamsForStatistics)) + "\t");
        builder.append(asString(estSem.getTValue(parameter, maxFreeParamsForStatistics)) + "\t");
        builder.append(asString(estSem.getPValue(parameter, maxFreeParamsForStatistics)) + "\t");
      }

      List<Node> nodes = estSem.getVariableNodes();

      for (int j = 0; j < nodes.size(); j++) {
        Node node = nodes.get(j);

        int n = estSem.getSampleSize();
        int df = n - 1;
        double mean = estSem.getMean(node);
        double stdDev = estSem.getMeanStdDev(node);
        double stdErr = stdDev / Math.sqrt(n);

        double tValue = mean / stdErr;
        double p = 2.0 * (1.0 - ProbUtils.tCdf(Math.abs(tValue), df));

        builder.append("\n");
        builder.append(dataName + "\t");
        builder.append(nodes.get(j) + "\t");
        builder.append(nodes.get(j) + "\t");
        builder.append("Mean" + "\t");
        builder.append(asString(mean) + "\t");
        builder.append(asString(stdErr) + "\t");
        builder.append(asString(tValue) + "\t");
        builder.append(asString(p) + "\t");
      }
    }

    return builder.toString();
  }

  private String asString(double value) {
    if (Double.isNaN(value)) {
      return " * ";
    } else {
      return nf.format(value);
    }
  }

  private String typeString(Parameter parameter) {
    ParamType type = parameter.getType();

    if (type == ParamType.COEF) {
      return "Coef";
    }

    if (type == ParamType.VAR) {
      // return "Variance";
      return "StdDev";
    }

    if (type == ParamType.COVAR) {
      return "Covar";
    }

    throw new IllegalStateException("Unknown param type.");
  }

  private double paramValue(SemIm im, Parameter parameter) {
    double paramValue = im.getParamValue(parameter);

    if (parameter.getType() == ParamType.VAR) {
      paramValue = Math.sqrt(paramValue);
    }

    return paramValue;
  }

  private void reestimate() {
    SemOptimizer optimizer;

    String type = wrapper.getSemOptimizerType();

    if ("Regression".equals(type)) {
      optimizer = new SemOptimizerRegression();
    } else if ("EM".equals(type)) {
      optimizer = new SemOptimizerEm();
    } else if ("Powell".equals(type)) {
      optimizer = new SemOptimizerPowell();
    } else if ("Random Search".equals(type)) {
      optimizer = new SemOptimizerScattershot();
    } else if ("RICF".equals(type)) {
      optimizer = new SemOptimizerRicf();
    } else if ("Powell".equals(type)) {
      optimizer = new SemOptimizerPowell();
    } else {
      throw new IllegalArgumentException("Unexpected optimizer " + "type: " + type);
    }

    int numRestarts = wrapper.getNumRestarts();
    optimizer.setNumRestarts(numRestarts);

    java.util.List<SemEstimator> estimators = wrapper.getMultipleResultList();
    java.util.List<SemEstimator> newEstimators = new ArrayList<>();

    for (SemEstimator estimator : estimators) {
      SemPm semPm = estimator.getSemPm();

      DataSet dataSet = estimator.getDataSet();
      ICovarianceMatrix covMatrix = estimator.getCovMatrix();

      SemEstimator newEstimator;

      if (dataSet != null) {
        newEstimator = new SemEstimator(dataSet, semPm, optimizer);
        newEstimator.setNumRestarts(numRestarts);
        newEstimator.setScoreType(wrapper.getScoreType());
      } else if (covMatrix != null) {
        newEstimator = new SemEstimator(covMatrix, semPm, optimizer);
        newEstimator.setNumRestarts(numRestarts);
        newEstimator.setScoreType(wrapper.getScoreType());
      } else {
        throw new IllegalStateException(
            "Only continuous "
                + "rectangular data sets and covariance matrices "
                + "can be processed.");
      }

      newEstimator.estimate();
      newEstimators.add(newEstimator);
    }

    wrapper.setSemEstimator(newEstimators.get(0));

    wrapper.setMultipleResultList(newEstimators);
    resetSemImEditor();
  }

  private void resetSemImEditor() {
    java.util.List<SemEstimator> semEstimators = wrapper.getMultipleResultList();

    if (semEstimators.size() == 1) {
      SemEstimator estimatedSem = semEstimators.get(0);
      SemImEditor editor = new SemImEditor(new SemImWrapper(estimatedSem.getEstimatedSem()));
      panel.removeAll();
      panel.add(editor, BorderLayout.CENTER);
      panel.revalidate();
      panel.repaint();

    } else {
      JTabbedPane tabs = new JTabbedPane();

      for (int i = 0; i < semEstimators.size(); i++) {
        SemEstimator estimatedSem = semEstimators.get(i);
        SemImEditor editor = new SemImEditor(new SemImWrapper(estimatedSem.getEstimatedSem()));
        JPanel _panel = new JPanel();
        _panel.setLayout(new BorderLayout());
        _panel.add(editor, BorderLayout.CENTER);
        tabs.addTab(estimatedSem.getDataSet().getName(), _panel);
      }

      panel.removeAll();
      panel.add(tabs);
      panel.validate();
    }
  }
}