/* * @HEADER * * *********************************************************************** * * Zoltan Toolkit for Load-balancing, Partitioning, Ordering and Coloring * Copyright 2012 Sandia Corporation * * Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, * the U.S. Government retains certain rights in this software. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the Corporation nor the names of the * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Questions? Contact Karen Devine kddevin@sandia.gov * Erik Boman egboman@sandia.gov * * *********************************************************************** * * @HEADER */ #ifdef __cplusplus /* if C++, define the rest of this header file as extern C */ extern "C" { #endif #include "zz_util_const.h" #include "third_library_const.h" #include "third_library_tools.h" #include "graph_util.h" /* comparison routine for bsearch */ static int Zoltan_Compare_Indextypes(const void *key, const void *arg); #include "zz_sort.h" /*********************************************************************/ /* Verify ParMetis graph structure. */ /* */ /* Input parameter graph_type specifies the type of graph: */ /* LOCAL_GRAPH - each proc owns a local METIS style graph */ /* GLOBAL_GRAPH - the procs share a global ParMETIS graph */ /* */ /* Input parameter check_graph specifies the level of verification: */ /* 0 - perform no checks at all */ /* 1 - verify on-processor part of graph */ /* 2 - verify complete graph (requires communication) */ /* */ /* Input parameter output_level specifies the level of verbosity: */ /* 0 - suppress warnings */ /* 1 - print summary of warnings */ /* 2 - detailed output for each warning */ /* */ /* Output: an error code (the same on all procs) */ /* */ /* Fatal error : */ /* - non-symmetric graph */ /* - incorrect vertex number */ /* - negative vertex or edge weight */ /* */ /* Warning : */ /* - no vertices or no edges in graph */ /* - zero sum of vertex or edge weights */ /* - self-edge */ /* - multiple edges between a pair of vertices */ /* - singletons (vertices with no edges) */ /* - non-symmetric edge weights */ /* */ /*********************************************************************/ int Zoltan_Verify_Graph(MPI_Comm comm, indextype *vtxdist, indextype *xadj, indextype *adjncy, weighttype *vwgt, weighttype *adjwgt, int vwgt_dim, int ewgt_dim, int graph_type, int check_graph, int output_level) { int flag, cross_edges = 0, mesg_size, sum; int ierr; /* Error in the graph; always return the worst ierr found */ int runerr;/* Error running this routine */ int nprocs, proc, *proclist, errors, global_errors; int *perm=NULL; int free_adjncy_sort=0; ZOLTAN_COMM_OBJ *comm_plan; static char *yo = "Zoltan_Verify_Graph"; char msg[256]; ZOLTAN_GNO_TYPE num_obj; int nrecv; indextype *ptr, *ptr1, *ptr2; indextype global_i, global_j; indextype *sendgno=NULL, *recvgno=NULL, *adjncy_sort=NULL; weighttype *sendwgt, *recvwgt; ZOLTAN_GNO_TYPE num_duplicates, num_singletons; ZOLTAN_GNO_TYPE num_selfs, nedges, global_sum, num_zeros; ZOLTAN_GNO_TYPE i, j, ii, k; MPI_Datatype zoltan_gno_mpi_type; ierr = ZOLTAN_OK; runerr = ZOLTAN_OK; zoltan_gno_mpi_type = Zoltan_mpi_gno_type(); /* Make sure all procs have same value of check_graph. */ MPI_Allreduce(&check_graph, &i, 1, MPI_INT, MPI_MAX, comm); check_graph = i; if (check_graph == 0) /* perform no error checking at all */ return ierr; /* Get number of procs and my rank */ MPI_Comm_size(comm, &nprocs); MPI_Comm_rank(comm, &proc); /* Check number of vertices (objects) */ num_obj = (ZOLTAN_GNO_TYPE)(vtxdist[proc+1] - vtxdist[proc]); MPI_Reduce(&num_obj, &global_sum, 1, zoltan_gno_mpi_type, MPI_SUM, 0, comm); if ((proc==0) && (global_sum==0)){ if (ierr == ZOLTAN_OK) ierr = ZOLTAN_WARN; if (output_level>0) ZOLTAN_PRINT_WARN(proc, yo, "No vertices in graph."); } /* Verify that vertex weights are non-negative */ num_zeros = 0; if (vwgt_dim){ for (i=0; i1) { sprintf(msg, "Zero vertex (object) weights for object " ZOLTAN_GNO_SPEC ".", i); ZOLTAN_PRINT_WARN(proc, yo, msg); } if (ierr == ZOLTAN_OK) ierr = ZOLTAN_WARN; } } MPI_Reduce(&num_zeros, &global_sum, 1, zoltan_gno_mpi_type, MPI_SUM, 0, comm); if ((proc==0) && (global_sum>0)){ if (ierr == ZOLTAN_OK) ierr = ZOLTAN_WARN; if (output_level>0){ sprintf(msg, ZOLTAN_GNO_SPEC " objects have zero weights.", global_sum); ZOLTAN_PRINT_WARN(proc, yo, msg); } } } /* Check number of edges */ nedges = (ZOLTAN_GNO_TYPE)xadj[num_obj]; MPI_Reduce(&nedges, &global_sum, 1, zoltan_gno_mpi_type, MPI_SUM, 0, comm); if ((proc==0) && (global_sum==0)){ if (ierr == ZOLTAN_OK) ierr = ZOLTAN_WARN; if (output_level>0) ZOLTAN_PRINT_WARN(proc, yo, "No edges in graph."); } /* Verify that edge weights are non-negative */ num_zeros = 0; if (ewgt_dim){ for (j=0; j1) { sprintf(msg, "Zero edge (communication) weights for edge " ZOLTAN_GNO_SPEC ".", j); ZOLTAN_PRINT_WARN(proc, yo, msg); } if (ierr == ZOLTAN_OK) ierr = ZOLTAN_WARN; } } MPI_Reduce(&num_zeros, &global_sum, 1, zoltan_gno_mpi_type, MPI_SUM, 0, comm); if ((proc==0) && (global_sum>0)){ if (ierr == ZOLTAN_OK) ierr = ZOLTAN_WARN; if (output_level>0){ sprintf(msg, ZOLTAN_GNO_SPEC " edges have zero weights.", global_sum); ZOLTAN_PRINT_WARN(proc, yo, msg); } } } /* Verify that the graph is symmetric (edge weights, too) */ /* Also check for self-edges and multi-edges */ /* Pre-processing: Check if edge lists are sorted. If not, */ /* make a copy and sort so we can save time in the lookups. */ flag = 0; /* Assume sorted. */ for (i=0; (i adjncy[ii+1]){ flag = 1; /* Not sorted. */ break; } } } if (flag){ /* Need to sort. */ adjncy_sort = (indextype *) ZOLTAN_MALLOC(nedges*sizeof(indextype)); perm = (int *) ZOLTAN_MALLOC(nedges*sizeof(int)); free_adjncy_sort = 1; if (nedges && (!adjncy_sort || !perm)){ /* Out of memory. */ ZOLTAN_PRINT_ERROR(proc, yo, "Out of memory."); runerr = ZOLTAN_MEMERR; } else { for (k=0; k1){ sprintf(msg, "Vertex " TPL_IDX_SPEC " has no edges.", global_i); ZOLTAN_PRINT_WARN(proc, yo, msg); } } for (ii=xadj[i]; ii= vtxdist[nprocs]))) || (IS_LOCAL_GRAPH(graph_type) && ((global_j < 0) || (global_j >= num_obj)))){ sprintf(msg, "Edge to invalid vertex " TPL_IDX_SPEC " detected.", global_j); ZOLTAN_PRINT_ERROR(proc, yo, msg); ierr = ZOLTAN_FATAL; } /* Self edge? */ if (global_j == global_i){ num_selfs++; if (output_level>1){ sprintf(msg, "Self edge for vertex " TPL_IDX_SPEC " detected.", global_i); ZOLTAN_PRINT_WARN(proc, yo, msg); } } /* Duplicate edge? */ if ((ii+11){ sprintf(msg, "Duplicate edge (" TPL_IDX_SPEC "," TPL_IDX_SPEC ") detected.", global_i, global_j); ZOLTAN_PRINT_WARN(proc, yo, msg); } } /* Is global_j a local vertex? */ if (IS_LOCAL_GRAPH(graph_type) || (IS_GLOBAL_GRAPH(graph_type) && (global_j >= vtxdist[proc]) && (global_j < vtxdist[proc+1]))){ /* Check if (global_j, global_i) is an edge */ if (IS_GLOBAL_GRAPH(graph_type)) j = global_j - vtxdist[proc]; else /* graph_type == LOCAL_GRAPH */ j = global_j; /* Binary search for edge (global_j, global_i) */ ptr = (indextype *)bsearch(&global_i, &adjncy_sort[xadj[j]], (int)(xadj[j+1]-xadj[j]), sizeof(indextype), Zoltan_Compare_Indextypes); if (ptr){ /* OK, found edge (global_j, global_i) */ if ((adjncy_sort==adjncy) && ewgt_dim){ /* Compare weights */ /* EBEB For now, don't compare weights if we sorted edge lists. */ flag = 0; for (k=0; k0){ sprintf(msg, "Graph is numerically nonsymmetric " "in edge (" TPL_IDX_SPEC "," TPL_IDX_SPEC ")", global_i, global_j); ZOLTAN_PRINT_WARN(proc, yo, msg); } } } else { /* bsearch failed */ sprintf(msg, "Graph is not symmetric. " "Edge (" TPL_IDX_SPEC "," TPL_IDX_SPEC ") exists, but no edge (" TPL_IDX_SPEC "," TPL_IDX_SPEC ").", global_i, global_j, global_j, global_i); ZOLTAN_PRINT_ERROR(proc, yo, msg); ierr = ZOLTAN_FATAL; } } else { cross_edges++; } } } /* Sum up warnings so far. */ MPI_Reduce(&num_selfs, &global_sum, 1, zoltan_gno_mpi_type, MPI_SUM, 0, comm); if ((proc==0) && (global_sum>0)){ if (ierr == ZOLTAN_OK) ierr = ZOLTAN_WARN; if (output_level>0){ sprintf(msg, ZOLTAN_GNO_SPEC " self-edges in graph.", global_sum); ZOLTAN_PRINT_WARN(proc, yo, msg); } } MPI_Reduce(&num_duplicates, &global_sum, 1, zoltan_gno_mpi_type, MPI_SUM, 0, comm); if ((proc==0) && (global_sum>0)){ if (ierr == ZOLTAN_OK) ierr = ZOLTAN_WARN; if (output_level>0){ sprintf(msg, ZOLTAN_GNO_SPEC " duplicate edges in graph.", global_sum); ZOLTAN_PRINT_WARN(proc, yo, msg); } } MPI_Reduce(&num_singletons, &global_sum, 1, zoltan_gno_mpi_type, MPI_SUM, 0, comm); if ((proc==0) && (global_sum>0)){ if (ierr == ZOLTAN_OK) ierr = ZOLTAN_WARN; if (output_level>0){ sprintf(msg, ZOLTAN_GNO_SPEC " vertices in the graph are singletons (have no edges).", global_sum); ZOLTAN_PRINT_WARN(proc, yo, msg); } } /* Check if any processor has encountered an error so far */ errors = 0; if (ierr == ZOLTAN_WARN) errors |= 1; if (ierr == ZOLTAN_MEMERR) errors |= 2; if (ierr == ZOLTAN_FATAL) errors |= 4; MPI_Allreduce(&errors, &global_errors, 1, MPI_INT, MPI_BOR, comm); if (global_errors & 4){ /* Fatal error: return now */ if (free_adjncy_sort) ZOLTAN_FREE(&adjncy_sort); if (free_adjncy_sort) ZOLTAN_FREE(&perm); return ZOLTAN_FATAL; } if ((IS_GLOBAL_GRAPH(graph_type)) && (check_graph >= 2)) { /* Test for consistency across processors. */ /* Allocate space for off-proc data */ mesg_size = (2*sizeof(indextype)) + (ewgt_dim * sizeof(weighttype)); sendgno = (indextype *) ZOLTAN_MALLOC(cross_edges*mesg_size); recvgno = (indextype *) ZOLTAN_MALLOC(cross_edges*mesg_size); proclist = (int *) ZOLTAN_MALLOC(cross_edges*sizeof(int)); if (cross_edges && !(sendgno && recvgno && proclist)){ ZOLTAN_PRINT_ERROR(proc, yo, "Out of memory."); runerr = ZOLTAN_MEMERR; } else { /* Second pass: Copy data to send buffer */ nedges = 0; ptr1 = (indextype *) sendgno; for (i=0; i= vtxdist[proc+1])){ /* Add to list */ k=0; while (global_j >= vtxdist[k+1]) k++; proclist[nedges++] = k; /* Copy (global_i,global_j) and corresponding weights to sendgno */ *ptr1++ = global_i; *ptr1++ = global_j; for (k=0; k0){ sprintf(msg, "Edge weight (" TPL_IDX_SPEC "," TPL_IDX_SPEC ") is not symmetric", ptr1[0], ptr1[1]); ZOLTAN_PRINT_WARN(proc, yo, msg); } } ptr2 = (indextype *)(recvwgt + ewgt_dim); } if (!flag){ sprintf(msg, "Graph is not symmetric. " "Edge (" TPL_IDX_SPEC "," TPL_IDX_SPEC ") exists, but not (" TPL_IDX_SPEC "," TPL_IDX_SPEC ").", ptr1[0], ptr1[1], ptr1[1], ptr1[0]); ZOLTAN_PRINT_ERROR(proc, yo, msg); ierr = ZOLTAN_FATAL; } ptr1 = (indextype *)(sendwgt + ewgt_dim); } } } } /* Free memory */ ZOLTAN_FREE(&sendgno); ZOLTAN_FREE(&recvgno); ZOLTAN_FREE(&proclist); } if (free_adjncy_sort) ZOLTAN_FREE(&adjncy_sort); if (free_adjncy_sort) ZOLTAN_FREE(&perm); /* Compute global error code */ errors = 0; if (ierr == ZOLTAN_WARN) errors |= 1; if (ierr == ZOLTAN_MEMERR) errors |= 2; if (ierr == ZOLTAN_FATAL) errors |= 4; MPI_Allreduce(&errors, &global_errors, 1, MPI_INT, MPI_BOR, comm); if (global_errors & 4){ return ZOLTAN_FATAL; } else if (global_errors & 2){ return ZOLTAN_MEMERR; } else if (global_errors & 1){ return ZOLTAN_WARN; } else { if (proc==0 && output_level>0){ printf("ZOLTAN %s: The graph is valid with check_graph = %1d\n", yo, check_graph); } return ZOLTAN_OK; } } /* comparison routine for bsearch */ static int Zoltan_Compare_Indextypes(const void *key, const void *arg) { if ( *(indextype*) key > (*(indextype*) arg)) return 1; if ( *(indextype*) key < (*(indextype*) arg)) return -1; return 0; /* equal */ } #ifdef __cplusplus } /* closing bracket for extern "C" */ #endif