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); } }
/** * @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; }
private boolean existsUnblockedSemiDirectedPath(Node from, Node to, List<Node> cond, Graph G) { Queue<Node> Q = new LinkedList<Node>(); Set<Node> V = new HashSet<Node>(); Q.offer(from); V.add(from); while (!Q.isEmpty()) { Node t = Q.remove(); if (t == to) return true; for (Node u : G.getAdjacentNodes(t)) { Edge edge = G.getEdge(t, u); Node c = Edges.traverseSemiDirected(t, edge); if (c == null) continue; if (cond.contains(c)) continue; if (c == to) return true; if (!V.contains(c)) { V.add(c); Q.offer(c); } } } return false; }
/** * Sets the covariance for the a<->b edge to the given covariance, if within range. Otherwise does * nothing. * * @param a a <-> b * @param b a <-> b * @param covar The covariance of a <-> b. * @return true if the coefficent was set (i.e. was within range), false if not. */ public boolean setErrorCovariance(Node a, Node b, final double covar) { Edge edge = Edges.bidirectedEdge(semGraph.getExogenous(a), semGraph.getExogenous(b)); if (edgeParameters.get(edge) == null) { throw new IllegalArgumentException("Not a covariance parameter in this model: " + edge); } if (editingEdge == null || !edge.equals(editingEdge)) { range = getParameterRange(edge); editingEdge = edge; } if (covar > range.getLow() && covar < range.getHigh()) { edgeParameters.put(edge, covar); return true; } else { return false; } // if (!paramInBounds(edge, coef)) { // edgeParameters.put(edge, d); // return false; // } // // edgeParameters.put(edge, coef); // return true; // if (!paramInBounds(edge, covar)) { // edgeParameters.put(edge, d); // return false; // } // // edgeParameters.put(edge, covar); // return true; }
/** * Sets the coefficient for the a->b edge to the given coefficient, if within range. Otherwise * does nothing. * * @param a a -> b * @param b a -> b * @param coef The coefficient of a -> b. * @return true if the coefficent was set (i.e. was within range), false if not. */ public boolean setEdgeCoefficient(Node a, Node b, final double coef) { Edge edge = Edges.directedEdge(a, b); if (edgeParameters.get(edge) == null) { throw new NullPointerException("Not a coefficient parameter in this model: " + edge); } if (editingEdge == null || !edge.equals(editingEdge)) { range = getParameterRange(edge); editingEdge = edge; } if (coef > range.getLow() && coef < range.getHigh()) { edgeParameters.put(edge, coef); return true; } return false; // if (!paramInBounds(edge, coef)) { // edgeParameters.put(edge, d); // return false; // } // // edgeParameters.put(edge, coef); // return true; }
private boolean isUndirected(Graph graph, Node x, Node y) { List<Edge> edges = graph.getEdges(x, y); if (edges.size() == 1) { Edge edge = graph.getEdge(x, y); return Edges.isUndirectedEdge(edge); } return false; }
/** * @param a a->b * @param b a->b * @return The coefficient for a->b. */ public double getErrorCovariance(Node a, Node b) { Edge edge = Edges.bidirectedEdge(semGraph.getExogenous(a), semGraph.getExogenous(b)); Double d = edgeParameters.get(edge); if (d == null) { throw new IllegalArgumentException("Not a covariance parameter in this model: " + edge); } return d; }
/** * @param a a->b * @param b a->b * @return The coefficient for a->b. */ public double getEdgeCoefficient(Node a, Node b) { Edge edge = Edges.directedEdge(a, b); Double d = edgeParameters.get(edge); if (d == null) { return Double.NaN; // throw new IllegalArgumentException("Not a directed edge in this model: " + // edge); } return d; }
private Graph pickDag(Graph graph) { SearchGraphUtils.basicPattern(graph, false); addRequiredEdges(graph); boolean containsUndirected; do { containsUndirected = false; for (Edge edge : graph.getEdges()) { if (Edges.isUndirectedEdge(edge)) { containsUndirected = true; graph.removeEdge(edge); Edge _edge = Edges.directedEdge(edge.getNode1(), edge.getNode2()); graph.addEdge(_edge); } } meekOrient(graph, getKnowledge()); } while (containsUndirected); return graph; }
private void ruleR1(Graph skeleton, Graph graph, List<Node> nodes) { for (Node node : nodes) { SortedMap<Double, String> scoreReports = new TreeMap<Double, String>(); List<Node> adj = skeleton.getAdjacentNodes(node); DepthChoiceGenerator gen = new DepthChoiceGenerator(adj.size(), adj.size()); int[] choice; double maxScore = Double.NEGATIVE_INFINITY; List<Node> parents = null; while ((choice = gen.next()) != null) { List<Node> _parents = GraphUtils.asList(choice, adj); double score = score(node, _parents); scoreReports.put(-score, _parents.toString()); if (score > maxScore) { maxScore = score; parents = _parents; } } for (double score : scoreReports.keySet()) { TetradLogger.getInstance() .log( "score", "For " + node + " parents = " + scoreReports.get(score) + " score = " + -score); } TetradLogger.getInstance().log("score", ""); if (parents == null) { continue; } if (normal(node, parents)) continue; for (Node _node : adj) { if (parents.contains(_node)) { Edge parentEdge = Edges.directedEdge(_node, node); if (!graph.containsEdge(parentEdge)) { graph.addEdge(parentEdge); } } } } }
/** @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(); }
/** Get all nodes that are connected to Y by an undirected edge and not adjacent to X. */ private static List<Node> getTNeighbors(Node x, Node y, Graph graph) { List<Edge> yEdges = graph.getEdges(y); List<Node> tNeighbors = new ArrayList<Node>(); for (Edge edge : yEdges) { if (!Edges.isUndirectedEdge(edge)) { continue; } Node z = edge.getDistalNode(y); if (graph.isAdjacentTo(z, x)) { continue; } tNeighbors.add(z); } return tNeighbors; }
/** * Find all nodes that are connected to Y by an undirected edge that are adjacent to X (that is, * by undirected or directed edge). */ private static List<Node> getNaYX(Node x, Node y, Graph graph) { List<Edge> yEdges = graph.getEdges(y); List<Node> nayx = new ArrayList<Node>(); for (Edge edge : yEdges) { if (!Edges.isUndirectedEdge(edge)) { continue; } Node z = edge.getDistalNode(y); if (!graph.isAdjacentTo(z, x)) { continue; } nayx.add(z); } return nayx; }
private void initializeArrowsBackward(Graph graph) { sortedArrows.clear(); lookupArrows.clear(); for (Edge edge : graph.getEdges()) { Node x = edge.getNode1(); Node y = edge.getNode2(); if (!knowledgeEmpty()) { if (!getKnowledge().noEdgeRequired(x.getName(), y.getName())) { continue; } } if (Edges.isDirectedEdge(edge)) { calculateArrowsBackward(x, y, graph); } else { calculateArrowsBackward(x, y, graph); calculateArrowsBackward(y, x, graph); } } }
/** * @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; }
public ParameterRange getCoefficientRange(Node a, Node b) { return getParameterRange(Edges.directedEdge(a, b)); }
/** * @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); }
public ParameterRange getCovarianceRange(Node a, Node b) { return getParameterRange( Edges.bidirectedEdge(semGraph.getExogenous(a), semGraph.getExogenous(b))); }
/** * 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; }
/** Do an actual deletion (Definition 13 from Chickering, 2002). */ private void delete(Node x, Node y, List<Node> subset, Graph graph, double bump) { Edge trueEdge = null; if (trueGraph != null) { Node _x = trueGraph.getNode(x.getName()); Node _y = trueGraph.getNode(y.getName()); trueEdge = trueGraph.getEdge(_x, _y); } if (log && verbose) { Edge oldEdge = graph.getEdge(x, y); String label = trueGraph != null && trueEdge != null ? "*" : ""; TetradLogger.getInstance() .log( "deletedEdges", (graph.getNumEdges() - 1) + ". DELETE " + oldEdge + " " + subset + " (" + bump + ") " + label); out.println( (graph.getNumEdges() - 1) + ". DELETE " + oldEdge + " " + subset + " (" + bump + ") " + label); } else { int numEdges = graph.getNumEdges() - 1; if (numEdges % 50 == 0) out.println(numEdges); } graph.removeEdge(x, y); for (Node h : subset) { Edge oldEdge = graph.getEdge(y, h); graph.removeEdge(y, h); graph.addDirectedEdge(y, h); if (log) { TetradLogger.getInstance() .log("directedEdges", "--- Directing " + oldEdge + " to " + graph.getEdge(y, h)); } if (verbose) { out.println("--- Directing " + oldEdge + " to " + graph.getEdge(y, h)); } if (Edges.isUndirectedEdge(graph.getEdge(x, h))) { if (!graph.isAdjacentTo(x, h)) throw new IllegalArgumentException("Not adjacent: " + x + ", " + h); oldEdge = graph.getEdge(x, h); graph.removeEdge(x, h); graph.addDirectedEdge(x, h); if (log) { TetradLogger.getInstance() .log("directedEdges", "--- Directing " + oldEdge + " to " + graph.getEdge(x, h)); } if (verbose) { out.println("--- Directing " + oldEdge + " to " + graph.getEdge(x, h)); } } } }
/** * 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()); }