|
| 1 | +#include <math.h> |
| 2 | +#include "mex.h" |
| 3 | +#include <stdio.h> |
| 4 | +#include "cliquer.h" |
| 5 | + |
| 6 | +/* Output Arguments */ |
| 7 | +#define OUTPUT plhs[0] |
| 8 | + |
| 9 | +#if !defined(MAX) |
| 10 | +#define MAX(A, B) ((A) > (B) ? (A) : (B)) |
| 11 | +#endif |
| 12 | + |
| 13 | +#if !defined(MIN) |
| 14 | +#define MIN(A, B) ((A) < (B) ? (A) : (B)) |
| 15 | +#endif |
| 16 | + |
| 17 | +graph_t* MatrixToGraph(double *adjac_mtx_ptr, int iGraphOrder) |
| 18 | +{ |
| 19 | + int i, j, idx; |
| 20 | + graph_t *ptrGraph; |
| 21 | + |
| 22 | + /* Initialize the graph that we want to return. */ |
| 23 | + ptrGraph = graph_new(iGraphOrder); |
| 24 | + |
| 25 | + /* Loop through the adjacency matrix to create the graph. We assume |
| 26 | + * that the adjacency matrix is symmetric and only consider the super- |
| 27 | + * diagonals of the matrix. */ |
| 28 | + /* The indexing here seems flipped, but it's due to the translation |
| 29 | + * between MATLAB and C element ordering. */ |
| 30 | + for (j = 0; j < iGraphOrder; j++) |
| 31 | + for (i = j + 1; i < iGraphOrder; i++) |
| 32 | + { |
| 33 | + /* The matrix adj_mtx is stored as a 1-dimensional array, so |
| 34 | + * we must convert the coordinate (i, j) to the corresponding |
| 35 | + * 1-dimensional index. */ |
| 36 | + idx = j + i * iGraphOrder; |
| 37 | + |
| 38 | + /* If the entry of the adjacency matrix is a 1, we want to add |
| 39 | + * an edge to our graph. */ |
| 40 | + if(adjac_mtx_ptr[idx] == 1) |
| 41 | + GRAPH_ADD_EDGE(ptrGraph, i, j); |
| 42 | + } |
| 43 | + |
| 44 | + /* Just to be cautios, ensure that we've produced a valid graph. */ |
| 45 | + ASSERT(graph_test(ptrGraph, NULL)); |
| 46 | + |
| 47 | + return ptrGraph; |
| 48 | +} |
| 49 | + |
| 50 | +int FindCliques(graph_t *ptrGraph, int iMinWeight, int iMaxWeight, |
| 51 | + int bOnlyMaximal, int iMaxNumCliques, set_t *ptrCliqueList, |
| 52 | + int iCliqueListLength) |
| 53 | +{ |
| 54 | + int iNumCliques; |
| 55 | + clique_options localopts; |
| 56 | + |
| 57 | + /* Set the clique_options. These fields should all be null except |
| 58 | + * 'clique_list' and 'clique_list_length', which store the list of |
| 59 | + * cliques and the maximum length of the list of cliques, respectively. */ |
| 60 | + localopts.time_function = NULL; |
| 61 | + localopts.reorder_function = NULL; |
| 62 | + localopts.reorder_map = NULL; |
| 63 | + localopts.clique_list = ptrCliqueList; |
| 64 | + localopts.clique_list_length = iCliqueListLength; |
| 65 | + localopts.user_function = NULL; |
| 66 | + localopts.user_data = NULL; |
| 67 | + |
| 68 | + /* Find all of the maximal (argument 4) cliques in this graph of |
| 69 | + * minimum size 1 (argument 2) and no maximum size (argument 3). */ |
| 70 | + iNumCliques = clique_find_all(ptrGraph, iMinWeight, iMaxWeight, bOnlyMaximal, |
| 71 | + &localopts); |
| 72 | + |
| 73 | + return iNumCliques; |
| 74 | +} |
| 75 | + |
| 76 | +/** |
| 77 | + * Find all of the maximal cliques in the provided matlab matrix, which |
| 78 | + * should represent an adjacency matrix. Most of this function just |
| 79 | + * translates from MATLAB input for processing with Cliquer and then |
| 80 | + * translates the output of Cliquer back into MATLAB data. |
| 81 | + * |
| 82 | + * The MATLAB arguments are as follows: |
| 83 | + * prhs[0] mtxAdj The input adjacency matrix. Only the upper- |
| 84 | + * triangular part of this is used. |
| 85 | + * prhs[1] iMinWeight The minimum weight of cliques to find. |
| 86 | + * prhs[2] iMaxWeight The maximum weight of cliques to find. |
| 87 | + * prhs[3] bOnlyMaximal If `false` (i.e., zero), return all cliques; |
| 88 | + * otherwise, return only maximal cliques. |
| 89 | + * prhs[4] iMaxNumCliques The maximum number of cliques to be returned |
| 90 | + * (due to a [perceived] limitation of Cliquer). |
| 91 | + */ |
| 92 | +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) |
| 93 | +{ |
| 94 | + /* Ensure that an appropriate number of arguments were provided and that an |
| 95 | + * an appropriate number of return variables were requested. */ |
| 96 | + if (nrhs != 5) |
| 97 | + mexErrMsgTxt("Exactly five input arguments are required."); |
| 98 | + else if (nlhs > 2) |
| 99 | + mexErrMsgTxt("Too many output arguments were requested."); |
| 100 | + |
| 101 | + /* Retrieve and store the size of the input adjacency matrix for size |
| 102 | + * checking and later use. */ |
| 103 | + mwSize iRows = mxGetM(prhs[0]); |
| 104 | + mwSize iCols = mxGetN(prhs[0]); |
| 105 | + |
| 106 | + /* Perform size- and type-checking on the input values. */ |
| 107 | + if ((iRows != iCols) || !mxIsNumeric(prhs[0]) || mxIsComplex(prhs[0])) |
| 108 | + mexErrMsgTxt("The first argument must be an integer-valued square matrix."); |
| 109 | + else if (mxGetM(prhs[1]) != 1 || mxGetN(prhs[1]) != 1 || !mxIsNumeric(prhs[1])) |
| 110 | + mexErrMsgTxt("The second argument must be an integer."); |
| 111 | + else if (mxGetM(prhs[2]) != 1 || mxGetN(prhs[2]) != 1 || !mxIsNumeric(prhs[2])) |
| 112 | + mexErrMsgTxt("The third argument must be an integer."); |
| 113 | + else if (mxGetM(prhs[3]) != 1 || mxGetN(prhs[3]) != 1 || !mxIsLogical(prhs[3])) |
| 114 | + mexErrMsgTxt("The fourth argument must be a logical scalar."); |
| 115 | + else if (mxGetM(prhs[4]) != 1 || mxGetN(prhs[4]) != 1 || !mxIsNumeric(prhs[4])) |
| 116 | + mexErrMsgTxt("The fifth argument must be an integer."); |
| 117 | + |
| 118 | + /* Declare variables to hold the input arguments (except the first). Each |
| 119 | + * of these can be retrieved by indexing `prhs`. */ |
| 120 | + int iMinWeight = MAX(0, (int) mxGetScalar(prhs[1])); |
| 121 | + int iMaxWeight = MIN(iCols, (int) mxGetScalar(prhs[2])); |
| 122 | + int bOnlyMaximal = (mxGetScalar(prhs[3]) == 0) ? FALSE : TRUE; |
| 123 | + int iMaxNumCliques = (int) mxGetScalar(prhs[4]); |
| 124 | + |
| 125 | + /* These variables are for storing the graph corresponding to the input |
| 126 | + * adjacency matrix, the list of cliques found in that graph, and the return |
| 127 | + * value for this MATLAB function. */ |
| 128 | + graph_t *ptrGraph; |
| 129 | + set_t arrCliqueList[iMaxNumCliques]; |
| 130 | + double *ptrOutputMatrix; |
| 131 | + |
| 132 | + /* Miscellaneous variable declarations. */ |
| 133 | + int i, j, idx, iNumCliques, iNumCliquesReturned; |
| 134 | + |
| 135 | + /* Create a graph from the adjacency matrix `prhs[0]`. */ |
| 136 | + ptrGraph = MatrixToGraph(mxGetPr(prhs[0]), iCols); |
| 137 | + |
| 138 | + /* Find the cliques in the associated graph. */ |
| 139 | + iNumCliques = FindCliques(ptrGraph, iMinWeight, iMaxWeight, bOnlyMaximal, |
| 140 | + iMaxNumCliques, arrCliqueList, iMaxNumCliques); |
| 141 | + |
| 142 | + /* We are done with the graph. Free the memory used to store it. */ |
| 143 | + graph_free(ptrGraph); |
| 144 | + |
| 145 | + /* Retrieve the number of cliques returned by the function, which is bounded |
| 146 | + * above by `iMaxNumCliques`. */ |
| 147 | + iNumCliquesReturned = MIN(iNumCliques, iMaxNumCliques); |
| 148 | + |
| 149 | + /* Output the total number of cliques found. */ |
| 150 | + plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL); |
| 151 | + mxGetPr(plhs[0])[0] = iNumCliques; |
| 152 | + |
| 153 | + /* Create the output matrix, which will have one row for each clique and |
| 154 | + * one column for each node of the graph. */ |
| 155 | + plhs[1] = mxCreateDoubleMatrix(iNumCliquesReturned, iCols, mxREAL); |
| 156 | + ptrOutputMatrix = mxGetPr(plhs[1]); |
| 157 | + |
| 158 | + /* Fill in the rows of the output matrix by looping through the cliques |
| 159 | + * that were found. */ |
| 160 | + for (i = 0; i < iNumCliquesReturned; i++) |
| 161 | + { |
| 162 | + /* Fill in the entries of this row by looping through the corresponding |
| 163 | + * clique to find the vertices contained in the clique. */ |
| 164 | + for (j = 0; j < SET_MAX_SIZE(arrCliqueList[i]); j++) |
| 165 | + { |
| 166 | + /* The entries of the output matrix are initialized to zeros. If |
| 167 | + * the vertex 'j' is in clique 'i', place a 1 in the (i, j) entry |
| 168 | + * of the output matrix. */ |
| 169 | + if (SET_CONTAINS(arrCliqueList[i], j)) |
| 170 | + { |
| 171 | + /* Matrices in Matlab are stored column-wise (i.e., the columns |
| 172 | + * of the matrix are stacked and stored as a column vector); so, |
| 173 | + * we must access the output as a 1-dimensional array. The index |
| 174 | + * of the entry corresponding to the (i, j) position in this |
| 175 | + * matrix is calculated in 'idx' below. */ |
| 176 | + idx = j * iNumCliques + i; |
| 177 | + ptrOutputMatrix[idx] = 1; |
| 178 | + } |
| 179 | + } |
| 180 | + |
| 181 | + /* Now that we've stored this clique as a row in a matrix, we can free |
| 182 | + * the memory used to store the clique. */ |
| 183 | + set_free(arrCliqueList[i]); |
| 184 | + } |
| 185 | + |
| 186 | + return; |
| 187 | +} |
0 commit comments