/* ========================================== * 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. */ /* ----------------- * EquivalenceIsomorphismInspector.java * ----------------- * (C) Copyright 2005-2008, by Assaf Lehr and Contributors. * * Original Author: Assaf Lehr * Contributor(s): - * * $Id: EquivalenceIsomorphismInspector.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.*; /** * The current implementation uses the vertexComparator to greatly increase the * test speed by dividing the vertexes into equivalent groups and permuting * inside them only. The EdgeComparator is used to test edges, but not to make a * finer division, thus it adds overhead. Use it only when needed. * * @author Assaf * @since Jul 29, 2005 */ class EquivalenceIsomorphismInspector<V, E> extends AbstractExhaustiveIsomorphismInspector<V, E> { /** * @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 EquivalenceIsomorphismInspector( 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) { super(graph1, graph2, vertexChecker, edgeChecker); } /** * Constructor which uses the default comparators. * * @see ExhaustiveIsomorphismInspector(Graph,Graph,EquivalenceComparator,EquivalenceComparator) */ public EquivalenceIsomorphismInspector( Graph<V, E> graph1, Graph<V, E> graph2) { super(graph1, graph2); } /** * Creates the permutation iterator according to equivalance class. * * <p>1. Get the eq.group (ordered by size) array of the source vertex set * (vertexSet1) * * <p>2. Get the eq.group ordered array of vertexSet2. * * <p>3. Reorder the second array to match the group order of the first * array sets. 4. Use CompoundPermutationIter (and not regular * IntegerPermutationIter) to permute only inside groups. * * <p> * <p>That's it. If the eq.group comaparator is strong enough to provide * small groups, this algortihm will produce a small possible permutations * numbers. example: G1: [A,B,F,X,Y] [A->B,B->X,X->Y] * * <p>G2: [D,Z,C,U,F] [D->C,Z->C,U->Z] * * <p>vertexEq: three groups , one all letters A-E , second all letters S-Z * , third the letter 'f'. 1. [(f)size=1, (X,Y)size=2 , (A,B)size=2] 2. * [(f)size=1 ,(C,D)size=2 , (Z,U)size=2] 3. the match is done by reordering * the second array to have the equiviavlant order :##[(f)size=1 , * (Z,U)size=2 , (C,D)size=2]## 4.for example G2 will not do all 5!=120 * permutations , but 2!x2!x1!=4 permutations only which are: (of the 3rd * array) [ F, Z , U , C , D ] [ F, Z , U , D , C ] [ F, U , Z, C , D ] [ F, * U , Z , D , C ] * * @return null, if the eq.group do not match (there cannot be any * permutation for eq.groups) or the sets do not match in size; otherwise, * the permutationiterator otherwise * * @see AbstractExhaustiveIsomorphismInspector#createPermutationIterator(Set, * Set) */ @SuppressWarnings("unchecked") protected CollectionPermutationIter<V> createPermutationIterator( Set<V> vertexSet1, Set<V> vertexSet2) { if (vertexSet1.size() != vertexSet2.size()) { // throw new IllegalArgumentException("the two vertx-sets // parameters must be of" // +"the same size. The first size was:"+vertexSet1.size() // +" the other size was:" +vertexSet2.size() ); return null; // only instead of exception } // 1// EquivalenceSet [] eqGroupArray1 = EquivalenceSetCreator.createEqualityGroupOrderedArray( vertexSet1, this.vertexComparator, this.graph1); // 2// EquivalenceSet [] eqGroupArray2 = EquivalenceSetCreator.createEqualityGroupOrderedArray( vertexSet2, this.vertexComparator, this.graph2); // 3// boolean reorderSuccess = reorderTargetArrayToMatchSourceOrder(eqGroupArray1, eqGroupArray2); // 2 is the target if (!reorderSuccess) { // if reordering fail , no match can be done return null; } // reorder set1 (source), so when we work with the flat array of the // second array, // the permutations will be relevant. // note that it does not start in any way related to eqGroup sizes. V [] reorderingVertexSet1Temp = (V []) new Object[vertexSet1.size()]; fillElementsflatArray(eqGroupArray1, reorderingVertexSet1Temp); vertexSet1.clear(); vertexSet1.addAll(Arrays.asList(reorderingVertexSet1Temp)); // 4//use CompoundPermutationIter to permute only inside groups. // the CollectionPermutationIter needs a array/set of objects and a // permuter which will // work on that set/array order. lets make these two: // 1. create array of the vertexes , by flattening the eq.group array // contents V [] flatVertexArray = (V []) new Object[vertexSet2.size()]; fillElementsflatArray(eqGroupArray2, flatVertexArray); // 2. make the permuter according to the groups size int [] groupSizesArray = new int[eqGroupArray1.length]; // iterate over the EqualityGroup array for ( int eqGroupCounter = 0; eqGroupCounter < eqGroupArray2.length; eqGroupCounter++) { // now for (.2.) size count groupSizesArray[eqGroupCounter] = eqGroupArray2[eqGroupCounter].size(); } ArrayPermutationsIter arrayPermIter = PermutationFactory.createByGroups(groupSizesArray); CollectionPermutationIter<V> vertexPermIter = new CollectionPermutationIter<V>( Arrays.asList(flatVertexArray), arrayPermIter); return vertexPermIter; } /** * Reorders inplace targetArray * * <p>rules: * <li>try to match only group of the same size and then hashcode * <li>it is enough to choose one from each group to see if a match exist. * * <p>Algorithm: hold counters in the two arrays. [a,b,c,d,e] assume groups * are:a,(b,c,d),e [a,c,d,b,e] c1=0 , c2=0 check if eqvivalent . if not , * advance , as long as both size and hashcode are the same. if found a * match , swap the group positions in array2. if not , throws * IllegalArgumentExcpetion. Assumption: array size is the same. not * checked. * * @param sourceArray * @param targetArray * * @return true if the array was reordered successfully. false if not(It * will happen if there is no complete match between the groups) */ private boolean reorderTargetArrayToMatchSourceOrder( EquivalenceSet [] sourceArray, EquivalenceSet [] targetArray) { boolean result = true; for ( int sourceIndex = 0; sourceIndex < sourceArray.length; sourceIndex++) { int currTargetIndex = sourceIndex; // if they are already equivalent do nothing. EquivalenceSet sourceEqGroup = sourceArray[sourceIndex]; EquivalenceSet targetEqGroup = targetArray[currTargetIndex]; if (!sourceEqGroup.equals(targetEqGroup)) { // iterate through the next group in the targetArray until // a new size or hashcode is seen boolean foundMatch = false; int sourceSize = sourceEqGroup.size(); int sourceHashCode = sourceEqGroup.hashCode(); while ( (targetEqGroup.size() == sourceSize) && (targetEqGroup.hashCode() == sourceHashCode) && (currTargetIndex < (targetArray.length - 1))) { currTargetIndex++; targetEqGroup = targetArray[currTargetIndex]; if (targetEqGroup.equals(sourceEqGroup)) { foundMatch = true; // swap . targetEqGroup will serve as the temp // variable. targetArray[currTargetIndex] = targetArray[sourceIndex]; targetArray[sourceIndex] = targetEqGroup; } } if (!foundMatch) { // a match was not found // throw new IllegalArgumentException("could not reorder // the array , because the groups don`t match"); result = false; break; } } } return result; } /** * @param eqGroupArray * @param flatArray an empy array with the proper size */ protected void fillElementsflatArray( EquivalenceSet [] eqGroupArray, Object [] flatVertexArray) { int flatVertexArrayNextFree = 0; // the next free place in the array // iterate over the EqualityGroup array for ( int eqGroupCounter = 0; eqGroupCounter < eqGroupArray.length; eqGroupCounter++) { Object [] currGroupArray = eqGroupArray[eqGroupCounter].toArray(); // copy this small array to the free place in the big // flatVertexArray System.arraycopy( currGroupArray, // src 0, // srcPos flatVertexArray, // dest flatVertexArrayNextFree, // destPos currGroupArray.length // length ); flatVertexArrayNextFree += currGroupArray.length; } } /** * We know for sure, that the sets are alreay checked for equivalence , so * it will return true without any further checks. * * @see AbstractExhaustiveIsomorphismInspector#areVertexSetsOfTheSameEqualityGroup( * Set, Set) */ protected boolean areVertexSetsOfTheSameEqualityGroup( Set vertexSet1, Set vertexSet2) { return true; } } // End EquivalenceIsomorphismInspector.java