Esempio n. 1
0
  /**
   * This method straightforwardly applies the standard definition of the numerical estimates of the
   * second order partial derivatives. See for example Section 5.7 of Numerical Recipes in C.
   */
  public double secondPartialDerivative(FittingFunction f, int i, int j, double[] p, double delt) {
    double[] arg = new double[p.length];
    System.arraycopy(p, 0, arg, 0, p.length);

    double center = f.evaluate(arg);

    arg[i] += delt;
    arg[j] += delt;
    double ff1 = f.evaluate(arg);

    arg[j] -= 2 * delt;
    double ff2 = f.evaluate(arg);

    arg[i] -= 2 * delt;
    arg[j] += 2 * delt;
    double ff3 = f.evaluate(arg);

    arg[j] -= 2 * delt;
    double ff4 = f.evaluate(arg);

    if (Double.isNaN(ff1)) {
      ff1 = center;
    }

    if (Double.isNaN(ff2)) {
      ff2 = center;
    }

    if (Double.isNaN(ff3)) {
      ff3 = center;
    }

    if (Double.isNaN(ff4)) {
      ff4 = center;
    }

    double fsSum = ff1 - ff2 - ff3 + ff4;

    return fsSum / (4.0 * delt * delt);
  }
Esempio n. 2
0
  /**
   * This method computes the information matrix or Hessian matrix of second order partial
   * derivatives of the fitting function (4B_2 on page 135 of Bollen) with respect to the free
   * freeParameters of the estimated SEM. It then computes the inverse of the the information matrix
   * and calculates the standard errors of the freeParameters as the square roots of the diagonal
   * elements of that matrix.
   *
   * @param estSem the estimated SEM.
   */
  public void computeStdErrors(ISemIm estSem) {
    //        if (!unmeasuredLatents(estSem.getSemPm()).isEmpty()) {
    //            int n = estSem.getFreeParameters().size();
    //            stdErrs = new double[n];
    //
    //            for (int i = 0; i < n; i++) {
    //                stdErrs[i] = Double.NaN;
    //            }
    //
    //            return;
    //        }

    //        this.semIm = estSem;
    estSem.setParameterBoundsEnforced(false);
    double[] paramsOriginal = estSem.getFreeParamValues();
    double delta;
    FittingFunction fcn = new SemFittingFunction(estSem);
    boolean ridder = false; // Ridder is more accurate but a lot slower.

    int n = fcn.getNumParameters();

    // Store the free freeParameters of the SemIm so that they can be reset to these
    // values.  The differentiation methods change them.
    double[] params = new double[n];
    System.arraycopy(paramsOriginal, 0, params, 0, n);

    // If the Ridder method (secondPartialDerivativeRidr) is used to search for
    // the best delta it is initially set to 0.1.  Otherwise the delta is set to
    // 0.005.  That value has worked well for those fitting functions tested to
    // date.
    if (ridder) {
      delta = 0.1;
    } else {
      delta = 0.005;
    }

    // The Hessian matrix of second order partial derivatives is called the
    // information matrix.
    TetradMatrix hess = new TetradMatrix(n, n);

    List<Parameter> freeParameters = estSem.getFreeParameters();
    boolean containsCovararianceParameter = false;

    for (Parameter p : freeParameters) {
      if (p.getType() == ParamType.COVAR) {
        containsCovararianceParameter = true;
        break;
      }
    }

    for (int i = 0; i < n; i++) {
      for (int j = i; j < n; j++) {
        Parameter pi = freeParameters.get(i);
        Parameter pj = freeParameters.get(j);

        if (!containsCovararianceParameter) {

          // Restrict off-diagonal to just collider edge freeParameters.
          if (i != j && (pi.getType() != ParamType.COEF || pj.getType() != ParamType.COEF)) {
            continue;
          }

          if (pi.getNodeB() != pj.getNodeB()) {
            continue;
          }
        }

        double v;

        if (ridder) {
          v = secondPartialDerivativeRidr(fcn, i, j, params, delta);
        } else {
          v = secondPartialDerivative(fcn, i, j, params, delta);
        }

        if (Math.abs(v) < 1e-7) {
          v = 0;
        }

        //                if (Double.isNaN(v)) {
        //                    v = 0;
        //                }

        hess.set(i, j, v);
        hess.set(j, i, v);
      }
    }

    ROWS:
    for (int i = 0; i < hess.rows(); i++) {
      for (int j = 0; j < hess.columns(); j++) {
        if (hess.get(i, j) != 0) {
          continue ROWS;
        }
      }

      //            System.out.println("Zero row for " + freeParameters.get(i));
    }

    // The diagonal elements of the inverse of the information matrix are the
    // squares of the standard errors of the freeParameters.  Their order is the
    // same as in the array of free parameter values stored in paramsOriginal.
    try {

      TetradMatrix hessInv = hess.inverse();
      //            TetradMatrix hessInv = hess.ginverse();

      //            System.out.println("Inverse: " + hessInv);

      //            for (int i = 0; i < freeParameters.size(); i++) {
      //                System.out.println(i + " = " + freeParameters.get(i));
      //            }

      stdErrs = new double[n];

      // Hence the standard errors of the freeParameters are the square roots of the
      // diagonal elements of the inverse of the information matrix.
      for (int i = 0; i < n; i++) {
        double v = Math.sqrt((2.0 / (estSem.getSampleSize() - 1)) * hessInv.get(i, i));

        if (v == 0) {
          System.out.println("v = " + v + " hessInv(i, i) = " + hessInv.get(i, i));
        }

        if (v == 0) {
          stdErrs[i] = Double.NaN;
        } else {
          stdErrs[i] = v;
        }
      }
    } catch (Exception e) {
      e.printStackTrace();

      stdErrs = new double[n];

      for (int i = 0; i < n; i++) {
        stdErrs[i] = Double.NaN;
      }
    }

    // Restore the freeParameters of the estimated SEM to their original values.
    estSem.setFreeParamValues(paramsOriginal);
    estSem.setParameterBoundsEnforced(true);
  }
