private double paramValue(SemIm im, Parameter parameter) { double paramValue = im.getParamValue(parameter); if (parameter.getType() == ParamType.VAR) { paramValue = Math.sqrt(paramValue); } return paramValue; }
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."); }
/** * Computes the maximum likelihood function value for the given freeParameters values as given * by the optimizer. These values are mapped to parameter values. */ public double evaluate(double[] parameters) { List<Parameter> _parameters = sem.getSemPm().getFreeParameters(); for (int i = 0; i < _parameters.size(); i++) { Parameter parameter = _parameters.get(i); if (parameter.getType() == ParamType.VAR && parameters[i] < 0) { parameters[i] = 0; } } sem.setFreeParamValues(parameters); // This needs to be FML-- see Bollen p. 109. // try { return sem.getScore(); // } catch (Exception e) { // return Double.NEGATIVE_INFINITY; // } }
private static Element makeJointErrorDistribution(SemIm semIm) { Element jointErrorElement = new Element(SemXmlConstants.JOINT_ERROR_DISTRIBUTION); Element normal; Parameter param; for (Parameter parameter : semIm.getSemPm().getParameters()) { param = parameter; if (param.getType() == ParamType.COVAR) { normal = new Element(SemXmlConstants.NORMAL); normal.addAttribute(new Attribute(SemXmlConstants.NODE_1, param.getNodeA().getName())); normal.addAttribute(new Attribute(SemXmlConstants.NODE_2, param.getNodeB().getName())); normal.addAttribute( new Attribute(SemXmlConstants.COVARIANCE, Double.toString(param.getStartingValue()))); jointErrorElement.appendChild(normal); } } return jointErrorElement; }
private static Element makeEdges(SemIm semIm) { Element edgesElement = new Element(SemXmlConstants.EDGES); Parameter param; Element edge; for (Parameter parameter : semIm.getSemPm().getParameters()) { param = parameter; if (param.getType() == ParamType.COEF) { edge = new Element(SemXmlConstants.EDGE); edge.addAttribute(new Attribute(SemXmlConstants.CAUSE_NODE, param.getNodeA().getName())); edge.addAttribute(new Attribute(SemXmlConstants.EFFECT_NODE, param.getNodeB().getName())); edge.addAttribute( new Attribute(SemXmlConstants.VALUE, Double.toString(semIm.getParamValue(param)))); edge.addAttribute( new Attribute(SemXmlConstants.FIXED, Boolean.valueOf(param.isFixed()).toString())); edgesElement.appendChild(edge); } } return edgesElement; }
/** * 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); }
/** * Constructs a new standardized SEM IM from the freeParameters in the given SEM IM. * * @param im Stop asking me for these things! The given SEM IM!!! * @param initialization CALCULATE_FROM_SEM if the initial values will be calculated from the * given SEM IM; INITIALIZE_FROM_DATA if data will be simulated from the given SEM, * standardized, and estimated. */ public StandardizedSemIm(SemIm im, Initialization initialization) { this.semPm = new SemPm(im.getSemPm()); this.semGraph = new SemGraph(semPm.getGraph()); semGraph.setShowErrorTerms(true); if (semGraph.existsDirectedCycle()) { throw new IllegalArgumentException("The cyclic case is not handled."); } if (initialization == Initialization.CALCULATE_FROM_SEM) { // This code calculates the new coefficients directly from the old ones. edgeParameters = new HashMap<Edge, Double>(); List<Node> nodes = im.getVariableNodes(); TetradMatrix impliedCovar = im.getImplCovar(true); for (Parameter parameter : im.getSemPm().getParameters()) { if (parameter.getType() == ParamType.COEF) { Node a = parameter.getNodeA(); Node b = parameter.getNodeB(); int aindex = nodes.indexOf(a); int bindex = nodes.indexOf(b); double vara = impliedCovar.get(aindex, aindex); double stda = Math.sqrt(vara); double varb = impliedCovar.get(bindex, bindex); double stdb = Math.sqrt(varb); double oldCoef = im.getEdgeCoef(a, b); double newCoef = (stda / stdb) * oldCoef; edgeParameters.put(Edges.directedEdge(a, b), newCoef); } else if (parameter.getType() == ParamType.COVAR) { Node a = parameter.getNodeA(); Node b = parameter.getNodeB(); Node exoa = semGraph.getExogenous(a); Node exob = semGraph.getExogenous(b); double covar = im.getErrCovar(a, b) / Math.sqrt(im.getErrVar(a) * im.getErrVar(b)); edgeParameters.put(Edges.bidirectedEdge(exoa, exob), covar); } } } else { // This code estimates the new coefficients from simulated data from the old model. DataSet dataSet = im.simulateData(1000, false); TetradMatrix _dataSet = dataSet.getDoubleData(); _dataSet = DataUtils.standardizeData(_dataSet); DataSet dataSetStandardized = ColtDataSet.makeData(dataSet.getVariables(), _dataSet); SemEstimator estimator = new SemEstimator(dataSetStandardized, im.getSemPm()); SemIm imStandardized = estimator.estimate(); edgeParameters = new HashMap<Edge, Double>(); for (Parameter parameter : imStandardized.getSemPm().getParameters()) { if (parameter.getType() == ParamType.COEF) { Node a = parameter.getNodeA(); Node b = parameter.getNodeB(); double coef = imStandardized.getEdgeCoef(a, b); edgeParameters.put(Edges.directedEdge(a, b), coef); } else if (parameter.getType() == ParamType.COVAR) { Node a = parameter.getNodeA(); Node b = parameter.getNodeB(); Node exoa = semGraph.getExogenous(a); Node exob = semGraph.getExogenous(b); double covar = -im.getErrCovar(a, b) / Math.sqrt(im.getErrVar(a) * im.getErrVar(b)); edgeParameters.put(Edges.bidirectedEdge(exoa, exob), covar); } } } this.measuredNodes = Collections.unmodifiableList(semPm.getMeasuredNodes()); }