├── .gitignore ├── GSI.py ├── GSI_cpp ├── Makefile ├── data │ ├── d.g │ ├── data.g │ ├── data_pair.g │ ├── q.g │ ├── query.g │ ├── query_pair.g │ ├── readme │ ├── simple.g │ ├── star.g │ ├── triangle.g │ └── wedge.g ├── graph │ ├── Graph.cpp │ └── Graph.h ├── io │ ├── IO.cpp │ └── IO.h ├── isomorphism.cpython-312-x86_64-linux-gnu.so ├── libisomorphism.so ├── main │ ├── isomorphism.cu │ ├── isomorphism.h │ ├── run.cu │ └── test.cpp ├── match │ ├── Match.cu │ ├── Match.h │ └── gputimer.h ├── objs │ ├── .gitkeep │ ├── Graph.o │ ├── IO.o │ ├── Match.o │ ├── Util.o │ ├── isomorphism.o │ └── pybind_wrapper.o ├── pybind_wrapper.cu ├── scripts │ ├── cover.sh │ ├── filter.sh │ ├── run.sh │ ├── sumline.sh │ └── watdiv.sh └── util │ ├── Util.cpp │ └── Util.h ├── GSI_example.py ├── LICENSE ├── README.md ├── isomorphism.cpython-312-x86_64-linux-gnu.so ├── test.py └── test2.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.i 2 | *.ii 3 | *.gpu 4 | *.ptx 5 | *.cubin 6 | *.fatbin 7 | ans.txt 8 | *.exe -------------------------------------------------------------------------------- /GSI.py: -------------------------------------------------------------------------------- 1 | # Imports 2 | from GSI_cpp.isomorphism import create_graph, find_isomorphisms, print_mapping, initializeGPU 3 | import networkx as nx 4 | 5 | # Initialize GPU 6 | def initGPU(dev=0,verbose=True): 7 | """ 8 | Initialize the GPU with the specified device number. 9 | 10 | Args: 11 | dev (int): The GPU device number to initialize. Default is 0. 12 | """ 13 | initializeGPU(dev,verbose) 14 | 15 | # Create graph 16 | def createGraph(node_ids, node_labels, edge_ids, edge_labels, column_oriented): 17 | """ 18 | Create a custom Graph object using the provided node IDs, node labels, edge IDs, and edge labels. 19 | 20 | Args: 21 | node_ids (list of int): List of node IDs. 22 | node_labels (list of int): List of node labels corresponding to node IDs. 23 | edge_ids (list of tuple): List of tuples representing edge connections (from, to). 24 | edge_labels (list of int): List of edge labels corresponding to edge IDs. 25 | column_oriented (bool): Flag indicating whether the graph should be column-oriented. 26 | 27 | Returns: 28 | Graph: The created custom Graph object. 29 | """ 30 | return create_graph(node_ids, node_labels, edge_ids, edge_labels, column_oriented) 31 | 32 | # Find Isomorphisms 33 | def findIsomorphism(query, data): 34 | """ 35 | Find isomorphisms between the query graph and the data graph. 36 | 37 | Args: 38 | query (Graph): The query graph. 39 | data (Graph): The data graph. 40 | 41 | Returns: 42 | tuple: A tuple containing a boolean indicating if isomorphisms were found and the list of mappings. 43 | """ 44 | mappings = find_isomorphisms(query, data) 45 | 46 | if len(mappings) == 0: 47 | return False, mappings 48 | else: 49 | return True, mappings 50 | 51 | # Print Mappings 52 | def printMappings(mappings): 53 | """ 54 | Print the mappings of isomorphisms. 55 | 56 | Args: 57 | mappings (list of dict): The list of mappings from the query graph to the data graph. 58 | """ 59 | for mapping, n in zip(mappings, range(len(mappings))): 60 | print(f"====== Mapping #{n+1} =====") 61 | print_mapping(mapping) 62 | 63 | # NetworkX Graph to Isomorphism Graph 64 | def nxGraph(nx_graph, bidirectional = True, column_oriented = False): 65 | """ 66 | Convert a NetworkX graph to a custom Graph object suitable for isomorphism detection. 67 | 68 | Args: 69 | nx_graph (networkx.Graph): The NetworkX graph to convert. 70 | bidirectional (bool): Flag indicating whether edges should be treated as bidirectional. Default is True. 71 | column_oriented (bool): Flag indicating whether the graph should be column-oriented. Default is False. 72 | 73 | Returns: 74 | Graph: The created custom Graph object. 75 | """ 76 | 77 | # Extract node IDs and labels 78 | node_ids = list(nx_graph.nodes()) 79 | node_labels = [nx_graph.nodes[node_id].get('label', 1) for node_id in node_ids] # Default label to 1 if not provided 80 | 81 | # Extract edge IDs and labels 82 | edge_ids = list(nx_graph.edges()) 83 | edge_labels = [1 for edge_id in edge_ids] # Default label to 1 84 | 85 | ''' 86 | print(node_ids) 87 | print(edge_ids) 88 | print("OK SO FAR") 89 | ''' 90 | 91 | # Check if bidirectional flag is set 92 | if bidirectional: 93 | # Create bidirectional edges 94 | bidirectional_edges = [] 95 | for from_node, to_node in edge_ids: 96 | bidirectional_edges.append((from_node, to_node)) 97 | bidirectional_edges.append((to_node, from_node)) 98 | edge_ids = bidirectional_edges 99 | edge_labels = [1 for _ in edge_ids] # Default label to 1 for all edges 100 | 101 | ''' 102 | print("Node ID's: ", node_ids) 103 | print("Node Label's: ", node_labels) 104 | print("zipped: ", list(zip(node_labels,node_ids))) 105 | 106 | print("Edge ID's: ", edge_ids) 107 | print("Edge Label's: ", edge_labels) 108 | #print("OK SO FAR") 109 | print() 110 | ''' 111 | 112 | # Sort nodes by their label in increasing order 113 | _combined = list(zip(node_labels,node_ids)) 114 | _sorted_combined = sorted(_combined) 115 | 116 | # Unzip the sorted combined array back into two arrays 117 | A_sorted, B_sorted = zip(*_sorted_combined) 118 | 119 | # Convert the tuples back to lists 120 | node_labels = list(A_sorted) 121 | node_ids = list(B_sorted) 122 | 123 | index_node_ids = list(range(len(node_ids))) 124 | algo_map = {inid: nid for nid, inid in zip(node_ids, index_node_ids)} 125 | 126 | ''' 127 | print() 128 | print("Node Label's: ", node_labels) 129 | print("Node id's: ",node_ids) 130 | print("Index node ID's: ", index_node_ids) 131 | print("zipped: ", list(zip(node_labels,index_node_ids))) 132 | print("Index map: ", algo_map) 133 | ''' 134 | 135 | # Convert node IDs and edge IDs to required format 136 | edge_ids = [(node_ids.index(from_node), node_ids.index(to_node)) for from_node, to_node in edge_ids] 137 | 138 | # Create the custom Graph object 139 | return createGraph(index_node_ids, node_labels, edge_ids, edge_labels, column_oriented), algo_map 140 | 141 | # Process chemical graphs 142 | def chemGraphProcess(G, label_map): 143 | # Create a new node property "label" 144 | # Encode each unique symbol / elemental_type to an integer in "label" 145 | 146 | # Extract the mode ('symbol' or 'elemental_type') 147 | mode = list(list(G.nodes.data())[0][1].keys())[0] 148 | 149 | # Stores the unique elements 150 | label_list = [] 151 | # Stores the node -> labels mapping 152 | label_dict = {} 153 | 154 | # For every node 155 | for node in G.nodes(mode): 156 | node_index = node[0] 157 | node_symbol = node[1] 158 | 159 | label_dict[node_index] = label_map.index(node_symbol)+1 160 | 161 | # Set the node attribute 162 | nx.set_node_attributes(G, label_dict, "label") 163 | 164 | return G 165 | 166 | # Print a graph 167 | def printGraph(G): 168 | return G.print_graph() 169 | 170 | # Print networkx graph: 171 | def print_nx_graph(nx_graph): 172 | """ 173 | Prints the details of a NetworkX graph including the number of nodes, nodes (ID, label), 174 | number of edges, and edges ((START NODE, END NODE), label). 175 | 176 | Args: 177 | nx_graph (networkx.Graph): The NetworkX graph to print. 178 | """ 179 | # Get nodes and their labels 180 | node_ids = list(nx_graph.nodes()) 181 | node_labels = {node: nx_graph.nodes[node].get('symbol', 1) for node in node_ids} # Default label to 1 if not provided 182 | 183 | # Get edges and their labels 184 | edge_ids = list(nx_graph.edges()) 185 | edge_labels = {(u, v): nx_graph.edges[u, v].get('label', 1) for u, v in edge_ids} # Default label to 1 if not provided 186 | 187 | # Print the number of nodes 188 | print(f"Graph contains {len(node_ids)} nodes.") 189 | 190 | # Print nodes (ID, label) 191 | print("Nodes (ID, label):") 192 | for node_id in node_ids: 193 | print(f"({node_id}, {node_labels[node_id]})") 194 | 195 | # Print the number of edges 196 | print(f"Graph contains {len(edge_ids)} edges.") 197 | 198 | # Print edges ((START NODE, END NODE), label) 199 | print("Edges ((START NODE, END NODE), label):") 200 | for (start_node, end_node) in edge_ids: 201 | print(f"(({start_node}, {end_node}), {edge_labels[(start_node, end_node)]})") 202 | 203 | 204 | # Given the parent graph (Reactant), return a mapping between chemical symbols/labels to a number 205 | # Encode each unique symbol / elemental_type to an integer in "label" 206 | def encodeLabels(G): 207 | # Extract the mode ('symbol' or 'elemental_type') 208 | mode = list(list(G.nodes.data())[0][1].keys())[0] 209 | 210 | 211 | _tmp = [] 212 | 213 | # Iterate through 214 | for node in list(G.nodes.data()): 215 | #ID = node[0] 216 | symbol = node[1][mode] 217 | _tmp.append(symbol) 218 | #print("Encoding: ", list(dict.fromkeys(_tmp))) 219 | return list(dict.fromkeys(_tmp)) 220 | -------------------------------------------------------------------------------- /GSI_cpp/Makefile: -------------------------------------------------------------------------------- 1 | # Define compilers and options 2 | 3 | # CUDA Path 4 | # If CUDA_PATH is not already defined, use the default path 5 | CUDA_PATH = /usr/local/cuda-12.5 6 | 7 | # Path to gcc-11 8 | # If GCC_11_PATH is not already defined, use the default path installed by Linuxbrew 9 | GCC_11_PATH = /home/linuxbrew/.linuxbrew/Cellar/gcc@11/11.4.0 10 | 11 | # Define the compilers explicitly 12 | CC = $(GCC_11_PATH)/bin/gcc-11 13 | CXX = $(GCC_11_PATH)/bin/g++-11 14 | NVCC = $(CUDA_PATH)/bin/nvcc 15 | 16 | # Compilation flags 17 | CFLAGS = -std=c++14 -c -O2 -I$(CUDA_PATH)/include 18 | EXEFLAG = -O2 -shared 19 | LDFLAGS = -L$(CUDA_PATH)/lib64 -lcudart -lcudadevrt #-ldl -lm 20 | INCLUDES = -I$(CUDA_PATH)/include 21 | GPU_ARCHITECTURE = sm_86 22 | 23 | # Pybind11 and Python includes 24 | PYBIND11_INCLUDES = $(shell python3 -m pybind11 --includes) 25 | PYTHON_LDFLAGS = $(shell python3-config --ldflags) 26 | 27 | # NVCC command 28 | NVCCFLAGS = -arch=$(GPU_ARCHITECTURE) -rdc=true --ptxas-options=-v -Xcompiler -fPIC $(INCLUDES) --compiler-bindir $(GCC_11_PATH)/bin -std=c++14 -c -O2 -I$(CUDA_PATH)/include 29 | 30 | # Object directory and files 31 | objdir = ./objs/ 32 | objfile = $(objdir)Util.o $(objdir)IO.o $(objdir)Match.o $(objdir)Graph.o $(objdir)isomorphism.o $(objdir)pybind_wrapper.o 33 | 34 | # Default target 35 | all: libisomorphism.so isomorphism$(shell python3-config --extension-suffix) 36 | 37 | # Link all object files to create the shared object file 38 | libisomorphism.so: $(objfile) 39 | $(NVCC) -arch=sm_86 -shared -o libisomorphism.so $(objfile) $(LDFLAGS) 40 | 41 | 42 | # Compile source files into object files 43 | $(objdir)Util.o: util/Util.cpp util/Util.h | $(objdir) 44 | $(CC) -fPIC $(CFLAGS) util/Util.cpp -o $(objdir)Util.o 45 | 46 | $(objdir)Graph.o: graph/Graph.cpp graph/Graph.h | $(objdir) 47 | $(CC) -fPIC $(CFLAGS) graph/Graph.cpp -o $(objdir)Graph.o 48 | 49 | $(objdir)IO.o: io/IO.cpp io/IO.h | $(objdir) 50 | $(CC) -fPIC $(CFLAGS) io/IO.cpp -o $(objdir)IO.o 51 | 52 | $(objdir)Match.o: match/Match.cu match/Match.h | $(objdir) 53 | $(NVCC) $(NVCCFLAGS) match/Match.cu -o $(objdir)Match.o 54 | 55 | $(objdir)isomorphism.o: main/isomorphism.cu main/isomorphism.h | $(objdir) 56 | $(NVCC) $(NVCCFLAGS) main/isomorphism.cu -o $(objdir)isomorphism.o 57 | 58 | # Ensure the object directory exists 59 | $(objdir): 60 | mkdir -p $(objdir) 61 | 62 | # Pybind11 module 63 | $(objdir)pybind_wrapper.o: pybind_wrapper.cu main/isomorphism.h graph/Graph.h match/Match.h io/IO.h util/Util.h | $(objdir) 64 | $(NVCC) $(NVCCFLAGS) -Xcompiler -fPIC $(CFLAGS) $(PYBIND11_INCLUDES) pybind_wrapper.cu -o $(objdir)pybind_wrapper.o 65 | 66 | isomorphism$(shell python3-config --extension-suffix): $(objdir)pybind_wrapper.o $(objdir)Util.o $(objdir)IO.o $(objdir)Match.o $(objdir)Graph.o $(objdir)isomorphism.o 67 | $(NVCC) -arch=$(GPU_ARCHITECTURE) -rdc=true --ptxas-options=-v -Xcompiler -fPIC $(INCLUDES) --compiler-bindir $(GCC_11_PATH)/bin -std=c++14 -O2 -I$(CUDA_PATH)/include $(EXEFLAG) -o isomorphism$(shell python3-config --extension-suffix) $(objdir)pybind_wrapper.o $(objdir)Util.o $(objdir)IO.o $(objdir)Match.o $(objdir)Graph.o $(objdir)isomorphism.o $(PYTHON_LDFLAGS) $(LDFLAGS) 68 | 69 | 70 | # Phony targets 71 | .PHONY: clean dist tarball test sumlines doc 72 | 73 | clean: 74 | rm -f $(objdir)* 75 | 76 | dist: clean 77 | rm -f *.txt *.exe *.so *.cpython-*.so 78 | rm -f *.g 79 | rm -f cuda-memcheck.* 80 | 81 | tarball: 82 | tar -czvf gsi.tar.gz main util match io graph Makefile README.md objs 83 | 84 | sumlines: 85 | bash script/sumline.sh 86 | 87 | doc: 88 | doxygen 89 | -------------------------------------------------------------------------------- /GSI_cpp/data/d.g: -------------------------------------------------------------------------------- 1 | t # 0 2 | 5 6 3 2 3 | v 0 1 4 | v 1 2 5 | v 2 3 6 | v 3 1 7 | v 4 2 8 | e 0 1 1 9 | e 0 2 2 10 | e 1 3 1 11 | e 2 3 2 12 | e 3 4 1 13 | e 2 4 1 14 | t # -1 15 | -------------------------------------------------------------------------------- /GSI_cpp/data/data.g: -------------------------------------------------------------------------------- 1 | t # 0 2 | 3 6 1 1 3 | v 0 1 4 | v 1 1 5 | v 2 1 6 | e 0 1 1 7 | e 1 2 1 8 | e 2 0 1 9 | e 1 0 1 10 | e 2 1 1 11 | e 0 2 1 12 | t # -1 13 | -------------------------------------------------------------------------------- /GSI_cpp/data/data_pair.g: -------------------------------------------------------------------------------- 1 | t # 0 2 | 4 10 1 1 3 | v 0 1 4 | v 1 1 5 | v 2 1 6 | v 3 1 7 | e 0 1 1 8 | e 1 2 1 9 | e 2 0 1 10 | e 0 3 1 11 | e 2 3 1 12 | e 1 0 1 13 | e 2 1 1 14 | e 0 2 1 15 | e 3 0 1 16 | e 3 2 1 17 | t # -1 -------------------------------------------------------------------------------- /GSI_cpp/data/q.g: -------------------------------------------------------------------------------- 1 | t # 0 2 | 3 3 3 2 3 | v 0 1 4 | v 1 2 5 | v 2 3 6 | e 0 1 1 7 | e 0 2 2 8 | e 1 2 1 9 | t # -1 10 | -------------------------------------------------------------------------------- /GSI_cpp/data/query.g: -------------------------------------------------------------------------------- 1 | t # 0 2 | 3 4 1 1 3 | v 0 1 4 | v 1 1 5 | v 2 1 6 | e 0 1 1 7 | e 1 0 1 8 | e 1 2 1 9 | e 2 1 1 10 | t # -1 -------------------------------------------------------------------------------- /GSI_cpp/data/query_pair.g: -------------------------------------------------------------------------------- 1 | t # 0 2 | 3 4 1 1 3 | v 0 1 4 | v 1 1 5 | v 2 1 6 | e 0 1 1 7 | e 1 0 1 8 | e 0 2 1 9 | e 2 0 1 10 | t # -1 -------------------------------------------------------------------------------- /GSI_cpp/data/readme: -------------------------------------------------------------------------------- 1 | #### Introduction 2 | 3 | each data file and each query file both contain only one graph. 4 | In each graph, vertex has its ID and label, edges are directed and labeled. 5 | (parallel edges and self loops are not allowed, and separated query graphs are not accepted) 6 | When a query graph is separated into several parts which are totally not connected to each other, 7 | the results can be acquired by do a Cartesian product after the results of each part are found. 8 | 9 | --- 10 | 11 | #### Format 12 | 13 | t # 0 14 | numVertex numEdges MaximumVertexLabel MaximumEdgeLabel 15 | v 0 1 16 | v 1 2 17 | ... 18 | e 0 1 1 19 | e 0 2 3 20 | ... 21 | t # -1 22 | 23 | --- 24 | 25 | #### NOTICE 26 | 27 | 1. all labels begin from 1 28 | 2. vertex ID begin from 0 and grows in order 29 | 30 | -------------------------------------------------------------------------------- /GSI_cpp/data/simple.g: -------------------------------------------------------------------------------- 1 | t # 0 2 | 3 3 1 1 3 | v 0 1 4 | v 1 1 5 | v 2 1 6 | e 0 1 1 7 | e 1 2 1 8 | e 2 0 1 9 | t # -1 -------------------------------------------------------------------------------- /GSI_cpp/data/star.g: -------------------------------------------------------------------------------- 1 | t # 0 2 | 3 2 1 1 3 | v 0 1 4 | v 1 1 5 | v 2 1 6 | e 0 1 1 7 | e 2 1 1 8 | t # -1 9 | -------------------------------------------------------------------------------- /GSI_cpp/data/triangle.g: -------------------------------------------------------------------------------- 1 | t # 0 2 | 3 3 1 1 3 | v 0 1 4 | v 1 1 5 | v 2 1 6 | e 0 1 1 7 | e 1 2 1 8 | e 2 0 1 9 | t # -1 -------------------------------------------------------------------------------- /GSI_cpp/data/wedge.g: -------------------------------------------------------------------------------- 1 | t # 0 2 | 3 2 1 1 3 | v 0 1 4 | v 1 1 5 | v 2 1 6 | e 0 1 1 7 | e 1 2 1 8 | t # -1 9 | -------------------------------------------------------------------------------- /GSI_cpp/graph/Graph.cpp: -------------------------------------------------------------------------------- 1 | /*============================================================================= 2 | # Filename: Graph.cpp 3 | # Author: Bookug Lobert 4 | # Mail: 1181955272@qq.com 5 | # Last Modified: 2016-10-24 23:01 6 | # Description: Implementation of the Graph class, including methods for adding 7 | # vertices and edges, preprocessing, building signatures, transforming to CSR, 8 | # and more. 9 | =============================================================================*/ 10 | 11 | #include "Graph.h" 12 | 13 | using namespace std; 14 | 15 | /** 16 | * @brief Sets the number of unique vertex labels. 17 | * 18 | * @param num Number of unique vertex labels. 19 | */ 20 | void Graph::setVertexLabelNum(unsigned num) 21 | { 22 | vertexLabelNum = num; 23 | } 24 | 25 | /** 26 | * @brief Sets the number of unique edge labels. 27 | * 28 | * @param num Number of unique edge labels. 29 | */ 30 | void Graph::setEdgeLabelNum(unsigned num) 31 | { 32 | edgeLabelNum = num; 33 | } 34 | 35 | /** 36 | * @brief Hash function for generating signatures. 37 | * 38 | * @param key Pointer to the key. 39 | * @param len Length of the key. 40 | * @param seed Seed value for the hash function. 41 | * @return uint32_t Hashed value. 42 | */ 43 | uint32_t hash(const void * key, int len, uint32_t seed) 44 | { 45 | return Util::MurmurHash2(key, len, seed); 46 | } 47 | 48 | /** 49 | * @brief Adds a vertex with a given label to the graph. 50 | * 51 | * @param _vlb Label of the vertex. 52 | */ 53 | void Graph::addVertex(LABEL _vlb) 54 | { 55 | this->vertices.push_back(Vertex(_vlb)); 56 | } 57 | 58 | /** 59 | * @brief Adds a directed edge with a given label between two vertices. 60 | * 61 | * @param _from Source vertex ID. 62 | * @param _to Destination vertex ID. 63 | * @param _elb Label of the edge. 64 | */ 65 | void Graph::addEdge(VID _from, VID _to, LABEL _elb) 66 | { 67 | this->vertices[_from].out.push_back(Neighbor(_to, _elb)); 68 | this->vertices[_to].in.push_back(Neighbor(_from, _elb)); 69 | } 70 | 71 | /** 72 | * @brief Preprocesses the graph by transforming it to CSR format and building signatures. 73 | * 74 | * @param column_oriented Boolean flag indicating whether to use column-oriented signatures. 75 | */ 76 | void Graph::preprocessing(bool column_oriented) 77 | { 78 | unsigned deg = this->countMaxDegree(); 79 | //cout << "maximum degree: " << deg << endl; 80 | //long t1 = Util::get_cur_time(); 81 | this->transformToCSR(); 82 | this->buildSignature(column_oriented); 83 | ///long t2 = Util::get_cur_time(); 84 | //printf("time of preprocessing(not included in matching): %ld ms\n", t2-t1); 85 | // now we can release the memory of the original structure 86 | // this->vertices.clear(); 87 | 88 | } 89 | 90 | /** 91 | * @brief Builds the signature for the graph. 92 | * 93 | * @param column_oriented Boolean flag indicating whether to use column-oriented signatures. 94 | */ 95 | void Graph::buildSignature(bool column_oriented) 96 | { 97 | // cout << "build signature for a new graph" << endl; 98 | // build row-oriented signatures for query graph 99 | unsigned signum = SIGLEN / VLEN; 100 | unsigned tablen = this->vertex_num * signum; 101 | unsigned* signature_table = new unsigned[tablen]; 102 | memset(signature_table, 0, sizeof(unsigned) * tablen); 103 | unsigned gnum = 240, gsize = 2; 104 | 105 | // Loop through each vertex to create its signature 106 | for (int i = 0; i < this->vertex_num; ++i) { 107 | Vertex& v = this->vertices[i]; 108 | int pos = ::hash(&(v.label), 4, HASHSEED) % VLEN; 109 | signature_table[signum * i] = 1 << pos; 110 | 111 | // Process incoming edges 112 | for (int j = 0; j < v.in.size(); ++j) { 113 | Neighbor& nb = v.in[j]; 114 | int sig[2]; 115 | sig[0] = this->vertices[nb.vid].label; 116 | sig[1] = nb.elb; 117 | pos = ::hash(sig, 8, HASHSEED) % gnum; 118 | int a = pos / 16, b = pos % 16; 119 | unsigned t = signature_table[signum * i + 1 + a]; 120 | unsigned c = 3 << (2 * b); 121 | c = c & t; 122 | c = c >> (2 * b); 123 | switch (c) { 124 | case 0: c = 1; break; 125 | case 1: c = 3; break; 126 | default: c = 3; break; 127 | } 128 | c = c << (2 * b); 129 | t = t | c; 130 | signature_table[signum * i + 1 + a] = t; 131 | } 132 | 133 | // Process outgoing edges 134 | for (int j = 0; j < v.out.size(); ++j) { 135 | Neighbor& nb = v.out[j]; 136 | int sig[2]; 137 | sig[0] = this->vertices[nb.vid].label; 138 | sig[1] = -nb.elb; 139 | pos = ::hash(sig, 8, HASHSEED) % gnum; 140 | int a = pos / 16, b = pos % 16; 141 | unsigned t = signature_table[signum * i + 1 + a]; 142 | unsigned c = 3 << (2 * b); 143 | c = c & t; 144 | c = c >> (2 * b); 145 | switch (c) { 146 | case 0: c = 1; break; 147 | case 1: c = 3; break; 148 | default: c = 3; break; 149 | } 150 | c = c << (2 * b); 151 | t = t | c; 152 | signature_table[signum * i + 1 + a] = t; 153 | } 154 | 155 | /* 156 | cout << "Signature for vertex " << i << ": "; 157 | for (int k = 0; k < 16; ++k) { 158 | Util::DisplayBinary(signature_table[signum * i + k]); 159 | cout << " "; 160 | } 161 | cout << endl; 162 | */ 163 | } 164 | 165 | // If column-oriented, adjust the table structure 166 | if (column_oriented) { 167 | unsigned* new_table = new unsigned[tablen]; 168 | unsigned base = 0; 169 | for (int k = 0; k < 16; ++k) { 170 | for (int i = 0; i < this->vertex_num; ++i) { 171 | new_table[base++] = signature_table[signum * i + k]; 172 | } 173 | } 174 | delete[] signature_table; 175 | signature_table = new_table; 176 | // cout << "column oriented signature table" << endl; 177 | // for (int k = 0; k < 16; ++k) { 178 | // for (int i = 0; i < this->vertex_num; ++i) { 179 | // Util::DisplayBinary(signature_table[this->vertex_num * k + i]); 180 | // cout << " "; 181 | // } 182 | // cout << endl; 183 | // } 184 | } 185 | 186 | this->signature_table = signature_table; 187 | } 188 | 189 | /** 190 | * @brief Transforms the graph into Compressed Sparse Row (CSR) format. 191 | */ 192 | void Graph::transformToCSR() 193 | { 194 | this->vertex_num = this->vertices.size(); 195 | this->vertex_value = new unsigned[this->vertex_num]; 196 | for (int i = 0; i < this->vertex_num; ++i) { 197 | this->vertex_value[i] = this->vertices[i].label; 198 | // Sort on label, when label is identical, sort on VID 199 | sort(this->vertices[i].in.begin(), this->vertices[i].in.end()); 200 | sort(this->vertices[i].out.begin(), this->vertices[i].out.end()); 201 | } 202 | 203 | // Initialize CSR structures 204 | this->csrs_in = new PCSR[this->edgeLabelNum + 1]; 205 | this->csrs_out = new PCSR[this->edgeLabelNum + 1]; 206 | vector* keys_in = new vector[this->edgeLabelNum + 1]; 207 | vector* keys_out = new vector[this->edgeLabelNum + 1]; 208 | 209 | // Populate the keys for the CSR structures 210 | for (int i = 0; i < this->vertex_num; ++i) { 211 | int insize = this->vertices[i].in.size(), outsize = this->vertices[i].out.size(); 212 | //cout << "Processing vertex " << i << endl; 213 | for (int j = 0; j < insize; ++j) { 214 | int vid = this->vertices[i].in[j].vid; 215 | int elb = this->vertices[i].in[j].elb; 216 | 217 | 218 | int tsize = keys_in[elb].size(); 219 | 220 | /* 221 | cout << "Processing incoming edge " << j << " of vertex " << i << endl; 222 | cout << "Edge label (elb): " << elb << ", tsize: " << tsize << endl; 223 | cout << "Edge Label Num: " <edgeLabelNum< 0) { 235 | cout << "Accessing keys_in[" << elb << "][0]: " << keys_in[elb][0] << endl; 236 | cout << "Accessing keys_in[" << elb << "][" << tsize - 1 << "]: " << keys_in[elb][tsize - 1] << endl; 237 | cout << "Comparison result: " << (keys_in[elb][tsize - 1] != i) << endl; 238 | } 239 | */ 240 | if (tsize == 0 || keys_in[elb][tsize - 1] != i) { 241 | //cout << "Condition met, pushing back vertex " << i << " to keys_in[" << elb << "]" << endl; 242 | keys_in[elb].push_back(i); 243 | //cout << "Updated keys_in[" << elb << "]: "; 244 | /* 245 | for (unsigned k = 0; k < keys_in[elb].size(); ++k) { 246 | cout << keys_in[elb][k] << " "; 247 | } 248 | cout << endl; 249 | */ 250 | } 251 | 252 | PCSR* tcsr = &this->csrs_in[elb]; 253 | tcsr->edge_num++; 254 | } 255 | //cout << "Finished processing incoming edges for vertex " << i << endl; 256 | 257 | for (int j = 0; j < outsize; ++j) { 258 | int vid = this->vertices[i].out[j].vid; 259 | int elb = this->vertices[i].out[j].elb; 260 | int tsize = keys_out[elb].size(); 261 | 262 | //cout << "Processing outgoing edge " << j << " of vertex " << i << endl; 263 | //cout << "Edge label (elb): " << elb << ", tsize: " << tsize << endl; 264 | 265 | if (tsize == 0 || keys_out[elb][tsize - 1] != i) { 266 | keys_out[elb].push_back(i); 267 | } 268 | 269 | PCSR* tcsr = &this->csrs_out[elb]; 270 | tcsr->edge_num++; 271 | } 272 | //cout << "Finished processing outgoing edges for vertex " << i << endl; 273 | } 274 | 275 | /* 276 | cout << "Final keys_in contents:" << endl; 277 | for (int i = 0; i <= this->edgeLabelNum; ++i) { 278 | cout << "keys_in[" << i << "]: "; 279 | for (unsigned k = 0; k < keys_in[i].size(); ++k) { 280 | cout << keys_in[i][k] << " "; 281 | } 282 | cout << endl; 283 | } 284 | */ 285 | 286 | // Build CSR structures 287 | for (int i = 1; i <= this->edgeLabelNum; ++i) { 288 | PCSR* tcsr = &this->csrs_in[i]; 289 | this->buildPCSR(tcsr, keys_in[i], i, true); 290 | tcsr = &this->csrs_out[i]; 291 | this->buildPCSR(tcsr, keys_out[i], i, false); 292 | } 293 | 294 | delete[] keys_in; 295 | delete[] keys_out; 296 | } 297 | 298 | /** 299 | * @brief Builds the CSR representation for a given PCSR structure. 300 | * 301 | * @param pcsr Pointer to the PCSR structure. 302 | * @param keys Vector of keys for the CSR structure. 303 | * @param label Label of the edge. 304 | * @param incoming Boolean flag indicating whether the edges are incoming. 305 | */ 306 | void Graph::buildPCSR(PCSR* pcsr, vector& keys, int label, bool incoming) 307 | { 308 | unsigned key_num = keys.size(); 309 | unsigned* row_offset = new unsigned[key_num * 32]; 310 | unsigned edge_num = pcsr->edge_num; 311 | unsigned* column_index = new unsigned[edge_num]; 312 | pcsr->key_num = key_num; 313 | pcsr->row_offset = row_offset; 314 | pcsr->column_index = column_index; 315 | for (int i = 0; i < key_num * 16; ++i) { 316 | row_offset[2 * i] = INVALID; 317 | // This is nonsense in fact because it will be overwritten later for empty buckets 318 | row_offset[2 * i + 1] = 0; 319 | } 320 | for (int i = 0; i < edge_num; ++i) { 321 | column_index[i] = INVALID; 322 | } 323 | 324 | vector* buckets = new vector[key_num]; 325 | for (int i = 0; i < key_num; ++i) { 326 | unsigned id = keys[i]; 327 | unsigned pos = ::hash(&id, 4, HASHSEED) % key_num; 328 | buckets[pos].push_back(id); 329 | } 330 | queue empty_buckets; 331 | for (int i = 0; i < key_num; ++i) { 332 | if (buckets[i].empty()) { 333 | empty_buckets.push(i); 334 | } 335 | } 336 | for (int i = 0; i < key_num; ++i) { 337 | if (buckets[i].empty()) { 338 | continue; 339 | } 340 | int tsize = buckets[i].size(), j; 341 | if (tsize > 15) { 342 | cout << "DETECTED: more than 1 buckets are needed!" << endl; 343 | exit(1); 344 | } else if (tsize > 30) { 345 | cout << "DETECTED: more than 2 buckets are needed!" << endl; 346 | exit(1); 347 | } 348 | for (j = 0; j < 15 && j < tsize; ++j) { 349 | row_offset[32 * i + 2 * j] = buckets[i][j]; 350 | } 351 | if (j < tsize) { 352 | int another_bucket = empty_buckets.front(), k = 0; 353 | empty_buckets.pop(); 354 | row_offset[32 * i + 30] = another_bucket; 355 | while (j < tsize) { 356 | row_offset[32 * another_bucket + 2 * k] = buckets[i][j]; 357 | ++j; 358 | ++k; 359 | } 360 | } 361 | } 362 | delete[] buckets; 363 | 364 | // Copy elements to column index and set offset in each group 365 | unsigned pos = 0; 366 | for (int i = 0; i < key_num; ++i) { 367 | int j; 368 | for (j = 0; j < 15; ++j) { 369 | unsigned id = row_offset[32 * i + 2 * j]; 370 | if (id == INVALID) { 371 | break; 372 | } 373 | vector* adjs = &this->vertices[id].out; 374 | if (incoming) { 375 | adjs = &this->vertices[id].in; 376 | } 377 | row_offset[32 * i + 2 * j + 1] = pos; 378 | for (int k = 0; k < adjs->size(); ++k) { 379 | if ((*adjs)[k].elb == label) { 380 | column_index[pos++] = (*adjs)[k].vid; 381 | } 382 | } 383 | } 384 | // Set final next offset in this group, also the start offset of next valid ID 385 | row_offset[32 * i + 2 * j + 1] = pos; 386 | // row_offset[32 * i + 31] = pos; 387 | } 388 | } 389 | 390 | /** 391 | * @brief Counts the maximum degree of the vertices in the graph. 392 | * 393 | * @return unsigned Maximum degree. 394 | */ 395 | unsigned Graph::countMaxDegree() 396 | { 397 | // Count the degree based on direction and edge labels 398 | int size = this->vertices.size(); 399 | unsigned maxv = 0; 400 | for (int i = 0; i < size; ++i) { 401 | unsigned t = vertices[i].in.size() + vertices[i].out.size(); 402 | if (t > maxv) { 403 | maxv = t; 404 | } 405 | } 406 | return maxv; 407 | } 408 | 409 | /** 410 | * @brief Prints the graph's vertices, edges, and their values. 411 | */ 412 | void Graph::printGraph() 413 | { 414 | int i, n = this->vertex_num; 415 | cout << "Graph contains " << n << " nodes and "; 416 | 417 | // Calculate the number of edges 418 | int edge_count = 0; 419 | for (i = 0; i < n; ++i) { 420 | edge_count += this->vertices[i].out.size(); 421 | } 422 | cout << edge_count << " edges." << endl; 423 | 424 | cout << "numEdgeLabel: " << this->edgeLabelNum << endl; 425 | cout << "numVertexLabel: " << this->vertexLabelNum << endl; 426 | 427 | // Print node information 428 | cout << "Nodes (ID, label):" << endl; 429 | for (i = 0; i < n; ++i) { 430 | cout << "(" << i << ", " << this->vertex_value[i] << ")" << endl; 431 | } 432 | 433 | // Print edge information 434 | cout << "Edges ((START NODE, END NODE), label):" << endl; 435 | for (i = 0; i < n; ++i) { 436 | for (auto& edge : this->vertices[i].out) { 437 | cout << "((" << i << ", " << edge.vid << "), " << edge.elb << ")" << endl; 438 | } 439 | } 440 | } 441 | -------------------------------------------------------------------------------- /GSI_cpp/graph/Graph.h: -------------------------------------------------------------------------------- 1 | /*============================================================================= 2 | # Filename: Graph.h 3 | # Author: Bookug Lobert 4 | # Mail: 1181955272@qq.com 5 | # Last Modified: 2016-10-24 23:00 6 | # Description: 7 | =============================================================================*/ 8 | 9 | //Data Structure: CSR with 4 arrays, we should use structure of array instead of array of structure 10 | //row offsets 11 | //column indices 12 | //values(label of edge) 13 | //flags(label of vertice) 14 | 15 | #ifndef _GRAPH_GRAPH_H 16 | #define _GRAPH_GRAPH_H 17 | 18 | #include "../util/Util.h" 19 | 20 | class Neighbor 21 | { 22 | public: 23 | VID vid; 24 | LABEL elb; 25 | Neighbor() 26 | { 27 | vid = -1; 28 | elb = -1; 29 | } 30 | Neighbor(int _vid, int _elb) 31 | { 32 | vid = _vid; 33 | elb = _elb; 34 | } 35 | bool operator<(const Neighbor& _nb) const 36 | { 37 | if(this->elb == _nb.elb) 38 | { 39 | return this->vid < _nb.vid; 40 | } 41 | else 42 | { 43 | return this->elb < _nb.elb; 44 | } 45 | } 46 | }; 47 | 48 | class Element 49 | { 50 | public: 51 | int label; 52 | int id; 53 | bool operator<(const Element& _ele) const 54 | { 55 | if(this->label == _ele.label) 56 | { 57 | return this->id <_ele.id; 58 | } 59 | else 60 | { 61 | return this->label < _ele.label; 62 | } 63 | } 64 | }; 65 | 66 | class Vertex 67 | { 68 | public: 69 | //VID id; 70 | LABEL label; 71 | //NOTICE:VID and EID is just used in this single graph 72 | std::vector in; 73 | std::vector out; 74 | Vertex() 75 | { 76 | label = -1; 77 | } 78 | Vertex(LABEL lb):label(lb) 79 | { 80 | } 81 | }; 82 | 83 | class PCSR 84 | { 85 | public: 86 | unsigned* row_offset; //the size is 32*key_num 87 | unsigned* column_index; 88 | unsigned key_num; //also the group number 89 | unsigned edge_num; 90 | PCSR() 91 | { 92 | row_offset = NULL; 93 | column_index = NULL; 94 | key_num = 0; 95 | edge_num = 0; 96 | } 97 | ~PCSR() 98 | { 99 | delete[] row_offset; 100 | delete[] column_index; 101 | } 102 | inline unsigned getEdgeNum() const 103 | { 104 | return this->edge_num; 105 | } 106 | }; 107 | 108 | class Graph 109 | { 110 | public: 111 | std::vector vertices; 112 | void addVertex(LABEL _vlb); 113 | void addEdge(VID _from, VID _to, LABEL _elb); 114 | 115 | // Setter functions 116 | void setVertexLabelNum(unsigned num); 117 | void setEdgeLabelNum(unsigned num); 118 | 119 | unsigned vertexLabelNum, edgeLabelNum; 120 | //CSR format: 4 pointers 121 | unsigned vertex_num; 122 | unsigned* vertex_value; 123 | 124 | PCSR* csrs_in; 125 | PCSR* csrs_out; 126 | //unsigned* row_offset_in; //range is 0~vertex_num, the final is a border(not valid vertex) 127 | //unsigned* edge_value_in; 128 | //unsigned* edge_offset_in; 129 | //unsigned* column_index_in; 130 | //unsigned* row_offset_out; 131 | //unsigned* edge_value_out; 132 | //unsigned* edge_offset_out; 133 | //unsigned* column_index_out; 134 | //Inverse Label List 135 | //unsigned label_num; 136 | //unsigned* inverse_label; 137 | //unsigned* inverse_offset; 138 | //unsigned* inverse_vertex; 139 | 140 | //signature table 141 | //column oriented for data graph 142 | //row oriented for query graph 143 | unsigned* signature_table; 144 | 145 | Graph() 146 | { 147 | //row_offset_in = row_offset_out = vertex_value = column_index_in = column_index_out = edge_value_in = edge_value_out = NULL; 148 | //edge_offset_in = edge_offset_out = NULL; 149 | vertex_num = 0; 150 | //inverse_label = inverse_offset = inverse_vertex = NULL; 151 | //label_num = 0; 152 | signature_table = NULL; 153 | csrs_in = csrs_out = NULL; 154 | } 155 | ~Graph() 156 | { 157 | delete[] vertex_value; 158 | //delete[] row_offset_in; 159 | //delete[] row_offset_out; 160 | //delete[] column_index_in; 161 | //delete[] column_index_out; 162 | //delete[] edge_value_in; 163 | //delete[] edge_value_out; 164 | //delete[] edge_offset_in; 165 | //delete[] edge_offset_out; 166 | 167 | //delete[] inverse_label; 168 | //delete[] inverse_offset; 169 | //delete[] inverse_vertex; 170 | delete[] signature_table; 171 | delete[] csrs_in; 172 | delete[] csrs_out; 173 | } 174 | 175 | void buildPCSR(PCSR* pcsr, std::vector& keys, int label, bool incoming); 176 | void transformToCSR(); 177 | void preprocessing(bool column_oriented=false); 178 | void buildSignature(bool column_oriented); 179 | void printGraph(); 180 | 181 | //inline unsigned eSizeIn() const 182 | //{ 183 | //unsigned in_label_num = this->row_offset_in[vertex_num]; 184 | //return this->edge_offset_in[in_label_num]; 185 | //} 186 | //inline unsigned eSizeOut() const 187 | //{ 188 | //unsigned out_label_num = this->row_offset_out[vertex_num]; 189 | //return this->edge_offset_out[out_label_num]; 190 | //} 191 | //inline unsigned eSize() const 192 | //{ 193 | //return eSizeIn() + eSizeOut(); 194 | //} 195 | inline unsigned vSize() const 196 | { 197 | return vertex_num; 198 | } 199 | //inline void getInNeighbor(unsigned id, unsigned& in_neighbor_num, unsigned& in_neighbor_offset) 200 | //{ 201 | //in_neighbor_offset = this->edge_offset_in[row_offset_in[id]]; 202 | //in_neighbor_num = this->edge_offset_in[row_offset_in[id+1]] - in_neighbor_offset; 203 | //} 204 | //inline void getOutNeighbor(unsigned id, unsigned& out_neighbor_num, unsigned& out_neighbor_offset) 205 | //{ 206 | //out_neighbor_offset = this->edge_offset_out[row_offset_out[id]]; 207 | //out_neighbor_num = this->edge_offset_out[row_offset_out[id+1]] - out_neighbor_offset; 208 | //} 209 | unsigned countMaxDegree(); 210 | 211 | }; 212 | 213 | #endif 214 | 215 | -------------------------------------------------------------------------------- /GSI_cpp/io/IO.cpp: -------------------------------------------------------------------------------- 1 | /*============================================================================= 2 | # Filename: IO.cpp 3 | # Author: Bookug Lobert 4 | # Mail: 1181955272@qq.com 5 | # Last Modified: 2016-10-24 22:55 6 | # Description: 7 | =============================================================================*/ 8 | 9 | #include "IO.h" 10 | 11 | using namespace std; 12 | 13 | IO::IO() 14 | { 15 | this->qfp = NULL; 16 | this->dfp = NULL; 17 | this->ofp = NULL; 18 | this->data_id = -1; 19 | } 20 | 21 | IO::IO(string query, string data, string file) 22 | { 23 | this->data_id = -1; 24 | this->line = "============================================================"; 25 | qfp = fopen(query.c_str(), "r"); 26 | if (qfp == NULL) { 27 | cerr << "input open error!" << endl; 28 | return; 29 | } 30 | dfp = fopen(data.c_str(), "r"); 31 | if (dfp == NULL) { 32 | cerr << "input open error!" << endl; 33 | return; 34 | } 35 | ofp = fopen(file.c_str(), "w+"); 36 | if (ofp == NULL) { 37 | cerr << "output open error!" << endl; 38 | return; 39 | } 40 | } 41 | 42 | Graph* 43 | IO::input(FILE* fp) 44 | { 45 | char c1, c2; 46 | int id0, id1, id2, lb; 47 | bool flag = false; 48 | Graph* ng = NULL; 49 | 50 | while (true) { 51 | fscanf(fp, "%c", &c1); 52 | if (c1 == 't') { 53 | if (flag) { 54 | fseek(fp, -1, SEEK_CUR); 55 | return ng; 56 | } 57 | flag = true; 58 | fscanf(fp, " %c %d\n", &c2, &id0); 59 | if (id0 == -1) { 60 | return NULL; 61 | } else { 62 | ng = new Graph; 63 | } 64 | //read vertex num, edge num, vertex label num, edge label num 65 | int numVertex, numEdge; 66 | fscanf(fp, " %d %d %d %d\n", &numVertex, &numEdge, &(ng->vertexLabelNum), &(ng->edgeLabelNum)); 67 | } else if (c1 == 'v') { 68 | fscanf(fp, " %d %d\n", &id1, &lb); 69 | //NOTICE: we add 1 to labels for both vertex and edge, to ensure the label is positive! 70 | //ng->addVertex(lb+1); 71 | ng->addVertex(lb); 72 | } else if (c1 == 'e') { 73 | fscanf(fp, " %d %d %d\n", &id1, &id2, &lb); 74 | //NOTICE:we treat this graph as directed, each edge represents two 75 | //This may cause too many matchings, if to reduce, only add the first one 76 | //ng->addEdge(id1, id2, lb+1); 77 | ng->addEdge(id1, id2, lb); 78 | //ng->addEdge(id2, id1, lb); 79 | } else { 80 | cerr << "ERROR in input() -- invalid char" << endl; 81 | return NULL; 82 | } 83 | } 84 | return NULL; 85 | } 86 | 87 | bool 88 | IO::input(Graph*& data_graph) 89 | { 90 | data_graph = this->input(this->dfp); 91 | if (data_graph == NULL) 92 | return false; 93 | this->data_id++; 94 | data_graph->preprocessing(true); 95 | return true; 96 | } 97 | 98 | bool 99 | IO::input(vector& query_list) 100 | { 101 | Graph* graph = NULL; 102 | while (true) { 103 | graph = this->input(qfp); 104 | if (graph == NULL) //to the end 105 | break; 106 | graph->preprocessing(false); 107 | query_list.push_back(graph); 108 | } 109 | 110 | return true; 111 | } 112 | 113 | bool 114 | IO::output(int qid) 115 | { 116 | fprintf(ofp, "query graph:%d data graph:%d\n", qid, this->data_id); 117 | fprintf(ofp, "%s\n", line.c_str()); 118 | return true; 119 | } 120 | 121 | bool 122 | IO::output() 123 | { 124 | fprintf(ofp, "\n\n\n"); 125 | return true; 126 | } 127 | 128 | bool 129 | IO::output(unsigned* final_result, unsigned result_row_num, unsigned result_col_num, int* id_map) 130 | { 131 | cout << "result: " << result_row_num << " " << result_col_num << endl; 132 | int i, j, k; 133 | for (i = 0; i < result_row_num; ++i) { 134 | unsigned* ans = final_result + i * result_col_num; 135 | for (j = 0; j < result_col_num; ++j) { 136 | k = ans[id_map[j]]; 137 | fprintf(ofp, "(%u, %u) ", j, k); 138 | } 139 | fprintf(ofp, "\n"); 140 | } 141 | fprintf(ofp, "\n\n\n"); 142 | return true; 143 | } 144 | 145 | bool 146 | IO::output(int* m, int size) 147 | { 148 | for (int i = 0; i < size; ++i) { 149 | fprintf(ofp, "(%d, %d) ", i, m[i]); 150 | } 151 | fprintf(ofp, "\n"); 152 | return true; 153 | } 154 | 155 | void 156 | IO::flush() 157 | { 158 | fflush(this->ofp); 159 | } 160 | 161 | IO::~IO() 162 | { 163 | fclose(this->qfp); 164 | this->qfp = NULL; 165 | fclose(this->dfp); 166 | this->dfp = NULL; 167 | fclose(this->ofp); 168 | this->ofp = NULL; 169 | } 170 | -------------------------------------------------------------------------------- /GSI_cpp/io/IO.h: -------------------------------------------------------------------------------- 1 | /*============================================================================= 2 | # Filename: IO.h 3 | # Author: Bookug Lobert 4 | # Mail: 1181955272@qq.com 5 | # Last Modified: 2016-10-24 22:55 6 | # Description: 7 | =============================================================================*/ 8 | 9 | #ifndef _IO_IO_H 10 | #define _IO_IO_H 11 | 12 | #include "../util/Util.h" 13 | #include "../graph/Graph.h" 14 | 15 | class IO 16 | { 17 | public: 18 | IO(); 19 | IO(std::string query, std::string data, std::string file); 20 | bool input(std::vector& query_list); 21 | bool input(Graph*& data_graph); 22 | Graph* input(FILE* fp); 23 | bool output(int qid); 24 | bool output(); 25 | bool output(unsigned* final_result, unsigned result_row_num, unsigned result_col_num, int* id_map); 26 | bool output(int* m, int size); 27 | void flush(); 28 | ~IO(); 29 | private: 30 | std::string line; 31 | int data_id; 32 | //query file pointer 33 | FILE* qfp; 34 | //data file pointer 35 | FILE* dfp; 36 | //output file pointer 37 | FILE* ofp; 38 | }; 39 | 40 | #endif 41 | 42 | -------------------------------------------------------------------------------- /GSI_cpp/isomorphism.cpython-312-x86_64-linux-gnu.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkumod/GSI/8f098a6e7be056d6aee8d3a00d06f73ed2032ab7/GSI_cpp/isomorphism.cpython-312-x86_64-linux-gnu.so -------------------------------------------------------------------------------- /GSI_cpp/libisomorphism.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkumod/GSI/8f098a6e7be056d6aee8d3a00d06f73ed2032ab7/GSI_cpp/libisomorphism.so -------------------------------------------------------------------------------- /GSI_cpp/main/isomorphism.cu: -------------------------------------------------------------------------------- 1 | #include "../graph/Graph.h" 2 | #include "../match/Match.h" 3 | #include "isomorphism.h" 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | using namespace std; 10 | 11 | /** 12 | * @brief Creates a graph using given node IDs, node labels, edge IDs, and edge labels. 13 | * 14 | * @param nodeIDs Vector of node IDs. 15 | * @param nodeLabels Vector of node labels corresponding to node IDs. 16 | * @param edgeIDs Vector of pairs representing edge connections (from, to). 17 | * @param edgeLabels Vector of edge labels corresponding to edge IDs. 18 | * @param column_oriented Boolean flag indicating whether the graph should be column-oriented. 19 | * @return Pointer to the created Graph object. 20 | */ 21 | Graph* createGraph(const std::vector& nodeIDs, const std::vector& nodeLabels, 22 | const std::vector>& edgeIDs, const std::vector& edgeLabels, 23 | bool column_oriented) { 24 | 25 | Graph* g = new Graph(); 26 | 27 | // Add vertices in order of node label's from least to greatest 28 | for (size_t i = 0; i < nodeIDs.size(); ++i) { 29 | g->addVertex(nodeLabels[i]); // Add vertex with corresponding label 30 | } 31 | 32 | // Add edges 33 | for (size_t i = 0; i < edgeIDs.size(); ++i) { 34 | int from = edgeIDs[i].first; 35 | int to = edgeIDs[i].second; 36 | int label = edgeLabels[i]; 37 | g->addEdge(from, to, label); // Add edge with corresponding label 38 | } 39 | 40 | // Set vertexLabelNum and edgeLabelNum 41 | std::set uniqueVertexCount(nodeLabels.begin(), nodeLabels.end()); 42 | std::set uniqueEdgeCount(edgeLabels.begin(), edgeLabels.end()); 43 | g->setVertexLabelNum(uniqueVertexCount.size()); 44 | g->setEdgeLabelNum(uniqueEdgeCount.size()); 45 | 46 | g->preprocessing(column_oriented); // Preprocess the graph with the given column_oriented boolean flag 47 | //g->printGraph(); // Uncomment to print the graph for debugging purposes 48 | return g; 49 | } 50 | 51 | /** 52 | * @brief Finds all isomorphisms between the query graph and the data graph. 53 | * 54 | * @param query Pointer to the query Graph object. 55 | * @param data Pointer to the data Graph object. 56 | * @return Vector of unordered maps, where each map represents a vertex mapping for one isomorphism. 57 | */ 58 | std::vector> findIsomorphisms(Graph* query, Graph* data) { 59 | unsigned* final_result = nullptr; 60 | int* id_map = nullptr; 61 | unsigned result_row_num = 0, result_col_num = 0; 62 | 63 | Match m(query, data); 64 | 65 | // Perform the matching 66 | m.match(final_result, result_row_num, result_col_num, id_map); 67 | 68 | /* 69 | // Print final_result, result_row_num, result_col_num, and id_map 70 | std::cout << "Result Row Num: " << result_row_num << std::endl; 71 | std::cout << "Result Col Num: " << result_col_num << std::endl; 72 | 73 | if (id_map) { 74 | std::cout << "ID Map: "; 75 | for (unsigned j = 0; j < result_col_num; ++j) { 76 | std::cout << id_map[j] << " "; 77 | } 78 | std::cout << std::endl; 79 | } 80 | 81 | if (final_result) { 82 | std::cout << "Final Result:" << std::endl; 83 | for (unsigned i = 0; i < result_row_num; ++i) { 84 | for (unsigned j = 0; j < result_col_num; ++j) { 85 | std::cout << final_result[i * result_col_num + j] << " "; 86 | } 87 | std::cout << std::endl; 88 | } 89 | } 90 | */ 91 | std::vector> mappings; 92 | if (final_result) { 93 | int i, j, k; 94 | for (i = 0; i < result_row_num; ++i) { 95 | // Calculate the pointer to the start of the i-th row in the 1D array 96 | unsigned* ans = final_result + i * result_col_num; 97 | 98 | // Store a mapping as a hashmap 99 | std::unordered_map mapping; 100 | 101 | // Iterate over each column in the current row 102 | for (j = 0; j < result_col_num; ++j) { 103 | // Access the element in the j-th column of the current row using the id_map for column index 104 | k = ans[id_map[j]]; 105 | // Print the column index and the value to the file 106 | mapping[j] = k; 107 | } 108 | 109 | mappings.push_back(mapping); 110 | } 111 | delete[] final_result; // Ensure to free the allocated memory 112 | final_result = nullptr; // Prevent double free 113 | } 114 | return mappings; 115 | } 116 | 117 | /** 118 | * @brief Prints the vertex mappings from the query graph to the data graph. 119 | * 120 | * @param mapping Unordered map where keys are query graph vertex IDs and values are data graph vertex IDs. 121 | */ 122 | void printMapping(const unordered_map& mapping) { 123 | if (mapping.empty()) { 124 | cout << "No isomorphism found." << endl; 125 | } else { 126 | //cout << "Mappings found:" << endl; 127 | for (const auto& pair : mapping) { 128 | cout << "Query vertex " << pair.first << " -> Data vertex " << pair.second << endl; 129 | } 130 | cout << endl; 131 | } 132 | } 133 | 134 | /** 135 | * @brief Initializes the GPU for processing. 136 | * 137 | * @param dev The GPU device ID to initialize. 138 | * @param verbose If GPU initialization status should be printed to the console. 139 | */ 140 | void initializeGPU(int dev, bool verbose){ 141 | Match::initGPU(dev,verbose); 142 | } 143 | -------------------------------------------------------------------------------- /GSI_cpp/main/isomorphism.h: -------------------------------------------------------------------------------- 1 | #ifndef ISOMORPHISM_H 2 | #define ISOMORPHISM_H 3 | 4 | #include "../graph/Graph.h" 5 | #include "../match/Match.h" 6 | #include 7 | #include 8 | #include 9 | 10 | // Forward declarations 11 | class Graph; 12 | 13 | std::vector> findIsomorphisms(Graph* query, Graph* data); 14 | void printMapping(const std::unordered_map& mapping); 15 | 16 | void initializeGPU(int dev, bool verbose); 17 | 18 | // Declare the function to create a graph from data 19 | Graph* createGraph(const std::vector& nodeIDs, const std::vector& nodeLabels, 20 | const std::vector>& edgeIDs, const std::vector& edgeLabels, 21 | bool column_oriented); 22 | 23 | #endif // ISOMORPHISM_H 24 | -------------------------------------------------------------------------------- /GSI_cpp/main/run.cu: -------------------------------------------------------------------------------- 1 | /*============================================================================= 2 | # Filename: run.cpp 3 | # Author: bookug 4 | # Mail: bookug@qq.com 5 | # Last Modified: 2017-12-21 16:47 6 | # Description: 7 | how to time the program? 8 | https://blog.csdn.net/litdaguang/article/details/50520549 9 | warmup GPU and the timing should be the average of 10 runs 10 | =============================================================================*/ 11 | 12 | #include "../util/Util.h" 13 | #include "../io/IO.h" 14 | #include "../graph/Graph.h" 15 | #include "../match/Match.h" 16 | 17 | using namespace std; 18 | 19 | //NOTICE:a pattern occurs in a graph, then support++(not the matching num in a graph), support/N >= minsup 20 | vector query_list; 21 | 22 | int 23 | main(int argc, const char * argv[]) 24 | { 25 | int i; 26 | 27 | string output = "ans.txt"; 28 | if(argc > 5 || argc < 3) 29 | { 30 | cerr<<"invalid arguments!"<= 4) 36 | { 37 | output = argv[3]; 38 | } 39 | int dev = 0; 40 | if(argc == 5) 41 | { 42 | dev = atoi(argv[4]); 43 | } 44 | 45 | //set the GPU and warmup 46 | Match::initGPU(dev); 47 | 48 | long t1 = Util::get_cur_time(); 49 | 50 | IO io = IO(query, data, output); 51 | //read query file and keep all queries in memory 52 | io.input(query_list); 53 | int qnum = query_list.size(); 54 | 55 | cerr<<"input ok!"<printGraph(); 74 | /*getchar();*/ 75 | //NOTICE: we just compare the matching time(include the communication with GPU) 76 | long start = Util::get_cur_time(); 77 | for(i = 0; i < qnum; ++i) 78 | { 79 | Match m(query_list[i], data_graph); 80 | io.output(i); 81 | /*getchar();*/ 82 | //long tt1 = Util::get_cur_time(); 83 | m.match(io, final_result, result_row_num, result_col_num, id_map); 84 | //long tt2 = Util::get_cur_time(); 85 | //cerr<<"match used: "<<(tt2-tt1)<<"ms"< 10 | #include "Match.h" 11 | 12 | using namespace std; 13 | 14 | //on Titan XP the pointer consumes 8 bytes 15 | //it uses little-endian byte order 16 | 17 | //Contsant memory and cache 18 | //The constant memory size is 64 KB for compute capability 1.0-3.0 devices. The cache working set is only 8KB 19 | //part of constant memory is used for compiling and kernel executation 20 | //https://stackoverflow.com/questions/10256402/why-is-the-constant-memory-size-limited-in-cuda 21 | //Local Memory (no limit other than the capacity of global memory) 22 | //https://stackoverflow.com/questions/28810365/amount-of-local-memory-per-cuda-thread 23 | //Shared Memory 24 | //https://devblogs.nvidia.com/using-shared-memory-cuda-cc/ 25 | //Read-only cache: 48KB, no L1 cache in Titan XP 26 | //But we should not occupy the whole cache with __ldg() 27 | //When the compiler detects that 28 | //the read-only condition is satisfied for some data, it will use __ldg() to read it. The 29 | //compiler might not always be able to detect that the read-only condition is satisfied 30 | //for some data. Marking pointers used for loading such data with both the const and 31 | //__restrict__ qualifiers increases the likelihood that the compiler will detect the readonly condition. 32 | 33 | //NOTICE: if we use too many checkCudaErrors in the program, then it may report error: 34 | //too many resources requested for launch cudaGetLastError() 35 | //#define DEBUG 1 36 | #define MAXIMUM_SCORE 100000000.0f 37 | //! the maximum degree in the query graph, used for linking structures 38 | #define MAX_DEGREE 20 39 | #define SUMMARY_SIZE 2*1024 //2048 unsigneds=8KB 40 | #define SUMMARY_BYTES SUMMARY_SIZE*4 //8KB 41 | #define SUMMARY_BITS SUMMARY_BYTES*8 //8KB=1024*64bits 42 | #define MAX_QUERY_SIZE 50 43 | 44 | //GPU上用new/delete大量处理小内存的性能会比较差 45 | //如果中间表实在太大(可能最终结果本身就很多),那么需要考虑分段或者中间表结构的压缩(是否可以按列存?) 46 | //block或warp内部考虑用共享内存来去重排序并使负载均衡,根据两表大小决定用哈希表还是二分, 47 | //同一时刻只要一个哈希表且可动态生成,并在shared memory中加缓存来优化显存访问 48 | 49 | //Constant memory has 64KB cache for each SM 50 | //constant variable is not a pointer, and can not be declared in a function 51 | //no need to alloc or free for constant variables, it is only readable for kernel functions 52 | __constant__ unsigned* c_row_offset; 53 | __constant__ unsigned* c_column_index; 54 | __constant__ unsigned c_key_num; 55 | __constant__ unsigned* c_result_tmp_pos; 56 | __constant__ unsigned* c_result; 57 | __constant__ unsigned* c_candidate; 58 | /*__constant__ unsigned c_candidate_num;*/ 59 | __constant__ unsigned c_result_row_num; 60 | __constant__ unsigned c_result_col_num; 61 | /*__constant__ unsigned c_link_num;*/ 62 | /*__constant__ unsigned c_link_pos[MAX_DEGREE];*/ 63 | /*__constant__ unsigned c_link_edge[MAX_DEGREE];*/ 64 | __constant__ unsigned c_link_pos; 65 | __constant__ unsigned c_link_edge; 66 | __constant__ unsigned c_signature[SIGNUM]; 67 | 68 | void 69 | Match::initGPU(int dev, bool verbose) 70 | { 71 | int deviceCount; 72 | cudaGetDeviceCount(&deviceCount); 73 | if (deviceCount == 0 && verbose) { 74 | fprintf(stderr, "error: no devices supporting CUDA.\n"); 75 | exit(EXIT_FAILURE); 76 | } 77 | cudaSetDevice(dev); 78 | //NOTE: 48KB shared memory per block, 1024 threads per block, 30 SMs and 128 cores per SM 79 | cudaDeviceProp devProps; 80 | if (cudaGetDeviceProperties(&devProps, dev) == 0 && verbose) 81 | { 82 | printf("Using device %d:\n", dev); 83 | printf("%s; global mem: %luB; compute v%d.%d; clock: %d kHz; shared mem: %dB; block threads: %d; SM count: %d\n", 84 | devProps.name, devProps.totalGlobalMem, 85 | (int)devProps.major, (int)devProps.minor, 86 | (int)devProps.clockRate, 87 | devProps.sharedMemPerBlock, devProps.maxThreadsPerBlock, devProps.multiProcessorCount); 88 | } 89 | if(verbose){ 90 | cout<<"GPU selected"<= 4) 150 | { 151 | uint32_t k = *(uint32_t*) data; 152 | k *= m; 153 | k ^= k >> r; 154 | k *= m; 155 | h *= m; 156 | h ^= k; 157 | data += 4; 158 | len -= 4; 159 | } 160 | // Handle the last few bytes of the input array 161 | switch (len) 162 | { 163 | case 3: 164 | h ^= data[2] << 16; 165 | case 2: 166 | h ^= data[1] << 8; 167 | case 1: 168 | h ^= data[0]; 169 | h *= m; 170 | }; 171 | // Do a few final mixes of the hash to ensure the last few 172 | // bytes are well-incorporated. 173 | h ^= h >> 13; 174 | h *= m; 175 | h ^= h >> 15; 176 | return h; 177 | } 178 | 179 | 180 | //NOTICE: below is for smid, detecting running on which SM. 181 | #define DEVICE_INTRINSIC_QUALIFIERS __device__ __forceinline__ 182 | 183 | DEVICE_INTRINSIC_QUALIFIERS 184 | unsigned int 185 | smid() 186 | { 187 | unsigned int r; 188 | asm("mov.u32 %0, %%smid;" : "=r"(r)); 189 | return r; 190 | } 191 | 192 | DEVICE_INTRINSIC_QUALIFIERS 193 | unsigned int 194 | nsmid() 195 | { 196 | #if (__CUDA_ARCH__ >= 200) 197 | unsigned int r; 198 | asm("mov.u32 %0, %%nsmid;" : "=r"(r)); 199 | return r; 200 | #else 201 | return 30; 202 | #endif 203 | } 204 | 205 | 206 | void 207 | Match::copyHtoD(unsigned*& d_ptr, unsigned* h_ptr, unsigned bytes) 208 | { 209 | unsigned* p = NULL; 210 | cudaMalloc(&p, bytes); 211 | cudaMemcpy(p, h_ptr, bytes, cudaMemcpyHostToDevice); 212 | d_ptr = p; 213 | checkCudaErrors(cudaGetLastError()); 214 | } 215 | 216 | void Match::exclusive_sum(unsigned* d_array, unsigned size) 217 | { 218 | // Determine temporary device storage requirements 219 | void *d_temp_storage = NULL; //must be set to distinguish two phase 220 | size_t temp_storage_bytes = 0; 221 | cub::DeviceScan::ExclusiveSum(d_temp_storage, temp_storage_bytes, d_array, d_array, size); 222 | // Allocate temporary storage 223 | cudaMalloc(&d_temp_storage, temp_storage_bytes); 224 | // Run exclusive prefix sum 225 | cub::DeviceScan::ExclusiveSum(d_temp_storage, temp_storage_bytes, d_array, d_array, size); 226 | cudaFree(d_temp_storage); 227 | } 228 | 229 | 230 | Match::Match(Graph* _query, Graph* _data) 231 | { 232 | this->query = _query; 233 | this->data = _data; 234 | id2pos = pos2id = NULL; 235 | } 236 | 237 | Match::~Match() 238 | { 239 | delete[] this->id2pos; 240 | } 241 | 242 | inline void 243 | Match::add_mapping(int _id) 244 | { 245 | pos2id[current_pos] = _id; 246 | id2pos[_id] = current_pos; 247 | this->current_pos++; 248 | } 249 | 250 | //! \fn get_minimum_idx(float* score, int qsize) 251 | //! \brief 252 | //! \note nodes are selected based on connectivity(the next node should be linked to at least one mapped node) 253 | int 254 | Match::get_minimum_idx(float* score, int qsize) 255 | { 256 | float* min_ptr = NULL; 257 | float minscore = FLT_MAX; 258 | //choose the start node based on score 259 | if(this->current_pos == 0) 260 | { 261 | min_ptr = min_element(score, score+qsize); 262 | minscore = *min_ptr; 263 | } 264 | 265 | for(int i = 0; i < this->current_pos; ++i) 266 | { 267 | int id = this->pos2id[i]; 268 | int insize = this->query->vertices[id].in.size(), outsize = this->query->vertices[id].out.size(); 269 | for(int j = 0; j < insize; ++j) 270 | { 271 | int id2 = this->query->vertices[id].in[j].vid; 272 | if(score[id2] < minscore) 273 | { 274 | minscore = score[id2]; 275 | min_ptr = score+id2; 276 | } 277 | } 278 | for(int j = 0; j < outsize; ++j) 279 | { 280 | int id2 = this->query->vertices[id].out[j].vid; 281 | if(score[id2] < minscore) 282 | { 283 | minscore = score[id2]; 284 | min_ptr = score+id2; 285 | } 286 | } 287 | } 288 | int min_idx = min_ptr - score; 289 | //set this ID to maximum so it will not be chosed again 290 | memset(min_ptr, 0x7f, sizeof(float)); 291 | /*thrust::device_ptr dev_ptr(d_score);*/ 292 | /*float* min_ptr = thrust::raw_pointer_cast(thrust::min_element(dev_ptr, dev_ptr+qsize));*/ 293 | /*int min_idx = min_ptr - d_score;*/ 294 | /*//set this node's score to maximum so it won't be chosed again*/ 295 | /*cudaMemset(min_ptr, 0x7f, sizeof(float));*/ 296 | 297 | //NOTICE: memset is used per-byte, so do not set too large value, otherwise it will be negative 298 | //http://blog.csdn.net/Vmurder/article/details/46537613 299 | /*cudaMemset(d_score+min_idx, 1000.0f, sizeof(float));*/ 300 | /*float tmp = 0.0f;*/ 301 | /*cout<<"to check the score: ";*/ 302 | /*for(int i = 0; i < qsize; ++i)*/ 303 | /*{*/ 304 | /*cudaMemcpy(&tmp, d_score+i, sizeof(float), cudaMemcpyDeviceToHost);*/ 305 | /*cout<current_pos; ++i) 315 | { 316 | std::cout << pos2id[i] << " "; 317 | } 318 | std::cout << std::endl; 319 | */ 320 | 321 | this->add_mapping(min_idx); 322 | return min_idx; 323 | } 324 | 325 | void 326 | Match::copyGraphToGPU() 327 | { 328 | //BETTER: we may not include this time for final comparison because it only needs once 329 | 330 | //cout<<"to copy graph"< _key) 364 | { 365 | return INVALID; 366 | } 367 | 368 | unsigned low = 0; 369 | unsigned high = _array_num - 1; 370 | unsigned mid; 371 | while (low <= high) 372 | { 373 | mid = (high - low) / 2 + low; //same to (low+high)/2 374 | if (_array[mid] == _key) 375 | { 376 | return mid; 377 | } 378 | if (_array[mid] > _key) 379 | { 380 | high = mid - 1; 381 | } 382 | else 383 | { 384 | low = mid + 1; 385 | } 386 | } 387 | return INVALID; 388 | } 389 | 390 | //BETTER: maybe we can use dynamic parallism here 391 | __device__ unsigned 392 | binary_search(unsigned _key, unsigned* _array, unsigned _array_num) 393 | { 394 | //http://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#host 395 | /*#if defined(__CUDA_ARCH__)*/ 396 | //MAYBE: return the right border, or use the left border and the right border as parameters 397 | if (_array_num == 0 || _array == NULL) 398 | { 399 | return INVALID; 400 | } 401 | 402 | unsigned _first = _array[0]; 403 | unsigned _last = _array[_array_num - 1]; 404 | 405 | if (_last == _key) 406 | { 407 | return _array_num - 1; 408 | } 409 | 410 | if (_last < _key || _first > _key) 411 | { 412 | return INVALID; 413 | } 414 | 415 | unsigned low = 0; 416 | unsigned high = _array_num - 1; 417 | unsigned mid; 418 | while (low <= high) 419 | { 420 | mid = (high - low) / 2 + low; //same to (low+high)/2 421 | if (_array[mid] == _key) 422 | { 423 | return mid; 424 | } 425 | if (_array[mid] > _key) 426 | { 427 | high = mid - 1; 428 | } 429 | else 430 | { 431 | low = mid + 1; 432 | } 433 | } 434 | return INVALID; 435 | /*#else*/ 436 | /*#endif*/ 437 | } 438 | 439 | 440 | __host__ float 441 | compute_score(int size) 442 | { 443 | return 0.0f +size; 444 | } 445 | 446 | bool 447 | Match::score_node(float* _score, int* _qnum) 448 | { 449 | bool success = true; 450 | for(int i = 0; i < this->query->vertex_num; ++i) 451 | { 452 | //BETTER: consider degree and substructure in the score 453 | if(_qnum[i] == 0) // not found 454 | { 455 | /*d_score[i] = -1.0f;*/ 456 | success = false; 457 | break; 458 | } 459 | else 460 | { 461 | _score[i] = compute_score(_qnum[i]); 462 | /**d_success = true;*/ 463 | } 464 | } 465 | return success; 466 | } 467 | 468 | __global__ void 469 | filter_kernel(unsigned* d_signature_table, unsigned* d_status, unsigned dsize) 470 | { 471 | int i = blockIdx.x * blockDim.x + threadIdx.x; 472 | if(i >= dsize) 473 | { 474 | return; 475 | } 476 | unsigned flag = 1; 477 | 478 | char usig_binary[33]; // Buffer to hold the binary string representation 479 | char vsig_binary[33]; // Buffer to hold the binary string representation 480 | 481 | //TODO+DEBUG: the first vertex label, should be checked via a==b 482 | for(int j = 0; j < SIGNUM; ++j) 483 | { 484 | unsigned usig = c_signature[j]; 485 | unsigned vsig = d_signature_table[dsize*j+i]; 486 | 487 | // Convert usig to binary string 488 | for (int k = 0; k < 32; ++k) { 489 | usig_binary[31 - k] = (usig & (1 << k)) ? '1' : '0'; 490 | } 491 | usig_binary[32] = '\0'; 492 | 493 | // Convert vsig to binary string 494 | for (int k = 0; k < 32; ++k) { 495 | vsig_binary[31 - k] = (vsig & (1 << k)) ? '1' : '0'; 496 | } 497 | vsig_binary[32] = '\0'; 498 | 499 | // Print binary strings 500 | //printf("Thread %d: usig = %s, vsig = %s, flag = %u\n", i, usig_binary, vsig_binary, flag); 501 | 502 | //BETTER: reduce memory access here? 503 | if(flag) 504 | { 505 | //WARN: usig&vsig==usig is not right because the priority of == is higher than bitwise operation 506 | flag = ((usig & vsig) == usig)?1:0; 507 | //WARN: below is wrong because usig may have many 1s 508 | /*flag = ((usig & vsig) != 0)?1:0;*/ 509 | } 510 | } 511 | d_status[i] = flag; 512 | } 513 | 514 | __global__ void 515 | scatter_kernel(unsigned* d_status, unsigned* d_cand, unsigned dsize) 516 | { 517 | int i = blockIdx.x * blockDim.x + threadIdx.x; 518 | if(i >= dsize) 519 | { 520 | return; 521 | } 522 | int pos = d_status[i]; 523 | if(pos != d_status[i+1]) 524 | { 525 | d_cand[pos] = i; 526 | } 527 | } 528 | 529 | //NOTICE: the performance of this function varies sharply under the same configuration. 530 | //The reason may be the terrible implementation of exclusive_scan in Thrust library! 531 | bool 532 | Match::filter(float* _score, int* _qnum) 533 | { 534 | int qsize = this->query->vertex_num, dsize = this->data->vertex_num; 535 | this->candidates = new unsigned*[qsize]; 536 | int bytes = dsize * SIGBYTE; 537 | unsigned* d_signature_table = NULL; 538 | cudaMalloc(&d_signature_table, bytes); 539 | cudaMemcpy(d_signature_table, this->data->signature_table, bytes, cudaMemcpyHostToDevice); 540 | 541 | unsigned* d_status = NULL; 542 | cudaMalloc(&d_status, sizeof(unsigned)*(dsize+1)); 543 | int BLOCK_SIZE = 1024; 544 | int GRID_SIZE = (dsize+BLOCK_SIZE-1)/BLOCK_SIZE; 545 | 546 | /* 547 | // Print query signature table in binary 548 | cout << "Query signature table (binary):" << endl; 549 | for (int i = 0; i < qsize * SIGNUM; ++i) { 550 | cout << bitset<32>(this->query->signature_table[i]) << " "; 551 | if ((i + 1) % SIGNUM == 0) cout << endl; 552 | } 553 | 554 | // Print data signature table in binary 555 | cout << "Data signature table (binary):" << endl; 556 | for (int i = 0; i < dsize * SIGNUM; ++i) { 557 | cout << bitset<32>(this->data->signature_table[i]) << " "; 558 | if ((i + 1) % SIGNUM == 0) cout << endl; 559 | } 560 | */ 561 | 562 | for(int i = 0; i < qsize; ++i) 563 | { 564 | // Store the signature of the query graph in constant memory 565 | cudaMemcpyToSymbol(c_signature, this->query->signature_table + SIGNUM * i, SIGBYTE); 566 | 567 | /* 568 | // Print the signature being copied to constant memory 569 | cout << "Signature for vertex " << i << " being copied to constant memory (binary): "; 570 | for (int j = 0; j < SIGNUM; ++j) { 571 | cout << bitset<32>(this->query->signature_table[SIGNUM * i + j]) << " "; 572 | } 573 | cout << endl; 574 | */ 575 | 576 | // Launch filter kernel 577 | filter_kernel<<>>(d_signature_table, d_status, dsize); 578 | cudaDeviceSynchronize(); 579 | checkCudaErrors(cudaGetLastError()); 580 | 581 | 582 | /* 583 | // Print d_status before exclusive sum 584 | unsigned* h_status_before = new unsigned[dsize + 1]; 585 | cudaMemcpy(h_status_before, d_status, sizeof(unsigned) * (dsize), cudaMemcpyDeviceToHost); 586 | cout << "Vertex "<< i << ": "<< "d_status before exclusive sum: "; 587 | for (int j = 0; j < dsize; ++j) { 588 | cout << h_status_before[j] << " "; 589 | } 590 | cout << endl; 591 | delete[] h_status_before; 592 | */ 593 | 594 | // Perform exclusive sum 595 | exclusive_sum(d_status, dsize + 1); 596 | cudaDeviceSynchronize(); 597 | checkCudaErrors(cudaGetLastError()); 598 | 599 | /* 600 | // Print d_status after exclusive sum 601 | unsigned* h_status_after = new unsigned[dsize + 1]; 602 | cudaMemcpy(h_status_after, d_status, sizeof(unsigned) * (dsize + 1), cudaMemcpyDeviceToHost); 603 | cout << "d_status after exclusive sum: "; 604 | for (int j = 0; j < dsize + 1; ++j) { 605 | cout << h_status_after[j] << " "; 606 | } 607 | cout << endl; 608 | delete[] h_status_after; 609 | */ 610 | 611 | // Copy the number of candidates from the device to the host 612 | cudaMemcpy(&_qnum[i], d_status + dsize, sizeof(unsigned), cudaMemcpyDeviceToHost); 613 | 614 | // Debug print for the number of candidates 615 | //cout << "Number of candidates for vertex " << i << ": " << _qnum[i] << endl; 616 | 617 | // Break if no candidates are found 618 | if (_qnum[i] == 0) { 619 | break; 620 | } 621 | 622 | // Allocate memory for candidates on the device 623 | unsigned* d_cand = NULL; 624 | cudaMalloc(&d_cand, sizeof(unsigned) * _qnum[i]); 625 | checkCudaErrors(cudaGetLastError()); 626 | 627 | // Launch scatter kernel 628 | scatter_kernel<<>>(d_status, d_cand, dsize); 629 | cudaDeviceSynchronize(); 630 | checkCudaErrors(cudaGetLastError()); 631 | 632 | // Assign the candidate pointer 633 | this->candidates[i] = d_cand; 634 | } 635 | 636 | cudaFree(d_status); 637 | cudaFree(d_signature_table); 638 | 639 | //get the num of candidates and compute scores 640 | bool success = score_node(_score, _qnum); 641 | 642 | /* 643 | // Print scores 644 | cout << "Scores for each vertex in the query graph:" << endl; 645 | for (int i = 0; i < qsize; ++i) { 646 | cout << "Vertex " << i << " score: " << _score[i] << endl; 647 | } 648 | */ 649 | // print qnum 650 | 651 | 652 | if(!success) 653 | { 654 | #ifdef DEBUG 655 | cout<<"query already fail after filter"<= candidate_num) 678 | { 679 | return; 680 | } 681 | //int atomicOr(int* address, int val); 682 | unsigned ele = d_candidate_tmp[i]; 683 | unsigned num = ele >> 5; 684 | ele &= 0x1f; 685 | //NOTICE: we assume that the architecture is little-endian 686 | //in big-endian it should be 1<<(32-ele) 687 | //ANALYSIS: it may be ok no matter the machine is little-endian or not, if we use the same type of read/write data 688 | ele = 1 << ele; 689 | atomicOr(d_candidate+num, ele); 690 | } 691 | 692 | void 693 | Match::update_score(float* _score, int qsize, int _idx) 694 | { 695 | //BETTER: acquire it from edge label frequence: p = (P2num)/T, divide in and out edge? 696 | //score the node or the edge?? how about m*n*p, cost of the current step and the join result's size(cost of the next step) 697 | float p = 0.9f; 698 | /*float p = 0.1f;*/ 699 | /*float p = 0.5f;*/ 700 | int insize = this->query->vertices[_idx].in.size(), outsize = this->query->vertices[_idx].out.size(); 701 | int i, j; 702 | for(i = 0; i < insize; ++i) //in neighbor 703 | { 704 | j = this->query->vertices[_idx].in[i].vid; 705 | _score[j] *= p; 706 | } 707 | for(i = 0; i < outsize; ++i) //out neighbor 708 | { 709 | j = this->query->vertices[_idx].out[i].vid; 710 | _score[j] *= p; 711 | } 712 | } 713 | 714 | void 715 | Match::acquire_linking(int*& link_pos, int*& link_edge, int& link_num, int idx) 716 | { 717 | vector tmp_vertex, tmp_edge; 718 | int i, qsize = this->query->vertex_num; 719 | int insize = this->query->vertices[idx].in.size(), outsize = this->query->vertices[idx].out.size(); 720 | 721 | //cout<<"INSIZE"<current_pos<query->vertices[idx].in[i].elb; 733 | int vid = this->query->vertices[idx].in[i].vid; 734 | edge2value[vid] = label; 735 | } 736 | 737 | /* 738 | // Print the contents of edge2value after processing in-neighbors 739 | std::cout << "edge2value after processing in-neighbors: "; 740 | for (int i = 0; i < qsize; ++i) { 741 | std::cout << edge2value[i] << " "; 742 | } 743 | std::cout << std::endl; 744 | */ 745 | 746 | for(i = 0; i < this->current_pos; ++i) 747 | { 748 | int id = this->pos2id[i]; 749 | //cout<<"i: "<< i<<" ID: "<< id<query->vertices[idx].out[i].elb; 763 | int vid = this->query->vertices[idx].out[i].vid; 764 | edge2value[vid] = label; 765 | } 766 | 767 | /* 768 | // Print the contents of edge2value after processing in-neighbors 769 | std::cout << "edge2value after processing out-neighbors: "; 770 | for (int i = 0; i < qsize; ++i) { 771 | std::cout << edge2value[i] << " "; 772 | } 773 | std::cout << std::endl; 774 | */ 775 | 776 | for(i = 0; i < this->current_pos; ++i) 777 | { 778 | int id = this->pos2id[i]; 779 | int label = edge2value[id]; 780 | if(label != -1) 781 | { 782 | tmp_vertex.push_back(i); 783 | tmp_edge.push_back(0 - label); 784 | } 785 | } 786 | 787 | delete[] edge2value; 788 | link_num = tmp_vertex.size(); 789 | link_pos = new int[link_num]; 790 | link_edge = new int[link_num]; 791 | for(i = 0; i ());*/ 800 | // 801 | //BETTER: choose between merge-join and bianry-search, or using multiple threads to do intersection 802 | //or do inetrsection per-element, use compact operation finally to remove invalid elements 803 | __device__ void 804 | intersect(unsigned*& cand, unsigned& cand_num, unsigned* list, unsigned list_num) 805 | { 806 | int i, cnt = 0; 807 | for(i = 0; i < cand_num; ++i) 808 | { 809 | unsigned key = cand[i]; 810 | unsigned found = binary_search(key, list, list_num); 811 | if(found != INVALID) 812 | { 813 | cand[cnt++] = key; 814 | } 815 | } 816 | cand_num = cnt; 817 | } 818 | 819 | __device__ void 820 | subtract(unsigned*& cand, unsigned& cand_num, unsigned* record, unsigned result_col_num) 821 | { 822 | //DEBUG: this will cause error when using dynamic allocation, gowalla with q0 823 | int i, j, cnt = 0; 824 | for(j = 0; j < cand_num; ++j) 825 | { 826 | unsigned key = cand[j]; 827 | for(i = 0; i < result_col_num; ++i) 828 | { 829 | if(record[i] == key) 830 | { 831 | break; 832 | } 833 | } 834 | if(i == result_col_num) 835 | { 836 | cand[cnt++] = key; 837 | } 838 | } 839 | cand_num = cnt; 840 | } 841 | 842 | //WARN: in case of 2-node loops like: A->B and B->A (this can be called generalized parallel edge) 843 | //BETTER: implement warp-binary-search method 844 | __global__ void 845 | first_kernel(unsigned* d_result_tmp_pos) 846 | { 847 | //NOTICE: if a shared structure has not be used really, the compiler(even without optimization options) will not assign space for it on SM 848 | //the three pools request 12KB for each block 849 | __shared__ unsigned s_pool1[1024]; 850 | __shared__ unsigned s_pool2[1024]; 851 | /*__shared__ unsigned s_pool3[1024];*/ 852 | //WARN: for unsigned variable, never use >0 and -- to judge!(overflow) 853 | //NOTICE: for signed type, right shift will add 1s in the former! 854 | unsigned i = blockIdx.x * blockDim.x + threadIdx.x; 855 | unsigned idx = i & 0x1f; 856 | i = i >> 5; //group ID 857 | /*printf("compare %d and %d\n", i, result_row_num);*/ 858 | if(i >= c_result_row_num) 859 | { 860 | return; 861 | } 862 | 863 | /*printf("thread id %d\n", i);*/ 864 | unsigned* record = c_result+i*c_result_col_num; 865 | /*unsigned test = __ldg(&d_result_tmp_num[0]);*/ 866 | //NOTICE: we use this function to verify that on Titan XP the pointer consumes 8 bytes 867 | /*if(i == 0)*/ 868 | /*{*/ 869 | /*printf("check pointer %u\n", sizeof(record));*/ 870 | /*}*/ 871 | 872 | unsigned id = record[c_link_pos]; 873 | //NOTICE: here we should use the group number within the block instead of grid 874 | unsigned bgroup = threadIdx.x & 0xffffffe0; //equal to (x/32)*32 875 | 876 | //BETTER: control the whole block, share the inputs 877 | //find the same ID within the block 878 | 879 | unsigned bucket = MurmurHash2(&id, 4, HASHSEED) % c_key_num; 880 | s_pool1[bgroup+idx] = c_row_offset[32*bucket+idx]; 881 | if(idx == 0) 882 | { 883 | s_pool2[bgroup] = INVALID; 884 | s_pool2[bgroup+1] = INVALID; 885 | } 886 | __syncthreads(); 887 | if(idx < 30 && (idx&1)==0) 888 | { 889 | if(s_pool1[bgroup+idx] == id) 890 | { 891 | s_pool2[bgroup] = s_pool1[bgroup+idx+1]; 892 | s_pool2[bgroup+1] = s_pool1[bgroup+idx+3]; 893 | } 894 | } 895 | //__syncthreads(); 896 | /*if(pool2[bgroup*32] == INVALID && pool1[32*bgroup+30] != INVALID)*/ 897 | /*{*/ 898 | /*//TODO:multiple groups*/ 899 | /*}*/ 900 | //NOTICE: not use all threads to write, though the conflicts do not matter 901 | //(experiments show the gst number is the same, and the performance is similar) 902 | if(idx == 0) 903 | { 904 | d_result_tmp_pos[i] = s_pool2[bgroup+1] - s_pool2[bgroup]; 905 | } 906 | } 907 | 908 | /*__device__ unsigned d_maxTaskLen;*/ 909 | /*__device__ unsigned d_minTaskLen;*/ 910 | //NOTICE: though the registers reported by compiler are reduced when using constant memory, 911 | //the real registers used when running may not be reduced so much. 912 | //when using constants, they are kept for a whole block instead of occupying registers for each thread 913 | //NOTICE: we can hack the mechanism by comparing with first_kernel, compiler and running(nvprof --print-gpu-trace) 914 | __global__ void 915 | second_kernel(unsigned* d_result_tmp, unsigned* d_result_tmp_num) 916 | { 917 | __shared__ unsigned s_pool1[1024]; 918 | __shared__ unsigned s_pool2[1024]; 919 | __shared__ unsigned s_pool3[1024]; 920 | __shared__ unsigned s_pool4[32]; 921 | unsigned i = blockIdx.x * blockDim.x + threadIdx.x; 922 | unsigned idx = i & 0x1f; 923 | i = i >> 5; // group ID (warp index) within the whole kernel 924 | 925 | if (i >= c_result_row_num) 926 | { 927 | return; 928 | } 929 | 930 | unsigned bgroup = threadIdx.x & 0xffffffe0; // equal to (x/32)*32 931 | unsigned gidx = threadIdx.x >> 5; // warp index within this block 932 | 933 | // Cache the record of this row in shared mem 934 | if (idx < c_result_col_num) 935 | { 936 | s_pool2[bgroup + idx] = c_result[i * c_result_col_num + idx]; 937 | } 938 | 939 | __syncthreads(); // Ensure all threads have cached their values 940 | 941 | unsigned bucket = MurmurHash2(&s_pool2[bgroup + c_link_pos], 4, HASHSEED) % c_key_num; 942 | s_pool1[bgroup + idx] = c_row_offset[32 * bucket + idx]; 943 | 944 | if (idx == 0) 945 | { 946 | s_pool3[bgroup] = INVALID; 947 | } 948 | 949 | __syncthreads(); // Ensure shared memory is updated before proceeding 950 | 951 | if (idx < 30 && (idx & 1) == 0) 952 | { 953 | if (s_pool1[bgroup + idx] == s_pool2[bgroup + c_link_pos]) 954 | { 955 | s_pool3[bgroup] = s_pool1[bgroup + idx + 1]; 956 | s_pool3[bgroup + 1] = s_pool1[bgroup + idx + 3]; 957 | // printf("Thread %d found a match: s_pool1[%d] == s_pool2[%d]\n", i, bgroup + idx, bgroup + c_link_pos); 958 | } 959 | } 960 | 961 | __syncthreads(); // Ensure updates to s_pool3 are complete before proceeding 962 | 963 | if (s_pool3[bgroup] == INVALID) // not found 964 | { 965 | if (idx == 0) 966 | { 967 | // printf("Thread %d did not find a match, setting d_result_tmp_num[%d] to 0\n", i, i); 968 | d_result_tmp_num[i] = 0; 969 | } 970 | return; 971 | } 972 | 973 | // BETTER: we may set d_result_tmp_num += i here and use i for other usage later 974 | d_result_tmp += c_result_tmp_pos[i]; 975 | unsigned size = s_pool3[bgroup + 1] - s_pool3[bgroup]; 976 | unsigned* list = c_column_index + s_pool3[bgroup]; 977 | unsigned pos = 0; 978 | unsigned loop = size >> 5; 979 | size = size & 0x1f; 980 | unsigned pred, presum; 981 | unsigned cand_num = 0; 982 | s_pool4[gidx] = 0; 983 | 984 | for (int j = 0; j < loop; ++j, pos += 32) 985 | { 986 | s_pool1[bgroup + idx] = list[pos + idx]; 987 | 988 | __syncthreads(); // Ensure s_pool1 is fully populated before proceeding 989 | 990 | unsigned k; 991 | for (k = 0; k < c_result_col_num; ++k) 992 | { 993 | if (s_pool2[bgroup + k] == s_pool1[bgroup + idx]) 994 | { 995 | break; 996 | } 997 | } 998 | pred = 0; 999 | if (k == c_result_col_num) 1000 | { 1001 | unsigned num = s_pool1[bgroup + idx] >> 5; 1002 | unsigned res = s_pool1[bgroup + idx] & 0x1f; 1003 | res = 1 << res; 1004 | if ((c_candidate[num] & res) == res) 1005 | { 1006 | pred = 1; 1007 | } 1008 | } 1009 | presum = pred; 1010 | 1011 | // Prefix sum in a warp to find positions 1012 | for (unsigned stride = 1; stride < 32; stride <<= 1) 1013 | { 1014 | unsigned tmp = __shfl_up_sync(0xFFFFFFFF, presum, stride); 1015 | if (idx >= stride) 1016 | { 1017 | presum += tmp; 1018 | } 1019 | } 1020 | unsigned total = __shfl_sync(0xFFFFFFFF, presum, 31); // Broadcast to all threads in the warp 1021 | presum = __shfl_up_sync(0xFFFFFFFF, presum, 1); 1022 | if (idx == 0) 1023 | { 1024 | presum = 0; 1025 | } 1026 | if (pred == 1) 1027 | { 1028 | if (s_pool4[gidx] + presum < 32) 1029 | { 1030 | s_pool3[bgroup + s_pool4[gidx] + presum] = s_pool1[bgroup + idx]; 1031 | } 1032 | } 1033 | if (s_pool4[gidx] + total >= 32) 1034 | { 1035 | d_result_tmp[cand_num + idx] = s_pool3[bgroup + idx]; 1036 | cand_num += 32; 1037 | if (pred == 1) 1038 | { 1039 | unsigned pos = s_pool4[gidx] + presum; 1040 | if (pos >= 32) 1041 | { 1042 | s_pool3[bgroup + pos - 32] = s_pool1[bgroup + idx]; 1043 | } 1044 | } 1045 | s_pool4[gidx] = s_pool4[gidx] + total - 32; 1046 | } 1047 | else 1048 | { 1049 | s_pool4[gidx] += total; 1050 | } 1051 | } 1052 | presum = pred = 0; 1053 | if (idx < size) 1054 | { 1055 | s_pool1[bgroup + idx] = list[pos + idx]; 1056 | unsigned k; 1057 | for (k = 0; k < c_result_col_num; ++k) 1058 | { 1059 | if (s_pool2[bgroup + k] == s_pool1[bgroup + idx]) 1060 | { 1061 | break; 1062 | } 1063 | } 1064 | if (k == c_result_col_num) 1065 | { 1066 | unsigned num = s_pool1[bgroup + idx] >> 5; 1067 | unsigned res = s_pool1[bgroup + idx] & 0x1f; 1068 | res = 1 << res; 1069 | if ((c_candidate[num] & res) == res) 1070 | { 1071 | pred = 1; 1072 | } 1073 | } 1074 | presum = pred; 1075 | } 1076 | 1077 | __syncthreads(); // Synchronize before final prefix sum and result writing 1078 | 1079 | for (unsigned stride = 1; stride < 32; stride <<= 1) 1080 | { 1081 | unsigned tmp = __shfl_up_sync(0xFFFFFFFF, presum, stride); 1082 | if (idx >= stride) 1083 | { 1084 | presum += tmp; 1085 | } 1086 | } 1087 | unsigned total = __shfl_sync(0xFFFFFFFF, presum, 31); 1088 | presum = __shfl_up_sync(0xFFFFFFFF, presum, 1); 1089 | if (idx == 0) 1090 | { 1091 | presum = 0; 1092 | } 1093 | if (pred == 1) 1094 | { 1095 | if (s_pool4[gidx] + presum < 32) 1096 | { 1097 | s_pool3[bgroup + s_pool4[gidx] + presum] = s_pool1[bgroup + idx]; 1098 | } 1099 | } 1100 | unsigned newsize = s_pool4[gidx] + total; 1101 | if (newsize >= 32) 1102 | { 1103 | d_result_tmp[cand_num + idx] = s_pool3[bgroup + idx]; 1104 | cand_num += 32; 1105 | if (pred == 1) 1106 | { 1107 | unsigned pos = s_pool4[gidx] + presum; 1108 | if (pos >= 32) 1109 | { 1110 | d_result_tmp[cand_num + pos - 32] = s_pool1[bgroup + idx]; 1111 | } 1112 | } 1113 | cand_num += (newsize - 32); 1114 | } 1115 | else 1116 | { 1117 | if (idx < newsize) 1118 | { 1119 | d_result_tmp[cand_num + idx] = s_pool3[bgroup + idx]; 1120 | } 1121 | cand_num += newsize; 1122 | } 1123 | 1124 | if (idx == 0) 1125 | { 1126 | d_result_tmp_num[i] = cand_num; 1127 | } 1128 | } 1129 | 1130 | 1131 | __global__ void 1132 | join_kernel(unsigned* d_result_tmp, unsigned* d_result_tmp_num) 1133 | { 1134 | __shared__ unsigned s_pool1[1024]; 1135 | __shared__ unsigned s_pool2[1024]; 1136 | __shared__ unsigned s_pool3[1024]; 1137 | __shared__ unsigned s_pool4[32]; 1138 | unsigned i = blockIdx.x * blockDim.x + threadIdx.x; 1139 | unsigned idx = i & 0x1f; 1140 | i = i >> 5; // group ID 1141 | 1142 | if (i >= c_result_row_num) 1143 | { 1144 | return; 1145 | } 1146 | 1147 | unsigned res_num = d_result_tmp_num[i]; 1148 | if (res_num == 0) // early termination 1149 | { 1150 | return; 1151 | } 1152 | 1153 | unsigned bgroup = threadIdx.x & 0xffffffe0; // equal to (x/32)*32 1154 | unsigned gidx = threadIdx.x >> 5; 1155 | 1156 | if (idx == 0) 1157 | { 1158 | s_pool2[bgroup + c_link_pos] = c_result[i * c_result_col_num + c_link_pos]; 1159 | } 1160 | 1161 | __syncthreads(); // Ensure shared memory is updated before proceeding 1162 | 1163 | unsigned bucket = MurmurHash2(&s_pool2[bgroup + c_link_pos], 4, HASHSEED) % c_key_num; 1164 | s_pool1[bgroup + idx] = c_row_offset[32 * bucket + idx]; 1165 | 1166 | if (idx == 0) 1167 | { 1168 | s_pool3[bgroup] = INVALID; 1169 | } 1170 | 1171 | __syncthreads(); // Synchronize before checking conditions 1172 | 1173 | if (idx < 30 && (idx & 1) == 0) 1174 | { 1175 | if (s_pool1[bgroup + idx] == s_pool2[bgroup + c_link_pos]) 1176 | { 1177 | s_pool3[bgroup] = s_pool1[bgroup + idx + 1]; 1178 | s_pool3[bgroup + 1] = s_pool1[bgroup + idx + 3]; 1179 | } 1180 | } 1181 | 1182 | __syncthreads(); // Ensure updates to s_pool3 are complete before proceeding 1183 | 1184 | if (s_pool3[bgroup] == INVALID) // not found 1185 | { 1186 | if (idx == 0) 1187 | { 1188 | d_result_tmp_num[i] = 0; 1189 | } 1190 | return; 1191 | } 1192 | 1193 | unsigned list_num = s_pool3[bgroup + 1] - s_pool3[bgroup]; 1194 | unsigned* list = c_column_index + s_pool3[bgroup]; 1195 | d_result_tmp += c_result_tmp_pos[i]; 1196 | 1197 | unsigned pos1 = 0, pos2 = 0; 1198 | unsigned pred, presum; 1199 | unsigned cand_num = 0; 1200 | int choice = 0; 1201 | s_pool4[gidx] = 0; 1202 | 1203 | while (pos1 < res_num && pos2 < list_num) 1204 | { 1205 | if (choice <= 0) 1206 | { 1207 | s_pool1[bgroup + idx] = INVALID; 1208 | if (pos1 + idx < res_num) 1209 | { 1210 | s_pool1[bgroup + idx] = d_result_tmp[pos1 + idx]; 1211 | } 1212 | } 1213 | 1214 | if (choice >= 0) 1215 | { 1216 | s_pool2[bgroup + idx] = INVALID; 1217 | if (pos2 + idx < list_num) 1218 | { 1219 | s_pool2[bgroup + idx] = list[pos2 + idx]; 1220 | } 1221 | } 1222 | 1223 | __syncthreads(); // Ensure both pools are updated before proceeding 1224 | 1225 | pred = 0; // some threads may fail in the judgement below 1226 | unsigned valid1 = (pos1 + 32 < res_num) ? 32 : (res_num - pos1); 1227 | unsigned valid2 = (pos2 + 32 < list_num) ? 32 : (list_num - pos2); 1228 | if (pos1 + idx < res_num) 1229 | { 1230 | pred = binary_search(s_pool1[bgroup + idx], s_pool2 + bgroup, valid2); 1231 | if (pred != INVALID) 1232 | { 1233 | pred = 1; 1234 | } 1235 | else 1236 | { 1237 | pred = 0; 1238 | } 1239 | } 1240 | 1241 | presum = pred; 1242 | 1243 | // Prefix sum in a warp to find positions 1244 | for (unsigned stride = 1; stride < 32; stride <<= 1) 1245 | { 1246 | unsigned tmp = __shfl_up_sync(0xFFFFFFFF, presum, stride); 1247 | if (idx >= stride) 1248 | { 1249 | presum += tmp; 1250 | } 1251 | } 1252 | 1253 | unsigned total = __shfl_sync(0xFFFFFFFF, presum, 31); // Broadcast to all threads in the warp 1254 | presum = __shfl_up_sync(0xFFFFFFFF, presum, 1); 1255 | if (idx == 0) 1256 | { 1257 | presum = 0; 1258 | } 1259 | 1260 | if (pred == 1) 1261 | { 1262 | if (s_pool4[gidx] + presum < 32) 1263 | { 1264 | s_pool3[bgroup + s_pool4[gidx] + presum] = s_pool1[bgroup + idx]; 1265 | } 1266 | } 1267 | 1268 | if (s_pool4[gidx] + total >= 32) 1269 | { 1270 | d_result_tmp[cand_num + idx] = s_pool3[bgroup + idx]; 1271 | cand_num += 32; 1272 | if (pred == 1) 1273 | { 1274 | unsigned pos = s_pool4[gidx] + presum; 1275 | if (pos >= 32) 1276 | { 1277 | s_pool3[bgroup + pos - 32] = s_pool1[bgroup + idx]; 1278 | } 1279 | } 1280 | s_pool4[gidx] = s_pool4[gidx] + total - 32; 1281 | } 1282 | else 1283 | { 1284 | s_pool4[gidx] += total; 1285 | } 1286 | 1287 | // Set the next movement 1288 | choice = s_pool1[bgroup + valid1 - 1] - s_pool2[bgroup + valid2 - 1]; 1289 | if (choice <= 0) 1290 | { 1291 | pos1 += 32; 1292 | } 1293 | if (choice >= 0) 1294 | { 1295 | pos2 += 32; 1296 | } 1297 | } 1298 | 1299 | if (idx < s_pool4[gidx]) 1300 | { 1301 | d_result_tmp[cand_num + idx] = s_pool3[bgroup + idx]; 1302 | } 1303 | 1304 | cand_num += s_pool4[gidx]; 1305 | 1306 | if (idx == 0) 1307 | { 1308 | d_result_tmp_num[i] = cand_num; 1309 | } 1310 | } 1311 | 1312 | //NOTICE: the load balance strategies of Merrill is wonderful, but it may fail when comparing with 1313 | //natural-balanced strategies because they do not need the work of preparation(which must be done to ensure balance) 1314 | __global__ void 1315 | link_kernel(unsigned* d_result_tmp, unsigned* d_result_tmp_pos, unsigned* d_result_tmp_num, unsigned* d_result_new) 1316 | { 1317 | //BETTER:consider bank conflicts here, should we use column-oriented table for global memory and shared memory? 1318 | //In order to keep in good occupancy(>=50%), the shared mem usage should <= 24KB for 1024-threads block 1319 | __shared__ unsigned cache[1024]; 1320 | /*__shared__ unsigned s_pool[1024*5]; //the work poll*/ 1321 | //NOTICE: though a block can be synchronized, we should use volatile to ensure data is not cached in private registers 1322 | //If shared mem(or global mem) is used by a warp, then volatile is not needed. 1323 | //http://www.it1352.com/539600.html 1324 | /*volatile __shared__ unsigned swpos[1024];*/ 1325 | __shared__ unsigned swpos[32]; 1326 | 1327 | unsigned i = blockIdx.x * blockDim.x + threadIdx.x; 1328 | i >>= 5; 1329 | //NOTICE: we should not use this if we want to control the whole block 1330 | //(another choice is to abandon the border block) 1331 | /*if(i >= c_result_row_num)*/ 1332 | /*{*/ 1333 | /*return; */ 1334 | /*}*/ 1335 | unsigned bgroup = threadIdx.x & 0xffffffe0; //equal to (x/32)*32 1336 | unsigned idx = threadIdx.x & 0x1f; //thread index within the warp 1337 | unsigned gidx = threadIdx.x >> 5; //warp ID within the block 1338 | 1339 | unsigned tmp_begin = 0, start = 0, size = 0; 1340 | if(i < c_result_row_num) 1341 | { 1342 | tmp_begin = d_result_tmp_pos[i]; 1343 | start = d_result_tmp_num[i]; 1344 | //NOTICE: the size is ok to be 0 here 1345 | size = d_result_tmp_num[i+1] - start; 1346 | start *= (c_result_col_num+1); 1347 | } 1348 | 1349 | //Usage of Shared Memory: cache records only when size > 0 1350 | if(idx == 0) 1351 | { 1352 | if(size > 0) 1353 | { 1354 | //NOTICE: we use a single thread to read a batch a time 1355 | memcpy(cache+gidx*32, c_result+i*c_result_col_num, sizeof(unsigned)*c_result_col_num); 1356 | } 1357 | } 1358 | unsigned curr = 0; 1359 | unsigned* record = cache + gidx * 32; 1360 | 1361 | //Usage of Load Balance 1362 | __syncthreads(); 1363 | //use a block to deal with tasks >=1024 1364 | while(true) 1365 | { 1366 | if(threadIdx.x == 0) 1367 | { 1368 | swpos[0] = INVALID; 1369 | } 1370 | //NOTICE: the sync function is needed, but had better not use it too much 1371 | //It is costly, which may stop the running warps and replace them with other warps 1372 | __syncthreads(); 1373 | if(size >= curr+1024) 1374 | { 1375 | swpos[0] = gidx; 1376 | } 1377 | __syncthreads(); 1378 | if(swpos[0] == INVALID) 1379 | { 1380 | break; 1381 | } 1382 | //WARN:output info within kernel function will degrade perfomance heavily 1383 | //printf("FOUND: use a block!\n"); 1384 | unsigned* ptr = cache + 32 * swpos[0]; 1385 | if(swpos[0] == gidx) 1386 | { 1387 | swpos[1] = tmp_begin; 1388 | swpos[2] = start; 1389 | swpos[3] = curr; 1390 | swpos[4] = size; 1391 | } 1392 | __syncthreads(); 1393 | //NOTICE: here we use a block to handle the task as much as possible 1394 | //(this choice may save the work of preparation) 1395 | //Another choice is only do 1024 and set size-=1024, later vote again 1396 | while(swpos[3]+1023 0 1469 | //long begin = Util::get_cur_time(); 1470 | for(int i = 0; i < link_num; ++i) 1471 | { 1472 | cudaMemcpyToSymbol(c_link_pos, link_pos+i, sizeof(unsigned)); 1473 | int label = link_edge[i]; 1474 | unsigned *d_row_offset = NULL, *d_column_index = NULL; 1475 | PCSR* tcsr; 1476 | if(label < 0) 1477 | { 1478 | label = -label; 1479 | tcsr = &(this->data->csrs_in[label]); 1480 | } 1481 | else 1482 | { 1483 | tcsr = &(this->data->csrs_out[label]); 1484 | } 1485 | copyHtoD(d_row_offset, tcsr->row_offset, sizeof(unsigned)*(tcsr->key_num*32)); 1486 | copyHtoD(d_column_index, tcsr->column_index, sizeof(unsigned)*(tcsr->getEdgeNum())); 1487 | cudaMemcpyToSymbol(c_row_offset, &d_row_offset, sizeof(unsigned*)); 1488 | cudaMemcpyToSymbol(c_column_index, &d_column_index, sizeof(unsigned*)); 1489 | cudaMemcpyToSymbol(c_key_num, &(tcsr->key_num), sizeof(unsigned)); 1490 | cudaMemcpyToSymbol(c_link_edge, &label, sizeof(unsigned)); 1491 | cudaMemcpyToSymbol(c_result_tmp_pos, &d_result_tmp_pos, sizeof(unsigned*)); 1492 | //cout<<"the "<>>(d_result_tmp_pos); 1498 | cudaDeviceSynchronize(); 1499 | checkCudaErrors(cudaGetLastError()); 1500 | //cout<<"first kernel finished"< dev_ptr(d_result_tmp_pos);*/ 1517 | /*thrust::exclusive_scan(dev_ptr, dev_ptr+result_row_num+1, dev_ptr);*/ 1518 | exclusive_sum(d_result_tmp_pos, result_row_num+1); 1519 | cudaDeviceSynchronize(); 1520 | /* 1521 | // Print the contents of d_result_tmp_pos 1522 | unsigned* b_result_tmp_pos = new unsigned[result_row_num + 1]; 1523 | cudaMemcpy(b_result_tmp_pos, d_result_tmp_pos, sizeof(unsigned) * (result_row_num + 1), cudaMemcpyDeviceToHost); 1524 | std::cout << "Contents of b_result_tmp_pos:" << std::endl; 1525 | for (size_t j = 0; j < result_row_num + 1; ++j) { 1526 | std::cout << b_result_tmp_pos[j] << " "; 1527 | } 1528 | std::cout << std::endl; 1529 | delete[] b_result_tmp_pos; 1530 | */ 1531 | cudaMemcpy(&sum, &d_result_tmp_pos[result_row_num], sizeof(unsigned), cudaMemcpyDeviceToHost); 1532 | //cout<<"To malloc on GPU: "<>>(d_result_tmp, d_result_tmp_num); 1538 | cudaDeviceSynchronize(); 1539 | 1540 | /* 1541 | unsigned* h_result_tmp = new unsigned[sum]; 1542 | cudaMemcpy(h_result_tmp, d_result_tmp, sizeof(unsigned)*sum, cudaMemcpyDeviceToHost); 1543 | for(int p = 0; p < sum; ++p) 1544 | { 1545 | cout<>>(d_result_tmp, d_result_tmp_num); 1565 | cudaDeviceSynchronize(); 1566 | } 1567 | //cudaDeviceSynchronize(); 1568 | checkCudaErrors(cudaGetLastError()); 1569 | //cout<<"iteration kernel finished"< dev_ptr(d_result_tmp_num);*/ 1580 | /*//link the temp result into a new table*/ 1581 | /*thrust::exclusive_scan(dev_ptr, dev_ptr+result_row_num+1, dev_ptr);*/ 1582 | exclusive_sum(d_result_tmp_num, result_row_num+1); 1583 | cudaDeviceSynchronize(); 1584 | #ifdef DEBUG 1585 | checkCudaErrors(cudaGetLastError()); 1586 | #endif 1587 | /*sum = thrust::reduce(dev_ptr, dev_ptr+result_row_num);*/ 1588 | cudaMemcpy(&sum, d_result_tmp_num+result_row_num, sizeof(unsigned), cudaMemcpyDeviceToHost); 1589 | //BETTER: judge if success here 1590 | /*cout<<"new table num: "< 0) 1603 | { 1604 | cudaMalloc(&d_result_new, sizeof(unsigned)*sum*(result_col_num+1)); 1605 | #ifdef DEBUG 1606 | checkCudaErrors(cudaGetLastError()); 1607 | #endif 1608 | /*BLOCK_SIZE = 512;*/ 1609 | /*GRID_SIZE = (result_row_num+BLOCK_SIZE-1)/BLOCK_SIZE;*/ 1610 | //BETTER?: combine into a large array(value is the record id) and link per element 1611 | //long begin = Util::get_cur_time(); 1612 | link_kernel<<>>(d_result_tmp, d_result_tmp_pos, d_result_tmp_num, d_result_new); 1613 | //checkCudaErrors(cudaGetLastError()); 1614 | cudaDeviceSynchronize(); 1615 | //long end = Util::get_cur_time(); 1616 | #ifdef DEBUG 1617 | cerr<<"link_kernel used: "<<(end-begin)<<"ms"<= candidate_num) 1654 | { 1655 | return; 1656 | } 1657 | unsigned id = d_array[i]; 1658 | 1659 | unsigned pos = MurmurHash2(&id, 4, HASHSEED) % SUMMARY_BITS; 1660 | unsigned num = pos >> 5; 1661 | pos &= 0x1f; 1662 | pos = 1 << pos; 1663 | atomicOr(d_summary+num, pos); 1664 | 1665 | pos = MurmurHash2(&id, 4, HASHSEED2) % SUMMARY_BITS; 1666 | num = pos >> 5; 1667 | pos &= 0x1f; 1668 | pos = 1 << pos; 1669 | atomicOr(d_summary+num, pos); 1670 | } 1671 | 1672 | void 1673 | //Match::match(IO& io, unsigned*& final_result, unsigned& result_row_num, unsigned& result_col_num, int*& id_map) 1674 | Match::match(unsigned*& final_result, unsigned& result_row_num, unsigned& result_col_num, int*& id_map) 1675 | { 1676 | //NOTICE: device variables can not be assigned and output directly on Host 1677 | /*unsigned maxTaskLen = 0, minTaskLen = 1000000;*/ 1678 | /*cudaMemcpyToSymbol(d_maxTaskLen, &maxTaskLen, sizeof(unsigned));*/ 1679 | /*cudaMemcpyToSymbol(d_minTaskLen, &minTaskLen, sizeof(unsigned));*/ 1680 | //CUDA device variable (can only de declared on Host, not in device/global functions), can be used in all kernel functions like constant variables 1681 | /*https://blog.csdn.net/rong_toa/article/details/78664902*/ 1682 | /*cudaGetSymbolAddress((void**)&dp,devData);*/ 1683 | /*cudaMemcpy(dp,&value,sizeof(float),cudaMemcpyHostToDevice);*/ 1684 | 1685 | //long t0 = Util::get_cur_time(); 1686 | copyGraphToGPU(); 1687 | //long t1 = Util::get_cur_time(); 1688 | //cerr<<"copy graph used: "<<(t1-t0)<<"ms"<query->vertex_num; 1694 | assert(qsize <= MAX_QUERY_SIZE); 1695 | float* score = new float[qsize]; 1696 | /*float* d_score = NULL;*/ 1697 | /*cudaMalloc(&d_score, sizeof(float)*qsize);*/ 1698 | /*checkCudaErrors(cudaGetLastError());*/ 1699 | /*cout<<"assign score"<data->vertex_num, sizeof(unsigned)*8); 1739 | //cout<<"data vertex num: "<data->vertex_num<<" bitset size: "<id2pos = new int[qsize]; 1764 | this->pos2id = new int[qsize]; 1765 | this->current_pos = 0; 1766 | memset(id2pos, -1, sizeof(int)*qsize); 1767 | memset(pos2id, -1, sizeof(int)*qsize); 1768 | //select the minium score and fill the table 1769 | 1770 | //cout<<"Current pos 1: "<current_pos<get_minimum_idx(score, qsize); 1773 | //cout<<"start node found: "<query->vertex_value[idx]<<" candidate size: "<current_pos<candidates[idx]; 1782 | //cout<<"intermediate table built"<current_pos<get_minimum_idx(score, qsize); 1817 | //long t6 = Util::get_cur_time(); 1818 | //cerr<<"get minimum idx used: "<<(t6-t5)<<"ms"<query->vertex_value[idx2]<<" candidate size: "<current_pos<current_pos; ++i) { 1829 | std::cout << this->pos2id[i] << " "; 1830 | } 1831 | std::cout << std::endl; 1832 | 1833 | std::cout << "id2pos before acquiring linkings: "; 1834 | for (int i = 0; i < this->query->vertex_num; ++i) { 1835 | std::cout << this->id2pos[i] << " "; 1836 | } 1837 | std::cout << std::endl; 1838 | */ 1839 | 1840 | 1841 | //acquire the edge linkings on CPU, and pass to GPU 1842 | int *link_pos, *link_edge, link_num; 1843 | this->acquire_linking(link_pos, link_edge, link_num, idx2); 1844 | 1845 | /* 1846 | // Display link_pos, link_edge, and link_num 1847 | std::cout << "link_num: " << link_num << std::endl; 1848 | 1849 | std::cout << "link_pos: "; 1850 | for (int i = 0; i < link_num; ++i) { 1851 | std::cout << link_pos[i] << " "; 1852 | } 1853 | std::cout << std::endl; 1854 | 1855 | std::cout << "link_edge: "; 1856 | for (int i = 0; i < link_num; ++i) { 1857 | std::cout << link_edge[i] << " "; 1858 | } 1859 | std::cout << std::endl; 1860 | */ 1861 | 1862 | //long t7 = Util::get_cur_time(); 1863 | //cerr<<"acquire linking used: "<<(t7-t6)<<"ms"<candidates[idx2], sizeof(unsigned) * candidate_num, cudaMemcpyDeviceToHost); 1878 | for (int i = 0; i < candidate_num; ++i) { 1879 | std::cout << h_candidates[i] << " "; 1880 | } 1881 | std::cout << std::endl; 1882 | delete[] h_candidates; 1883 | */ 1884 | 1885 | candidate_kernel<<>>(d_candidate, this->candidates[idx2], candidate_num); 1886 | //checkCudaErrors(cudaGetLastError()); 1887 | cudaDeviceSynchronize(); 1888 | //checkCudaErrors(cudaGetLastError()); 1889 | 1890 | /* 1891 | unsigned* h_candidate_bitset = new unsigned[bitset_size / sizeof(unsigned)]; 1892 | cudaMemcpy(h_candidate_bitset, d_candidate, bitset_size, cudaMemcpyDeviceToHost); 1893 | std::cout << "Contents of d_candidate after candidate_kernel: "; 1894 | for (int i = 0; i < bitset_size / sizeof(unsigned); ++i) { 1895 | std::cout << std::bitset<32>(h_candidate_bitset[i]) << " "; 1896 | } 1897 | std::cout << std::endl; 1898 | delete[] h_candidate_bitset; 1899 | */ 1900 | 1901 | //long tmp2 = Util::get_cur_time(); 1902 | //cout<<"candidate kernel used: "<<(tmp2-tmp1)<<"ms"<>>(this->candidates[idx2], candidate_num, d_summary);//*/ 1908 | //cudaDeviceSynchronize();//*/ 1909 | //METHOD 2: compressed bitmap 1910 | /*unsigned width = Util::RoundUpDivision(bitset_size, SUMMARY_BYTES);*/ 1911 | /*bitmap_kernel<<>>(d_candidate, bitset_size, d_summary);*/ 1912 | //METHOD 3: Interval Summary 1913 | //checkCudaErrors(cudaGetLastError());//*/ 1914 | //long tmp3 = Util::get_cur_time();//*/ 1915 | //cout<<"build summary used: "<<(tmp3-tmp2)<<"ms"<candidates[idx2]); 1918 | //checkCudaErrors(cudaGetLastError()); 1919 | //join the intermediate table with a candidate list 1920 | //BETTER: use segmented join if the table is too large! 1921 | success = this->join(d_summary, link_pos, link_edge, link_num, d_result, d_candidate, candidate_num, result_row_num, result_col_num); 1922 | 1923 | delete[] link_pos; 1924 | delete[] link_edge; 1925 | #ifdef DEBUG 1926 | checkCudaErrors(cudaGetLastError()); 1927 | #endif 1928 | if(!success) 1929 | { 1930 | break; 1931 | } 1932 | idx = idx2; 1933 | //cout<<"intermediate table: "<id2pos; 1979 | 1980 | 1981 | delete[] score; 1982 | delete[] qnum; 1983 | release(); 1984 | 1985 | //NOTICE: device variables can not be assigned and output directly on Host 1986 | /*cudaMemcpyFromSymbol(&maxTaskLen, d_maxTaskLen, sizeof(unsigned));*/ 1987 | /*cudaMemcpyFromSymbol(&minTaskLen, d_minTaskLen, sizeof(unsigned));*/ 1988 | /*cudaDeviceSynchronize();*/ 1989 | /*cout<<"Maximum and Minimum task size: "<pos2id; 1996 | delete[] this->candidates; 1997 | #ifdef DEBUG 1998 | checkCudaErrors(cudaGetLastError()); 1999 | #endif 2000 | } -------------------------------------------------------------------------------- /GSI_cpp/match/Match.h: -------------------------------------------------------------------------------- 1 | /*============================================================================= 2 | # Filename: Match.h 3 | # Author: Bookug Lobert 4 | # Mail: 1181955272@qq.com 5 | # Last Modified: 2016-10-24 22:55 6 | # Description: find all subgraph-graph mappings between query graph and data graph 7 | =============================================================================*/ 8 | 9 | //HELP: 10 | //nvcc compile: http://blog.csdn.net/wzk6_3_8/article/details/15501931 11 | //cuda-memcheck: http://docs.nvidia.com/cuda/cuda-memcheck/#about-cuda-memcheck 12 | //nvprof: http://blog.163.com/wujiaxing009@126/blog/static/71988399201701310151777?ignoreua 13 | // 14 | //Use 2D array on GPU: 15 | //http://blog.csdn.net/lavorange/article/details/42125029 16 | // 17 | //http://blog.csdn.net/langb2014/article/details/51348523 18 | //to see the memory frequency of device 19 | //nvidia-smi -a -q -d CLOCK | fgrep -A 3 "Max Clocks" | fgrep "Memory" 20 | // 21 | //GPU cache: 22 | //http://blog.csdn.net/langb2014/article/details/51348616 23 | 24 | //the visit array of query, can be placed in CPU directly 25 | //选择性过滤候选集, 查询图的搜索顺序应是动态调整的 26 | //要能快速估算每个点的候选集大小:标签频繁度,度数,图结构,或者用gpu启发式地训练、过滤 27 | //BETTER: how about combine BFS and DFS, according to computing resources and GPU memory 28 | 29 | #ifndef _MATCH_MATCH_H 30 | #define _MATCH_MATCH_H 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | #include "gputimer.h" 49 | 50 | #include "../util/Util.h" 51 | #include "../graph/Graph.h" 52 | #include "../io/IO.h" 53 | 54 | #define checkCudaErrors(val) check( (val), #val, __FILE__, __LINE__) 55 | 56 | template 57 | void check(T err, const char* const func, const char* const file, const int line) { 58 | if (err != cudaSuccess) { 59 | std::cerr << "CUDA error at: " << file << ":" << line << std::endl; 60 | std::cerr << cudaGetErrorString(err) << " " << func << std::endl; 61 | exit(1); 62 | } 63 | } 64 | 65 | 66 | class Match 67 | { 68 | public: 69 | Match(Graph* _query, Graph* _data); 70 | //void match(IO& io, unsigned*& final_result, unsigned& result_row_num, unsigned& result_col_num, int*& id_map); 71 | void match(unsigned*& final_result, unsigned& result_row_num, unsigned& result_col_num, int*& id_map); 72 | ~Match(); 73 | 74 | static void initGPU(int dev, bool verbose); 75 | 76 | private: 77 | Graph* query; 78 | Graph* data; 79 | 80 | unsigned** candidates; 81 | 82 | int current_pos; 83 | int* id2pos; 84 | int* pos2id; 85 | void add_mapping(int _id); 86 | 87 | int get_minimum_idx(float* score, int qsize); 88 | 89 | unsigned *d_query_vertex_num, *d_query_label_num, *d_query_vertex_value, *d_query_row_offset_in, *d_query_row_offset_out, *d_query_edge_value_in, *d_query_edge_offset_in, *d_query_edge_value_out, *d_query_edge_offset_out, *d_query_column_index_in, *d_query_column_index_out, *d_query_inverse_label, *d_query_inverse_offset, *d_query_inverse_vertex; 90 | unsigned *d_data_vertex_num, *d_data_label_num, *d_data_vertex_value, *d_data_row_offset_in, *d_data_row_offset_out, *d_data_edge_value_in, *d_data_edge_offset_in, *d_data_edge_value_out, *d_data_edge_offset_out, *d_data_column_index_in, *d_data_column_index_out, *d_data_inverse_label, *d_data_inverse_offset, *d_data_inverse_vertex; 91 | 92 | void copyGraphToGPU(); 93 | void release(); 94 | //candidates placed in GPU, only copy the num back 95 | bool filter(float* _score, int* _qnum); 96 | void acquire_linking(int*& link_pos, int*& link_edge, int& link_num, int idx); 97 | bool join(unsigned* d_summary, int* link_pos, int* link_edge, int link_num, unsigned*& d_result, unsigned* d_candidate, unsigned num, unsigned& result_row_num, unsigned& result_col_num); 98 | 99 | bool score_node(float* _score, int* _qnum); 100 | void update_score(float* _score, int qsize, int _idx); 101 | 102 | //utilities 103 | void copyHtoD(unsigned*& d_ptr, unsigned* h_ptr, unsigned bytes); 104 | void exclusive_sum(unsigned* d_array, unsigned size); 105 | }; 106 | 107 | #endif //_MATCH_MATCH_H 108 | 109 | -------------------------------------------------------------------------------- /GSI_cpp/match/gputimer.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPU_TIMER_H__ 2 | #define __GPU_TIMER_H__ 3 | 4 | struct GpuTimer 5 | { 6 | cudaEvent_t start; 7 | cudaEvent_t stop; 8 | 9 | GpuTimer() 10 | { 11 | cudaEventCreate(&start); 12 | cudaEventCreate(&stop); 13 | } 14 | 15 | ~GpuTimer() 16 | { 17 | cudaEventDestroy(start); 18 | cudaEventDestroy(stop); 19 | } 20 | 21 | void Start() 22 | { 23 | cudaEventRecord(start, 0); 24 | } 25 | 26 | void Stop() 27 | { 28 | cudaEventRecord(stop, 0); 29 | } 30 | 31 | float Elapsed() 32 | { 33 | float elapsed; 34 | cudaEventSynchronize(stop); 35 | cudaEventElapsedTime(&elapsed, start, stop); 36 | return elapsed; 37 | } 38 | }; 39 | 40 | #endif /* __GPU_TIMER_H__ */ 41 | -------------------------------------------------------------------------------- /GSI_cpp/objs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkumod/GSI/8f098a6e7be056d6aee8d3a00d06f73ed2032ab7/GSI_cpp/objs/.gitkeep -------------------------------------------------------------------------------- /GSI_cpp/objs/Graph.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkumod/GSI/8f098a6e7be056d6aee8d3a00d06f73ed2032ab7/GSI_cpp/objs/Graph.o -------------------------------------------------------------------------------- /GSI_cpp/objs/IO.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkumod/GSI/8f098a6e7be056d6aee8d3a00d06f73ed2032ab7/GSI_cpp/objs/IO.o -------------------------------------------------------------------------------- /GSI_cpp/objs/Match.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkumod/GSI/8f098a6e7be056d6aee8d3a00d06f73ed2032ab7/GSI_cpp/objs/Match.o -------------------------------------------------------------------------------- /GSI_cpp/objs/Util.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkumod/GSI/8f098a6e7be056d6aee8d3a00d06f73ed2032ab7/GSI_cpp/objs/Util.o -------------------------------------------------------------------------------- /GSI_cpp/objs/isomorphism.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkumod/GSI/8f098a6e7be056d6aee8d3a00d06f73ed2032ab7/GSI_cpp/objs/isomorphism.o -------------------------------------------------------------------------------- /GSI_cpp/objs/pybind_wrapper.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkumod/GSI/8f098a6e7be056d6aee8d3a00d06f73ed2032ab7/GSI_cpp/objs/pybind_wrapper.o -------------------------------------------------------------------------------- /GSI_cpp/pybind_wrapper.cu: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "main/isomorphism.h" 4 | #include "graph/Graph.h" 5 | #include "util/Util.h" 6 | #include "io/IO.h" 7 | #include "match/Match.h" 8 | 9 | namespace py = pybind11; 10 | 11 | /** 12 | * @brief Python bindings for the C++ classes and functions using pybind11. 13 | */ 14 | PYBIND11_MODULE(isomorphism, m) { 15 | /** 16 | * @brief Binds the Graph class to Python. 17 | */ 18 | py::class_(m, "Graph") 19 | .def(py::init<>()) // Constructor 20 | .def("add_vertex", &Graph::addVertex, "Add a vertex to the graph with a given label") 21 | .def("add_edge", &Graph::addEdge, "Add an edge to the graph between two vertices with a given label") 22 | .def("transform_to_CSR", &Graph::transformToCSR, "Transform the graph to CSR format") 23 | .def("preprocessing", &Graph::preprocessing, "Preprocess the graph for isomorphism checking") 24 | .def("build_signature", &Graph::buildSignature, "Build the signature for the graph") 25 | .def("print_graph", &Graph::printGraph, "Print the graph") 26 | .def("count_max_degree", &Graph::countMaxDegree, "Count the maximum degree of vertices in the graph") 27 | .def("vSize", &Graph::vSize, "Get the number of vertices in the graph"); 28 | 29 | /** 30 | * @brief Binds the findIsomorphisms function to Python. 31 | * 32 | * @param query The query graph. 33 | * @param data The data graph. 34 | * @return A vector of unordered maps representing the isomorphisms. 35 | */ 36 | m.def("find_isomorphisms", &findIsomorphisms, "A function that finds isomorphisms between two graphs", 37 | py::arg("query"), py::arg("data")); 38 | 39 | /** 40 | * @brief Binds the printMapping function to Python. 41 | * 42 | * @param mapping The mapping of vertices from the query graph to the data graph. 43 | */ 44 | m.def("print_mapping", &printMapping, "A function that prints the mapping of isomorphisms", 45 | py::arg("mapping")); 46 | 47 | /** 48 | * @brief Binds the createGraph function to Python. 49 | * 50 | * @param nodeIDs The IDs of the nodes. 51 | * @param nodeLabels The labels of the nodes. 52 | * @param edgeIDs The pairs of node IDs representing edges. 53 | * @param edgeLabels The labels of the edges. 54 | * @param flag A boolean flag indicating column-oriented processing. 55 | * @return A pointer to the created Graph. 56 | */ 57 | m.def("create_graph", &createGraph, py::arg("nodeIDs"), py::arg("nodeLabels"), 58 | py::arg("edgeIDs"), py::arg("edgeLabels"), py::arg("flag"), 59 | py::return_value_policy::take_ownership); 60 | 61 | /** 62 | * @brief Binds the initializeGPU function to Python. 63 | * 64 | * @param dev The GPU device ID to initialize. 65 | * @param verbose If GPU initialization status should be printed to the console. 66 | */ 67 | m.def("initializeGPU", &initializeGPU, py::arg("dev"), py::arg("verbose")); 68 | } -------------------------------------------------------------------------------- /GSI_cpp/scripts/cover.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/bash 2 | 3 | echo "test start" 4 | 5 | lcov -z -d ./ 6 | ./dig data/big.g 0.1 7 | gcov -a -b -c dig.cpp 8 | lcov --no-external --directory . --capture --output-file dig_big.info 9 | #genhtml --output-directory . --frames --show-details dig_big.info 10 | #mv 4.8.2 RESULT/big 11 | #mv dig.cpp.gcov RESULT/big/ 12 | 13 | echo "big data tested" 14 | 15 | lcov -z -d ./ 16 | ./dig data/Chemical_340 0.1 17 | gcov -a -b -c dig.cpp 18 | lcov --no-external --directory . --capture --output-file dig_chemical.info 19 | #genhtml --output-directory . --frames --show-details dig_chemical.info 20 | #mv 4.8.2 RESULT/chemical 21 | #mv dig.cpp.gcov RESULT/chemical/ 22 | 23 | echo "chemical data tested" 24 | 25 | lcov -z -d ./ 26 | ./dig data/Compound_422 0.1 27 | gcov -a -b -c dig.cpp 28 | lcov --no-external --directory . --capture --output-file dig_compound.info 29 | #genhtml --output-directory . --frames --show-details dig_compound.info 30 | #mv 4.8.2 RESULT/compound 31 | #mv dig.cpp.gcov RESULT/compound/ 32 | 33 | echo "compound data tested" 34 | 35 | lcov -add-tracefile dig.info -a dig_big.info -a dig_chemical.info -a dig_compound.info 36 | genhtml --output-directory RESULT --frames --show-details dig.info 37 | 38 | -------------------------------------------------------------------------------- /GSI_cpp/scripts/filter.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # USE the 3-th GPU 4 | 5 | #make clean 6 | #make 7 | 8 | # nvprof -m gld_transactions -m gst_transactions ./GSI.exe /home/data/DATASET/gowalla/loc-gowalla_edges.g /home/data/DATASET/gowalla/query/q0.g >& gowalla.prof 9 | 10 | # TO find out which one fails, using grep -r "match used" xxx.log 11 | # To count using grep -r "match used" xxx.log |wc -l 12 | 13 | data=/home/data/DATASET/ 14 | 15 | # To run delaunay_n13 16 | query=/home/data/DATASET/query4de/q0.g 17 | #echo $query 18 | file=${query##*/} 19 | # we run the same query 3-times and select the final trial 20 | ./GSI.exe /home/data/DATASET/delaunay_n13.g ${query} ${result1}${file%.*}.txt 0 >& delaunay_n13.log 21 | echo "delaunay_n13 ends" 22 | 23 | # To run enron 24 | query=/home/data/DATASET/query4en/q0.g 25 | file=${query##*/} 26 | ./GSI.exe /home/data/DATASET/enron.g ${query} ${result2}${file%.*}.txt 0 >& enron.log 27 | echo "enron ends" 28 | 29 | # To run gowalla 30 | query=/home/data/DATASET/query4go/q0.g 31 | file=${query##*/} 32 | ./GSI.exe /home/data/DATASET/gowalla.g ${query} ${result3}${file%.*}.txt 0 >& gowalla.log 33 | echo "gowalla ends" 34 | 35 | # To run road_central 36 | query=/home/data/DATASET/query4ro/q0.g 37 | file=${query##*/} 38 | ./GSI.exe /home/data/DATASET/road_central.g ${query} ${result4}${file%.*}.txt 0 >& road_central.log 39 | echo "road_central ends" 40 | 41 | # To run watdiv100M 42 | query=/home/data/DATASET/query4wat100/q0.g 43 | file=${query##*/} 44 | ./GSI.exe /home/data/DATASET/watdiv100M.g ${query} ${result5}${file%.*}.txt 0 >& watdiv100M.log 45 | echo "watdiv100M ends" 46 | 47 | # To run dbpedia170M 48 | query=/home/data/DATASET/query4db/q0.g 49 | file=${query##*/} 50 | ./GSI.exe /home/data/DATASET/dbpedia170M.g ${query} ${result6}${file%.*}.txt 0 >& dbpedia170M.log 51 | echo "dbpedia170M ends" 52 | 53 | -------------------------------------------------------------------------------- /GSI_cpp/scripts/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # USE the 3-th GPU 4 | 5 | #make clean 6 | #make 7 | 8 | # nvprof -m gld_transactions -m gst_transactions ./GSI.exe /home/data/DATASET/gowalla/loc-gowalla_edges.g /home/data/DATASET/gowalla/query/q0.g >& gowalla.prof 9 | 10 | # TO find out which one fails, using grep -r "match used" xxx.log 11 | # To count using grep -r "match used" xxx.log |wc -l 12 | 13 | data=/home/data/DATASET/ 14 | 15 | # To run delaunay_n13 16 | result1=delaunay_n13.log/ 17 | file1=delaunay_n13.tmp 18 | /bin/rm -rf ${result1} 19 | mkdir ${result1} 20 | for query in `ls /home/data/DATASET/query4de/*` 21 | do 22 | #echo $query 23 | file=${query##*/} 24 | # we run the same query 3-times and select the final trial 25 | ./GSI.exe /home/data/DATASET/delaunay_n13.g ${query} ${result1}${file%.*}.txt 3 >& ${result1}${file%.*}.log 26 | #./GSI.exe /home/data/DATASET/delaunay_n13.g ${query} ${result1}${file%.*}.txt 3 >& ${result1}${file%.*}.log 27 | #./GSI.exe /home/data/DATASET/delaunay_n13.g ${query} ${result1}${file%.*}.txt 3 >& ${result1}${file%.*}.log 28 | grep "match used" ${result1}${file%.*}.log >> ${file1} 29 | done 30 | awk 'BEGIN{t=0.0;cnt=0}{t = t + $3;cnt=cnt+1}END{printf("the average time of answering delaunay_n13 queries: %.2f ms\n", t/cnt);}' ${file1} 31 | /bin/rm -f $file1 32 | echo "delaunay_n13 ends" 33 | 34 | # To run enron 35 | result2=enron.log/ 36 | file2=enron.tmp 37 | /bin/rm -rf ${result2} 38 | mkdir ${result2} 39 | for query in `ls /home/data/DATASET/query4en/*` 40 | do 41 | file=${query##*/} 42 | ./GSI.exe /home/data/DATASET/enron.g ${query} ${result2}${file%.*}.txt 3 >& ${result2}${file%.*}.log 43 | grep "match used" ${result2}${file%.*}.log >> ${file2} 44 | done 45 | awk 'BEGIN{t=0.0;cnt=0}{t = t + $3;cnt=cnt+1}END{printf("the average time of answering enron queries: %.2f ms\n", t/cnt);}' ${file2} 46 | /bin/rm -f $file2 47 | echo "enron ends" 48 | 49 | # To run gowalla 50 | result3=gowalla.log/ 51 | file3=gowalla.tmp 52 | /bin/rm -rf ${result3} 53 | mkdir ${result3} 54 | for query in `ls /home/data/DATASET/query4go/*` 55 | do 56 | file=${query##*/} 57 | ./GSI.exe /home/data/DATASET/gowalla.g ${query} ${result3}${file%.*}.txt 3 >& ${result3}${file%.*}.log 58 | grep "match used" ${result3}${file%.*}.log >> ${file3} 59 | done 60 | awk 'BEGIN{t=0.0;cnt=0}{t = t + $3;cnt=cnt+1}END{printf("the average time of answering gowalla queries: %.2f ms\n", t/cnt);}' ${file3} 61 | /bin/rm -f $file3 62 | echo "gowalla ends" 63 | 64 | # To run road_central 65 | result4=road_central.log/ 66 | file4=road_central.tmp 67 | /bin/rm -rf ${result4} 68 | mkdir ${result4} 69 | for query in `ls /home/data/DATASET/query4ro/*` 70 | do 71 | file=${query##*/} 72 | ./GSI.exe /home/data/DATASET/road_central.g ${query} ${result4}${file%.*}.txt 3 >& ${result4}${file%.*}.log 73 | grep "match used" ${result4}${file%.*}.log >> ${file4} 74 | done 75 | awk 'BEGIN{t=0.0;cnt=0}{t = t + $3;cnt=cnt+1}END{printf("the average time of answering road_central queries: %.2f ms\n", t/cnt);}' ${file4} 76 | /bin/rm -f $file4 77 | echo "road_central ends" 78 | 79 | # To run watdiv100M 80 | result5=watdiv100M.log/ 81 | file5=watdiv100M.tmp 82 | /bin/rm -rf ${result5} 83 | mkdir ${result5} 84 | for query in `ls /home/data/DATASET/query4wat100/*` 85 | do 86 | file=${query##*/} 87 | ./GSI.exe /home/data/DATASET/watdiv100M.g ${query} ${result5}${file%.*}.txt 3 >& ${result5}${file%.*}.log 88 | grep "match used" ${result5}${file%.*}.log >> ${file5} 89 | done 90 | awk 'BEGIN{t=0.0;cnt=0}{t = t + $3;cnt=cnt+1}END{printf("the average time of answering watdiv100M queries: %.2f ms\n", t/cnt);}' ${file5} 91 | /bin/rm -f $file5 92 | echo "watdiv100M ends" 93 | 94 | # To run dbpedia170M 95 | result6=dbpedia170M.log/ 96 | file6=dbpedia170M.tmp 97 | /bin/rm -rf ${result6} 98 | mkdir ${result6} 99 | for query in `ls /home/data/DATASET/query4db/*` 100 | do 101 | file=${query##*/} 102 | ./GSI.exe /home/data/DATASET/dbpedia170M.g ${query} ${result6}${file%.*}.txt 3 >& ${result6}${file%.*}.log 103 | grep "match used" ${result6}${file%.*}.log >> ${file6} 104 | done 105 | awk 'BEGIN{t=0.0;cnt=0}{t = t + $3;cnt=cnt+1}END{printf("the average time of answering dbpedia170M queries: %.2f ms\n", t/cnt);}' ${file6} 106 | /bin/rm -f $file6 107 | echo "dbpedia170M ends" 108 | 109 | -------------------------------------------------------------------------------- /GSI_cpp/scripts/sumline.sh: -------------------------------------------------------------------------------- 1 | find . -type f -print | grep -E "\.(c(pp)?|h)$" | xargs wc -l 2 | 3 | -------------------------------------------------------------------------------- /GSI_cpp/scripts/watdiv.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # USE the 3-th GPU 4 | 5 | #make clean 6 | #make 7 | 8 | # nvprof -m gld_transactions -m gst_transactions ./GSI.exe /home/data/DATASET/gowalla/loc-gowalla_edges.g /home/data/DATASET/gowalla/query/q0.g >& gowalla.prof 9 | 10 | # TO find out which one fails, using grep -r "match used" xxx.log 11 | # To count using grep -r "match used" xxx.log |wc -l 12 | 13 | data=/home/data/DATASET/ 14 | 15 | # To run watdiv10M 16 | result=watdiv10M.log/ 17 | file=watdiv10M.tmp 18 | /bin/rm -rf ${result} 19 | mkdir ${result} 20 | for query in `ls /home/data/DATASET/query4wat10/*` 21 | do 22 | qq=${query##*/} 23 | ./GSI.exe /home/data/DATASET/watdiv10M.g ${query} ${result}${qq%.*}.txt 3 >& ${result}${qq%.*}.log 24 | grep "match used" ${result}${qq%.*}.log >> ${file} 25 | done 26 | awk 'BEGIN{t=0.0;cnt=0}{t = t + $3;cnt=cnt+1}END{printf("the average time of answering watdiv10M queries: %.2f ms\n", t/cnt);}' ${file} 27 | #/bin/rm -f $file 28 | echo "watdiv10M ends" 29 | 30 | # To run watdiv20M 31 | result=watdiv20M.log/ 32 | file=watdiv20M.tmp 33 | /bin/rm -rf ${result} 34 | mkdir ${result} 35 | for query in `ls /home/data/DATASET/query4wat20/*` 36 | do 37 | qq=${query##*/} 38 | ./GSI.exe /home/data/DATASET/watdiv20M.g ${query} ${result}${qq%.*}.txt 3 >& ${result}${qq%.*}.log 39 | grep "match used" ${result}${qq%.*}.log >> ${file} 40 | done 41 | awk 'BEGIN{t=0.0;cnt=0}{t = t + $3;cnt=cnt+1}END{printf("the average time of answering watdiv20M queries: %.2f ms\n", t/cnt);}' ${file} 42 | #/bin/rm -f $file 43 | echo "watdiv20M ends" 44 | 45 | # To run watdiv30M 46 | result=watdiv30M.log/ 47 | file=watdiv30M.tmp 48 | /bin/rm -rf ${result} 49 | mkdir ${result} 50 | for query in `ls /home/data/DATASET/query4wat30/*` 51 | do 52 | qq=${query##*/} 53 | ./GSI.exe /home/data/DATASET/watdiv30M.g ${query} ${result}${qq%.*}.txt 3 >& ${result}${qq%.*}.log 54 | grep "match used" ${result}${qq%.*}.log >> ${file} 55 | done 56 | awk 'BEGIN{t=0.0;cnt=0}{t = t + $3;cnt=cnt+1}END{printf("the average time of answering watdiv30M queries: %.2f ms\n", t/cnt);}' ${file} 57 | #/bin/rm -f $file 58 | echo "watdiv30M ends" 59 | 60 | # To run watdiv40M 61 | result=watdiv40M.log/ 62 | file=watdiv40M.tmp 63 | /bin/rm -rf ${result} 64 | mkdir ${result} 65 | for query in `ls /home/data/DATASET/query4wat40/*` 66 | do 67 | qq=${query##*/} 68 | ./GSI.exe /home/data/DATASET/watdiv40M.g ${query} ${result}${qq%.*}.txt 3 >& ${result}${qq%.*}.log 69 | grep "match used" ${result}${qq%.*}.log >> ${file} 70 | done 71 | awk 'BEGIN{t=0.0;cnt=0}{t = t + $3;cnt=cnt+1}END{printf("the average time of answering watdiv40M queries: %.2f ms\n", t/cnt);}' ${file} 72 | #/bin/rm -f $file 73 | echo "watdiv40M ends" 74 | 75 | # To run watdiv50M 76 | result=watdiv50M.log/ 77 | file=watdiv50M.tmp 78 | /bin/rm -rf ${result} 79 | mkdir ${result} 80 | for query in `ls /home/data/DATASET/query4wat50/*` 81 | do 82 | qq=${query##*/} 83 | ./GSI.exe /home/data/DATASET/watdiv50M.g ${query} ${result}${qq%.*}.txt 3 >& ${result}${qq%.*}.log 84 | grep "match used" ${result}${qq%.*}.log >> ${file} 85 | done 86 | awk 'BEGIN{t=0.0;cnt=0}{t = t + $3;cnt=cnt+1}END{printf("the average time of answering watdiv50M queries: %.2f ms\n", t/cnt);}' ${file} 87 | #/bin/rm -f $file 88 | echo "watdiv50M ends" 89 | 90 | # To run watdiv60M 91 | result=watdiv60M.log/ 92 | file=watdiv60M.tmp 93 | /bin/rm -rf ${result} 94 | mkdir ${result} 95 | for query in `ls /home/data/DATASET/query4wat60/*` 96 | do 97 | qq=${query##*/} 98 | ./GSI.exe /home/data/DATASET/watdiv60M.g ${query} ${result}${qq%.*}.txt 3 >& ${result}${qq%.*}.log 99 | grep "match used" ${result}${qq%.*}.log >> ${file} 100 | done 101 | awk 'BEGIN{t=0.0;cnt=0}{t = t + $3;cnt=cnt+1}END{printf("the average time of answering watdiv60M queries: %.2f ms\n", t/cnt);}' ${file} 102 | #/bin/rm -f $file 103 | echo "watdiv60M ends" 104 | 105 | # To run watdiv70M 106 | result=watdiv70M.log/ 107 | file=watdiv70M.tmp 108 | /bin/rm -rf ${result} 109 | mkdir ${result} 110 | for query in `ls /home/data/DATASET/query4wat70/*` 111 | do 112 | qq=${query##*/} 113 | ./GSI.exe /home/data/DATASET/watdiv70M.g ${query} ${result}${qq%.*}.txt 3 >& ${result}${qq%.*}.log 114 | grep "match used" ${result}${qq%.*}.log >> ${file} 115 | done 116 | awk 'BEGIN{t=0.0;cnt=0}{t = t + $3;cnt=cnt+1}END{printf("the average time of answering watdiv70M queries: %.2f ms\n", t/cnt);}' ${file} 117 | #/bin/rm -f $file 118 | echo "watdiv70M ends" 119 | 120 | # To run watdiv80M 121 | result=watdiv80M.log/ 122 | file=watdiv80M.tmp 123 | /bin/rm -rf ${result} 124 | mkdir ${result} 125 | for query in `ls /home/data/DATASET/query4wat80/*` 126 | do 127 | qq=${query##*/} 128 | ./GSI.exe /home/data/DATASET/watdiv80M.g ${query} ${result}${qq%.*}.txt 3 >& ${result}${qq%.*}.log 129 | grep "match used" ${result}${qq%.*}.log >> ${file} 130 | done 131 | awk 'BEGIN{t=0.0;cnt=0}{t = t + $3;cnt=cnt+1}END{printf("the average time of answering watdiv80M queries: %.2f ms\n", t/cnt);}' ${file} 132 | #/bin/rm -f $file 133 | echo "watdiv80M ends" 134 | 135 | # To run watdiv90M 136 | result=watdiv90M.log/ 137 | file=watdiv90M.tmp 138 | /bin/rm -rf ${result} 139 | mkdir ${result} 140 | for query in `ls /home/data/DATASET/query4wat90/*` 141 | do 142 | qq=${query##*/} 143 | ./GSI.exe /home/data/DATASET/watdiv90M.g ${query} ${result}${qq%.*}.txt 3 >& ${result}${qq%.*}.log 144 | grep "match used" ${result}${qq%.*}.log >> ${file} 145 | done 146 | awk 'BEGIN{t=0.0;cnt=0}{t = t + $3;cnt=cnt+1}END{printf("the average time of answering watdiv90M queries: %.2f ms\n", t/cnt);}' ${file} 147 | #/bin/rm -f $file 148 | echo "watdiv90M ends" 149 | 150 | 151 | # To run watdiv100M 152 | result=watdiv100M.log/ 153 | file=watdiv100M.tmp 154 | /bin/rm -rf ${result} 155 | mkdir ${result} 156 | for query in `ls /home/data/DATASET/query4wat100/*` 157 | do 158 | qq=${query##*/} 159 | ./GSI.exe /home/data/DATASET/watdiv100M.g ${query} ${result}${qq%.*}.txt 3 >& ${result}${qq%.*}.log 160 | grep "match used" ${result}${qq%.*}.log >> ${file} 161 | done 162 | awk 'BEGIN{t=0.0;cnt=0}{t = t + $3;cnt=cnt+1}END{printf("the average time of answering watdiv100M queries: %.2f ms\n", t/cnt);}' ${file} 163 | #/bin/rm -f $file 164 | echo "watdiv100M ends" 165 | 166 | -------------------------------------------------------------------------------- /GSI_cpp/util/Util.cpp: -------------------------------------------------------------------------------- 1 | /*============================================================================= 2 | # Filename: Util.cpp 3 | # Author: Bookug Lobert 4 | # Mail: 1181955272@qq.com 5 | # Last Modified: 2016-10-24 17:23 6 | # Description: 7 | =============================================================================*/ 8 | 9 | #include "Util.h" 10 | 11 | using namespace std; 12 | 13 | Util::Util() 14 | { 15 | } 16 | 17 | Util::~Util() 18 | { 19 | } 20 | 21 | long 22 | Util::get_cur_time() 23 | { 24 | timeval tv; 25 | gettimeofday(&tv, NULL); 26 | return (tv.tv_sec*1000 + tv.tv_usec/1000); 27 | } 28 | 29 | unsigned 30 | Util::RoundUp(int num, int base) 31 | { 32 | unsigned tmp = (num+base-1)/base; 33 | return tmp * base; 34 | } 35 | 36 | unsigned 37 | Util::RoundUpDivision(int num, int base) 38 | { 39 | return (num+base-1)/base; 40 | } 41 | 42 | //the seed is a prime, which can be well chosed to yield good performance(low conflicts) 43 | uint32_t 44 | Util::MurmurHash2(const void * key, int len, uint32_t seed) 45 | { 46 | // 'm' and 'r' are mixing constants generated offline. 47 | // They're not really 'magic', they just happen to work well. 48 | const uint32_t m = 0x5bd1e995; 49 | const int r = 24; 50 | // Initialize the hash to a 'random' value 51 | uint32_t h = seed ^ len; 52 | // Mix 4 bytes at a time into the hash 53 | const unsigned char * data = (const unsigned char *) key; 54 | while (len >= 4) 55 | { 56 | uint32_t k = *(uint32_t*) data; 57 | k *= m; 58 | k ^= k >> r; 59 | k *= m; 60 | h *= m; 61 | h ^= k; 62 | data += 4; 63 | len -= 4; 64 | } 65 | // Handle the last few bytes of the input array 66 | switch (len) 67 | { 68 | case 3: 69 | h ^= data[2] << 16; 70 | case 2: 71 | h ^= data[1] << 8; 72 | case 1: 73 | h ^= data[0]; 74 | h *= m; 75 | }; 76 | // Do a few final mixes of the hash to ensure the last few 77 | // bytes are well-incorporated. 78 | h ^= h >> 13; 79 | h *= m; 80 | h ^= h >> 15; 81 | return h; 82 | } 83 | 84 | void 85 | Util::DisplayBinary(int num) 86 | { 87 | int i, j; 88 | j = sizeof(int)*8; 89 | for (i = 0; i < j; i ++) 90 | { 91 | //NOTICE:the most significant bit is output first 92 | //&0x1 is needed because for negative integers the right shift will add 1 in the former bits 93 | printf("%d", (num>>(j-i-1)&0x1)); 94 | } 95 | } 96 | 97 | -------------------------------------------------------------------------------- /GSI_cpp/util/Util.h: -------------------------------------------------------------------------------- 1 | /*============================================================================= 2 | # Filename: util.h 3 | # Author: Bookug Lobert 4 | # Mail: 1181955272@qq.com 5 | # Last Modified: 2016-10-24 17:20 6 | # Description: 7 | =============================================================================*/ 8 | 9 | #ifndef _UTIL_UTIL_H 10 | #define _UTIL_UTIL_H 11 | 12 | //basic macros and types are defined here, including common headers 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | //NOTICE:below are restricted to C++, C files should not include(maybe nested) this header! 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | 60 | //NOTICE:below are libraries need to link 61 | #include 62 | #include 63 | #include 64 | 65 | //indicate that in debug mode 66 | //#define DEBUG FALSE 67 | 68 | #define MAX_PATTERN_SIZE 1000000000 69 | 70 | //for signatures 71 | #define SIGLEN 64*8 72 | #define VLEN 32 73 | #define SIGNUM SIGLEN/VLEN 74 | #define SIGBYTE sizeof(unsigned)*SIGNUM 75 | #define HASHSEED 17 76 | #define HASHSEED2 53 77 | 78 | 79 | #define xfree(x) free(x); x = NULL; 80 | 81 | typedef int LABEL; 82 | typedef int VID; 83 | typedef int EID; 84 | typedef int GID; 85 | typedef long PID; 86 | typedef long LENGTH; 87 | 88 | static const unsigned INVALID = UINT_MAX; 89 | 90 | /******** all static&universal constants and fucntions ********/ 91 | class Util 92 | { 93 | public: 94 | Util(); 95 | ~Util(); 96 | static long get_cur_time(); 97 | static unsigned RoundUp(int num, int base); 98 | static unsigned RoundUpDivision(int num, int base); 99 | static uint32_t MurmurHash2(const void * key, int len, uint32_t seed) ; 100 | static void DisplayBinary(int num); 101 | }; 102 | 103 | #endif //_UTIL_UTIL_H 104 | 105 | -------------------------------------------------------------------------------- /GSI_example.py: -------------------------------------------------------------------------------- 1 | import timeit 2 | import networkx as nx 3 | from GSI import initGPU, createGraph, findIsomorphism, printMappings, nxGraph, printGraph 4 | from networkx.algorithms import isomorphism 5 | import test 6 | 7 | # Initialize GPU 8 | initGPU(dev=0) 9 | print() 10 | 11 | ''' 12 | # Create two sample graphs using raw data 13 | node_ids1 = [0, 1, 2] 14 | node_labels1 = [1, 2, 3] 15 | edge_ids1 = [(0, 1), (1, 2)] 16 | edge_labels1 = [10, 20] 17 | 18 | node_ids2 = [0, 1, 2] 19 | node_labels2 = [1, 2, 3] 20 | edge_ids2 = [(0, 1), (1, 2)] 21 | edge_labels2 = [10, 20] 22 | 23 | # Create custom Graph objects 24 | graph1 = createGraph(node_ids1, node_labels1, edge_ids1, edge_labels1, column_oriented=False) 25 | graph2 = createGraph(node_ids2, node_labels2, edge_ids2, edge_labels2, column_oriented=True) 26 | 27 | # Profile the time taken to find isomorphisms between the two graphs using timeit 28 | execution_time_custom = timeit.timeit( 29 | stmt='findIsomorphism(graph1, graph2)', 30 | setup='from __main__ import findIsomorphism, graph1, graph2', 31 | number=1 32 | ) 33 | 34 | # Find isomorphisms between the two graphs 35 | is_isomorphic_custom, mappings_custom = findIsomorphism(graph1, graph2) 36 | 37 | # Print the result 38 | if is_isomorphic_custom: 39 | print("Isomorphism found!") 40 | printMappings(mappings_custom) 41 | else: 42 | print("No isomorphism found.") 43 | 44 | print(f"Time taken to find isomorphisms with custom method: {execution_time_custom:.6f} seconds") 45 | ''' 46 | ''' 47 | # Demonstration using NetworkX graph 48 | # Create a NetworkX graph 49 | query_nx = nx.Graph() 50 | query_nx.add_nodes_from([(i, {'label': i % 3 + 1}) for i in range(25)]) 51 | query_edges = [(i, (i+1) % 25, {'label': i % 5 + 1}) for i in range(25)] 52 | query_nx.add_edges_from(query_edges) 53 | 54 | # Create a larger data graph using NetworkX 55 | data_nx = nx.Graph() 56 | data_nx.add_nodes_from([(i, {'label': i % 3 + 1}) for i in range(100)]) 57 | data_edges = [(i, (i+1) % 100, {'label': i % 5 + 1}) for i in range(100)] 58 | data_edges += [(i, (i+2) % 100, {'label': i % 5 + 1}) for i in range(50)] 59 | data_nx.add_edges_from(data_edges) 60 | ''' 61 | 62 | # Data graph 63 | data_nx = test.generate_numbered_bidirectional_grid_graph(10, 10, 1, 1) 64 | 65 | 66 | # Query graph 67 | query_nx = test.generate_numbered_bidirectional_grid_graph(5, 4, 1, 1) 68 | 69 | 70 | # Convert NetworkX graphs to custom Graph objects 71 | query_graph = nxGraph(query_nx, column_oriented=False) 72 | data_graph = nxGraph(data_nx, column_oriented=True) 73 | 74 | printGraph(data_graph) 75 | 76 | quit() 77 | 78 | # Profile the time taken to find isomorphisms between NetworkX-based custom graphs using timeit 79 | execution_time_custom_nx = timeit.timeit( 80 | stmt='findIsomorphism(query_graph, data_graph)', 81 | setup='from __main__ import findIsomorphism, query_graph, data_graph', 82 | number=1 83 | ) 84 | 85 | # Find isomorphisms between NetworkX-based custom graphs 86 | is_isomorphic_custom_nx, mappings_custom_nx = findIsomorphism(query_graph, data_graph) 87 | 88 | # Print the result 89 | if is_isomorphic_custom_nx: 90 | print("Isomorphism found between NetworkX graphs using custom method!") 91 | print(mappings_custom_nx[0]) 92 | #printMappings(mappings_custom_nx) 93 | else: 94 | print("No isomorphism found between NetworkX graphs using custom method.") 95 | 96 | print(f"Time taken to find isomorphisms between NetworkX graphs with custom method: {execution_time_custom_nx:.6f} seconds") 97 | 98 | # Profile the time taken to find isomorphisms using NetworkX's built-in method on the larger graphs 99 | execution_time_nx = timeit.timeit( 100 | stmt='isomorphism.GraphMatcher(data_nx, query_nx).subgraph_is_isomorphic()', 101 | #setup='import networkx as nx; from networkx.algorithms import isomorphism; import test;data_nx=test.generate_numbered_bidirectional_grid_graph(10, 10, 2, 1);query_nx=test.generate_numbered_bidirectional_grid_graph(4, 5, 2, 1)', 102 | setup='from __main__ import query_nx, data_nx; from networkx import isomorphism', 103 | number=1 104 | ) 105 | 106 | # Find isomorphisms using NetworkX's built-in method 107 | GM = isomorphism.GraphMatcher(data_nx, query_nx) 108 | is_isomorphic_nx = GM.subgraph_is_isomorphic() 109 | 110 | # Print the result 111 | if is_isomorphic_nx: 112 | print("Isomorphism found between NetworkX graphs using NetworkX method!") 113 | else: 114 | print("No isomorphism found between NetworkX graphs using NetworkX method.") 115 | 116 | print(f"Time taken to find isomorphisms between NetworkX graphs with NetworkX method: {execution_time_nx:.6f} seconds") -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GSI 2 | 3 | GPU-friendly Subgraph Isomorphism 4 | 5 | We target at a one-to-one mapping at a time, the query graph is small (vertices less than 100), while the data graph can be very large (but all can be placed in GPU's global memory). 6 | 7 | --- 8 | 9 | #### Installation 10 | 11 | This code was tested with the following dependencies: 12 | - nvcc 13 | - CUDA, version 12.5 14 | - GCC, version 11.4 15 | 16 | This code was tested with the following hardware/software: 17 | - Fedora 40 (CUDA 12.5 does not officially support Fedora 40, see notes) 18 | - NVIDIA GeForce RTX 3070 (SM_86) 19 | 20 | Find your GPU architecture by looking up your NVIDIA GPU on this website: [https://arnon.dk/matching-sm-architectures-arch-and-gencode-for-various-nvidia-cards/] 21 | 22 | You can install GCC-11 with homebrew, which is what I did: 23 | ```sh 24 | $ brew install gcc@11 25 | ``` 26 | Whatever you do, find the path to your GCC-11 install. Mine was ```/home/linuxbrew/.linuxbrew/Cellar/gcc@11/11.4.0```. If you also run Linux and used homebrew, this may be the same for you. 27 | 28 | Next, install CUDA and NVCC. If you run a supported OS, you can just follow the instructions [here](https://developer.nvidia.com/cuda-downloads). Otherwise, you will have to do some googling online to see exactly how to install CUDA-12.5. 29 | 30 | You can check your CUDA install version with the following command; Ensure that it is CUDA-12.5. 31 | ```sh 32 | $ nvcc --version 33 | ``` 34 | 35 | You also need to update your ```$PATH``` in order for the makefile to work properly: 36 | ```sh 37 | export CUDA_PATH=/path/to/your/cuda 38 | export GCC_11_PATH=/path/to/your/gcc_11 39 | ``` 40 | 41 | ### Makefile Parameters 42 | 43 | The makefile provided requires defining paths to CUDA and GCC installations. Here are the key sections to update: 44 | 45 | #### CUDA Path 46 | If `CUDA_PATH` is not already defined, use the default path: 47 | ```sh 48 | CUDA_PATH ?= /usr/local/cuda-12.5 49 | ``` 50 | 51 | #### GCC Path 52 | If `GCC_11_PATH` is not already defined, use the default path installed by Linuxbrew: 53 | ```sh 54 | GCC_11_PATH ?= /home/linuxbrew/.linuxbrew/Cellar/gcc@11/11.4.0 55 | ``` 56 | 57 | #### Compilation Flags 58 | Ensure the correct paths and flags for compiling CUDA and C++ code: 59 | ```sh 60 | CC = $(GCC_11_PATH)/bin/gcc-11 61 | CXX = $(GCC_11_PATH)/bin/g++-11 62 | NVCC = $(CUDA_PATH)/bin/nvcc 63 | 64 | CFLAGS = -std=c++14 -c -O2 -I$(CUDA_PATH)/include 65 | EXEFLAG = -O2 -shared 66 | LDFLAGS = -L$(CUDA_PATH)/lib64 -lcudart -lcudadevrt 67 | INCLUDES = -I$(CUDA_PATH)/include 68 | GPU_ARCHITECTURE = sm_86 69 | 70 | PYBIND11_INCLUDES = $(shell python3 -m pybind11 --includes) 71 | PYTHON_LDFLAGS = $(shell python3-config --ldflags) 72 | 73 | NVCCFLAGS = -arch=$(GPU_ARCHITECTURE) -rdc=true --ptxas-options=-v -Xcompiler -fPIC $(INCLUDES) --compiler-bindir $(GCC_11_PATH)/bin -std=c++14 -c -O2 -I$(CUDA_PATH)/include 74 | ``` 75 | 76 | ### Building the Project 77 | 78 | To build this project, simply run `make` within this directory: 79 | ```sh 80 | $ make 81 | ``` 82 | 83 | This will compile all necessary object files and create the shared library `libisomorphism.so` and the Python extension `isomorphism$(shell python3-config --extension-suffix)`. 84 | 85 | --- 86 | 87 | #### Usage 88 | 89 | A simple python wrapper module for the CUDA and C++ code has been created. 90 | Using the wrapper module, you can test for the isomorphism between a query graph and a data graph using the GPU. 91 | 92 | A example of the usage may be found in ```GSI_example.py``` 93 | 94 | 95 | --- 96 | 97 | #### Dataset 98 | 99 | NOTICE: we add 1 to labels for both vertex and edge, to ensure the label is positive! 100 | 101 | see `data/readme` and `data/*.g` 102 | 103 | --- 104 | 105 | #### Publication 106 | 107 | Li Zeng, Lei Zou, M. Tamer Özsu, Lin Hu, Fan Zhang. **GSI: GPU-friendly Subgraph Isomorphism**. *International Conference on Data Engineering*, 2020. 108 | 109 | --- 110 | 111 | #### Others 112 | 113 | Implementation of [GunrockSM](https://github.com/bookug/GunrockSM), [GpSM](https://github.com/bookug/GpSM), and [GPU utilities](https://github.com/bookug/gutil). 114 | 115 | --- 116 | 117 | -------------------------------------------------------------------------------- /isomorphism.cpython-312-x86_64-linux-gnu.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkumod/GSI/8f098a6e7be056d6aee8d3a00d06f73ed2032ab7/isomorphism.cpython-312-x86_64-linux-gnu.so -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | import networkx as nx 2 | import random 3 | #import matplotlib.pyplot as plt 4 | 5 | def generate_bidirectional_graph(num_nodes, node_label_range, edge_label): 6 | # Create an empty directed graph 7 | G = nx.DiGraph() 8 | 9 | # Add nodes with random labels 10 | for i in range(num_nodes): 11 | G.add_node(i, label=random.randint(1, node_label_range)) 12 | 13 | # Add edges to ensure the graph is mostly connected 14 | # First, create a connected component 15 | for i in range(num_nodes - 1): 16 | G.add_edge(i, i + 1, label=edge_label) 17 | G.add_edge(i + 1, i, label=edge_label) # Add reverse direction 18 | 19 | # Add additional random edges to increase connectivity 20 | additional_edges = num_nodes // 2 21 | for _ in range(additional_edges): 22 | u = random.randint(0, num_nodes - 1) 23 | v = random.randint(0, num_nodes - 1) 24 | if u != v and not G.has_edge(u, v): 25 | G.add_edge(u, v, label=edge_label) 26 | G.add_edge(v, u, label=edge_label) # Add reverse direction 27 | 28 | return G 29 | 30 | def generate_numbered_bidirectional_grid_graph(rows, cols, node_label_range, edge_label): 31 | # Create an empty graph 32 | G = nx.DiGraph() 33 | 34 | # Add nodes with random labels 35 | for i in range(rows * cols): 36 | G.add_node(i, label=random.randint(1, node_label_range)) 37 | 38 | # Add edges and make them bidirectional 39 | for i in range(rows): 40 | for j in range(cols): 41 | current = i * cols + j 42 | if j < cols - 1: # Horizontal edge 43 | right = current + 1 44 | G.add_edge(current, right, label=edge_label) 45 | G.add_edge(right, current, label=edge_label) 46 | if i < rows - 1: # Vertical edge 47 | down = current + cols 48 | G.add_edge(current, down, label=edge_label) 49 | G.add_edge(down, current, label=edge_label) 50 | 51 | return G 52 | 53 | ''' 54 | # Parameters 55 | num_nodes = 100 56 | node_label_range = 5 57 | edge_label = 1 58 | 59 | # Generate the bidirectional graph 60 | G = generate_bidirectional_graph(num_nodes, node_label_range, edge_label) 61 | 62 | # Draw the graph 63 | pos = nx.spring_layout(G) # positions for all nodes 64 | node_labels = nx.get_node_attributes(G, 'label') 65 | edge_labels = nx.get_edge_attributes(G, 'label') 66 | 67 | nx.draw(G, pos, with_labels=True, node_color='lightblue', node_size=500, font_size=10) 68 | nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_color='red') 69 | nx.draw_networkx_labels(G, pos, labels=node_labels, font_size=12, font_color='black') 70 | 71 | plt.show() 72 | 73 | # Print some basic information about the graph 74 | print("Number of nodes:", G.number_of_nodes()) 75 | print("Number of edges:", G.number_of_edges()) 76 | 77 | # Save the graph in GraphML format 78 | #nx.write_graphml(G, "generated_bidirectional_graph.graphml") 79 | ''' -------------------------------------------------------------------------------- /test2.py: -------------------------------------------------------------------------------- 1 | import networkx as nx 2 | import random 3 | #import matplotlib.pyplot as plt 4 | 5 | def generate_numbered_bidirectional_grid_graph(rows, cols, node_label_range, edge_label): 6 | # Create an empty graph 7 | G = nx.DiGraph() 8 | 9 | # Add nodes with random labels 10 | for i in range(rows * cols): 11 | G.add_node(i, label=random.randint(1, node_label_range)) 12 | 13 | # Add edges and make them bidirectional 14 | for i in range(rows): 15 | for j in range(cols): 16 | current = i * cols + j 17 | if j < cols - 1: # Horizontal edge 18 | right = current + 1 19 | G.add_edge(current, right, label=edge_label) 20 | G.add_edge(right, current, label=edge_label) 21 | if i < rows - 1: # Vertical edge 22 | down = current + cols 23 | G.add_edge(current, down, label=edge_label) 24 | G.add_edge(down, current, label=edge_label) 25 | 26 | return G 27 | 28 | ''' 29 | # Parameters 30 | rows = 10 31 | cols = 10 32 | node_label_range = 1 33 | edge_label = 1 34 | 35 | # Generate the bidirectional grid graph 36 | G = generate_numbered_bidirectional_grid_graph(rows, cols, node_label_range, edge_label) 37 | 38 | # Draw the graph 39 | pos = {i: (i % cols, -i // cols) for i in G.nodes()} 40 | node_labels = nx.get_node_attributes(G, 'label') 41 | edge_labels = nx.get_edge_attributes(G, 'label') 42 | 43 | nx.draw(G, pos, with_labels=True, node_color='lightblue', node_size=500, font_size=10) 44 | nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_color='red') 45 | #nx.draw_networkx_labels(G, pos, labels=node_labels, font_size=12, font_color='black') 46 | 47 | plt.show() 48 | 49 | # Print some basic information about the graph 50 | print("Number of nodes:", G.number_of_nodes()) 51 | print("Number of edges:", G.number_of_edges()) 52 | ''' --------------------------------------------------------------------------------