Esempio n. 3
0
  /**
   * This method implements Ridder's algorithm for computing the second order partial derivatives.
   * It is a translation of the C program in section 5.7 of Numerical Recipes in C. It is more
   * robust than the above method in that it searches for a perferred value of delt. But based on
   * our experience to date with SEM fitting functions, the above method seems to be adequately
   * accurate and faster that this one.
   */
  public double secondPartialDerivativeRidr(
      FittingFunction f, int i, int j, double[] args, double delt) {

    double[] arg = new double[args.length];
    double[][] a = new double[NTAB][NTAB];
    double hh = delt;
    double errt;
    double ans = 0.0;
    double fac;

    System.arraycopy(args, 0, arg, 0, args.length);

    double center = f.evaluate(arg);

    arg[i] += delt;
    arg[j] += delt;
    double ff1 = f.evaluate(arg);

    arg[j] -= 2 * delt;
    double ff2 = f.evaluate(arg);

    arg[i] -= 2 * delt;
    arg[j] += 2 * delt;
    double ff3 = f.evaluate(arg);

    arg[j] -= 2 * delt;
    double ff4 = f.evaluate(arg);

    if (Double.isNaN(ff1)) {
      ff1 = center;
    }

    if (Double.isNaN(ff2)) {
      ff2 = center;
    }

    if (Double.isNaN(ff3)) {
      ff3 = center;
    }

    if (Double.isNaN(ff4)) {
      ff4 = center;
    }

    a[0][0] = (ff1 - ff2 - ff3 + ff4) / (4.0 * delt * delt);
    double err = BIG;

    for (int ii = 1; ii < NTAB; ii++) {
      hh /= CON;

      System.arraycopy(args, 0, arg, 0, args.length);

      arg[i] += hh;
      arg[j] += hh;
      ff1 = f.evaluate(arg);

      arg[j] -= 2 * hh;
      ff2 = f.evaluate(arg);

      arg[i] -= 2 * hh;
      arg[j] += 2 * hh;
      ff3 = f.evaluate(arg);

      arg[j] -= 2 * hh;
      ff4 = f.evaluate(arg);

      if (Double.isNaN(ff1)) {
        ff1 = center;
      }

      if (Double.isNaN(ff2)) {
        ff2 = center;
      }

      if (Double.isNaN(ff3)) {
        ff3 = center;
      }

      if (Double.isNaN(ff4)) {
        ff4 = center;
      }

      a[0][ii] = (ff1 - ff2 - ff3 + ff4) / (4.0 * hh * hh);

      fac = CON2;

      for (int jj = 1; jj < ii; jj++) {
        a[jj][ii] = (a[jj - 1][ii] * fac - a[jj - 1][ii - 1]) / (fac - 1.0);
        fac = CON2 * fac;
        errt =
            Math.max(Math.abs(a[jj][ii] - a[jj - 1][ii]), Math.abs(a[jj][ii] - a[jj - 1][ii - 1]));
        if (errt < err) {
          err = errt;
          ans = a[jj][ii];
        }
      }

      if (Math.abs(a[ii][ii] - a[ii - 1][ii - 1]) >= SAFE * err) {
        break;
      }
    }

    return ans;
  }