Skip to content
Snippets Groups Projects
testHelper_DisconnectedMatrix.c 14.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • #include "testHelper_DisconnectedMatrix.h"
    
    #define min(x,y) ( ( (x)<(y) ) ? (x) : (y) )
    #define max(x,y) ( ( (x)>(y) ) ? (x) : (y) )
    
    
    /**
     * Construct a random (disconnected) matrix.
     * 
     * Input:
     * symmetric         : Whether the matrix should be symmetric
     * dummies           : Whether the matrix should contain dummy diagonal nonzeros
     * colWeights        : Whether the matrix should be weighted with column weights
     * numCompMin        : Minimum number of connected components
     * numCompMax        : Maximum number of connected components
     * 
     * Output:
     * pA                : The generated matrix
     * pNumComponents    : The number of components generated
     * pComponent_m      : Height of each connected component
     * pComponent_n      : Width of each connected component
     * pComponent_weights: Weight of each connected component (effective number of nonzeros or sum of column weights)
     * p_i_to_I          : I = p_i_to_I[cmpnnt][i] is the row index I in the large matrix which corresponds to the row index i in component cmpnnt
     * p_j_to_J          : J = p_j_to_J[cmpnnt][j] is the column index J in the large matrix which corresponds to the column index j in component cmpnnt
     * 
     * Returned memory allocations (to be freed with DestructDisconnectedMatrix()):
     *     pA:
     *         pA->i             O(pA->m)
     *         pA->j             O(pA->n)
     *         pA->Pstart        O(2+1)
     *         pA->dummy         O(pA->m)            (only if dummy == TRUE)
     *         pA->ColWeights    O(pA->n)            (only if colWeights == TRUE)
     *     pComponent_m:         O(numComponents)
     *     pComponent_n:         O(numComponents)    (only if symmetric == FALSE)
     *     pComponent_weights:   O(numComponents)
     *     p_i_to_I:             O(numComponents)
     *     p_i_to_I[0]:          O(pA->m)
     *     p_j_to_J:             O(numComponents)    (only if symmetric == FALSE)
     *     p_j_to_J[0]:          O(pA->n)            (only if symmetric == FALSE)
     */
    void ConstructDisconnectedMatrix(struct sparsematrix *pA, int symmetric, int dummies, int colWeights,
                                     long numCompMin, long numCompMax, long *pNumComponents,
                                     long **pComponent_m, long **pComponent_n, long **pComponent_weights,
                                     long ***p_i_to_I, long ***p_j_to_J) {
        
        long i, j, I, J, m, n;
        long cmpnnt;
        
        /* Determine number of components (submatrices), the dimensions of each submatrix, and their numbers of nonzeros */
        long numComponents = Random1(numCompMin, numCompMax);
        long *component_dummies = (long *)malloc(numComponents*sizeof(long));
        long *component_m = (long *)malloc(numComponents*sizeof(long));
        long *component_n;
        if(symmetric || dummies) {
            component_n = component_m;
        }
        else {
            component_n = (long *)malloc(numComponents*sizeof(long));
        }
        long *component_nnz = (long *)malloc(numComponents*sizeof(long));
        
        if (component_dummies == NULL || component_m  == NULL || component_n == NULL || component_nnz  == NULL) {
            printf("Error\n");
            exit(1);
        }
        
        long M=0, N=0, NNZ=0, totalDummies = 0;
        for(cmpnnt=0; cmpnnt<numComponents; ++cmpnnt) {
            
            /* component_nnz[] should uniquely identify a component; i.e. all components should have different nnz.
             * This helps when identifying the components in test functions.
             */
            int present;
            long nonDummyDiagNonZeros[2];
            long full_nnz[2];
            
            do {
                /* The dimensions should at least be above 10, such that generating the
                 * matrices won't take too long (or, when below 5, may become impossible).
                 * Additionally, we should have enough freedom to ensure each component has
                 * a unique number of nonzeros
                 */
                component_m[cmpnnt] = Random1(14, 20) + Random1(numComponents, numCompMax);
                if(!symmetric && !dummies) {
                    component_n[cmpnnt] = Random1(14, 20) + Random1(numComponents, numCompMax);
                }
                long maxdim = (component_m[cmpnnt]>component_n[cmpnnt]) ? component_m[cmpnnt] : component_n[cmpnnt];
                
                /* Determine number of nonzeros */
                if(symmetric) {
                    component_nnz[cmpnnt] = 2*maxdim-1 + Random1(maxdim/2, maxdim);
                }
                else {
                    component_nnz[cmpnnt] = 2*maxdim-1 + Random1(maxdim, 2*maxdim);
                }
                
                component_dummies[cmpnnt] = dummies ? Random1(maxdim/4, 3*maxdim/4) : 0;
                component_nnz[cmpnnt] -= component_dummies[cmpnnt];
                
                /* Check that this number of non-dummy nonzeros is unique */
                present = 0;
                for(i=0; i<cmpnnt; ++i) {
                    if(symmetric) {
                        nonDummyDiagNonZeros[0] = component_m[cmpnnt] - component_dummies[cmpnnt];
                        full_nnz[0] = (component_nnz[cmpnnt]-nonDummyDiagNonZeros[0])*2 + nonDummyDiagNonZeros[0];
                        
                        nonDummyDiagNonZeros[1] = component_m[i] - component_dummies[i];
                        full_nnz[1] = (component_nnz[i]-nonDummyDiagNonZeros[1])*2 + nonDummyDiagNonZeros[1];
                        
                        if(full_nnz[0] == full_nnz[1]) {
                            present = 1;
                            break;
                        }
                    }
                    else {
                        
                        if(component_nnz[i] == component_nnz[cmpnnt]) {
                            present = 1;
                            break;
                        }
                    }
                }
            }
            while(present);
            
            /* Update totals */
            M += component_m[cmpnnt];
            N += component_n[cmpnnt];
            NNZ += component_nnz[cmpnnt];
            totalDummies += component_dummies[cmpnnt];
        }
        
        /* Allocate space for a mapping from submatrix indices (i,j) to global indices (I,J) */
        long *i_to_I_alloc, *j_to_J_alloc, **i_to_I, **j_to_J;
        i_to_I_alloc = (long *)malloc(M*sizeof(long));
        i_to_I = (long **)malloc(numComponents*sizeof(long *));
        if(symmetric || dummies) {
            j_to_J_alloc = i_to_I_alloc;
            j_to_J = i_to_I;
        }
        else {
            j_to_J_alloc = (long *)malloc(N*sizeof(long));
            j_to_J = (long **)malloc(numComponents*sizeof(long *));
        }
        
        if (i_to_I_alloc == NULL || i_to_I  == NULL || j_to_J_alloc == NULL || j_to_J  == NULL) {
            printf("Error\n");
            exit(1);
        }
        
        i_to_I[0] = i_to_I_alloc;
        if(!symmetric && !dummies)
            j_to_J[0] = j_to_J_alloc;
        for(cmpnnt=1; cmpnnt<numComponents; ++cmpnnt) {
            i_to_I[cmpnnt] = &i_to_I[cmpnnt-1][component_m[cmpnnt-1]];
            if(!symmetric && !dummies)
                j_to_J[cmpnnt] = &j_to_J[cmpnnt-1][component_n[cmpnnt-1]];
        }
        
        /* Create a permutation of the identity */
        for(I=0; I<M; ++I) {
            i_to_I_alloc[I] = I;
        }
        if(!symmetric && !dummies) {
            for(J=0; J<N; ++J) {
                j_to_J_alloc[J] = J;
            }
        }
        
        RandomPermute(i_to_I_alloc, NULL, NULL, NULL, 0, M-1);
        if(!symmetric && !dummies)
            RandomPermute(j_to_J_alloc, NULL, NULL, NULL, 0, N-1);
        
        /* Set up matrix struct */
        MMSparseMatrixInit(pA);
        pA->m = M;
        pA->n = N;
        pA->NrNzElts = NNZ + totalDummies;
        pA->NrProcs = 2; /* maximum number of parts */
    
        pA->i = (long *)malloc(pA->NrNzElts*sizeof(long));
        pA->j = (long *)malloc(pA->NrNzElts*sizeof(long));
        pA->Pstart = (long *)malloc((pA->NrProcs+1)*sizeof(long));
    
        pA->RowLambda = (int *)malloc(pA->m*sizeof(int));
        pA->ColLambda = (int *)malloc(pA->n*sizeof(int));
    
        if (pA->i == NULL || pA->j == NULL || pA->Pstart == NULL || pA->RowLambda == NULL || pA->ColLambda == NULL) {
    
            printf("Error\n");
            exit(1);
        }
        
        if(colWeights)
            pA->MMTypeCode[0]='W'; /* weighted matrix */
        else
            pA->MMTypeCode[0]='M'; /* normal matrix */
        pA->MMTypeCode[1]='C'; /* coordinate scheme */
        pA->MMTypeCode[2]='P'; /* pattern only */
        if(symmetric)
            pA->MMTypeCode[3]='S'; /* symmetric */
        else
            pA->MMTypeCode[3]='G'; /* general, no symmetry */
        pA->NrDummies = 0;
        pA->dummy = NULL;
        pA->Pstart[0] = 0;
        pA->Pstart[1] = pA->NrNzElts;
        pA->Pstart[2] = pA->NrNzElts;
        pA->NrDummies = totalDummies;
        
        if(dummies) {
            pA->dummy = (int *)malloc(pA->m*sizeof(int));
            if (pA->dummy == NULL) {
                printf("Error\n");
                exit(1);
            }
        }
        
        /* Generate each component */
        long maxdim, t = 0, t2, T, nnzFilled, dummiesUsed;
        
        for(cmpnnt=0; cmpnnt<numComponents; ++cmpnnt) {
            m = component_m[cmpnnt];
            n = component_n[cmpnnt];
            maxdim = (m>n) ? m : n;
            nnzFilled = 0;
            dummiesUsed = 0;
            T = t;
            
            /* Make sure we create connected components */
            if(Random1(0,1) == 0) {
                /* Here, we generate a submatrix with nonzero diagonal
                 * and one nonzero subdiagonal. This way, each row is
                 * connected with the next, and hence all are connected.
                 */
                for(i=0; i<maxdim; ++i) {
                    I = i_to_I[cmpnnt][i%m];
                    J = j_to_J[cmpnnt][i%n];
                    pA->i[t] = symmetric ? max(I,J) : I;
                    pA->j[t] = symmetric ? min(I,J) : J;
                    ++t;
                    ++nnzFilled;
                    
                    if(dummies) {
                        pA->dummy[I] = (dummiesUsed < component_dummies[cmpnnt]) ? 1 : 0;
                        if(pA->dummy[I] == 1) {
                            ++dummiesUsed;
                            --nnzFilled;
                        }
                    }
                    
                    if(i < maxdim-1) {
                        I = i_to_I[cmpnnt][(i+1)%m];
                        J = j_to_J[cmpnnt][i%n];
                        pA->i[t] = symmetric ? max(I,J) : I;
                        pA->j[t] = symmetric ? min(I,J) : J;
                        ++t;
                        ++nnzFilled;
                    }
                }
            }
            else {
                /* Here, we generate a submatrix with nonzero diagonal
                 * and a dense first column. This way, all rows are
                 * connected with the first column, hence all rows and
                 * columns are connected.
                 */
                for(i=0; i<maxdim; ++i) {
                    I = i_to_I[cmpnnt][i%m];
                    J = j_to_J[cmpnnt][i%n];
                    pA->i[t] = symmetric ? max(I,J) : I;
                    pA->j[t] = symmetric ? min(I,J) : J;
                    ++t;
                    ++nnzFilled;
                    
                    if(dummies) {
                        pA->dummy[I] = (dummiesUsed < component_dummies[cmpnnt]) ? 1 : 0;
                        if(pA->dummy[I] == 1) {
                            ++dummiesUsed;
                            --nnzFilled;
                        }
                    }
                    
                    if(i > 0) {
                        I = i_to_I[cmpnnt][i%m];
                        J = j_to_J[cmpnnt][0];
                        pA->i[t] = symmetric ? max(I,J) : I;
                        pA->j[t] = symmetric ? min(I,J) : J;
                        ++t;
                        ++nnzFilled;
                    }
                }
            }
            
            /* Fill the submatrix until the desired number of nonzeros */
            while(nnzFilled < component_nnz[cmpnnt]) {
                i = Random1(0, m-1);
                j = Random1(0, n-1);
                I = i_to_I[cmpnnt][i%m];
                J = j_to_J[cmpnnt][j%n];
                
                I = symmetric ? max(I,J) : I;
                J = symmetric ? min(I,J) : J;
                
                /* Make sure this element is not already nonzero.
                 * This search is expensive in terms of complexity, but
                 * as we are just testing, this does not matter very much.
                 */
                int present = 0;
                for(t2=T; t2<t; ++t2) {
                    if(pA->i[t2] == I && pA->j[t2] == J) {
                        present = 1;
                        break;
                    }
                }
                if(present) {
                    continue;
                }
                
                pA->i[t] = I;
                pA->j[t] = J;
                ++t;
                ++nnzFilled;
            }
        }
        
        if(symmetric) {
            /* Update nonzero count, as we now only have counted the elements in the lower-left triangle */
            NNZ = 0;
            long nonDummyDiagNonZeros;
            for(cmpnnt=0; cmpnnt<numComponents; ++cmpnnt) {
                nonDummyDiagNonZeros = component_m[cmpnnt] - component_dummies[cmpnnt];
                component_nnz[cmpnnt] = (component_nnz[cmpnnt]-nonDummyDiagNonZeros)*2 + nonDummyDiagNonZeros;
                NNZ += component_nnz[cmpnnt];
            }
        }
        
        /* Return value pointers */
        *pNumComponents = numComponents;
        *pComponent_m = component_m;
        *pComponent_n = component_n;
        *pComponent_weights = component_nnz;
        *p_i_to_I = i_to_I;
        *p_j_to_J = j_to_J;
        
        
        if(colWeights) {
            /* Reassign weights based on columns */
            long *component_weights = *pComponent_weights;
            pA->NrColWeights = N;
            pA->ColWeights = malloc( N * sizeof( long ) );
            if (pA->ColWeights == NULL) {
                printf("Error\n");
                exit(1);
            }
            for(J=0; J<N; ++J) {
                pA->ColWeights[J] = Random1(20, 50);
            }
            
            for(cmpnnt=0; cmpnnt<numComponents; ++cmpnnt) {
                component_weights[cmpnnt] = 0;
                for(j=0; j<component_n[cmpnnt]; ++j) {
                    component_weights[cmpnnt] += pA->ColWeights[j_to_J[cmpnnt][j]];
                }
                
                /* Make sure each component has a unique weight, to be able to use it as identification */
                while(TRUE) {
                    int found = FALSE;
                    for(i=0; i<cmpnnt; ++i) {
                        if(component_weights[cmpnnt] == component_weights[i]) {
                            found = TRUE;
                            ++pA->ColWeights[j_to_J[cmpnnt][0]];
                            ++component_weights[cmpnnt];
                            break;
                        }
                    }
                    if(!found) {
                        break;
                    }
                }
            }
            
        }
        
        free(component_dummies);
        
    } /* end ConstructDisconnectedMatrix */
    
    
    /**
     * Free memory for a matrix constructed with ConstructDisconnectedMatrix().
     * See ConstructDisconnectedMatrix() for details on the parameters.
     */
    void DestructDisconnectedMatrix(struct sparsematrix *pA, int symmetric, int dummies, int colWeights,
                                    long **pComponent_m, long **pComponent_n, long **pComponent_weights,
                                    long ***p_i_to_I, long ***p_j_to_J) {
        free(*pComponent_weights);
        free(*pComponent_m);
        free(*p_i_to_I[0]);
        free(*p_i_to_I);
        if(!symmetric && !dummies) {
            free(*pComponent_n);
            free(*p_j_to_J[0]);
            free(*p_j_to_J);
        }
        
        MMSparseMatrixFreeMemory(pA);
        
    } /* end DestructDisconnectedMatrix */