/* ==========================================
* JGraphT : a free Java graph-theory library
* ==========================================
*
* Project Info: http://jgrapht.sourceforge.net/
* Project Creator: Barak Naveh (http://sourceforge.net/users/barak_naveh)
*
* (C) Copyright 2003-2008, by Barak Naveh and Contributors.
*
* This program and the accompanying materials are dual-licensed under
* either
*
* (a) the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation, or (at your option) any
* later version.
*
* or (per the licensee's choosing)
*
* (b) the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation.
*/
/* -----------------
* AbstractExhaustiveIsomorphismInspector.java
* -----------------
* (C) Copyright 2005-2008, by Assaf Lehr and Contributors.
*
* Original Author: Assaf Lehr
* Contributor(s): -
*
* $Id: AbstractExhaustiveIsomorphismInspector.java 485 2006-06-26 09:12:14Z
* perfecthash $
*
* Changes
* -------
*/
package org.jgrapht.experimental.isomorphism;
import java.util.*;
import org.jgrapht.*;
import org.jgrapht.experimental.equivalence.*;
import org.jgrapht.experimental.permutation.*;
import org.jgrapht.util.*;
/**
* Abstract base for isomorphism inspectors which exhaustively test the possible
* mappings between graphs. The current algorithms do not support graphs with
* multiple edges (Multigraph / Pseudograph). For the maintainer: The reason is
* the use of GraphOrdering which currently does not support all graph types.
*
* @author Assaf Lehr
* @since May 20, 2005 ver5.3
*/
abstract class AbstractExhaustiveIsomorphismInspector<V, E>
implements GraphIsomorphismInspector<IsomorphismRelation>
{
public static EquivalenceComparator<Object, Object>
edgeDefaultIsomorphismComparator =
new UniformEquivalenceComparator<Object, Object>();
public static EquivalenceComparator<Object, Object>
vertexDefaultIsomorphismComparator =
new UniformEquivalenceComparator<Object, Object>();
protected EquivalenceComparator<? super E, ? super Graph<V, ? super E>>
edgeComparator;
protected EquivalenceComparator<? super V, ? super Graph<? super V, E>>
vertexComparator;
protected Graph<V, E> graph1;
protected Graph<V, E> graph2;
private PrefetchIterator<IsomorphismRelation> nextSupplier;
// kept as member, to ease computations
private GraphOrdering lableGraph1;
private LinkedHashSet<V> graph1VertexSet;
private LinkedHashSet<E> graph2EdgeSet;
private CollectionPermutationIter<V> vertexPermuteIter;
private Set<V> currVertexPermutation; // filled every iteration, used in the
// result relation.
/**
* @param graph1
* @param graph2
* @param vertexChecker eq. group checker for vertexes. If null,
* UniformEquivalenceComparator will be used as default (always return true)
* @param edgeChecker eq. group checker for edges. If null,
* UniformEquivalenceComparator will be used as default (always return true)
*/
public AbstractExhaustiveIsomorphismInspector(
Graph<V, E> graph1,
Graph<V, E> graph2,
// XXX hb 060128: FOllowing parameter may need Graph<? super V,? super
// E>
EquivalenceComparator<? super V, ? super Graph<? super V, ? super E>> vertexChecker,
EquivalenceComparator<? super E, ? super Graph<? super V, ? super E>> edgeChecker)
{
this.graph1 = graph1;
this.graph2 = graph2;
if (vertexChecker != null) {
this.vertexComparator = vertexChecker;
} else {
this.vertexComparator = vertexDefaultIsomorphismComparator;
}
// Unlike vertexes, edges have better performance, when not tested for
// Equivalence, so if the user did not supply one, use null
// instead of edgeDefaultIsomorphismComparator.
if (edgeChecker != null) {
this.edgeComparator = edgeChecker;
}
init();
}
/**
* Constructor which uses the default comparators.
*
* @param graph1
* @param graph2
*
* @see #AbstractExhaustiveIsomorphismInspector(Graph,Graph,EquivalenceComparator,EquivalenceComparator)
*/
public AbstractExhaustiveIsomorphismInspector(
Graph<V, E> graph1,
Graph<V, E> graph2)
{
this(
graph1,
graph2,
edgeDefaultIsomorphismComparator,
vertexDefaultIsomorphismComparator);
}
/**
* Inits needed data-structures , among them:
* <li>LabelsGraph which is a created in the image of graph1
* <li>vertexPermuteIter which is created after the vertexes were divided to
* equivalence groups. This saves order-of-magnitude in performance, because
* the number of possible permutations dramatically decreases.
*
* <p>for example: if the eq.group are even/odd - only two groups. A graph
* with consist of 10 nodes of which 5 are even , 5 are odd , will need to
* test 5!*5! (14,400) instead of 10! (3,628,800).
*
* <p>besides the EquivalenceComparator`s supplied by the user, we also use
* predefined topological comparators.
*/
private void init()
{
this.nextSupplier =
new PrefetchIterator<IsomorphismRelation>(
// XXX hb 280106: I don't understand this warning, yet :-)
new NextFunctor());
this.graph1VertexSet = new LinkedHashSet<V>(this.graph1.vertexSet());
// vertexPermuteIter will be null, if there is no match
this.vertexPermuteIter =
createPermutationIterator(
this.graph1VertexSet,
this.graph2.vertexSet());
this.lableGraph1 =
new GraphOrdering<V, E>(
this.graph1,
this.graph1VertexSet,
this.graph1.edgeSet());
this.graph2EdgeSet = new LinkedHashSet<E>(this.graph2.edgeSet());
}
/**
* Creates the permutation iterator for vertexSet2 . The subclasses may make
* either cause it to depend on equality groups or use vertexSet1 for it.
*
* @param vertexSet1 [i] may be reordered
* @param vertexSet2 [i] may not.
*
* @return permutation iterator
*/
protected abstract CollectionPermutationIter<V> createPermutationIterator(
Set<V> vertexSet1,
Set<V> vertexSet2);
/**
* <p>1. Creates a LabelsGraph of graph1 which will serve as a source to all
* the comparisons which will follow.
*
* <p>2. extract the edge array of graph2; it will be permanent too.
*
* <p>3. for each permutation of the vertexes of graph2, test :
*
* <p>3.1. vertices
*
* <p>3.2. edges (in labelsgraph)
*
* <p>Implementation Notes and considerations: Let's consider a trivial
* example: graph of strings "A","B","C" with two edges A->B,B->C. Let's
* assume for this example that the vertex comparator always returns true,
* meaning String value does not matter, only the graph structure does. So
* "D" "E" "A" with D->E->A will be isomorphic , but "A","B,"C"with
* A->B,A->C will not.
*
* <p>First let's extract the important info for isomorphism from the graph.
* We don't care what the vertexes are, we care that there are 3 of them
* with edges from first to second and from second to third. So the source
* LabelsGraph will be: vertexes:[1,2,3] edges:[[1->2],[2->3]] Now we will
* do several permutations of D,E,A. A few examples: D->E , E->A
* [1,2,3]=[A,D,E] so edges are: 2->3 , 3->1 . does it match the source? NO.
* [1,2,3]=[D,A,E] so edges are: 1->3 , 3->2 . no match either.
* [1,2,3]=[D,E,A] so edges are: 1->2 , 2->3 . MATCH FOUND ! Trivial
* algorithm: We will iterate on all permutations
* [abc][acb][bac][bca][cab][cba]. (n! of them,3!=6) For each, first compare
* vertexes using the VertexComparator(always true). Then see that the edges
* are in the exact order 1st->2nd , 2nd->3rd. If we found a match stop and
* return true, otherwise return false; we will compare vetices and edges by
* their order (1st,2nd,3rd,etc) only. Two graphs are the same, by this
* order, if: 1. for each i, sourceVertexArray[i] is equivalent to
* targetVertexArray[i] 2. for each vertex, the edges which start in it (it
* is the source) goes to the same ordered vertex. For multiple ones, count
* them too.
*
* @return IsomorphismRelation for a permutation found, or null if no
* permutation was isomorphic
*/
private IsomorphismRelation<V, E> findNextIsomorphicGraph()
{
boolean result = false;
IsomorphismRelation<V, E> resultRelation = null;
if (this.vertexPermuteIter != null) {
// System.out.println("Souce LabelsGraph="+this.lableGraph1);
while (this.vertexPermuteIter.hasNext()) {
currVertexPermutation = this.vertexPermuteIter.getNextSet();
// compare vertexes
if (!areVertexSetsOfTheSameEqualityGroup(
this.graph1VertexSet,
currVertexPermutation))
{
continue; // this one is not iso, so try the next one
}
// compare edges
GraphOrdering<V, E> currPermuteGraph =
new GraphOrdering<V, E>(
this.graph2,
currVertexPermutation,
this.graph2EdgeSet);
// System.out.println("target LablesGraph="+currPermuteGraph);
if (this.lableGraph1.equalsByEdgeOrder(currPermuteGraph)) {
// create result object.
resultRelation =
new IsomorphismRelation<V, E>(
new ArrayList<V>(graph1VertexSet),
new ArrayList<V>(currVertexPermutation),
graph1,
graph2);
// if the edge comparator exists, check equivalence by it
boolean edgeEq =
areAllEdgesEquivalent(
resultRelation,
this.edgeComparator);
if (edgeEq) // only if equivalent
{
result = true;
break;
}
}
}
}
if (result == true) {
return resultRelation;
} else {
return null;
}
}
/**
* Will be called on every two sets of vertexes returned by the permutation
* iterator. From findNextIsomorphicGraph(). Should make sure that the two
* sets are euqivalent. Subclasses may decide to implements it as an always
* true methods only if they make sure that the permutationIterator will
* always be already equivalent.
*
* @param vertexSet1 FIXME Document me
* @param vertexSet2 FIXME Document me
*/
protected abstract boolean areVertexSetsOfTheSameEqualityGroup(
Set<V> vertexSet1,
Set<V> vertexSet2);
/**
* For each edge in g1, get the Correspondence edge and test the pair.
*
* @param resultRelation
* @param edgeComparator if null, always return true.
*/
protected boolean areAllEdgesEquivalent(
IsomorphismRelation<V, E> resultRelation,
EquivalenceComparator<? super E, ? super Graph<V, E>> edgeComparator)
{
boolean checkResult = true;
if (edgeComparator == null) {
// nothing to check
return true;
}
try {
Set<E> edgeSet = this.graph1.edgeSet();
for (E currEdge : edgeSet) {
E correspondingEdge =
resultRelation.getEdgeCorrespondence(currEdge, true);
// if one edge test fail , fail the whole method
if (!edgeComparator.equivalenceCompare(
currEdge,
correspondingEdge,
this.graph1,
this.graph2))
{
checkResult = false;
break;
}
}
} catch (IllegalArgumentException illegal) {
checkResult = false;
}
return checkResult;
}
/**
* return nextElement() casted as IsomorphismRelation
*/
public IsomorphismRelation nextIsoRelation()
{
return next();
}
/**
* Efficiency: The value is known after the first check for isomorphism
* activated on this class and returned there after in O(1). If called on a
* new ("virgin") class, it activates 1 iso-check.
*
* @return <code>true</code> iff the two graphs are isomorphic
*/
public boolean isIsomorphic()
{
return !(this.nextSupplier.isEnumerationStartedEmpty());
}
/* (non-Javadoc)
* @see java.util.Enumeration#hasMoreElements()
*/
public boolean hasNext()
{
boolean result = this.nextSupplier.hasMoreElements();
return result;
}
/**
* @see java.util.Iterator#next()
*/
public IsomorphismRelation next()
{
return this.nextSupplier.nextElement();
}
/* (non-Javadoc)
* @see java.util.Iterator#remove()
*/
public void remove()
{
throw new UnsupportedOperationException(
"remove() method is not supported in AdaptiveIsomorphismInspectorFactory."
+ " There is no meaning to removing an isomorphism result.");
}
private class NextFunctor
implements PrefetchIterator.NextElementFunctor<IsomorphismRelation>
{
public IsomorphismRelation nextElement()
throws NoSuchElementException
{
IsomorphismRelation resultRelation = findNextIsomorphicGraph();
if (resultRelation != null) {
return resultRelation;
} else {
throw new NoSuchElementException(
"IsomorphismInspector does not have any more elements");
}
}
}
}
// End AbstractExhaustiveIsomorphismInspector.java