/****************************************************************************** * * LHMain.c * * Author: Nicholas Harvey * * Implements all LHMatch API functions and other functions that are common to * all LHMatch algorithms. * ******************************************************************************/ /***** Header Files *****/ #include #include #include #include #include "LHMatchInt.h" /***** Constants *****/ /* MIN_ADJ_LIST: The minimum size of a Vertex's adjacency list */ #define MIN_ADJ_LIST 4 /***** Globals *****/ #ifdef DBG int gDebugPrint=0; #endif /***** CheckGraph *****/ /* Verify that a graph structure we were passed is okay */ Graph* CheckGraph( LHGRAPH graph ) { Graph* g = (Graph*) graph; if( g==NULL || g->magic1!=MAGIC1 || g->magic2!=MAGIC2 ) { return NULL; } return g; } /***** AddVtxToBucket *****/ void AddVtxToBucket(Graph *g, Vertex *v, int b) { v->fLink = g->Buckets[b]; v->bLink = NULL; if(g->Buckets[b]) g->Buckets[b]->bLink=v; g->Buckets[b] = v; } /***** RemoveVtxFromBucket *****/ void RemoveVtxFromBucket(Graph *g, Vertex *v, int b) { if(v->fLink) { v->fLink->bLink = v->bLink; } if(v->bLink) { v->bLink->fLink = v->fLink; } else { g->Buckets[b] = v->fLink; } } /***** DestroyBuckets *****/ void DestroyBuckets(Graph *g) { free(g->Buckets); g->Buckets = NULL; } /***** InitializeLHSBuckets *****/ static int InitializeLHSBuckets(Graph *g) { Vertex *vtx=g->lVtx; int i, maxLHSDegree, numLHSVtx=g->numLHSVtx; /* Find degree of LHS vertices */ maxLHSDegree=0; for(i=0;iBuckets = (Vertex**) calloc(maxLHSDegree+1, sizeof(Vertex*)); if( NULL==g->Buckets ) { return LH_MEM_ERR; } /* Add LHS vertices to their buckets */ for(i=0;i0 ) { AddVtxToBucket(g, &vtx[i], vtx[i].degree); } } return maxLHSDegree; } /***** OrderedGreedyAssignment *****/ /* Use a greedy approach to find an initial assignment. Examine LHS * vertices in order of their degree. */ int OrderedGreedyAssignment(Graph *g) { int b,j, maxLHSDegree; Vertex *u, *r,*bestR; maxLHSDegree = InitializeLHSBuckets(g); if( maxLHSDegree<0 ) { /* Error occurred */ return maxLHSDegree; } /* Examine each bucket */ for(b=1;b<=maxLHSDegree;b++) { /* And each LHS vertex in the bucket */ for( u=g->Buckets[b]; u; u=u->fLink ) { /* If u has already been matched, skip it */ if( NULL!=u->matchedWith ) continue; /* Find right-hand neighbour with lowest matching-degree */ bestR = u->adjList[0]; for(j=1;jdegree;j++) { r=u->adjList[j]; #ifdef WORST_GREEDY if(r->numMatched>bestR->numMatched) bestR=r; #else if(r->numMatchednumMatched) bestR=r; #endif } /* Assign LHS vertex to lowest matching-degree RHS vertex */ u->matchedWith = bestR; u->numMatched = 1; bestR->numMatched++; } } DestroyBuckets(g); return LH_SUCCESS; } /***** GreedyAssignment *****/ /* Simple Greedy Assignment */ void GreedyAssignment(Graph *g) { int i,j; Vertex *r,*bestR,*vtx=g->lVtx; /* For each LHS vertex */ for(i=0;inumLHSVtx;i++) { /* Find right-hand neighbour with lowest matching-degree */ bestR = vtx[i].adjList[0]; for(j=1;jnumMatchednumMatched) bestR=r; } /* Assign LHS vertex to lowest matching-degree RHS vertex */ vtx[i].matchedWith = bestR; vtx[i].numMatched = 1; bestR->numMatched++; } } /***** InitializeRHSBuckets *****/ /* Insert all RHS vertices into buckets. Their matched-degree * (i.e. numMatched) determines what bucket they live in. */ int InitializeRHSBuckets(Graph *g) { Vertex *vtx=g->rVtx; int i, maxRHSLoad, numRHSVtx=g->numRHSVtx; /* Find maximum M-degree of RHS vertices */ maxRHSLoad=0; for(i=0;imaxRHSLoad = maxRHSLoad; /* Allocate array of buckets */ g->Buckets = (Vertex**) calloc(maxRHSLoad+1, sizeof(Vertex*)); if( NULL==g->Buckets ) { return LH_MEM_ERR; } /* Add RHS vertices to their buckets */ for(i=0;i0 ) { AddVtxToBucket( g, &vtx[i], vtx[i].numMatched ); } } return LH_SUCCESS; } /***** InitializeQueue *****/ int InitializeQueue(Graph *g) { g->Qsize=0; g->Queue=(Vertex**) malloc((g->numLHSVtx+g->numRHSVtx)*sizeof(Vertex*)); if(NULL==g->Queue) { return LH_MEM_ERR; } return LH_SUCCESS; } /***** DestroyQueue *****/ void DestroyQueue(Graph *g) { free(g->Queue); g->Queue=NULL; g->Qsize=0; } /***** ClearVertex *****/ /* Clear one vertex */ static void ClearVertex(Vertex *v) { v->parent = NULL; v->fLink = NULL; v->bLink = NULL; } /***** ClearAlgState *****/ /* Clears all the internal algorithm state from the graph. * Note: Does not affect the edges or the matching */ void ClearAlgState(Graph *g) { int i; /* Clear state that is stored in the graph structure */ g->maxRHSLoad = 0; g->minRHSLoad = 0; g->Qsize = 0; /* Clear state that is stored in the vertices */ for(i=0;inumLHSVtx;i++) { ClearVertex(&(g->lVtx[i])); } for(i=0;inumRHSVtx;i++) { ClearVertex(&(g->rVtx[i])); } } /***** DumpGraph *****/ void DumpGraph(Graph *g) { int i,j,m=0; /* Count # edges */ for(i=0;inumLHSVtx;i++) { m+=g->lVtx[i].degree; } printf("--- Dumping graph---\n"); printf("|lhs|=%d, |rhs|=%d |edge|=%d\n", g->numLHSVtx, g->numRHSVtx, m); /* Dump each LHS vertex and its list of neighbours */ for(i=0;inumLHSVtx;i++) { printf("LHS Vtx %02d: Match=%d, Deg=%d, Nbr=[ ", i, g->lVtx[i].matchedWith ? g->lVtx[i].matchedWith->id : -1, g->lVtx[i].degree ); for(j=0;jlVtx[i].degree;j++) { printf("%d ", g->lVtx[i].adjList[j]->id); } printf("]\n"); } /* Dump each RHS vertex and its list of neighbours */ for(i=0;inumRHSVtx;i++) { printf("RHS Vtx %02d: NumMatch=%d, Deg=%d, Nbr=[ ", i+g->numLHSVtx, g->rVtx[i].numMatched, g->rVtx[i].degree ); for(j=0;jrVtx[i].degree;j++) { printf("%d ", g->rVtx[i].adjList[j]->id); } printf("]\n"); } printf("--- Finished dumping graph---\n"); } /***** DumpLoad *****/ void DumpLoad(Graph *g) { int i; printf("--- Dumping load ---\n"); for(i=0;inumRHSVtx;i++) { printf("RHS Vtx %02d: |M-nbrs|=%d\n",g->rVtx[i].id,g->rVtx[i].numMatched); } printf("--- Finished dumping load ---\n"); } /***** LHCreateGraph *****/ /* * Description: * * Create a graph structure on which which to compute the matching. * * Parameters: * * IN numLHSVtx The number of vertices on the left-hand side of * the bipartite graph. Must be > 0. * * IN numRHSVtx The number of vertices on the right-hand side of * the bipartite graph. Must be > 0. * * OUT pGraph On successful completion, pGraph will contain a * valid graph structure. * * Return Value: * * Error Code */ int LHCreateGraph( int numLHSVtx, int numRHSVtx, LHGRAPH* pGraph ) { Graph *g; int i; /* Check parameters */ if( numLHSVtx<=0 || numRHSVtx<=0 || NULL==pGraph) { return LH_PARAM_ERR; } /* Allocate the graph structure */ g = (Graph*) calloc( 1, sizeof(Graph) ); if( NULL==g ) { return LH_MEM_ERR; } /* Allocate the LHS vertices */ g->lVtx = (Vertex*) calloc( numLHSVtx, sizeof(Vertex) ); if( NULL==g->lVtx ) { free(g); return LH_MEM_ERR; } /* Allocate the RHS vertices */ g->rVtx = (Vertex*) calloc( numRHSVtx, sizeof(Vertex) ); if( NULL==g->rVtx ) { free(g->lVtx); free(g); return LH_MEM_ERR; } /* Initialize the vertices */ for(i=0;ilVtx[i].id = i; } for(i=0;irVtx[i].id = i+numLHSVtx; } /* Initialize the graph structure */ g->numLHSVtx = numLHSVtx; g->numRHSVtx = numRHSVtx; g->magic1 = MAGIC1; g->magic2 = MAGIC2; /* Return the graph */ *pGraph = g; return LH_SUCCESS; } /***** GrowAdjList *****/ int GrowAdjList( Vertex *v, int newSize ) { Vertex **newAdjList; /* If we have no list, make the initial allocation */ if( 0==v->adjListSize ) { v->adjList = (Vertex**) malloc( sizeof(Vertex*)*MIN_ADJ_LIST ); if( NULL==v->adjList ) { return LH_MEM_ERR; } v->adjListSize = MIN_ADJ_LIST; } /* Check to see if we already have enough space in the list */ while( newSize>v->adjListSize ) { /* Not enough: double the size of the list */ newAdjList = (Vertex**) realloc( v->adjList, sizeof(Vertex*)*(v->adjListSize*2) ); if( NULL==newAdjList ) { return LH_MEM_ERR; } v->adjList = newAdjList; v->adjListSize *= 2; } return LH_SUCCESS; } /***** LHAddEdge *****/ /* * Description: * * Add an edge to the graph connecting lhsVtx and rhsVtx * * Parameters: * * IN graph A graph successfully created by LHGraphCreate. * * IN lhsVtx The ID of the left-hand vertex. Legal values are * 0 <= lhsVtx < numLHSVtx * * IN rhsVtx The ID of the right-hand vertex. Legal values are * 0 <= rhsVtx < numRHSVtx * * Return Value: * * Error Code */ int LHAddEdge( LHGRAPH graph, int lhsVtx, int rhsVtx ) { Graph *g; Vertex *lv, *rv; int i; /* Check parameters */ g = CheckGraph(graph); if( NULL==g ) { return LH_PARAM_ERR; } if( lhsVtx<0 || lhsVtx>=g->numLHSVtx ) { return LH_PARAM_ERR; } if( rhsVtx<0 || rhsVtx>=g->numRHSVtx ) { return LH_PARAM_ERR; } /* Get pointers to the two vertices */ lv = &g->lVtx[lhsVtx]; rv = &g->rVtx[rhsVtx]; /* Check if edge already exists */ for( i=0; idegree; i++ ) { if( lv->adjList[i]==rv ) { return LH_EDGE_EXISTS; } } /* Increase the size of the adjacency lists */ if( LH_SUCCESS!=GrowAdjList(lv,lv->degree+1) ) { return LH_MEM_ERR; } if( LH_SUCCESS!=GrowAdjList(rv,rv->degree+1) ) { return LH_MEM_ERR; } /* Update the adjacency lists */ lv->adjList[ lv->degree++ ] = rv; rv->adjList[ rv->degree++ ] = lv; return LH_SUCCESS; } /***** LHSetMatchingEdge *****/ /* * Description: * * Set the edge connecting lhsVtx and rhsVtx in the graph to be * a matching edge. The edge (lhsVtx,rhsVtx) must have already been * created with a call to LHVtxAddEdge. * * Left-hand vertices may only have one matching edge. If lhsVtx * already has a matching edge, that edge will be demoted from a * matching edge to a normal edge. Right-hand vertices may have * multiple matching edges. * * Parameters: * * IN graph A graph successfully created by LHGraphCreate. * * IN lhsVtx The ID of the left-hand vertex. Legal values are * 0 <= lhsVtx < numLHSVtx * * IN rhsVtx The ID of the right-hand vertex. Legal values are * 0 <= rhsVtx < numRHSVtx * * Return Value: * * Error Code */ int LHSetMatchingEdge( LHGRAPH graph, int lhsVtx, int rhsVtx ) { Graph *g; Vertex *lv,*rv; int i; /* Check parameters */ g = CheckGraph(graph); if( NULL==g ) { return LH_PARAM_ERR; } if( lhsVtx<0 || lhsVtx>=g->numLHSVtx ) { return LH_PARAM_ERR; } if( rhsVtx<0 || rhsVtx>=g->numRHSVtx ) { return LH_PARAM_ERR; } /* Get pointers to the two vertices */ lv = &g->lVtx[lhsVtx]; rv = &g->rVtx[rhsVtx]; /* Verify that the edge exists */ for(i=0;idegree;i++) { if(lv->adjList[i]==rv) { break; } } if(i==lv->degree) { /* Edge does not exist */ return LH_PARAM_ERR; } /* Check if the edge is already a matching edge */ if( rv==lv->matchedWith ) { /* lv and rv are already matched */ return LH_SUCCESS; } /* If lv is already matched with another vertex, that vertex must be updated */ if( NULL!=lv->matchedWith ) { lv->matchedWith->numMatched--; } /* Match lv with rv */ lv->matchedWith=rv; rv->numMatched++; return LH_SUCCESS; } /***** LHGetDegree *****/ /* * Description: * * Get the degree (number of neighbours) of a vertex. * * Parameters: * * IN graph A graph successfully created by LHGraphCreate. * * IN vtxID The ID of the vertex to examine. * * IN left If the vertex to examine is a left-vertex, this * parameter should be TRUE. If the vertex is a right- * vertex, this parameter should be FALSE. * * Return Value: * * >=0 The number of neighbours * * <0 Error Code */ int LHGetDegree( LHGRAPH graph, int vtxID, char left ) { Graph *g; int degree; /* Check parameters */ g = CheckGraph(graph); if( NULL==g ) { return LH_PARAM_ERR; } if( vtxID<0 ) { return LH_PARAM_ERR; } if( left && vtxID>=g->numLHSVtx ) { return LH_PARAM_ERR; } if( !left && vtxID>=g->numRHSVtx ) { return LH_PARAM_ERR; } /* Find the degree of the specified vertex */ if( left ) { degree = g->lVtx[vtxID].degree; } else { degree = g->rVtx[vtxID].degree; } assert( degree>=0 ); return degree; } /***** LHGetMatchedDegree *****/ /* * Description: * * Get the matched-degree (number of matched neighbours) of a * right-hand vertex. * * Parameters: * * IN graph A graph successfully created by LHGraphCreate. * * IN vtxID The ID of the right hand vertex to examine. * * Return Value: * * >=0 The number of neighbours * * <0 Error Code */ int LHGetMatchedDegree( LHGRAPH graph, int vtxID) { Graph *g; int degree; /* Check parameters */ g = CheckGraph(graph); if( NULL==g ) { return LH_PARAM_ERR; } if( vtxID<0 ) { return LH_PARAM_ERR; } if( vtxID>=g->numRHSVtx ) { return LH_PARAM_ERR; } /* Find the degree of the specified vertex */ degree = g->rVtx[vtxID].numMatched; assert( degree>=0 ); return degree; } /***** LHGetNeighbour *****/ /* * Description: * * Get the n'th neighbour of the vertex specified by vtxID. * * Parameters: * * IN graph A graph successfully created by LHGraphCreate. * * IN vtxID The ID of the vertex to examine. * * IN left If the vertex to examine is a left-vertex, this * parameter should be TRUE. If the vertex is a right- * vertex, this parameter should be FALSE. * * IN n The index of the neighbour to retrieve. Legal values * are 0 <= n < Degree(vtxID). * * Return Value: * * >=0 The vertex ID of the n'th neighbour. If 'left' is TRUE, * this ID refers to a right-hand vertex. Conversely, if * 'left' is FALSE, this ID refers to a left-hand vertex. * * <0 Error Code */ int LHGetNeighbour( LHGRAPH graph, int vtxID, char left, int n ) { Graph *g; int degree; Vertex* v; /* Leverage the LHGetDegree function to do most of the input validation */ g = CheckGraph(graph); degree = LHGetDegree( graph, vtxID, left ); if( degree < 0 ) { return degree; } /* Check parameter n */ if( n<0 || n>=degree ) { return LH_PARAM_ERR; } if( left ) { v = g->lVtx[vtxID].adjList[n]; assert(v); /* Transform internal ID to external ID */ return (v->id - g->numLHSVtx); } else { v = g->rVtx[vtxID].adjList[n]; assert(v); return v->id; } } /***** LHFindLHMatching *****/ /* * Description: * * Find an optimal matching in the graph. * * Parameters: * * IN graph A graph successfully created by LHGraphCreate, * to which edges have been added using LHAddEdge. * * IN alg Specifies which algorithm to use. For most purposes, * LH_ALG_DEFAULT is fine. * * Return Value: * * Error Code */ int LHFindLHMatching( LHGRAPH graph, LHALGTYPE alg ) { Graph *g; g = CheckGraph(graph); if( NULL==g ) { return LH_PARAM_ERR; } ClearAlgState(g); switch( alg ) { case LH_ALG_ONLINE: return LHAlgOnline(g); break; case LH_ALG_BFS: return LHAlgBFS(g); break; default: /* Invalid algorithm selection */ return LH_PARAM_ERR; } } /***** LHGetMatchedVtx *****/ /* * Description: * * Determine which vertex on the right-hand side is matched to a * given vertex on the left-hand side. * * Parameters: * * IN graph A graph successfully created by LHGraphCreate, * to which edges have been added using LHAddEdge. * * IN lhsVtx The left-hand vertex to query. * * Return Value: * * >=0 The index of the right-hand vertex that is matched * with lhsVtx. * * <0 Error Code. If LH_MATCHING_ERR is returned, then * lhsVtx is not matched with any right-hand vertex. */ int LHGetMatchedVtx( LHGRAPH graph, int lhsVtx ) { Graph *g; Vertex *lv,*rv; /* Check parameters */ g = CheckGraph(graph); if( NULL==g ) { return LH_PARAM_ERR; } if( lhsVtx<0 || lhsVtx>=g->numLHSVtx ) { return LH_PARAM_ERR; } /* Get pointer to left-hand vertex */ lv = &(g->lVtx[lhsVtx]); /* Find matching partner */ rv = lv->matchedWith; if( rv==NULL ) { return LH_MATCHING_ERR; } /* Transform internal ID to external ID */ return (rv->id - g->numLHSVtx); } /***** LHGetStatistics *****/ /* * Description: * * Obtain statistics about the current matching. * * Parameters: * * IN graph A graph for which an optimal LH Matching has * been computed using LHFindLHMatching(). * * Return Value: * * Error Code */ int LHGetStatistics( LHGRAPH graph, LHSTATS *stats ) { Graph *g; int i, m=0, mrv=0, trmd=0, cost=0; /* Check parameters */ g = CheckGraph(graph); if( NULL==g || NULL==stats ) { return LH_PARAM_ERR; } /* Loop over all right-hand vertices */ for(i=0;inumRHSVtx;i++) { m+=g->rVtx[i].degree; if(g->rVtx[i].numMatched>0) mrv++; trmd+=g->rVtx[i].numMatched; cost+=g->rVtx[i].numMatched*(g->rVtx[i].numMatched+1)/2; } stats->numEdges = m; stats->numMatchingEdges = trmd; stats->matchingCost = cost; stats->bipMatchingSize = mrv; return LH_SUCCESS; } /***** LHClearMatching *****/ /* * Description: * * Clear the current matching. * * Parameters: * * IN graph A graph successfully created by LHGraphCreate, * to which edges have been added using LHAddEdge. * * Return Value: * * Error Code */ int LHClearMatching( LHGRAPH graph ) { Graph *g; int i; /* Check parameters */ g = CheckGraph(graph); if( NULL==g ) { return LH_PARAM_ERR; } /* Clear left-hand vertices */ for( i=0; inumLHSVtx; i++ ) { g->lVtx[i].matchedWith=NULL; } /* Clear right-hand vertices */ for( i=0; inumRHSVtx; i++ ) { g->rVtx[i].numMatched=0; } return LH_SUCCESS; } /***** LHDestroyGraph *****/ /* * Description: * * Destroy the current graph. * * Parameters: * * IN graph A graph successfully created by LHGraphCreate. * * Return Value: * * Error Code */ int LHDestroyGraph( LHGRAPH graph ) { Graph *g; int i; /* Check parameters */ g = CheckGraph(graph); if( NULL==g ) { return LH_PARAM_ERR; } /* Free each vertex's adjacency list */ for(i=0;inumLHSVtx;i++) { free( g->lVtx[i].adjList ); } for(i=0;inumRHSVtx;i++) { free( g->rVtx[i].adjList ); } /* Free all other lists */ if( g->lVtx ) free( g->lVtx ); if( g->rVtx ) free( g->rVtx ); if( g->Buckets ) free( g->Buckets ); if( g->Queue ) free( g->Queue ); /* Clear and free the graph itself */ memset(graph,0,sizeof(Graph)); free(graph); return LH_SUCCESS; }