From da9cc95dc116584cefe6d8b228f7cbc88570fd07 Mon Sep 17 00:00:00 2001 From: Oliver Alvarado Rodriguez Date: Tue, 13 Feb 2024 22:44:01 -0500 Subject: [PATCH] ADDS parallel, recursive subgraph isomorphism replacing sequential version --- arachne/server/SubgraphIsomorphism.chpl | 659 +++++++++++------------- 1 file changed, 307 insertions(+), 352 deletions(-) diff --git a/arachne/server/SubgraphIsomorphism.chpl b/arachne/server/SubgraphIsomorphism.chpl index 467f40f7..4b2bcf66 100644 --- a/arachne/server/SubgraphIsomorphism.chpl +++ b/arachne/server/SubgraphIsomorphism.chpl @@ -21,32 +21,113 @@ module SubgraphIsomorphism { use SegStringSort; use SegmentedString; + /** Keeps track of the isomorphic mapping state during the execution process of VF2.*/ + class State { + var n1: int; // size of main graph + var n2: int; // size of subgraph + + var D_core: domain(1) = {0.. -1 then { - found = true; - var foundRels = edgeRelationships[edgeFound]; - return(found, foundRels); - } - return (found, emptyRels); - } - - /** Returns the set of internal identifiers of labels for a given vertex.*/ - proc getLabels(node:int, ref nodeLabels) throws { - var found : bool = false; - var emptyLabels = new set(int, parSafe=true); - - try { - var foundLabels = nodeLabels[node]; - found = true; - return(found, foundLabels); - } - - return (found, emptyLabels); - } - - /** Keeps track of the isomorphic mapping state during the execution process of VF2.*/ - record State { - var n1, n2: int; - var core1, core2: map(int, int); - var mapping: set((int , int)); - - // NOTE: Not used, saved for future work to automatically return true once we reach - // depth equal to the subgraph size. - var depth: int; - - // NOTE: Not used, saved for future work to allow comparison of edge weights and - // attributes to only return the subgraphs that are less than the given cost. - var cost: real; - - // Tin tracks in-neighbors - nodes with edges to current partial mapping. - // Tout tracks out-neighbors - nodes with edges from current mapping. - var Tin1, Tout1, Tin2, Tout2: domain(int); - - /** State initializer.*/ - proc init() { - this.n1 = 0; - this.n2 = 0; - this.core1 = new map(int, int); - this.core2 = new map(int, int); - this.mapping = new set((int, int)); - this.depth = 0; - this.cost = 0.0; - this.Tin1 = {1..0}; - this.Tout1 = {1..0}; - this.Tin2 = {1..0}; - this.Tout2 = {1..0}; - } - - /** Initialized based on given sizes `n1` and `n2`.*/ - proc init(n1, n2) { - this.n1 = n1; - this.n2 = n2; - this.core1 = new map(int, int); - this.core2 = new map(int, int); - this.mapping = new set((int, int)); - this.depth = 0; - this.cost = 0.0; - this.Tin1 = {1..0}; - this.Tout1 = {1..0}; - this.Tin2 = {1..0}; - this.Tout2 = {1..0}; - } - - /** Copy current state information to a new state.*/ - proc copy() { - var state = new State(n1, n2); - state.core1 = this.core1; - state.core2 = this.core2; - state.mapping = this.mapping; - state.depth = this.depth; - state.cost = this.cost; - state.Tin1 = this.Tin1; - state.Tout1 = this.Tout1; - state.Tin2 = this.Tin2; - state.Tout2 = this.Tout2; - return state; - } - - /** Reset vectors during backtracking.*/ - proc ref reset() { - this.mapping.clear(); // reset to empty - this.core1.clear(); - this.core2.clear(); - this.depth -= 1; - this.cost -= 1; - this.Tin1.clear(); - this.Tout1.clear(); - this.Tin2.clear(); - this.Tout2.clear(); - } - - /** Add a vertex pair `(x1, x2)` to the mapping.*/ - proc ref addPair(x1: int, x2: int) { - this.core1.add(x1, x2); - this.core2.add(x2, x1); - this.mapping.add((x1, x2)); - this.depth += 1; - } - - /** Check if a given node is mapped in g1.*/ - proc isMappedn1(node: int): bool { - if this.core1.contains(node) then return true; - else return false; - } - - /** Check if a given node is mapped in g2.*/ - proc isMappedn2(node: int): bool { - if this.core2.contains(node) then return true; - else return false; - } - } //end of State record - - /**Find vertices that point to this state and all vertices that this state points to.*/ - proc addToTinTout(ref state: State, u: int, v: int): State throws { var inNeighbors = dstRG1[segRG1[u].. 0 && state.Tout2.size > 0 { - var minTout2 = min reduce state.Tout2; - for n1 in state.Tout1 do candidates.add((n1, minTout2)); - } else { - //If Tin1 and Tin2 are both nonempty. - if state.Tin1.size > 0 && state.Tin2.size > 0 { - var minTin2 = min reduce state.Tin2; - for n1 in state.Tin1 do candidates.add((n1, minTin2)); - } else { // not (Tin1 or Tin2) NOTE: What does this mean? - if unmapped.size > 0 { - var minUnmapped = min reduce unmapped; - for n1 in 0..#g1.n_vertices do if !state.core1.contains(n1) then candidates.add((n1, minUnmapped)); - } - } - } - timergetCandidatePairsOpti.stop(); - TimerArrNew[7] += timergetCandidatePairsOpti.elapsed(); - return candidates; - } // end of getCandidatePairsOpti - - - /** Check that node labels are the same.*/ - proc nodesLabelCompatible(n1: int, n2: int): bool throws { - var timernodesLabelCompatible:stopwatch; - timernodesLabelCompatible.start(); - - var label1 = getLabels(n1, convertedLabelsG1)[1]; - var label2 = getLabels(n2, convertedLabelsG2)[1]; - - if (label1 & label2).size <= 0 { - timernodesLabelCompatible.stop(); - TimerArrNew[4] += timernodesLabelCompatible.elapsed(); - return false; - } - timernodesLabelCompatible.stop(); - TimerArrNew[4] += timernodesLabelCompatible.elapsed(); - return true; - } // end of nodesLabelCompatible - - /** Check if a pair of candidates are feasible.*/ - proc isFeasible(state: State, n1: int, n2: int) throws { - var timerisFeasible:stopwatch; - timerisFeasible.start(); - + /** Check to see if the mapping of n1 from g1 to n2 from g2 is feasible.*/ + proc isFeasible(n1: int, n2: int, state: State) throws { var termout1, termout2, termin1, termin2, new1, new2 : int = 0; - - if !nodesLabelCompatible(n1, n2) { - timerisFeasible.stop(); - TimerArrNew[2] += timerisFeasible.elapsed(); - return false; - } - // Get out neighbors of G1 and G2 - var getOutN1 = dstNodesG1[segGraphG1[n1].. 0 { - var state = stack.popBack(); - if state.mapping.size == g2.n_vertices then allmappings.pushBack(state.mapping); + return (UnMapG1, UnMapG2); + } // end of getBothUnmappedNodes + + /** Create candidates based on current state and retuns a set of pairs.*/ + proc getCandidatePairsOpti(state: State): set((int, int)) throws { + var candidates = new set((int, int), parSafe = true); - var candidatesOpti = getCandidatePairsOpti(state); + if state.Tout1.size > 0 && state.Tout2.size > 0 { + var minTout2: int; + for elem in state.Tout2{ + minTout2 = elem; + break; + } + for n1 in state.Tout1 do candidates.add((n1, minTout2)); + } else { + if state.Tin1.size > 0 && state.Tin2.size > 0 { + var minTin2: int; + for elem in state.Tin2{ + minTin2 = elem; + break; + } + for n1 in state.Tin1 do candidates.add((n1, minTin2)); + + } else { + var (unmappedG1, unmappedG2) = getBothUnmappedNodes(state); + var flagunmappedG2 = false; + var minUnmapped2 : int; + + for elem in unmappedG2{ + if elem != -1 { + minUnmapped2 = elem; + flagunmappedG2 = true; + } + } - for (n1, n2) in candidatesOpti { - if isFeasible(state, n1, n2) { - var newState = state.copy(); - newState.addPair(n1, n2); - newState = addToTinTout(newState, n1, n2); - stack.pushBack(newState); + if !flagunmappedG2 { + for i in 0..