/** * @return Returns the error covariance matrix of the model. i.e. [a][b] is the covariance of E_a * and E_b, with [a][a] of course being the variance of E_a. THESE ARE NOT PARAMETERS OF THE * MODEL; THEY ARE CALCULATED. Note that elements of this matrix may be Double.NaN; this * indicates that these elements cannot be calculated. */ private TetradMatrix errCovar(Map<Node, Double> errorVariances) { List<Node> variableNodes = getVariableNodes(); List<Node> errorNodes = new ArrayList<Node>(); for (Node node : variableNodes) { errorNodes.add(semGraph.getExogenous(node)); } TetradMatrix errorCovar = new TetradMatrix(errorVariances.size(), errorVariances.size()); for (int index = 0; index < errorNodes.size(); index++) { Node error = errorNodes.get(index); double variance = getErrorVariance(error); errorCovar.set(index, index, variance); } for (int index1 = 0; index1 < errorNodes.size(); index1++) { for (int index2 = 0; index2 < errorNodes.size(); index2++) { Node error1 = errorNodes.get(index1); Node error2 = errorNodes.get(index2); Edge edge = semGraph.getEdge(error1, error2); if (edge != null && Edges.isBidirectedEdge(edge)) { double covariance = getErrorCovariance(error1, error2); errorCovar.set(index1, index2, covariance); } } } return errorCovar; }
public boolean containsParameter(Edge edge) { if (Edges.isBidirectedEdge(edge)) { edge = Edges.bidirectedEdge( semGraph.getExogenous(edge.getNode1()), semGraph.getExogenous(edge.getNode2())); } return edgeParameters.keySet().contains(edge); }
public void setParameterValue(Edge edge, double value) { if (Edges.isDirectedEdge(edge)) { setEdgeCoefficient(edge.getNode1(), edge.getNode2(), value); } else if (Edges.isBidirectedEdge(edge)) { setErrorCovariance(edge.getNode1(), edge.getNode2(), value); } else { throw new IllegalArgumentException( "Only directed and bidirected edges are supported: " + edge); } }
private void uncorrelationExogenousVariables() { Graph graph = getWorkbench().getGraph(); Set<Edge> edges = graph.getEdges(); for (Edge edge : edges) { if (Edges.isBidirectedEdge(edge)) { try { graph.removeEdge(edge); } catch (Exception e) { // Ignore. } } } }
private void correlateExogenousVariables() { Graph graph = getWorkbench().getGraph(); if (graph instanceof Dag) { JOptionPane.showMessageDialog( JOptionUtils.centeringComp(), "Cannot add bidirected edges to DAG's."); return; } List<Node> nodes = graph.getNodes(); List<Node> exoNodes = new LinkedList<Node>(); for (int i = 0; i < nodes.size(); i++) { Node node = nodes.get(i); if (graph.isExogenous(node)) { exoNodes.add(node); } } for (int i = 0; i < exoNodes.size(); i++) { loop: for (int j = i + 1; j < exoNodes.size(); j++) { Node node1 = exoNodes.get(i); Node node2 = exoNodes.get(j); List<Edge> edges = graph.getEdges(node1, node2); for (int k = 0; k < edges.size(); k++) { Edge edge = edges.get(k); if (Edges.isBidirectedEdge(edge)) { continue loop; } } graph.addBidirectedEdge(node1, node2); } } }
/** * @return The edge coefficient matrix of the model, a la SemIm. Note that this will normally need * to be transposed, since [a][b] is the edge coefficient for a-->b, not b-->a. Sorry. * History. THESE ARE PARAMETERS OF THE MODEL--THE ONLY PARAMETERS. */ public TetradMatrix edgeCoef() { List<Node> variableNodes = getVariableNodes(); TetradMatrix edgeCoef = new TetradMatrix(variableNodes.size(), variableNodes.size()); for (Edge edge : edgeParameters.keySet()) { if (Edges.isBidirectedEdge(edge)) { continue; } Node a = edge.getNode1(); Node b = edge.getNode2(); int aindex = variableNodes.indexOf(a); int bindex = variableNodes.indexOf(b); double coef = edgeParameters.get(edge); edgeCoef.set(aindex, bindex, coef); } return edgeCoef; }
/** @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(); }
/** Constructs a new SemPm from the given SemGraph. */ public GeneralizedSemPm(SemGraph graph) { if (graph == null) { throw new NullPointerException("Graph must not be null."); } // if (graph.existsDirectedCycle()) { // throw new IllegalArgumentExcneption("Cycles are not supported."); // } // Cannot afford to allow error terms on this graph to be shown or hidden from the outside; must // make a // hidden copy of it and make sure error terms are shown. this.graph = new SemGraph(graph); this.graph.setShowErrorTerms(true); for (Edge edge : this.graph.getEdges()) { if (Edges.isBidirectedEdge(edge)) { throw new IllegalArgumentException( "The generalized SEM PM cannot currently deal with bidirected " + "edges. Sorry."); } } this.nodes = Collections.unmodifiableList(this.graph.getNodes()); for (Node node : nodes) { namesToNodes.put(node.getName(), node); } this.variableNodes = new ArrayList<>(); this.measuredNodes = new ArrayList<>(); for (Node variable : this.nodes) { if (variable.getNodeType() == NodeType.MEASURED || variable.getNodeType() == NodeType.LATENT) { variableNodes.add(variable); } if (variable.getNodeType() == NodeType.MEASURED) { measuredNodes.add(variable); } } this.errorNodes = new ArrayList<>(); for (Node variable : this.variableNodes) { List<Node> parents = this.graph.getParents(variable); boolean added = false; for (Node _node : parents) { if (_node.getNodeType() == NodeType.ERROR) { errorNodes.add(_node); added = true; break; } } if (!added) { errorNodes.add(null); } } this.referencedParameters = new HashMap<>(); this.referencedNodes = new HashMap<>(); this.nodeExpressions = new HashMap<>(); this.nodeExpressionStrings = new HashMap<>(); this.parameterExpressions = new HashMap<>(); this.parameterExpressionStrings = new HashMap<>(); this.parameterEstimationInitializationExpressions = new HashMap<>(); this.parameterEstimationInitializationExpressionStrings = new HashMap<>(); this.startsWithParametersTemplates = new HashMap<>(); this.startsWithParametersEstimationInitializationTemplates = new HashMap<>(); this.variableNames = new ArrayList<>(); for (Node _node : variableNodes) variableNames.add(_node.getName()); for (Node _node : errorNodes) variableNames.add(_node.getName()); try { List<Node> variableNodes = getVariableNodes(); for (Node node : variableNodes) { if (!this.graph.isParameterizable(node)) continue; if (nodeExpressions.get(node) != null) { continue; } String variablestemplate = getVariablesTemplate(); String formula = TemplateExpander.getInstance().expandTemplate(variablestemplate, this, node); setNodeExpression(node, formula); Set<String> parameters = getReferencedParameters(node); String parametersTemplate = getParametersTemplate(); for (String parameter : parameters) { if (parameterExpressions.get(parameter) == null) { if (parametersTemplate != null) { setParameterExpression(parameter, parametersTemplate); } else if (this.graph.isTimeLagModel()) { String expressionString = "Split(-0.9, -.1, .1, 0.9)"; setParameterExpression(parameter, expressionString); setParametersTemplate(expressionString); } else { String expressionString = "Split(-1.5, -.5, .5, 1.5)"; setParameterExpression(parameter, expressionString); setParametersTemplate(expressionString); } } } for (String parameter : parameters) { if (parameterEstimationInitializationExpressions.get(parameter) == null) { if (parametersTemplate != null) { setParameterEstimationInitializationExpression(parameter, parametersTemplate); } else if (this.graph.isTimeLagModel()) { String expressionString = "Split(-0.9, -.1, .1, 0.9)"; setParameterEstimationInitializationExpression(parameter, expressionString); } else { String expressionString = "Split(-1.5, -.5, .5, 1.5)"; setParameterEstimationInitializationExpression(parameter, expressionString); } } setStartsWithParametersTemplate("s", "Split(-1.5, -.5, .5, 1.5)"); setStartsWithParametersEstimationInitializaationTemplate( "s", "Split(-1.5, -.5, .5, 1.5)"); } } for (Node node : errorNodes) { if (node == null) continue; String template = getErrorsTemplate(); String formula = TemplateExpander.getInstance().expandTemplate(template, this, node); setNodeExpression(node, formula); Set<String> parameters = getReferencedParameters(node); setStartsWithParametersTemplate("s", "U(1, 3)"); setStartsWithParametersEstimationInitializaationTemplate("s", "U(1, 3)"); for (String parameter : parameters) { setParameterExpression(parameter, "U(1, 3)"); } } } catch (ParseException e) { throw new IllegalStateException("Parse error in constructing initial model.", e); } }
/** * Calculates the error variance for the given error node, given all of the coefficient values in * the model. * * @param error An error term in the model--i.e. a variable with NodeType.ERROR. * @return The value of the error variance, or Double.NaN is the value is undefined. */ private double calculateErrorVarianceFromParams(Node error) { error = semGraph.getNode(error.getName()); Node child = semGraph.getChildren(error).get(0); List<Node> parents = semGraph.getParents(child); double otherVariance = 0; for (Node parent : parents) { if (parent == error) continue; double coef = getEdgeCoefficient(parent, child); otherVariance += coef * coef; } if (parents.size() >= 2) { ChoiceGenerator gen = new ChoiceGenerator(parents.size(), 2); int[] indices; while ((indices = gen.next()) != null) { Node node1 = parents.get(indices[0]); Node node2 = parents.get(indices[1]); double coef1, coef2; if (node1.getNodeType() != NodeType.ERROR) { coef1 = getEdgeCoefficient(node1, child); } else { coef1 = 1; } if (node2.getNodeType() != NodeType.ERROR) { coef2 = getEdgeCoefficient(node2, child); } else { coef2 = 1; } List<List<Node>> treks = GraphUtils.treksIncludingBidirected(semGraph, node1, node2); double cov = 0.0; for (List<Node> trek : treks) { double product = 1.0; for (int i = 1; i < trek.size(); i++) { Node _node1 = trek.get(i - 1); Node _node2 = trek.get(i); Edge edge = semGraph.getEdge(_node1, _node2); double factor; if (Edges.isBidirectedEdge(edge)) { factor = edgeParameters.get(edge); } else if (!edgeParameters.containsKey(edge)) { factor = 1; } else if (semGraph.isParentOf(_node1, _node2)) { factor = getEdgeCoefficient(_node1, _node2); } else { factor = getEdgeCoefficient(_node2, _node1); } product *= factor; } cov += product; } otherVariance += 2 * coef1 * coef2 * cov; } } return 1.0 - otherVariance <= 0 ? Double.NaN : 1.0 - otherVariance; }
/** * @param edge a->b or a<->b. * @return the range of the covariance parameter for a->b or a<->b. */ public ParameterRange getParameterRange(Edge edge) { if (Edges.isBidirectedEdge(edge)) { edge = Edges.bidirectedEdge( semGraph.getExogenous(edge.getNode1()), semGraph.getExogenous(edge.getNode2())); } if (!(edgeParameters.keySet().contains(edge))) { throw new IllegalArgumentException("Not an edge in this model: " + edge); } double initial = edgeParameters.get(edge); if (initial == Double.NEGATIVE_INFINITY) { initial = Double.MIN_VALUE; } else if (initial == Double.POSITIVE_INFINITY) { initial = Double.MAX_VALUE; } double value = initial; // look upward for a point that fails. double high = value + 1; while (paramInBounds(edge, high)) { high = value + 2 * (high - value); if (high == Double.POSITIVE_INFINITY) { break; } } // find the boundary using binary search. double rangeHigh; if (high == Double.POSITIVE_INFINITY) { rangeHigh = high; } else { double low = value; while (high - low > 1e-10) { double midpoint = (high + low) / 2.0; if (paramInBounds(edge, midpoint)) { low = midpoint; } else { high = midpoint; } } rangeHigh = (high + low) / 2.0; } // look downard for a point that fails. double low = value - 1; while (paramInBounds(edge, low)) { low = value - 2 * (value - low); if (low == Double.NEGATIVE_INFINITY) { break; } } double rangeLow; if (low == Double.NEGATIVE_INFINITY) { rangeLow = low; } else { // find the boundary using binary search. high = value; while (high - low > 1e-10) { double midpoint = (high + low) / 2.0; if (paramInBounds(edge, midpoint)) { high = midpoint; } else { low = midpoint; } } rangeLow = (high + low) / 2.0; } if (Edges.isDirectedEdge(edge)) { edgeParameters.put(edge, initial); } else if (Edges.isBidirectedEdge(edge)) { edgeParameters.put(edge, initial); } return new ParameterRange(edge, value, rangeLow, rangeHigh); }