/******************************************************************************* * Copyright (c) 2013 Michael Kutschke. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Michael Kutschke - initial API and implementation ******************************************************************************/ package org.eclipse.recommenders.jayes.util.triangulation; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.recommenders.jayes.util.Graph; import org.eclipse.recommenders.jayes.util.Graph.Edge; /** * Quotient graphs are special data structures for the perfect elimination order problem. Their size stays in O(|E|) * where E is the set of edges. Using plain graphs would result in a storage complexity of O(|E*|), where E* is the set * of Edges united with the set of "fill-in" edges generated during elimination. <br/> * <br/> * See "An Approximate Minimum Degree Ordering Algorithm" (Amestoy et al. 1996) */ public class QuotientGraph { private Graph variables; private Graph variablesToElements; private final Map<Integer, Set<Integer>> neighborCache = new HashMap<Integer, Set<Integer>>(); public QuotientGraph(Graph graph) { this.variables = graph.clone(); this.variablesToElements = new Graph(); variablesToElements.initialize(variables.getAdjacency().size()); } public Set<Integer> getNeighbors(int variable) { if (neighborCache.containsKey(variable)) { return neighborCache.get(variable); } Set<Integer> neighbors = new HashSet<Integer>(); neighbors.addAll(getNeighbors(variables, variable)); for (Edge e : variablesToElements.getIncidentEdges(variable)) { neighbors.addAll(getNeighbors(variablesToElements, e.getSecond())); } neighbors.remove(variable); neighborCache.put(variable, Collections.unmodifiableSet(neighbors)); return Collections.unmodifiableSet(neighbors); } public void eliminate(int variable) { for (int elementNeighbor : getNeighbors(variablesToElements, variable)) { // merge eliminated nodes merge(variablesToElements, variable, elementNeighbor); } for (Edge e : variables.getIncidentEdges(variable)) { // interconnect neigbors variablesToElements.addEdge(variable, e.getSecond()); } virtualRemoveNode(variables, variable); neighborCache.clear(); } private List<Integer> getNeighbors(Graph graph, int var) { List<Integer> elementNeighbors = new ArrayList<Integer>(); for (Edge e : graph.getIncidentEdges(var)) { elementNeighbors.add(e.getSecond()); } return elementNeighbors; } public void merge(Graph graph, int v1, int v2) { for (int e2 : getNeighbors(graph, v2)) { if (v1 != e2) { graph.addEdge(v1, e2); } } virtualRemoveNode(graph, v2); } // isolating node = virtually removing it private void virtualRemoveNode(Graph graph, final int node) { while (!graph.getIncidentEdges(node).isEmpty()) { graph.removeEdge(graph.getIncidentEdges(node).iterator().next()); } } // TODO indistinguishable variables and external degree }