/*******************************************************************************
* 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.LinkedHashSet;
import java.util.Set;
import org.eclipse.recommenders.jayes.util.Graph;
/**
* 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 final Graph graph;
private final boolean[] isElement;
private final Set<Integer>[] neighborCache;
@SuppressWarnings("unchecked")
public QuotientGraph(Graph graph) {
this.graph = graph.clone();
isElement = new boolean[graph.numberOfVertices()];
neighborCache = new Set[graph.numberOfVertices()];
}
public Set<Integer> getNeighbors(int variable) {
if (neighborCache[variable] != null) {
return neighborCache[variable];
}
Set<Integer> neighbors = new LinkedHashSet<Integer>();
if (!isElement[variable]) {
for (int neighbor : graph.getNeighbors(variable)) {
if (isElement[neighbor]) {
neighbors.addAll(graph.getNeighbors(neighbor));
} else {
neighbors.add(neighbor);
}
}
neighbors.remove(variable);
}
neighborCache[variable] = neighbors;
return neighborCache[variable];
}
public void eliminate(int variable) {
if (isElement[variable]) {
throw new IllegalArgumentException("variable already eliminated");
}
Set<Integer> neighbors = getNeighbors(variable);
neighborCache[variable] = null;
for (Integer neighbor : neighbors) {
neighborCache[neighbor] = null;
}
for (int elementNeighbor : new ArrayList<Integer>(graph.getNeighbors(variable))) { // merge eliminated nodes
if (isElement[elementNeighbor]) {
merge(graph, variable, elementNeighbor);
}
}
isElement[variable] = true;
}
private void merge(Graph graph, int v1, int v2) {
for (int n : graph.getNeighbors(v2)) {
if (v1 != n) {
graph.addEdge(v1, n);
}
}
virtualRemoveNode(graph, v2);
}
// isolating node = virtually removing it
private void virtualRemoveNode(Graph graph, final int node) {
graph.removeIncidentEdges(node);
}
// TODO indistinguishable variables and external degree
}