bool FMatchRecursive() { Match mchCur; CandidateFinder cf; if (FCompleteMatch()) { return(true); } cf = new CandidateFinder(this); while ((mchCur = cf.NextCandidateMatch()) != null) { if (FFeasible(mchCur)) { BacktrackRecord btr = new BacktrackRecord(); AddMatchToSolution(mchCur, btr); if (FMatchRecursive()) { _fSuccessfulMatch = true; return(true); } btr.Backtrack(this); } } return(false); }
/// <summary> /// Add a match to the isomorphism /// </summary> /// <remarks> /// This is only a proposed match. If it fails, the passed in BacktrackRecord /// will have all the actions taken as a consequence and they can be undone using /// that BacktrackRecord. /// </remarks> /// <param name="mtc">Proposed match</param> /// <param name="btr">BacktrackRecord to record actions into</param> /// <returns>True if match is locally consistent with a full isomorphism</returns> private bool FAddMatchToSolution(Match mtc, BacktrackRecord <TV, TE> btr) { var ivtx1 = mtc.Ivtx1; var ivtx2 = mtc.Ivtx2; // Record the match action in the backtrackRecord btr.SetMatch(mtc.Ivtx1, mtc.Ivtx2, this); // In and Out neighbors of the vertices in the match var lstIn1 = VfGraph1.InNeighbors(ivtx1); var lstIn2 = VfGraph2.InNeighbors(ivtx2); var lstOut1 = VfGraph1.OutNeighbors(ivtx1); var lstOut2 = VfGraph2.OutNeighbors(ivtx2); // Reclassify any neighbors of the added vertices that require it foreach (var ivtx in lstOut1) { if (((int)VfGraph1.GetGroup(ivtx) & (int)(Group.Disconnected | Group.ToMapping)) != 0) { btr.MoveToGroup(1, ivtx, Group.FromMapping, this); } } foreach (var ivtx in lstIn1) { if (((int)VfGraph1.GetGroup(ivtx) & (int)(Group.Disconnected | Group.FromMapping)) != 0) { btr.MoveToGroup(1, ivtx, Group.ToMapping, this); } } foreach (var ivtx in lstOut2) { if (((int)VfGraph2.GetGroup(ivtx) & (int)(Group.Disconnected | Group.ToMapping)) != 0) { btr.MoveToGroup(2, ivtx, Group.FromMapping, this); } } foreach (var ivtx in lstIn2) { if (((int)VfGraph2.GetGroup(ivtx) & (int)(Group.Disconnected | Group.FromMapping)) != 0) { btr.MoveToGroup(2, ivtx, Group.ToMapping, this); } } // If the total degrees into or out of the isomorphism don't match up properly (less for // subgraph isomorphism equal for exact isomorphism) then we can never match properly. if (!FnCompareDegrees(_outDegreeTotal1, _outDegreeTotal2) || !FnCompareDegrees(_inDegreeTotal1, _inDegreeTotal2)) { return(false); } // Also check the vertex counts which is a different check from the total degree above... return(FnCompareDegrees(lstOut1.Count, lstOut2.Count) && FnCompareDegrees(lstIn1.Count, lstIn2.Count)); }
private bool FAddMatchToSolution(Match mtc, BacktrackRecord btr) { int inod1 = mtc.Inod1; int inod2 = mtc.Inod2; btr.SetMatch(mtc.Inod1, mtc.Inod2, this); List <int> lstIn1 = _vfgr1.InNeighbors(inod1); List <int> lstIn2 = _vfgr2.InNeighbors(inod2); List <int> lstOut1 = _vfgr1.OutNeighbors(inod1); List <int> lstOut2 = _vfgr2.OutNeighbors(inod2); foreach (int inod in lstOut1) { if (((int)_vfgr1.GetGroup(inod) & (int)(Groups.Disconnected | Groups.ToMapping)) != 0) { btr.MoveToGroup(1, inod, Groups.FromMapping, this); } } foreach (int inod in lstIn1) { if (((int)_vfgr1.GetGroup(inod) & (int)(Groups.Disconnected | Groups.FromMapping)) != 0) { btr.MoveToGroup(1, inod, Groups.ToMapping, this); } } foreach (int inod in lstOut2) { if (((int)_vfgr2.GetGroup(inod) & (int)(Groups.Disconnected | Groups.ToMapping)) != 0) { btr.MoveToGroup(2, inod, Groups.FromMapping, this); } } foreach (int inod in lstIn2) { if (((int)_vfgr2.GetGroup(inod) & (int)(Groups.Disconnected | Groups.FromMapping)) != 0) { btr.MoveToGroup(2, inod, Groups.ToMapping, this); } } if (!fnCmp(_outDegreeTotal1, _outDegreeTotal2) || !fnCmp(_inDegreeTotal1, _inDegreeTotal2)) { return(false); } return(fnCmp(lstOut1.Count, lstOut2.Count) && fnCmp(lstIn1.Count, lstIn2.Count)); }
public void TestMatchBacktrack() { var vfs = VfsTest(); var btr = new BacktrackRecord(); btr.SetMatch(0, 1, vfs); var grp1 = vfs.VfGraph1.GetGroup(0); var grp2 = vfs.VfGraph2.GetGroup(1); Assert.IsTrue((((int)grp1 & (int)Group.ContainedInMapping)) != 0); Assert.IsTrue((((int)grp2 & (int)Group.ContainedInMapping)) != 0); Assert.AreEqual(Group.ContainedInMapping, vfs.VfGraph1.GetGroup(0)); Assert.AreEqual(Group.ContainedInMapping, vfs.VfGraph2.GetGroup(1)); btr.Backtrack(vfs); grp1 = vfs.VfGraph1.GetGroup(0); grp2 = vfs.VfGraph2.GetGroup(1); Assert.IsFalse((((int)grp1 & (int)Group.ContainedInMapping)) != 0); Assert.IsFalse((((int)grp2 & (int)Group.ContainedInMapping)) != 0); Assert.AreEqual(Group.Disconnected, vfs.VfGraph1.GetGroup(0)); Assert.AreEqual(Group.Disconnected, vfs.VfGraph2.GetGroup(1)); }
public void TestMatchBacktrack() { VfState vfs = VfsTest(); BacktrackRecord btr = new BacktrackRecord(); btr.SetMatch(0, 1, vfs); Groups grp1 = vfs.Vfgr1.GetGroup(0); Groups grp2 = vfs.Vfgr2.GetGroup(1); Assert.IsTrue((((int)grp1 & (int)Groups.ContainedInMapping)) != 0); Assert.IsTrue((((int)grp2 & (int)Groups.ContainedInMapping)) != 0); Assert.AreEqual(Groups.ContainedInMapping, vfs.Vfgr1.GetGroup(0)); Assert.AreEqual(Groups.ContainedInMapping, vfs.Vfgr2.GetGroup(1)); btr.Backtrack(vfs); grp1 = vfs.Vfgr1.GetGroup(0); grp2 = vfs.Vfgr2.GetGroup(1); Assert.IsFalse((((int)grp1 & (int)Groups.ContainedInMapping)) != 0); Assert.IsFalse((((int)grp2 & (int)Groups.ContainedInMapping)) != 0); Assert.AreEqual(Groups.Disconnected, vfs.Vfgr1.GetGroup(0)); Assert.AreEqual(Groups.Disconnected, vfs.Vfgr2.GetGroup(1)); }
// Find an isomorphism between a subgraph of _vfgr1 and the entirity of _vfgr2... public bool FMatch() { if (!FCompatibleDegrees()) { return(false); } Stack <CandidateFinder> stkcf = new Stack <CandidateFinder>(); Stack <BacktrackRecord> stkbr = new Stack <BacktrackRecord>(); Match mchCur; CandidateFinder cf; BacktrackRecord btr; bool fPopOut = false; #if GATHERSTATS int cSearchGuesses = 0; int cBackTracks = 0; int cInfeasible = 0; #endif if (_fMatched) { return(false); } _fMatched = true; // Since the subgraph of subgraph isomorphism is in _vfgr1, it must have at // least as many nodes as _vfgr2... if (!fnCmp(_vfgr1.NodeCount, _vfgr2.NodeCount)) { return(false); } if (FCompleteMatch()) { _fSuccessfulMatch = true; return(true); } // Non-recursive implementation of a formerly recursive function while (true) { if (fPopOut) { if (stkcf.Count <= 0) { break; // Out of the top level while loop and return false } cf = stkcf.Pop(); btr = stkbr.Pop(); #if GATHERSTATS cBackTracks++; #endif btr.Backtrack(this); } else { cf = new CandidateFinder(this); btr = new BacktrackRecord(); } fPopOut = true; while ((mchCur = cf.NextCandidateMatch()) != null) { if (FFeasible(mchCur)) { if (FAddMatchToSolution(mchCur, btr) && FCompleteMatch()) { _fSuccessfulMatch = true; #if GATHERSTATS Console.WriteLine("cBackTracks = {0}", cBackTracks); Console.WriteLine("cSearchGuesses = {0}", cSearchGuesses); Console.WriteLine("cInfeasible = {0}", cInfeasible); #endif if (_fFindAll) { // Record this match and simulate a failure... RecordCurrentMatch(); stkcf.Push(cf); stkbr.Push(btr); break; } else { return(true); } } #if GATHERSTATS // Made a bad guess, count it up... cSearchGuesses++; #endif stkcf.Push(cf); stkbr.Push(btr); fPopOut = false; break; // Out of the inner level while loop to "call" into the outer loop } #if GATHERSTATS else { cInfeasible++; } #endif } } #if GATHERSTATS Console.WriteLine("cBackTracks = {0}", cBackTracks); Console.WriteLine("cSearchGuesses = {0}", cSearchGuesses); Console.WriteLine("cInfeasible = {0}", cInfeasible); #endif return(_fSuccessfulMatch); }
public IEnumerable <FullMapping> Matches() { // Check for an empty second graph if (_ldr2.VertexCount == 0) { // If the second is empty, check for a successful "match" if (FnCompareDegrees(_ldr1.VertexCount, 0)) { // Return empty mapping - successful, but nothing to map to yield return(new FullMapping(new Dictionary <int, int>(), new Dictionary <int, int>())); } yield break; } // Quick easy check to make the degrees compatible. if (!FCompatibleDegrees()) { yield break; } var stkcf = new Stack <CandidateFinder <TV, TE> >(); var stkbr = new Stack <BacktrackRecord <TV, TE> >(); var fBacktrack = false; #if GATHERSTATS int cSearchGuesses = 0; int cBackTracks = 0; int cInfeasible = 0; #endif // The general structure here is: // While (true) // if (backtracking) // if there are no graph2 vertices to backtrack to, we can't find any other isomorphisms so return // pop off the previous graph2 vertex to continue it's match // perform backtrack actions // else // pick a new graph2 vertex to try to match // while (there are still potential graph1 vertices to match our current graph2 vertex) // if (the selected graph1 vertex is a feasible match to our current graph2 vertex) // Add the match (and record it if we later need to backtrack) // if (we've got a complete isomorphism) // yield the isomorphism // Push our current graph2 vertex so it will be popped off during backtracking // break the inner loop to trigger artificial backtrack // push the current graph2 vertex search and continue in outer loop with no backtracking // while (true) { CandidateFinder <TV, TE> cf; BacktrackRecord <TV, TE> btr; // If it's time to backtrack... if (fBacktrack) { // If there are no more candidates left, we've failed to find an isomorphism if (stkcf.Count <= 0) { break; // Out of the top level while loop and end enumeration } // Pop off the previous candidate finder so we can continue in our list cf = stkcf.Pop(); #if GATHERSTATS cBackTracks++; #endif // Get the backtrack record... btr = stkbr.Pop(); // ...and undo any actions that need to be backtracked btr.Backtrack(this); } else { // Moving forward - new candidate finder // Each new candidate finder we produce here picks a vertex in graph2 and works // at matching it to a vertex in graph1 until there are no possibile graph1 vertices left. // at that point we know that the current isomorphism is impossible since no // graph1 vertex can be matched to the selected graph2 vertex. At that point we // will pop the candidate finder, backtrack any changes we've made and move to // the next graph1 candidate in the previous candidate finder on the stack. cf = new CandidateFinder <TV, TE>(this); // Start a new backtracking record in case we fail to find a match for our // selected graph2 vertex. btr = new BacktrackRecord <TV, TE>(); } // Assume failure fBacktrack = true; Match mchCur; // For all the graph1 vertices that could potentially match up with the current // candidateFinder's graph2 vertex... while ((mchCur = cf.NextCandidateMatch()) != null) { // If the candidate match is feasible if (FFeasible(mchCur)) { // Add it to the isomorphism so far and see if we've completed the isomorphism if (FAddMatchToSolution(mchCur, btr) && FCompleteMatch()) { // Yay! Isomorphism found! #if GATHERSTATS Console.WriteLine("cBackTracks = {0}", cBackTracks); Console.WriteLine("cSearchGuesses = {0}", cSearchGuesses); Console.WriteLine("cInfeasible = {0}", cInfeasible); #endif // Record this match Dictionary <int, int> vidToVid1; Dictionary <int, int> vidToVid2; // Change the ivertex to ivertex VfGraph mapping in _vfGraphIvtx1To2Isomorphism // to an vid to vid mapping in the original graph... VfGraphVfGraphIvtxToGraphGraphVid( _vfGraphIvtx1To2Isomorphism, _degreeSortedToOriginal1, _degreeSortedToOriginal2, out vidToVid1, out vidToVid2); yield return(new FullMapping(vidToVid1, vidToVid2)); // This will cause a backtrack where this candidateFinder // will be popped off the stack and we'll continue // with this graph2 vertex looking for the next solution. stkcf.Push(cf); stkbr.Push(btr); break; } #if GATHERSTATS // Made a bad guess, count it up... cSearchGuesses++; #endif // Dang! No full isomorphism yet but our choices so far aren't infeasible. // Push candidate finder/backtrack record and break out of the inner loop which will // cause us to pick another candidate finder/graph2 vertex to be mapped. stkcf.Push(cf); stkbr.Push(btr); fBacktrack = false; break; // Out of the inner level while loop to "call" into the outer loop } #if GATHERSTATS else { cInfeasible++; } #endif } } #if GATHERSTATS Console.WriteLine("cBackTracks = {0}", cBackTracks); Console.WriteLine("cSearchGuesses = {0}", cSearchGuesses); Console.WriteLine("cInfeasible = {0}", cInfeasible); #endif }